dsh-env-manager-plugin 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +273 -250
  3. package/client.js +2581 -2581
  4. package/cordis.patch.yml +4 -4
  5. package/docs/00-project/current-state.md +32 -6
  6. package/docs/00-project/glossary.md +215 -215
  7. package/docs/00-project/project-brief.md +114 -114
  8. package/docs/00-project/roadmap.md +89 -89
  9. package/docs/01-architecture/DSH_Environment_Service_Memory_Skill_Brainstorm.md +493 -493
  10. package/docs/01-architecture/api-verification.md +416 -416
  11. package/docs/01-architecture/architecture-overview.md +222 -222
  12. package/docs/01-architecture/data-model.md +930 -930
  13. package/docs/01-architecture/decisions/ADR-0001-plugin-as-bundle.md +29 -29
  14. package/docs/01-architecture/decisions/ADR-0002-file-based-storage.md +25 -25
  15. package/docs/01-architecture/decisions/ADR-0003-skill-router-over-search.md +25 -25
  16. package/docs/01-architecture/decisions/ADR-0004-typescript-host-js-client.md +31 -31
  17. package/docs/01-architecture/decisions/ADR-0005-layered-rule-resolution.md +36 -36
  18. package/docs/01-architecture/decisions/ADR-0006-credentials-as-references.md +30 -30
  19. package/docs/01-architecture/decisions/ADR-0007-session-scoped-default-environment.md +28 -28
  20. package/docs/01-architecture/decisions/ADR-0008-memory-bridge-not-second-store.md +31 -31
  21. package/docs/01-architecture/decisions/ADR-0009-client-host-http-transport.md +87 -87
  22. package/docs/01-architecture/decisions/ADR-0010-environment-plane-model.md +87 -87
  23. package/docs/01-architecture/decisions/ADR-0011-k8s-node-execution-model.md +92 -92
  24. package/docs/01-architecture/design.md +666 -666
  25. package/docs/01-architecture/detailed-design.md +961 -961
  26. package/docs/01-architecture/implementation-plan.md +323 -323
  27. package/docs/01-architecture/module-boundaries.md +306 -306
  28. package/docs/01-architecture/security-model.md +227 -227
  29. package/docs/01-architecture/storage-contract.md +224 -224
  30. package/docs/01-architecture/tool-contract.md +438 -438
  31. package/docs/01-architecture/ui-design.md +587 -587
  32. package/docs/02-harness/agent-behavior.md +36 -36
  33. package/docs/02-harness/change-snapshot-policy.md +29 -29
  34. package/docs/02-harness/client-ui-integration.md +110 -110
  35. package/docs/02-harness/context-policy.md +43 -43
  36. package/docs/02-harness/harness-standard.md +91 -91
  37. package/docs/02-harness/task-execution-policy.md +26 -26
  38. package/docs/02-harness/verification-policy.md +97 -97
  39. package/docs/03-openspec/archive-rule.md +25 -25
  40. package/docs/03-openspec/bugfix-rule.md +30 -30
  41. package/docs/03-openspec/design-rule.md +33 -33
  42. package/docs/03-openspec/openspec-usage-guide.md +31 -31
  43. package/docs/03-openspec/proposal-rule.md +70 -70
  44. package/docs/03-openspec/spec-rule.md +33 -33
  45. package/docs/03-openspec/tasks-rule.md +31 -31
  46. package/docs/04-development/coding-style.md +67 -67
  47. package/docs/04-development/dependency-policy.md +43 -43
  48. package/docs/04-development/development-guide.md +85 -85
  49. package/docs/04-development/directory-convention.md +105 -105
  50. package/docs/04-development/local-run-guide.md +61 -55
  51. package/docs/04-development/testing-guide.md +102 -102
  52. package/docs/05-domain/deployment-template.md +76 -76
  53. package/docs/05-domain/environment-template.md +165 -165
  54. package/docs/05-domain/operation-binding-template.md +55 -55
  55. package/docs/05-domain/operation-record-template.md +55 -55
  56. package/docs/05-domain/prompt-template.md +90 -90
  57. package/docs/05-domain/rule-template.md +73 -73
  58. package/docs/05-domain/service-template.md +39 -39
  59. package/docs/06-operations/deployment-guide.md +54 -54
  60. package/docs/06-operations/release.md +198 -188
  61. package/docs/06-operations/security-policy.md +53 -53
  62. package/docs/06-operations/troubleshooting.md +141 -91
  63. package/docs/07-templates/adr-template.md +21 -21
  64. package/docs/07-templates/bugfix-template.md +11 -11
  65. package/docs/07-templates/case-template.md +15 -15
  66. package/docs/07-templates/design-template.md +29 -29
  67. package/docs/07-templates/proposal-template.md +19 -19
  68. package/docs/07-templates/spec-template.md +15 -15
  69. package/docs/07-templates/tasks-template.md +12 -12
  70. package/docs/99-archive/README.md +13 -13
  71. package/docs/README.md +142 -142
  72. package/icon.svg +4 -4
  73. package/index.js +31 -8
  74. package/lib/skill.js +37 -37
  75. package/locale/en.json +6 -6
  76. package/locale/zh.json +6 -6
  77. package/package.json +101 -99
  78. package/scripts/repair-profile.mjs +141 -0
