@deepstrike/sdk 0.2.38 → 0.2.40

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 (102) hide show
  1. package/README.md +61 -31
  2. package/dist/collaboration/contract.d.ts +2 -2
  3. package/dist/collaboration/contract.js +2 -2
  4. package/dist/collaboration/handoff.d.ts +2 -14
  5. package/dist/collaboration/handoff.js +1 -17
  6. package/dist/collaboration/harness.d.ts +16 -47
  7. package/dist/collaboration/harness.js +57 -158
  8. package/dist/collaboration/index.d.ts +2 -2
  9. package/dist/collaboration/index.js +1 -2
  10. package/dist/collaboration/modes/creator-verifier.js +42 -6
  11. package/dist/collaboration/pool.d.ts +8 -0
  12. package/dist/collaboration/pool.js +56 -3
  13. package/dist/harness/harness.d.ts +97 -97
  14. package/dist/harness/harness.js +190 -144
  15. package/dist/harness/judge.d.ts +3 -2
  16. package/dist/harness/judge.js +7 -3
  17. package/dist/harness/public.d.ts +5 -3
  18. package/dist/harness/public.js +3 -2
  19. package/dist/index.d.ts +22 -7
  20. package/dist/index.js +8 -1
  21. package/dist/kernel.d.ts +12 -39
  22. package/dist/memory/agent.d.ts +8 -41
  23. package/dist/memory/agent.js +34 -117
  24. package/dist/memory/extraction.d.ts +4 -0
  25. package/dist/memory/extraction.js +79 -0
  26. package/dist/memory/in-memory-store.d.ts +28 -15
  27. package/dist/memory/in-memory-store.js +74 -25
  28. package/dist/memory/protocols.d.ts +56 -56
  29. package/dist/memory/protocols.js +1 -0
  30. package/dist/memory/public.d.ts +3 -1
  31. package/dist/memory/public.js +1 -0
  32. package/dist/memory/ranking.d.ts +33 -0
  33. package/dist/memory/ranking.js +77 -0
  34. package/dist/memory/retention.d.ts +17 -0
  35. package/dist/memory/retention.js +54 -0
  36. package/dist/os/public.d.ts +3 -3
  37. package/dist/os/public.js +1 -1
  38. package/dist/providers/base.d.ts +8 -0
  39. package/dist/providers/base.js +0 -0
  40. package/dist/providers/gemini.js +12 -0
  41. package/dist/providers/ollama.js +3 -1
  42. package/dist/providers/openai-responses.js +9 -2
  43. package/dist/providers/profiles.d.ts +5 -3
  44. package/dist/providers/profiles.js +3 -3
  45. package/dist/runtime/context-policy.d.ts +35 -0
  46. package/dist/runtime/context-policy.js +66 -0
  47. package/dist/runtime/eval.d.ts +6 -2
  48. package/dist/runtime/eval.js +2 -2
  49. package/dist/runtime/event-stream.d.ts +9 -0
  50. package/dist/runtime/event-stream.js +25 -11
  51. package/dist/runtime/execution-plane.d.ts +5 -1
  52. package/dist/runtime/execution-plane.js +9 -3
  53. package/dist/runtime/facade.js +2 -1
  54. package/dist/runtime/kernel-event-log.d.ts +0 -2
  55. package/dist/runtime/kernel-event-log.js +61 -13
  56. package/dist/runtime/kernel-primitives-dashboard.js +1 -1
  57. package/dist/runtime/kernel-rebuild.d.ts +13 -0
  58. package/dist/runtime/kernel-rebuild.js +75 -0
  59. package/dist/runtime/kernel-step.d.ts +157 -8
  60. package/dist/runtime/kernel-step.js +220 -7
  61. package/dist/runtime/kernel-transaction-log.d.ts +61 -0
  62. package/dist/runtime/kernel-transaction-log.js +149 -0
  63. package/dist/runtime/large-result-spool.d.ts +3 -1
  64. package/dist/runtime/large-result-spool.js +24 -5
  65. package/dist/runtime/loop-driver.d.ts +1 -1
  66. package/dist/runtime/loop-driver.js +2 -7
  67. package/dist/runtime/mcp-proxy-plane.d.ts +1 -0
  68. package/dist/runtime/mcp-proxy-plane.js +23 -6
  69. package/dist/runtime/os-profile.d.ts +9 -10
  70. package/dist/runtime/os-profile.js +14 -10
  71. package/dist/runtime/os-snapshot.d.ts +19 -0
  72. package/dist/runtime/os-snapshot.js +33 -3
  73. package/dist/runtime/process-sandbox-plane.js +16 -11
  74. package/dist/runtime/reaction-checkpoint.d.ts +51 -0
  75. package/dist/runtime/reaction-checkpoint.js +83 -0
  76. package/dist/runtime/reactive-session.d.ts +9 -3
  77. package/dist/runtime/reactive-session.js +44 -14
  78. package/dist/runtime/reliability.d.ts +48 -0
  79. package/dist/runtime/reliability.js +86 -0
  80. package/dist/runtime/remote-vpc-plane.js +4 -3
  81. package/dist/runtime/run-group.d.ts +38 -35
  82. package/dist/runtime/run-group.js +97 -54
  83. package/dist/runtime/runner.d.ts +117 -78
  84. package/dist/runtime/runner.js +1516 -1197
  85. package/dist/runtime/session-log.d.ts +71 -12
  86. package/dist/runtime/session-log.js +192 -32
  87. package/dist/runtime/session-repair.d.ts +11 -7
  88. package/dist/runtime/session-repair.js +11 -8
  89. package/dist/runtime/sub-agent-orchestrator.d.ts +6 -2
  90. package/dist/runtime/sub-agent-orchestrator.js +45 -18
  91. package/dist/signals/gateway.d.ts +23 -10
  92. package/dist/signals/gateway.js +81 -17
  93. package/dist/signals/scheduled.js +0 -1
  94. package/dist/signals/types.d.ts +21 -13
  95. package/dist/skills/loader.js +12 -2
  96. package/dist/tools/index.d.ts +2 -0
  97. package/dist/types/agent.d.ts +40 -1
  98. package/dist/types/agent.js +61 -1
  99. package/dist/types.d.ts +6 -1
  100. package/dist/workflow/public.d.ts +2 -3
  101. package/dist/workflow/public.js +0 -1
  102. package/package.json +2 -2
