dsh-ntr 0.3.4 → 0.3.5

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.
package/src/impl.ts CHANGED
@@ -2665,6 +2665,51 @@ export interface SessionLogLike {
2665
2665
  header?: { agentPreset?: unknown }
2666
2666
  }
2667
2667
 
2668
+ /**
2669
+ * Structural view of a LIVE host session that can take catalog appends. The
2670
+ * host's `Session.append` validates the payload before committing; an older
2671
+ * host without the `subagent/catalog` event accepts the method but throws,
2672
+ * which the helper below treats as "not supported".
2673
+ */
2674
+ export interface SessionCatalogLike extends SessionLogLike {
2675
+ append?: (type: string, data: Record<string, unknown>) => unknown
2676
+ }
2677
+
2678
+ /**
2679
+ * Record one ACP-side worker in the host's parent-owned subagent catalog as an
2680
+ * EXTERNAL execution. Display-only: external entries have no child session, no
2681
+ * activity/timing and no continuation, and `list_agents` omits them. The
2682
+ * catalog is append-only, so a replayed/resumed dispatch must never add the
2683
+ * same `childId` twice. Returns whether a new fact was appended.
2684
+ */
2685
+ export function appendExternalSubagentCatalog(
2686
+ session: SessionCatalogLike | undefined,
2687
+ childId: string,
2688
+ label: string,
2689
+ now = Date.now(),
2690
+ ): boolean {
2691
+ if (childId === '' || session?.append === undefined) return false
2692
+ const events = session.events
2693
+ if (Array.isArray(events)) {
2694
+ for (let index = events.length - 1; index >= 0; index -= 1) {
2695
+ const event = events[index] as { type?: unknown; data?: { childId?: unknown } } | undefined
2696
+ if (event?.type === 'subagent/catalog' && event.data?.childId === childId) return false
2697
+ }
2698
+ }
2699
+ try {
2700
+ session.append('subagent/catalog', {
2701
+ version: 2,
2702
+ childId,
2703
+ childCreatedAt: now,
2704
+ mode: 'external',
2705
+ label,
2706
+ })
2707
+ return true
2708
+ } catch {
2709
+ return false
2710
+ }
2711
+ }
2712
+
2668
2713
  /**
2669
2714
  * The agent preset a session actually runs.
2670
2715
  *
package/src/index.ts CHANGED
@@ -9,9 +9,9 @@ import type { Context } from '@deepseek-ai/cordis'
9
9
  import type {} from '@deepseek-ai/dsh-host-webserver'
10
10
  import type { ConnectionRpcHandler, HostConnectionHandle } from '@deepseek-ai/dsh-client-connection'
11
11
  import { ACP_PRESET_ID, AcpLlmAdapter, BridgeManager, PLUGIN_VERSION } from './bridge.ts'
12
- import { EventHub, dispatchRpc, permissionReplayFrames, sessionAgentPreset, sessionAgentPresetFromLog, validateConfig } from './impl.ts'
12
+ import { appendExternalSubagentCatalog, EventHub, dispatchRpc, permissionReplayFrames, sessionAgentPreset, sessionAgentPresetFromLog, validateConfig } from './impl.ts'
13
13
  import type { AcpEvent, ApprovalDeadlineKey } from './protocol.ts'
14
- import type { RosterPresetRow, SessionLogLike } from './impl.ts'
14
+ import type { RosterPresetRow, SessionCatalogLike } from './impl.ts'
15
15
  import { UpdateService } from './update.ts'
16
16
  import type { UpdateManagerLike } from './update.ts'
17
17
 
@@ -39,7 +39,7 @@ interface ChannelRegisteringConnection extends HostConnectionHandle {
39
39
 
40
40
  /** Structural view of the host session store (`@deepseek-ai/dsh-session`'s `ctx.sessions`). */
41
41
  interface SessionsLike {
42
- get(sessionId: string): SessionLogLike | undefined
42
+ get(sessionId: string): SessionCatalogLike | undefined
43
43
  }
44
44
 
45
45
  /**
@@ -260,6 +260,27 @@ export function apply(ctx: Context, config?: unknown): void {
260
260
  }
261
261
  }
262
262
 
263
+ /**
264
+ * Optional host integration: record detached ACP workers as EXTERNAL
265
+ * children in the parent chat session's subagent catalog. Display-only — the
266
+ * host renders them as read-only "external task" rows with no child
267
+ * session, activity or continuation — and is skipped silently on hosts
268
+ * without the catalog event.
269
+ */
270
+ const wireSubagentCatalog = (): void => {
271
+ const sessions = (typeof ctx.get === 'function' ? ctx.get('sessions') : undefined) as SessionsLike | undefined
272
+ if (sessions === undefined) return
273
+ manager.setSubagentCatalogReporter((dshSessionId, childId, label) => {
274
+ let session: SessionCatalogLike | undefined
275
+ try {
276
+ session = sessions.get(dshSessionId)
277
+ } catch {
278
+ return
279
+ }
280
+ appendExternalSubagentCatalog(session, childId, label)
281
+ })
282
+ }
283
+
263
284
  wireWorkspace()
264
285
  if (typeof ctx.inject === 'function') {
265
286
  // Late arrival for the workspace collaborators: a deployment missing one
@@ -271,9 +292,11 @@ export function apply(ctx: Context, config?: unknown): void {
271
292
  // The ACP-preset verdict only needs the session store (live) and the
272
293
  // persistence store (cold); the sandbox knobs are NOT its dependencies.
273
294
  wireAcpMode()
295
+ wireSubagentCatalog()
274
296
  if (typeof ctx.inject === 'function') {
275
297
  ctx.inject(['sessions'], () => {
276
298
  wireAcpMode()
299
+ wireSubagentCatalog()
277
300
  })
278
301
  }
279
302
 
package/src/protocol.ts CHANGED
@@ -346,9 +346,10 @@ export interface AgentView {
346
346
 
347
347
  /**
348
348
  * Why the host released a session's agent process. The browser turns this into
349
- * one runtime-log line per cause; `idle` stays silent (expected lifecycle).
349
+ * one runtime-log line per cause; `idle` and `shutdown` stay silent (both are
350
+ * expected lifecycle transitions, not faults).
350
351
  */
351
- export type AcpReleaseCause = 'idle' | 'workspace' | 'resume-failed' | 'stalled' | 'died'
352
+ export type AcpReleaseCause = 'idle' | 'workspace' | 'resume-failed' | 'stalled' | 'died' | 'shutdown'
352
353
 
353
354
  export type AcpEvent =
354
355
  | { type: 'agent/status'; agentId: string; state: AgentProcessState; error?: string; agent?: AgentView }