@morlay/dsh-subagent 0.0.10 → 0.0.11-alpha.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.
@@ -10,22 +10,17 @@
10
10
  * @module @deepseek-ai/dsh-subagent/out-of-process
11
11
  */
12
12
 
13
- import { accessSync, constants, statSync } from "node:fs";
14
- import { isAbsolute } from "node:path";
15
- import type { ContentBlock } from "@deepseek-ai/dsh-llm";
16
- import type {
17
- SubagentCapabilities,
18
- SubagentResult,
19
- SubagentRun,
20
- SubagentStopReason,
21
- } from "./types.ts";
13
+ import { accessSync, constants, statSync } from 'node:fs'
14
+ import { isAbsolute } from 'node:path'
15
+ import type { ContentBlock } from '@deepseek-ai/dsh-llm'
16
+ import type { SubagentCapabilities, SubagentResult, SubagentRun, SubagentStopReason } from './types.ts'
22
17
 
23
18
  /** Maximum UTF-8 size of {@link SubagentResult.diagnostic}. */
24
- const MAX_SUBAGENT_DIAGNOSTIC_BYTES = 4_096;
19
+ const MAX_SUBAGENT_DIAGNOSTIC_BYTES = 4_096
25
20
 
26
- const DIAGNOSTIC_TRUNCATION_SUFFIX = "\n[diagnostic truncated]";
27
- const utf8Encoder = new TextEncoder();
28
- const utf8Decoder = new TextDecoder();
21
+ const DIAGNOSTIC_TRUNCATION_SUFFIX = '\n[diagnostic truncated]'
22
+ const utf8Encoder = new TextEncoder()
23
+ const utf8Decoder = new TextDecoder()
29
24
 
30
25
  /**
31
26
  * Limit provider-authored failure detail without splitting a UTF-8 sequence.
@@ -33,22 +28,23 @@ const utf8Decoder = new TextDecoder();
33
28
  * @returns the original text, or a visibly truncated value within the limit.
34
29
  */
35
30
  function limitSubagentDiagnostic(diagnostic: string): string {
36
- const bytes = utf8Encoder.encode(diagnostic);
37
- if (bytes.byteLength <= MAX_SUBAGENT_DIAGNOSTIC_BYTES) return diagnostic;
31
+ const bytes = utf8Encoder.encode(diagnostic)
32
+ if (bytes.byteLength <= MAX_SUBAGENT_DIAGNOSTIC_BYTES) return diagnostic
38
33
 
39
- const suffixBytes = utf8Encoder.encode(DIAGNOSTIC_TRUNCATION_SUFFIX).byteLength;
40
- let prefixBytes = MAX_SUBAGENT_DIAGNOSTIC_BYTES - suffixBytes;
34
+ const suffixBytes = utf8Encoder.encode(DIAGNOSTIC_TRUNCATION_SUFFIX).byteLength
35
+ let prefixBytes = MAX_SUBAGENT_DIAGNOSTIC_BYTES - suffixBytes
41
36
  while (((bytes[prefixBytes] as number) & 0b1100_0000) === 0b1000_0000) {
42
- prefixBytes -= 1;
37
+ prefixBytes -= 1
43
38
  }
44
- return utf8Decoder.decode(bytes.subarray(0, prefixBytes)) + DIAGNOSTIC_TRUNCATION_SUFFIX;
39
+ return utf8Decoder.decode(bytes.subarray(0, prefixBytes))
40
+ + DIAGNOSTIC_TRUNCATION_SUFFIX
45
41
  }
46
42
 
47
43
  /** Enforce the byte limit on a provider-returned diagnostic. */
48
44
  function normalizeSubagentDiagnostic(result: SubagentResult): SubagentResult {
49
45
  return result.diagnostic === undefined
50
46
  ? result
51
- : { ...result, diagnostic: limitSubagentDiagnostic(result.diagnostic) };
47
+ : { ...result, diagnostic: limitSubagentDiagnostic(result.diagnostic) }
52
48
  }
