@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.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export { runAgent, runFanout } from "./runtime/facade.js";
2
+ export { runLoop, LoopDriver, foldLoopState } from "./runtime/loop-driver.js";
3
+ export type { LoopSpec, LoopOutcome } from "./runtime/loop-driver.js";
2
4
  export type { RunAgentOptions, RunFanoutOptions } from "./runtime/facade.js";
3
5
  export { RuntimeRunner, collectText } from "./runtime/runner.js";
4
6
  export type { RuntimeOptions } from "./runtime/runner.js";
@@ -29,5 +31,5 @@ export { Governance } from "./governance.js";
29
31
  export type { GovernanceVerdict, GovernancePolicy, GovernanceConstraint } from "./governance.js";
30
32
  export { AgentPool } from "./collaboration/pool.js";
31
33
  export type { RuntimeSignal, SignalSource } from "./signals/types.js";
32
- export type { Message, ToolCall, ToolResult, ToolSchema, ContentPart, TextPart, ImagePart, AudioPart, StreamEvent, TextDelta, ThinkingDelta, ToolCallEvent, ToolChunk, ToolDeltaEvent, ToolSuspendEvent, ToolResultEvent, ToolAuditFailedEvent, DoneEvent, ErrorEvent, PermissionRequestEvent, PermissionResolvedEvent, PermissionResponse, LLMProvider, RetryConfig, TokenUsage, } from "./types.js";
34
+ export type { Message, ToolCall, ToolResult, ToolSchema, ContentPart, TextPart, ImagePart, AudioPart, StreamEvent, TextDelta, ThinkingDelta, ToolCallEvent, ToolChunk, ToolDeltaEvent, ToolSuspendEvent, ToolResultEvent, ToolAuditFailedEvent, DoneEvent, ErrorEvent, PermissionRequestEvent, PermissionResolvedEvent, PermissionResponse, EntropySample, EntropySampleEvent, EntropyAlertEvent, EntropyWatchOptions, LLMProvider, RetryConfig, TokenUsage, } from "./types.js";
33
35
  export type { WorkflowSpec, WorkflowNodeSpec, } from "./types/agent.js";
package/dist/index.js CHANGED
@@ -12,6 +12,8 @@
12
12
  // ╚══════════════════════════════════════════════════════════════════════════╝
13
13
  // ── Start here: the canonical entry points ─────────────────────────────────
14
14
  export { runAgent, runFanout } from "./runtime/facade.js";
15
+ // ③ dynamic loop agents: self-pacing rounds over the kernel pacing trap.
16
+ export { runLoop, LoopDriver, foldLoopState } from "./runtime/loop-driver.js";
15
17
  export { RuntimeRunner, collectText } from "./runtime/runner.js";
16
18
  // ── Execution plane + session log (the defaults) ────────────────────────────
17
19
  export { LocalExecutionPlane } from "./runtime/execution-plane.js";
@@ -1,6 +1,6 @@
1
1
  export { DEFAULT_NATIVE_ATTENTION_POLICY, DEFAULT_NATIVE_GOVERNANCE_POLICY, assertNativeProfile, osProfile, } from "../runtime/os-profile.js";
2
2
  export type { NativeOsProfile, OsProfileId } from "../runtime/os-profile.js";
3
- export { rebuildOsSnapshotFromSessionEvents, sessionLogHasRequiredCategories } from "../runtime/os-snapshot.js";
3
+ export { rebuildOsSnapshotFromSessionEvents } from "../runtime/os-snapshot.js";
4
4
  export type { OsSnapshot } from "../runtime/os-snapshot.js";
5
5
  export type { KernelEventCategory } from "../runtime/kernel-event-log.js";
6
6
  export { KernelPrimitivesDashboard } from "../runtime/kernel-primitives-dashboard.js";
package/dist/os/public.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // `@deepstrike/sdk/os` — Agent-OS diagnostics, profiles, signal/permission machinery, replay-testing,
2
2
  // and the scheduler/quota/policy types referenced by advanced `RuntimeOptions` fields.
