Merge remote-tracking branch 'origin/master' into feature/subagent-policy-inheritance

# Conflicts:
#	.agents/notes/implemented/feature/2026-07-06-approval-seam.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-06-approval-seam.md
#	.agents/notes/implemented/feature/2026-07-06-approval-seam.zh.md
#	.agents/notes/implemented/feature/2026-07-06-sandbox.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-06-sandbox.md
#	.agents/notes/implemented/feature/2026-07-06-sandbox.zh.md
#	docs/event-producer-consumer.md
#	examples/acp-agent/tests/acp.snapshot.ts
This commit is contained in:
kingwl
2026-07-25 14:37:21 +08:00
647 changed files with 14011 additions and 25019 deletions
@@ -196,6 +196,32 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
},
],
},
{
key: 'clientModuleHost',
summary: 'The web plugin table service: incremental dshClient scan + wire composition + bundle route + index tap.',
methods: [
{
signature: 'graph(): WebBootGraph',
jsDoc: '/**\n * Current composed entry graph (stable object between changes).\n * @returns the graph served as `window.__DSH_BOOT__`.\n */',
},
{
signature: 'clientPath(id: string): string | undefined',
jsDoc: '/**\n * Absolute path of an entry\'s client bundle.\n * @param id - entry id (package name).\n * @returns the path, or undefined for an unknown id.\n */',
},
{
signature: 'rebuilt(id: string): string | undefined',
jsDoc: '/**\n * Re-hash one bundle (the HMR watch\'s registration hook — the only entry\n * point through which bundle content changes reach the graph).\n * @param id - entry id (package name).\n * @returns the new rev, or undefined for an unknown id.\n */',
},
{
signature: 'onRebuilt(listener: (id: string, rev: string) => void): () => void',
jsDoc: '/**\n * Subscribe to bundle rebuilds; fires only when the re-hash changed the rev.\n * @param listener - receives the entry id and its new bundle rev.\n * @returns the unsubscriber.\n */',
},
{
signature: 'onGraphChanged(listener: () => void): () => void',
jsDoc: '/**\n * Fires after any flush that recomposed the graph (row added/removed, or a\n * rebuilt rev change). Pull model: listeners re-read {@link graph}.\n * @param listener - notified with no payload.\n * @returns the unsubscriber.\n */',
},
],
},
{
key: 'codeRuntime',
summary: 'Registers one `ctx.codeRuntime` implementation.',
@@ -322,6 +348,20 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
},
],
},
{
key: 'httpServer',
summary: 'The web-shape HTTP carrier service.',
methods: [
{
signature: 'register(route: WebRoute): () => void',
jsDoc: '/**\n * Register a named route. Duplicate (kind, path) throws — route patterns are\n * a composition-level contract, so a collision is a misconfiguration.\n * @param route - kind, path, and the owning handler.\n * @returns the disposer removing the route.\n */',
},
{
signature: 'tapIndex(transform: (html: string) => string): () => void',
jsDoc: '/**\n * Register an index.html transform, applied to every index response in\n * registration order.\n * @param transform - pure html-to-html function.\n * @returns the disposer removing the transform.\n */',
},
],
},
{
key: 'invariants',
summary: 'Package-owned invariant registry with global and regex-based selection.',
@@ -658,6 +698,20 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
},
],
},
{
key: 'storage',
summary: 'The storage hub service.',
methods: [
{
signature: 'mount<K extends keyof StorageForms>(form: K, facility: StorageForms[K]): () => void',
jsDoc: '/**\n * Mount a data-form facility on the hub. Mounting is an effect: the\n * returned disposer unmounts the form.\n * @param form - Form key declared in {@link StorageForms}.\n * @param facility - The facility instance to expose.\n * @returns the disposer that unmounts the form.\n */',
},
{
signature: 'form<K extends keyof StorageForms>(form: K): StorageForms[K]',
jsDoc: '/**\n * Resolve a mounted data form.\n * @param form - Form key declared in {@link StorageForms}.\n * @returns the mounted facility.\n */',
},
],
},
{
key: 'subagents',
summary: 'Named provider registry and capability-checked start surface.',
@@ -862,6 +916,28 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
},
],
},
{
key: 'workspace',
summary: 'The workspace registry service.',
methods: [
{
signature: 'async create(path: string, title?: string): Promise<Workspace>',
jsDoc: '/**\n * Create a workspace over an existing directory. The path is canonicalized\n * through `fs.realpath` first — a nonexistent path rejects with the\n * original `ENOENT`, a path resolving to anything but a directory rejects,\n * and a canonical path already owned by another workspace (including a\n * symlink resolving to it) rejects.\n * @param path - Directory the workspace points at; canonicalized before storing.\n * @param title - Display title; defaults to `basename` of the canonical path.\n * @returns the created workspace after durability.\n */',
},
{
signature: 'get(id: WorkspaceId): Workspace | undefined',
jsDoc: '/**\n * Look up a workspace by id.\n * @param id - The workspace id.\n * @returns the workspace, or `undefined` when unknown.\n */',
},
{
signature: 'list(): Workspace[]',
jsDoc: '/**\n * Snapshot of all workspaces, in load-then-creation order.\n * @returns a fresh array of the cached entities.\n */',
},
{
signature: 'async resolveByPath(path: string): Promise<Workspace | undefined>',
jsDoc: '/**\n * Resolve a workspace by directory path, through the same `fs.realpath`\n * canon as {@link create} (hence async). A path that does not exist rejects\n * with the original error — a missing directory has no canonical form to\n * compare (a workspace whose recorded directory vanished is only reachable\n * by id; see `Workspace.status`).\n * @param path - Directory path in any spelling (symlinks, `..`, trailing slash).\n * @returns the owning workspace, or `undefined` when none matches.\n */',
},
],
},
]
/** Every harness event, sorted by name. */
@@ -1013,6 +1089,13 @@ export const EVENT_API: readonly EventApiEntry[] = [
jsDoc: '/**\n * A command was registered or unregistered. This is an unfiltered registry\n * notification because a global or scoped change may affect any UI view.\n * Observer failures are contained and cannot veto the registry mutation.\n * @mode emit\n */',
summary: 'A command was registered or unregistered.',
},
{
name: 'domain/changed',
mode: 'emit',
signature: '\'domain/changed\'(change: DomainChanged): void',
jsDoc: '/**\n * A domain record or the global singleton changed, emitted once per write\n * strictly after the backend acknowledged durability. Events of one\n * domain arrive in its write-chain order.\n * @param change - domain, table (`\'\'` for global), key (`\'\'` for global),\n * operation discriminant, and on `put` the new snapshot.\n * @mode emit\n */',
summary: 'A domain record or the global singleton changed, emitted once per write strictly after the backend acknowledged durability.',
},
{
name: 'fs/edit-intent',
mode: 'waterfall',
@@ -2015,6 +2098,10 @@ export const TYPE_API: readonly TypeApiEntry[] = [
name: 'SpillSource',
declaration: 'export interface SpillSource {\n toolName: string;\n callId: CallId;\n label: string;\n}',
},
{
name: 'StorageForms',
declaration: 'export interface StorageForms {\n}',
},
{
name: 'StreamChunk',
declaration: 'export type StreamChunk = {\n type: \'block-start\';\n index: number;\n blockType: ContentBlockType;\n} | {\n type: \'text-delta\';\n index: number;\n text: string;\n} | {\n type: \'reasoning-delta\';\n index: number;\n text: string;\n} | {\n type: \'tool-call-delta\';\n index: number;\n id: CallId;\n name?: string;\n argumentsDelta: string;\n} | {\n type: \'block-end\';\n index: number;\n block: ContentBlock;\n} | {\n type: \'usage\';\n usage: TokenUsage;\n} | {\n type: \'finish\';\n reason: FinishReason;\n replayState?: unknown;\n};',
@@ -2307,6 +2394,14 @@ export const TYPE_API: readonly TypeApiEntry[] = [
name: 'WebFetchResult',
declaration: 'export interface WebFetchResult {\n readonly url: string;\n readonly statusCode: number;\n readonly body: WebFetchBody;\n readonly truncated: boolean;\n}',
},
{
name: 'WebRoute',
declaration: 'export interface WebRoute {\n kind: WebRouteKind;\n path: string;\n handler: (req: IncomingMessage, res: ServerResponse) => void | Promise<void>;\n}',
},
{
name: 'WebRouteKind',
declaration: 'export type WebRouteKind = \'exact\' | \'prefix\';',
},
{
name: 'WebSearchProvider',
declaration: 'export interface WebSearchProvider {\n readonly id: string;\n available(): boolean;\n search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult>;\n}',
@@ -2351,6 +2446,10 @@ export const TYPE_API: readonly TypeApiEntry[] = [
name: 'WorkflowStopReason',
declaration: 'export type WorkflowStopReason = \'completed\' | \'cancelled\' | \'error\';',
},
{
name: 'Workspace',
declaration: 'export interface Workspace {\n readonly id: WorkspaceId;\n readonly path: string;\n readonly title: string;\n readonly sessionIds: readonly SessionId[];\n setTitle(title: string): Promise<void>;\n attachSession(sessionId: SessionId): Promise<void>;\n detachSession(sessionId: SessionId): Promise<void>;\n status(): Promise<\'ok\' | \'missing-dir\'>;\n}',
},
]
/** The inherited `ctx` surface (cordis core + loader/hmr/timer), in curated order. */
+4 -4
View File
@@ -1,5 +1,5 @@
/**
* ACP render intents for the three cordis tools — all `generic` cards, decided
* UI render intents for the three cordis tools — all `generic` cards, decided
* up front as part of the tool design. Presenters are pure functions of the
* call arguments (they run on replay too): no I/O, no session state, no clock.
* No `presentResult` overrides exist — the tools' text results are their
@@ -13,7 +13,7 @@ import type { GenericCallView } from '@deepseek-ai/dsh-tools'
/**
* The `cordis_inspect` call card: a read, titled with the requested section.
* @param args - the validated call arguments.
* @returns the generic card the ACP bridge renders.
* @returns the generic call card.
*/
export function presentInspectCall(args: { what?: string; name?: string }): GenericCallView {
const target = args.name === undefined ? args.what : `${args.what}: ${args.name}`
@@ -27,7 +27,7 @@ export function presentInspectCall(args: { what?: string; name?: string }): Gene
/**
* The `cordis_mount` call card: an execute carrying the mount code as raw input.
* @param args - the validated call arguments.
* @returns the generic card the ACP bridge renders.
* @returns the generic call card.
*/
export function presentMountCall(args: { code: string }): GenericCallView {
return {
@@ -41,7 +41,7 @@ export function presentMountCall(args: { code: string }): GenericCallView {
/**
* The `cordis_unmount` call card: a delete, titled with the mount id.
* @param args - the validated call arguments.
* @returns the generic card the ACP bridge renders.
* @returns the generic call card.
*/
export function presentUnmountCall(args: { id: string }): GenericCallView {
return {