53
49
 
54
50
  /**
@@ -62,7 +58,7 @@ export const NO_START_CAPABILITIES: SubagentCapabilities = Object.freeze({
62
58
  agentPreset: false,
63
59
  outputSchema: false,
64
60
  depthLimit: false,
65
- });
61
+ })
66
62
 
67
63
  /**
68
64
  * Assert a configured timing bound is a positive finite number (it bounds a
@@ -73,7 +69,7 @@ export const NO_START_CAPABILITIES: SubagentCapabilities = Object.freeze({
73
69
  */
74
70
  export function assertPositiveFinite(prefix: string, name: string, value: number): void {
75
71
  if (!Number.isFinite(value) || value <= 0) {
76
- throw new Error(`${prefix}: ${name} must be a positive finite number`);
72
+ throw new Error(`${prefix}: ${name} must be a positive finite number`)
77
73
  }
78
74
  }
79
75
 
@@ -84,14 +80,14 @@ export function assertPositiveFinite(prefix: string, name: string, value: number
84
80
  */
85
81
  function isEnterableDirectory(path: string): boolean {
86
82
  try {
87
- if (!statSync(path).isDirectory()) return false;
88
- accessSync(path, constants.X_OK);
89
- return true;
83
+ if (!statSync(path).isDirectory()) return false
84
+ accessSync(path, constants.X_OK)
85
+ return true
90
86
  } catch {
91
87
  // statSync/accessSync throw only filesystem access errors here
92
88
  // (ENOENT/EACCES/ENOTDIR/…), and every one of them means the path cannot
93
89
  // serve as the child's cwd.
94
- return false;
90
+ return false
95
91
  }
96
92
  }
97
93
 
@@ -107,12 +103,12 @@ function isEnterableDirectory(path: string): boolean {
107
103
  */
108
104
  export function assertUsableCwd(prefix: string, label: string, cwd: string): string {
109
105
  if (!isAbsolute(cwd)) {
110
- throw new Error(`${prefix}: ${label} must be an absolute path: ${cwd}`);
106
+ throw new Error(`${prefix}: ${label} must be an absolute path: ${cwd}`)
111
107
  }
112
108
  if (!isEnterableDirectory(cwd)) {
113
- throw new Error(`${prefix}: ${label} is not an accessible directory: ${cwd}`);
109
+ throw new Error(`${prefix}: ${label} is not an accessible directory: ${cwd}`)
114
110
  }
115
- return cwd;
111
+ return cwd
116
112
  }
117
113
 
118
114
  /** Normalize an unknown thrown value to an Error (the catch binding is `unknown`). */
@@ -121,25 +117,25 @@ function toError(value: unknown): Error {
121
117
  // `Error`s; the `String(value)` arm is a defensive fallback for a non-Error
122
118
  // throw the typed surfaces cannot produce.
123
119
  /* v8 ignore next */
124
- return value instanceof Error ? value : new Error(String(value));
120
+ return value instanceof Error ? value : new Error(String(value))
125
121
  }
126
122
 
127
123
  /** Inputs to {@link settleRunResult}. */
128
124
  export interface RunResultSettlement {
129
125
  /** The turn attempt (typically racing local cancellation); returns the terminal result. */
130
- attempt: () => Promise<SubagentResult>;
126
+ attempt: () => Promise<SubagentResult>
131
127
  /** Snapshot the provider exposes when cancellation or failure wins settlement. */
132
- collectOutput: () => readonly ContentBlock[];
128
+ collectOutput: () => readonly ContentBlock[]
133
129
  /** Snapshot safe provider-authored detail when a failure wins settlement. */
134
- collectDiagnostic?: (() => string | undefined) | undefined;
130
+ collectDiagnostic?: (() => string | undefined) | undefined
135
131
  /** Whether local cancellation settled before the attempt's outcome is observed. */
136
- cancelled: () => boolean;
132
+ cancelled: () => boolean
137
133
  /** Diagnostic sink for a failure flattened to a stop reason; a throw from it is contained. */
138
- onError?: ((error: Error, stopReason: SubagentStopReason) => void) | undefined;
134
+ onError?: ((error: Error, stopReason: SubagentStopReason) => void) | undefined
139
135
  /** The request's cancellation signal (the listener is removed at settlement). */
140
- signal: AbortSignal;
136
+ signal: AbortSignal
141
137
  /** The abort listener registered on {@link signal} at start. */
142
- onAbort: () => void;
138
+ onAbort: () => void
143
139
  }
144
140
 
145
141
  /**
@@ -154,45 +150,47 @@ export interface RunResultSettlement {
154
150
  */
155
151
  export async function settleRunResult(parts: RunResultSettlement): Promise<SubagentResult> {
156
152
  try {
157
- const result = await parts.attempt();
153
+ const result = await parts.attempt()
158
154
  return parts.cancelled()
159
- ? { output: parts.collectOutput(), stopReason: "aborted" }
160
- : normalizeSubagentDiagnostic(result);
155
+ ? { output: parts.collectOutput(), stopReason: 'aborted' }
156
+ : normalizeSubagentDiagnostic(result)
161
157
  } catch (error: unknown) {
162
158
  // Cover a rejection already queued when cancellation arrives.
163
- if (parts.cancelled()) return { output: parts.collectOutput(), stopReason: "aborted" };
159
+ if (parts.cancelled()) return { output: parts.collectOutput(), stopReason: 'aborted' }
164
160
  // Flatten post-publication transport failures while preserving diagnostics.
165
161
  try {
166
- parts.onError?.(toError(error), "error");
162
+ parts.onError?.(toError(error), 'error')
167
163
  } catch {
168
164
  // The diagnostic sink cannot reject the run result.
169
165
  }
170
- const collected = parts.collectDiagnostic?.();
171
- const diagnostic = collected === undefined ? undefined : limitSubagentDiagnostic(collected);
166
+ const collected = parts.collectDiagnostic?.()
167
+ const diagnostic = collected === undefined
168
+ ? undefined
169
+ : limitSubagentDiagnostic(collected)
172
170
  return {
173
171
  output: parts.collectOutput(),
174
- ...(diagnostic === undefined ? {} : { diagnostic }),
175
- stopReason: "error",
176
- };
172
+ ...diagnostic === undefined ? {} : { diagnostic },
173
+ stopReason: 'error',
174
+ }
177
175
  } finally {
178
- parts.signal.removeEventListener("abort", parts.onAbort);
176
+ parts.signal.removeEventListener('abort', parts.onAbort)
179
177
  }
180
178
  }
181
179
 
182
180
  /** Inputs to {@link subprocessRunHandle}. */
183
181
  export interface SubprocessRunHandleParts {
184
182
  /** The parent-scoped run id. */
185
- id: SubagentRun["id"];
183
+ id: SubagentRun['id']
186
184
  /** The flattened, never-rejecting result (the seam contract). */
187
- result: Promise<SubagentResult>;
185
+ result: Promise<SubagentResult>
188
186
  /** The request's cancellation signal (the listener is removed on dispose). */
189
- signal: AbortSignal;
187
+ signal: AbortSignal
190
188
  /** The abort listener registered on {@link signal} at start. */
191
- onAbort: () => void;
189
+ onAbort: () => void
192
190
  /** Settle local cancellation so {@link result} resolves without the child. */
193
- requestCancel: () => void;
191
+ requestCancel: () => void
194
192
  /** Tear the child process down to quiescence (backend-owned ladder). */
195
- teardown: () => Promise<void>;
193
+ teardown: () => Promise<void>
196
194
  }
197
195
 
198
196
  /**
@@ -204,16 +202,16 @@ export interface SubprocessRunHandleParts {
204
202
  * @returns the external run handle.
205
203
  */
206
204
  export function subprocessRunHandle(parts: SubprocessRunHandleParts): SubagentRun {
207
- let disposal: Promise<void> | undefined;
205
+ let disposal: Promise<void> | undefined
208
206
  return {
209
207
  id: parts.id,
210
208
  result: parts.result,
211
209
  dispose(): Promise<void> {
212
- if (disposal !== undefined) return disposal;
213
- parts.signal.removeEventListener("abort", parts.onAbort);
214
- parts.requestCancel();
215
- disposal = parts.teardown();
216
- return disposal;
210
+ if (disposal !== undefined) return disposal
211
+ parts.signal.removeEventListener('abort', parts.onAbort)
212
+ parts.requestCancel()
213
+ disposal = parts.teardown()
214
+ return disposal
217
215
  },
218
- };
216
+ }
219
217
  }