3
3
  export { DEFAULT_NATIVE_ATTENTION_POLICY, DEFAULT_NATIVE_GOVERNANCE_POLICY, assertNativeProfile, osProfile, } from "../runtime/os-profile.js";
4
- export { rebuildOsSnapshotFromSessionEvents, sessionLogHasRequiredCategories } from "../runtime/os-snapshot.js";
4
+ export { rebuildOsSnapshotFromSessionEvents } from "../runtime/os-snapshot.js";
5
5
  export { KernelPrimitivesDashboard } from "../runtime/kernel-primitives-dashboard.js";
6
6
  // Signals + SDK-side permissions.
7
7
  export { ScheduledPrompt } from "../signals/scheduled.js";
@@ -15,6 +15,7 @@
15
15
  import { RuntimeRunner, collectText } from "./runner.js";
16
16
  import { LocalExecutionPlane } from "./execution-plane.js";
17
17
  import { InMemorySessionLog } from "./session-log.js";
18
+ import { fanoutSynthesize } from "../types/agent.js";
18
19
  /**
19
20
  * Run a single agent to completion and return its final text — `RuntimeRunner` + `run` + `collectText`
20
21
  * in one call. Register tools by passing `tools`; everything else has a working default.
@@ -46,17 +47,16 @@ export async function runFanout(opts) {
46
47
  maxTokens: opts.maxTokens ?? 32_000,
47
48
  ...(opts.maxTurns !== undefined ? { maxTurns: opts.maxTurns } : {}),
48
49
  });
49
- const workerRole = opts.workerRole ?? "explore";
50
- const spec = {
51
- nodes: [
52
- ...opts.tasks.map(task => ({ task, role: workerRole })),
53
- {
54
- task: opts.synthesize,
55
- role: opts.synthesisRole ?? "plan",
56
- dependsOn: opts.tasks.map((_, i) => i),
57
- },
58
- ],
59
- };
50
+ // W-N8: build the spec via the ONE fanout template (it pins the pattern's isolation /
51
+ // context-inheritance choices — read-only system-only workers, full-context synthesizer — which
52
+ // this facade used to silently drop), then apply the caller's role overrides on top.
53
+ const spec = fanoutSynthesize(opts.tasks, opts.synthesize);
54
+ if (opts.workerRole) {
55
+ for (const node of spec.nodes.slice(0, opts.tasks.length))
56
+ node.role = opts.workerRole;
57
+ }
58
+ if (opts.synthesisRole)
59
+ spec.nodes[spec.nodes.length - 1].role = opts.synthesisRole;
60
60
  const outcome = await runner.runWorkflow(spec, opts.sessionId ? { sessionId: opts.sessionId } : undefined);
61
61
  // The synthesis node is the last spec node; the kernel ids nodes `wf-node{index}`. Prefer that id,
62
62
  // but fall back to the last completed node's output so a kernel id-scheme change can't silently
@@ -3,12 +3,6 @@ import type { SessionEvent } from "./session-log.js";
3
3
  /** Agent OS kernel event category (Phase 5). */
4
4
  export type KernelEventCategory = "syscall" | "sched" | "mm" | "proc" | "ipc";
5
5
  export declare function categoryForKind(kind: string): KernelEventCategory;
6
- export declare function withCategory<T extends {
7
- kind: string;
8
- }>(event: T): T & {
9
- category: KernelEventCategory;
10
- primitive: KernelPrimitive;
11
- };
12
6
  type CompressionAction = Extract<SessionEvent, {
13
7
  kind: "compressed";
14
8
  }>["action"];
@@ -22,33 +22,16 @@ export function categoryForKind(kind) {
22
22
  return "sched";
23
23
  }
24
24
  }
