diff --git a/docs/config-catalog.i18n.yaml b/docs/config-catalog.i18n.yaml index 6cdc1e42ee..e3de095ad5 100644 --- a/docs/config-catalog.i18n.yaml +++ b/docs/config-catalog.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/config-catalog.md -config-catalog.md: 5a265c583382b846507f7aae6f59be4341d0a823 -config-catalog.zh.md: d410d383bf7cbe576227edf792f92bb019ad043a +config-catalog.md: d25843091d453687bad5035c49bcb200cb03fff2 +config-catalog.zh.md: 1cc3002799afc10ca84f74044c7b28f91490ffa3 diff --git a/docs/config-catalog.md b/docs/config-catalog.md index 5a265c5833..d25843091d 100644 --- a/docs/config-catalog.md +++ b/docs/config-catalog.md @@ -863,7 +863,7 @@ export interface Config { } ``` -Source: [`packages/host/webserver/src/index.ts:45`](../packages/host/webserver/src/index.ts) +Source: [`packages/host/webserver/src/index.ts:59`](../packages/host/webserver/src/index.ts) diff --git a/docs/config-catalog.zh.md b/docs/config-catalog.zh.md index d410d383bf..1cc3002799 100644 --- a/docs/config-catalog.zh.md +++ b/docs/config-catalog.zh.md @@ -865,7 +865,7 @@ export interface Config { } ``` -来源:[`packages/host/webserver/src/index.ts:45`](../packages/host/webserver/src/index.ts) +来源:[`packages/host/webserver/src/index.ts:59`](../packages/host/webserver/src/index.ts) diff --git a/docs/event-producer-consumer.i18n.yaml b/docs/event-producer-consumer.i18n.yaml index 5fdbf36ed6..77c2dd6536 100644 --- a/docs/event-producer-consumer.i18n.yaml +++ b/docs/event-producer-consumer.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/event-producer-consumer.md -event-producer-consumer.md: d75d64b58b041c117bf2eaa1612c1b4683378a00 -event-producer-consumer.zh.md: adfc11782e179077b17199fc4ea6e8f52b3c465b +event-producer-consumer.md: d3901b1ddc7d99d4960324ab7459388fa4612bbe +event-producer-consumer.zh.md: 346a293a226719f781a9587eb0adefcf3f6bed08 diff --git a/docs/event-producer-consumer.md b/docs/event-producer-consumer.md index d75d64b58b..d3901b1ddc 100644 --- a/docs/event-producer-consumer.md +++ b/docs/event-producer-consumer.md @@ -57,6 +57,7 @@ This matrix shows which packages dispatch each harness-owned event and which pac | `tools/post-execute` | `waterfall` | [`packages/core/tools/src/index.ts:175`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`spill-policy`](../packages/spill/spill-policy), [`tool-fs-search`](../packages/fs/tool-fs-search) | | `tools/pre-execute` | `waterfall` | [`packages/core/tools/src/index.ts:152`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`tool-jobs`](../packages/jobs/tool-jobs) | | `tools/result` | `emit` | [`packages/core/tools/src/index.ts:197`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`events.dispatch`) | [`agent-instructions`](../packages/context/agent-instructions), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver) | +| `webserver/index-inject` | `emit` | [`packages/host/webserver/src/index.ts:34`](../packages/host/webserver/src/index.ts) | `webserver` (`emit`) | - | | `workflow/agent-end` | `emit` | [`packages/workflow/workflow/src/index.ts:79`](../packages/workflow/workflow/src/index.ts) | [`workflow`](../packages/workflow/workflow) (`events.dispatch`) | [`tool-workflow`](../packages/workflow/tool-workflow), [`workflow`](../packages/workflow/workflow) | | `workflow/agent-start` | `emit` | [`packages/workflow/workflow/src/index.ts:68`](../packages/workflow/workflow/src/index.ts) | [`workflow`](../packages/workflow/workflow) (`events.dispatch`) | [`tool-workflow`](../packages/workflow/tool-workflow), [`workflow`](../packages/workflow/workflow) | | `workflow/end` | `emit` | [`packages/workflow/workflow/src/index.ts:89`](../packages/workflow/workflow/src/index.ts) | [`workflow`](../packages/workflow/workflow) (`events.dispatch`) | [`workflow`](../packages/workflow/workflow) | diff --git a/docs/event-producer-consumer.zh.md b/docs/event-producer-consumer.zh.md index adfc11782e..346a293a22 100644 --- a/docs/event-producer-consumer.zh.md +++ b/docs/event-producer-consumer.zh.md @@ -59,6 +59,7 @@ | `tools/post-execute` | `waterfall` | [`packages/core/tools/src/index.ts:175`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`spill-policy`](../packages/spill/spill-policy), [`tool-fs-search`](../packages/fs/tool-fs-search) | | `tools/pre-execute` | `waterfall` | [`packages/core/tools/src/index.ts:152`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`waterfall`) | [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex), [`tool-jobs`](../packages/jobs/tool-jobs) | | `tools/result` | `emit` | [`packages/core/tools/src/index.ts:197`](../packages/core/tools/src/index.ts) | [`tools`](../packages/core/tools) (`events.dispatch`) | [`agent-instructions`](../packages/context/agent-instructions), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver) | +| `webserver/index-inject` | `emit` | [`packages/host/webserver/src/index.ts:34`](../packages/host/webserver/src/index.ts) | `webserver` (`emit`) | - | | `workflow/agent-end` | `emit` | [`packages/workflow/workflow/src/index.ts:79`](../packages/workflow/workflow/src/index.ts) | [`workflow`](../packages/workflow/workflow) (`events.dispatch`) | [`tool-workflow`](../packages/workflow/tool-workflow), [`workflow`](../packages/workflow/workflow) | | `workflow/agent-start` | `emit` | [`packages/workflow/workflow/src/index.ts:68`](../packages/workflow/workflow/src/index.ts) | [`workflow`](../packages/workflow/workflow) (`events.dispatch`) | [`tool-workflow`](../packages/workflow/tool-workflow), [`workflow`](../packages/workflow/workflow) | | `workflow/end` | `emit` | [`packages/workflow/workflow/src/index.ts:89`](../packages/workflow/workflow/src/index.ts) | [`workflow`](../packages/workflow/workflow) (`events.dispatch`) | [`workflow`](../packages/workflow/workflow) | diff --git a/docs/subsystems/client-modules.i18n.yaml b/docs/subsystems/client-modules.i18n.yaml index 130e99d258..4c8f701de2 100644 --- a/docs/subsystems/client-modules.i18n.yaml +++ b/docs/subsystems/client-modules.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/subsystems/client-modules.md -client-modules.md: 52fa4ef73766e8e7e7601e604f78aded9940328f -client-modules.zh.md: e830349be77000759684a76b2834478a803e3e11 +client-modules.md: 81c85e2f0eb1d7248459f6ca092ed89f602e8415 +client-modules.zh.md: 37b8acc0d6d335802333100b5e55e709153911da diff --git a/docs/subsystems/client-modules.md b/docs/subsystems/client-modules.md index 52fa4ef737..81c85e2f0e 100644 --- a/docs/subsystems/client-modules.md +++ b/docs/subsystems/client-modules.md @@ -82,7 +82,7 @@ Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnp ### `ctx.clientModules` — `ClientModuleRegistry` -The web plugin table service: incremental `dsh.client` scan + wire composition + bundle route + index tap. Construction runs the activation scan synchronously — a malformed declaration or missing bundle among the already-loaded entries aggregates into one loud throw (FAILED fiber; the boot activation audit reports it). +The web plugin table service: incremental `dsh.client` scan + wire composition + bundle route + index injection rows. Construction runs the activation scan synchronously — a malformed declaration or missing bundle among the already-loaded entries aggregates into one loud throw (FAILED fiber; the boot activation audit reports it). ```ts cordis-catalog /** @@ -122,5 +122,5 @@ onRebuilt(listener: (id: string, rev: string) => void): () => void onGraphChanged(listener: () => void): () => void ``` -Source: [`packages/client/modules/src/index.ts:295`](../../packages/client/modules/src/index.ts) +Source: [`packages/client/modules/src/index.ts:282`](../../packages/client/modules/src/index.ts) diff --git a/docs/subsystems/client-modules.zh.md b/docs/subsystems/client-modules.zh.md index e830349be7..37b8acc0d6 100644 --- a/docs/subsystems/client-modules.zh.md +++ b/docs/subsystems/client-modules.zh.md @@ -82,7 +82,7 @@ Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnp ### `ctx.clientModules` — `ClientModuleRegistry` -The web plugin table service: incremental `dsh.client` scan + wire composition + bundle route + index tap. Construction runs the activation scan synchronously — a malformed declaration or missing bundle among the already-loaded entries aggregates into one loud throw (FAILED fiber; the boot activation audit reports it). +The web plugin table service: incremental `dsh.client` scan + wire composition + bundle route + index injection rows. Construction runs the activation scan synchronously — a malformed declaration or missing bundle among the already-loaded entries aggregates into one loud throw (FAILED fiber; the boot activation audit reports it). ```ts cordis-catalog /** @@ -122,5 +122,5 @@ onRebuilt(listener: (id: string, rev: string) => void): () => void onGraphChanged(listener: () => void): () => void ``` -Source: [`packages/client/modules/src/index.ts:295`](../../packages/client/modules/src/index.ts) +Source: [`packages/client/modules/src/index.ts:282`](../../packages/client/modules/src/index.ts) diff --git a/docs/subsystems/web-server.i18n.yaml b/docs/subsystems/web-server.i18n.yaml index 8ed53c1fa7..519c6e7043 100644 --- a/docs/subsystems/web-server.i18n.yaml +++ b/docs/subsystems/web-server.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/subsystems/web-server.md -web-server.md: 414863d88cdc34163882cdc3dfe8dc7c1471ac77 -web-server.zh.md: d756192ce733a3fe79c9eaf44e5d556c4bac90a8 +web-server.md: 7dd700d24f63d31f34d8711069477414292e3bf6 +web-server.zh.md: ca67ff231e9aad9144fa5203ee46435f636a93da diff --git a/docs/subsystems/web-server.md b/docs/subsystems/web-server.md index 414863d88c..7dd700d24f 100644 --- a/docs/subsystems/web-server.md +++ b/docs/subsystems/web-server.md @@ -102,7 +102,46 @@ tapIndex(transform: (html: string) => string): () => void * @returns the transformed body. */ applyIndexTaps(html: string): string + +/** + * Gather the structured injection table: one `webserver/index-inject` emit, + * every subscriber pushes its current rows. Fresh per call, so subscribers + * read live state (module graph, theme preference) at emit time. + * @returns rows in subscriber activation order. + */ +collectIndexInjections(): IndexInjection[] + +/** + * Render one index.html body: the structured injection table first, then + * the raw `tapIndex` transforms over the result. + * @param html - the raw index.html body. + * @returns the transformed body. + */ +renderIndex(html: string): string ``` -Source: [`packages/host/webserver/src/index.ts:59`](../../packages/host/webserver/src/index.ts) +Source: [`packages/host/webserver/src/index.ts:73`](../../packages/host/webserver/src/index.ts) + + + +### `webserver/*` events + + + +#### `webserver/index-inject` — emit + +Collect the structured index injection table. Emitted on every index render and every worker boot-payload request; listeners push their current rows, so a row's data is read fresh at emit time. + +```ts cordis-catalog +/** + * Collect the structured index injection table. Emitted on every index + * render and every worker boot-payload request; listeners push their + * current rows, so a row's data is read fresh at emit time. + * @param table - Mutable row table; listeners append in activation order. + * @mode emit + */ +'webserver/index-inject'(table: IndexInjection[]): void +``` + +Source: [`packages/host/webserver/src/index.ts:34`](../../packages/host/webserver/src/index.ts) diff --git a/docs/subsystems/web-server.zh.md b/docs/subsystems/web-server.zh.md index d756192ce7..ca67ff231e 100644 --- a/docs/subsystems/web-server.zh.md +++ b/docs/subsystems/web-server.zh.md @@ -102,7 +102,46 @@ tapIndex(transform: (html: string) => string): () => void * @returns the transformed body. */ applyIndexTaps(html: string): string + +/** + * Gather the structured injection table: one `webserver/index-inject` emit, + * every subscriber pushes its current rows. Fresh per call, so subscribers + * read live state (module graph, theme preference) at emit time. + * @returns rows in subscriber activation order. + */ +collectIndexInjections(): IndexInjection[] + +/** + * Render one index.html body: the structured injection table first, then + * the raw `tapIndex` transforms over the result. + * @param html - the raw index.html body. + * @returns the transformed body. + */ +renderIndex(html: string): string ``` -Source: [`packages/host/webserver/src/index.ts:59`](../../packages/host/webserver/src/index.ts) +Source: [`packages/host/webserver/src/index.ts:73`](../../packages/host/webserver/src/index.ts) + + + +### `webserver/*` events + + + +#### `webserver/index-inject` — emit + +Collect the structured index injection table. Emitted on every index render and every worker boot-payload request; listeners push their current rows, so a row's data is read fresh at emit time. + +```ts cordis-catalog +/** + * Collect the structured index injection table. Emitted on every index + * render and every worker boot-payload request; listeners push their + * current rows, so a row's data is read fresh at emit time. + * @param table - Mutable row table; listeners append in activation order. + * @mode emit + */ +'webserver/index-inject'(table: IndexInjection[]): void +``` + +Source: [`packages/host/webserver/src/index.ts:34`](../../packages/host/webserver/src/index.ts) diff --git a/packages/extensions/tool-cordis/src/api-catalog.ts b/packages/extensions/tool-cordis/src/api-catalog.ts index 708c179df5..a20c0b9aff 100644 --- a/packages/extensions/tool-cordis/src/api-catalog.ts +++ b/packages/extensions/tool-cordis/src/api-catalog.ts @@ -459,8 +459,8 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [ }, { key: 'clientModules', - summary: 'The web plugin table service: incremental `dsh.client` scan + wire composition + bundle route + index tap.', - description: 'The web plugin table service: incremental `dsh.client` scan + wire composition + bundle route + index tap. Construction runs the activation scan synchronously — a malformed declaration or missing bundle among the already-loaded entries aggregates into one loud throw (FAILED fiber; the boot activation audit reports it).', + summary: 'The web plugin table service: incremental `dsh.client` scan + wire composition + bundle route + index injection rows.', + description: 'The web plugin table service: incremental `dsh.client` scan + wire composition + bundle route + index injection rows. Construction runs the activation scan synchronously — a malformed declaration or missing bundle among the already-loaded entries aggregates into one loud throw (FAILED fiber; the boot activation audit reports it).', methods: [ { signature: 'graph(): WebBootGraph', @@ -2217,6 +2217,18 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [ parameters: [{ name: 'html', description: 'the raw index.html body.' }], returns: 'the transformed body.', }, + { + signature: 'collectIndexInjections(): IndexInjection[]', + description: 'Gather the structured injection table: one `webserver/index-inject` emit, every subscriber pushes its current rows. Fresh per call, so subscribers read live state (module graph, theme preference) at emit time.', + parameters: [], + returns: 'rows in subscriber activation order.', + }, + { + signature: 'renderIndex(html: string): string', + description: 'Render one index.html body: the structured injection table first, then the raw `tapIndex` transforms over the result.', + parameters: [{ name: 'html', description: 'the raw index.html body.' }], + returns: 'the transformed body.', + }, ], }, { @@ -2685,6 +2697,14 @@ export const EVENT_API: readonly EventApiEntry[] = [ description: 'Observe the frozen, lossless-JSON final outcome. Listener failures are contained. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): keyed by `exec.agent`.', parameters: [{ name: 'exec', description: 'the execution object that traversed the pipeline.' }, { name: 'result', description: 'a deep-frozen snapshot of the final returned result.' }], }, + { + name: 'webserver/index-inject', + mode: 'emit', + signature: '\'webserver/index-inject\'(table: IndexInjection[]): void', + summary: 'Collect the structured index injection table.', + description: 'Collect the structured index injection table. Emitted on every index render and every worker boot-payload request; listeners push their current rows, so a row\'s data is read fresh at emit time.', + parameters: [{ name: 'table', description: 'Mutable row table; listeners append in activation order.' }], + }, { name: 'workflow/agent-end', mode: 'emit', @@ -3297,6 +3317,14 @@ export const TYPE_API: readonly TypeApiEntry[] = [ name: 'InboxTarget', declaration: 'export type InboxTarget = \'next-turn\' | \'next-step\';', }, + { + name: 'IndexInjection', + declaration: 'export type IndexInjection = {\n kind: \'global\';\n name: string;\n value: unknown;\n} | {\n kind: \'script\';\n placement: IndexInjectionPlacement;\n text: string;\n} | {\n kind: \'script-src\';\n placement: IndexInjectionPlacement;\n src: string;\n} | {\n kind: \'style\';\n text: string;\n} | {\n kind: \'html\';\n placement: IndexInjectionPlacement;\n html: string;\n};', + }, + { + name: 'IndexInjectionPlacement', + declaration: 'export type IndexInjectionPlacement = \'head\' | \'body\';', + }, { name: 'InvariantFailure', declaration: 'export type InvariantFailure = (message: string) => never;', diff --git a/scripts/gen-cordis-catalog.ts b/scripts/gen-cordis-catalog.ts index a088890413..4b1f71c5e9 100644 --- a/scripts/gen-cordis-catalog.ts +++ b/scripts/gen-cordis-catalog.ts @@ -184,6 +184,7 @@ export const EVENT_SCOPE_PAGE: Record = { 'system-prompt': 'system-prompt.md', 'session-telemetry': 'session-telemetry.md', 'tools': 'tools.md', + 'webserver': 'web-server.md', 'workflow': 'workflow.md', } @@ -480,6 +481,7 @@ export const LINK_MAP: Readonly> = { PresetSpec: 'permission-presets.md', InvariantInstaller: 'invariants.md', WebRoute: 'web-server.md', + IndexInjection: 'web-server.md', StorageBackend: 'storage.md', StorageForms: 'storage.md', Domain: 'storage.md',