@shendeguize/dsh-agent-sidecar 0.1.0

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 (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +167 -0
  3. package/cordis.patch.yml +10 -0
  4. package/lib/client.js +8062 -0
  5. package/lib/client.js.map +1 -0
  6. package/lib/index.d.ts +396 -0
  7. package/lib/index.js +4166 -0
  8. package/package.json +101 -0
  9. package/src/analysis.ts +782 -0
  10. package/src/bridge.ts +841 -0
  11. package/src/client/analysis/AnalysisPanel.tsx +191 -0
  12. package/src/client/analysis/analysis.module.css +183 -0
  13. package/src/client/analysis-glue.ts +331 -0
  14. package/src/client/api.ts +380 -0
  15. package/src/client/board/Board.tsx +214 -0
  16. package/src/client/board/board.module.css +302 -0
  17. package/src/client/board/logic.ts +556 -0
  18. package/src/client/board/project-view-logic.ts +361 -0
  19. package/src/client/board/project-view.module.css +307 -0
  20. package/src/client/board/project-view.tsx +189 -0
  21. package/src/client/board/strings.ts +112 -0
  22. package/src/client/commands.ts +484 -0
  23. package/src/client/controller.ts +360 -0
  24. package/src/client/css-modules.d.ts +11 -0
  25. package/src/client/detail/SessionDetail.tsx +270 -0
  26. package/src/client/detail/detail.module.css +433 -0
  27. package/src/client/detail/logic.ts +779 -0
  28. package/src/client/detail/strings.ts +98 -0
  29. package/src/client/detail/transport.ts +175 -0
  30. package/src/client/detail-glue.ts +397 -0
  31. package/src/client/detail-view.module.css +79 -0
  32. package/src/client/detail-view.tsx +233 -0
  33. package/src/client/dsh-tools/LineageTree.tsx +210 -0
  34. package/src/client/dsh-tools/SearchPanel.tsx +169 -0
  35. package/src/client/dsh-tools/dsh-tools.module.css +374 -0
  36. package/src/client/dsh-tools/logic.ts +596 -0
  37. package/src/client/dsh-tools/strings.ts +90 -0
  38. package/src/client/index.ts +315 -0
  39. package/src/client/inject/InjectPanel.tsx +482 -0
  40. package/src/client/inject/inject.module.css +446 -0
  41. package/src/client/inject/logic.ts +516 -0
  42. package/src/client/inject/overlay.module.css +22 -0
  43. package/src/client/inject-glue.ts +171 -0
  44. package/src/client/locales/command.ts +48 -0
  45. package/src/client/locales/en.ts +385 -0
  46. package/src/client/locales/index.ts +123 -0
  47. package/src/client/locales/zh.ts +402 -0
  48. package/src/client/m3-transport.ts +151 -0
  49. package/src/client/mount.tsx +307 -0
  50. package/src/client/project-glue.ts +134 -0
  51. package/src/client/search-glue.ts +143 -0
  52. package/src/client/settings-card.module.css +359 -0
  53. package/src/client/settings-card.tsx +565 -0
  54. package/src/client/settings-glue.ts +130 -0
  55. package/src/client/sidebar-tab.tsx +494 -0
  56. package/src/client/sse.ts +366 -0
  57. package/src/client/widget.tsx +80 -0
  58. package/src/config.ts +193 -0
  59. package/src/dsh-inject.ts +240 -0
  60. package/src/fusion.ts +988 -0
  61. package/src/guard.ts +274 -0
  62. package/src/index.ts +950 -0
  63. package/src/inject-gateway.ts +574 -0
  64. package/src/routes.ts +1133 -0
  65. package/src/send-cli.ts +340 -0
  66. package/src/session-store.ts +184 -0
  67. package/src/skills-provider.ts +293 -0
  68. package/src/supervisor.ts +463 -0
@@ -0,0 +1,293 @@
1
+ /**
2
+ * Skill path two (design §7): register the `agent-sidecar` skill on dsh's
3
+ * `ctx.skills` registry via `registerProvider`, so installing the plugin
4
+ * yields the skill without running `scripts/install-skill.sh`.
5
+ *
6
+ * Coexistence with the filesystem path (design risk 8, resolved 2026-08-25
7
+ * by live test + source, recorded in
8
+ * `.local/tasks/make_dsh_mode/design/env_facts.md`): dsh's SkillRegistry
9
+ * natively resolves same-name skills to a SINGLE catalog entry — the merged
10
+ * catalog is a by-name Map, so a double listing is structurally impossible
11
+ * — and the filesystem copy always wins when present, via two mechanisms:
12
+ * within one layer, candidates sort by rank (lower wins) and same-name
13
+ * losers are dropped with a warn log (source `collectLayer`; filesystem
14
+ * roots rank 100-500 vs BUNDLED_SKILL_RANK 600); across layers, the
15
+ * nearest scope layer replaces farther ones outright — which is the
16
+ * operative rule in dsh-web compositions, where skill-filesystem is
17
+ * host-disabled and mounted per agent-preset scope while this provider
18
+ * sits in the global layer (live-verified: with `~/.dsh/skills/
19
+ * agent-sidecar` present the RPC catalog showed exactly one entry, the
20
+ * filesystem one; removing it flipped the same entry to this provider's
21
+ * without a restart). The yield rule therefore needs no filesystem
22
+ * probing: registering at BUNDLED_SKILL_RANK (600, the sanctioned rank for
23
+ * packaged providers, installed d.ts:16) is the complete, TOCTOU-free
24
+ * yield marker in every topology.
25
+ *
26
+ * The skill body is a dsh-scene condensation of the canonical
27
+ * `skills/agent-sidecar/SKILL.md` (same repo): observation flows through
28
+ * the CLI and this plugin's web board, injection flows through the plugin
29
+ * panel (sidecar `send` is `unsupported_dsh` for dsh sessions), and the
30
+ * S5/S6 wording — send only on an explicit same-turn request, never retry
31
+ * `delivery: "unknown"` — is preserved verbatim in spirit. The body is
32
+ * embedded (not read from disk) so the npm package needs no extra assets;
33
+ * `resourceBase` is `opaque` pointing readers at the canonical repo docs.
34
+ *
35
+ * Faces below are structural on purpose (repo rule: the plugin's type
36
+ * surface stays on the devDependency SDKs; service packages resolve at
37
+ * runtime from the dsh profile tree).
38
+ *
39
+ * @module
40
+ */
41
+
42
+ /** Invocation controls mirrored from @deepseek-ai/dsh-skill (d.ts:37-42). */
43
+ export interface SkillInvocationFace {
44
+ readonly modelInvocable: boolean
45
+ readonly userInvocable: boolean
46
+ }
47
+
48
+ /** Provider-declared resource base (opaque flavor only; d.ts:32-35). */
49
+ export interface SkillResourceBaseFace {
50
+ readonly kind: 'opaque'
51
+ readonly description: string
52
+ }
53
+
54
+ /** Catalog candidate returned by `provider.list()` (d.ts:61-70). */
55
+ export interface SkillCandidateFace {
56
+ readonly name: string
57
+ readonly description: string
58
+ readonly whenToUse?: string
59
+ readonly invocation: SkillInvocationFace
60
+ readonly source: string
61
+ readonly provider: string
62
+ readonly resourceBase?: SkillResourceBaseFace
63
+ /** Lower ranks win same-name duplicates within one layer (d.ts:62-63). */
64
+ readonly rank: number
65
+ /** Opaque provider-owned handle passed back to `provider.get()`. */
66
+ readonly locator: unknown
67
+ }
68
+
69
+ /** Full definition returned by `provider.get()` (d.ts:71-79). */
70
+ export interface SkillDefinitionFace {
71
+ readonly name: string
72
+ readonly description: string
73
+ readonly invocation: SkillInvocationFace
74
+ readonly source: string
75
+ readonly provider: string
76
+ readonly resourceBase?: SkillResourceBaseFace
77
+ readonly content: string
78
+ }
79
+
80
+ /** Lookup options are borrowed opaquely; this provider is context-free. */
81
+ export interface SkillLookupOptionsFace {
82
+ readonly cwd?: string | undefined
83
+ readonly signal?: AbortSignal | undefined
84
+ }
85
+
86
+ /** One same-process skill source (d.ts:168-188). */
87
+ export interface SkillProviderFace {
88
+ readonly name: string
89
+ readonly list: (
90
+ options: SkillLookupOptionsFace,
91
+ ) => Promise<readonly SkillCandidateFace[]>
92
+ readonly get: (
93
+ candidate: SkillCandidateFace,
94
+ options: SkillLookupOptionsFace,
95
+ ) => Promise<SkillDefinitionFace | undefined>
96
+ }
97
+
98
+ /** Registration-scoped lifecycle control handed to the factory (d.ts:189-195). */
99
+ export interface SkillProviderControlFace {
100
+ readonly signal: AbortSignal
101
+ readonly invalidate: () => void
102
+ }
103
+
104
+ /**
105
+ * `ctx.skills` face (provider registration only). Source: installed
106
+ * @deepseek-ai/dsh-skill 0.1.1-rc.2 lib/types/index.d.ts:249 —
107
+ * `registerProvider(create): () => void`. Registration is synchronous
108
+ * during plugin apply, rides the CALLER's fiber (duplicate provider names
109
+ * in one layer throw), and the returned disposer is the exact cordis
110
+ * effect disposer that unregisters the provider.
111
+ */
112
+ export interface SkillsServiceFace {
113
+ registerProvider(
114
+ create: (control: SkillProviderControlFace) => SkillProviderFace,
115
+ ): () => void
116
+ }
117
+
118
+ /** Log face shared with the rest of the host half. */
119
+ export type SkillsProviderLog = (
120
+ level: 'debug' | 'info' | 'warn' | 'error',
121
+ msg: string,
122
+ meta?: Record<string, unknown>,
123
+ ) => void
124
+
125
+ /** Registry name of this provider (distinct from the skill it serves). */
126
+ export const SKILL_PROVIDER_NAME = 'agent-sidecar-plugin'
127
+
128
+ /** The one skill this provider serves (same name as the filesystem copy). */
129
+ export const SIDECAR_SKILL_NAME = 'agent-sidecar'
130
+
131
+ /**
132
+ * BUNDLED_SKILL_RANK, pinned from @deepseek-ai/dsh-skill 0.1.1-rc.2
133
+ * (lib/types/index.d.ts:16, "Standard precedence rank for packaged skill
134
+ * providers and local bundled roots"). This IS the yield rule: filesystem
135
+ * roots rank 100/200/300/400/500, all lower, so any user-managed copy of
136
+ * the skill shadows this packaged one (live-verified 2026-08-25).
137
+ */
138
+ export const SIDECAR_SKILL_RANK = 600
139
+
140
+ /** Routing description; aligned with skills/agent-sidecar/SKILL.md frontmatter. */
141
+ export const SIDECAR_SKILL_DESCRIPTION =
142
+ 'Monitors readonly local AI agent sessions (claude/codex/cursor/dsh/kimi/copilot) and reports ' +
143
+ 'their state and progress via the agent-sidecar CLI and the Sidecar board in dsh web. Use when ' +
144
+ 'the user asks for agent status, session progress, to monitor agents, which agent is waiting or ' +
145
+ 'working, or explicitly asks to send a message or feedback to an agent.'
146
+
147
+ /**
148
+ * dsh-scene skill body: semantically consistent with the canonical
149
+ * `skills/agent-sidecar/SKILL.md`, condensed for the plugin context —
150
+ * observation goes CLI/board, injection goes the plugin panel (design §7
151
+ * path two: "dsh 会话注入应引导走插件通路而非 send,因 unsupported_dsh").
152
+ */
153
+ export const SIDECAR_SKILL_CONTENT = `# Agent Sidecar (dsh plugin edition)
154
+
155
+ This dsh composition runs the \`dsh-agent-sidecar\` plugin. Observation is
156
+ the default; every mutation needs an explicit user request in the same turn.
157
+
158
+ ## Observe
159
+
160
+ 1. Check \`command -v agent-sidecar\`. If missing, do not install anything
161
+ unless the user explicitly asks; point them at the agent_sidecar repo
162
+ install options instead.
163
+ 2. Run \`agent-sidecar status --json\` first; summarize sessions by agent,
164
+ status, title, project, and age from \`updated_at\`.
165
+ 3. Other observation commands, only when they match the request:
166
+ \`list --json\` (48h window), \`list --all --json\`, \`ps --json\`,
167
+ \`watch <session-prefix> --json\`, \`watch --all --json\`, \`tui\`.
168
+ 4. The plugin also serves a live multi-agent board in dsh web (the
169
+ "Sidecar" conversation tab). Prefer pointing the user there for
170
+ continuous monitoring instead of polling the CLI yourself.
171
+ 5. Treat \`working\`/\`waiting\` as inferred observations from persisted
172
+ data, not control-plane guarantees; Cursor IDE can report \`waiting\`
173
+ several minutes late.
174
+
175
+ ## Inject (explicit request only)
176
+
177
+ - For **dsh sessions**, \`agent-sidecar send\` is unsupported
178
+ (\`unsupported_dsh\`: DSH has neither session resume nor stdin prompt
179
+ transport). Route the user to the plugin's inject panel on the Sidecar
180
+ board, which injects in-process (queue/steer) behind the plugin's
181
+ \`inject.enabled\` gate and confirmation dialog.
182
+ - For **claude / codex / cursor-cli** sessions in \`waiting\`/\`idle\`, use
183
+ the plugin panel, or run \`send\` only when the user explicitly requests
184
+ the exact message or action in the same turn. Never infer consent from a
185
+ request to observe, watch, report, or wait. That explicit same-turn
186
+ request is the permission required to use \`--allow-write\`; never add it
187
+ otherwise:
188
+
189
+ \`\`\`sh
190
+ agent-sidecar send <session-prefix> "<exact-message>" --allow-write --request-id "<stable-unique-id>" --json
191
+ \`\`\`
192
+
193
+ - Preserve the returned \`request_id\` and \`replayed\` fields. Never send
194
+ to remote, \`working\`, \`dead\`, child, or unsupported-agent sessions
195
+ (\`cursor-ide\`, \`copilot\`, \`kimi\`, \`dsh\`).
196
+ - Never retry \`failed\`, \`timed_out\`, \`request_pending\`,
197
+ \`audit_error\`, \`cleanup_incomplete\`, or any result with
198
+ \`delivery: "unknown"\` — the agent may already have received the
199
+ message. Report the unknown state plainly and ask the user what to do.
200
+ - The audit store is fail-closed; never run \`agent-sidecar audit reset\`
201
+ automatically.
202
+
203
+ ## Reference
204
+
205
+ Full schemas, exit codes, and boundaries: \`skills/agent-sidecar/SKILL.md\`
206
+ and \`reference.md\` in the agent_sidecar repository (also installable as a
207
+ filesystem skill via \`scripts/install-skill.sh\`; a filesystem copy under
208
+ \`~/.dsh/skills/\` automatically shadows this plugin-provided one).`
209
+
210
+ const RESOURCE_BASE: SkillResourceBaseFace = {
211
+ kind: 'opaque',
212
+ description:
213
+ 'Self-contained skill provided by the dsh-agent-sidecar plugin; the canonical long-form ' +
214
+ 'reference (SKILL.md + reference.md) lives in the agent_sidecar repository under skills/agent-sidecar/.',
215
+ }
216
+
217
+ const INVOCATION: SkillInvocationFace = { modelInvocable: true, userInvocable: true }
218
+
219
+ /** The single catalog candidate this provider lists (skill-badge template). */
220
+ export const SIDECAR_SKILL_CANDIDATE: SkillCandidateFace = {
221
+ name: SIDECAR_SKILL_NAME,
222
+ description: SIDECAR_SKILL_DESCRIPTION,
223
+ invocation: INVOCATION,
224
+ source: 'bundled',
225
+ provider: SKILL_PROVIDER_NAME,
226
+ resourceBase: RESOURCE_BASE,
227
+ rank: SIDECAR_SKILL_RANK,
228
+ locator: SIDECAR_SKILL_NAME,
229
+ }
230
+
231
+ /** Dependencies of {@link registerSidecarSkillProvider}. */
232
+ export interface SkillsProviderDeps {
233
+ /** The bound `ctx.skills` registry. */
234
+ skills: SkillsServiceFace
235
+ /** Config gate `skill.provide` (read once at apply; restart semantics). */
236
+ provide: boolean
237
+ log: SkillsProviderLog
238
+ }
239
+
240
+ /** The provider instance: one static candidate, embedded body. */
241
+ const provider: SkillProviderFace = {
242
+ name: SKILL_PROVIDER_NAME,
243
+ list: () => Promise.resolve([SIDECAR_SKILL_CANDIDATE]),
244
+ get: (candidate) =>
245
+ Promise.resolve(
246
+ candidate.name === SIDECAR_SKILL_NAME
247
+ ? {
248
+ name: SIDECAR_SKILL_NAME,
249
+ description: SIDECAR_SKILL_DESCRIPTION,
250
+ invocation: INVOCATION,
251
+ source: 'bundled',
252
+ provider: SKILL_PROVIDER_NAME,
253
+ resourceBase: RESOURCE_BASE,
254
+ content: SIDECAR_SKILL_CONTENT,
255
+ }
256
+ : undefined,
257
+ ),
258
+ }
259
+
260
+ /**
261
+ * Register the agent-sidecar skill provider on `ctx.skills`.
262
+ *
263
+ * Yield rule (per live test, env_facts.md): none needed beyond the rank —
264
+ * dsh's registry dedupes same-name skills natively, and this provider's
265
+ * BUNDLED rank (600) loses to every filesystem root, so a filesystem copy
266
+ * always shadows the plugin copy and the catalog shows exactly one entry
267
+ * either way. `provide=false` skips registration entirely.
268
+ *
269
+ * @param deps - registry face, config gate, and log sink.
270
+ * @returns the registry's unregister disposer, or `null` when the gate is
271
+ * off or registration failed (duplicate provider name in this layer —
272
+ * only reachable if the plugin is mounted twice in one scope).
273
+ */
274
+ export function registerSidecarSkillProvider(
275
+ deps: SkillsProviderDeps,
276
+ ): (() => void) | null {
277
+ if (!deps.provide) {
278
+ deps.log('debug', 'skill provider disabled (skill.provide=false)')
279
+ return null
280
+ }
281
+ try {
282
+ const dispose = deps.skills.registerProvider(() => provider)
283
+ deps.log('debug', 'skill provider registered', {
284
+ provider: SKILL_PROVIDER_NAME,
285
+ skill: SIDECAR_SKILL_NAME,
286
+ rank: SIDECAR_SKILL_RANK,
287
+ })
288
+ return dispose
289
+ } catch (err) {
290
+ deps.log('warn', `skill provider registration failed: ${String(err)}`)
291
+ return null
292
+ }
293
+ }