@@ -1,7 +1,7 @@
1
1
  import { agentRunSpecToKernel, findSpawnProcessObservation, spawnObservationToManifest } from "../types/agent.js";
2
2
  import { FilteredExecutionPlane } from "./filtered-plane.js";
3
3
  import { WorktreeExecutionPlane } from "./worktree-plane.js";
4
- import { kernelApply } from "./kernel-step.js";
4
+ import { durableKernelApply } from "./kernel-step.js";
5
5
  function terminationFromStatus(status) {
6
6
  const normalized = status.toLowerCase();
7
7
  if (normalized === "completed" ||
@@ -142,6 +142,8 @@ export class SubAgentOrchestrator {
142
142
  async *stream(ctx) {
143
143
  const { childRunner, inheritEvents, cleanupWorktree } = await this.buildChild(ctx);
144
144
  try {
145
+ if (ctx.contextInput)
146
+ childRunner.injectNote(ctx.contextInput);
145
147
  yield* childRunner.run({
146
148
  sessionId: ctx.spec.identity.sessionId,
147
149
  goal: ctx.spec.goal,
@@ -154,22 +156,25 @@ export class SubAgentOrchestrator {
154
156
  }
155
157
  async run(ctx) {
156
158
  if (ctx.harness) {
157
- const { HarnessLoop } = await import("../harness/harness.js");
158
- // W-N4: same construction as the direct path — the harness child now honors the inherited
159
- // system prompt (`system_only`). NOTE: `full` inheritance is NOT consumable under a harness
160
- // (HarnessLoop mints its own per-attempt session ids, so parent events can't be replayed)
161
- // the inherited events are dropped here by construction, not by drift.
162
- const { childRunner, cleanupWorktree } = await this.buildChild(ctx);
163
- const loop = new HarnessLoop(childRunner, ctx.harness.evalProvider, {
164
- maxAttempts: ctx.harness.maxAttempts ?? 3,
159
+ const { AttemptLoop, RuntimeAttemptBody } = await import("../harness/harness.js");
160
+ const { LlmEvalJudge } = await import("../harness/judge.js");
161
+ const { childRunner, inheritEvents, cleanupWorktree } = await this.buildChild(ctx);
162
+ if (ctx.contextInput)
163
+ childRunner.injectNote(ctx.contextInput);
164
+ const loop = new AttemptLoop({
165
+ body: new RuntimeAttemptBody(childRunner),
166
+ judge: new LlmEvalJudge(ctx.harness.evalProvider, false),
167
+ stop: { maxAttempts: ctx.harness.maxAttempts ?? 3 },
165
168
  });
166
169
  let outcome;
167
170
  try {
168
171
  outcome = await loop.run({
172
+ sessionId: ctx.spec.identity.sessionId,
169
173
  goal: ctx.spec.goal,
170
174
  criteria: (ctx.spec.milestones?.phases.flatMap(p => p.criteria) ?? [])
171
175
  .filter((t) => typeof t === "string")
172
176
  .map(text => ({ text, required: true })),
177
+ inheritEvents,
173
178
  });
174
179
  }
175
180
  finally {
@@ -177,12 +182,7 @@ export class SubAgentOrchestrator {
177
182
  }
178
183
  return {
179
184
  agentId: ctx.spec.identity.agentId,
180
- result: {
181
- termination: outcome.passed ? "completed" : "error",
182
- turnsUsed: outcome.iterations,
183
- totalTokensUsed: outcome.totalTokens,
184
- ...(outcome.result ? { finalMessage: { role: "assistant", content: outcome.result, toolCalls: [] } } : {}),
185
- },
185
+ result: attemptOutcomeToLoopResult(outcome),
186
186
  // R3-1: surface nodes the agent submitted under the harness so `runWorkflow` appends them.
187
187
  ...(outcome.submittedNodes?.length ? { submittedNodes: outcome.submittedNodes } : {}),
188
188
  };
@@ -219,8 +219,34 @@ export class SubAgentOrchestrator {
219
219
  }
220
220
  }
221
221
  export const defaultSubAgentOrchestrator = new SubAgentOrchestrator();
222
+ export function attemptOutcomeToLoopResult(outcome) {
223
+ // H4: the run-health axis survives independently from the judge axis. A one-shot judge reject
224
+ // is a healthy completion carrying verdict=false; only exhausting the retry policy promotes the
225
+ // harness-level failure to a workflow error. A body run_error keeps its concrete kernel
226
+ // termination when valid, and normalizes non-kernel statuses (for example invalid_arg) to error.
227
+ const runTermination = terminationFromStatus(outcome.runStatus);
228
+ const termination = outcome.outcome === "exhausted"
229
+ ? "error"
230
+ : outcome.outcome === "run_error" && runTermination !== "error" && runTermination !== "user_abort"
231
+ ? "error"
232
+ : runTermination;
233
+ return {
234
+ termination,
235
+ ...(outcome.result
236
+ ? { finalMessage: { role: "assistant", content: outcome.result, toolCalls: [] } }
237
+ : {}),
238
+ turnsUsed: outcome.turns,
239
+ totalTokensUsed: outcome.totalTokens,
240
+ attempt: {
241
+ outcome: outcome.outcome,
242
+ runStatus: outcome.runStatus,
243
+ attempts: outcome.attempts,
244
+ ...(outcome.verdict ? { verdict: outcome.verdict } : {}),
245
+ },
246
+ };
247
+ }
222
248
  /** Kernel spawn without an active parent run loop (harness / coordinator use). */
223
- export async function spawnStandalone(parentOpts, parentSessionId, spec, orchestrator = defaultSubAgentOrchestrator) {
249
+ export async function spawnStandalone(parentOpts, parentSessionId, spec, orchestrator = defaultSubAgentOrchestrator, contextInput) {
224
250
  const kernel = (await import("../kernel.js")).getKernel();
225
251
  const runtime = new kernel.KernelRuntime({
226
252
  maxTokens: parentOpts.maxTokens,
@@ -228,8 +254,8 @@ export async function spawnStandalone(parentOpts, parentSessionId, spec, orchest
228
254
  timeoutMs: parentOpts.timeoutMs !== undefined ? BigInt(parentOpts.timeoutMs) : undefined,
229
255
  });
230
256
  const pending = [];
231
- kernelApply(runtime, pending, { kind: "start_run", task: { goal: "coordinator", criteria: [] } });
232
- const observations = kernelApply(runtime, pending, {
257
+ await durableKernelApply(runtime, parentOpts.sessionLog, parentSessionId, pending, { kind: "start_run", task: { goal: "coordinator", criteria: [] } });
258
+ const observations = await durableKernelApply(runtime, parentOpts.sessionLog, parentSessionId, pending, {
233
259
  kind: "spawn_sub_agent",
234
260
  spec: agentRunSpecToKernel(spec),
235
261
  parent_session_id: parentSessionId,
@@ -256,5 +282,6 @@ export async function spawnStandalone(parentOpts, parentSessionId, spec, orchest
256
282
  spec,
257
283
  manifest,
258
284
  sessionLog: parentOpts.sessionLog,
285
+ ...(contextInput ? { contextInput } : {}),
259
286
  });
260
287
  }
@@ -1,11 +1,18 @@
1
- import type { RuntimeSignal, SignalSource } from "./types.js";
1
+ import type { RuntimeSignal, SignalClaim, SignalDeliveryReceipt, SignalSource } from "./types.js";
2
2
  import type { ScheduledPrompt } from "./scheduled.js";
3
+ import type { ObserverErrorHandler } from "../runtime/reliability.js";
4
+ export interface SignalGatewayOptions {
5
+ onObserverError?: ObserverErrorHandler;
6
+ /** Injectable wall clock for deterministic lease tests. */
7
+ now?: () => number;
8
+ /** Default claim lease. Must be positive. Default: 30 seconds. */
9
+ defaultLeaseMs?: number;
10
+ }
3
11
  /**
4
12
  * SignalGateway — entry point for all external signals into the agent.
5
13
  *
6
14
  * Implements `SignalSource` so it can be passed directly to `AgentOptions.signalSource`.
7
- * The gateway maintains an internal FIFO queue; `nextSignal()` drains it one entry at a time
8
- * on each agent turn.
15
+ * The gateway maintains an internal FIFO queue and exposes claim/ack/nack delivery leases.
9
16
  *
10
17
  * Responsibilities:
11
18
  * - Cron scheduling: fires ScheduledPrompts at the right wall-clock time (idempotent by goal+time)
@@ -13,16 +20,19 @@ import type { ScheduledPrompt } from "./scheduled.js";
13
20
  * - Listener API: `onSignal()` for side-channel observers that don't need the pull interface
14
21
  */
15
22
  export declare class SignalGateway implements SignalSource {
23
+ private readonly opts;
16
24
  private timers;
17
25
  private queue;
18
26
  private listeners;
19
- /**
20
- * Called by the agent loop each turn. Returns the oldest queued signal or null.
21
- * When `recipient` is given, returns only the oldest signal addressed to it (plus
22
- * unaddressed broadcasts); signals addressed to other recipients stay queued, so one
23
- * shared gateway can serve N peer loops. Omit ⇒ legacy FIFO drain (any signal).
24
- */
25
- nextSignal(recipient?: string): Promise<RuntimeSignal | null>;
27
+ private deliverySeq;
28
+ private leaseSeq;
29
+ constructor(opts?: SignalGatewayOptions);
30
+ /** Claim one visible signal without deleting it. Unacked claims are redelivered after expiry. */
31
+ claimSignal(recipient?: string, leaseMs?: number): Promise<SignalClaim | null>;
32
+ /** Permanently remove the delivery iff the receipt still owns its current lease. */
33
+ ackSignal(receipt: SignalDeliveryReceipt): Promise<boolean>;
34
+ /** Release the current lease for immediate retry. Stale receipts are ignored. */
35
+ nackSignal(receipt: SignalDeliveryReceipt): Promise<boolean>;
26
36
  /** Register a listener that is called synchronously whenever a signal is emitted.
27
37
  * Returns an unsubscribe function — long-lived consumers (e.g. a loop's
28
38
  * `signalAwareSleeper`, re-registered per sleep) must call it or the listener leaks. */
@@ -33,9 +43,12 @@ export declare class SignalGateway implements SignalSource {
33
43
  cancel(goal: string, runAtMs: number): void;
34
44
  /** Push a raw signal directly (e.g. from a webhook handler). */
35
45
  ingest(sig: RuntimeSignal): void;
46
+ /** Fan one logical signal out to a known recipient set. Each recipient gets one queue item. */
47
+ broadcast(recipients: Iterable<string>, sig: RuntimeSignal): void;
36
48
  /** Number of signals currently buffered in the queue. */
37
49
  get depth(): number;
38
50
  /** Clear all pending timers. Call when shutting down to avoid process leaks. */
39
51
  destroy(): void;
40
52
  private emit;
53
+ private currentLeaseIndex;
41
54
  }
@@ -1,9 +1,9 @@
1
+ import { reportObserverFailure } from "../runtime/reliability.js";
1
2
  /**
2
3
  * SignalGateway — entry point for all external signals into the agent.
3
4
  *
4
5
  * Implements `SignalSource` so it can be passed directly to `AgentOptions.signalSource`.
5
- * The gateway maintains an internal FIFO queue; `nextSignal()` drains it one entry at a time
6
- * on each agent turn.
6
+ * The gateway maintains an internal FIFO queue and exposes claim/ack/nack delivery leases.
7
7
  *
8
8
  * Responsibilities:
9
9
  * - Cron scheduling: fires ScheduledPrompts at the right wall-clock time (idempotent by goal+time)
@@ -11,23 +11,59 @@
11
11
  * - Listener API: `onSignal()` for side-channel observers that don't need the pull interface
12
12
  */
13
13
  export class SignalGateway {
14
+ opts;
14
15
  timers = new Map();
15
16
  queue = [];
16
17
  listeners = [];
18
+ deliverySeq = 0;
19
+ leaseSeq = 0;
20
+ constructor(opts = {}) {
21
+ this.opts = opts;
22
+ }
17
23
  // ── SignalSource interface (pull model) ─────────────────────────────────────
18
- /**
19
- * Called by the agent loop each turn. Returns the oldest queued signal or null.
20
- * When `recipient` is given, returns only the oldest signal addressed to it (plus
21
- * unaddressed broadcasts); signals addressed to other recipients stay queued, so one
22
- * shared gateway can serve N peer loops. Omit ⇒ legacy FIFO drain (any signal).
23
- */
24
- async nextSignal(recipient) {
25
- if (recipient === undefined)
26
- return this.queue.shift() ?? null;
27
- const idx = this.queue.findIndex(s => s.recipient === undefined || s.recipient === recipient);
24
+ /** Claim one visible signal without deleting it. Unacked claims are redelivered after expiry. */
25
+ async claimSignal(recipient, leaseMs = this.opts.defaultLeaseMs ?? 30_000) {
26
+ if (!Number.isFinite(leaseMs) || leaseMs <= 0)
27
+ throw new RangeError("leaseMs must be positive");
28
+ const now = this.opts.now?.() ?? Date.now();
29
+ const idx = this.queue.findIndex(entry => {
30
+ const visible = recipient === undefined
31
+ || entry.signal.recipient === undefined
32
+ || entry.signal.recipient === recipient;
33
+ const available = entry.lease === undefined || entry.lease.expiresAtMs <= now;
34
+ return visible && available;
35
+ });
28
36
  if (idx === -1)
29
37
  return null;
30
- return this.queue.splice(idx, 1)[0];
38
+ const entry = this.queue[idx];
39
+ entry.deliveryAttempts += 1;
40
+ const token = `${entry.deliveryId}:lease-${++this.leaseSeq}`;
41
+ const expiresAtMs = now + leaseMs;
42
+ entry.lease = { token, expiresAtMs };
43
+ return {
44
+ deliveryId: entry.deliveryId,
45
+ leaseToken: token,
46
+ signalId: entry.signalId,
47
+ deliveryAttempt: entry.deliveryAttempts,
48
+ leaseExpiresAtMs: expiresAtMs,
49
+ signal: entry.signal,
50
+ };
51
+ }
52
+ /** Permanently remove the delivery iff the receipt still owns its current lease. */
53
+ async ackSignal(receipt) {
54
+ const idx = this.currentLeaseIndex(receipt);
55
+ if (idx === -1)
56
+ return false;
57
+ this.queue.splice(idx, 1);
58
+ return true;
59
+ }
60
+ /** Release the current lease for immediate retry. Stale receipts are ignored. */
61
+ async nackSignal(receipt) {
62
+ const idx = this.currentLeaseIndex(receipt);
63
+ if (idx === -1)
64
+ return false;
65
+ delete this.queue[idx].lease;
66
+ return true;
31
67
  }
32
68
  // ── Push API ────────────────────────────────────────────────────────────────
33
69
  /** Register a listener that is called synchronously whenever a signal is emitted.
@@ -49,7 +85,6 @@ export class SignalGateway {
49
85
  const fire = () => {
50
86
  this.timers.delete(key);
51
87
  this.emit({
52
- kind: "scheduled",
53
88
  source: "cron",
54
89
  signalType: "job",
55
90
  urgency: "normal",
@@ -78,6 +113,16 @@ export class SignalGateway {
78
113
  ingest(sig) {
79
114
  this.emit(sig);
80
115
  }
116
+ /** Fan one logical signal out to a known recipient set. Each recipient gets one queue item. */
117
+ broadcast(recipients, sig) {
118
+ const seen = new Set();
119
+ for (const recipient of recipients) {
120
+ if (!recipient || seen.has(recipient))
121
+ continue;
122
+ seen.add(recipient);
123
+ this.emit({ ...sig, recipient });
124
+ }
125
+ }
81
126
  /** Number of signals currently buffered in the queue. */
82
127
  get depth() {
83
128
  return this.queue.length;
@@ -91,8 +136,27 @@ export class SignalGateway {
91
136
  }
92
137
  // ── Internal ────────────────────────────────────────────────────────────────
93
138
  emit(sig) {
94
- this.queue.push(sig);
95
- for (const l of this.listeners)
96
- l(sig);
139
+ this.queue.push({
140
+ deliveryId: `signal-${++this.deliverySeq}`,
141
+ signalId: crypto.randomUUID(),
142
+ deliveryAttempts: 0,
143
+ signal: sig,
144
+ });
145
+ for (const listener of this.listeners) {
146
+ try {
147
+ listener(sig);
148
+ }
149
+ catch (cause) {
150
+ reportObserverFailure(this.opts.onObserverError, {
151
+ component: "SignalGateway",
152
+ operation: "signal_listener",
153
+ cause,
154
+ });
155
+ }
156
+ }
157
+ }
158
+ currentLeaseIndex(receipt) {
159
+ return this.queue.findIndex(entry => entry.deliveryId === receipt.deliveryId
160
+ && entry.lease?.token === receipt.leaseToken);
97
161
  }
98
162
  }
@@ -11,7 +11,6 @@ export class ScheduledPrompt {
11
11
  }
12
12
  toSignal() {
13
13
  return {
14
- kind: "scheduled",
15
14
  source: "cron",
16
15
  signalType: "job",
17
16
  urgency: "normal",
@@ -7,20 +7,28 @@ export interface RuntimeSignal {
7
7
  urgency: RuntimeSignalUrgency;
8
8
  payload: Record<string, unknown>;
9
9
  dedupeKey?: string;
10
- /** Target a specific session loop (its `sessionId`). Omitted broadcast (any puller). */
10
+ /** Target a specific session loop. Omitted means a shared item consumed by one eligible puller. */
11
11
  recipient?: string;
12
- /** Optional pub/sub topic (carried through; multi-subscriber routing deferred). */
13
- topic?: string;
14
- /** @deprecated Use source/signalType/urgency directly. */
15
- kind?: "interrupt" | "scheduled" | "external";
16
- /** @deprecated Prefer explicit `urgency`. */
17
- priority?: number;
12
+ /** Absolute journal-clock deadline for optional urgency escalation. */
13
+ deadlineMs?: number;
14
+ /** Merge with an unconsumed queued signal carrying the same key. */
15
+ coalesceKey?: string;
16
+ /** Number of host signals deterministically represented by this signal. */
17
+ coalescedCount?: number;
18
18
  }
19
19
  export interface SignalSource {
20
- /**
21
- * Pull the next pending signal. When `recipient` is given, return only signals
22
- * addressed to it (plus unaddressed broadcasts); other recipients' signals stay
23
- * queued. Omit ⇒ legacy FIFO drain (any signal).
24
- */
25
- nextSignal(recipient?: string): Promise<RuntimeSignal | null>;
20
+ claimSignal(recipient?: string, leaseMs?: number): Promise<SignalClaim | null>;
21
+ ackSignal(receipt: SignalDeliveryReceipt): Promise<boolean>;
22
+ nackSignal(receipt: SignalDeliveryReceipt): Promise<boolean>;
23
+ }
24
+ /** Opaque proof that one consumer currently owns a leased signal delivery. */
25
+ export interface SignalDeliveryReceipt {
26
+ deliveryId: string;
27
+ leaseToken: string;
28
+ }
29
+ export interface SignalClaim extends SignalDeliveryReceipt {
30
+ signalId: string;
31
+ deliveryAttempt: number;
32
+ signal: RuntimeSignal;
33
+ leaseExpiresAtMs: number;
26
34
  }
@@ -1,5 +1,12 @@
1
1
  import { readFile, readdir } from "fs/promises";
2
2
  import path from "path";
3
+ const SAFE_SKILL_NAME = /^[A-Za-z0-9_-]+$/;
4
+ function skillPath(skillDir, name) {
5
+ if (!SAFE_SKILL_NAME.test(name)) {
6
+ throw new Error(`invalid skill name "${name}": use only letters, digits, "-", "_"`);
7
+ }
8
+ return path.join(skillDir, `${name}.md`);
9
+ }
3
10
  /** Parse a frontmatter tool list: `read, write` or `[read, write]` → ["read","write"]. */
4
11
  function parseToolList(v) {
5
12
  if (v == null || v === "")
@@ -22,11 +29,14 @@ function parseFrontmatter(content) {
22
29
  }
23
30
  /** Read one skill file and return its body (frontmatter stripped). */
24
31
  export async function readSkillFile(skillDir, name) {
32
+ const file = skillPath(skillDir, name);
25
33
  try {
26
- const raw = await readFile(path.join(skillDir, `${name}.md`), "utf8");
34
+ const raw = await readFile(file, "utf8");
27
35
  return parseFrontmatter(raw).body;
28
36
  }
29
- catch {
37
+ catch (error) {
38
+ if (error.code !== "ENOENT")
39
+ throw error;
30
40
  return null;
31
41
  }
32
42
  }
@@ -1,4 +1,5 @@
1
1
  import type { ToolChunk, ToolSchema, ToolResult } from "../types.js";
2
+ import type { OperationContext } from "../runtime/reliability.js";
2
3
  /** M3/G4: the runtime context a tool may read when executing. Carries the working directory the tool
3
4
  * should operate in — set to a sub-agent's git worktree for `isolation: "worktree"` nodes. A narrow,
4
5
  * dependency-free shape; the execution plane's `RunContext` is structurally assignable to it.
@@ -9,6 +10,7 @@ import type { ToolChunk, ToolSchema, ToolResult } from "../types.js";
9
10
  * the tool still completes successfully — avoiding the foot-gun where a transient audit-store
10
11
  * outage flips an already-committed write into `isError: true` and triggers a duplicate retry. */
11
12
  export interface ToolExecContext {
13
+ operation?: OperationContext;
12
14
  cwd?: string;
13
15
  audit?: (label: string, fn: () => Promise<void> | void) => Promise<void>;
14
16
  }
@@ -90,6 +90,14 @@ export interface LoopResult {
90
90
  * vocabulary (stop → loopContinue=false); the legacy text-sniffed signal is the fallback.
91
91
  * SDK-internal — stripped by `subAgentResultToKernel`. */
92
92
  paceDecision?: import("../runtime/kernel-step.js").PaceDecision;
93
+ /** Two-axis AttemptLoop result. Serialized alongside `termination` so hosts can observe judge
94
+ * failure independently from run health. */
95
+ attempt?: {
96
+ outcome: import("../harness/harness.js").AttemptOutcomeKind;
97
+ runStatus: string;
98
+ attempts: number;
99
+ verdict?: import("../runtime/eval.js").Verdict;
100
+ };
93
101
  }
94
102
  export interface SubAgentResult {
95
103
  agentId: string;
@@ -130,6 +138,9 @@ export type WorkflowTaskSpec = {
130
138
  } | string;
131
139
  /** W3 trust level for a workflow node. */
132
140
  export type NodeTrust = "trusted" | "quarantined";
141
+ export type WorkflowDependencyPolicy = "all_success" | "accept_partial" | "all_terminal" | "optional";
142
+ export type WorkflowNodeStatus = "completed" | "completed_partial" | "failed" | "skipped_upstream_failed";
143
+ export declare function workflowNodeStatusFromTermination(termination: TerminationReason | string): WorkflowNodeStatus;
133
144
  /** One node in a declarative workflow DAG (camelCase host shape). */
134
145
  export interface WorkflowNodeSpec {
135
146
  task: WorkflowTaskSpec;
@@ -171,11 +182,39 @@ export interface WorkflowNodeSpec {
171
182
  maxWallMs?: number;
172
183
  /** Indices of nodes this node depends on. */
173
184
  dependsOn?: number[];
185
+ /** How dependency terminal states gate this node. Defaults to `all_success`. */
186
+ depPolicy?: WorkflowDependencyPolicy;
174
187
  }
175
188
  /** A declarative workflow DAG the kernel runs node-by-node, gating each spawn. */
176
189
  export interface WorkflowSpec {
177
190
  nodes: WorkflowNodeSpec[];
178
191
  }
192
+ export interface KernelWorkflowNodeOutcome {
193
+ node_id: string;
194
+ status: WorkflowNodeStatus;
195
+ termination?: TerminationReason;
196
+ output?: {
197
+ role: Message["role"];
198
+ content: string;
199
+ tool_calls?: Array<{
200
+ id: string;
201
+ name: string;
202
+ arguments?: Record<string, unknown>;
203
+ }>;
204
+ token_count?: number;
205
+ };
206
+ }
207
+ export interface WorkflowNodeOutcome {
208
+ nodeId: string;
209
+ status: WorkflowNodeStatus;
210
+ termination?: TerminationReason;
211
+ output?: Message;
212
+ }
213
+ export interface WorkflowOutcome {
214
+ nodeOutcomes: WorkflowNodeOutcome[];
215
+ outputs: Record<string, string>;
216
+ }
217
+ export declare function workflowNodeOutcomeFromKernel(raw: KernelWorkflowNodeOutcome): WorkflowNodeOutcome;
179
218
  /** Per-node spawn descriptor carried in the `workflow_batch_spawned` observation. */
180
219
  export interface WorkflowSpawnInfo {
181
220
  agent_id: string;
@@ -267,7 +306,7 @@ export declare function generateAndFilter(generators: WorkflowTaskSpec[], filter
267
306
  * Generate→evaluate quality gate (the EvalPipeline successor, #6): a `loop` worker node (re-run up
268
307
  * to `maxIters`, stopping early on a `loop_continue=false` self-signal) + a bias-resistant `verify`
269
308
  * eval node gated on it, carrying the kernel's verdict `outputSchema`. Mirrors the kernel `gen_eval`
270
- * template. For the iterative retry-with-feedback variant, drive it with `HarnessLoop`.
309
+ * template. For the iterative retry-with-feedback variant, drive it with `AttemptLoop`.
271
310
  */
272
311
  export declare function genEval(worker: WorkflowTaskSpec, evaluate: WorkflowTaskSpec, maxIters?: number, extractSkillOnPass?: boolean): WorkflowSpec;
273
312
  /**
@@ -89,6 +89,7 @@ function safeParseToolArgs(raw) {
89
89
  }
90
90
  export function subAgentResultToKernel(result) {
91
91
  const finalMessage = result.result.finalMessage;
92
+ const attempt = result.result.attempt;
92
93
  return {
93
94
  agent_id: result.agentId,
94
95
  result: {
@@ -112,6 +113,30 @@ export function subAgentResultToKernel(result) {
112
113
  ...(result.result.loopContinue !== undefined ? { loop_continue: result.result.loopContinue } : {}),
113
114
  ...(result.result.classifyBranch !== undefined ? { classify_branch: result.result.classifyBranch } : {}),
114
115
  ...(result.result.tournamentWinner !== undefined ? { tournament_winner: result.result.tournamentWinner } : {}),
116
+ ...(attempt
117
+ ? {
118
+ attempt: {
119
+ outcome: attempt.outcome,
120
+ run_status: attempt.runStatus,
121
+ attempts: attempt.attempts,
122
+ ...(attempt.verdict
123
+ ? {
124
+ verdict: {
125
+ passed: attempt.verdict.passed,
126
+ overall_score: attempt.verdict.overallScore,
127
+ feedback: attempt.verdict.feedback,
128
+ details: attempt.verdict.details.map(detail => ({
129
+ criterion: detail.criterion,
130
+ passed: detail.passed,
131
+ score: detail.score,
132
+ feedback: detail.feedback,
133
+ })),
134
+ },
135
+ }
136
+ : {}),
137
+ },
138
+ }
139
+ : {}),
115
140
  },
116
141
  };
117
142
  }
@@ -121,6 +146,35 @@ export function milestoneCheckPass(phaseId) {
121
146
  export function milestoneCheckFail(phaseId, reason) {
122
147
  return { phaseId, passed: false, reason };
123
148
  }
149
+ export function workflowNodeStatusFromTermination(termination) {
150
+ if (termination === "completed")
151
+ return "completed";
152
+ if (termination === "error" || termination === "user_abort")
153
+ return "failed";
154
+ return "completed_partial";
155
+ }
156
+ export function workflowNodeOutcomeFromKernel(raw) {
157
+ const output = raw.output;
158
+ return {
159
+ nodeId: raw.node_id,
160
+ status: raw.status,
161
+ ...(raw.termination ? { termination: raw.termination } : {}),
162
+ ...(output
163
+ ? {
164
+ output: {
165
+ role: output.role,
166
+ content: output.content,
167
+ toolCalls: (output.tool_calls ?? []).map(call => ({
168
+ id: call.id,
169
+ name: call.name,
170
+ arguments: JSON.stringify(call.arguments ?? {}),
171
+ })),
172
+ ...(output.token_count != null ? { tokenCount: output.token_count } : {}),
173
+ },
174
+ }
175
+ : {}),
176
+ };
177
+ }
124
178
  /** G4: a concise, human-readable budget note appended to a coordinator node's goal, so its agent can
125
179
  * size a `submit_workflow_nodes` batch to what is actually available. Returns "" when nothing is
126
180
  * bounded (no quota ⇒ no signal). */
@@ -192,6 +246,7 @@ export function workflowNodeSpecToKernel(n) {
192
246
  ...(n.maxTurns != null ? { max_turns: n.maxTurns } : {}),
193
247
  ...(n.maxWallMs != null ? { max_wall_ms: n.maxWallMs } : {}),
194
248
  ...(n.dependsOn && n.dependsOn.length ? { depends_on: n.dependsOn } : {}),
249
+ dep_policy: n.depPolicy ?? "all_success",
195
250
  };
196
251
  }
197
252
  /** Map a host `WorkflowSpec` to the snake_case kernel JSON (`load_workflow.spec`). */
@@ -321,6 +376,11 @@ const workflowNodesArraySchema = {
321
376
  items: { type: "integer" },
322
377
  description: "Batch-relative, backward-only dependency indices within this submission.",
323
378
  },
379
+ depPolicy: {
380
+ type: "string",
381
+ enum: ["all_success", "accept_partial", "all_terminal", "optional"],
382
+ description: "How dependency terminal states gate this node; defaults to all_success.",
383
+ },
324
384
  },
325
385
  required: ["task", "role"],
326
386
  },
@@ -451,7 +511,7 @@ export function generateAndFilter(generators, filter) {
451
511
  * Generate→evaluate quality gate (the EvalPipeline successor, #6): a `loop` worker node (re-run up
452
512
  * to `maxIters`, stopping early on a `loop_continue=false` self-signal) + a bias-resistant `verify`
453
513
  * eval node gated on it, carrying the kernel's verdict `outputSchema`. Mirrors the kernel `gen_eval`
454
- * template. For the iterative retry-with-feedback variant, drive it with `HarnessLoop`.
514
+ * template. For the iterative retry-with-feedback variant, drive it with `AttemptLoop`.
455
515
  */
456
516
  export function genEval(worker, evaluate, maxIters = 3, extractSkillOnPass = true) {
457
517
  const schema = JSON.parse(getKernel().verdictOutputSchema(extractSkillOnPass));
package/dist/types.d.ts CHANGED
@@ -154,7 +154,6 @@ export interface DoneEvent extends StreamEvent {
154
154
  iterations: number;
155
155
  totalTokens: number;
156
156
  status: string;
157
- dreamResult?: import("./memory/protocols.js").DreamResult;
158
157
  /** ③ loop-agent: the kernel-adjudicated after-round decision (absent on non-loop runs). */
159
158
  paceDecision?: import("./runtime/kernel-step.js").PaceDecision;
160
159
  }
@@ -372,6 +371,12 @@ export interface RenderedContext {
372
371
  * yet) ⇒ the provider falls back to the rolling-pair placement.
373
372
  */
374
373
  frozenPrefixLen?: number;
374
+ budgetOverflow?: ContextBudgetOverflow;
375
+ }
376
+ export interface ContextBudgetOverflow {
377
+ kind: "fixed_context" | "protected_tail";
378
+ requiredTokens: number;
379
+ maxTokens: number;
375
380
  }
376
381
  /**
377
382
  * Runtime execution policy advertised by a provider.
@@ -4,12 +4,11 @@ export { builtinReducers, resolveReducer } from "../runtime/reducers.js";
4
4
  export type { Reducer, ReducerRegistry, ReducerInput } from "../runtime/reducers.js";
5
5
  export { FileWorkflowStore } from "../runtime/workflow-store.js";
6
6
  export { submitWorkflowNodesTool, startWorkflowTool, generateAndFilter, verifyRules, genEval, milestoneCheckPass, milestoneCheckFail, } from "../types/agent.js";
7
- export type { AgentCapabilityFilter, AgentIdentity, AgentIsolation, AgentRunSpec, AgentProcessChangedObservation, ContextInheritance, KernelAgentRole, LoopResult, MilestoneCheckResult, MilestoneContract, MilestonePhase, MilestonePolicy, SubAgentResult, TerminationReason, WorkflowSpawnInfo, WorkflowTaskSpec, } from "../types/agent.js";
7
+ export type { AgentCapabilityFilter, AgentIdentity, AgentIsolation, AgentRunSpec, AgentProcessChangedObservation, ContextInheritance, KernelAgentRole, LoopResult, MilestoneCheckResult, MilestoneContract, MilestonePhase, MilestonePolicy, SubAgentResult, TerminationReason, WorkflowSpawnInfo, WorkflowTaskSpec, WorkflowDependencyPolicy, WorkflowNodeStatus, WorkflowNodeOutcome, WorkflowOutcome, } from "../types/agent.js";
8
8
  export type { AcceptanceCriterion, VerificationContract, ContractCheckResult } from "../collaboration/contract.js";
9
9
  export { ContractBuilder, formatContractForSystemPrompt, contractToCriteriaStrings } from "../collaboration/contract.js";
10
10
  export type { AgentRole, IsolatedVerifierContext, CoordinatorConfig } from "../collaboration/pool.js";
11
- export { ContractDrivenHarness } from "../collaboration/harness.js";
12
- export type { ContractOutcome, ContractHarnessOptions, Violation } from "../collaboration/harness.js";
11
+ export type { ContractOutcome } from "../collaboration/harness.js";
13
12
  export { HandoffBus } from "../collaboration/handoff.js";
14
13
  export type { HandoffArtifact, ContractOutcomeInput } from "../collaboration/handoff.js";
15
14
  export { CreatorVerifierMode, OrchestrationMode } from "../collaboration/modes/creator-verifier.js";
@@ -7,7 +7,6 @@ export { builtinReducers, resolveReducer } from "../runtime/reducers.js";
7
7
  export { FileWorkflowStore } from "../runtime/workflow-store.js";
8
8
  export { submitWorkflowNodesTool, startWorkflowTool, generateAndFilter, verifyRules, genEval, milestoneCheckPass, milestoneCheckFail, } from "../types/agent.js";
9
9
  export { ContractBuilder, formatContractForSystemPrompt, contractToCriteriaStrings } from "../collaboration/contract.js";
10
- export { ContractDrivenHarness } from "../collaboration/harness.js";
11
10
  export { HandoffBus } from "../collaboration/handoff.js";
12
11
  export { CreatorVerifierMode, OrchestrationMode } from "../collaboration/modes/creator-verifier.js";
13
12
  // Skills loader + lower-level tool execution helpers.