@@ -4,35 +4,37 @@
4
4
  * @module @deepseek-ai/dsh-subagent/projection-types
5
5
  */
6
6
 
7
- import type { SessionId, SessionSeq } from "@deepseek-ai/dsh-session/types";
7
+ import type { SessionId, SessionSeq } from '@deepseek-ai/dsh-session/types'
8
8
 
9
9
  /** One current direct-child discovery row materialized from parent facts. */
10
- export type SubagentCatalogEntry = {
11
- readonly id: SessionId;
12
- readonly createdAt: number;
13
- } & (
14
- | { readonly mode: "one-shot"; readonly label?: string }
15
- | { readonly mode: "external"; readonly label?: string }
16
- | { readonly mode: "continuable"; readonly label: string }
17
- | { readonly mode: "unknown"; readonly label?: string }
18
- );
10
+ export type SubagentCatalogEntry =
11
+ & {
12
+ readonly id: SessionId
13
+ readonly createdAt: number
14
+ }
15
+ & (
16
+ | { readonly mode: 'one-shot'; readonly label?: string }
17
+ | { readonly mode: 'external'; readonly label?: string }
18
+ | { readonly mode: 'continuable'; readonly label: string }
19
+ | { readonly mode: 'unknown'; readonly label?: string }
20
+ )
19
21
 
