2026-06-20 19:47:09 +08:00
/ * *
2026-07-30 21:40:58 +08:00
* Generate the per - subsystem Cordis service / event reference regions from the
* Typert catalog projection . Every harness ` ctx.<key> ` service and event scope
* maps to exactly one ` docs/subsystems/ ` page through the curated tables below ;
2026-07-24 19:54:25 +08:00
* the generator injects each page ' s Cordis API reference between its GENERATED markers —
2026-08-18 19:00:37 +08:00
* into both language sides of the pair , localizing paired document paths for
* the Chinese side while retaining every other byte — and re - records a pair ' s
* ` .i18n.yaml ` only when nothing outside the region changed . The
2026-07-30 21:40:58 +08:00
* projection enforces event modes , JSDoc parameter / return completeness , and
* signature type - link coverage ; the inherited ( vendor ) tier renders to
* ` docs/cordis-api/inherited.md ` . ` --check ` verifies every generated artifact .
2026-08-12 23:51:31 +08:00
*
* Generated regions embed ` file:line ` source pointers , so inserting lines ABOVE a
* recorded symbol makes the committed output stale even though nothing about the
* symbol changed . Regenerate after editing any file this projection records — the
* failure otherwise surfaces as the " reproduces every committed catalog artifact
* byte for byte " test failing , which reads like a snapshot regression rather than
* a missing regeneration .
2026-06-20 19:47:09 +08:00
* /
2026-07-28 23:48:35 +08:00
import { mkdirSync , readFileSync , writeFileSync } from 'node:fs'
import { dirname , resolve } from 'node:path'
import {
projectCordisCatalog ,
2026-07-30 21:40:58 +08:00
renderInheritedPage ,
renderPageRegion ,
REGION_BEGIN ,
REGION_END ,
2026-07-28 23:48:35 +08:00
} from '@deepseek-ai/dsh-typert-generator'
import type { CordisCatalogPolicy } from '@deepseek-ai/dsh-typert-generator'
2026-07-20 16:32:08 +08:00
import { renderCordisCoreApiPages } from './cordis-core-api.ts'
2026-08-09 02:39:12 +08:00
import { contextKeyMap , contextMergeFiles , eventNameList } from './cordis-walk.ts'
2026-07-30 21:40:58 +08:00
import {
blobHash ,
parsePairMeta ,
2026-08-19 02:26:57 +08:00
parseTranslationPairingManifest ,
2026-07-30 21:40:58 +08:00
partitionGeneratedRegions ,
renderPairMeta ,
2026-08-19 02:26:57 +08:00
translationPairSourcePredicate ,
2026-07-30 21:40:58 +08:00
} from './translation-pairing.ts'
2026-08-18 19:00:37 +08:00
import { rewriteTranslationLinkLocales } from './translation-links.ts'
2026-06-20 19:47:09 +08:00
const root = resolve ( import . meta . dirname , '..' )
2026-07-30 21:40:58 +08:00
const SUBSYSTEMS_DIR = 'docs/subsystems'
const OUT_INHERITED = 'docs/cordis-api/inherited.md'
2026-08-13 00:36:22 +08:00
const OUT_RUNTIME_API = 'packages/extensions/tool-cordis/src/api-catalog.ts'
2026-06-20 19:47:09 +08:00
2026-07-30 21:40:58 +08:00
export { REGION_BEGIN , REGION_END }
/ * *
* The owning subsystems page for every harness ` ctx.<key> ` service the
* projection discovers . Fail - closed both ways : a discovered key absent here
* and an entry whose key the projection no longer discovers are both hard
2026-07-24 19:54:25 +08:00
* errors , so the partition can never silently drift from the service API .
2026-07-30 21:40:58 +08:00
* /
export const SERVICE_PAGE : Record < string , string > = {
agentLoop : 'core.md' ,
2026-08-09 12:13:58 +08:00
agentDefaultModel : 'core.md' ,
2026-08-09 02:50:45 +08:00
agentPresets : 'core.md' ,
2026-07-30 21:40:58 +08:00
agents : 'core.md' ,
2026-08-12 23:51:31 +08:00
apiProxy : 'typert.md' ,
2026-07-30 21:40:58 +08:00
approval : 'approval.md' ,
2026-08-09 23:33:35 +08:00
attachments : 'attachment.md' ,
2026-08-13 00:36:22 +08:00
shell : 'shell.md' ,
shellEnv : 'shell.md' ,
clientModules : 'client-modules.md' ,
2026-07-30 21:40:58 +08:00
codeRuntime : 'code-runtime.md' ,
commands : 'commands.md' ,
2026-08-13 00:36:22 +08:00
compaction : 'compaction.md' ,
2026-08-13 01:59:32 +08:00
cordisInspect : 'extensions.md' ,
2026-08-13 15:33:29 +08:00
authorization : 'credentials.md' ,
2026-07-30 21:40:58 +08:00
credentials : 'credentials.md' ,
directoryPicker : 'workspace.md' ,
2026-08-22 20:03:23 +08:00
deepseekLlmApiExtensions : 'llm-streaming.md' ,
2026-08-13 01:59:32 +08:00
dynamicCordisRunner : 'extensions.md' ,
2026-07-30 21:40:58 +08:00
e2b : 'subprocess.md' ,
2026-08-14 16:18:40 +08:00
fileReferences : 'session-reference.md' ,
2026-07-30 21:40:58 +08:00
fs : 'filesystem.md' ,
goals : 'goal.md' ,
2026-08-13 00:36:22 +08:00
webServer : 'web-server.md' ,
2026-07-30 21:40:58 +08:00
invariants : 'invariants.md' ,
llm : 'llm-streaming.md' ,
2026-08-12 23:51:31 +08:00
lsp : 'lsp.md' ,
2026-08-10 11:28:38 -07:00
messageFeedback : 'feedback.md' ,
2026-08-13 00:36:22 +08:00
permissionPresets : 'permission-presets.md' ,
2026-07-30 21:40:58 +08:00
planMode : 'plan.md' ,
2026-08-13 00:36:22 +08:00
terminals : 'terminal.md' ,
2026-07-30 21:40:58 +08:00
sandbox : 'sandbox.md' ,
sandboxPolicy : 'sandbox.md' ,
sessionPersistence : 'persistence.md' ,
sessionQuery : 'session-query.md' ,
2026-08-13 00:36:22 +08:00
sessionReferenceResolver : 'session-reference.md' ,
2026-07-30 21:40:58 +08:00
sessionProjectionCache : 'session-projection.md' ,
sessionProjections : 'session-projection.md' ,
2026-08-22 21:13:53 +08:00
sessionController : 'session.md' ,
2026-07-30 21:40:58 +08:00
sessions : 'session.md' ,
settings : 'settings.md' ,
sessionTitle : 'session-title.md' ,
skills : 'skills.md' ,
spillStore : 'spill.md' ,
storage : 'storage.md' ,
storageDomain : 'storage.md' ,
2026-08-24 18:23:42 +08:00
subagentModelSelection : 'subagent.md' ,
2026-07-30 21:40:58 +08:00
subagents : 'subagent.md' ,
subprocess : 'subprocess.md' ,
systemPrompt : 'system-prompt.md' ,
2026-08-13 00:36:22 +08:00
jobs : 'jobs.md' ,
sessionTelemetry : 'session-telemetry.md' ,
2026-08-19 22:28:18 +08:00
agentTeams : 'agent-team.md' ,
2026-07-30 21:40:58 +08:00
tokenMeter : 'token-meter.md' ,
2026-08-13 00:36:22 +08:00
toolResultPruner : 'compaction.md' ,
2026-07-30 21:40:58 +08:00
tools : 'tools.md' ,
typert : 'typert.md' ,
typertGateway : 'typert.md' ,
2026-08-13 00:36:22 +08:00
userQuestions : 'user-questions.md' ,
2026-07-30 21:40:58 +08:00
web : 'web.md' ,
2026-08-13 00:36:22 +08:00
workflowEngine : 'workflow.md' ,
2026-08-22 23:44:56 +08:00
webhookRuntime : 'webhook.md' ,
2026-08-13 00:36:22 +08:00
workspaceRegistry : 'workspace.md' ,
2026-08-23 06:14:32 +08:00
workspaceController : 'workspace.md' ,
2026-07-30 21:40:58 +08:00
}
/ * *
* Context keys declared in ` interface Context ` merges that the rendering
* projection cannot see , each with the reason and its documentation owner .
build(vendor): rescope the vendored Cordis packages into @deepseek-ai
Machine-produced by `pnpm run rescope-vendor --apply` plus the regeneration it
prints: `pnpm install` for the lockfile, `pnpm run gen-third-party-notices`,
`verify-translation-pairing --write` for the touched bilingual pairs,
`gen-doc-graphs`, and one typert snapshot whose ids embed character offsets.
`pnpm run rescope-vendor --check` verifies the result.
Renames nine vendored packages (cordis, cosmokit, schemastery and the six
@cordisjs plugins) and every reference that resolves them: manifest names and
dependency keys, module specifiers including declare-module merges, cordis.yml
plugin names, tsconfig paths, every Markdown fence, and `docs/` prose.
Directory names, upstream versions, and dependency ranges are unchanged, so
vendor/README.md still reads as an upstream snapshot; its manifest table gains
an upstream-name column so THIRD_PARTY_NOTICES keeps MIT attribution pointed
at each fork's origin.
The tutorial tier follows the rename end to end: its yaml fences named plugins
the Loader can no longer resolve, its `ts ignore-check` fences disagreed with
the compiled fences beside them, and its prose quoted both. The contracts that
told readers to keep upstream names — the root convention and the vendoring
cookbook's tree comment and manifest invariant — now say to rescope instead.
Two rules read `@deepseek-ai/` as "another workspace plugin": the client bundle
purity gate now names the vendored libraries a browser bundle inlines, and the
files where a bare `cordis` is an agent-preset id keep that product data.
2026-08-10 22:04:06 +08:00
* The scan that enforces this list reads EVERY ` declare module '@deepseek-ai/cordis' `
2026-08-09 02:39:12 +08:00
* Context merge under ` packages/x/x/src/** ` — any depth , not only root
* ` index.ts ` files with a same - named service class — so a new service can
* never silently join this blind spot : it either enters { @link SERVICE_PAGE }
* or names itself here . Client - face keys ( the projection analyzes the host
2026-08-12 23:51:31 +08:00
* face only ) name the package README that owns their surface .
*
* Two categories remain , and neither is a projection gap a scanning rule could
* close . An OPTIONAL key ( ` key?: X ` ) is a value the launcher or boot code
* installs before the tree mounts , which the analyzer skips by rule because no
* plugin provides it and ` inject ` cannot reach it . A client - face key belongs to
* the browser Context , which this host - face program never sees ; the browser
* surface has its own generated catalog ( ` scripts/gen-client-catalog.ts ` , served
* to a model as ` cordis_runtime_inspect what:"client" ` ) .
2026-07-30 21:40:58 +08:00
* /
export const SERVICE_WALK_EXEMPTIONS : Record < string , string > = {
agent : 'not a service: the DX accessor field on Agent.ctx (root accessor defaulting to undefined) — docs/subsystems/core.md owns the Agent handle' ,
feat(profiles): add the SDK application bundle
Introduce @deepseek-ai/dsh-sdk-app as the thin application layer for the built-in sdk profile. The bundle contributes the JSON-RPC server and startup-only profile metadata, while dsh-base continues to own the shared agent, provider, persistence, and tool composition.
Publish ctx.appReady from the launcher only after the Loader tree and launcher-owned setup succeed. The stdio lifetime binding leaves stdin unread until the protocol transport claims it and defers EOF exit 0 until readiness commits, so early protocol frames remain buffered and a racing startup failure remains the nonzero process outcome. Fiber disposal cancels both pending lifecycle listeners.
Register the bundle in the CLI resolver closure, generated configuration catalog, workspace graph, and built-bin smoke. Startup tests prove that base plus sdk-app exposes the SDK server without taking ownership of shared runtime plugins; focused and built-bin regressions cover early input, EOF readiness, and startup-error precedence.
2026-08-23 01:43:36 +08:00
appReady : 'not a service: launcher-provided successful-startup signal — packages/boot/cmdline/README.md owns the launcher contract' ,
2026-08-09 18:23:25 +08:00
appExit : 'not a service: launcher-provided bounded process-exit callback — packages/boot/cmdline/README.md owns the launcher contract' ,
cmdlineArgs : 'not a service: launcher-provided immutable app argument accessor — packages/boot/cmdline/README.md owns the launcher contract' ,
2026-08-09 15:27:21 +08:00
configuredAgentIdentities : 'not a service: launcher-provided boot-context value (ConfiguredAgentIdentities | undefined) — packages/core/agent-loop/README.md owns this launcher contract' ,
launcherSessionQueryPath : 'not a service: launcher-provided boot-context value (string | undefined) — packages/session-query/session-query-sqlite/README.md owns this launcher contract' ,
2026-07-30 21:40:58 +08:00
dshHomePath : 'not a service: boot-provided root accessor function (typeof dshHomePath | undefined) for Loader !!js config expressions — packages/boot/app-boot/README.md owns the boot contract' ,
2026-08-13 00:36:22 +08:00
launchEnvironment : 'not a service: launcher-provided root accessor value (LaunchEnvironmentSnapshot | undefined) — packages/util/launch-environment/README.md owns this launcher contract' ,
2026-08-13 01:59:32 +08:00
connection : 'interface-typed (HostConnectionHandle); implementing class HostConnectionService is declared in rpc-host.ts — packages/client/connection/README.md owns the API' ,
2026-08-17 14:21:34 +08:00
uiRenderer : 'client-side interface-typed browser service — packages/client/ui-renderer/README.md owns the API' ,
2026-08-23 13:15:20 +08:00
uiSession : 'client-side Session source adapter — packages/client/ui-session/README.md owns the API' ,
2026-08-22 21:20:23 +08:00
uiConversation : 'client-side Conversation registries and assembler — packages/client/ui-conversation/README.md owns the API' ,
2026-08-23 13:15:20 +08:00
uiWorkspace : 'client-side Workspace navigation adapter — packages/client/ui-workspace/README.md owns the API' ,
2026-08-17 13:39:18 +08:00
settingsSchema : 'client-side schema introspection service — packages/client/ui-settings/README.md owns the API' ,
docs: state the Host-face rule for the browser e2e and settle the follow-ups
apps/web/tests/README.md records why these e2e type-check in the Host aggregate
and why importing a Client package there pulls its project tree into the Host
build graph, with mirroring as the standing answer. The Agent Note drops the
directory-picker face split (assessed and declined) and the grep-level gate in
favour of that README.
docs: regenerate the catalogs and retarget the moved declarations
The forwarded-event change moved three owner packages' cordis `Events`
declarations and their branded types into client-safe `./types` modules, and
the settings-scope split moves the shell spec into ui-settings-general. Point
the type-equivalence manifest and the affected Agent Note at those homes,
register the new `remote/*` event scope and the `ctx.settingsScope` service in
the catalog partition, and re-run the generators.
`$on` joins the documented `TypeRTClientRemote` surface, and the two Agent Note
fences that quote a bare member signature are marked `ignore-check`: they are
declaration fragments, not compilable units.
refactor(client): make ui-settings the settings domain's base layer
The settings-namespace transport lived in client/runtime, where every feature
could value-import it because runtime is a platform module. It belongs to the
settings domain, but moving it into ui-settings as a shared function fails
twice: the client bundle purity gate forbids cross-plugin value imports, and
ui-settings reached ui-sidebar for its shell, so any feature depending on it
closed a cycle through ui-layout and ui-theme.
Both halves move. `ctx.settingsScope` is now a cordis service — the
collaboration shape the purity gate prescribes, and the service proxy binds
`this.ctx` to the caller, so a bound scope's disposer belongs to the calling
fiber. The shell ui-settings used to own (the `sidebar.settings` occupant, its
navigation, and the nav-row projection) moves to ui-settings-general, which
already owns the chrome and the General section. What stays in ui-settings is
what carries no `ui-*` dependency: the scope service and the canonical settings
slot types, `settings.general.item` included. That type was parked in the locale
package precisely because the declarer was unreachable without a cycle; every
registrant now depends on this base layer, so it comes home.
The scope CONTRACT stays in client/runtime: a feature service accepts a scope
through its own signature without depending on the surface that binds it.
The forwarded settings invalidation replaces the deleted client-side
`settings/changed` event, so the transport reads `ctx.remote.$on`. It reaches
`$on` through the gateway's Client half plus the allowlist's type-only subpath
rather than api-remotes' Client face: that face imports a Host-tsdown-generated
artifact, and this package is reachable from the Host build graph through its
callers.
refactor(client): reach the settings transport through ctx.settingsScope
Every feature that owns a preference row switches from value-importing a shared
binder to the settings domain's service, and declares the two injections that
binding needs: `settingsScope` for the transport and `remote` for the forwarded
invalidation it subscribes to on the caller's own context.
The rows stay with the features that own the preferences — Language with locale,
Appearance with ui-theme, Composer Enter with ui-conversation. Only their route
to the transport changes, so no settings surface moves and no feature gains a
dependency on the shell.
The `settings.general.item` slot type now arrives from ui-settings, the base
layer every registrant already depends on, which retires the re-export outlet
ui-theme kept and the parked declaration in the locale package.
client/runtime drops its settings-form and schemastery dependencies with the
transport that used them.
test(client): bind the settings transport in the specs that boot a preference row
Every bench that activates a plugin owning a preference row now supplies the two
services that plugin injects: the forwarded-event port and the scope service.
Specs that exercise no settings path get the minimal doubles; the ones that do
drive their refresh chains through `remote/host-event`, the same signal
client/runtime republishes from a forwarded frame, replacing the deleted
client-side `settings/changed` event.
Also fixes a publication defect the built-invariant gate catches once it runs:
api-remotes' invariant companion shared the allowlist module with the package
index, so rolldown hoisted it into a third chunk beside the two bundled entries
— a file the mechanically derived publication list does not carry, leaving an
installed companion unable to import it. The companion now reads the allowlist
through this package's own published `./types` subpath, which the bundle keeps
external, so each entry stays self-contained.
The dynamic-subscription cast in apiproxy is gone: after the vendored cordis
rescope, `on` accepts the rest-parameter handler directly, and the allowlist's
shape assertion still carries the safety argument.
fix(client): carry the settings-scope move across the release manifests
Rebasing onto the publishable release set replaced every manifest's dependency
block, so the packages this change touches restate their additions in the
workspace-protocol form: the base layer's own transport dependencies, and the
`ui-settings` plus `remote` edges each preference-row owner now needs.
ui-settings-general takes clsx with the shell it received, and client/runtime
drops the settings-form and schemastery dependencies that left with the
transport.
fix(api-gateway): give each $on subscription its own registration and containment
Two defects in the forwarded-event subscription table, both raised in review:
A set keyed on listener identity stored one entry when two callers subscribed the
same function object to the same event, so the first frame reached it once instead
of twice and either disposer silenced the surviving registration. Subscriptions are
now records addressed by registration, which is what "the disposer belongs to the
calling fiber" requires.
A listener declared void may still be `async`, and the synchronous `try/catch`
could not see its rejection: the promise was dropped and surfaced as an unhandled
rejection outside the documented containment. Delivery now attaches a rejection
handler when a listener returns a promise, so both failure modes are logged and
isolated alike.
Delivery also iterates a snapshot, so a listener that subscribes or disposes during
a frame no longer changes who receives that frame, and production matches the
TestRemote double instead of relying on live Set iteration order.
Both fixes are pinned by tests that fail against the previous implementation. The
double gains its own spec for the `$mount` refusal and the unsubscribed-name drop —
per-file coverage reaches it — plus a note that it propagates a throwing listener
where production contains one, so no spec mistakes it for the containment guarantee.
Three prose corrections: `assertJsonArgs` states where its throw actually surfaces
(the emitter's listener containment, not load or emit time), the browser e2e README
names every standing Client import rather than claiming one exception, and two
comments and a test title state the forwarded event instead of the deleted
client-side one.
refactor(remote): deliver forwarded frames through ctx.remote.$dispatch
The carrier used to relay each decoded frame over an internal
`remote/host-event` cordis event so the delivery port could stay off the Remote
contract. The relay was the wrong shape twice over: it put a client-face event
into a scan whose subject is the Host vocabulary, forcing a walk exemption for
something that is not a Host event at all, and it made a direct handoff between
two Client plugins look like a broadcast any plugin participates in.
`TypeRTClientRemote` now carries both roles of one surface — consumers subscribe
with `$on`, and whoever owns the Host frame sink hands frames over with
`$dispatch` — so client/runtime calls the Remote service directly and the event
declaration is gone. A cordis service method is the collaboration shape the
client bundle purity gate prescribes, and it needs no relay to satisfy it.
The trade is that the handoff is now developer-visible: any plugin holding
`ctx.remote` can synthesize a forwarded event. That is the exposure the relay
already had — `ctx.emit` was equally reachable — stated in the contract instead
of hidden behind a private subscriber.
runtime reaches `ctx.remote` through the gateway's Client face rather than
api-remotes': that face imports a Host-tsdown-generated artifact, and this
project sits in the Host build graph.
refactor(api-remotes): keep the allowlist value out of types.ts
`src/types.ts` carries only types by package convention, but it held the
forwarded-event array, so the type-only subpath published runtime code. The
array moves to `src/remote-events.ts` and `types.ts` derives its projection from
it; both compiler faces list both files, so the Host forwarding loop and the
consumer key face still read one declaration and the package's exports are
unchanged.
The invariant companion returns to an empty installer. Its dispatch-shape check
was the only reason the companion imported the allowlist, which made the two
bundled entries share a module: rolldown hoisted it into a third chunk that the
mechanically derived publication list does not carry, so an installed companion
could not import it. Dropping the check retires that coupling along with the
subpath-import and bundle-external workarounds it needed, and the shape the
check enforced at runtime is the part the Host face's `TypeRTForwardableEvent`
assertion already refuses at compile time.
test(ui-task): bind the locale plugin's new injections in its bench
The bench boots the real locale plugin, which now injects the settings-scope
service and the forwarded-event port, so it stayed pending and left `ctx.locale`
undefined. Supplies both doubles like the other benches that boot a plugin
owning a preference row.
docs: close the documentation gates for the forwarded-event surface
Regenerates the two graph catalogs and re-records every bilingual pair this
branch edited. Several pairs needed real work beyond the record:
- The generators write only the English side, so the Chinese sides of
`event-producer-consumer` and `module-graph` had drifted: the former still
listed the three deleted client-face events and pointed at declaration sites
this branch moved into `types.ts` modules, and the latter carried a stale
dependency graph.
- `TypeRTClientRemote`'s documented declaration gains `$dispatch` on both sides.
- The pairing contract requires both sides to link the same target, so the
apiproxy README and the design note now link the English note from both
languages, and the note's code blocks are byte-identical across the pair
(a translated comment inside a fence counts as divergence).
- `apps/web/tests/README.md` gains its Chinese counterpart; the browser e2e lane
documents a discipline reviewers apply, so it belongs in the bilingual corpus
rather than in the pairing exemption list.
- Four fences in the design note are marked `ignore-check`: each quotes a member
signature, a union arm, or a snippet that names symbols it does not import, so
none is a compilable unit.
docs(agent-note): transition the forwarded-event note to implemented
The design shipped in this PR, so the pair moves into `implemented/` and takes
that folder's skeleton: `## Proposal` becomes a present-tense `## Decision`,
and `## Acceptance criteria` plus `## Risks` fold into `## Verification` (what
pins the behavior) and `## Consequences` (what the shipped shape costs).
Facts that moved after the proposal are corrected rather than preserved: the
allowlist value now lives in `remote-events.ts` beside a type-only `types.ts`,
the delivery port is `$dispatch` rather than an internal cordis event, and the
invariant companion is an explained empty installer. `Verification` states the
two `$on` defects the review found — independent registration identity and
async-rejection containment — since those are now the properties tests pin.
Supersession is partial, so five active notes stay active and gain a
cross-link each: `web-config-plane`, `web-client-session-scope`,
`config-plane-boundaries`, `versioned-gui-welcome-onboarding`, and
`permission-default-for-new-sessions` each described a frame this change
replaced. Only the mechanism sentence is annotated; every conclusion those
notes own is untouched, and `host/models-changed` remains apiproxy's own
derived frame in all of them.
Also pins the disposer's idempotence: calling one `$on` disposer twice must not
splice a surviving twin registration out from under its owner.
fix: docs
fix: test
2026-08-10 22:06:25 +08:00
settingsScope : 'client-side settings-namespace transport service — packages/client/ui-settings/README.md owns the API' ,
2026-08-22 21:20:23 +08:00
chatFileMentions : 'client-side slot-contract accessor (ChatFileMentions) — packages/client/ui-chat/README.md owns the API' ,
2026-08-13 00:36:22 +08:00
commandUi : 'client-side interface-typed browser service — packages/client/ui-commands/README.md owns the API' ,
2026-07-24 19:54:25 +08:00
conversation : 'client-side interface-typed browser service — packages/client/ui-conversation/README.md owns the API' ,
layout : 'client-side interface-typed browser service — packages/client/ui-layout/README.md owns the API' ,
locale : 'client-side interface-typed browser service — packages/client/locale/README.md owns the API' ,
2026-08-13 00:36:22 +08:00
modelDirectories : 'client-side interface-typed browser service — packages/client/ui-model-selection/README.md owns the API' ,
2026-07-24 19:54:25 +08:00
modules : 'client-side interface-typed browser service — packages/client/modules/README.md owns the API' ,
remote : 'client-side interface-typed gateway accessor (ClientRemote) — packages/api/gateway/README.md owns the API' ,
2026-08-13 05:02:00 +08:00
sessionLogDownload : 'client-side browser download controller — packages/session-query/session-log-export/README.md owns the API' ,
2026-08-13 00:36:22 +08:00
inputTriggers : 'client-side interface-typed browser service — packages/client/ui-input-trigger/README.md owns the API' ,
2026-08-13 00:19:15 +08:00
timer : 'client-side dynamic-package timer service — packages/extensions/cordis-client-runner/README.md owns the API' ,
2026-08-22 21:20:23 +08:00
slots : 'client-side interface-typed browser service — packages/client/ui-renderer/README.md owns the API' ,
2026-07-24 19:54:25 +08:00
theme : 'client-side interface-typed browser service — packages/client/ui-theme/README.md owns the API' ,
2026-08-22 21:20:23 +08:00
workspaces : 'client-side interface-typed browser service — packages/api/workspace-controller/README.md owns the API' ,
2026-07-30 21:40:58 +08:00
}
/ * *
* The owning subsystems page for every harness event scope ( the segment
2026-08-09 02:39:12 +08:00
* before the first ` / ` ) the projection renders . Fail - closed exactly like
* { @link SERVICE_PAGE } . Client - face events ( ` slash/* ` , ` theme/change ` , … ) are
* invisible to the host - face projection and therefore never reach this map ;
* { @link EVENT_WALK_EXEMPTIONS } names each one with its documentation owner .
2026-07-30 21:40:58 +08:00
* /
export const EVENT_SCOPE_PAGE : Record < string , string > = {
'agent' : 'core.md' ,
'agent-loop' : 'core.md' ,
2026-08-11 17:12:41 +08:00
'agent-preset' : 'core.md' ,
2026-08-22 21:13:53 +08:00
'api-session' : 'session.md' ,
2026-07-30 21:40:58 +08:00
'approval' : 'approval.md' ,
'commands' : 'commands.md' ,
2026-08-13 01:59:32 +08:00
'cordis' : 'extensions.md' ,
2026-08-13 15:33:29 +08:00
'authorization' : 'credentials.md' ,
2026-07-30 21:40:58 +08:00
'credentials' : 'credentials.md' ,
'domain' : 'storage.md' ,
'fs' : 'filesystem.md' ,
'goal' : 'goal.md' ,
'llm' : 'llm-streaming.md' ,
'session' : 'session.md' ,
'settings' : 'settings.md' ,
'skills' : 'skills.md' ,
'subagent' : 'subagent.md' ,
'system-prompt' : 'system-prompt.md' ,
2026-08-13 00:36:22 +08:00
'session-telemetry' : 'session-telemetry.md' ,
2026-07-30 21:40:58 +08:00
'tools' : 'tools.md' ,
2026-08-23 06:14:32 +08:00
'user-questions' : 'user-questions.md' ,
2026-08-19 12:16:00 +08:00
'webserver' : 'web-server.md' ,
2026-07-30 21:40:58 +08:00
'workflow' : 'workflow.md' ,
}
2026-06-20 19:47:09 +08:00
2026-08-09 02:39:12 +08:00
/ * *
* Event names declared in ` interface Events ` merges that the rendering
* projection cannot see , each with the reason and its documentation owner .
* The mirror of { @link SERVICE_WALK_EXEMPTIONS } for events : an independent
build(vendor): rescope the vendored Cordis packages into @deepseek-ai
Machine-produced by `pnpm run rescope-vendor --apply` plus the regeneration it
prints: `pnpm install` for the lockfile, `pnpm run gen-third-party-notices`,
`verify-translation-pairing --write` for the touched bilingual pairs,
`gen-doc-graphs`, and one typert snapshot whose ids embed character offsets.
`pnpm run rescope-vendor --check` verifies the result.
Renames nine vendored packages (cordis, cosmokit, schemastery and the six
@cordisjs plugins) and every reference that resolves them: manifest names and
dependency keys, module specifiers including declare-module merges, cordis.yml
plugin names, tsconfig paths, every Markdown fence, and `docs/` prose.
Directory names, upstream versions, and dependency ranges are unchanged, so
vendor/README.md still reads as an upstream snapshot; its manifest table gains
an upstream-name column so THIRD_PARTY_NOTICES keeps MIT attribution pointed
at each fork's origin.
The tutorial tier follows the rename end to end: its yaml fences named plugins
the Loader can no longer resolve, its `ts ignore-check` fences disagreed with
the compiled fences beside them, and its prose quoted both. The contracts that
told readers to keep upstream names — the root convention and the vendoring
cookbook's tree comment and manifest invariant — now say to rescope instead.
Two rules read `@deepseek-ai/` as "another workspace plugin": the client bundle
purity gate now names the vendored libraries a browser bundle inlines, and the
files where a bare `cordis` is an agent-preset id keep that product data.
2026-08-10 22:04:06 +08:00
* scan reads EVERY ` declare module '@deepseek-ai/cordis' ` Events merge under
2026-08-09 02:39:12 +08:00
* ` packages/x/x/src/** ` , so a declared event either renders onto a subsystems
* page ( via { @link EVENT_SCOPE_PAGE } ) or names itself here — never vanishes
2026-08-11 16:50:03 +08:00
* silently . Keys are full event names rather than scopes , so a scope - level
* exemption cannot mask another declaration in that scope .
2026-08-09 02:39:12 +08:00
* /
export const EVENT_WALK_EXEMPTIONS : Record < string , string > = {
2026-08-13 00:36:22 +08:00
'command/executed' : 'client-face local command acknowledgment — packages/client/ui-commands/README.md owns the API' ,
2026-08-22 21:20:23 +08:00
'connection/reset' : 'client-face transport signal — packages/api/session-controller/README.md owns the API' ,
2026-07-24 19:54:25 +08:00
'locale/change' : 'client-face locale switch signal — packages/client/locale/README.md owns the API' ,
2026-08-13 00:36:22 +08:00
'slash/input-begin-command' : 'client-face slash-input protocol — packages/client/ui-input-trigger/README.md owns the API' ,
'slash/input-consume-token' : 'client-face slash-input protocol — packages/client/ui-input-trigger/README.md owns the API' ,
'slash/input-insert-reference' : 'client-face slash-input protocol — packages/client/ui-input-trigger/README.md owns the API' ,
'slash/input-insert-text' : 'client-face slash-input protocol — packages/client/ui-input-trigger/README.md owns the API' ,
2026-08-22 21:20:23 +08:00
'slots/changed' : 'client-face slot invalidation signal — packages/client/ui-renderer/README.md owns the API' ,
2026-07-24 19:54:25 +08:00
'theme/change' : 'client-face theme switch signal — packages/client/ui-theme/README.md owns the API' ,
2026-08-09 02:39:12 +08:00
}
2026-07-30 21:40:58 +08:00
/ * *
* One primary subsystems page per project type used by a generated
* signature . This stays curated because union names intentionally do not
* reuse the type - equivalence manifest ' s map - symbol entries and some symbols
* appear on more than one page .
* /
2026-07-28 23:48:35 +08:00
export const LINK_MAP : Readonly < Record < string , string > > = {
2026-06-20 19:47:09 +08:00
Agent : 'core.md' ,
2026-07-21 12:48:46 +08:00
AgentCancelCause : 'core.md' ,
docs(subsystems): open core.md on agent creation/ownership and the Agent contract; enforce a complete folder index
core.md claimed to be the packages/core reference but opened on repo-wide type patterns and never documented the ownership vocabulary: AgentHandle, CreateAgentOptions, ResumeAgentOptions, and AgentFactory were TYPE_LINK_EXEMPTIONS pointing at a package README, invisible to the folder that calls itself the type reference. The page now reads spine map -> creation and ownership (AgentHandle pasted; the options and factory summarized with links into the generated registry section) -> the Agent handle (AgentStatus, AgentOptions, SteeringOutcome, SteeringReceipt, and SettleReason now pasted; the one settlement prose wall split by topic; delivery vocabulary ordered as a message travels) -> initiator -> interception -> a Sessions summary -> the ToolDefinition pointer -> an explicitly framed repo-wide patterns tail (the ...Map pattern, branded ids). The duplicate SessionEvent paste is gone -- session.md owns it and LINK_MAP follows -- the four ownership types moved from TYPE_LINK_EXEMPTIONS into LINK_MAP -> core.md, and three dead LINK_MAP entries (ContinuationDecision, ContinuationStop, HookContext) no longer name types absent from the source tree. The "what this page owns" meta-section folds into the intro.
The subsystems README index silently lost tasks.md and session-reference.md on both language sides during a base absorption; the rows are restored and scripts/project-doc-site.spec.ts now fails when any page misses either side of the index (proven red on a removed row). tools.md links ToolSchema to its llm-streaming.md declaration instead of calling it core; subagent.md links AgentHandle and CreateAgentOptions.seed to the new section. A new Agent Note records the package-anchored page-scoping decision; the 2026-06-20 catalog note marks its spine-vs-seam rule superseded as the page-scoping rule while keeping the type-equiv mechanism current, and docs/AGENTS.md cites the new note.
2026-08-03 16:34:00 +08:00
AgentFactory : 'core.md' ,
AgentHandle : 'core.md' ,
2026-08-09 12:13:58 +08:00
ModelSelection : 'core.md' ,
2026-07-19 14:57:52 +08:00
AgentOptions : 'core.md' ,
AgentStatus : 'core.md' ,
docs: anchor each subsystem page to its package group; make group READMEs thin tables
core.md read as a type grab-bag: LLM wire vocabulary up front, the agent/loop story buried, and no correspondence to packages/core. It now opens on the packages/core control spine — the package-by-package loop map with a Page column into session/system-prompt/tools/scope — and keeps only what the spine group declares plus the repo-wide patterns: the Agent handle with its delivery/cancellation/interception contracts, the SessionEvent envelope, branded ids, the …Map pattern. The conversation vocabulary (Message/ContentBlock, the model request, adapters — 17 type-equiv blocks) moves to llm-streaming.md, which now declares packages/llm end-to-end; the duplicate ContentBlockMap paste near its seam section folds into the moved section, and the manifest, LINK_MAP, README table rows, website label (Core data structures → Core), and inbound anchors follow.
Every packages/<group>/README pair is now a thin front door in one shape: a why-first intro (bash's seam-pattern-first paragraph rewritten as 'shell execution for the agent'), the package table, and a closing pointer to the owning docs/subsystems page — the bash-style table stays the load-bearing middle. Load-bearing trailing paragraphs relocate rather than vanish: the fs no-timeout rationale becomes a filesystem.md section (both languages), session's four sectioned tables merge into one 12-row table, examples' legacy-bin H2 collapses to a pointer at jsonrpc-demo's README, and design rationale that already lives in an Agent Note or subsystem page is now linked instead of restated. All 40 pair records re-recorded.
2026-08-02 05:54:15 +08:00
ContentBlock : 'llm-streaming.md' ,
docs(subsystems): open core.md on agent creation/ownership and the Agent contract; enforce a complete folder index
core.md claimed to be the packages/core reference but opened on repo-wide type patterns and never documented the ownership vocabulary: AgentHandle, CreateAgentOptions, ResumeAgentOptions, and AgentFactory were TYPE_LINK_EXEMPTIONS pointing at a package README, invisible to the folder that calls itself the type reference. The page now reads spine map -> creation and ownership (AgentHandle pasted; the options and factory summarized with links into the generated registry section) -> the Agent handle (AgentStatus, AgentOptions, SteeringOutcome, SteeringReceipt, and SettleReason now pasted; the one settlement prose wall split by topic; delivery vocabulary ordered as a message travels) -> initiator -> interception -> a Sessions summary -> the ToolDefinition pointer -> an explicitly framed repo-wide patterns tail (the ...Map pattern, branded ids). The duplicate SessionEvent paste is gone -- session.md owns it and LINK_MAP follows -- the four ownership types moved from TYPE_LINK_EXEMPTIONS into LINK_MAP -> core.md, and three dead LINK_MAP entries (ContinuationDecision, ContinuationStop, HookContext) no longer name types absent from the source tree. The "what this page owns" meta-section folds into the intro.
The subsystems README index silently lost tasks.md and session-reference.md on both language sides during a base absorption; the rows are restored and scripts/project-doc-site.spec.ts now fails when any page misses either side of the index (proven red on a removed row). tools.md links ToolSchema to its llm-streaming.md declaration instead of calling it core; subagent.md links AgentHandle and CreateAgentOptions.seed to the new section. A new Agent Note records the package-anchored page-scoping decision; the 2026-06-20 catalog note marks its spine-vs-seam rule superseded as the page-scoping rule while keeping the type-equiv mechanism current, and docs/AGENTS.md cites the new note.
2026-08-03 16:34:00 +08:00
CreateAgentOptions : 'core.md' ,
docs: anchor each subsystem page to its package group; make group READMEs thin tables
core.md read as a type grab-bag: LLM wire vocabulary up front, the agent/loop story buried, and no correspondence to packages/core. It now opens on the packages/core control spine — the package-by-package loop map with a Page column into session/system-prompt/tools/scope — and keeps only what the spine group declares plus the repo-wide patterns: the Agent handle with its delivery/cancellation/interception contracts, the SessionEvent envelope, branded ids, the …Map pattern. The conversation vocabulary (Message/ContentBlock, the model request, adapters — 17 type-equiv blocks) moves to llm-streaming.md, which now declares packages/llm end-to-end; the duplicate ContentBlockMap paste near its seam section folds into the moved section, and the manifest, LINK_MAP, README table rows, website label (Core data structures → Core), and inbound anchors follow.
Every packages/<group>/README pair is now a thin front door in one shape: a why-first intro (bash's seam-pattern-first paragraph rewritten as 'shell execution for the agent'), the package table, and a closing pointer to the owning docs/subsystems page — the bash-style table stays the load-bearing middle. Load-bearing trailing paragraphs relocate rather than vanish: the fs no-timeout rationale becomes a filesystem.md section (both languages), session's four sectioned tables merge into one 12-row table, examples' legacy-bin H2 collapses to a pointer at jsonrpc-demo's README, and design rationale that already lives in an Agent Note or subsystem page is now linked instead of restated. All 40 pair records re-recorded.
2026-08-02 05:54:15 +08:00
GenerateOptions : 'llm-streaming.md' ,
2026-07-28 14:11:18 +08:00
InboxItem : 'core.md' ,
2026-07-27 22:48:20 +08:00
InboxPlacement : 'core.md' ,
docs: anchor each subsystem page to its package group; make group READMEs thin tables
core.md read as a type grab-bag: LLM wire vocabulary up front, the agent/loop story buried, and no correspondence to packages/core. It now opens on the packages/core control spine — the package-by-package loop map with a Page column into session/system-prompt/tools/scope — and keeps only what the spine group declares plus the repo-wide patterns: the Agent handle with its delivery/cancellation/interception contracts, the SessionEvent envelope, branded ids, the …Map pattern. The conversation vocabulary (Message/ContentBlock, the model request, adapters — 17 type-equiv blocks) moves to llm-streaming.md, which now declares packages/llm end-to-end; the duplicate ContentBlockMap paste near its seam section folds into the moved section, and the manifest, LINK_MAP, README table rows, website label (Core data structures → Core), and inbound anchors follow.
Every packages/<group>/README pair is now a thin front door in one shape: a why-first intro (bash's seam-pattern-first paragraph rewritten as 'shell execution for the agent'), the package table, and a closing pointer to the owning docs/subsystems page — the bash-style table stays the load-bearing middle. Load-bearing trailing paragraphs relocate rather than vanish: the fs no-timeout rationale becomes a filesystem.md section (both languages), session's four sectioned tables merge into one 12-row table, examples' legacy-bin H2 collapses to a pointer at jsonrpc-demo's README, and design rationale that already lives in an Agent Note or subsystem page is now linked instead of restated. All 40 pair records re-recorded.
2026-08-02 05:54:15 +08:00
MessageId : 'llm-streaming.md' ,
docs(subsystems): open core.md on agent creation/ownership and the Agent contract; enforce a complete folder index
core.md claimed to be the packages/core reference but opened on repo-wide type patterns and never documented the ownership vocabulary: AgentHandle, CreateAgentOptions, ResumeAgentOptions, and AgentFactory were TYPE_LINK_EXEMPTIONS pointing at a package README, invisible to the folder that calls itself the type reference. The page now reads spine map -> creation and ownership (AgentHandle pasted; the options and factory summarized with links into the generated registry section) -> the Agent handle (AgentStatus, AgentOptions, SteeringOutcome, SteeringReceipt, and SettleReason now pasted; the one settlement prose wall split by topic; delivery vocabulary ordered as a message travels) -> initiator -> interception -> a Sessions summary -> the ToolDefinition pointer -> an explicitly framed repo-wide patterns tail (the ...Map pattern, branded ids). The duplicate SessionEvent paste is gone -- session.md owns it and LINK_MAP follows -- the four ownership types moved from TYPE_LINK_EXEMPTIONS into LINK_MAP -> core.md, and three dead LINK_MAP entries (ContinuationDecision, ContinuationStop, HookContext) no longer name types absent from the source tree. The "what this page owns" meta-section folds into the intro.
The subsystems README index silently lost tasks.md and session-reference.md on both language sides during a base absorption; the rows are restored and scripts/project-doc-site.spec.ts now fails when any page misses either side of the index (proven red on a removed row). tools.md links ToolSchema to its llm-streaming.md declaration instead of calling it core; subagent.md links AgentHandle and CreateAgentOptions.seed to the new section. A new Agent Note records the package-anchored page-scoping decision; the 2026-06-20 catalog note marks its spine-vs-seam rule superseded as the page-scoping rule while keeping the type-equiv mechanism current, and docs/AGENTS.md cites the new note.
2026-08-03 16:34:00 +08:00
ResumeAgentOptions : 'core.md' ,
2026-07-27 17:38:42 +08:00
SettleReason : 'core.md' ,
docs: anchor each subsystem page to its package group; make group READMEs thin tables
core.md read as a type grab-bag: LLM wire vocabulary up front, the agent/loop story buried, and no correspondence to packages/core. It now opens on the packages/core control spine — the package-by-package loop map with a Page column into session/system-prompt/tools/scope — and keeps only what the spine group declares plus the repo-wide patterns: the Agent handle with its delivery/cancellation/interception contracts, the SessionEvent envelope, branded ids, the …Map pattern. The conversation vocabulary (Message/ContentBlock, the model request, adapters — 17 type-equiv blocks) moves to llm-streaming.md, which now declares packages/llm end-to-end; the duplicate ContentBlockMap paste near its seam section folds into the moved section, and the manifest, LINK_MAP, README table rows, website label (Core data structures → Core), and inbound anchors follow.
Every packages/<group>/README pair is now a thin front door in one shape: a why-first intro (bash's seam-pattern-first paragraph rewritten as 'shell execution for the agent'), the package table, and a closing pointer to the owning docs/subsystems page — the bash-style table stays the load-bearing middle. Load-bearing trailing paragraphs relocate rather than vanish: the fs no-timeout rationale becomes a filesystem.md section (both languages), session's four sectioned tables merge into one 12-row table, examples' legacy-bin H2 collapses to a pointer at jsonrpc-demo's README, and design rationale that already lives in an Agent Note or subsystem page is now linked instead of restated. All 40 pair records re-recorded.
2026-08-02 05:54:15 +08:00
AdapterRegistrationHandle : 'llm-streaming.md' ,
DirectoryRegistrationHandle : 'llm-streaming.md' ,
2026-08-22 20:03:23 +08:00
DeepSeekLlmApiExtensionMap : 'llm-streaming.md' ,
DeepSeekLlmApiExtensionProvider : 'llm-streaming.md' ,
DeepSeekLlmApiExtensionRequest : 'llm-streaming.md' ,
docs: anchor each subsystem page to its package group; make group READMEs thin tables
core.md read as a type grab-bag: LLM wire vocabulary up front, the agent/loop story buried, and no correspondence to packages/core. It now opens on the packages/core control spine — the package-by-package loop map with a Page column into session/system-prompt/tools/scope — and keeps only what the spine group declares plus the repo-wide patterns: the Agent handle with its delivery/cancellation/interception contracts, the SessionEvent envelope, branded ids, the …Map pattern. The conversation vocabulary (Message/ContentBlock, the model request, adapters — 17 type-equiv blocks) moves to llm-streaming.md, which now declares packages/llm end-to-end; the duplicate ContentBlockMap paste near its seam section folds into the moved section, and the manifest, LINK_MAP, README table rows, website label (Core data structures → Core), and inbound anchors follow.
Every packages/<group>/README pair is now a thin front door in one shape: a why-first intro (bash's seam-pattern-first paragraph rewritten as 'shell execution for the agent'), the package table, and a closing pointer to the owning docs/subsystems page — the bash-style table stays the load-bearing middle. Load-bearing trailing paragraphs relocate rather than vanish: the fs no-timeout rationale becomes a filesystem.md section (both languages), session's four sectioned tables merge into one 12-row table, examples' legacy-bin H2 collapses to a pointer at jsonrpc-demo's README, and design rationale that already lives in an Agent Note or subsystem page is now linked instead of restated. All 40 pair records re-recorded.
2026-08-02 05:54:15 +08:00
LlmCallConfig : 'llm-streaming.md' ,
LlmModelContext : 'llm-streaming.md' ,
LlmModelReasoningInfo : 'llm-streaming.md' ,
LlmResolvedModelInfo : 'llm-streaming.md' ,
2026-07-20 03:34:19 +08:00
LlmFailure : 'llm-streaming.md' ,
2026-08-24 19:15:30 +08:00
LlmImageRequestPricing : 'llm-streaming.md' ,
docs: anchor each subsystem page to its package group; make group READMEs thin tables
core.md read as a type grab-bag: LLM wire vocabulary up front, the agent/loop story buried, and no correspondence to packages/core. It now opens on the packages/core control spine — the package-by-package loop map with a Page column into session/system-prompt/tools/scope — and keeps only what the spine group declares plus the repo-wide patterns: the Agent handle with its delivery/cancellation/interception contracts, the SessionEvent envelope, branded ids, the …Map pattern. The conversation vocabulary (Message/ContentBlock, the model request, adapters — 17 type-equiv blocks) moves to llm-streaming.md, which now declares packages/llm end-to-end; the duplicate ContentBlockMap paste near its seam section folds into the moved section, and the manifest, LINK_MAP, README table rows, website label (Core data structures → Core), and inbound anchors follow.
Every packages/<group>/README pair is now a thin front door in one shape: a why-first intro (bash's seam-pattern-first paragraph rewritten as 'shell execution for the agent'), the package table, and a closing pointer to the owning docs/subsystems page — the bash-style table stays the load-bearing middle. Load-bearing trailing paragraphs relocate rather than vanish: the fs no-timeout rationale becomes a filesystem.md section (both languages), session's four sectioned tables merge into one 12-row table, examples' legacy-bin H2 collapses to a pointer at jsonrpc-demo's README, and design rationale that already lives in an Agent Note or subsystem page is now linked instead of restated. All 40 pair records re-recorded.
2026-08-02 05:54:15 +08:00
LlmModelInfo : 'llm-streaming.md' ,
LlmProviderInfo : 'llm-streaming.md' ,
LlmConfigurableProvider : 'llm-streaming.md' ,
LlmModelDiscoveryRequest : 'llm-streaming.md' ,
LlmDiscoveredModel : 'llm-streaming.md' ,
2026-07-25 10:18:16 +08:00
ResolvedRetryPolicy : 'llm-streaming.md' ,
docs: anchor each subsystem page to its package group; make group READMEs thin tables
core.md read as a type grab-bag: LLM wire vocabulary up front, the agent/loop story buried, and no correspondence to packages/core. It now opens on the packages/core control spine — the package-by-package loop map with a Page column into session/system-prompt/tools/scope — and keeps only what the spine group declares plus the repo-wide patterns: the Agent handle with its delivery/cancellation/interception contracts, the SessionEvent envelope, branded ids, the …Map pattern. The conversation vocabulary (Message/ContentBlock, the model request, adapters — 17 type-equiv blocks) moves to llm-streaming.md, which now declares packages/llm end-to-end; the duplicate ContentBlockMap paste near its seam section folds into the moved section, and the manifest, LINK_MAP, README table rows, website label (Core data structures → Core), and inbound anchors follow.
Every packages/<group>/README pair is now a thin front door in one shape: a why-first intro (bash's seam-pattern-first paragraph rewritten as 'shell execution for the agent'), the package table, and a closing pointer to the owning docs/subsystems page — the bash-style table stays the load-bearing middle. Load-bearing trailing paragraphs relocate rather than vanish: the fs no-timeout rationale becomes a filesystem.md section (both languages), session's four sectioned tables merge into one 12-row table, examples' legacy-bin H2 collapses to a pointer at jsonrpc-demo's README, and design rationale that already lives in an Agent Note or subsystem page is now linked instead of restated. All 40 pair records re-recorded.
2026-08-02 05:54:15 +08:00
Message : 'llm-streaming.md' ,
MessageSource : 'llm-streaming.md' ,
2026-08-10 11:28:38 -07:00
MessageFeedbackDeleteRequest : 'feedback.md' ,
MessageFeedbackDeleteResult : 'feedback.md' ,
MessageFeedbackDeleteValue : 'feedback.md' ,
MessageFeedbackFailure : 'feedback.md' ,
MessageFeedbackItem : 'feedback.md' ,
MessageFeedbackListRequest : 'feedback.md' ,
MessageFeedbackListResult : 'feedback.md' ,
MessageFeedbackListValue : 'feedback.md' ,
MessageFeedbackNoteBlank : 'feedback.md' ,
MessageFeedbackNoteTooLarge : 'feedback.md' ,
MessageFeedbackPutRequest : 'feedback.md' ,
MessageFeedbackPutResult : 'feedback.md' ,
MessageFeedbackRating : 'feedback.md' ,
MessageFeedbackRejected : 'feedback.md' ,
MessageFeedbackSessionNotFound : 'feedback.md' ,
MessageFeedbackSuccess : 'feedback.md' ,
MessageFeedbackTargetNotFound : 'feedback.md' ,
MessageFeedbackVersion : 'feedback.md' ,
MessageFeedbackVersionConflict : 'feedback.md' ,
2026-07-28 13:55:59 +08:00
UserMessage : 'session.md' ,
2026-08-22 21:13:53 +08:00
ApiSessionAgentResult : 'session.md' ,
2026-07-31 19:21:16 +08:00
PreStepDecision : 'core.md' ,
PreStepContext : 'core.md' ,
2026-07-28 23:48:35 +08:00
RequestErrorAction : 'core.md' ,
2026-07-30 13:49:57 +08:00
RequestFailureContext : 'core.md' ,
2026-07-21 16:46:48 +08:00
PreparedReferencedMessage : 'session-reference.md' ,
2026-08-14 16:18:40 +08:00
FileReferenceCandidate : 'session-reference.md' ,
2026-07-21 16:46:48 +08:00
SessionReferenceCandidate : 'session-reference.md' ,
refactor(reference): serve discovery through typert Remote faces
Replace the legacy reference.* API Proxy domain with @Remote methods on the
owning services, following the typert gateway design master adopted on
2026-08-02 (message-feedback and plugin-inventory precedents):
- FileReferenceService and SessionReferenceResolver extend TypertRemoteService;
fileReferences/list and sessionReferenceResolver/candidates are unary Remote
methods cancelled through the reserved trailing signal, and the candidates
face attaches each candidate's canonical mention under the configured limit
- move the wire types to type-only ./types subpaths (FileReferenceCandidate,
SessionReferenceMentionCandidate) and export ./typert plus ./remote artifacts
- mount both contributions in the api-remotes client assembly; ui-reference
consumes ctx.remote instead of connection.api.references and registers zh/en
locale dictionaries for its sections and labels
- delete the reference.* routes, schemas, map rows, client stubs, and fixtures;
the connection fixture serves the Remote endpoints instead
- release deliverPrompt admission listeners when the agent is disposed with the
prepared prompt still pending, and cover the reference-* RpcError codes in
the schema spec
- add the missing tsconfig paths for the /grammar and /types subpaths (clean-
tree vitest could not resolve @deepseek-ai/dsh-file-reference/grammar)
- regenerate the cordis catalog, capability seams, and event matrix; update the
owning bilingual READMEs, Agent Notes, and the reference-composer golden
2026-08-17 18:35:04 +08:00
SessionReferenceMentionCandidate : 'session-reference.md' ,
2026-07-21 16:46:48 +08:00
SessionReferenceInput : 'session-reference.md' ,
2026-08-22 21:13:53 +08:00
SessionAttachmentRequest : 'session.md' ,
SessionAttachmentValue : 'session.md' ,
SessionCancelRequest : 'session.md' ,
SessionCancelValue : 'session.md' ,
SessionControlFrame : 'session.md' ,
SessionCreateRequest : 'session.md' ,
SessionCreateValue : 'session.md' ,
docs(subsystems): open core.md on agent creation/ownership and the Agent contract; enforce a complete folder index
core.md claimed to be the packages/core reference but opened on repo-wide type patterns and never documented the ownership vocabulary: AgentHandle, CreateAgentOptions, ResumeAgentOptions, and AgentFactory were TYPE_LINK_EXEMPTIONS pointing at a package README, invisible to the folder that calls itself the type reference. The page now reads spine map -> creation and ownership (AgentHandle pasted; the options and factory summarized with links into the generated registry section) -> the Agent handle (AgentStatus, AgentOptions, SteeringOutcome, SteeringReceipt, and SettleReason now pasted; the one settlement prose wall split by topic; delivery vocabulary ordered as a message travels) -> initiator -> interception -> a Sessions summary -> the ToolDefinition pointer -> an explicitly framed repo-wide patterns tail (the ...Map pattern, branded ids). The duplicate SessionEvent paste is gone -- session.md owns it and LINK_MAP follows -- the four ownership types moved from TYPE_LINK_EXEMPTIONS into LINK_MAP -> core.md, and three dead LINK_MAP entries (ContinuationDecision, ContinuationStop, HookContext) no longer name types absent from the source tree. The "what this page owns" meta-section folds into the intro.
The subsystems README index silently lost tasks.md and session-reference.md on both language sides during a base absorption; the rows are restored and scripts/project-doc-site.spec.ts now fails when any page misses either side of the index (proven red on a removed row). tools.md links ToolSchema to its llm-streaming.md declaration instead of calling it core; subagent.md links AgentHandle and CreateAgentOptions.seed to the new section. A new Agent Note records the package-anchored page-scoping decision; the 2026-06-20 catalog note marks its spine-vs-seam rule superseded as the page-scoping rule while keeping the type-equiv mechanism current, and docs/AGENTS.md cites the new note.
2026-08-03 16:34:00 +08:00
SessionEvent : 'session.md' ,
2026-08-22 21:13:53 +08:00
SessionFollowFrame : 'session.md' ,
SessionFollowRequest : 'session.md' ,
SessionForkRequest : 'session.md' ,
SessionForkValue : 'session.md' ,
2026-07-19 14:57:52 +08:00
SessionId : 'core.md' ,
2026-08-22 21:13:53 +08:00
SessionListRequest : 'session.md' ,
SessionListValue : 'session.md' ,
SessionModels : 'session.md' ,
SessionModelsRequest : 'session.md' ,
SessionPage : 'session.md' ,
SessionPageRequest : 'session.md' ,
SessionPromptRequest : 'session.md' ,
SessionPromptValue : 'session.md' ,
SessionRenameRequest : 'session.md' ,
SessionRenameValue : 'session.md' ,
SessionRespondReceipt : 'session.md' ,
SessionRespondRequest : 'session.md' ,
SessionSearchValue : 'session.md' ,
SessionSelectModelRequest : 'session.md' ,
SessionSelectModelValue : 'session.md' ,
SessionSummary : 'session.md' ,
SessionUpdateQueueRequest : 'session.md' ,
SessionUpdateQueueValue : 'session.md' ,
2026-07-12 08:57:05 +08:00
SessionStartSource : 'core.md' ,
2026-07-24 12:31:26 +08:00
SessionLogSnapshot : 'session-query.md' ,
2026-07-21 16:46:48 +08:00
SessionSurfaceSnapshot : 'session-query.md' ,
2026-07-11 21:37:38 +08:00
ApprovalOutcome : 'approval.md' ,
ApprovalPolicy : 'approval.md' ,
ApprovalRequest : 'approval.md' ,
2026-08-23 06:14:32 +08:00
ApprovalRequestEvent : 'approval.md' ,
2026-07-19 14:57:52 +08:00
ApprovalService : 'approval.md' ,
2026-08-23 06:14:32 +08:00
AskUserQuestionRequestEvent : 'user-questions.md' ,
feat(commands): route composer image attachments through slash commands
A claimed slash command consumed only the text half of the composer
submission: /goal with reference images executed, cleared the draft, and
silently stranded the images in the rail. Model-visible attachment intent
had no route through the command plane.
The submission envelope is now modeled end to end. CommandDefinition
input.images declares acceptance; the declaration rides the descriptor to
every client, onto the minted CommandClaim, and into the input machine's
claim snapshot. commands.execute carries the submission's base64 images
and enforces the declaration in the executor: non-declaring commands, a
missing attachment store, and exceeded batch limits settle as logged
error results before the handler runs. Admission reuses the attachment
package's new admitEncodedImages, extracted from api-proxy's prompt path
so both wire endpoints share one limits/validation/commit sequence.
Producers own model visibility: /goal submits one user followup (image
blocks + a fixed reference line) after a successful create/edit so goal
rounds read the images from session history; /plan folds them into its
steered message. Grammar misfits (/goal pause, bare /plan, /plan off)
return direct errors and the composer keeps the images.
On the client, enter adjudication carries a SubmitEnvelope and every
command route that cannot consume images throws a localized refusal that
renders as one composer notice with draft and images retained; the
claimed pre-gate applies the same copy. An accepting claim serializes the
draft images, forwards them to commands.execute, and clears plus releases
them only on a success outcome.
The assembled web test roster gains the ui-input-trigger and ui-commands
plugins, mirroring the shipped composition, so slash submissions exercise
the command plane; a new keyless snapshot pins the refusal banner and the
accepting /goal flow over the built client graph.
2026-08-17 18:57:55 +08:00
EncodedImageAttachment : 'attachment.md' ,
2026-08-24 14:51:03 +08:00
ImageAttachmentAccess : 'llm-streaming.md' ,
2026-07-23 15:20:47 +08:00
ImageAttachmentRef : 'attachment.md' ,
2026-08-20 18:19:23 +08:00
ImageRequestPolicy : 'attachment.md' ,
RequestImageAttachment : 'attachment.md' ,
2026-07-23 15:20:47 +08:00
SaveImageAttachment : 'attachment.md' ,
StoredImageAttachment : 'attachment.md' ,
2026-08-13 00:36:22 +08:00
ShellExecRequest : 'shell.md' ,
ShellExecSpec : 'shell.md' ,
ShellProcess : 'shell.md' ,
ShellRunResult : 'shell.md' ,
refactor(subprocess): rename the process seam to subprocess and address review
Review feedback (tianyicui): 'process' is a poor service name. The family is
now packages/subprocess/ — @deepseek-ai/dsh-subprocess (ctx.subprocess,
abstract SubprocessService, Subprocess* vocabulary) and
@deepseek-ai/dsh-subprocess-local (LocalSubprocessService) — renamed
throughout code, compositions, docs (en+zh, pairs re-recorded), catalogs,
and gates. 'subprocess' is the precise term for managed OS children (the
Python-stdlib sense), avoids colliding with Node's global process object,
and reads as one system beside dsh-subagent-subprocess.
ds-review-bot findings addressed:
- kill() on a settled handle is now a no-op (no signal to a possibly-reused
pgid, no referenced grace timer delaying exit); pinned by a spy test.
- The moved DshEnvironmentKey/DshEnvironment/CollectedOutput types get
drift-checked type-equiv blocks on the new subprocess.md page, restoring
their manifest registration.
- subprocess.md is registered in the core.md sub-page index (en+zh).
2026-07-26 12:43:14 +08:00
DshEnvironment : 'subprocess.md' ,
SubprocessHandle : 'subprocess.md' ,
SubprocessOutcome : 'subprocess.md' ,
SubprocessOutputRead : 'subprocess.md' ,
SubprocessOutputReader : 'subprocess.md' ,
SubprocessSpawnSpec : 'subprocess.md' ,
2026-07-28 23:00:00 +08:00
SubprocessTerminalHandle : 'subprocess.md' ,
SubprocessTerminalSpawnSpec : 'subprocess.md' ,
2026-07-08 02:38:47 +08:00
CodeRunRequest : 'code-runtime.md' ,
CodeRunResult : 'code-runtime.md' ,
2026-07-19 14:57:52 +08:00
CompactionResult : 'compaction.md' ,
2026-07-19 16:41:51 +08:00
CompactionTrigger : 'compaction.md' ,
2026-07-19 17:54:55 +08:00
PruneResult : 'compaction.md' ,
2026-07-19 14:57:52 +08:00
FileReadOutcome : 'filesystem.md' ,
FsDirEntry : 'filesystem.md' ,
2026-06-22 14:53:36 +08:00
FsEditOutcome : 'filesystem.md' ,
FsEditRequest : 'filesystem.md' ,
2026-06-26 18:14:30 +08:00
FsInfo : 'filesystem.md' ,
2026-08-09 15:22:50 +08:00
FsObservation : 'filesystem.md' ,
2026-07-19 14:57:52 +08:00
FsPathInfo : 'filesystem.md' ,
2026-08-13 00:36:22 +08:00
FsObservationActor : 'filesystem.md' ,
2026-06-22 14:53:36 +08:00
FsTarget : 'filesystem.md' ,
FsVersion : 'filesystem.md' ,
fix(fs): address review — rename to dsh-fs-policy, fs/*-intent events, RFC currency, ENOTDIR
Rename per review naming decisions:
- package dsh-file-context → dsh-fs-policy (dir, package name, plugin name,
tsconfig refs, importers, type-equiv manifest, generated catalog + module-graph)
- events fs/write-expectation → fs/write-intent, fs/edit-expectation → fs/edit-intent
(fs/observed unchanged); type FsWriteExpectation → FsWriteIntent, "expectation"
wording → "intent" throughout
- exported FileContextExec → FsPolicyExec
Make the implemented RFCs describe what shipped, not the superseded designs:
the 2026-06-17 capability-seam + tool-schemas RFCs no longer place policy on
ctx.fs or use full/partial-view authorization, and the fsspec RFC's ctx.fileContext
service prose is rewritten to the fs/* event-gate reality (freshness-based auth).
Sharpen docs/rfc/implemented/AGENTS.md: a rename is a fact to fix IN PLACE — the
"new RFC" escape hatch is for macro decision reversals only, not renames.
Code fixes from review:
- fsio.ts resolveLocalTarget/probe translate ENOTDIR (a parent path segment is a
file) into the structured FsError taxonomy instead of leaking a raw Node error;
resolve reports FS_NOT_FOUND, probe reports absent. Regression tests proven to
fail on the unfixed code.
- tool-fs HMR test now asserts prompt sections (not just tool schemas) are
withdrawn on disposal.
- fs/observed is a plain (unguarded) ctx.emit: correct the fs-policy comment,
filesystem.md, and tool-fs module doc that wrongly claimed the tool "contains"
a throwing listener; a throw surfaces as the tool's isError result.
- drop the false "loaded by the default product config" claim (no config wires
the fs tools yet), the duplicate ctx.bash service-map row, the stale
FileReadRequest catalog link-map entry, and the fs/fs README EOF blank line;
correct the dsh-fs package.json description.
2026-07-02 03:12:38 +08:00
FsWriteIntent : 'filesystem.md' ,
2026-06-22 14:53:36 +08:00
FsWriteOutcome : 'filesystem.md' ,
2026-07-19 18:47:34 +08:00
CreateGoalRequest : 'goal.md' ,
EditGoalRequest : 'goal.md' ,
2026-07-20 16:39:40 +08:00
GoalBlockReason : 'goal.md' ,
2026-07-19 18:47:34 +08:00
GoalChanged : 'goal.md' ,
GoalRef : 'goal.md' ,
GoalView : 'goal.md' ,
2026-07-30 21:40:58 +08:00
CreateGoalResult : 'goal.md' ,
2026-07-19 22:11:59 +08:00
CommandDefinition : 'commands.md' ,
CommandDescriptor : 'commands.md' ,
2026-08-09 15:48:56 +08:00
CommandId : 'commands.md' ,
2026-07-19 22:11:59 +08:00
CommandResult : 'commands.md' ,
2026-08-12 23:51:31 +08:00
CommandSurface : 'commands.md' ,
LspProvider : 'lsp.md' ,
LspQueryRequest : 'lsp.md' ,
LspQueryResult : 'lsp.md' ,
2026-07-19 14:57:52 +08:00
LlmAdapter : 'llm-streaming.md' ,
2026-07-25 22:59:45 +08:00
PreparedLlmCall : 'llm-streaming.md' ,
2026-08-22 20:03:23 +08:00
PreparedDeepSeekLlmApiExtensions : 'llm-streaming.md' ,
2026-08-13 00:36:22 +08:00
LlmRuntime : 'llm-streaming.md' ,
2026-07-19 14:57:52 +08:00
StreamChunk : 'llm-streaming.md' ,
2026-07-29 02:06:07 +08:00
SkillProviderControl : 'skills.md' ,
2026-07-19 14:57:52 +08:00
CreateSessionOptions : 'persistence.md' ,
2026-08-05 22:54:55 +08:00
PrepareSessionOptions : 'persistence.md' ,
2026-07-19 14:57:52 +08:00
SessionHeader : 'persistence.md' ,
2026-08-05 22:54:55 +08:00
SessionInspection : 'persistence.md' ,
2026-08-25 06:10:25 +08:00
BorrowedSessionSource : 'persistence.md' ,
2026-07-19 14:57:52 +08:00
SessionLocation : 'persistence.md' ,
2026-08-05 22:54:55 +08:00
SessionPreparation : 'persistence.md' ,
2026-07-23 13:56:56 +08:00
SessionPersistenceSnapshot : 'persistence.md' ,
2026-08-10 17:47:17 +08:00
SessionRawArtifact : 'persistence.md' ,
2026-07-19 14:57:52 +08:00
ConfinedArgv : 'sandbox.md' ,
2026-07-21 00:44:28 +08:00
SandboxExecutionPolicy : 'sandbox.md' ,
2026-07-19 14:57:52 +08:00
SandboxMode : 'sandbox.md' ,
SandboxPolicy : 'sandbox.md' ,
2026-08-13 00:36:22 +08:00
TerminalBackend : 'terminal.md' ,
TerminalReadRequest : 'terminal.md' ,
TerminalReadResult : 'terminal.md' ,
TerminalSendOperation : 'terminal.md' ,
TerminalSendRequest : 'terminal.md' ,
TerminalSessionId : 'terminal.md' ,
TerminalSessionSnapshot : 'terminal.md' ,
TerminalSignal : 'terminal.md' ,
TerminalSignalResult : 'terminal.md' ,
TerminalSpawnRequest : 'terminal.md' ,
TerminalSpawnResult : 'terminal.md' ,
2026-07-21 00:44:28 +08:00
SandboxPolicyRequest : 'sandbox.md' ,
2026-07-19 14:57:52 +08:00
ScopeKey : 'scope.md' ,
Scoped : 'scope.md' ,
EpochHeader : 'session.md' ,
Session : 'session.md' ,
2026-07-21 01:54:00 +08:00
SessionEventMap : 'session.md' ,
2026-07-19 14:57:52 +08:00
TurnEndReason : 'session.md' ,
2026-07-21 01:54:00 +08:00
TurnTrigger : 'session.md' ,
2026-07-19 14:57:52 +08:00
SessionEventReadRequest : 'session-query.md' ,
SessionEventRecord : 'session-query.md' ,
2026-07-23 13:56:56 +08:00
SessionEventResultFilter : 'session-query.md' ,
SessionEventSearchDocument : 'session-query.md' ,
SessionEventSearchHit : 'session-query.md' ,
2026-07-24 16:40:08 +08:00
SessionEventSearchPage : 'session-query.md' ,
2026-07-23 13:56:56 +08:00
SessionEventSearchRequest : 'session-query.md' ,
2026-07-19 14:57:52 +08:00
SessionEventTrace : 'session-query.md' ,
2026-07-24 16:40:08 +08:00
SessionEventTraceObservation : 'session-query.md' ,
2026-07-19 14:57:52 +08:00
SessionEventTraceRequest : 'session-query.md' ,
SessionEventWindow : 'session-query.md' ,
SessionLineageTrace : 'session-query.md' ,
2026-08-25 06:10:25 +08:00
SessionObservation : 'session-query.md' ,
SessionObservationOptions : 'session-query.md' ,
2026-07-19 14:57:52 +08:00
SessionRecord : 'session-query.md' ,
2026-07-23 13:56:56 +08:00
SessionResultFilter : 'session-query.md' ,
SessionSearchExecContext : 'session-query.md' ,
SessionSearchHit : 'session-query.md' ,
SessionSearchPage : 'session-query.md' ,
SessionSearchRequest : 'session-query.md' ,
2026-07-24 16:40:08 +08:00
SessionTitleObservation : 'session-query.md' ,
2026-07-24 18:13:11 +08:00
SessionTitleObservationResult : 'session-query.md' ,
2026-07-21 01:54:00 +08:00
SessionTitleProvider : 'session-title.md' ,
SessionTitleSnapshot : 'session-title.md' ,
2026-07-27 16:50:56 +08:00
SkillCatalogSnapshot : 'skills.md' ,
2026-07-19 14:57:52 +08:00
SkillDefinition : 'skills.md' ,
SkillLookupOptions : 'skills.md' ,
SkillProvider : 'skills.md' ,
2026-07-29 21:08:10 +08:00
SkillProviderObservation : 'skills.md' ,
2026-07-19 14:57:52 +08:00
SkillRegistration : 'skills.md' ,
2026-08-09 22:29:53 +08:00
SkillViewOptions : 'skills.md' ,
2026-07-19 14:57:52 +08:00
SkillSummary : 'skills.md' ,
SaveTextSpill : 'spill.md' ,
SpillRef : 'spill.md' ,
2026-07-30 13:41:59 +08:00
ContinuableCreateRequest : 'subagent.md' ,
ContinuableCreateSpec : 'subagent.md' ,
2026-07-31 22:45:21 +08:00
ContinuableSetupContribution : 'subagent.md' ,
feat(subagent): continuable background subagents
Implement the continuable background subagents RFC: a durable child
session with a series of Task-backed activations, each disposing its
run before the Task settles.
- dsh-subagent: rename SubagentRun.sendMessage to strict steer, drop
run-level resume, add SubagentProvider.resume dispatch via
SubagentService.resume, the continuation start field, and the
versioned model-hidden subagent/descriptor session event.
- dsh-subagent-inprocess/-spawn/-fork: publish the control-allocated
child id, append the descriptor inside the initial turn, implement
cold resume from the child's own transcript under the live parent
scope, and strict running-only steer.
- dsh-subagent-control (new): SubagentControlService owning stable
child ids, descriptor snapshot/fold/authorization, Task-backed
activation with settle-then-dispose ordering, the process-local
active-run association, and steer-or-resume sendMessage routing.
- dsh-tool-subagent: background route branches on the provider's
resume capability (continuable via the control service; one-shot
task for ACP), returning both child and task ids.
- dsh-tool-subagent-control (new): the globally named send_message
tool rendering steered/started routes.
Keyless coverage spans Task ownership and disposal ordering, running
delivery, cold follow-up, descriptor rejection and rollback, known-id
reconstruction, kill during lookup, admission races, and a new
subagent-continuable ACP snapshot scenario.
2026-07-23 17:07:38 +08:00
ContinuableStart : 'subagent.md' ,
ContinuableStartSpec : 'subagent.md' ,
2026-07-24 13:18:35 +08:00
CoordinatorMessageSource : 'subagent.md' ,
2026-08-06 13:46:55 +08:00
SubagentDescendantListEntry : 'subagent.md' ,
2026-07-28 00:10:24 +08:00
SubagentFollowupOptions : 'subagent.md' ,
2026-08-06 11:59:19 +08:00
SubagentInterruptAuthority : 'subagent.md' ,
2026-07-26 02:32:34 +08:00
SubagentListEntry : 'subagent.md' ,
2026-07-19 14:57:52 +08:00
SubagentProvider : 'subagent.md' ,
2026-07-31 22:45:21 +08:00
SubagentReportDelivery : 'subagent.md' ,
SubagentReportMessageSource : 'subagent.md' ,
SubagentReportOptions : 'subagent.md' ,
2026-07-19 14:57:52 +08:00
SubagentRun : 'subagent.md' ,
2026-08-13 00:36:22 +08:00
SubagentRuntime : 'subagent.md' ,
2026-07-19 14:57:52 +08:00
SubagentStartRequest : 'subagent.md' ,
AssembleContext : 'system-prompt.md' ,
2026-07-30 22:09:15 +08:00
PromptContext : 'system-prompt.md' ,
2026-07-19 14:57:52 +08:00
PromptSection : 'system-prompt.md' ,
SystemPrompt : 'system-prompt.md' ,
ToolProviderResult : 'system-prompt.md' ,
2026-08-13 00:36:22 +08:00
JobDoneListener : 'jobs.md' ,
JobId : 'jobs.md' ,
JobRead : 'jobs.md' ,
JobSnapshot : 'jobs.md' ,
JobStart : 'jobs.md' ,
JobsChangedListener : 'jobs.md' ,
2026-08-19 22:44:23 +08:00
CreateTeamTaskRequest : 'agent-team.md' ,
SendTeamMessageRequest : 'agent-team.md' ,
SendTeamMessageResult : 'agent-team.md' ,
SpawnTeammateRequest : 'agent-team.md' ,
SpawnTeammateResult : 'agent-team.md' ,
TeamId : 'agent-team.md' ,
TeamMemberView : 'agent-team.md' ,
TeamMembership : 'agent-team.md' ,
TeamTaskId : 'agent-team.md' ,
TeamTaskView : 'agent-team.md' ,
TeamWaitResult : 'agent-team.md' ,
UpdateTeamTaskRequest : 'agent-team.md' ,
2026-07-19 14:57:52 +08:00
TokenMeasurement : 'token-meter.md' ,
2026-07-26 09:01:03 +08:00
CodeDispatchLog : 'tools.md' ,
2026-07-19 14:57:52 +08:00
PostToolDecision : 'tools.md' ,
PreToolDecision : 'tools.md' ,
ToolDefinition : 'tools.md' ,
ToolExecution : 'tools.md' ,
2026-07-19 23:38:54 +08:00
ToolDispatchExecution : 'tools.md' ,
2026-07-19 14:57:52 +08:00
ToolExecutionInput : 'tools.md' ,
ToolExecutionMode : 'tools.md' ,
ToolExecutionResult : 'tools.md' ,
ToolExecutionToken : 'tools.md' ,
ToolGuard : 'tools.md' ,
feat(tools): let one agent choose its tool presentation, and ship `code`
Code Mode was a deployment-wide field on the host `tools` row: a
deployment ran every session that way or none. The obvious product
shape — 代码模式 beside 标准/极简/创造 in the preset picker — had
nothing to hang on.
The registry itself cannot move into a preset; the agent loop's
scheduler, the api-proxy's presenters, and every tool plugin are its
consumers. So split the registry from its projection: `presentAs(mode)`
writes one cell on the calling agent's scope layer, exactly as
`restrict()` does, and the three reads that decided presentation take
that scope's mode instead of the service's. The config `mode` becomes
the default agents shadow rather than a process-wide fact.
Two consequences are load-bearing. `run_code` now enters a view only
for scopes whose own mode presents it — a native agent must not find it
dispatchable because another agent in the process does — and the
reserved name holds whatever the configured mode, since any agent may
select a code mode later.
`dsh-agent-tool-mode` is the row a preset carries to declare this. A
code mode waits for the host's `codeRuntime` rather than assuming it,
so a runtime-less deployment fails the preset at mount, naming the
row, instead of at the session's first request.
The shipped `code` preset is `standard` plus that row, ordered second.
2026-08-05 20:31:52 +08:00
ToolPresentationMode : 'tools.md' ,
2026-08-13 00:36:22 +08:00
ToolRuntime : 'tools.md' ,
2026-07-19 14:57:52 +08:00
ToolRestriction : 'tools.md' ,
ToolSchema : 'tools.md' ,
fix(settings): harden seam and provider per review findings
Confirmed and fixed, each with a regression test that failed first:
- Concurrent update() lost patches (merge over one stale snapshot):
per-namespace serialized write queues; a failed write cannot poison
the queue for later writers.
- Fixed-name .tmp write followed planted symlinks and kept stale modes:
random-suffix sibling, exclusive-create (wx), 0600, cleanup on
failure, then rename.
- A throwing settings/updated listener escaped commit and permanently
wedged the provider reload chain (rejected refreshTask): commit now
contains listener failures (INVARIANT-coded errors still propagate),
async watcher rejections are adopted and contained
(watch callbacks are officially void | Promise<void>), and the
provider chains refreshes on a settled tail with an error log.
- No way to remove a user override: scope/service replace(section)
sets the user section wholesale; replace({}) re-inherits base and
schema defaults.
- The three-primitive provider contract did not hold (base never
called load()): the base Service.init loads and publishes once;
settings-local delegates via yield* super[Service.init]().
- Dispose did not quiesce: teardown flags closed, closes the watcher,
then awaits queued/in-flight reloads; closed is re-checked across
await points.
- Invariant now checks the authoritative relation with the seam's own
deepEqualJson: emitted next must equal settings.get(ns), and
next/prev must differ structurally (cosmokit dependency dropped).
- New docs/core-data-structures/settings.{md,zh.md} with type-equiv
blocks + manifest entries; catalog types moved from exemptions to
LINK_MAP; website page registered.
Both packages stay at per-file 100% coverage.
2026-07-28 18:18:34 +08:00
SettingsNamespace : 'settings.md' ,
SettingsRegisterOptions : 'settings.md' ,
SettingsScope : 'settings.md' ,
SettingsDescriptor : 'settings.md' ,
feat(settings): detect stale writers with a revision, and announce raw changes
The remaining P1 from the #939 review, plus the P2 it shares a mechanism with.
Nothing carried a version, so two tabs editing one namespace silently
overwrote each other — reproduced as tab B's `reasoning` lost to tab A's
older draft. The seam's per-namespace write queue orders writes; it cannot
tell a fresh writer from one replaying a snapshot a predecessor superseded.
Each namespace now carries a monotonic `revision` over its RAW section. A
write may send `expectedRevision`, checked at the FRONT of the queue (not at
call time, which would race the very predecessor it guards against); a
mismatch rejects with `SettingsConflictError` → `settings-conflict` on the
wire, carrying both revisions. The editor captures the revision it opened at
and, on conflict, asks the user to reopen rather than replaying its snapshot.
The same counter fixes the missing broadcast. `settings/updated` is gated on
the resolved value — correct for consumers, wrong for configuration surfaces:
storing an override equal to the composition base leaves the resolved value
alone while changing what the document says (the field is now overridden, not
inherited) and moving every open editor's revision. `settings/document-updated
(ns, revision)` fires on any raw-section change, in-process or external, and
`host/settings-changed` now rides it.
That event also closes the stale model picker: editing a provider's `models`
changes no route, so `llm/adapters-updated` never fired and an open picker
kept serving the old catalog. A change to an exposed provider namespace now
emits `host/models-changed` too — that namespace holds the catalog.
Docs: both sides of the five touched README pairs, a type-equiv block for
`SettingsPathOp`, and an Agent Note recording what the plane exposes and who
may overwrite what. The deferred wire-redaction gaps (secrets behind
union/intersection/transform, `.default(...)` in the served envelope, schema
text in rejection messages, `new Function` rehydration, pi-ai's `headers`) are
recorded as TODO(settings-wire-redaction) and in Known Limitations rather than
half-fixed.
2026-07-30 19:24:21 +08:00
SettingsPathOp : 'settings.md' ,
2026-07-30 10:53:39 +08:00
SettingsDescribeOptions : 'settings.md' ,
fix(settings): harden seam and provider per review findings
Confirmed and fixed, each with a regression test that failed first:
- Concurrent update() lost patches (merge over one stale snapshot):
per-namespace serialized write queues; a failed write cannot poison
the queue for later writers.
- Fixed-name .tmp write followed planted symlinks and kept stale modes:
random-suffix sibling, exclusive-create (wx), 0600, cleanup on
failure, then rename.
- A throwing settings/updated listener escaped commit and permanently
wedged the provider reload chain (rejected refreshTask): commit now
contains listener failures (INVARIANT-coded errors still propagate),
async watcher rejections are adopted and contained
(watch callbacks are officially void | Promise<void>), and the
provider chains refreshes on a settled tail with an error log.
- No way to remove a user override: scope/service replace(section)
sets the user section wholesale; replace({}) re-inherits base and
schema defaults.
- The three-primitive provider contract did not hold (base never
called load()): the base Service.init loads and publishes once;
settings-local delegates via yield* super[Service.init]().
- Dispose did not quiesce: teardown flags closed, closes the watcher,
then awaits queued/in-flight reloads; closed is re-checked across
await points.
- Invariant now checks the authoritative relation with the seam's own
deepEqualJson: emitted next must equal settings.get(ns), and
next/prev must differ structurally (cosmokit dependency dropped).
- New docs/core-data-structures/settings.{md,zh.md} with type-equiv
blocks + manifest entries; catalog types moved from exemptions to
LINK_MAP; website page registered.
Both packages stay at per-file 100% coverage.
2026-07-28 18:18:34 +08:00
SettingsUpdateSource : 'settings.md' ,
2026-08-13 15:33:29 +08:00
AuthorizationEntry : 'credentials.md' ,
AuthorizationFlow : 'credentials.md' ,
AuthorizationInteraction : 'credentials.md' ,
AuthorizationMethod : 'credentials.md' ,
AuthorizationNotice : 'credentials.md' ,
AuthorizationOutcome : 'credentials.md' ,
AuthorizationPrompt : 'credentials.md' ,
AuthorizationRequest : 'credentials.md' ,
AuthorizationSession : 'credentials.md' ,
AuthorizationSettlement : 'credentials.md' ,
AuthorizationStatus : 'credentials.md' ,
docs: bilingual credentials/settings-consumer documentation, catalogs, and gates
New credentials data-structure page (type-equiv manifested), group README,
rewritten llm-deepseek/llm-pi-ai READMEs (dynamic configuration, dict
profiles, credential chain), capability-seams/service-role registration,
Agent Note (bilingual), demo compositions mounting settings-local +
credentials-local with no inline key plumbing, installSettingsSection
consumer helper on the settings seam (deduplicating both adapters' wiring),
jscpd symmetry markers for the provider twins, runtime-closure additions for
python/sdk-runtime, and doc-budget ceilings AGENTS.md 1750→1755 /
packages/README.md 850→865 for the structural one-line group rows.
2026-07-29 14:20:06 +08:00
CredentialRef : 'credentials.md' ,
feat(credentials): store durable credential records beside references
The seam answered one question — what is behind this environment-variable
name — and that shape cannot hold what an authorization grant is: a
multi-field, rotating value keyed by a provider id rather than by a POSIX
identifier. The Models page already works around the gap by inventing a
synthetic environment name (`MINIMAX_CN_API_KEY`) for a route the user added
by hand, because the store's key must look like one.
`CredentialKey` is `<scope>/<id>`, where the scope is the owning plugin's
registered name. The owner is in the key because a `grant` payload is written
in its owner's format: two plugins serving the same provider name would
otherwise read each other's payload, and a record left by an uninstalled
plugin could not be told from a live one. The `/` also keeps the grammar
disjoint from `CredentialRef`, so the key spaces cannot collide.
`CredentialRecord` is `api-key` (key and/or provider environment values) or
`grant` (an opaque, owner-owned payload). The asymmetry is deliberate: an api
key is the harness's own data, a grant is a package it carries for someone
else. `modifyRecord` is the only write path because a correct write depends
on the current value — a token refresh is read-decide-replace under one
cross-process lock, without which two processes rotating one refresh token
lose whichever wrote first.
`.credentials.yaml` becomes a versioned two-section document. The pre-release
flat layout is refused by name, with the entry count and the one edit needed,
rather than read as an empty store — which would surface as an authentication
failure on the first request instead of at load. A grant payload is admitted
in both directions, so a value the document could not read back exactly as
written is refused rather than stored lossily.
2026-08-13 15:00:09 +08:00
CredentialKey : 'credentials.md' ,
docs: bilingual credentials/settings-consumer documentation, catalogs, and gates
New credentials data-structure page (type-equiv manifested), group README,
rewritten llm-deepseek/llm-pi-ai READMEs (dynamic configuration, dict
profiles, credential chain), capability-seams/service-role registration,
Agent Note (bilingual), demo compositions mounting settings-local +
credentials-local with no inline key plumbing, installSettingsSection
consumer helper on the settings seam (deduplicating both adapters' wiring),
jscpd symmetry markers for the provider twins, runtime-closure additions for
python/sdk-runtime, and doc-budget ceilings AGENTS.md 1750→1755 /
packages/README.md 850→865 for the structural one-line group rows.
2026-07-29 14:20:06 +08:00
CredentialInfo : 'credentials.md' ,
feat(credentials): store durable credential records beside references
The seam answered one question — what is behind this environment-variable
name — and that shape cannot hold what an authorization grant is: a
multi-field, rotating value keyed by a provider id rather than by a POSIX
identifier. The Models page already works around the gap by inventing a
synthetic environment name (`MINIMAX_CN_API_KEY`) for a route the user added
by hand, because the store's key must look like one.
`CredentialKey` is `<scope>/<id>`, where the scope is the owning plugin's
registered name. The owner is in the key because a `grant` payload is written
in its owner's format: two plugins serving the same provider name would
otherwise read each other's payload, and a record left by an uninstalled
plugin could not be told from a live one. The `/` also keeps the grammar
disjoint from `CredentialRef`, so the key spaces cannot collide.
`CredentialRecord` is `api-key` (key and/or provider environment values) or
`grant` (an opaque, owner-owned payload). The asymmetry is deliberate: an api
key is the harness's own data, a grant is a package it carries for someone
else. `modifyRecord` is the only write path because a correct write depends
on the current value — a token refresh is read-decide-replace under one
cross-process lock, without which two processes rotating one refresh token
lose whichever wrote first.
`.credentials.yaml` becomes a versioned two-section document. The pre-release
flat layout is refused by name, with the entry count and the one edit needed,
rather than read as an empty store — which would surface as an authentication
failure on the first request instead of at load. A grant payload is admitted
in both directions, so a value the document could not read back exactly as
written is refused rather than stored lossily.
2026-08-13 15:00:09 +08:00
CredentialRecord : 'credentials.md' ,
CredentialRecordEntry : 'credentials.md' ,
CredentialRecordInfo : 'credentials.md' ,
docs: bilingual credentials/settings-consumer documentation, catalogs, and gates
New credentials data-structure page (type-equiv manifested), group README,
rewritten llm-deepseek/llm-pi-ai READMEs (dynamic configuration, dict
profiles, credential chain), capability-seams/service-role registration,
Agent Note (bilingual), demo compositions mounting settings-local +
credentials-local with no inline key plumbing, installSettingsSection
consumer helper on the settings seam (deduplicating both adapters' wiring),
jscpd symmetry markers for the provider twins, runtime-closure additions for
python/sdk-runtime, and doc-budget ceilings AGENTS.md 1750→1755 /
packages/README.md 850→865 for the structural one-line group rows.
2026-07-29 14:20:06 +08:00
ResolvedCredential : 'credentials.md' ,
2026-08-13 00:36:22 +08:00
AskUserQuestionAnswer : 'user-questions.md' ,
AskUserQuestionRequest : 'user-questions.md' ,
UserQuestionProvider : 'user-questions.md' ,
2026-07-19 14:57:52 +08:00
WebFetchProvider : 'web.md' ,
WebFetchRequest : 'web.md' ,
WebFetchResult : 'web.md' ,
WebSearchProvider : 'web.md' ,
WebSearchRequest : 'web.md' ,
WebSearchResult : 'web.md' ,
WorkflowRun : 'workflow.md' ,
2026-08-22 23:44:56 +08:00
VerifiedWebhookDelivery : 'webhook.md' ,
WebhookRule : 'webhook.md' ,
2026-08-13 00:36:22 +08:00
PresetOption : 'permission-presets.md' ,
PresetSpec : 'permission-presets.md' ,
2026-07-27 22:48:20 +08:00
InvariantInstaller : 'invariants.md' ,
2026-08-13 00:36:22 +08:00
WebRoute : 'web-server.md' ,
2026-08-19 12:16:00 +08:00
IndexInjection : 'web-server.md' ,
2026-07-27 22:48:20 +08:00
StorageBackend : 'storage.md' ,
StorageForms : 'storage.md' ,
Domain : 'storage.md' ,
DomainSpec : 'storage.md' ,
DomainChanged : 'storage.md' ,
DomainFacility : 'storage.md' ,
Workspace : 'workspace.md' ,
2026-08-23 06:14:32 +08:00
WorkspaceArchiveSessionRequest : 'workspace.md' ,
WorkspaceArchiveValue : 'workspace.md' ,
WorkspaceCreateRequest : 'workspace.md' ,
WorkspaceCreateValue : 'workspace.md' ,
WorkspaceDeleteRequest : 'workspace.md' ,
WorkspaceDeleteValue : 'workspace.md' ,
WorkspaceFollowFrame : 'workspace.md' ,
2026-07-27 22:48:20 +08:00
WorkspaceId : 'workspace.md' ,
2026-08-23 06:14:32 +08:00
WorkspaceInsertBeforeRequest : 'workspace.md' ,
WorkspaceInsertSessionBeforeRequest : 'workspace.md' ,
WorkspaceOrderValue : 'workspace.md' ,
WorkspaceRenameRequest : 'workspace.md' ,
WorkspaceValue : 'workspace.md' ,
2026-08-24 18:15:14 +08:00
ClientArtifactBaseline : 'client-modules.md' ,
2026-07-27 22:48:20 +08:00
WebBootGraph : 'client-modules.md' ,
2026-08-13 00:36:22 +08:00
SessionTelemetryRecord : 'session-telemetry.md' ,
2026-07-19 14:57:52 +08:00
WorkflowRunInfo : 'workflow.md' ,
WorkflowStartRequest : 'workflow.md' ,
2026-07-30 21:40:58 +08:00
ProjectionDefinition : 'session-projection.md' ,
SessionProjectionMap : 'session-projection.md' ,
2026-08-19 13:09:02 +08:00
SessionProjectionStateMap : 'session-projection.md' ,
2026-07-30 21:40:58 +08:00
ProjectionChangeListener : 'session-projection.md' ,
ProjectionSnapshot : 'session-projection.md' ,
ProjectionCheckpoint : 'session-projection.md' ,
DirectoryPickerCapability : 'workspace.md' ,
TypertContribution : 'invariants.md' ,
2026-08-23 06:14:32 +08:00
TypertRemoteEventSource : 'typert.md' ,
2026-07-30 21:40:58 +08:00
TypertFace : 'invariants.md' ,
TypertPackageFilter : 'invariants.md' ,
TypertPackageRecord : 'invariants.md' ,
TypertSchemaFilter : 'invariants.md' ,
TypertSchemaRecord : 'invariants.md' ,
2026-07-19 14:57:52 +08:00
}
2026-07-28 23:48:35 +08:00
/** TypeScript lib and pinned framework types with no repository-owned data page. */
export const FOUNDATION_TYPE_NAMES : ReadonlySet < string > = new Set ( [
2026-07-19 14:57:52 +08:00
'AbortSignal' ,
'AsyncIterable' ,
'Context' ,
'Error' ,
2026-08-22 20:03:23 +08:00
'EntryTree' ,
2026-08-19 13:09:02 +08:00
'Exclude' ,
2026-08-25 06:10:25 +08:00
'Extract' ,
2026-07-28 23:48:35 +08:00
'Map' ,
2026-08-19 13:09:02 +08:00
'NonNullable' ,
'Omit' ,
2026-07-28 02:11:31 +08:00
'Partial' ,
2026-07-19 14:57:52 +08:00
'Pick' ,
'Promise' ,
2026-07-28 23:00:00 +08:00
'Record' ,
2026-07-19 14:57:52 +08:00
'Readonly' ,
2026-08-10 15:09:07 +08:00
'Uint8Array' ,
2026-07-19 14:57:52 +08:00
] )
2026-07-27 22:48:20 +08:00
/** Project types deliberately documented outside the subsystems catalog. */
2026-07-28 23:48:35 +08:00
export const TYPE_LINK_EXEMPTIONS : Readonly < Record < string , string > > = {
feat(settings): add user-settings seam (ctx.settings) + file provider
Two-package capability family mirroring session-persistence/:
- dsh-settings: abstract Settings service — namespace registry with
caller-fiber effect registrations, layered resolution (schema defaults
< composition base < user document), schemastery validation,
per-namespace deep-equal commit detection, and the settings/updated
event. Boot/registration validation fails loud; provider publishes
keep last-good per namespace.
- dsh-settings-local: settings.yaml/.json provider — resolveSpec
defaulting to $DSH_HOME/settings.yaml, chokidar hot reload,
content-equality self-write suppression, atomic 0600 tmp+rename
writes, comment-preserving YAML namespace patching.
Consumers register inside ctx.inject(['settings'], …), so every
composition works unchanged without a mounted provider. Real Loader +
Include composition test proves cordis.yml boot and external-edit hot
propagation; HMR disposal test proves registry cleanup. Both packages
hold per-file 100% coverage.
Doc budgets rise 1705→1710 (AGENTS.md) and 835→845 (packages/README.md):
one structural line per file for the new package group.
Agent Note: .agents/notes/implemented/architecture/2026-07-28-user-settings-seam.md
2026-07-28 17:30:12 +08:00
z : 'schemastery schema constructor is owned by vendor/schemastery (vendored upstream)' ,
2026-08-13 00:36:22 +08:00
BeginCommandRequest : 'event-local request contract is owned by packages/client/ui-input-trigger/src/types.ts' ,
InsertReferenceRequest : 'event-local request contract is owned by packages/client/ui-input-trigger/src/types.ts' ,
ConsumeTokenRequest : 'event-local request contract is owned by packages/client/ui-input-trigger/src/types.ts' ,
InsertTextRequest : 'event-local request contract is owned by packages/client/ui-input-trigger/src/types.ts' ,
2026-07-19 14:57:52 +08:00
AgentHandle : 'agent ownership handle is owned by packages/core/agent/README.md' ,
2026-08-03 20:30:33 +08:00
AgentPreset : 'discovered preset record is owned by packages/preset/agent-presets/README.md' ,
feat(agent-presets): give a preset a name and a description
A picker showed directory names, so the settings page could only ever list
`standard` / `core-web` / `cordis` and hope the reader knew what they meant.
A preset may now publish display text in an optional `preset.yml` beside
its composition, and the section renders cards — name, description, and the
one in use — instead of rows.
The file carries display text ONLY. `id` is the directory name and `trust`
comes from the root a preset was discovered under, so neither is writable
there: otherwise a locally authored preset could name itself into the
shipped set. It is a separate file because a composition is a top-level list
of plugin rows — YAML cannot carry sibling keys beside it, and a fake
metadata row would hand the Loader something to load.
Every read failure degrades to no metadata; absent, malformed, wrongly
typed, and blank all mean the same thing and the picker falls back to the
id. Presentation is not capability: a preset whose name is broken still
mounts.
The editor gained name and description fields above the YAML, and clearing
both removes the file rather than storing a blank name.
2026-08-05 16:30:14 +08:00
PresetMetadata : 'preset display text is owned by packages/preset/agent-presets/README.md' ,
2026-08-13 00:36:22 +08:00
BashEnvContributor : 'service-local extension type is owned by packages/shell/tool-bash/src/index.ts' ,
BashEnvVariableInfo : 'service-local metadata type is owned by packages/shell/tool-bash/src/index.ts' ,
CompactionAgentContext : 'compaction service input is owned by packages/compaction/compaction/src/index.ts' ,
ManualCompactAgentContext : 'manual compaction service input is owned by packages/compaction/compaction/src/index.ts' ,
2026-08-12 23:51:31 +08:00
ClientResponse : 'wire response message is owned by packages/host/apiproxy/src/api/rpc.ts' ,
ApprovalRequestId : 'dynamic Plugin approval identity is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
CordisErrorDetails : 'Cordis runtime error payload is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
CordisInspectPlatform : 'Cordis inspect platform identity is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
CordisInspectProviderManifest : 'Cordis inspect provider manifest is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
CordisInspectProviderView : 'Cordis inspect provider view is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
CordisInspectQueryRequest : 'Cordis inspect transport payload is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
CordisInspectQueryResolution : 'Cordis inspect query result is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
CordisInspectQueryResolved : 'Cordis inspect transport payload is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
CordisInspectRequestId : 'Cordis inspect request identity is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
CordisInspectResolveAck : 'Cordis inspect resolution acknowledgement is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
CordisDynamicPackageId : 'dynamic Package identity is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
CordisDynamicPluginId : 'dynamic Plugin identity is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
CordisDynamicPluginRunId : 'dynamic Plugin run identity is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
CordisDynamicRunMode : 'dynamic Plugin activation mode is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
DynamicCordisClientSource : 'dynamic-package payload contract is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
DynamicCordisDefineReceipt : 'dynamic-package payload contract is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
DynamicCordisDefineRequest : 'dynamic-package payload contract is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
DynamicCordisHostHalfResult : 'dynamic-package payload contract is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
DynamicCordisInventoryRow : 'dynamic-package payload contract is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
DynamicCordisInvokeResult : 'dynamic-package payload contract is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
DynamicCordisPackageInspection : 'dynamic Package source inspection is owned by packages/extensions/cordis-host-runner/src/registry.ts' ,
DynamicCordisPluginInspection : 'dynamic Plugin inspection is owned by packages/extensions/cordis-host-runner/src/registry.ts' ,
DynamicCordisRequestResolved : 'dynamic-package payload contract is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
DynamicCordisRetracted : 'dynamic-package payload contract is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
DynamicCordisRunRequest : 'dynamic-package payload contract is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
DynamicCordisPackage : 'dynamic-package payload contract is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
DynamicCordisReference : 'dynamic Plugin reference is owned by packages/extensions/cordis-host-runner/src/registry.ts' ,
DynamicCordisRenderFailure : 'dynamic-package payload contract is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
DynamicCordisResolveAck : 'dynamic-package payload contract is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
DynamicCordisRunResolution : 'dynamic-package payload contract is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
DynamicCordisRunResponse : 'dynamic-package payload contract is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
DynamicCordisSnapshotRow : 'dynamic-package payload contract is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
DynamicCordisStopResponse : 'dynamic Plugin stop result is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
DynamicCordisUndefineReceipt : 'dynamic-package payload contract is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
HostCordisInspectProviderRegistration : 'Host inspect provider registration is owned by packages/extensions/cordis-host-runner/src/inspect-registry.ts' ,
2026-07-25 16:04:48 +08:00
DomainImpl : 'domain implementation contract is owned by packages/storage/storage-domain/README.md' ,
refactor(packages): dissolve ui/ and rename sdk/ to scaffold/
git mv per the regrouping RFC: the five human-collaboration seams and
tui join packages/interaction/, app-boot becomes packages/boot/, and
jsonrpc joins the renamed scaffold/ (formerly sdk/) as its server half
beside client/protocol/create-sdk/helper/scripts/telemetry, whose
folders drop the legacy sdk- prefix. Three new group README triplets
replace the ui/ and sdk/ ones; tsconfig references/paths/globs,
knip keys, vitest globs, gate scripts, catalogs, docs, and the
lockfile follow. Adds the four settled FIXME rename markers
(dsh-sdk-server, dsh-sdk-telemetry, dsh-sdk-helper, dsh-sdk-scripts).
The scaffold folders diverge from their npm names until those renames
land, so tsconfig.base.json maps the three affected names explicitly
beside the group wildcard. Also repairs two pre-existing stale-path
classes the strengthened sweep surfaced: docs/web-styling.md's retired
web-ui host package and type-model spec fixture-literal joins.
app-boot's three Loader-composition specs time out at the default 5s
under full-suite parallel load on this filesystem (pre-existing;
pass isolated with --testTimeout=30000); interaction/scaffold/boot
suites otherwise green (687 passed).
2026-07-30 03:13:49 +08:00
CommandExecution : 'executor return contract is owned by packages/interaction/commands/src/index.ts' ,
2026-07-28 23:48:35 +08:00
'z.core.JSONSchema.BaseSchema' : 'zod projection output is owned by the zod v4 API' ,
'z.core.ToJSONSchemaParams' : 'zod projection parameters are owned by the zod v4 API' ,
2026-08-13 00:36:22 +08:00
TypertDisposer : 'Typert lifecycle contract is owned by packages/typert/protocol/README.md' ,
2026-07-30 21:40:58 +08:00
InvokeRemoteRequest : 'gateway invocation contract is owned by packages/api/gateway/README.md' ,
2026-08-09 15:27:21 +08:00
LocaleDict : 'service-local dictionary fields are owned by packages/client/i18n/src/index.ts' ,
feat(gui): step1 skeleton — dsc web serves built web UI over booted harness host
Five new modules: apps/dsc (bin: parseArgs + node:http static server +
signal shutdown), packages/host/apiproxy (programmatic harness core
composition, agents:[]), packages/client/web-runtime (React-free browser
runtime), packages/client/web-ui (React mount), apps/web (vite build
entry producing dist consumed by apps/dsc via package exports).
Root wiring: apps/* workspace glob, dsh-* paths for host/client groups,
demo:web script, apps/web/dist gitignore. No protocol/API routes yet —
contract lands in step2 (see missions/tasks/20260719-1902-apiproxy-api-design).
Includes the design + implementation archives (spec v2.1, deepseekchat
baseline and harness boot research, implementation run log).
Acceptance: 12/12 passed incl. real-key llm.stream smoke (51 chunks).
feat(gui): apiproxy — four-quadrant RPC contract + fetch carriers, live end to end
Contract layer (src/api/, 14 files): four named wire message types
(ClientRequest / ServerResponse / ServerRequest / ClientResponse) as a
discriminated union over strict bidirectional rpcId (initiator mints,
responder echoes; channel and message fully decoupled — HTTP is the
client->server pipe, SSE the reverse); narrow RpcRequest<P>/
RpcResponse<T> signature forms; RpcMethodMap with RequestPayload<K>/
ResponseValue<K> derivation; typed RpcError details map; approval/
question responses modeled as ClientResponse via a single /api/respond
endpoint (RpcReceipt carrier ack); zod schemas anchored per Wire<T>
against exactOptionalPropertyTypes.
impl/api-proxy.ts: describe/list/create, both SSE streams (frame queue
pump, subscribed baseline, lifecycle frames, signal cleanup); history
pages on message boundaries (tail-back scan, partial included in the
tail page); prompt dispatches queue->agent.send / steer->agent.steer
with rpcId carried through MessageSource; cancel for attached sessions;
cold-session resume deduped via a per-id promise map; host-level
provider/model defaults injected at create/resume.
fetch/: mechanical UNARY_ROUTES table, two-level parse with
path==method check, SSE frames completed to ServerRequest full form;
client mints -> narrows -> envelopes outbound, verifies rpcId echo
inbound, streams SSE frames, four-quadrant onEnvelope tap (debug panel
choke point). Real-browser fixes: URL base resolves to location.origin
(hardcoded internal base broke real pages), browser-safe export paths.
Design archives: contract design.md v2.0 with decision log,
core-coverage audit, comparative studies, step2 impl run log. Probed
end to end over real HTTP: prompt -> live model stream -> history
returns the finished reply.
feat(gui): RpcLog debug panel — fixture-driven milestone, playwright-verified 10/10
web-runtime: rpcLog + ui slices (zustand), four-quadrant RpcLogEntry
(client-request / server-response / server-request / client-response),
onEnvelope tap -> microtask-batched pump with 500-entry ring buffer,
ConnectionController (private state, backoff reconnect), fixture API
with fake envelopes (?fixture switch), bootWebRuntime; contract types
via temporary local copies (api-types.ts, swapped for real imports when
W3 client lands).
web-ui: components/panels/RpcLog five-piece set (badge with unread
count, floating panel, direction glyphs per quadrant, same-rpcId
pair highlighting in two families, JSON payload expand, follow/pause,
clear), App shell, utils/formatRelative, light-theme CSS variables with
dark placeholders.
dsc bin: mime lookup fixed to use the actually-served file (naked
'/?query' no longer falls through to octet-stream download); shutdown
closes SSE keep-alive connections so SIGTERM actually exits.
Acceptance: scripts/verify-rpclog-panel.mjs (chromium headless) ALL
PASS 10/10 over design.md §D 1-6.
pkg: add web scripts for building
feat(gui): session milestone — list + conversation over Session OOP, styled RpcLog v2.1
web-runtime: Session/SessionManager object layer (resident instances,
mux frame routing, lineage flattening), foldSurface adapter with padding
sentinels for paged windows, chunk accumulator for streaming partials,
batched change notification (useSyncExternalStore contract), connection
sinks + reconnect fix (the 300ms self-abort reconnect storm that made
the session list flap is gone), fixture rewritten as a scripted host
(60-turn history, typewriter replay, resident pending approval, child
session); temporary contract copies deleted in favor of real apiproxy
imports.
web-ui: sessions screen (list with lineage indent + selection as
container-local state), conversation view (turn grouping, reasoning
fold, tool cards, steering, pending interaction cards, upward paging
with scroll anchoring), input bar with queue/steer/stop; RpcLog panel
restyled per docs/web-styling.md (tokenized palette, quadrant badge
glyphs now vertical ↑↓⇟⇞, pair highlighting, floating shadow).
docs/web-styling.md: living style guide (tokens, visual baseline,
coding rules, evolution log).
Acceptance: verify-session.mjs 31/31, verify-session-real.mjs 5/5
(real model streaming), verify-rpclog-panel.mjs 10/10.
feat(gui): hostruntime split + repo-wide package prefix rename
Package split (design: 20260720-0101-hostruntime-split-design):
dsh-host-runtime carries bootHost + createApiProxy + startHost()
(RunningHost {api, handler, defaults, ctx, dispose} — the seam Electron
and any future shell reuses; ctx is the official front-door mount
point); dsh-host-webserver carries the node:http static+API bridge
(fixed: abort now keys on res 'close' + writableEnded — req 'close'
fires on body end since Node 16 and was killing every SSE stream
instantly, the reconnect-storm root cause); apps/dsc is now a thin
assembly with web/-p subcommands. dsc -p runs the full isomorphic
carrier chain in process (second real protocol consumer; probed
end-to-end against the live model).
Naming rule (user decree): packages under host/ and client/ carry the
directory prefix in their npm name — dsh-host-apiproxy,
dsh-client-web-runtime, dsh-client-web-ui renamed repo-wide in one
frozen batch; explicit tsconfig paths entries added where the wildcard
no longer matches.
Acceptance: verify-session 31/31, verify-rpclog-panel 10/10,
verify-session-real 7/7 (incl. new 12s connection-stability sentinels),
tsc green, dsc web + dsc -p smoke both pass.
refactor(gui): AbstractApiClient class hierarchy — OO client with inheritable seams
AbstractApiClient (apiproxy) carries every protocol invariant: rpcId
minting, four-quadrant envelope wrap/unwrap, zod parsing, SSE frame
parsing, the payload-direct IApiClient surface (callers no longer mint
rpcIds — the carrier does), and the instance-level envelope observation
pump (batched via microtask; moved off module-level globals in
rpc-log.ts, which is now a pure subscriber mapping envelopes into store
entries — the debug panel observes the connection, it is not part of
it).
Platform subclasses own two abstract seams (doFetch, onEnvelope) plus
three protocol-level virtuals for transportless overrides:
InProcessApiClient (apiproxy; dsc -p uses new InProcessApiClient(
host.handler)), WebApiClient (web-runtime), FixtureApiClient (fixture
now subclasses instead of wrapping). Naming per decree: AbstractApiClient
/ IApiClient; ApiProxy stays the impl-side narrow-form contract.
headless.ts call sites drop rpcRequest wrappers (payload-direct);
split-design archive updated with the naming-rule ledger.
tsc green; verify-session 31/31, verify-rpclog-panel 10/10,
verify-session-real 7/7 (12s connection sentinel count=4); dsc -p smoke
CALLER-OK.
feat(gui): InputBar final form — bug batch, deepseekchat layout, single primary button, running locks input
Squashes the whole InputBar iteration batch: IME/caret/auto-grow/focus/dedup
bug fixes, layout aligned to the deepseekchat baseline, single primary button
with hover flyout, finalized button semantics with the Codex-style icon
circle, and running-state locking where stop is the only mid-turn action.
The same batch carried the Chinese-to-English code comment sweep
(density pruned), folded in here.
docs(gui): purge work-log references from code comments
76 design-doc references cleared across the GUI packages: section
pointers inlined as self-contained constraint statements, pure pointer
comments dropped, milestone codenames and ruling tags out, and the 14
contract file headers switched to the formal RFC (the only sanctioned
external reference). web-styling.md now cites the styling RFC instead
of the disposable research archive. grep for work-log reference
variants is clean across the GUI packages.
docs(gui): file-header comments self-contained — drop RFC filename references
RFC renames/reorgs must not require a source sweep (the 2026-07-20
two-way merge proved it). 11 headers lose only the '(RFC …)' tail and
stay self-contained; api-proxy.ts keeps its minimal-first note.
fix(gui): session streaming — freeze interrupted partials, sweep stale running calls, send force-scrolls
Aborted turns never emit the finalizing assistant/message, so the
accumulated partial and its running tool cards kept rendering below
later messages — the "new message lands above the stopped reply"
illusion. turn/end side effects now freeze content-bearing partials
into interrupted terminal nodes (fractional seq keeps flow order; the
live freeze and history replay converge through applyEventSideEffects,
so a refresh reconstructs identical frozen nodes) and turn running tool
cards into interrupted terminal cards; only content-free partials are
swept outright. ConversationView gains the send-force-scroll rule (own
words must be visible) alongside the pre-update atBottom follow flag.
Regressions pinned as E2-4a–c (real host) and §E1-11h (fixture).
feat(gui): webserver hardening verify script
feat(gui): dark-mode toggle pinned to the sidebar bottom
Interim home before the Settings page exists (the button re-homes with
zero logic change — mechanics live in utils/theme.ts): html[data-theme]
flip + dsc.theme localStorage, stored choice wins over the OS
prefers-color-scheme default, applied in mount() before first paint so
a dark reload never flashes light. Moon/sun inline SVG icon button at
the sidebar's pinned bottom row. Pure front-end local concern: no RPC,
no Session/store involvement. Dark sweep of list/conversation/input
card/RPC panel found no unreadable pairs — no token changes needed.
docs(gui): GUI RFCs and web styling handbook
Layering+RPC protocol and web client architecture RFCs (post-reorg,
developer-facing polish folded in) plus the styling engineering
handbook. Mission work logs live in the commit above; PRs can be cut
from this commit to include formal docs only.
fix(gui): client object-layer hardening — audit timing/reference/resilience batches (S3-S5,C1-C3,C5-C8)
fix(gui): carrier error channel + webserver backpressure (audit A1-A5,A7-A10,R2,R5)
feat(gui): session persistence surface — cold list, project cwd, legacy no-cwd retirement
refactor: rename dsc CLI to dsh — apps/cli, bin name, package scope
Includes the root tsconfig project-references fix for host/* and
client/web-runtime (originally a separate build fix commit).
test(gui): three-tier suite — protocol/object/browser lanes, tier-a fill to per-file 100%
test(gui): jsdom lane for web-ui + web-runtime coverage gate entry
docs(gui): GUI testing system RFC (zh)
feat(gui): tool-card views — contract slot, host-computed delivery, three-level card fallback
fix(gui): lint clean across GUI packages — wrap long doc comments, drop dead type args, sync-return methods without awaits
docs(gui): doc-sync mechanical fixes — JSDoc on apiproxy/host exports, RFC sketch fences ignore-check, md-wrap paragraphs, drop missions links, web-ui plain-ts entry
chore(gui): module-graph regen + knip clean — drop dead re-exports, internalize createFixtureApi, scan web-ui tsx and verify mjs scripts
build(gui): wire client/host packages into the lib build shape — tsc references + tsdown (web-ui css-external), lib manifests, cordis peer, apiproxy typed subpaths, vite src aliases
test(gui): host-side per-file 100% coverage — apiproxy schema/carrier suites, webserver http-bridge suite, host-runtime composition suite; client/* coverage excluded pending the browser-side testing work item
docs(gui): package READMEs for the five GUI packages — model-experience audit entries, limitations sections
docs(gui): bilingual RFC pairs + client JSDoc completion — translate the three GUI RFCs to English with i18n records and manifest ratchet, Consequences sections both sides, full client/* export JSDoc, regen doc graphs and RFC index
fix(scripts): doc-typecheck built-declarations mode maps /src/* subpath wildcards (apiproxy browser-safe channels)
docs(gui): apply dsh rename across pr-gates docs — READMEs, layering RFC en, web-ui entry comment, i18n re-record
fix(gui): post-rebase lint reconciliation — wrap main-tree long doc comments, read-through narrowing guards, abortError Error normalization, handleUnary generic justification
fix(gui): post-rebase doc/test reconciliation — align host specs with evolved carrier contracts (sentinel rpcId, stream/error surfacing, url-path transport messages, defaults.cwd), Agent Note titles and relocated links, KV Cache effect sections, JSDoc on evolved exports
fix(gui): second-rebase reconciliation to 509db0cb3 — restore api panel exports the baseline suites consume, knip workspace entries for jsdom lane and apps/web smokes, hoist result narrowing, align testing.md to the narrowed web-ui exclusion
fix(test): vitest-scoped tsconfig maps bare imports for tsx specs — with GUI manifests now pointing at lib, an unmapped importer loaded a second copy of the web-runtime singletons
fix(gui): typecheck + lint clean over the tool-card batch — brand callIds and object-form turn/end reason in the view spec, narrow fixture arg stringification, wrap long v8-ignore comments
docs(gui): export JSDoc for tool-card surfaces + testing-note pairing header
docs: rfc for web testing
feat: add tools to host-runtime
fix(gui): dispatch agent/error via agentEvents in host-runtime spec — mounted invariants plugin rejects raw ctx.emit without the scope carrier
fix(gui): restore GUI knip workspaces + scripts/mjs entries and regenerate lockfile after master rebase
fix(gui): post-rebase gate repairs — drop context-node envelope (master unwrapped injected content envelopes), regen event matrix, condense testing.md web-ui exclusion within budget
fix(session): browser-safe deep-equal in surface — node:util import broke the vite bundle
ci(gates): frontend vite build joins pre-push — node: imports in the client closure pass tsc but break the browser bundle
test(tui): drop the checkout-dependent process.cwd() harness default — a long worktree path pushes the footer token counters past the 88-column fake terminal
test(gui): jsdom behavior E2E — conversation main path over fixture runtime, reconnect banner lifecycle
test(gui): jsdom RPC panel behavior — ledger rows, expand, pairing, pause/clear, follow-pause, payload truncation
test(gui): jsdom tier-2 — InputBar guards, reasoning fold, JSON blocks, message variants, theme, create-then-select; act-harden banner case
test(gui): jsdom tier-3 — ConversationView states/paging/force-bottom, ToolCallCard arms, PendingCard, list rows
test(gui): jsdom tails — view-card variants, LogRow directions, registry hygiene, badge overflow, hook ops, mount glue
test(gui): jsdom tails round 2 — call-ref blocks, resume follow, view precedence, failed create, empty-diff arm
test(gui): jsdom final arms — anchor compensation, follow-off, interval ticks, view halves, node-over-running precedence
test(gui): web-ui joins the per-file 100% coverage gate
Annotation-only src changes plus the config swap. The web-ui exclusion is
replaced by a single index.tsx entry (stale byte-identical duplicate of
mount.tsx, nothing imports it; same entry-glue treatment as bin.ts) and the
coverage include gains .tsx.
v8-ignore sites (each with its reason inline):
- ConversationView 3x ref-null guards; InputBar disabled-click guard
- ToolCallCard both-null arms + windowless-custom argsRaw arm
- LogRow css-module key fallbacks (start/stop block); RpcLogBody 3x ref-null guards
- web-runtime drift from the tool-card batch: fixture presenter catch/str
typo-guards, dense-array guards (fold-adapter reset, session rebuild,
fixture backscan), live view-present arm (fixture replays are text-only;
view vocabulary is covered by the history samples)
test(gui): close the PR #443 host-side coverage gaps — apiproxy client abort arms, api-proxy cold/view paths, webserver drain
- apiproxy fetch/client.ts: 3 new cases (pre-aborted signal short-circuits
before transport + string reason mapping, non-Error/string reason falls to
the default AbortError message, signal-less doFetch passthrough)
- runtime/api-proxy.ts: one v8-ignore (summarizeCold cwd arm — list()
filters cwd-less legacy metas) + api-proxy-cold.spec.ts (cold list merge:
mtime source, locate-undefined and vanished-log fallbacks, lineage;
no-persistence/no-factory resume → internal) + 2 view cases (history views
with meta passthrough and orphan/bad-args/presenterless soft-falls,
session/disposed open-call cleanup on the mux stream)
- webserver/index.ts: /api/big fixture drives both drain-wait legs (full
8MiB readback after drain, mid-chunk disconnect wakes via 'close')
feat: app shell
fix: rebase conflicts
fix: coverage
fix(gui): lint clean after rebase — wrap long v8-ignore comments, unconditional v1 detail-block claim
chore(gui): remove browser/probe verify scripts from scripts/
The six GUI acceptance/probe scripts (carrier-errors, rpclog-panel,
session, session-real, webserver-backpressure, webserver-hardening)
leave the repo's scripts/ tree; the three code comments that pointed at
them now describe the coverage lane without naming a script path.
fix(webserver): guard the request callback — one malformed request must not kill the process
The async handle() had no top-level catch, so any throw inside it (a bad
%-escape reaching decodeURIComponent, a client dropping mid-body, a
response stream erroring) became an unhandled rejection and took the whole
process down (audit R1 must-fix). The guard answers 400 when headers are
not out yet, destroys the socket when they are, and reports the failure to
onError (the package never prints). Spec covers all three legs: %-escape
barrage → 400 + server stays alive, non-Error throw wrapped for onError,
mid-stream explosion → socket teardown.
feat: client AGENTS.md
fix: client/AGENTS.md
fix: rebase
feat(gui): T0 cut 1 — 12 client package skeletons with contract stubs, dshClient declarations, tsdown client preset, theme token sheets
feat(gui): T0 cut 2 — pure git mv migration per v3 §11 (connection six, runtime sessions/kernel, ui-conversation chat, ui-primitives markdown family, web shell + e2e)
feat(gui): T0 cuts 3+4 — import rewiring to new package names, .legacy demotion of owner-rewrite files, legacy web-runtime/web-ui/apps-web retired to attic
feat(gui): connection 对账刀——index.ts 精确导出清单替换 export *,intents.legacy 溶解删除
feat(client/ui-slots): SlotCore real implementation — kind semantics, sync version + microtask-batched notify, onMutate bridge
feat(gui): web shell vite alias — retarget to new client packages, shell static surface only
feat(gui): host 侧刀属地半——HostWebPluginRegistry(entries 扫描+internal/plugin 去抖重扫+dshClient 校验+exports./client 解析)、GET /plugins/<id>/client.js 分发端点、GET / 与 SPA fallback 注入 __DSH_BOOT__(webPlugins 可选注入,不传行为不变)
feat(web-react): add use-sync-external-store dep + local shim typings
feat(web-react): bindSnapshotSelector via uSES with-selector shim
feat(gui): ui-layout concession-chain solver — pure computeColumns with contract geometry
feat(gui): ui-layout LayoutService — four persisted stores, clamped actions, list-driven prune
feat(gui): ui-layout AppFrame styles — grid columns, collapse-safe borders, edge drag handles
test(gui): 存量 spec 平移——connection 三件+runtime 六件自 attic 捞回改包名路径全绿;api-helpers 按归属拆分(wire 半留 connection、classifier 半随 conversation.ts 入 runtime);boot-intents/preinit/rpc-log 随 intents/rpc-log 退役不迁(记 v3 §3.2 溶解项)
feat(client/ui-primitives): StateDot/Button/Pill/Input/Menu atoms, ConnectionBanner de-legacied to pure props, JsonBlock CSS on --dsw tokens
feat(web-react): createSnapshotStore engine (rafFlush batch, persist opt-in, dev freeze) + spec
feat(gui): ui-layout AppFrame — grid tracks, pointer-capture drag handles with rAF throttle, frame ResizeObserver
feat(web-react): useInvoke (external pending store, stable invoke, concurrency count) + spec
test(web-react): bind spec — equality bail, custom eq, zero resubscribe, StrictMode, method sources
feat(gui): ui-layout index rewiring — real exports, client apply provides ctx.layout and defines three slots
feat(web-react): SessionProvider (renderBody deps) + RootBindingProvider + binding contexts + spec
feat(gui): web shell AppRoot boot-page styles — self-contained with neutral token fallbacks
feat(gui): web shell AppRoot — boot gate over loader status, fail-loud plugin failure list
fix(gui): AppRoot gates on explicit settled signal — status-derived readiness races the incrementally filled table
feat(client/ui-theme): ThemeService real implementation — registry with built-in light/dark, apply toggles body[data-ds-dark-theme], third-party token overrides as body inline vars
feat(web-react): scopedSlots outlet (kind matrix, inject WeakMap caches, per-entry error boundary) + spec
feat(gui): web shell module-table seed — pure-library entities for the loader require surface
feat(client/i18n): I18nService real implementation — ns×locale registry, stable bind(ns) reference, zh fallback chain, zh/en skeleton dictionaries
feat(gui): web shell assembly closure — layout exports via module table, SessionProvider + scopedSlots + RootBindingProvider
feat: client/ui-conversation
feat: code
codedoc
build(gui): root bundle green — web shell excluded from the lib workspace (vite app), ui-primitives lib externalizes css side-effect imports (web-ui precedent)
gates(gui): verify-cordis-config follows aggregate tsconfig references (root is a shell over host/client programs); module graph regenerated for the twelve client packages
chore(gui): retire legacy migration sources — every owner rewrite landed (t0-checklist §7 ledger honored); orphan css of retired components removed
gates(gui): knip green groundwork — e2e/tsx entries for the new packages, loader-runtime deps ignored where loading is by specifier string, fake plugin ids un-bare-named, dead test export dropped
chore(client): manifest shape batch A — ui-slots/web-react/ui-primitives invariant companions, files whitelist, cordis+invariants peer/dev, tsconfig refs
chore(client): manifest shape batch B — connection/runtime/ui-conversation/ui-trajectory files whitelist, cordis peer+dev, explicit invariant lib entries (clientBundle signature)
chore(client): manifest shape batch C — i18n/ui-layout/ui-sidebar/ui-theme invariant companions, files whitelist, invariants peer/dev, tsconfig refs
chore(client): manifest shape batch D — web shell gains node-half lib entry + invariant companion + uniform files whitelist
chore(client): drop verified-unused deps — dsh-tools from runtime/ui-conversation (types ride /presentation), ui-primitives+clsx from ui-layout
gates(gui): doc-gate fixes — theme JSDoc prose, three client type-link exemptions, agent-note paths follow the migration, config catalog regenerated
gates(gui): type-equiv manifest follows the types.ts extraction, approval JSDoc keeps its link form, persistence catalog regenerated
docs(gui): per-constant JSDoc on the contract geometry exports (export-jsdoc gate)
test(gates): loader-composition budget covers cold tsx resolution after the program split (was flaking at the default 5s)
docs(gui): README substantiation batch 1 — ui-slots/ui-primitives/web-react/connection: Model Experience short form, real deferred-work ledgers, description accuracy pass
fix(client): theme/i18n dual-entry split — service classes + cordis merges move to src/client (host catalog scanner no longer misclassifies client services), node halves keep types + empty apply; catalogs regenerated
docs(gui): README substantiation batch 2 — runtime/ui-layout/ui-sidebar/ui-conversation: Model Experience short form, package-owned deferred-work ledgers (unload stub, watch approximation, /client value-import rule, global details state, two-state dots, stats duration gap, single-bundle caches)
docs(gui): README substantiation batch 3 — ui-trajectory/ui-theme/i18n/web: Model Experience short form, deferred-work ledgers (placeholder charter, no theme toggle owner, empty locale dictionaries, one-shot rendering); both README gates green
test(scripts): purity spec adopts clientBundle two-arg signature (explicit libEntry, no default)
gates(gui): knip green — declaration-merge dep ignored, fake plugin id assembled at runtime, invariants dep de-duplicated to peer+dev, stale apps/web section dropped
feat(gui): 门禁波次 host 三包 invariant 形状——apiproxy explained-empty 伴生(wire 契约层零事件面)、webserver 真关系伴生(manifest 行必解析出 clientPath,防 __DSH_BOOT__ 广告 404 bundle;apps/cli 发布 webPlugins 键供审计)、runtime 补 files 白名单;三包 exports/files/peer+dev/tsconfig refs 齐 fw-react 形状;constraints+invariants 双 gate 零违规
build(client): ui-layout/ui-sidebar tsdown configs adopt the explicit two-arg clientBundle signature (orphaned follow-up of the manifest shape batch)
refactor(gui): shell boot becomes a library face — bootWebShell(el) exported for the apps/web entry; main.ts retired
refactor(gui): exports 纪律刀1——ui-theme/i18n node index 收敛为只空 apply(Translate/LocaleDict/ThemeTokens 类型下沉 src/client/),ui-conversation 的 I18nService import 改 /client 子路径
build(typecheck): converge to root host aggregate + tsconfig.client.json — delete tsconfig.host.json, verify-cordis-config seeds both aggregates
feat(gui): apps/web restored as the vite application — thin main over bootWebShell; dsh-client-web becomes a plain lib (index exports shell surface, vite files and e2e moved out)
chore(gates): knip.json rewritten on the master base — same semantics, minimal diff (formatting churn dropped)
docs(gui): 时效清扫②——testing.md 删 web-ui 覆盖豁免残句;web-styling.md 加 token 换代头注(--dsw-* 现行、工程约束条款仍有效并注明收编处)
docs(gui): 时效清扫③——四对 GUI Agent Note 加路径更新头注(web-runtime/web-ui/dsh-frontend→现行 12 包结构;设计结论存续声明;双语对同步)
docs(gui): 时效清扫③b——四对 note 头注的 i18n 配对哈希重录
build(typecheck): minimal-diff tsconfig shape — drop root files entry (purity spec + preset move to client program), compress comments, drop redundant util/home root ref
feat(gui): apps/web restoration follow-through — dsh-frontend package name, cli dist resolve, root build:web filter, tsdown exemption dropped, vitest web lane + knip + client aggregate retargeted, e2e paths rebased
refactor(gui): exports 纪律刀2——connection wire 六件 git mv 进 src/client/(wire 即该 dshClient 插件的 client 半),node index=只空 apply,/client 半边整面导出(v3 §3.2 清单原样),包内 tests 改 src/client 直取
refactor(gui): exports 纪律刀3——runtime 实现整体下沉 src/client/(sessions/slots/loader;契约类型与 cordis merge 随迁 client/index),node index=只空 apply;./loader exports 指 client/loader;全消费面(web 壳/ui-sidebar/ui-trajectory/tests)bare→/client 机械跟改;vitest.e2e 换 tsconfig.vitest paths(root tsconfig 排除 client 会把 /client import 掉到 exports 的浏览器 dist bundle)
refactor(gui): exports 纪律刀3 补遗——ui-layout 三处 bare runtime import 改 /client(刀3 消费面机械跟改漏提交件;跨属地机械一行×3 报备 ui-shell)
test(gui): drop the getSessionManager singleton case — the init/get pair is a dead legacy-boot surface with zero live consumers (SessionsService constructs and holds the manager under the plugin architecture); source removal tracked with rt-core
refactor(gui): 删 manager.ts 尾部 initSessionManager/getSessionManager 单例对——旧 boot 直连遗物,插件化下 SessionsService 构造持有 manager,全仓零活消费者(convo-b 测试清扫对表,其测试用例已先行退役 7e2c51898);头注释同步去单例措辞
code
refactor
2026-07-19 21:17:57 +08:00
ThemeTokens : 'service-local token dictionary is owned by packages/client/ui-theme/src/index.ts' ,
Translate : 'service-local bound translator is owned by packages/client/i18n/src/index.ts' ,
2026-07-27 22:48:20 +08:00
WebUpgradeRoute :
'upgrade route registration contract is owned by packages/host/webserver/src/index.ts' ,
2026-08-13 00:36:22 +08:00
InvariantRegistration : 'service-local lifecycle handle is owned by packages/runtime-diagnostics/invariants/README.md' ,
2026-08-12 23:51:31 +08:00
JsonValue : 'JSON value union is owned by packages/core/session/src/json.ts' ,
2026-08-13 00:36:22 +08:00
KnobState : 'projection unit state fields are owned by packages/interaction/permission-presets/README.md' ,
PermissionSelect : 'permissions projection payload is owned by packages/interaction/permission-presets/src/types.ts' ,
2026-07-19 14:57:52 +08:00
PromptAssembly : 'assembly result is owned by packages/core/system-prompt/README.md' ,
2026-08-12 23:51:31 +08:00
RequestRunId : 'dynamic-package payload contract is owned by packages/extensions/cordis-host-runner/src/types.ts' ,
RpcReceipt : 'carrier-layer receipt is owned by packages/host/apiproxy/src/api/rpc.ts' ,
2026-07-28 10:05:30 +08:00
Sandbox : 'external E2B SDK handle is owned by packages/e2b/e2b/README.md' ,
2026-07-19 14:57:52 +08:00
SessionForkSource : 'service-local fork input is owned by packages/core/session/src/index.ts' ,
refactor(subagent): drop host-user authority and split lifecycle publication
Remove the host-user continuation capability and the public residency query,
then separate the seam's public event payloads from its internal lifecycle
control interfaces.
`followup()` now takes the exact live direct parent `Agent` instead of a
`SubagentAuthority` union. No production adapter ever supplied user authority,
so the `UserAuthorityGrant` brand token existed only to stop a forged
discriminant from bypassing the direct-parent check — deleting the branch
retires the token, its mint method, and that attack surface together.
Narrowing `parent` from `Agent | undefined` to `Agent` removes three special
cases, including the path where a parentless epoch dispatched its lifecycle
events unscoped. Scoped-versus-global dispatch is now decided by the event, not
by whether a caller happened to have a parent.
`activationState()` had no caller; `ActivationState`, `ActivationObserver`, and
`ContinuationHost` are package-private.
New `src/lifecycle.ts` owns the contained emitter, the one-shot run observer,
and the Activation observer, while `SubagentRunInfo`/`SubagentRunEndInfo` move
to `src/types.ts` beside the other consumer-facing contracts. Those payloads are
public API — dsh-jsonrpc, hooks-claude, and the package invariant all consume
them — whereas the observer is a contract between two in-package collaborators,
so they no longer share a home merely for both being lifecycle-shaped. The
service keeps ownership of the scope carrier: `scopeTarget()` composes the
service's own context filter, so a narrowed stand-in would silently change
scope filtering.
Also drops now-unused dsh-tasks-local and dsh-tool-tasks dev dependencies, and
corrects the README claim that a pre-residency failure emits a terminal edge —
that path only ever rethrew.
2026-07-30 17:47:48 +08:00
SubagentRunEndInfo : 'event payload contract is owned by packages/subagent/subagent/src/types.ts' ,
SubagentRunInfo : 'event payload contract is owned by packages/subagent/subagent/src/types.ts' ,
2026-07-19 14:57:52 +08:00
WorkflowAgentEndInfo : 'event-local snapshot is owned by packages/workflow/workflow/src/index.ts' ,
WorkflowAgentInfo : 'event-local snapshot is owned by packages/workflow/workflow/src/index.ts' ,
WorkflowResultInfo : 'event-local snapshot is owned by packages/workflow/workflow/src/index.ts' ,
}
2026-07-28 23:48:35 +08:00
/** Repository data policy consumed by the Cordis catalog projector. */
export const CORDIS_CATALOG_POLICY : CordisCatalogPolicy = {
linkedTypePages : LINK_MAP ,
foundationTypeNames : FOUNDATION_TYPE_NAMES ,
typeLinkExemptions : TYPE_LINK_EXEMPTIONS ,
2026-08-13 00:19:15 +08:00
runtimeServiceExclusions : new Set ( [ 'cordisInspect' , 'dynamicCordisRunner' ] ) ,
2026-08-12 23:51:31 +08:00
runtimeServices : [ {
key : 'timer' ,
type : 'TimerService' ,
abstract : false ,
doc : 'Disposable timer helpers mixed into Cordis contexts.' ,
source : 'vendor/timer/src/index.ts:12' ,
methods : [
{
signature : 'timeout(callback: () => void, delay: number): () => void' ,
jsDoc : '/** Run a callback once and return its disposer. */' ,
} ,
{
signature : 'timeout(delay: number): Promise<void>' ,
jsDoc : '/** Resolve after a delay; disposal rejects the pending promise. */' ,
} ,
{
signature : 'interval(callback: () => void, delay: number): () => void' ,
jsDoc : '/** Run a callback repeatedly and return its disposer. */' ,
} ,
{
signature : 'interval<R = any>(delay: number): AsyncIterableIterator<void, R, void>' ,
jsDoc : '/** Return an async iterator of timer ticks. */' ,
} ,
{
signature : 'throttle<F extends (...args: any[]) => void>(callback: F, delay: number, noTrailing?: boolean): F & { dispose: () => void }' ,
jsDoc : '/** Return a throttled function whose timer is disposed with the current fiber. */' ,
} ,
{
signature : 'debounce<F extends (...args: any[]) => void>(callback: F, delay: number): F & { dispose: () => void }' ,
jsDoc : '/** Return a debounced function whose timer is disposed with the current fiber. */' ,
} ,
] ,
} ] ,
2026-07-28 23:48:35 +08:00
inheritedEvents : [
{ name : 'internal/plugin' , summary : 'A plugin fiber was created.' , source : 'vendor/cordis/src/events.ts:328' } ,
{ name : 'internal/status' , summary : 'A fiber changed lifecycle state.' , source : 'vendor/cordis/src/events.ts:330' } ,
{ name : 'internal/service' , summary : 'Interception hook for a service binding (no core producer).' , source : 'vendor/cordis/src/events.ts:332' } ,
{ name : 'internal/update' , summary : 'Waterfall: a fiber config update is being applied.' , source : 'vendor/cordis/src/events.ts:334' } ,
{ name : 'internal/get' , summary : 'Waterfall: a service is being read from the store.' , source : 'vendor/cordis/src/events.ts:336' } ,
{ name : 'internal/set' , summary : 'Waterfall: a service is being written to the store.' , source : 'vendor/cordis/src/events.ts:338' } ,
{ name : 'internal/listener' , summary : 'A listener was registered.' , source : 'vendor/cordis/src/events.ts:340' } ,
{ name : 'internal/dispatch' , summary : 'An event is being dispatched to listeners.' , source : 'vendor/cordis/src/events.ts:342' } ,
{ name : 'hmr/change' , summary : 'A watched source file changed on disk.' , source : 'vendor/hmr/src/index.ts:20' } ,
2026-07-28 14:11:18 +08:00
{ name : 'hmr/reload' , summary : 'Plugins are being reloaded after a change.' , source : 'vendor/hmr/src/index.ts:21' } ,
2026-07-28 23:48:35 +08:00
{ name : 'exit' , summary : 'The process is exiting on a signal.' , source : 'vendor/loader/src/index.ts:23' } ,
{ name : 'loader/config-update' , summary : 'The loader config tree changed.' , source : 'vendor/loader/src/index.ts:24' } ,
{ name : 'loader/entry-init' , summary : 'A config entry is being initialized.' , source : 'vendor/loader/src/index.ts:25' } ,
{ name : 'loader/partial-dispose' , summary : 'An entry is being partially disposed on reload.' , source : 'vendor/loader/src/index.ts:26' } ,
{ name : 'loader/patch-context' , summary : 'A context is being patched during a reload.' , source : 'vendor/loader/src/index.ts:27' } ,
] ,
inheritedServices : [
{ name : 'ctx.on / ctx.once' , summary : 'Register an event listener (disposable).' , source : 'vendor/cordis/src/events.ts:34' } ,
2026-07-22 18:02:26 +08:00
{ name : 'ctx.emit / ctx.parallel / ctx.serial / ctx.bail / ctx.waterfall' , summary : 'Dispatch an event (sync / awaited / first-bail / short-circuit chain).' , source : 'vendor/cordis/src/events.ts:34' } ,
2026-07-28 23:48:35 +08:00
{ name : 'ctx.plugin / ctx.inject' , summary : 'Load a plugin / declare required services.' , source : 'vendor/cordis/src/registry.ts:164' } ,
{ name : 'ctx.effect' , summary : 'Register a disposable side effect tied to the fiber.' , source : 'vendor/cordis/src/fiber.ts:9' } ,
{ name : 'ctx.get / ctx.set / ctx.provide / ctx.accessor / ctx.mixin' , summary : 'Low-level service-store access and binding.' , source : 'vendor/cordis/src/reflect.ts:7' } ,
{ name : 'ctx.extend / ctx.isolate / ctx.intercept' , summary : 'Derive a child context (scoped services / isolation / interception).' , source : 'vendor/cordis/src/context.ts:42' } ,
{ name : 'ctx.root / ctx.scope / ctx.fiber / ctx.registry / ctx.reflect / ctx.events / ctx.logger' , summary : 'Ambient handles onto the running context graph.' , source : 'vendor/cordis/src/context.ts:16' } ,
2026-08-12 23:51:31 +08:00
{ name : 'ctx.timer (+ interval / timeout / throttle / debounce)' , summary : 'Disposable timer helpers. The `timer` key is provided at runtime; the four supported helpers are mixed onto ctx directly (declared via Pick).' , source : 'vendor/timer/src/index.ts:4' } ,
2026-07-28 23:48:35 +08:00
{ name : 'ctx.loader' , summary : 'The config Loader that booted the app (present under the loader).' , source : 'vendor/loader/src/index.ts:30' } ,
{ name : 'ctx.hmr' , summary : 'The hot-module-reload watcher (present under the hmr plugin).' , source : 'vendor/hmr/src/index.ts:15' } ,
] ,
2026-06-20 19:47:09 +08:00
}
2026-07-30 21:40:58 +08:00
/ * *
2026-07-24 19:54:25 +08:00
* Splice a page ' s generated Cordis API region into its Markdown content .
* The page must contain exactly one ` cordis-surface ` marker region ( the markers are
2026-07-30 21:40:58 +08:00
* part of the hand - owned page skeleton once , then owned by the generator ) ;
* zero or several is a partition error the caller reports with the page path .
* The match is on THIS generator ' s exact markers , not the generic region
* grammar , so a page carrying only some other generator ' s region fails loud
* instead of having that region overwritten .
* @param content - the page ' s current full Markdown text .
* @param region - the freshly rendered marker - delimited region .
* @returns the page text with the region replaced .
2026-06-20 19:47:09 +08:00
* /
2026-07-30 21:40:58 +08:00
export function spliceRegion ( content : string , region : string ) : string {
const lines = content . split ( '\n' )
const begins = lines . flatMap ( ( line , index ) = > ( line === REGION_BEGIN ? [ index ] : [ ] ) )
const ends = lines . flatMap ( ( line , index ) = > ( line === REGION_END ? [ index ] : [ ] ) )
if ( begins . length !== 1 || ends . length !== 1 ) {
throw new Error ( ` expected exactly 1 cordis-surface region, found ${ begins . length } BEGIN/ ${ ends . length } END; add the BEGIN/END cordis-surface markers once ` )
}
const begin = begins [ 0 ] ? ? - 1
const end = ends [ 0 ] ? ? - 1
if ( end < begin ) throw new Error ( 'cordis-surface END marker precedes its BEGIN' )
return [ . . . lines . slice ( 0 , begin ) , . . . region . split ( '\n' ) , . . . lines . slice ( end + 1 ) ] . join ( '\n' )
}
2026-08-09 02:39:12 +08:00
/** The declared-vs-rendered inputs {@link walkPartitionProblems} judges. */
export interface WalkPartitionInput {
/** Service key → source pointer, as the rendering projection produced them. */
readonly renderedKeys : ReadonlyMap < string , string >
/** Event scopes the rendering projection produced. */
readonly renderedScopes : ReadonlySet < string >
/** Event names the rendering projection produced. */
readonly renderedEventNames : ReadonlySet < string >
/** Context key → first declaring file, from the independent AST scan. */
readonly declaredKeys : ReadonlyMap < string , string >
/** Event name → first declaring file, from the independent AST scan. */
readonly declaredEvents : ReadonlyMap < string , string >
}
/** The curated partition maps {@link walkPartitionProblems} enforces. */
export interface WalkPartitionMaps {
readonly servicePage : Readonly < Record < string , string > >
readonly serviceWalkExemptions : Readonly < Record < string , string > >
readonly eventScopePage : Readonly < Record < string , string > >
readonly eventWalkExemptions : Readonly < Record < string , string > >
}
2026-08-18 19:00:37 +08:00
/** Project paired Markdown destinations in one generated region to the page's locale. */
export function localizePageRegion ( region : string , pageRel : string , scanRoot : string = root ) : string {
2026-08-19 02:26:57 +08:00
if ( ! pageRel . endsWith ( '.zh.md' ) ) return region
const manifest = parseTranslationPairingManifest (
readFileSync ( resolve ( scanRoot , 'scripts/translation-pairing.manifest.json' ) , 'utf8' ) ,
)
return rewriteTranslationLinkLocales ( region , {
repoRoot : scanRoot ,
sourcePath : pageRel ,
isTranslationPairSource : translationPairSourcePredicate ( manifest ) ,
} ) . content
2026-08-18 19:00:37 +08:00
}
2026-08-09 02:39:12 +08:00
/ * *
2026-07-24 19:54:25 +08:00
* Judge the rendered API and the independent AST scan against the curated
2026-08-09 02:39:12 +08:00
* partition maps , fail - closed in both directions for services AND events : a
* rendered key / scope must be mapped to a page , a mapped key / scope must still
* render , and — the backstop — a DECLARED key / event the projection cannot see
2026-08-09 10:29:13 +08:00
* must carry a named walk exemption ( a rendered one must not ) . A third
* direction guards the scan itself : everything rendered must also be declared
* to the scan , so a scan blind spot cannot decay silently . Pure so the
2026-08-09 02:39:12 +08:00
* acceptance paths are provable without running the projection .
2026-07-24 19:54:25 +08:00
* @param input - rendered API plus the declared - key / event scans .
2026-08-09 02:39:12 +08:00
* @param maps - the curated page maps and walk exemptions .
* @returns one message per violation , empty when the partition holds .
* /
export function walkPartitionProblems ( input : WalkPartitionInput , maps : WalkPartitionMaps ) : string [ ] {
const problems : string [ ] = [ ]
for ( const [ key , source ] of input . renderedKeys ) {
if ( ! Object . hasOwn ( maps . servicePage , key ) ) problems . push ( ` service ctx. ${ key } ( ${ source } ) has no SERVICE_PAGE entry; every service maps to exactly one subsystems page. ` )
}
for ( const scope of [ . . . input . renderedScopes ] . sort ( ) ) {
if ( ! Object . hasOwn ( maps . eventScopePage , scope ) ) problems . push ( ` event scope ' ${ scope } /*' has no EVENT_SCOPE_PAGE entry; every event scope maps to exactly one subsystems page. ` )
}
for ( const key of Object . keys ( maps . servicePage ) ) {
if ( ! input . renderedKeys . has ( key ) ) problems . push ( ` SERVICE_PAGE maps 'ctx. ${ key } ' but the projection discovers no such service; remove the stale entry. ` )
}
for ( const scope of Object . keys ( maps . eventScopePage ) ) {
if ( ! input . renderedScopes . has ( scope ) ) problems . push ( ` EVENT_SCOPE_PAGE maps ' ${ scope } /*' but the projection discovers no such scope; remove the stale entry. ` )
}
// The rendering projection only sees a Context key it can resolve to a
// documented service class. The independent scan reads EVERY Context merge
// so a key the projection cannot render must either be rendered (mapped) or
// carry a named SERVICE_WALK_EXEMPTIONS reason — never vanish silently.
for ( const [ key , rel ] of input . declaredKeys ) {
const rendered = input . renderedKeys . has ( key )
const exempt = Object . hasOwn ( maps . serviceWalkExemptions , key )
if ( ! rendered && ! exempt ) {
problems . push ( ` ctx. ${ key } ( ${ rel } ) is declared in a Context merge but invisible to the rendering projection; map it in SERVICE_PAGE (after making it renderable) or name it in SERVICE_WALK_EXEMPTIONS with its documentation owner. ` )
}
if ( rendered && exempt ) problems . push ( ` ctx. ${ key } is rendered by the projection but still listed in SERVICE_WALK_EXEMPTIONS; remove the stale exemption. ` )
}
for ( const key of Object . keys ( maps . serviceWalkExemptions ) ) {
if ( ! input . declaredKeys . has ( key ) ) problems . push ( ` SERVICE_WALK_EXEMPTIONS names 'ctx. ${ key } ' but no Context merge declares it; remove the stale exemption. ` )
}
// The event mirror of the service backstop: the projection walks only files
// reachable from host-face package exports, so a client-face or unreachable
// Events merge would otherwise vanish without a trace.
for ( const [ name , rel ] of input . declaredEvents ) {
const rendered = input . renderedEventNames . has ( name )
const exempt = Object . hasOwn ( maps . eventWalkExemptions , name )
if ( ! rendered && ! exempt ) {
problems . push ( ` event ' ${ name } ' ( ${ rel } ) is declared in an Events merge but invisible to the rendering projection; make it renderable (mapped via EVENT_SCOPE_PAGE) or name it in EVENT_WALK_EXEMPTIONS with its documentation owner. ` )
}
if ( rendered && exempt ) problems . push ( ` event ' ${ name } ' is rendered by the projection but still listed in EVENT_WALK_EXEMPTIONS; remove the stale exemption. ` )
}
for ( const name of Object . keys ( maps . eventWalkExemptions ) ) {
if ( ! input . declaredEvents . has ( name ) ) problems . push ( ` EVENT_WALK_EXEMPTIONS names ' ${ name } ' but no Events merge declares it; remove the stale exemption. ` )
}
2026-08-09 10:29:13 +08:00
// Self-check the scan itself: everything the projection renders is declared
// in a Context/Events merge the scan must also reach, so a rendered key or
// event the scan cannot see means the SCAN regressed (glob, prefilter, or
// block walk) — a partial blind spot that exemption staleness alone would
2026-07-24 19:54:25 +08:00
// never appear.
2026-08-09 10:29:13 +08:00
for ( const key of input . renderedKeys . keys ( ) ) {
if ( ! input . declaredKeys . has ( key ) ) problems . push ( ` ctx. ${ key } is rendered by the projection but the independent scan finds no Context merge declaring it; the scan has a blind spot (glob, prefilter, or module-block walk) — fix the scan, not the maps. ` )
}
for ( const name of input . renderedEventNames ) {
if ( ! input . declaredEvents . has ( name ) ) problems . push ( ` event ' ${ name } ' is rendered by the projection but the independent scan finds no Events merge declaring it; the scan has a blind spot (glob, prefilter, or module-block walk) — fix the scan, not the maps. ` )
}
2026-08-09 02:39:12 +08:00
return problems
}
2026-07-30 21:40:58 +08:00
/ * *
* Compute every generated artifact : the inherited - tier page , the model - facing
* runtime API module , plus , per mapped subsystems page , the pair ' s two updated
* documents with the injected region . Fail - loud partition checks live here : an
* unmapped service / event scope , a mapping whose page file does not exist , a
2026-08-09 02:39:12 +08:00
* curated entry whose key / scope the projection no longer discovers , a declared
* Context key or Events member the projection cannot see without a named walk
* exemption , and a mapped page missing its markers are all aggregated errors .
2026-07-30 21:40:58 +08:00
* @returns ` [repo-relative path, exact content] ` for every generated artifact .
* /
export function computeOutputs ( ) : [ string , string ] [ ] {
2026-07-28 23:48:35 +08:00
const { projector , model } = projectCordisCatalog ( root , CORDIS_CATALOG_POLICY )
2026-07-30 21:40:58 +08:00
const services = [ . . . model . services ]
const events = [ . . . model . events ]
const declaredKeys = new Map < string , string > ( )
2026-08-09 02:39:12 +08:00
const declaredEvents = new Map < string , string > ( )
2026-08-09 10:29:13 +08:00
for ( const { rel , sf , body } of contextMergeFiles ( root , [ 'packages/*/*/src/**/*.ts' , 'packages/*/*/src/**/*.tsx' ] ) ) {
2026-07-30 21:40:58 +08:00
for ( const key of contextKeyMap ( body , sf ) . keys ( ) ) {
if ( ! declaredKeys . has ( key ) ) declaredKeys . set ( key , rel )
}
2026-08-09 02:39:12 +08:00
for ( const name of eventNameList ( body , sf ) ) {
if ( ! declaredEvents . has ( name ) ) declaredEvents . set ( name , rel )
2026-07-30 21:40:58 +08:00
}
}
2026-08-09 02:39:12 +08:00
const problems = walkPartitionProblems ( {
renderedKeys : new Map ( services . map ( s = > [ s . key , s . source ] ) ) ,
renderedScopes : new Set ( events . map ( e = > e . scope ) ) ,
renderedEventNames : new Set ( events . map ( e = > e . name ) ) ,
declaredKeys ,
declaredEvents ,
} , {
servicePage : SERVICE_PAGE ,
serviceWalkExemptions : SERVICE_WALK_EXEMPTIONS ,
eventScopePage : EVENT_SCOPE_PAGE ,
eventWalkExemptions : EVENT_WALK_EXEMPTIONS ,
} )
2026-07-30 21:40:58 +08:00
if ( problems . length > 0 ) throw new Error ( ` gen-cordis-catalog: ${ problems . length } partition violation(s): \ n ${ problems . map ( p = > ` ${ p } ` ) . join ( '\n' ) } ` )
const pages = [ . . . new Set ( [ . . . Object . values ( SERVICE_PAGE ) , . . . Object . values ( EVENT_SCOPE_PAGE ) ] ) ] . sort ( )
Split the cordis catalog into separate events and services documents
gen-cordis-catalog.ts now emits docs/cordis-catalog/events.md and
docs/cordis-catalog/services.md instead of the combined
events-and-services.md: a reader is either finding what to listen to or
what to call, and each axis now scans and deep-links as its own page.
Headings promote one level (scopes and ctx.<key> entries become H2), the
dispatch-mode legend lives on the events page, and the inherited tier
splits accordingly. --check verifies both files and names whichever is
stale.
Every reference updated in the same change (no compat redirects,
pre-release stance): architecture.md, AGENTS.md, docs/AGENTS.md tier row,
filesystem/subagent core-data-structures pages (the ctx.fs anchor
survives — slugs are heading-level-independent), fs README, four RFCs,
the tool-catalog and persistence-catalog generator intros (both
regenerated), and the bilingual development.md pair (re-recorded).
2026-07-05 00:45:06 +08:00
const outputs : [ string , string ] [ ] = [
2026-07-30 21:40:58 +08:00
[ OUT_INHERITED , renderInheritedPage ( CORDIS_CATALOG_POLICY ) ] ,
2026-07-28 23:48:35 +08:00
[ OUT_RUNTIME_API , projector . renderRuntimeApi ( model ) ] ,
2026-07-30 21:40:58 +08:00
]
for ( const page of pages ) {
const region = renderPageRegion (
page ,
services . filter ( s = > SERVICE_PAGE [ s . key ] === page ) ,
events . filter ( e = > EVENT_SCOPE_PAGE [ e . scope ] === page ) ,
CORDIS_CATALOG_POLICY ,
)
for ( const side of [ page , page . replace ( /\.md$/ , '.zh.md' ) ] ) {
const rel = ` ${ SUBSYSTEMS_DIR } / ${ side } `
2026-08-18 19:00:37 +08:00
const localizedRegion = localizePageRegion ( region , rel )
2026-07-30 21:40:58 +08:00
let current : string
try {
current = readFileSync ( resolve ( root , rel ) , 'utf8' )
} catch {
// Both pair sides must exist before a region can be injected; the
// pairing gate owns pair completeness, this generator names the miss.
problems . push ( ` ${ rel } : mapped subsystems page does not exist. ` )
continue
}
try {
2026-08-18 19:00:37 +08:00
outputs . push ( [ rel , spliceRegion ( current , localizedRegion ) ] )
2026-07-30 21:40:58 +08:00
} catch ( error ) {
problems . push ( ` ${ rel } : ${ error instanceof Error ? error.message : String ( error ) } ` )
}
}
}
if ( problems . length > 0 ) throw new Error ( ` gen-cordis-catalog: ${ problems . length } page violation(s): \ n ${ problems . map ( p = > ` ${ p } ` ) . join ( '\n' ) } ` )
return outputs
}
/ * *
* Re - record a pair ' s ` .i18n.yaml ` after a region write ONLY when the write is
* region - confined : both sides ' region - stripped content must be byte - equal to
* the region - stripped previous content whose hashes the record holds . The
* caller supplies the previous bytes ( read before writing ) ; human - content
* drift leaves the record untouched so the pairing gate still demands the
* normal translation flow .
* @param pageRel - repo - relative English page path ( ` docs/subsystems/x.md ` ) .
* @param before - pre - write bytes per repo - relative path .
* @param scanRoot - repository root override for tests .
* @returns true when the record was refreshed .
* /
export function maybeRecordPair ( pageRel : string , before : Map < string , Buffer > , scanRoot : string = root ) : boolean {
const zhRel = pageRel . replace ( /\.md$/ , '.zh.md' )
const metaRel = pageRel . replace ( /\.md$/ , '.i18n.yaml' )
const metaAbs = resolve ( scanRoot , metaRel )
let meta : string
try {
meta = readFileSync ( metaAbs , 'utf8' )
} catch {
// No record yet: a brand-new pair is recorded by the author's --write
// after review, never silently by regeneration.
return false
}
2026-08-09 15:27:21 +08:00
// The record must contain exactly the two valid entries for THIS pair;
2026-07-30 21:40:58 +08:00
// a malformed or renamed-key sidecar is the pairing gate's problem to
// report, never something regeneration silently repairs into validity.
const recorded = parsePairMeta ( meta )
const names = [ pageRel , zhRel ] . map ( rel = > rel . split ( '/' ) . at ( - 1 ) ? ? rel )
if ( ! recorded || recorded . size !== 2 || ! names . every ( name = > recorded . has ( name ) ) ) return false
for ( const rel of [ pageRel , zhRel ] ) {
const previous = before . get ( rel )
if ( ! previous ) return false
if ( recorded . get ( rel . split ( '/' ) . at ( - 1 ) ? ? rel ) !== blobHash ( previous ) ) return false
const current = readFileSync ( resolve ( scanRoot , rel ) )
const strippedBefore = partitionGeneratedRegions ( previous . toString ( 'utf8' ) ) . stripped
const strippedAfter = partitionGeneratedRegions ( current . toString ( 'utf8' ) ) . stripped
if ( strippedBefore !== strippedAfter ) return false
}
const source = readFileSync ( resolve ( scanRoot , pageRel ) )
const zh = readFileSync ( resolve ( scanRoot , zhRel ) )
writeFileSync ( metaAbs , renderPairMeta ( pageRel , blobHash ( source ) , zhRel , blobHash ( zh ) ) )
return true
}
/ * * C L I e n t r y : d e f a u l t r e g e n e r a t e s e v e r y a r t i f a c t , ` - - c h e c k ` f a i l s i f a n y i s
* stale . Guarded behind an entry - point check so importing this module for
* tests neither regenerates the committed files nor calls process . exit .
* @returns nothing ; writes files or reports freshness through the process .
* /
export function main ( ) : void {
const outputs : [ string , string ] [ ] = [
. . . computeOutputs ( ) ,
2026-07-20 16:32:08 +08:00
. . . renderCordisCoreApiPages ( ) ,
Split the cordis catalog into separate events and services documents
gen-cordis-catalog.ts now emits docs/cordis-catalog/events.md and
docs/cordis-catalog/services.md instead of the combined
events-and-services.md: a reader is either finding what to listen to or
what to call, and each axis now scans and deep-links as its own page.
Headings promote one level (scopes and ctx.<key> entries become H2), the
dispatch-mode legend lives on the events page, and the inherited tier
splits accordingly. --check verifies both files and names whichever is
stale.
Every reference updated in the same change (no compat redirects,
pre-release stance): architecture.md, AGENTS.md, docs/AGENTS.md tier row,
filesystem/subagent core-data-structures pages (the ctx.fs anchor
survives — slugs are heading-level-independent), fs README, four RFCs,
the tool-catalog and persistence-catalog generator intros (both
regenerated), and the bilingual development.md pair (re-recorded).
2026-07-05 00:45:06 +08:00
]
2026-06-20 19:47:09 +08:00
if ( process . argv . includes ( '--check' ) ) {
Split the cordis catalog into separate events and services documents
gen-cordis-catalog.ts now emits docs/cordis-catalog/events.md and
docs/cordis-catalog/services.md instead of the combined
events-and-services.md: a reader is either finding what to listen to or
what to call, and each axis now scans and deep-links as its own page.
Headings promote one level (scopes and ctx.<key> entries become H2), the
dispatch-mode legend lives on the events page, and the inherited tier
splits accordingly. --check verifies both files and names whichever is
stale.
Every reference updated in the same change (no compat redirects,
pre-release stance): architecture.md, AGENTS.md, docs/AGENTS.md tier row,
filesystem/subagent core-data-structures pages (the ctx.fs anchor
survives — slugs are heading-level-independent), fs README, four RFCs,
the tool-catalog and persistence-catalog generator intros (both
regenerated), and the bilingual development.md pair (re-recorded).
2026-07-05 00:45:06 +08:00
const stale : string [ ] = [ ]
for ( const [ out , content ] of outputs ) {
let committed : string | null = null
try {
committed = readFileSync ( resolve ( root , out ) , 'utf8' )
} catch {
2026-07-30 21:40:58 +08:00
// Only ENOENT (not yet generated) is expected; a present-but-unreadable
// file is not a state this repo produces. Either way the remedy is the
// same — regenerate — so treat a read failure as "stale".
Split the cordis catalog into separate events and services documents
gen-cordis-catalog.ts now emits docs/cordis-catalog/events.md and
docs/cordis-catalog/services.md instead of the combined
events-and-services.md: a reader is either finding what to listen to or
what to call, and each axis now scans and deep-links as its own page.
Headings promote one level (scopes and ctx.<key> entries become H2), the
dispatch-mode legend lives on the events page, and the inherited tier
splits accordingly. --check verifies both files and names whichever is
stale.
Every reference updated in the same change (no compat redirects,
pre-release stance): architecture.md, AGENTS.md, docs/AGENTS.md tier row,
filesystem/subagent core-data-structures pages (the ctx.fs anchor
survives — slugs are heading-level-independent), fs README, four RFCs,
the tool-catalog and persistence-catalog generator intros (both
regenerated), and the bilingual development.md pair (re-recorded).
2026-07-05 00:45:06 +08:00
committed = null
}
if ( committed !== content ) stale . push ( out )
2026-06-20 19:47:09 +08:00
}
Split the cordis catalog into separate events and services documents
gen-cordis-catalog.ts now emits docs/cordis-catalog/events.md and
docs/cordis-catalog/services.md instead of the combined
events-and-services.md: a reader is either finding what to listen to or
what to call, and each axis now scans and deep-links as its own page.
Headings promote one level (scopes and ctx.<key> entries become H2), the
dispatch-mode legend lives on the events page, and the inherited tier
splits accordingly. --check verifies both files and names whichever is
stale.
Every reference updated in the same change (no compat redirects,
pre-release stance): architecture.md, AGENTS.md, docs/AGENTS.md tier row,
filesystem/subagent core-data-structures pages (the ctx.fs anchor
survives — slugs are heading-level-independent), fs README, four RFCs,
the tool-catalog and persistence-catalog generator intros (both
regenerated), and the bilingual development.md pair (re-recorded).
2026-07-05 00:45:06 +08:00
if ( stale . length === 0 ) {
2026-07-30 21:40:58 +08:00
console . log ( ` gen-cordis-catalog: ${ outputs . length } generated file(s)/region(s) are up to date. ` )
2026-06-20 19:47:09 +08:00
process . exit ( 0 )
}
2026-07-30 21:40:58 +08:00
console . error ( ` gen-cordis-catalog: stale — ${ stale . join ( ', ' ) } . Run \` pnpm run gen-cordis-catalog \` and commit the result. ` )
2026-06-20 19:47:09 +08:00
process . exit ( 1 )
}
2026-07-30 21:40:58 +08:00
const before = new Map < string , Buffer > ( )
for ( const [ out ] of outputs ) {
try {
before . set ( out , readFileSync ( resolve ( root , out ) ) )
} catch {
// First generation of this artifact; nothing to guard, nothing to record.
}
}
let changedPages = 0
let recorded = 0
2026-07-20 16:32:08 +08:00
for ( const [ out , content ] of outputs ) {
const destination = resolve ( root , out )
2026-07-30 21:40:58 +08:00
if ( before . get ( out ) ? . toString ( 'utf8' ) === content ) continue
2026-07-20 16:32:08 +08:00
mkdirSync ( dirname ( destination ) , { recursive : true } )
writeFileSync ( destination , content )
2026-07-30 21:40:58 +08:00
changedPages ++
2026-07-20 16:32:08 +08:00
}
2026-07-30 21:40:58 +08:00
for ( const page of [ . . . new Set ( [ . . . Object . values ( SERVICE_PAGE ) , . . . Object . values ( EVENT_SCOPE_PAGE ) ] ) ] ) {
const rel = ` ${ SUBSYSTEMS_DIR } / ${ page } `
const zhRel = rel . replace ( /\.md$/ , '.zh.md' )
const wroteEither = [ rel , zhRel ] . some ( ( side ) = > {
const previous = before . get ( side )
return previous !== undefined && previous . toString ( 'utf8' ) !== readFileSync ( resolve ( root , side ) , 'utf8' )
} )
if ( wroteEither && maybeRecordPair ( rel , before ) ) recorded ++
}
console . log ( ` gen-cordis-catalog: ${ outputs . length } artifact(s) computed, ${ changedPages } written, ${ recorded } pair record(s) refreshed. ` )
2026-06-20 19:47:09 +08:00
}
2026-07-30 21:40:58 +08:00
if ( process . argv [ 1 ] && import . meta . filename === resolve ( process . argv [ 1 ] ) ) {
main ( )
}