@deepstrike/sdk 0.2.36 → 0.2.38

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.
@@ -19,6 +19,16 @@ export interface AgentCapabilityFilter {
19
19
  allowedKinds?: string[];
20
20
  allowedIds?: string[];
21
21
  }
22
+ export interface LoopRoundSpec {
23
+ /** Hard round cap across the loop's lifetime; continue/sleep at the cap is coerced to stop. */
24
+ maxRounds?: number;
25
+ /** Sleep clamp floor (ms). */
26
+ minSleepMs?: number;
27
+ /** Sleep clamp ceiling (ms). */
28
+ maxSleepMs?: number;
29
+ /** Fallback when a round ends without a pace call: "stop" (goal loops, default) | "sleep" (cron loops). */
30
+ defaultAction?: "stop" | "sleep";
31
+ }
22
32
  export interface AgentRunSpec {
23
33
  identity: AgentIdentity;
24
34
  role: KernelAgentRole;
@@ -28,6 +38,9 @@ export interface AgentRunSpec {
28
38
  capabilityFilter?: AgentCapabilityFilter;
29
39
  milestones?: MilestoneContract;
30
40
  metadata?: Record<string, unknown>;
41
+ /** ③ loop-agent rounds: presence makes this run ONE round of a paced loop (gates the
42
+ * kernel `pace` meta-tool and arms the pacing trap). */
43
+ loopRound?: LoopRoundSpec;
31
44
  /** M1/G3: per-agent model preference (e.g. "opus"/"sonnet"/"haiku"); the host resolves it to a
32
45
  * provider via `RuntimeOptions.providerFor`. Host-side routing only — not sent to the kernel. */
33
46
  modelHint?: string;
@@ -72,6 +85,11 @@ export interface LoopResult {
72
85
  classifyBranch?: string;
73
86
  /** A#2 tournament verdict: a judge reports the winning entrant's agent id here. Sent only when set. */
74
87
  tournamentWinner?: string;
88
+ /** ③ loop-agent pacing: the kernel-adjudicated after-round decision, surfaced by the orchestrator
89
+ * from the child's done event. For a loop-node iteration this is the PRIMARY continuation
90
+ * vocabulary (stop → loopContinue=false); the legacy text-sniffed signal is the fallback.
91
+ * SDK-internal — stripped by `subAgentResultToKernel`. */
92
+ paceDecision?: import("../runtime/kernel-step.js").PaceDecision;
75
93
  }
76
94
  export interface SubAgentResult {
77
95
  agentId: string;
@@ -147,6 +165,10 @@ export interface WorkflowNodeSpec {
147
165
  };
148
166
  /** M4/G5: cap this node's child run at `tokenBudget` cumulative tokens (the per-node "use N tokens"). */
149
167
  tokenBudget?: number;
168
+ /** O3: cap this node's child run at `maxTurns` provider turns (falls back to the parent's). */
169
+ maxTurns?: number;
170
+ /** O3: cap this node's child run at `maxWallMs` wall-clock milliseconds. */
171
+ maxWallMs?: number;
150
172
  /** Indices of nodes this node depends on. */
151
173
  dependsOn?: number[];
152
174
  }
@@ -168,7 +190,8 @@ export interface WorkflowSpawnInfo {
168
190
  output_schema?: Record<string, unknown>;
169
191
  /** G2: for a reduce node, the name of the registered host function to run (no LLM). */
170
192
  reducer?: string;
171
- /** G2: the dependency agent ids whose outputs a reduce node consumes. */
193
+ /** The dependency agent ids for EVERY dependent node (W-N2: a DAG edge carries data). A reduce
194
+ * node's registered function consumes them; every other node gets its deps' outputs in context. */
172
195
  input_agent_ids?: string[];
173
196
  /** A#2: present only for a tournament *judge* spawn — the two entrant agent ids whose produced
174
197
  * outputs this judge compares. The runner looks them up and reports the winner as `tournamentWinner`. */
@@ -184,6 +207,10 @@ export interface WorkflowSpawnInfo {
184
207
  classify_labels?: string[];
185
208
  /** M4/G5: the node's per-node cumulative token cap, if set — the runner caps the child run here. */
186
209
  token_budget?: number;
210
+ /** O3: per-node turn cap → the child run's `maxTurns`. */
211
+ max_turns?: number;
212
+ /** O3: per-node wall-clock cap (ms) → the child run's timeout. */
213
+ max_wall_ms?: number;
187
214
  }
188
215
  /** G4 budget-as-signal: the workflow's remaining headroom under the active quota, carried on the
189
216
  * `workflow_batch_spawned` observation so a coordinator node can scale its next submission. */
@@ -46,6 +46,14 @@ export function agentRunSpecToKernel(spec) {
46
46
  out.verification_contract_id = spec.verificationContractId;
47
47
  if (spec.milestones)
48
48
  out.milestones = milestoneContractToKernel(spec.milestones);
49
+ if (spec.loopRound) {
50
+ out.loop_round = {
51
+ ...(spec.loopRound.maxRounds !== undefined ? { max_rounds: spec.loopRound.maxRounds } : {}),
52
+ ...(spec.loopRound.minSleepMs !== undefined ? { min_sleep_ms: spec.loopRound.minSleepMs } : {}),
53
+ ...(spec.loopRound.maxSleepMs !== undefined ? { max_sleep_ms: spec.loopRound.maxSleepMs } : {}),
54
+ ...(spec.loopRound.defaultAction !== undefined ? { default_action: spec.loopRound.defaultAction } : {}),
55
+ };
56
+ }
49
57
  return out;
50
58
  }
51
59
  export function milestoneContractToKernel(contract) {
@@ -180,6 +188,9 @@ export function workflowNodeSpecToKernel(n) {
180
188
  ...(kind ? { kind } : {}),
181
189
  // M4/G5: per-node token cap (additive; omitted when unset).
182
190
  ...(n.tokenBudget != null ? { token_budget: n.tokenBudget } : {}),
191
+ // O3: per-node turn / wall-clock caps (additive; omitted when unset).
192
+ ...(n.maxTurns != null ? { max_turns: n.maxTurns } : {}),
193
+ ...(n.maxWallMs != null ? { max_wall_ms: n.maxWallMs } : {}),
183
194
  ...(n.dependsOn && n.dependsOn.length ? { depends_on: n.dependsOn } : {}),
184
195
  };
185
196
  }
@@ -356,10 +367,15 @@ export const startWorkflowTool = {
356
367
  };
357
368
  /** Build a sub-agent run spec for a kernel-generated workflow node. */
358
369
  export function workflowNodeToSpec(node, parentSessionId) {
370
+ // W-N6 transcript-as-carry: a loop node's iterations share ONE stable session id (the `-i{k}`
371
+ // suffix names the spawn, not the session), so iteration k replays the transcript of 0..k-1 —
372
+ // "do the next increment" actually sees the previous increments. The agent_id keeps the
373
+ // per-iteration suffix (kernel completion routing).
374
+ const sessionNodeId = node.loop_max_iters != null ? node.agent_id.replace(/-i\d+$/, "") : node.agent_id;
359
375
  return {
360
376
  identity: {
361
377
  agentId: node.agent_id,
362
- sessionId: `${parentSessionId}-${node.agent_id}`,
378
+ sessionId: `${parentSessionId}-${sessionNodeId}`,
363
379
  isSubAgent: true,
364
380
  parentSessionId,
365
381
  },
@@ -370,6 +386,14 @@ export function workflowNodeToSpec(node, parentSessionId) {
370
386
  ...(node.model_hint ? { modelHint: node.model_hint } : {}),
371
387
  // M4/G5: carry the node's token cap so the orchestrator can bound the child run.
372
388
  ...(node.token_budget != null ? { tokenBudget: node.token_budget } : {}),
389
+ // O3: carry the node's turn / wall-clock caps (the orchestrator already honors these).
390
+ ...(node.max_turns != null ? { maxTurns: node.max_turns } : {}),
391
+ ...(node.max_wall_ms != null ? { maxWallMs: node.max_wall_ms } : {}),
392
+ // DW-3 one continuation vocabulary: a loop ITERATION runs with the pacing trap armed, so the
393
+ // agent signals continue/stop through the kernel-adjudicated `pace` meta-tool instead of a
394
+ // text-sniffed JSON blob. One iteration = one round; the DAG (not max_rounds) caps iterations,
395
+ // and default stop means "ended without pacing" = done — the CC silence-is-completion contract.
396
+ ...(node.loop_max_iters != null ? { loopRound: { defaultAction: "stop" } } : {}),
373
397
  };
374
398
  }
375
399
  /** Build the host manifest for a kernel-generated workflow node. */
package/dist/types.d.ts CHANGED
@@ -155,6 +155,8 @@ export interface DoneEvent extends StreamEvent {
155
155
  totalTokens: number;
156
156
  status: string;
157
157
  dreamResult?: import("./memory/protocols.js").DreamResult;
158
+ /** ③ loop-agent: the kernel-adjudicated after-round decision (absent on non-loop runs). */
159
+ paceDecision?: import("./runtime/kernel-step.js").PaceDecision;
158
160
  }
159
161
  export interface ErrorEvent extends StreamEvent {
160
162
  type: "error";
@@ -203,6 +205,54 @@ export interface ToolAuditFailedEvent extends StreamEvent {
203
205
  label: string;
204
206
  error: string;
205
207
  }
208
+ /** Kernel session-entropy measurement at a completed turn boundary. "Entropy" = session
209
+ * disorder: repetition, tool failures, rollbacks, context pressure. The component vector is
210
+ * the contract; `score` is a versioned default fold (`scoreVersion`). All normalized
211
+ * components are in [0, 1]. */
212
+ export interface EntropySample {
213
+ turn: number;
214
+ score: number;
215
+ scoreVersion: number;
216
+ /** Context pressure after this boundary's eviction pass. */
217
+ rho: number;
218
+ /** Consecutive-identical-turn streak, normalized against the RepeatFuse deny rung. */
219
+ repeatPressure: number;
220
+ /** Errored tool results / total tool results over the sliding window. */
221
+ failureRate: number;
222
+ /** Raw rollback count inside the window (normalize with `windowTurns`). */
223
+ rollbacksInWindow: number;
224
+ /** Effective window size in completed turns. */
225
+ windowTurns: number;
226
+ }
227
+ /** One kernel entropy sample, emitted once per completed turn (a heartbeat watch source:
228
+ * subscribe to drive an external supervisor without tailing the audit log). */
229
+ export interface EntropySampleEvent extends StreamEvent {
230
+ type: "entropy_sample";
231
+ sample: EntropySample;
232
+ }
233
+ /** The opt-in kernel entropy watch tripped: `score` crossed `threshold` while armed and
234
+ * cooled down (see `RunnerOptions.entropyWatch`). Correlate components via the same-turn
235
+ * `entropy_sample` event. */
236
+ export interface EntropyAlertEvent extends StreamEvent {
237
+ type: "entropy_alert";
238
+ turn: number;
239
+ score: number;
240
+ threshold: number;
241
+ }
242
+ /** Opt-in kernel-side threshold watch over the per-turn entropy score. Sampling itself is
243
+ * unconditional; this only controls alerting. `notifyModel` additionally routes the alert
244
+ * into the model's own signal channel (durable `[SIGNAL]` directive at the next boundary) —
245
+ * leave it off when a host supervisor injects task-aware guidance itself. */
246
+ export interface EntropyWatchOptions {
247
+ enabled?: boolean;
248
+ /** Alert when `score >= threshold` (kernel default 0.65). */
249
+ threshold?: number;
250
+ /** Re-arm only after the score falls below `threshold - hysteresis` (default 0.1). */
251
+ hysteresis?: number;
252
+ /** Minimum completed turns between two alerts (default 4). */
253
+ cooldownTurns?: number;
254
+ notifyModel?: boolean;
255
+ }
206
256
  export interface TokenUsage {
207
257
  /** Full prompt size: uncached input + cache reads + cache writes. */
208
258
  inputTokens: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepstrike/sdk",
3
- "version": "0.2.36",
3
+ "version": "0.2.38",
4
4
  "description": "DeepStrike Node.js SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -72,7 +72,7 @@
72
72
  },
73
73
  "dependencies": {
74
74
  "@anthropic-ai/sdk": "^0.99.0",
75
- "@deepstrike/core": "0.2.36",
75
+ "@deepstrike/core": "0.2.38",
76
76
  "@google/generative-ai": "^0.24.1",
77
77
  "openai": "^5.23.2"
78
78
  },