20
22
  /** Durable active-turn timing for one descriptor-backed child session. */
21
23
  export interface SubagentTimingProjection {
22
24
  /** Milliseconds accumulated across completed turns after the child's own descriptor. */
23
- settledMs: number;
25
+ settledMs: number
24
26
  /** Same-cut bounds of the currently open turn, when one has not reached `turn/end`. */
25
27
  active?: {
26
28
  /** Start of the open turn. */
27
- since: number;
29
+ since: number
28
30
  /** Latest event time folded into this projection cut. */
29
- through: number;
30
- };
31
+ through: number
32
+ }
31
33
  /**
32
34
  * Whether the latest closed turn after the child's own descriptor completed
33
35
  * normally; absent while a turn is open or before one closes.
34
36
  */
35
- lastTurnCompleted?: boolean;
37
+ lastTurnCompleted?: boolean
36
38
  }
37
39
 
38
40
  /**
@@ -43,33 +45,33 @@ export interface SubagentTimingProjection {
43
45
  */
44
46
  export type SubagentIdentityProjection =
45
47
  | {
46
- /** A terminal one-shot child. */
47
- mode: "one-shot";
48
- /** Optional durable creation label from the child's descriptor. */
49
- label?: string;
50
- /**
51
- * Seq of the `subagent/descriptor` event this identity was folded from.
52
- * `session.isOwnSeq(seq)` proves the identity comes from the child's
53
- * OWN log suffix — where a descriptor is immutable once appended — and
54
- * not from a fork seed's replayed ancestor descriptor.
55
- */
56
- seq: SessionSeq;
57
- }
48
+ /** A terminal one-shot child. */
49
+ mode: 'one-shot'
50
+ /** Optional durable creation label from the child's descriptor. */
51
+ label?: string
52
+ /**
53
+ * Seq of the `subagent/descriptor` event this identity was folded from.
54
+ * `session.isOwnSeq(seq)` proves the identity comes from the child's
55
+ * OWN log suffix — where a descriptor is immutable once appended — and
56
+ * not from a fork seed's replayed ancestor descriptor.
57
+ */
58
+ seq: SessionSeq
59
+ }
58
60
  | {
59
- /** A resumable conversation. */
60
- mode: "continuable";
61
- /** Durable creation label from the child's descriptor. */
62
- label: string;
63
- /** Seq of the folded descriptor event; see the one-shot arm for the own-suffix proof. */
64
- seq: SessionSeq;
65
- };
61
+ /** A resumable conversation. */
62
+ mode: 'continuable'
63
+ /** Durable creation label from the child's descriptor. */
64
+ label: string
65
+ /** Seq of the folded descriptor event; see the one-shot arm for the own-suffix proof. */
66
+ seq: SessionSeq
67
+ }
66
68
 