25
- export function withCategory(event) {
26
- const category = categoryForKind(event.kind);
27
- return {
28
- ...event,
29
- category,
30
- primitive: primitiveForCategory(category),
31
- };
32
- }
33
25
  export function kernelObservationToSessionEvent(obs, turn, opts = {}) {
34
26
  const t = obs.turn ?? turn;
35
27
  const compressionAction = opts.compressionAction ?? (() => undefined);
36
28
  switch (obs.kind) {
37
- case "page_out":
38
- return withCategory({
39
- kind: "page_out",
40
- turn: t,
41
- action: compressionAction(obs.action),
42
- summary: obs.summary,
43
- tier_hint: obs.tier_hint ?? "durable",
44
- message_count: Array.isArray(obs.archived) ? obs.archived.length : 0,
45
- });
46
29
  case "compressed": {
47
30
  const latest = opts.latestSeq ?? -1;
48
31
  const start = opts.nextArchiveStart ?? 0;
49
32
  if (latest < start)
50
33
  return null;
51
- return withCategory({
34
+ return {
52
35
  kind: "compressed",
53
36
  turn: t,
54
37
  archived_seq_range: [start, latest],
@@ -57,24 +40,24 @@ export function kernelObservationToSessionEvent(obs, turn, opts = {}) {
57
40
  summary_tokens: obs.summary ? Math.max(1, Math.ceil(obs.summary.length / 4)) : undefined,
58
41
  archive_ref: opts.archiveRef,
59
42
  preserved_refs: opts.preservedRefs ?? [],
60
- });
43
+ };
61
44
  }
62
45
  case "renewed":
63
- return withCategory({
46
+ return {
64
47
  kind: "context_renewed",
65
48
  turn: t,
66
49
  sprint: obs.sprint ?? 0,
67
50
  handoff_ref: "",
68
- });
51
+ };
69
52
  case "rollbacked":
70
- return withCategory({
53
+ return {
71
54
  kind: "rollbacked",
72
55
  turn: t,
73
56
  checkpoint_history_len: obs.checkpoint_history_len ?? 0,
74
57
  reason: obs.reason,
75
- });
58
+ };
76
59
  case "capability_changed":
77
- return withCategory({
60
+ return {
78
61
  kind: "capability_changed",
79
62
  turn: t,
80
63
  added: obs.added ?? [],
@@ -84,36 +67,48 @@ export function kernelObservationToSessionEvent(obs, turn, opts = {}) {
84
67
  ...(obs.version != null && { version: obs.version }),
85
68
  ...(obs.mounted_by != null && { mounted_by: obs.mounted_by }),
86
69
  ...(obs.mount_reason != null && { mount_reason: obs.mount_reason }),
87
- });
70
+ };
88
71
  case "milestone_advanced":
89
- return withCategory({
72
+ return {
90
73
  kind: "milestone_advanced",
91
74
  turn: t,
92
75
  phase_id: obs.phase_id ?? "",
93
76
  capabilities_unlocked: obs.capabilities_unlocked ?? [],
94
- });
77
+ };
95
78
  case "milestone_blocked":
96
- return withCategory({
79
+ return {
97
80
  kind: "milestone_blocked",
98
81
  turn: t,
99
82
  phase_id: obs.phase_id ?? "",
100
83
  reason: typeof obs.reason === "string" ? obs.reason : "",
101
- });
102
- case "milestone_evidence":
103
- return withCategory({
104
- kind: "milestone_evidence",
105
- turn: t,
106
- phase_id: obs.phase_id ?? "",
107
- evidence: obs.evidence ?? [],
108
- });
84
+ };
109
85
  case "checkpoint_taken":
110
- return withCategory({
86
+ return {
111
87
  kind: "checkpoint_taken",
112
88
  turn: t,
113
89
  history_len: obs.history_len ?? 0,
114
- });
90
+ };
91
+ case "entropy_sample":
92
+ return {
93
+ kind: "entropy_sample",
94
+ turn: t,
95
+ score: obs.score ?? 0,
96
+ score_version: obs.score_version ?? 0,
97
+ rho: obs.rho ?? 0,
98
+ repeat_pressure: obs.repeat_pressure ?? 0,
99
+ failure_rate: obs.failure_rate ?? 0,
100
+ rollbacks_in_window: obs.rollbacks_in_window ?? 0,
101
+ window_turns: obs.window_turns ?? 0,
102
+ };
103
+ case "entropy_alert":
104
+ return {
105
+ kind: "entropy_alert",
106
+ turn: t,
107
+ score: obs.score ?? 0,
108
+ threshold: obs.threshold ?? 0,
109
+ };
115
110
  case "agent_process_changed":
116
- return withCategory({
111
+ return {
117
112
  kind: "agent_process_changed",
118
113
  turn: t,
119
114
  agent_id: obs.agent_id ?? "",
@@ -126,47 +121,47 @@ export function kernelObservationToSessionEvent(obs, turn, opts = {}) {
126
121
  ...(obs.result_termination
127
122
  ? { result_termination: obs.result_termination }
128
123
  : {}),
129
- });
124
+ };
130
125
  case "tool_gated":
131
- return withCategory({
126
+ return {
132
127
  kind: "tool_gated",
133
128
  turn: t,
134
129
  call_id: obs.call_id ?? "",
135
130
  tool: obs.tool ?? "",
136
131
  reason: typeof obs.reason === "string" ? obs.reason : "",
137
- });
132
+ };
138
133
  case "signal_disposed":
139
- return withCategory({
134
+ return {
140
135
  kind: "signal_disposed",
141
136
  turn: t,
142
137
  signal_id: obs.signal_id ?? "",
143
138
  disposition: obs.disposition ?? "",
144
139
  queue_depth: obs.queue_depth ?? 0,
145
- });
140
+ };
146
141
  case "budget_exceeded":
147
- return withCategory({
142
+ return {
148
143
  kind: "budget_exceeded",
149
144
  turn: t,
150
145
  budget: obs.budget ?? "",
151
- });
146
+ };
152
147
  case "suspended":
153
- return withCategory({
148
+ return {
154
149
  kind: "suspended",
155
150
  turn: t,
156
151
  reason: typeof obs.reason === "string" ? obs.reason : "",
157
152
  pending_calls: obs.pending_calls ?? [],
158
- });
153
+ };
159
154
  case "resumed":
160
- return withCategory({
155
+ return {
161
156
  kind: "resumed",
162
157
  turn: t,
163
158
  approved: obs.approved ?? [],
164
159
  denied: obs.denied ?? [],
165
- });
160
+ };
166
161
  case "page_in_requested":
167
162
  return null;
168
163
  case "large_result_spooled":
169
- return withCategory({
164
+ return {
170
165
  kind: "large_result_spooled",
171
166
  turn: t,
172
167
  call_id: obs.call_id ?? "",
@@ -174,51 +169,51 @@ export function kernelObservationToSessionEvent(obs, turn, opts = {}) {
174
169
  original_size: obs.original_size ?? 0,
175
170
  preview_size: obs.preview_size ?? 0,
176
171
  spool_ref: opts.spoolRef,
177
- });
172
+ };
178
173
  case "memory_written":
179
- return withCategory({
174
+ return {
180
175
  kind: "memory_written",
181
176
  turn: t,
182
177
  memory_id: obs.memory_id ?? "",
183
178
  memory_kind: obs.memory_kind ?? "",
184
179
  size_bytes: obs.size_bytes ?? 0,
185
- });
180
+ };
186
181
  case "memory_queried":
187
- return withCategory({
182
+ return {
188
183
  kind: "memory_queried",
189
184
  turn: t,
190
185
  query_context: obs.query_context ?? "",
191
186
  requested_k: obs.requested_k ?? 0,
192
187
  requires_async_response: obs.requires_async_response ?? false,
193
- });
188
+ };
194
189
  case "memory_validation_failed":
195
- return withCategory({
190
+ return {
196
191
  kind: "memory_validation_failed",
197
192
  turn: t,
198
193
  memory_id: obs.memory_id ?? "",
199
194
  error: obs.error ?? "",
200
- });
195
+ };
201
196
  case "workflow_batch_spawned": {
202
197
  // Batch metadata persisted for resume recovery; individual nodes are
203
198
  // recorded when they complete (via workflow_node_completed).
204
199
  const nodes = obs.nodes ?? [];
205
- return withCategory({
200
+ return {
206
201
  kind: "workflow_batch_spawned",
207
202
  turn: t,
208
203
  node_count: nodes.length,
209
204
  node_ids: nodes.map((n) => n.agent_id ?? ""),
210
- });
205
+ };
211
206
  }
212
207
  case "workflow_completed": {
213
208
  const completed = obs.completed ?? [];
214
209
  const failed = obs.failed ?? [];
215
- return withCategory({
210
+ return {
216
211
  kind: "workflow_completed",
217
212
  turn: t,
218
213
  completed,
219
214
  failed,
220
215
  total_nodes: completed.length + failed.length,
221
- });
216
+ };
222
217
  }
223
218
  default:
224
219
  return null;
@@ -1,4 +1,4 @@
1
- import type { Message, RenderedContext, TaskUpdate, ToolCall, ToolResult, ToolSchema } from "../types.js";
1
+ import type { EntropySample, Message, RenderedContext, TaskUpdate, ToolCall, ToolResult, ToolSchema } from "../types.js";
2
2
  import type { SkillMetadata } from "../skills/loader.js";
3
3
  import type { RollbackReason } from "./session-log.js";
4
4
  export declare const KERNEL_ABI_VERSION = 1;
@@ -11,10 +11,19 @@ export interface KernelRuntimeHandle {
11
11
  drainNewMessages(): Message[];
12
12
  preservedRefs(): string[];
13
13
  }
14
+ export interface PaceDecision {
15
+ action: "continue" | "sleep" | "stop";
16
+ delayMs?: number;
17
+ reason: string;
18
+ /** Set when the kernel trap coerced the model's proposal (clamped delay / forced stop). */
19
+ coercedFrom?: string;
20
+ }
14
21
  export interface KernelLoopResult {
15
22
  termination: string;
16
23
  turnsUsed: number;
17
24
  totalTokensUsed: number;
25
+ /** ③ loop-agent: the kernel-adjudicated after-round decision (absent on non-loop runs). */
26
+ paceDecision?: PaceDecision;
18
27
  }
19
28
  export type MilestoneVerifierKind = {
20
29
  kind: "machine_check";
@@ -105,6 +114,14 @@ export interface KernelObservation {
105
114
  /** workflow_completed. */
106
115
  completed?: string[];
107
116
  failed?: string[];
117
+ score?: number;
118
+ score_version?: number;
119
+ rho?: number;
120
+ repeat_pressure?: number;
121
+ failure_rate?: number;
122
+ rollbacks_in_window?: number;
123
+ window_turns?: number;
124
+ threshold?: number;
108
125
  }
109
126
  export declare function toolSchemaToKernel(schema: ToolSchema): Record<string, unknown>;
110
127
  export declare function skillMetadataToKernel(skill: SkillMetadata): Record<string, unknown>;
@@ -116,6 +133,8 @@ export declare function capabilitySkill(skill: SkillMetadata): Record<string, un
116
133
  export declare function capabilityMarker(kind: string, id: string, description: string): Record<string, unknown>;
117
134
  export declare function capabilityCommandMount(capability: Record<string, unknown>, mountedBy?: string, mountReason?: string): Record<string, unknown>;
118
135
  export declare function capabilityCommandUnmount(capabilityKind: string, id: string): Record<string, unknown>;
136
+ /** Camel-case an `entropy_sample` kernel observation into the SDK's `EntropySample`. */
137
+ export declare function entropySampleFromObservation(obs: KernelObservation): EntropySample;
119
138
  export declare function kernelApply(runtime: KernelRuntimeHandle, pending: KernelObservation[], event: Record<string, unknown>): KernelObservation[];
120
139
  export declare function kernelAction(runtime: KernelRuntimeHandle, pending: KernelObservation[], event: Record<string, unknown>): KernelRunnerAction;
121
140
  /**
@@ -135,6 +135,19 @@ export function capabilityCommandUnmount(capabilityKind, id) {
135
135
  function parseStep(raw) {
136
136
  return JSON.parse(raw);
137
137
  }
138
+ /** Camel-case an `entropy_sample` kernel observation into the SDK's `EntropySample`. */
139
+ export function entropySampleFromObservation(obs) {
140
+ return {
141
+ turn: obs.turn ?? 0,
142
+ score: obs.score ?? 0,
143
+ scoreVersion: obs.score_version ?? 0,
144
+ rho: obs.rho ?? 0,
145
+ repeatPressure: obs.repeat_pressure ?? 0,
146
+ failureRate: obs.failure_rate ?? 0,
147
+ rollbacksInWindow: obs.rollbacks_in_window ?? 0,
148
+ windowTurns: obs.window_turns ?? 0,
149
+ };
150
+ }
138
151
  function kernelMessageToSdk(raw) {
139
152
  const content = raw.content;
140
153
  const message = {
@@ -240,12 +253,24 @@ function mapKernelAction(raw) {
240
253
  };
241
254
  case "done": {
242
255
  const result = raw.result ?? {};
256
+ const pace = result.pace_decision;
243
257
  return {
244
258
  kind: "done",
245
259
  result: {
246
260
  termination: String(result.termination ?? "error"),
247
261
  turnsUsed: Number(result.turns_used ?? 0),
248
262
  totalTokensUsed: Number(result.total_tokens_used ?? 0),
263
+ // ③ loop-agent: the kernel-adjudicated after-round decision (absent on non-loop runs).
264
+ ...(pace
265
+ ? {
266
+ paceDecision: {
267
+ action: (pace.action ?? "stop"),
268
+ delayMs: pace.delay_ms,
269
+ reason: pace.reason ?? "",
270
+ coercedFrom: pace.coerced_from,
271
+ },
272
+ }
273
+ : {}),
249
274
  },
250
275
  };
251
276
  }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * ③ Dynamic loop-agent engineering system — the SDK driver.
3
+ *
4
+ * A loop agent is NOT a new execution engine:
5
+ * - a ROUND is exactly one bounded `RuntimeRunner.run()` (compaction, RepeatFuse,
6
+ * criteria gate, and budget verdicts all apply per round for free);
7
+ * - CONTINUITY is the session log replayed under ONE stable sessionId;
8
+ * - LIFETIME GOVERNANCE is the RunGroup the rounds are members of;
9
+ * - the only new decision — what happens AFTER a round — is the model-proposed,
10
+ * kernel-adjudicated `pace` verb (see the kernel pacing trap). The kernel never
11
+ * sleeps; all timers and judge calls live here, in SDK I/O land.
12
+ *
13
+ * Durable pacing: every round appends `round_started` / `round_paced` to the loop's
14
+ * session log, so `LoopDriver.resume()`-style recovery is a fold over the log
15
+ * (the SessionLogGroupBudgetStore pattern) — zero new storage. A stateless host
16
+ * reads `wake_at_ms` from the fold and re-arms via its own cron/queue; an
17
+ * in-process host lets `run()` sleep inline.
18
+ */
19
+ import type { RuntimeRunner } from "./runner.js";
20
+ import type { SessionEvent } from "./session-log.js";
21
+ import type { PaceDecision } from "./kernel-step.js";
22
+ import type { StreamEvent } from "../types.js";
23
+ export interface LoopSpec {
24
+ /** Stable loop id = the ONE session id every round replays (transcript continuity). */
25
+ loopId: string;
26
+ goal: string;
27
+ criteria?: string[];
28
+ /** Hard round cap; the kernel coerces continue/sleep to stop at the cap. */
29
+ maxRounds?: number;
30
+ /** Sleep clamp bounds (ms), enforced in-kernel. */
31
+ minSleepMs?: number;
32
+ maxSleepMs?: number;
33
+ /** "stop" (goal loop, default) | "sleep" (cron loop) when a round never calls pace. */
34
+ defaultAction?: "stop" | "sleep";
35
+ /** Cross-round done-gate: judges a stop proposal; a failing verdict overrides
36
+ * stop→continue at most `maxVerdictOverrides` times, its feedback becoming the
37
+ * next round's steering note. The in-kernel O4 criteria gate is the per-round rung
38
+ * of the same ladder — this is the cross-round rung. */
39
+ verdictFn?: (ctx: {
40
+ loopId: string;
41
+ round: number;
42
+ reason: string;
43
+ }) => Promise<{
44
+ pass: boolean;
45
+ feedback?: string;
46
+ }> | {
47
+ pass: boolean;
48
+ feedback?: string;
49
+ };
50
+ maxVerdictOverrides?: number;
51
+ /** Sleep implementation (injectable for tests / stateless hosts). Default: setTimeout.
52
+ * Return `false` to hand the wake to an external scheduler and end `run()` dormant. */
53
+ sleeper?: (delayMs: number, wakeAtMs: number) => Promise<boolean>;
54
+ /** Per-round event tap (streaming passthrough). */
55
+ onEvent?: (round: number, event: StreamEvent) => void;
56
+ }
57
+ export interface LoopOutcome {
58
+ loopId: string;
59
+ roundsCompleted: number;
60
+ stopped: boolean;
61
+ /** "stopped" | "dormant" (sleeper handed off to an external scheduler) */
62
+ state: "stopped" | "dormant";
63
+ lastPace?: PaceDecision;
64
+ lastStatus?: string;
65
+ /** Absolute wake time when dormant. */
66
+ wakeAtMs?: number;
67
+ }
68
+ /** Fold the loop's session log into resumable pacing state — zero new storage. DW-5: the judge's
69
+ * override budget folds too, so a crash/restart can't grant the verdictFn fresh overrides. */
70
+ export declare function foldLoopState(events: Array<{
71
+ seq: number;
72
+ event: SessionEvent;
73
+ }>): {
74
+ roundsCompleted: number;
75
+ pendingWakeAtMs?: number;
76
+ lastPace?: {
77
+ action: string;
78
+ reason: string;
79
+ };
80
+ overridesUsed: number;
81
+ };
82
+ /**
83
+ * DW-6 completion→wake bridge, composed from two existing seams (zero new mechanism): a `sleeper`
84
+ * that races the timer against an L0 recipient-addressed signal on the shared gateway. Ingest a
85
+ * signal with `recipient: loopId` (a subagent/workflow completion, a webhook) and the sleeping loop
86
+ * wakes into its next round immediately — where the SAME queued signal then reaches the model
87
+ * through the kernel's normal signal path, so the wake reason is visible in-round.
88
+ */
89
+ export declare function signalAwareSleeper(gateway: {
90
+ onSignal(listener: (sig: {
91
+ recipient?: string;
92
+ }) => void): () => void;
93
+ }, loopId: string): NonNullable<LoopSpec["sleeper"]>;
94
+ export declare class LoopDriver {
95
+ private readonly runner;
96
+ private readonly spec;
97
+ private overridesUsed;
98
+ constructor(runner: RuntimeRunner, spec: LoopSpec);
99
+ /**
100
+ * Drive rounds until the loop stops or goes dormant. Resumable by construction:
101
+ * the round count and any pending wake are folded from the session log, so
102
+ * calling `run()` again after a crash / on a stateless host continues in place.
103
+ */
104
+ run(): Promise<LoopOutcome>;
105
+ private sleep;
106
+ }
107
+ /** Facade: run a self-pacing loop agent (joins runAgent/runFanout as an entry point). */
108
+ export declare function runLoop(runner: RuntimeRunner, spec: LoopSpec): Promise<LoopOutcome>;