@@ -1,416 +1,416 @@
1
- # API Verification
2
-
3
- > Status: verified against the DSH runtime shipped as `DeepSeek Harness 0.2.1-rc.5`.
4
- > Method: `cordis_inspect_*` providers plus package documentation and bundled source
5
- > extracted from the installed `app.asar`. This document records what the V1 design
6
- > may rely on, and what it must verify again at implementation time.
7
-
8
- ## 1. Purpose
9
-
10
- The first design draft left eight open questions about DSH integration. This document
11
- answers them from the runtime itself so that `detailed-design.md`,
12
- `tool-contract.md`, `storage-contract.md` and `ui-design.md` describe APIs that exist,
13
- not APIs that are assumed.
14
-
15
- Every finding has an ID (`F1`…`F21`). The design documents reference these IDs.
16
-
17
- ## 2. Findings
18
-
19
- ### F1 — Host plugin entry
20
-
21
- A Host package is an ES module exporting any of:
22
-
23
- ```ts
24
- export const name = 'dsh-env-manager'
25
- export const inject = ['tools']
26
- export const Config = z.object({ dataDir: z.string() })
27
- export function apply(ctx, config) { /* ... */ }
28
- ```
29
-
30
- Evidence: `@deepseek-ai/dsh-shell-env` (`lib/index.js`) and `@deepseek-ai/dsh-tool-todo`.
31
- `Config` is a `@deepseek-ai/schemastery` schema, not Zod. `inject` is a plain string
32
- array. The loader supplies the parsed config as the second `apply` argument.
33
-
34
- Design consequence: the Host entry keeps `apply(ctx, config)` and declares `inject`
35
- explicitly; `Config` is authored with schemastery, while record validation uses Zod.
36
-
37
- ### F2 — Tool registration
38
-
39
- `ctx.tools.register(definition)` registers globally or in the calling Agent scope and
40
- returns an exact disposer. Duplicate names within one layer throw.
41
-
42
- Tool authors should use `defineTool` from `@deepseek-ai/dsh-tools`:
43
-
44
- ```ts
45
- ctx.tools.register(defineTool({
46
- name: 'env_list',
47
- description: '...',
48
- parameters: { kind: { type: 'string', enum: [...] } },
49
- output: { schema: { ... }, render: (args, value) => [{ type: 'text', text: '...' }] },
50
- async execute(args, exec) { /* ... */ },
51
- }))
52
- ```
53
-
54
- Evidence: `dsh-tools` README §"Register a tool"; `dsh-tool-todo` source.
55
-
56
- Design consequence: all ten V1 tools are `defineTool` definitions, registered inside
57
- `apply` and disposed with the plugin fiber.
58
-
59
- ### F3 — Parameter schema DSL
60
-
61
- `defineTool.parameters` is DSH's own value-schema DSL, not raw JSON Schema. Supported
62
- node types: `string`, `number`, `integer`, `boolean`, `null`, `array`, `object`,
63
- author-only `json`, and exact-one `oneOf`. Object property `required: true` marks
64
- membership; `additionalProperties` must be explicitly `true` or `false`.
65
-
66
- Model arguments are validated before `execute` runs; invalid input becomes a normal
67
- error result (`ToolArgsError`).
68
-
69
- Design consequence: tool parameter tables in `tool-contract.md` use this DSL verbatim.
70
-
71
- ### F4 — Tool output contract
72
-
73
- `output.schema` is a `JsonSchemaNode` restricted to:
74
- `type`, `oneOf`, `properties`, `required`, `additionalProperties`, `items`, `enum`,
75
- `const`, plus annotations (`description`, `title`, `default`, `examples`).
76
- An **annotation-only schema** (`{ description: '…' }`) is the supported way to declare
77
- unconstrained JSON.
78
-
79
- The registry validates the returned value against `output.schema` after execution and
80
- throws `ToolOutputError` on mismatch. `render(args, value)` returns `ContentBlock[]`;
81
- one `{ type: 'text', text }` block is enough for a JSON-shaped tool.
82
-
83
- Evidence: `dsh-tools` `assertSupportedJsonSchema`, `checkSchemaNode`,
84
- `validateJsonSchemaValue`, and the output check at dispatch.
85
-
86
- Design consequence: every tool output is a precise object schema where practical, and
87
- uses `additionalProperties: true` for open record payloads. `render` emits a short
88
- human summary, because the canonical value is what the model and programmatic callers
89
- receive.
90
-
91
- ### F5 — Structured tool errors
92
-
93
- Only a thrown `HarnessError` produces structured `{ name, code }` failure info;
94
- plain `Error` produces a message only. Constructor:
95
-
96
- ```ts
97
- new HarnessError(message, code, options?)
98
- ```
99
-
100
- imported from `@deepseek-ai/dsh-llm`. `name` defaults to the subclass constructor name.
101
-
102
- Evidence: `dsh-tools` `errorInfo()` and `@deepseek-ai/dsh-llm` `HarnessError`.
103
-
104
- Design consequence: all expected failures throw a single `EnvManagerError extends
105
- HarnessError` carrying a stable code from the diagnostics catalogue. Success values
106
- carry no `ok` flag; failure is the error path.
107
-
108
- ### F6 — Tool execution context
109
-
110
- `execute(args, exec)` receives `ToolRunContext` extending `ToolExecution`:
111
-
112
- | Member | Use |
113
- |---|---|
114
- | `exec.signal` | cooperative cancellation; must be observed |
115
- | `exec.callId`, `exec.rootCallId` | correlation ids |
116
- | `exec.agent` | optional `Agent`; `exec.agent.session.header.id` is the session id |
117
- | `exec.deferContext(msg)` | attach extra model context |
118
- | `exec.concludeTurn()` | end the turn (not used in V1) |
119
-
120
- Evidence: Service contract `tools`, referenced type `ToolRunContext`; `dsh-shell-env`
121
- reads `execution.agent.session.header.id`.
122
-
123
- Design consequence: session-scoped tools require `exec.agent`; without it they fail
124
- with `SESSION_CONTEXT_UNAVAILABLE` instead of guessing a session.
125
-
126
- ### F7 — Approval
127
-
128
- `ctx.approval.request(req)` returns
129
- `'allowed-once' | 'rejected' | 'cancelled' | 'unavailable'`.
130
- Only `'allowed-once'` is a grant; every other outcome must be treated as denial.
131
- The request needs `agent`, `toolName`, optional `callId`, `reason`, `displayReason`
132
- (localized), and a `signal`. It requires an open turn.
133
-
134
- Evidence: Service contract `approval`.
135
-
136
- Design consequence: `env_delete` and production `env_save` call
137
- `ctx.approval.request` with a localized `displayReason`, and fail closed on any
138
- non-grant. Approval is per call; nothing is cached.
139
-
140
- ### F8 — Credentials
141
-
142
- `ctx.credentials.describe(ref)` returns `{ configured, source?, writable }` without
143
- the value. `set(ref, value)` writes to a provider-managed writable source and
144
- `unset(ref)` removes it. `resolve(ref)` returns the value and must not be called by this
145
- plugin. `CredentialRef` is a branded string; at runtime an ordinary string is accepted.
146
-
147
- Evidence: Service contract `credentials`; live `POST /env-manager/api` with
148
- `op: "credential"` returned `{ ref, configured: true }`, and `op: "credentialUnset"`
149
- returned `{ ref, removed: true }`. The panel used the same path to create an environment,
150
- then the record was read back and contained only the ref.
151
-
152
- Design consequence: `env_resolve` reports each `credentialRef` with
153
- `configured`/`source` only. Credential values may appear in the request body of
154
- `credential`, but never in a tool result, HTTP response, log line, diagnostic or registry
155
- file.
156
-
157
- ### F9 — Filesystem capability gap
158
-
159
- `ctx.fs` exposes `resolve`, `stat`, `listDir`, `readText`, `writeText` (atomic),
160
- `editText`, `readBytes`, `readByteRange`, `watch`, `contains` — but **no** `mkdir`,
161
- `unlink`, `rename`, or `copy`. `resolve` follows real paths, so it cannot target a
162
- file whose parent directory does not exist yet.
163
-
164
- Evidence: Service contract `fs`.
165
-
166
- Design consequence: registry storage uses `node:fs/promises` directly, confined to
167
- the resolved `dataDir`. The plugin is trusted Host code, not a sandboxed tool. Every
168
- path is built from a validated id and checked with `path.resolve` containment; no
169
- model-supplied path reaches the filesystem.
170
-
171
- ### F10 — Data directory
172
-
173
- `ctx.get('profileContext')` exposes at least `name` and `dir` at runtime
174
- (`dsh-shell-env` reads both), although the inspect contract lists only four other
175
- members. `dir` is the profile directory that owns `node_modules`, patches and data.
176
-
177
- Evidence: `dsh-shell-env` `collect()`; Service contract `profileContext`.
178
-
179
- Design consequence: `dataDir` resolution order:
180
-
181
- 1. `config.dataDir` (absolute, or relative to the profile directory)
182
- 2. `<profileContext.dir>/data/dsh-env-manager`
183
- 3. `$DSH_PROFILE_DIR/data/dsh-env-manager`
184
- 4. `<cwd>/data`
185
-
186
- The resolved path is logged once and exposed read-only through `env_get_context` and
187
- the UI overview. Task T1 verifies `profileContext.dir` on a live profile.
188
-
189
- ### F11 — Session identity and state
190
-
191
- `exec.agent.session` is a live `Session` with `header.id`, `append(type, data)` and
192
- `snapshotEvents()`. The session id is available for every agent-initiated tool call.
193
-
194
- There is a `sessionProjections` service (used by `todo_write`) for replayable plugin
195
- state, but it is designed around declared session event types and projection schemas;
196
- `SessionEventMap` is a closed TypeScript interface.
197
-
198
- Design consequence: V1 stores the default environment in
199
- `<dataDir>/sessions/<sessionId>.json` (small, explicit, testable, no session-log
200
- schema changes). A projection-based implementation is recorded as a P2 alternative.
201
-
202
- ### F12 — HTTP carrier
203
-
204
- `ctx.webServer.register({ kind: 'prefix' | 'exact', path, handler })` registers a
205
- route and returns a disposer; duplicate `(kind, path)` throws.
206
- `getOrigin()` returns the local origin. `tapIndex(transform)` can inject markup.
207
-
208
- Evidence: Service contract `webServer`.
209
-
210
- Design consequence: the Client panel talks to the Host through
211
- `POST /env-manager/api` registered by this plugin, authenticated by a required custom
212
- header plus an `Origin` check. See ADR-0009 and `ui-design.md` §4.
213
-
214
- ### F13 — Client module
215
-
216
- A bundle's `client.js` is served as `dsh.client`'s `./client` export and loaded by
217
- `window.__ModuleLoader__.load({ id, factory(require) { ... } })`. The factory may
218
- `require('react')`. The returned plugin declares `inject` services (`slots`,
219
- `locale`, …) and `apply(ctx)`.
220
-
221
- The dynamic-Cordis builtins (`React`, `host.call`, `styles`, `console`) belong to
222
- runtime-defined packages, **not** to installed bundle client halves.
223
-
224
- Evidence: `client.js` skeleton; `Slots`/`Service` client catalog; `dsh-cordis-client-runner`.
225
-
226
- Design consequence: the Client uses `slots` and `locale` only, plus browser `fetch`
227
- for the plugin's own HTTP route.
228
-
229
- ### F14 — Client-to-Host Remote is not usable here
230
-
231
- DSH's native Client↔Host RPC is the Typert Remote system:
232
- `ctx.remote.$mount()` mounts a **generated** Host-for-Client contribution; strict
233
- descriptors come from `@Remote`/`@RemoteScope` decorators processed by the build-time
234
- Typert loader into `ctx.typert.local` (`/remote` artifacts imported by the Client).
235
- Client-supplied fields without strict generated codecs fail before methods become
236
- callable. SRC mode is a development fallback for endpoints without a strict
237
- definition and does not give a standalone bundle a typed namespace.
238
-
239
- Evidence: `dsh-api-gateway` README (Host and Client sections), `dsh-api-remotes`
240
- README ("capability set is fixed by explicit build-time value imports"), client
241
- Service catalog (no `remote` service until the gateway Client face mounts one).
242
-
243
- Design consequence: a standalone plugin bundle cannot add a Remote namespace without
244
- adopting DSH's build toolchain. V1 therefore uses the plugin-owned HTTP route
245
- (ADR-0009), which is same-origin, token-free, header-gated, and reuses the exact same
246
- application handlers as the tools.
247
-
248
- ### F15 — Slots
249
-
250
- Live topology confirms:
251
-
252
- | Slot | Kind | Registration |
253
- |---|---|---|
254
- | `sidebar.panellist` | list | `{ id, order?, label? }` — one sidebar icon |
255
- | `main` | keyed | `{ key }` — the selected panel body |
256
- | `plugins.bundle.config` | keyed | `{ key: '<package name>' }`, `view: 'page'` only |
257
- | `conversation.composer.dock` | list | `{ id, order?, label? }` — ambient composer entry |
258
-
259
- `ctx.slots.inject(slotName, () => ctx.slots.register({ name, ... }, Component))` waits
260
- for the slot to exist and disposes with the fiber.
261
-
262
- Design consequence: the manager registers a `sidebar.panellist` icon
263
- (id `env-manager`), a `main` panel keyed `env-manager`, and optionally a bundle
264
- config page. The composer dock is not used for the main UI.
265
-
266
- ### F16 — Client services
267
-
268
- Available to the Client half: `layout`, `locale`, `sessions`, `slots`, `theme`,
269
- `timer`, `uiWorkspace`, `workspaces`.
270
-
271
- - `ctx.locale.register(ns, { en, zh })` and `ctx.locale.bind(ns)` provide copy.
272
- - `ctx.theme` owns theme selection; the plugin only consumes CSS variables.
273
- - `ctx.layout.selectPanel(id)` can select the panel programmatically.
274
-
275
- Design consequence: UI copy is registered as namespace `envManager`; no string is
276
- hardcoded twice.
277
-
278
- ### F17 — Theme tokens
279
-
280
- The current theme exposes `--dsw-alias-*` tokens:
281
- `bg-base`, `bg-layer-1`, `bg-layer-2`, `bg-overlay`, `border-l1`, `border-l2`,
282
- `brand-primary`, `label-primary`, `label-secondary`, `state-error-primary`,
283
- `state-idle-primary`, `state-success-primary`, `state-warn-primary`, and
284
- `--dsw-specific-sidebar-fill`.
285
-
286
- Design consequence: the panel styles itself only with these variables; no hardcoded
287
- palette, including the sidebar icon (except its own brand mark color).
288
-
289
- ### F18 — Config schema shape
290
-
291
- `Config` is `@deepseek-ai/schemastery` (`z.object({...})`), and the loader rejects
292
- invalid config before `apply`. There is no runtime config *write* API for a plugin;
293
- users edit the profile patch. The Client may show the effective config read-only.
294
-
295
- Design consequence: the bundle config page is informational; `dataDir` is changed in
296
- the profile patch or `cordis.patch.yml`, not through the UI.
297
-
298
- ### F19 — Package layout and bundling
299
-
300
- The bundle needs `index.js` (Host entry), `client.js` (Client entry),
301
- `cordis.patch.yml`, `package.json` with `dsh.bundle.patch` and `dsh.client`.
302
- DSH itself uses prebuilt ESM in `lib/` and ships no bundler requirement for the Host.
303
- The Client half is a single plain-JS module.
304
-
305
- Design consequence: TypeScript Host compiled to `lib/`, `index.js` as a thin
306
- re-export shim, no client bundler. This matches ADR-0004.
307
-
308
- ### F20 — Skill catalog
309
-
310
- The Host exposes a `skills` service:
311
-
312
- ```ts
313
- ctx.skills.list({ scope?, cwd?, signal? }): Promise<SkillSummary[]>
314
- ctx.skills.snapshot({ scope?, cwd?, signal? }): Promise<{ skills: SkillSummary[]; complete: boolean }>
315
- ctx.skills.get(name, { scope?, cwd? }): Promise<SkillSummary | undefined>
316
- ctx.skills.register(...), ctx.skills.registerProvider(...)
317
- ```
318
-
319
- `SkillSummary` fields: `name`, `description`, `whenToUse?`, `invocation`, `source`,
320
- `provider`, `path?`, `resourceBase?`. There is **no digest field**, and no API returns
321
- Skill file contents or a content hash.
322
-
323
- `snapshot.complete === false` means the catalog is incomplete for the requested scope;
324
- absence of a name then proves nothing.
325
-
326
- Design consequence:
327
-
328
- - Availability checks use `snapshot`, not a name-only `get`.
329
- - A missing/service-unavailable/incomplete catalog yields `available: 'unknown'` with a
330
- warning, never a silent pass.
331
- - Digest pinning is unsupported. `SkillReference` uses `expectedSource` and
332
- `expectedProvider`; `expectedDigest` was removed.
333
-
334
- ### F21 — Runtime Skill registration from a plugin
335
-
336
- `ctx.skills.register(skill)` is the official Host API for a plugin-owned runtime Skill:
337
-
338
- ```ts
339
- const dispose = ctx.skills.register({
340
- name: 'dsh-env-manager',
341
- description: '...',
342
- whenToUse: '...',
343
- content: '# ...',
344
- source: 'runtime',
345
- invocation: { modelInvocable: true, userInvocable: true },
346
- })
347
- ```
348
-
349
- Evidence: Service contract `skills` (methods `register`, `registerProvider`, `list`,
350
- `snapshot`, `get`; types `SkillRegistration`, `SkillDefinition`, `SkillSummary`,
351
- `SkillInvocationPolicy`). The service description states that an unscoped registration
352
- files into the **global layer**, while a registration made in a scoped context (for
353
- example an agent preset) files into that scope. The disposer unregisters the provider
354
- and invalidates catalog caches.
355
-
356
- A live registration was verified in the `desktop` profile: after rebuilding and
357
- reloading `dsh-env-manager`, the Skill catalog added `dsh-env-manager`, and the `skill`
358
- tool returned the registered content. This is runtime registration, not a filesystem
359
- `SKILL.md`; no workspace file or separate Skill installation is involved.
360
-
361
- Design consequence:
362
-
363
- - The bundled `dsh-env-manager` discovery Skill is registered in `apply()` with
364
- `ctx.get('skills')`, inside the plugin effect so it is disposed with the fiber.
365
- - The Skill is visible in the same profile's other workspaces; it remains a catalog
366
- summary until invoked, so it does not put the full workflow in every system prompt.
367
- - `config.registerSkill` defaults to `true`; setting it to `false` suppresses the entry.
368
- - `ctx.skills` is optional. If the service is absent or lacks `register`, the plugin
369
- keeps its tools and panel and skips the Skill.
370
- - Operation execution still requires an `operationBinding`; the discovery Skill does
371
- not execute the routed Skill.
372
-
373
- ## 3. Consequences for the V1 design
374
-
375
- 1. **Tool contract is concrete.** Ten `defineTool` definitions with a parameter DSL,
376
- output schema, render function and `HarnessError` codes.
377
- 2. **Transport is plugin-owned HTTP.** No Typert codegen; one same-origin route; the
378
- same application service answers tools and UI.
379
- 3. **Storage uses Node fs.** `ctx.fs` cannot create or delete directories/files, so
380
- the plugin owns `<dataDir>` with `node:fs/promises` and its own atomic writes.
381
- 4. **Approval is per tool call.** Delete and production writes ask the human, and any
382
- outcome other than `allowed-once` denies.
383
- 5. **Session context is file-backed.** No session-log schema changes in V1.
384
- 6. **UI is a real panel.** Sidebar entry + `main` panel; no composer-dock widget.
385
-
386
- ## 4. Remaining verification tasks (implementation phase)
387
-
388
- | ID | Verify | Fallback | Status |
389
- |---|---|---|---|
390
- | V1 | `profileContext.dir` present and writable | `$DSH_PROFILE_DIR`, then `<cwd>/data` | Verified: live `meta` reports `C:\Users\18811\.dsh\profiles\desktop\data` via the env fallback |
391
- | V2 | `slots.register` accepts `main` with `key` | register under `main.conversation` | Verified live: `sidebar.panellist` occupant `dsh-env-manager` and `main` key `dsh-env-manager` are active |
392
- | V3 | `fetch('/env-manager/api')` works from the bundle | fall back to Remote when DSH exposes runtime descriptors | Verified: live `POST /env-manager/api` returns `200` and a save/list/remove round-trip succeeds |
393
- | V4 | `approval.request` from a tool body resolves | show a UI confirmation and deny without approval | Implemented fail-closed; not exercised live |
394
- | V5 | Output schema accepts `additionalProperties: true` objects | annotation-only schema | Verified locally: all ten schemas pass `assertObjectJsonSchema` |
395
- | V6 | `CredentialRef` accepts a plain string at runtime | import the branded factory | Implemented with string refs; live `describe` not exercised |
396
- | V7 | Directory watcher parity on Windows | reload per request (already the V1 plan) | Verified: each request reloads the registry |
397
- | V8 | `webServer` route prefix does not collide with shell routes | choose a more specific prefix | Verified live: `/env-manager/api` is served beside the GUI |
398
-
399
- ## 5. Evidence index
400
-
401
- | Source | What it proves |
402
- |---|---|
403
- | `cordis_inspect_query(host, Service, tools)` | `register` signature, `ToolDefinition`, `ToolRunContext` |
404
- | `cordis_inspect_query(host, Service, approval)` | approval outcomes, request fields |
405
- | `cordis_inspect_query(host, Service, credentials)` | describe/resolve split |
406
- | `cordis_inspect_query(host, Service, fs)` | capability gap (no mkdir/unlink) |
407
- | `cordis_inspect_query(host, Service, skills)` | `snapshot`/`list`/`get`/`register`, `SkillRegistration`, no digest |
408
- | Live `dsh-env-manager` registration | Skill catalog gained `dsh-env-manager`; `skill` tool loaded its content |
409
- | `cordis_inspect_query(host, Service, webServer)` | route registration, origin |
410
- | `cordis_inspect_query(client, Slots, listSubTree)` | slot topology and registration fields |
411
- | `cordis_inspect_query(client, Builtin, listBuiltins)` | dynamic-half builtins (`host.call`) |
412
- | `cordis_inspect_query(client, Theme, listTokens)` | `--dsw-alias-*` tokens |
413
- | `dsh-tools` README + `lib/index.js` | `defineTool`, schema subset, output validation, `HarnessError` |
414
- | `dsh-shell-env` `lib/index.js` | `Config` schemastery, `profileContext.dir`, session id |
415
- | `dsh-api-gateway` / `dsh-api-remotes` README | Typert Remote requires generated descriptors |
416
- | `dsh-tool-todo` `lib/index.js` | a complete tool definition in practice |
1
+ # API Verification
2
+
3
+ > Status: verified against the DSH runtime shipped as `DeepSeek Harness 0.2.1-rc.5`.
4
+ > Method: `cordis_inspect_*` providers plus package documentation and bundled source
5
+ > extracted from the installed `app.asar`. This document records what the V1 design
6
+ > may rely on, and what it must verify again at implementation time.
7
+
8
+ ## 1. Purpose
9
+
10
+ The first design draft left eight open questions about DSH integration. This document
11
+ answers them from the runtime itself so that `detailed-design.md`,
12
+ `tool-contract.md`, `storage-contract.md` and `ui-design.md` describe APIs that exist,
13
+ not APIs that are assumed.
14
+
15
+ Every finding has an ID (`F1`…`F21`). The design documents reference these IDs.
16
+
17
+ ## 2. Findings
18
+
19
+ ### F1 — Host plugin entry
20
+
21
+ A Host package is an ES module exporting any of:
22
+
23
+ ```ts
24
+ export const name = 'dsh-env-manager'
25
+ export const inject = ['tools']
26
+ export const Config = z.object({ dataDir: z.string() })
27
+ export function apply(ctx, config) { /* ... */ }
28
+ ```
29
+
30
+ Evidence: `@deepseek-ai/dsh-shell-env` (`lib/index.js`) and `@deepseek-ai/dsh-tool-todo`.
31
+ `Config` is a `@deepseek-ai/schemastery` schema, not Zod. `inject` is a plain string
32
+ array. The loader supplies the parsed config as the second `apply` argument.
33
+
34
+ Design consequence: the Host entry keeps `apply(ctx, config)` and declares `inject`
35
+ explicitly; `Config` is authored with schemastery, while record validation uses Zod.
36
+
37
+ ### F2 — Tool registration
38
+
39
+ `ctx.tools.register(definition)` registers globally or in the calling Agent scope and
40
+ returns an exact disposer. Duplicate names within one layer throw.
41
+
42
+ Tool authors should use `defineTool` from `@deepseek-ai/dsh-tools`:
43
+
44
+ ```ts
45
+ ctx.tools.register(defineTool({
46
+ name: 'env_list',
47
+ description: '...',
48
+ parameters: { kind: { type: 'string', enum: [...] } },
49
+ output: { schema: { ... }, render: (args, value) => [{ type: 'text', text: '...' }] },
50
+ async execute(args, exec) { /* ... */ },
51
+ }))
52
+ ```
53
+
54
+ Evidence: `dsh-tools` README §"Register a tool"; `dsh-tool-todo` source.
55
+
56
+ Design consequence: all ten V1 tools are `defineTool` definitions, registered inside
57
+ `apply` and disposed with the plugin fiber.
58
+
59
+ ### F3 — Parameter schema DSL
60
+
61
+ `defineTool.parameters` is DSH's own value-schema DSL, not raw JSON Schema. Supported
62
+ node types: `string`, `number`, `integer`, `boolean`, `null`, `array`, `object`,
63
+ author-only `json`, and exact-one `oneOf`. Object property `required: true` marks
64
+ membership; `additionalProperties` must be explicitly `true` or `false`.
65
+
66
+ Model arguments are validated before `execute` runs; invalid input becomes a normal
67
+ error result (`ToolArgsError`).
68
+
69
+ Design consequence: tool parameter tables in `tool-contract.md` use this DSL verbatim.
70
+
71
+ ### F4 — Tool output contract
72
+
73
+ `output.schema` is a `JsonSchemaNode` restricted to:
74
+ `type`, `oneOf`, `properties`, `required`, `additionalProperties`, `items`, `enum`,
75
+ `const`, plus annotations (`description`, `title`, `default`, `examples`).
76
+ An **annotation-only schema** (`{ description: '…' }`) is the supported way to declare
77
+ unconstrained JSON.
78
+
79
+ The registry validates the returned value against `output.schema` after execution and
80
+ throws `ToolOutputError` on mismatch. `render(args, value)` returns `ContentBlock[]`;
81
+ one `{ type: 'text', text }` block is enough for a JSON-shaped tool.
82
+
83
+ Evidence: `dsh-tools` `assertSupportedJsonSchema`, `checkSchemaNode`,
84
+ `validateJsonSchemaValue`, and the output check at dispatch.
85
+
86
+ Design consequence: every tool output is a precise object schema where practical, and
87
+ uses `additionalProperties: true` for open record payloads. `render` emits a short
88
+ human summary, because the canonical value is what the model and programmatic callers
89
+ receive.
90
+
91
+ ### F5 — Structured tool errors
92
+
93
+ Only a thrown `HarnessError` produces structured `{ name, code }` failure info;
94
+ plain `Error` produces a message only. Constructor:
95
+
96
+ ```ts
97
+ new HarnessError(message, code, options?)
98
+ ```
99
+
100
+ imported from `@deepseek-ai/dsh-llm`. `name` defaults to the subclass constructor name.
101
+
102
+ Evidence: `dsh-tools` `errorInfo()` and `@deepseek-ai/dsh-llm` `HarnessError`.
103
+
104
+ Design consequence: all expected failures throw a single `EnvManagerError extends
105
+ HarnessError` carrying a stable code from the diagnostics catalogue. Success values
106
+ carry no `ok` flag; failure is the error path.
107
+
108
+ ### F6 — Tool execution context
109
+
110
+ `execute(args, exec)` receives `ToolRunContext` extending `ToolExecution`:
111
+
112
+ | Member | Use |
113
+ |---|---|
114
+ | `exec.signal` | cooperative cancellation; must be observed |
115
+ | `exec.callId`, `exec.rootCallId` | correlation ids |
116
+ | `exec.agent` | optional `Agent`; `exec.agent.session.header.id` is the session id |
117
+ | `exec.deferContext(msg)` | attach extra model context |
118
+ | `exec.concludeTurn()` | end the turn (not used in V1) |
119
+
120
+ Evidence: Service contract `tools`, referenced type `ToolRunContext`; `dsh-shell-env`
121
+ reads `execution.agent.session.header.id`.
122
+
123
+ Design consequence: session-scoped tools require `exec.agent`; without it they fail
124
+ with `SESSION_CONTEXT_UNAVAILABLE` instead of guessing a session.
125
+
126
+ ### F7 — Approval
127
+
128
+ `ctx.approval.request(req)` returns
129
+ `'allowed-once' | 'rejected' | 'cancelled' | 'unavailable'`.
130
+ Only `'allowed-once'` is a grant; every other outcome must be treated as denial.
131
+ The request needs `agent`, `toolName`, optional `callId`, `reason`, `displayReason`
132
+ (localized), and a `signal`. It requires an open turn.
133
+
134
+ Evidence: Service contract `approval`.
135
+
136
+ Design consequence: `env_delete` and production `env_save` call
137
+ `ctx.approval.request` with a localized `displayReason`, and fail closed on any
138
+ non-grant. Approval is per call; nothing is cached.
139
+
140
+ ### F8 — Credentials
141
+
142
+ `ctx.credentials.describe(ref)` returns `{ configured, source?, writable }` without
143
+ the value. `set(ref, value)` writes to a provider-managed writable source and
144
+ `unset(ref)` removes it. `resolve(ref)` returns the value and must not be called by this
145
+ plugin. `CredentialRef` is a branded string; at runtime an ordinary string is accepted.
146
+
147
+ Evidence: Service contract `credentials`; live `POST /env-manager/api` with
148
+ `op: "credential"` returned `{ ref, configured: true }`, and `op: "credentialUnset"`
149
+ returned `{ ref, removed: true }`. The panel used the same path to create an environment,
150
+ then the record was read back and contained only the ref.
151
+
152
+ Design consequence: `env_resolve` reports each `credentialRef` with
153
+ `configured`/`source` only. Credential values may appear in the request body of
154
+ `credential`, but never in a tool result, HTTP response, log line, diagnostic or registry
155
+ file.
156
+
157
+ ### F9 — Filesystem capability gap
158
+
159
+ `ctx.fs` exposes `resolve`, `stat`, `listDir`, `readText`, `writeText` (atomic),
160
+ `editText`, `readBytes`, `readByteRange`, `watch`, `contains` — but **no** `mkdir`,
161
+ `unlink`, `rename`, or `copy`. `resolve` follows real paths, so it cannot target a
162
+ file whose parent directory does not exist yet.
163
+
164
+ Evidence: Service contract `fs`.
165
+
166
+ Design consequence: registry storage uses `node:fs/promises` directly, confined to
167
+ the resolved `dataDir`. The plugin is trusted Host code, not a sandboxed tool. Every
168
+ path is built from a validated id and checked with `path.resolve` containment; no
169
+ model-supplied path reaches the filesystem.
170
+
171
+ ### F10 — Data directory
172
+
173
+ `ctx.get('profileContext')` exposes at least `name` and `dir` at runtime
174
+ (`dsh-shell-env` reads both), although the inspect contract lists only four other
175
+ members. `dir` is the profile directory that owns `node_modules`, patches and data.
176
+
177
+ Evidence: `dsh-shell-env` `collect()`; Service contract `profileContext`.
178
+
179
+ Design consequence: `dataDir` resolution order:
180
+
181
+ 1. `config.dataDir` (absolute, or relative to the profile directory)
182
+ 2. `<profileContext.dir>/data/dsh-env-manager`
183
+ 3. `$DSH_PROFILE_DIR/data/dsh-env-manager`
184
+ 4. `<cwd>/data`
185
+
186
+ The resolved path is logged once and exposed read-only through `env_get_context` and
187
+ the UI overview. Task T1 verifies `profileContext.dir` on a live profile.
188
+
189
+ ### F11 — Session identity and state
190
+
191
+ `exec.agent.session` is a live `Session` with `header.id`, `append(type, data)` and
192
+ `snapshotEvents()`. The session id is available for every agent-initiated tool call.
193
+
194
+ There is a `sessionProjections` service (used by `todo_write`) for replayable plugin
195
+ state, but it is designed around declared session event types and projection schemas;
196
+ `SessionEventMap` is a closed TypeScript interface.
197
+
198
+ Design consequence: V1 stores the default environment in
199
+ `<dataDir>/sessions/<sessionId>.json` (small, explicit, testable, no session-log
200
+ schema changes). A projection-based implementation is recorded as a P2 alternative.
201
+
202
+ ### F12 — HTTP carrier
203
+
204
+ `ctx.webServer.register({ kind: 'prefix' | 'exact', path, handler })` registers a
205
+ route and returns a disposer; duplicate `(kind, path)` throws.
206
+ `getOrigin()` returns the local origin. `tapIndex(transform)` can inject markup.
207
+
208
+ Evidence: Service contract `webServer`.
209
+
210
+ Design consequence: the Client panel talks to the Host through
211
+ `POST /env-manager/api` registered by this plugin, authenticated by a required custom
212
+ header plus an `Origin` check. See ADR-0009 and `ui-design.md` §4.
213
+
214
+ ### F13 — Client module
215
+
216
+ A bundle's `client.js` is served as `dsh.client`'s `./client` export and loaded by
217
+ `window.__ModuleLoader__.load({ id, factory(require) { ... } })`. The factory may
218
+ `require('react')`. The returned plugin declares `inject` services (`slots`,
219
+ `locale`, …) and `apply(ctx)`.
220
+
221
+ The dynamic-Cordis builtins (`React`, `host.call`, `styles`, `console`) belong to
222
+ runtime-defined packages, **not** to installed bundle client halves.
223
+
224
+ Evidence: `client.js` skeleton; `Slots`/`Service` client catalog; `dsh-cordis-client-runner`.
225
+
226
+ Design consequence: the Client uses `slots` and `locale` only, plus browser `fetch`
227
+ for the plugin's own HTTP route.
228
+
229
+ ### F14 — Client-to-Host Remote is not usable here
230
+
231
+ DSH's native Client↔Host RPC is the Typert Remote system:
232
+ `ctx.remote.$mount()` mounts a **generated** Host-for-Client contribution; strict
233
+ descriptors come from `@Remote`/`@RemoteScope` decorators processed by the build-time
234
+ Typert loader into `ctx.typert.local` (`/remote` artifacts imported by the Client).
235
+ Client-supplied fields without strict generated codecs fail before methods become
236
+ callable. SRC mode is a development fallback for endpoints without a strict
237
+ definition and does not give a standalone bundle a typed namespace.
238
+
239
+ Evidence: `dsh-api-gateway` README (Host and Client sections), `dsh-api-remotes`
240
+ README ("capability set is fixed by explicit build-time value imports"), client
241
+ Service catalog (no `remote` service until the gateway Client face mounts one).
242
+
243
+ Design consequence: a standalone plugin bundle cannot add a Remote namespace without
244
+ adopting DSH's build toolchain. V1 therefore uses the plugin-owned HTTP route
245
+ (ADR-0009), which is same-origin, token-free, header-gated, and reuses the exact same
246
+ application handlers as the tools.
247
+
248
+ ### F15 — Slots
249
+
250
+ Live topology confirms:
251
+
252
+ | Slot | Kind | Registration |
253
+ |---|---|---|
254
+ | `sidebar.panellist` | list | `{ id, order?, label? }` — one sidebar icon |
255
+ | `main` | keyed | `{ key }` — the selected panel body |
256
+ | `plugins.bundle.config` | keyed | `{ key: '<package name>' }`, `view: 'page'` only |
257
+ | `conversation.composer.dock` | list | `{ id, order?, label? }` — ambient composer entry |
258
+
259
+ `ctx.slots.inject(slotName, () => ctx.slots.register({ name, ... }, Component))` waits
260
+ for the slot to exist and disposes with the fiber.
261
+
262
+ Design consequence: the manager registers a `sidebar.panellist` icon
263
+ (id `env-manager`), a `main` panel keyed `env-manager`, and optionally a bundle
264
+ config page. The composer dock is not used for the main UI.
265
+
266
+ ### F16 — Client services
267
+
268
+ Available to the Client half: `layout`, `locale`, `sessions`, `slots`, `theme`,
269
+ `timer`, `uiWorkspace`, `workspaces`.
270
+
271
+ - `ctx.locale.register(ns, { en, zh })` and `ctx.locale.bind(ns)` provide copy.
272
+ - `ctx.theme` owns theme selection; the plugin only consumes CSS variables.
273
+ - `ctx.layout.selectPanel(id)` can select the panel programmatically.
274
+
275
+ Design consequence: UI copy is registered as namespace `envManager`; no string is
276
+ hardcoded twice.
277
+
278
+ ### F17 — Theme tokens
279
+
280
+ The current theme exposes `--dsw-alias-*` tokens:
281
+ `bg-base`, `bg-layer-1`, `bg-layer-2`, `bg-overlay`, `border-l1`, `border-l2`,
282
+ `brand-primary`, `label-primary`, `label-secondary`, `state-error-primary`,
283
+ `state-idle-primary`, `state-success-primary`, `state-warn-primary`, and
284
+ `--dsw-specific-sidebar-fill`.
285
+
286
+ Design consequence: the panel styles itself only with these variables; no hardcoded
287
+ palette, including the sidebar icon (except its own brand mark color).
288
+
289
+ ### F18 — Config schema shape
290
+
291
+ `Config` is `@deepseek-ai/schemastery` (`z.object({...})`), and the loader rejects
292
+ invalid config before `apply`. There is no runtime config *write* API for a plugin;
293
+ users edit the profile patch. The Client may show the effective config read-only.
294
+
295
+ Design consequence: the bundle config page is informational; `dataDir` is changed in
296
+ the profile patch or `cordis.patch.yml`, not through the UI.
297
+
298
+ ### F19 — Package layout and bundling
299
+
300
+ The bundle needs `index.js` (Host entry), `client.js` (Client entry),
301
+ `cordis.patch.yml`, `package.json` with `dsh.bundle.patch` and `dsh.client`.
302
+ DSH itself uses prebuilt ESM in `lib/` and ships no bundler requirement for the Host.
303
+ The Client half is a single plain-JS module.
304
+
305
+ Design consequence: TypeScript Host compiled to `lib/`, `index.js` as a thin
306
+ re-export shim, no client bundler. This matches ADR-0004.
307
+
308
+ ### F20 — Skill catalog
309
+
310
+ The Host exposes a `skills` service:
311
+
312
+ ```ts
313
+ ctx.skills.list({ scope?, cwd?, signal? }): Promise<SkillSummary[]>
314
+ ctx.skills.snapshot({ scope?, cwd?, signal? }): Promise<{ skills: SkillSummary[]; complete: boolean }>
315
+ ctx.skills.get(name, { scope?, cwd? }): Promise<SkillSummary | undefined>
316
+ ctx.skills.register(...), ctx.skills.registerProvider(...)
317
+ ```
318
+
319
+ `SkillSummary` fields: `name`, `description`, `whenToUse?`, `invocation`, `source`,
320
+ `provider`, `path?`, `resourceBase?`. There is **no digest field**, and no API returns
321
+ Skill file contents or a content hash.
322
+
323
+ `snapshot.complete === false` means the catalog is incomplete for the requested scope;
324
+ absence of a name then proves nothing.
325
+
326
+ Design consequence:
327
+
328
+ - Availability checks use `snapshot`, not a name-only `get`.
329
+ - A missing/service-unavailable/incomplete catalog yields `available: 'unknown'` with a
330
+ warning, never a silent pass.
331
+ - Digest pinning is unsupported. `SkillReference` uses `expectedSource` and
332
+ `expectedProvider`; `expectedDigest` was removed.
333
+
334
+ ### F21 — Runtime Skill registration from a plugin
335
+
336
+ `ctx.skills.register(skill)` is the official Host API for a plugin-owned runtime Skill:
337
+
338
+ ```ts
339
+ const dispose = ctx.skills.register({
340
+ name: 'dsh-env-manager',
341
+ description: '...',
342
+ whenToUse: '...',
343
+ content: '# ...',
344
+ source: 'runtime',
345
+ invocation: { modelInvocable: true, userInvocable: true },
346
+ })
347
+ ```
348
+
349
+ Evidence: Service contract `skills` (methods `register`, `registerProvider`, `list`,
350
+ `snapshot`, `get`; types `SkillRegistration`, `SkillDefinition`, `SkillSummary`,
351
+ `SkillInvocationPolicy`). The service description states that an unscoped registration
352
+ files into the **global layer**, while a registration made in a scoped context (for
353
+ example an agent preset) files into that scope. The disposer unregisters the provider
354
+ and invalidates catalog caches.
355
+
356
+ A live registration was verified in the `desktop` profile: after rebuilding and
357
+ reloading `dsh-env-manager`, the Skill catalog added `dsh-env-manager`, and the `skill`
358
+ tool returned the registered content. This is runtime registration, not a filesystem
359
+ `SKILL.md`; no workspace file or separate Skill installation is involved.
360
+
361
+ Design consequence:
362
+
363
+ - The bundled `dsh-env-manager` discovery Skill is registered in `apply()` with
364
+ `ctx.get('skills')`, inside the plugin effect so it is disposed with the fiber.
365
+ - The Skill is visible in the same profile's other workspaces; it remains a catalog
366
+ summary until invoked, so it does not put the full workflow in every system prompt.
367
+ - `config.registerSkill` defaults to `true`; setting it to `false` suppresses the entry.
368
+ - `ctx.skills` is optional. If the service is absent or lacks `register`, the plugin
369
+ keeps its tools and panel and skips the Skill.
370
+ - Operation execution still requires an `operationBinding`; the discovery Skill does
371
+ not execute the routed Skill.
372
+
373
+ ## 3. Consequences for the V1 design
374
+
375
+ 1. **Tool contract is concrete.** Ten `defineTool` definitions with a parameter DSL,
376
+ output schema, render function and `HarnessError` codes.
377
+ 2. **Transport is plugin-owned HTTP.** No Typert codegen; one same-origin route; the
378
+ same application service answers tools and UI.
379
+ 3. **Storage uses Node fs.** `ctx.fs` cannot create or delete directories/files, so
380
+ the plugin owns `<dataDir>` with `node:fs/promises` and its own atomic writes.
381
+ 4. **Approval is per tool call.** Delete and production writes ask the human, and any
382
+ outcome other than `allowed-once` denies.
383
+ 5. **Session context is file-backed.** No session-log schema changes in V1.
384
+ 6. **UI is a real panel.** Sidebar entry + `main` panel; no composer-dock widget.
385
+
386
+ ## 4. Remaining verification tasks (implementation phase)
387
+
388
+ | ID | Verify | Fallback | Status |
389
+ |---|---|---|---|
390
+ | V1 | `profileContext.dir` present and writable | `$DSH_PROFILE_DIR`, then `<cwd>/data` | Verified: live `meta` reports `C:\Users\18811\.dsh\profiles\desktop\data` via the env fallback |
391
+ | V2 | `slots.register` accepts `main` with `key` | register under `main.conversation` | Verified live: `sidebar.panellist` occupant `dsh-env-manager` and `main` key `dsh-env-manager` are active |
392
+ | V3 | `fetch('/env-manager/api')` works from the bundle | fall back to Remote when DSH exposes runtime descriptors | Verified: live `POST /env-manager/api` returns `200` and a save/list/remove round-trip succeeds |
393
+ | V4 | `approval.request` from a tool body resolves | show a UI confirmation and deny without approval | Implemented fail-closed; not exercised live |
394
+ | V5 | Output schema accepts `additionalProperties: true` objects | annotation-only schema | Verified locally: all ten schemas pass `assertObjectJsonSchema` |
395
+ | V6 | `CredentialRef` accepts a plain string at runtime | import the branded factory | Implemented with string refs; live `describe` not exercised |
396
+ | V7 | Directory watcher parity on Windows | reload per request (already the V1 plan) | Verified: each request reloads the registry |
397
+ | V8 | `webServer` route prefix does not collide with shell routes | choose a more specific prefix | Verified live: `/env-manager/api` is served beside the GUI |
398
+
399
+ ## 5. Evidence index
400
+
401
+ | Source | What it proves |
402
+ |---|---|
403
+ | `cordis_inspect_query(host, Service, tools)` | `register` signature, `ToolDefinition`, `ToolRunContext` |
404
+ | `cordis_inspect_query(host, Service, approval)` | approval outcomes, request fields |
405
+ | `cordis_inspect_query(host, Service, credentials)` | describe/resolve split |
406
+ | `cordis_inspect_query(host, Service, fs)` | capability gap (no mkdir/unlink) |
407
+ | `cordis_inspect_query(host, Service, skills)` | `snapshot`/`list`/`get`/`register`, `SkillRegistration`, no digest |
408
+ | Live `dsh-env-manager` registration | Skill catalog gained `dsh-env-manager`; `skill` tool loaded its content |
409
+ | `cordis_inspect_query(host, Service, webServer)` | route registration, origin |
410
+ | `cordis_inspect_query(client, Slots, listSubTree)` | slot topology and registration fields |
411
+ | `cordis_inspect_query(client, Builtin, listBuiltins)` | dynamic-half builtins (`host.call`) |
412
+ | `cordis_inspect_query(client, Theme, listTokens)` | `--dsw-alias-*` tokens |
413
+ | `dsh-tools` README + `lib/index.js` | `defineTool`, schema subset, output validation, `HarnessError` |
414
+ | `dsh-shell-env` `lib/index.js` | `Config` schemastery, `profileContext.dir`, session id |
415
+ | `dsh-api-gateway` / `dsh-api-remotes` README | Typert Remote requires generated descriptors |
416
+ | `dsh-tool-todo` `lib/index.js` | a complete tool definition in practice |