67
- declare module "@deepseek-ai/dsh-session-projection/types" {
69
+ declare module '@deepseek-ai/dsh-session-projection/types' {
68
70
  interface SessionProjectionMap {
69
71
  /** Direct children in parent catalog event order, excluding fork-inherited facts. */
70
- subagentCatalog: SubagentCatalogEntry[];
72
+ subagentCatalog: SubagentCatalogEntry[]
71
73
  /** Active-turn duration and latest closed-turn completion for a descriptor-backed subagent session. */
72
- subagentTiming: SubagentTimingProjection;
74
+ subagentTiming: SubagentTimingProjection
73
75
  /**
74
76
  * Identity of a descriptor-backed subagent session. `null` ⟺ no valid
75
77
  * descriptor (missing, malformed, or unrecognized-version — deliberately
@@ -79,6 +81,6 @@ declare module "@deepseek-ai/dsh-session-projection/types" {
79
81
  * identity would survive on the receiving side. The entry itself stays
80
82
  * non-optional.
81
83
  */
82
- subagent: SubagentIdentityProjection | null;
84
+ subagent: SubagentIdentityProjection | null
83
85
  }
84
86
  }
@@ -5,64 +5,50 @@
5
5
  * @module @deepseek-ai/dsh-subagent/projection
6
6
  */
7
7
 
8
- import { z } from "zod";
9
- import { SessionSeq } from "@deepseek-ai/dsh-session";
10
- import type { ProjectionDefinition } from "@deepseek-ai/dsh-session-projection";
11
- import type { SessionEvent } from "@deepseek-ai/dsh-session";
12
- import { foldSubagentDescriptor } from "./descriptor.ts";
13
- import type { SubagentDescriptorData } from "./descriptor.ts";
14
- import type { SubagentIdentityProjection, SubagentTimingProjection } from "./projection-types.ts";
8
+ import { z } from 'zod'
9
+ import { SessionSeq } from '@deepseek-ai/dsh-session'
10
+ import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection'
11
+ import type { SessionEvent } from '@deepseek-ai/dsh-session'
12
+ import { foldSubagentDescriptor } from './descriptor.ts'
13
+ import type { SubagentDescriptorData } from './descriptor.ts'
14
+ import type { SubagentIdentityProjection, SubagentTimingProjection } from './projection-types.ts'
15
15
 
16
16
  /** Fold state for a subagent's latest timing snapshot. */
17
17
  export interface TimingState {
18
18
  /** Milliseconds accumulated across completed post-descriptor turns. */
19
- settledMs: number;
19
+ settledMs: number
20
20
  /** Current open interval kept paired inside the fold. */
21
- active?: { since: number; through: number } | undefined;
21
+ active?: { since: number; through: number } | undefined
22
22
  /** Latest pre-descriptor turn start, promoted when the child's own descriptor arrives. */
23
- pendingTurnStart?: number | undefined;
23
+ pendingTurnStart?: number | undefined
24
24
  /** Whether the fold has crossed a descriptor in this logical log. */
25
- descriptorSeen: boolean;
25
+ descriptorSeen: boolean
26
26
  /** Whether the latest closed post-descriptor turn completed normally; absent while a turn is open or before one closes. */
27
- lastTurnCompleted?: boolean | undefined;
27
+ lastTurnCompleted?: boolean | undefined
28
28
  }
29
29
 
30
- const activeIntervalSchema = z
31
- .object({
32
- since: z.number().int().nonnegative(),
33
- through: z.number().int().nonnegative(),
34
- })
35
- .strict();
30
+ const activeIntervalSchema = z.object({
31
+ since: z.number().int().nonnegative(),
32
+ through: z.number().int().nonnegative(),
33
+ }).strict()
36
34
 
37
- const projectionSchema: z.ZodType<SubagentTimingProjection> = z
38
- .object({
39
- settledMs: z.number().int().nonnegative(),
40
- active: activeIntervalSchema.optional(),
41
- lastTurnCompleted: z.boolean().optional(),
42
- })
43
- .strict()
44
- .transform(({ settledMs, active, lastTurnCompleted }) => ({
45
- settledMs,
46
- ...(active === undefined ? {} : { active }),
47
- ...(lastTurnCompleted === undefined ? {} : { lastTurnCompleted }),
48
- }));
35
+ const projectionSchema: z.ZodType<SubagentTimingProjection> = z.object({
36
+ settledMs: z.number().int().nonnegative(),
37
+ active: activeIntervalSchema.optional(),
38
+ lastTurnCompleted: z.boolean().optional(),
39
+ }).strict().transform(({ settledMs, active, lastTurnCompleted }) => ({
40
+ settledMs,
41
+ ...active === undefined ? {} : { active },
42
+ ...lastTurnCompleted === undefined ? {} : { lastTurnCompleted },
43
+ }))
49
44
 
50
- const timingStateSchema: z.ZodType<TimingState> = z
51
- .object({
52
- settledMs: z.number().int().nonnegative(),
53
- active: activeIntervalSchema.optional(),
54
- pendingTurnStart: z.number().int().nonnegative().optional(),
55
- descriptorSeen: z.boolean(),
56
- lastTurnCompleted: z.boolean().optional(),
57
- })
58
- .strict();
59
-
60
- declare module "@deepseek-ai/dsh-session-projection/types" {
61
- interface SessionProjectionStateMap {
62
- subagentTiming: TimingState;
63
- subagent: IdentityState;
64
- }
65
- }
45
+ const timingStateSchema: z.ZodType<TimingState> = z.object({
46
+ settledMs: z.number().int().nonnegative(),
47
+ active: activeIntervalSchema.optional(),
48
+ pendingTurnStart: z.number().int().nonnegative().optional(),
49
+ descriptorSeen: z.boolean(),
50
+ lastTurnCompleted: z.boolean().optional(),
51
+ }).strict()
66
52
 
67
53
  /**
68
54
  * Fold turn boundaries around the child's own durable descriptor.
@@ -73,59 +59,57 @@ declare module "@deepseek-ai/dsh-session-projection/types" {
73
59
  * the final reset the child's authoritative timing origin.
74
60
  */
75
61
  export const subagentTimingProjectionDefinition = {
76
- key: "subagentTiming",
62
+ key: 'subagentTiming',
77
63
  stateSchema: timingStateSchema,
78
64
  init: () => ({ descriptorSeen: false, settledMs: 0 }),
79
65
  apply: (state, event) => {
80
- if (event.type === "turn/start") {
81
- const { lastTurnCompleted: _closed, ...openState } = state;
66
+ if (event.type === 'turn/start') {
67
+ const { lastTurnCompleted: _closed, ...openState } = state
82
68
  return state.descriptorSeen
83
69
  ? { ...openState, active: { since: event.time, through: event.time } }
84
- : { ...openState, pendingTurnStart: event.time };
70
+ : { ...openState, pendingTurnStart: event.time }
85
71
  }
86
- if (event.type === "subagent/descriptor") {
87
- const activeSince = state.active?.since ?? state.pendingTurnStart;
72
+ if (event.type === 'subagent/descriptor') {
73
+ const activeSince = state.active?.since ?? state.pendingTurnStart
88
74
  return {
89
75
  descriptorSeen: true,
90
76
  settledMs: 0,
91
77
  ...(activeSince === undefined
92
78
  ? {}
93
79
  : { active: { since: activeSince, through: event.time } }),
94
- };
80
+ }
95
81
  }
96
- if (event.type === "turn/end") {
82
+ if (event.type === 'turn/end') {
97
83
  if (!state.descriptorSeen) {
98
- if (state.pendingTurnStart === undefined) return state;
99
- const { pendingTurnStart: _closed, ...next } = state;
100
- return next;
84
+ if (state.pendingTurnStart === undefined) return state
85
+ const { pendingTurnStart: _closed, ...next } = state
86
+ return next
101
87
  }
102
- if (state.active === undefined) return state;
103
- const { active, ...rest } = state;
88
+ if (state.active === undefined) return state
89
+ const { active, ...rest } = state
104
90
  return {
105
91
  ...rest,
106
92
  settledMs: state.settledMs + Math.max(0, event.time - active.since),
107
- lastTurnCompleted: event.data.reason.kind === "completed",
108
- };
93
+ lastTurnCompleted: event.data.reason.kind === 'completed',
94
+ }
109
95
  }
110
- if (state.active === undefined) return state;
111
- return { ...state, active: { ...state.active, through: event.time } };
96
+ if (state.active === undefined) return state
97
+ return { ...state, active: { ...state.active, through: event.time } }
112
98
  },
113
99
  wire: {
114
100
  viewSchema: projectionSchema,
115
- view: (state) => ({
101
+ view: state => ({
116
102
  settledMs: state.settledMs,
117
103
  ...(state.active === undefined ? {} : { active: state.active }),
118
- ...(state.lastTurnCompleted === undefined
119
- ? {}
120
- : { lastTurnCompleted: state.lastTurnCompleted }),
104
+ ...(state.lastTurnCompleted === undefined ? {} : { lastTurnCompleted: state.lastTurnCompleted }),
121
105
  }),
122
106
  },
123
107
  stateVersion: 3,
124
- } satisfies ProjectionDefinition<"subagentTiming", TimingState>;
108
+ } satisfies ProjectionDefinition<'subagentTiming', TimingState>
125
109
 
126
110
  interface IdentityState {
127
111
  /** Identity from the last valid descriptor; absent before one, and after an invalid one. */
128
- identity?: SubagentIdentityProjection | undefined;
112
+ identity?: SubagentIdentityProjection | undefined
129
113
  }
130
114
 
131
115
  // The cast bridges only the optional-label arm: Zod's optional output
@@ -133,49 +117,43 @@ interface IdentityState {
133
117
  // from the public interface. The no-value state itself is the serializable
134
118
  // `null` arm — never `undefined` — so every registry read and push frame
135
119
  // survives JSON.stringify losslessly.
136
- const identityValueSchema = z.discriminatedUnion("mode", [
137
- z
138
- .object({
139
- mode: z.literal("one-shot"),
140
- label: z.string().optional(),
141
- seq: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).transform(SessionSeq),
142
- })
143
- .strict(),
144
- z
145
- .object({
146
- mode: z.literal("continuable"),
147
- label: z.string(),
148
- seq: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).transform(SessionSeq),
149
- })
150
- .strict(),
151
- ]) as unknown as z.ZodType<SubagentIdentityProjection>;
120
+ const identityValueSchema = z.discriminatedUnion('mode', [
121
+ z.object({
122
+ mode: z.literal('one-shot'),
123
+ label: z.string().optional(),
124
+ seq: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).transform(SessionSeq),
125
+ }).strict(),
126
+ z.object({
127
+ mode: z.literal('continuable'),
128
+ label: z.string(),
129
+ seq: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).transform(SessionSeq),
130
+ }).strict(),
131
+ ]) as unknown as z.ZodType<SubagentIdentityProjection>
152
132
 
