Science
September 10, 2026 · View on GitHub
English | 中文
Science services own interpreter execution, project artifacts, and artifact-edit admission. The Science Runtime, artifact store, and tool package define their configuration and persistence responsibilities. The Science application bundle controls activation; an unavailable method rejects execution explicitly.
The Science client uses the native right Sidebar: a project-library guide, stable artifact-identity resources and exact-version navigation. Process is a sibling of Trajectory. Public conversation slots handle input targets, turn-end artifacts and tool views; upstream Files owns workspace browsing. See the client package and decision.
Cordis API
Generated from source by scripts/gen-cordis-catalog.ts (verified fresh by pnpm run verify-cordis-catalog in doc-sync; regenerate with pnpm run gen-cordis-catalog) — the language sides differ only in locale-specific paired document paths. Signature blocks use a ts cordis-catalog fence and keep the original source JSDoc; dispatch modes are defined in the primer, and the framework-inherited ctx API lives in cordis-api/inherited.md.
ctx.scienceArtifactStore — ScienceArtifactStore
The project artifact store service. Registers as ctx.scienceArtifactStore; every method is self-sufficient given a projectId (no prior openProject call is required in the same process), so a Host restart or a second session in the same project can resume work against a project it already knows the id of.
/**
* Resolve a workspace directory's project identity and ensure its store is open.
* @param workspacePath - the workspace directory to resolve.
* @returns the resolved identity, store root, and how it was resolved.
*/
openProject(workspacePath: string): Promise<OpenedProject>
/**
* Create a new artifact and its first version.
* @param projectId - the owning project.
* @param input - the first version's bytes, kind, provenance, and optional explicit baseline.
* @returns the created artifact and its first version.
*/
createArtifact(projectId: ProjectId, input: CreateArtifactInput): Promise<{ artifact: ArtifactRecord; version: VersionRecord }>
/**
* Append a new version onto an existing artifact, linearized against every
* other concurrent append to the same artifact.
* @param projectId - the owning project.
* @param artifactId - the artifact to append to.
* @param input - the new version's bytes, provenance, and optional explicit baseline.
* @returns the appended version.
*/
appendVersion(projectId: ProjectId, artifactId: ArtifactId, input: AppendVersionInput): Promise<VersionRecord>
/**
* Append one metadata edit onto a version.
* @param projectId - the owning project.
* @param versionId - the version to annotate.
* @param patch - the edit's author and the fields to change.
* @returns the version, reflecting the newly appended annotation.
*/
annotateVersion(projectId: ProjectId, versionId: VersionId, patch: AnnotateVersionInput): Promise<VersionRecord>
/**
* Look up one artifact by id.
* @param projectId - the owning project.
* @param artifactId - the artifact to look up.
* @returns the artifact, or `undefined` when no such artifact exists.
*/
getArtifact(projectId: ProjectId, artifactId: ArtifactId): Promise<ArtifactRecord | undefined>
/**
* Look up one version by id.
* @param projectId - the owning project.
* @param versionId - the version to look up.
* @returns the version, or `undefined` when no such version exists.
*/
getVersion(projectId: ProjectId, versionId: VersionId): Promise<VersionRecord | undefined>
/**
* Look up an artifact's current latest version.
* @param projectId - the owning project.
* @param artifactId - the artifact whose latest version to fetch.
* @returns the latest version, or `undefined` when the artifact does not exist.
*/
getLatestVersion(projectId: ProjectId, artifactId: ArtifactId): Promise<VersionRecord | undefined>
/**
* List every artifact in a project, oldest first.
* @param projectId - the owning project.
* @returns every artifact currently in the project's store.
*/
listArtifacts(projectId: ProjectId): Promise<readonly ArtifactRecord[]>
/**
* List one artifact's versions in ordinal order.
* @param projectId - the owning project.
* @param artifactId - the artifact whose versions to list.
* @returns every version of the artifact, oldest first.
*/
listVersions(projectId: ProjectId, artifactId: ArtifactId): Promise<readonly VersionRecord[]>
/**
* List one artifact's active (non-removed) notes, oldest first.
* @param projectId - the owning project.
* @param artifactId - the artifact whose notes to list.
* @returns every note that has not been removed.
*/
listNotes(projectId: ProjectId, artifactId: ArtifactId): Promise<readonly ArtifactNoteRecord[]>
/**
* Add a new note.
* @param projectId - the owning project.
* @param input - the artifact (and optional version) to attach the note to, its text, and its author.
* @returns the created note.
*/
putNote(projectId: ProjectId, input: PutNoteInput): Promise<ArtifactNoteRecord>
/**
* Soft-delete a note.
* @param projectId - the owning project.
* @param noteId - the note to remove.
*/
removeNote(projectId: ProjectId, noteId: NoteId): Promise<void>
/**
* Look up one version's live-figure-object state.
* @param projectId - the owning project.
* @param versionId - the version whose figure state to fetch.
* @returns the figure state, or `undefined` when this version carries none.
*/
getFigureState(projectId: ProjectId, versionId: VersionId): Promise<FigureStateRecord | undefined>
/**
* Apply a reconciliation-status patch to one version.
* @param projectId - the owning project.
* @param versionId - the version whose health to update.
* @param patch - fields to overwrite; an omitted field keeps its current value.
* @returns the updated health row.
*/
setVersionHealth(projectId: ProjectId, versionId: VersionId, patch: VersionHealthPatch): Promise<VersionHealthRecord>
/**
* Read one version's bytes by content address.
* @param projectId - the owning project.
* @param sha256 - the digest from an already-resolved version row.
* @returns the verified bytes.
*/
readBlob(projectId: ProjectId, sha256: string): Promise<Uint8Array>
/**
* Reconcile one project's store against session-log events a caller has
* already read and folded — see the package README's Reconciliation
* section for the seven-case table this decides. This package never reads
* session logs itself; `dsh-science-runtime` reads them (bounded by its
* own `reconcileMaxSessions` Config) and folds duplicate events per
* `versionId` (last write wins) before calling this. Never throws for one
* bad item — see `ReconcileResult.errors` — and never writes a session
* log; the store is the sole write target.
* @param projectId - the project to reconcile.
* @param events - every `science/artifact-saved` event the caller read from
* this project's session logs, folded per `versionId`.
* @param eventSetComplete - whether the caller read every relevant session
* log and event; when false, an absent event cannot mark or clear orphan health.
* @param cursor - prior bounded-walk progress over this stable event set.
* @returns what this call checked, reconstructed, and could not fully reconcile, bounded by the configured `reconcileMaxVersions`.
*/
reconcileProject( projectId: ProjectId, events: ReadonlyMap<VersionId, ReconcileArtifactSavedEvent>, eventSetComplete: boolean, cursor?: ReconcileCursor, ): Promise<ReconcileResult>
/**
* Read project-wide reconciliation health — the read interface a Host
* BFF (`dsh-api-proxy`) surfaces to a client's Files panel: aggregate
* `orphan`/`reconstructed`/`missingContent` counts plus the per-version
* list backing them. A pure read of whatever the last `reconcileProject`
* call recorded; it never itself compares the store against a session log.
* @param projectId - the owning project.
* @returns aggregate counts and the unhealthy version list, most recently checked first.
*/
getReconciliationSummary(projectId: ProjectId): Promise<ReconciliationSummary>
/**
* Permanently delete a project's entire store. The one cascade boundary:
* session deletion never calls this, and never removes artifact rows.
* @param projectId - the project to delete.
*/
deleteProject(projectId: ProjectId): Promise<void>
Source: packages/science/science-artifact-store/src/index.ts
ctx.scienceEdits — ScienceEditService
Remote service admitting browser edit gestures into the addressed live agent.
/**
* Validate exact current artifact selections and queue one structured edit
* message. Media type and live-figure-object state — the store's, since
* the T1/T2 artifact-authority migration — gate each target: a region
* target's raster is read back from the project artifact store and
* admitted as an ordinary session message attachment, so the model-visible
* image stays reconstructable from the session log alone; an element
* target must match one addressable chart entry's id, kind, axes, label,
* and current-value summary, read from the store's `figure_state` row and
* never minting an attachment.
* @param agent - exact live agent resolved by the Remote lookup policy.
* @param request - selected versions, targets, and shared user instruction.
* @returns durable-inbox admission receipt.
*/
@Remote('submit') async submit(agent: Agent, request: ScienceEditRequest): Promise<ScienceEditReceipt>
/**
* Apply deterministic operations to one exact current addressable chart.
* @param agent - Agent whose session owns the chart.
* @param request - Exact chart version and ordered operations.
* @param signal - Client-owned cancellation for the Runtime operation.
* @returns the committed direct-edit version and unresolved operation targets.
*/
@Remote('applyChartOps') async applyChartOps( agent: Agent, request: ScienceChartEditRequest, signal: AbortSignal, ): Promise<ScienceChartEditReceipt>
/**
* Render chart operations through the Runtime for live preview without
* committing a new artifact version: the preview PNG rides back as base64
* and no store or session state is published.
* @param agent - exact live agent whose session owns the chart artifact.
* @param request - exact target artifact/version and operations to preview.
* @param signal - caller-owned cancellation for the kernel round-trip.
* @returns the base64 preview PNG, its re-extracted chart state, and any operations whose targets could not be resolved.
*/
@Remote('previewChartOps') async previewChartOps( agent: Agent, request: ScienceChartEditRequest, signal: AbortSignal, ): Promise<import('./types.ts').ScienceChartPreviewReceipt>
/**
* Add one user-only note after validating its exact visible artifact version.
* @param agent - Agent whose session owns the artifact.
* @param request - Exact artifact version and plain note text.
* @returns acceptance receipt after the note event commits.
*/
@Remote('addArtifactNote') addArtifactNote(agent: Agent, request: ScienceArtifactNoteAddRequest): ScienceArtifactNoteReceipt
/**
* Remove one active user-only note owned by the named logical artifact.
* @param agent - Agent whose session owns the note.
* @param request - Logical artifact and add-event sequence identifying the note.
* @returns acceptance receipt after the removal event commits.
*/
@Remote('removeArtifactNote') removeArtifactNote(agent: Agent, request: ScienceArtifactNoteRemoveRequest): ScienceArtifactNoteReceipt
/**
* Duplicate one exact committed artifact version into a brand-new logical
* artifact in the same project. A viewer-only operation — never exposed
* as a model tool.
* @param agent - Agent whose session owns the new artifact's origin.
* @param request - Store version id to duplicate and the new logical name.
* @param signal - Client-owned cancellation for the Runtime operation.
* @returns the new artifact's identity and first version.
*/
@Remote('saveArtifactAs') async saveArtifactAs( agent: Agent, request: ScienceSaveArtifactAsRequest, signal: AbortSignal, ): Promise<ScienceSaveArtifactAsReceipt>
Types: Agent
Source: packages/science/tool-science/src/edit-message.ts
ctx.scienceReads — ScienceReadService
Read-only Remote service over a session's project and durable attachment references.
/**
* Read one session-authorized immutable version.
* @param sessionId - Authorizing session.
* @param versionId - Exact version.
* @returns Verified bytes encoded as base64.
*/
@Remote async scienceArtifact(sessionId: SessionId, versionId: VersionId): Promise<{ versionId: VersionId; mediaType: string; byteCount: number; data: string }>
/**
* Read an exact version's editable chart state.
* @param sessionId - Authorizing session.
* @param versionId - Exact version.
* @returns Chart state or null for non-chart versions.
*/
@Remote async scienceChartState(sessionId: SessionId, versionId: VersionId): Promise<{ chart: ScienceChartState | null }>
/**
* Read a referenced UTF-8 file.
* @param sessionId - Authorizing session.
* @param attachmentId - Durable file identity.
* @returns Validated text with its reference.
*/
@Remote async textAttachment(sessionId: SessionId, attachmentId: AttachmentId): Promise<{ attachment: FileAttachmentRef; data: string }>
/**
* Read current project metadata.
* @param sessionId - Authorizing session.
* @returns Current authorized store facts.
*/
@Remote async scienceLibrary(sessionId: SessionId): Promise<{ projectId: string; artifacts: ScienceLibraryArtifact[]; health: ScienceLibraryHealth }>
/**
* Read current project metadata.
* @param sessionId - Authorizing session.
* @param versionIds - Exact versions to resolve.
* @returns Current authorized store facts.
*/
@Remote async scienceVersions(sessionId: SessionId, versionIds: readonly VersionId[]): Promise<{ versions: ScienceVersionSummary[] }>
Types: FileAttachmentRef · SessionId
Source: packages/science/tool-science/src/read-service.ts
ctx.scienceRuntime — ScienceRuntime
Folded local Science Runtime provider with public types free of Host paths.
/**
* Observe one configured existing Conda profile and append its whole-value
* environment revision. Static unusability becomes an honest `invalid`
* revision; capability, cancellation, and I/O failures append nothing.
* @param request - Exact live Session, profile identity, and caller signal.
* @returns The accepted durable environment revision.
*/
async bindEnvironment(request: BindScienceEnvironmentRequest): Promise<ScienceEnvironmentBinding>
/**
* Install packages into one language's applied prefix through micromamba,
* then, only on a successful install, re-observe the whole profile —
* exactly the operation `bindEnvironment`'s own post-first-run guard
* refuses. A re-observation that differs from the session's current
* binding appends a fresh whole-value `science/environment-bound`
* revision; one that matches it exactly appends none and returns the
* existing binding, since every requested package was already present (or
* an earlier attempt this session retried after a `'timed-out'`
* misclassification had, in fact, already finished — see
* `runMicromambaInstall`). A live kernel serving a superseded revision is
* left running: the next `startRun` for either language finds the
* revision mismatch and ends it (`environment-rebound`) before starting a
* fresh one, the same path an out-of-band rebind already takes (`kernel-set.ts`).
* @param request - Exact live Session, target language, package specs, and cancellation.
* @returns The install's terminal classification, output tails, and — on
* success — the environment as it now stands plus whether this call
* appended it as a fresh revision.
*/
async installPackages(request: InstallScienceEnvironmentPackagesRequest): Promise<InstallScienceEnvironmentPackagesResult>
/**
* Resolve and materialize exact artifact inputs, acquire this run's
* persistent kernel, publish its run start, then settle exactly one
* matching terminal fact and baseline-attributed capture walk.
* @param request - Exact live Session, source, authorization facts, optional artifact inputs and edit baselines, and cancellation.
* @returns A handle exposed only after `science/run-started` committed.
*/
async startRun(request: StartScienceRunRequest): Promise<ScienceRunHandle>
/**
* Apply one direct-edit request and commit its successful operations as a new PNG version.
*
* @param request - The exact chart version, operations, and cancellation context.
* @returns The committed artifact and any operations whose targets could not be resolved.
*/
async applyChartEdit(request: ScienceChartEditRequest): Promise<ScienceChartEditResult>
/**
* Render one direct-edit request without publishing store or session state:
* the shared warm/replay path exports a PNG and re-extracts its chart, but
* no artifact version or `science/artifact-saved` event is committed.
* Cold recovery uses an isolated interpreter and the operation's cancellation/deadline.
* @param request - Exact session, target artifact/version, and operations to render for preview.
* @returns The rendered preview PNG bytes, its re-extracted chart state, and any operations whose targets could not be resolved.
*/
async previewChartEdit(request: ScienceChartEditRequest): Promise<ScienceChartPreviewResult>
/**
* Re-commit an existing artifact version's exact store content reference
* with a curated title and caption: metadata-only, appending one new
* `version_annotations` row (`annotateVersion`) rather than opening a new
* version whose bytes would repeat their predecessor's. The store's
* annotation write is the sole authority for this metadata edit's own
* provenance (`actor: 'model'`, `sessionId`, `toolCallId`,
* `requestHeaderSeq`) — this operation never rebuilds a full version value
* and never lets the curating call's identity stand in for the content's
* own producer. A vetoed append after the store update leaves the store
* curated with no matching event — accepted metadata decay, resolved by
* the fold's own value staying the projection authority. A committed
* event is never rolled back because a later step fails; there is no
* later step here that can fail after the append.
* @param request - Exact live Session, target logical artifact (and optional version), title/caption, and cancellation.
* @returns The durable curated version this operation committed.
*/
async annotateArtifact(request: AnnotateScienceArtifactRequest): Promise<ScienceArtifactVersion>
/**
* Duplicate one existing artifact version into a brand-new logical
* artifact in the same project. Content-addressed bytes are reused (the
* store's blob admission is idempotent by digest, so re-admitting the
* source's own bytes never duplicates them on disk); provenance is a
* fresh fact this session originates, not a copy of the source's own
* producer — `baseVersionId` names the source explicitly instead. A
* viewer operation: no authorizing tool call, so `session.append` records
* only the store reference and the presentation snapshot the store just
* committed; the new version's `producerTurn` is the session's last
* started turn at the moment this method was called, so a save-as
* during an idle gap between turns attributes to the turn that was
* current then, never to whichever turn is newest by the time the store
* write commits.
* @param request - Exact Session, the store version to duplicate, and the new logical name.
* @returns The durable new artifact version this operation appended.
* @throws {@link ScienceRuntimeError} (`ARTIFACT_VERSION_NOT_FOUND`) when
* `sourceVersionId` does not identify a committed version in the
* session's owning project, or (`ARTIFACT_LOGICAL_NAME_CONFLICT`) when
* `newLogicalName` is already used in that project.
*/
async saveArtifactAs(request: SaveScienceArtifactAsRequest): Promise<ScienceArtifactVersion>