153
- const identitySchema = identityValueSchema.nullable();
133
+ const identitySchema = identityValueSchema.nullable()
154
134
 
155
- const identityStateSchema: z.ZodType<IdentityState> = z
156
- .object({
157
- identity: identityValueSchema.optional(),
158
- })
159
- .strict();
135
+ const identityStateSchema: z.ZodType<IdentityState> = z.object({
136
+ identity: identityValueSchema.optional(),
137
+ }).strict()
160
138
 
161
139
  /** Interpret one `subagent/descriptor` event's identity; no value when the payload cannot be trusted. */
162
140
  function descriptorIdentity(event: SessionEvent): SubagentIdentityProjection | undefined {
163
- let descriptor: SubagentDescriptorData | undefined;
141
+ let descriptor: SubagentDescriptorData | undefined
164
142
  try {
165
- descriptor = foldSubagentDescriptor([event]);
143
+ descriptor = foldSubagentDescriptor([event])
166
144
  } catch {
167
145
  // Only a malformed current-version payload throws in descriptor parsing;
168
146
  // a projection fold must never throw, so damage folds to no value.
169
- descriptor = undefined;
147
+ descriptor = undefined
170
148
  }
171
- if (descriptor === undefined) return undefined;
172
- return descriptor.mode === "one-shot"
149
+ if (descriptor === undefined) return undefined
150
+ return descriptor.mode === 'one-shot'
173
151
  ? {
174
- mode: "one-shot",
175
- ...(descriptor.label !== undefined ? { label: descriptor.label } : {}),
176
- seq: event.seq,
177
- }
178
- : { mode: "continuable", label: descriptor.label, seq: event.seq };
152
+ mode: 'one-shot',
153
+ ...descriptor.label !== undefined ? { label: descriptor.label } : {},
154
+ seq: event.seq,
155
+ }
156
+ : { mode: 'continuable', label: descriptor.label, seq: event.seq }
179
157
  }
180
158
 
181
159
  /**
@@ -190,16 +168,16 @@ function descriptorIdentity(event: SessionEvent): SubagentIdentityProjection | u
190
168
  * `null` ⟺ no valid descriptor, with the causes deliberately undistinguished.
191
169
  */
192
170
  export const subagentIdentityProjectionDefinition = {
193
- key: "subagent",
171
+ key: 'subagent',
194
172
  stateSchema: identityStateSchema,
195
173
  init: () => ({}),
196
174
  apply: (state, event) => {
197
- if (event.type !== "subagent/descriptor") return state;
198
- const identity = descriptorIdentity(event);
199
- return identity === undefined ? {} : { identity };
175
+ if (event.type !== 'subagent/descriptor') return state
176
+ const identity = descriptorIdentity(event)
177
+ return identity === undefined ? {} : { identity }
200
178
  },
201
- wire: { viewSchema: identitySchema, view: (state) => state.identity ?? null },
179
+ wire: { viewSchema: identitySchema, view: state => state.identity ?? null },
202
180
  // Bumped when the identity gained its `seq` field: an older checkpoint row
203
181
  // would replay into a value the schema rejects, so it must refold instead.
204
182
  stateVersion: 2,
205
- } satisfies ProjectionDefinition<"subagent", IdentityState>;
183
+ } satisfies ProjectionDefinition<'subagent', IdentityState>