@tangle-network/agent-runtime 0.48.0 → 0.49.0

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 (40) hide show
  1. package/README.md +79 -15
  2. package/dist/agent.js +1 -1
  3. package/dist/chunk-GHX7XOJ2.js +433 -0
  4. package/dist/chunk-GHX7XOJ2.js.map +1 -0
  5. package/dist/{chunk-TJS7S3HJ.js → chunk-IQS4HI3F.js} +14 -5
  6. package/dist/chunk-IQS4HI3F.js.map +1 -0
  7. package/dist/{chunk-IW2LMLK6.js → chunk-PXUTIMGJ.js} +767 -129
  8. package/dist/chunk-PXUTIMGJ.js.map +1 -0
  9. package/dist/{chunk-656G2XCL.js → chunk-U2VEWKKK.js} +3 -3
  10. package/dist/{chunk-JNPK46YH.js → chunk-VIEDXELL.js} +408 -6
  11. package/dist/chunk-VIEDXELL.js.map +1 -0
  12. package/dist/{chunk-VR4JIC5H.js → chunk-XTEZ3YJ4.js} +2 -2
  13. package/dist/index.d.ts +29 -4
  14. package/dist/index.js +109 -21
  15. package/dist/index.js.map +1 -1
  16. package/dist/kb-gate-CsXpNRk7.d.ts +1145 -0
  17. package/dist/{loop-runner-bin-DEm4roYF.d.ts → loop-runner-bin-Cgn0A-NW.d.ts} +1 -1
  18. package/dist/loop-runner-bin.d.ts +2 -2
  19. package/dist/loop-runner-bin.js +3 -3
  20. package/dist/loops.d.ts +2 -2
  21. package/dist/loops.js +11 -1
  22. package/dist/mcp/bin.js +187 -24
  23. package/dist/mcp/bin.js.map +1 -1
  24. package/dist/mcp/index.d.ts +27 -124
  25. package/dist/mcp/index.js +28 -6
  26. package/dist/mcp/index.js.map +1 -1
  27. package/dist/platform.js +2 -2
  28. package/dist/platform.js.map +1 -1
  29. package/dist/runtime.d.ts +285 -8
  30. package/dist/runtime.js +11 -1
  31. package/dist/workflow.js +1 -1
  32. package/package.json +6 -5
  33. package/dist/chunk-IW2LMLK6.js.map +0 -1
  34. package/dist/chunk-JNPK46YH.js.map +0 -1
  35. package/dist/chunk-LX66I3SC.js +0 -218
  36. package/dist/chunk-LX66I3SC.js.map +0 -1
  37. package/dist/chunk-TJS7S3HJ.js.map +0 -1
  38. package/dist/kb-gate-51BlLlVM.d.ts +0 -529
  39. /package/dist/{chunk-656G2XCL.js.map → chunk-U2VEWKKK.js.map} +0 -0
  40. /package/dist/{chunk-VR4JIC5H.js.map → chunk-XTEZ3YJ4.js.map} +0 -0
@@ -0,0 +1,1145 @@
1
+ import { C as CoderOutput, a as CoderTask } from './coder-CVZNGbyg.js';
2
+ import { S as SandboxClient, A as AgentRunSpec, e as LoopTraceEmitter } from './types-nBMuollC.js';
3
+ import { SandboxEvent, SandboxInstance } from '@tangle-network/sandbox';
4
+ import { AgentEvalError } from '@tangle-network/agent-eval';
5
+ import { a as UiLens, U as UiFinding } from './substrate-CUgk7F7s.js';
6
+
7
+ /**
8
+ * @experimental
9
+ *
10
+ * Persistence port for the MCP delegation queue.
11
+ *
12
+ * `DelegationTaskQueue` keeps its working set in memory (status/history
13
+ * reads stay synchronous) and journals every record mutation through a
14
+ * `DelegationStore`. `DelegationTaskQueue.restore({ store })` is the load
15
+ * path: it reads the full record set once at construction and rehydrates
16
+ * the queue from it. After that the store only sees writes.
17
+ *
18
+ * Records MUST be JSON-safe — `FileDelegationStore` round-trips them
19
+ * through `JSON.stringify`/`JSON.parse`, so a `Date`, `Map`, or function
20
+ * smuggled into `args`/`result` would corrupt the journal.
21
+ */
22
+
23
+ /** @experimental */
24
+ interface DelegationStore {
25
+ /**
26
+ * Read every persisted record. Called once, by
27
+ * `DelegationTaskQueue.restore`, before any write. A missing backing
28
+ * file is an empty store; an unparseable one throws
29
+ * `DelegationStateCorruptError`.
30
+ */
31
+ loadAll(): Promise<DelegationRecord[]>;
32
+ /** Insert or replace the record keyed by `record.taskId`. */
33
+ upsert(record: DelegationRecord): Promise<void>;
34
+ /**
35
+ * Resolve an idempotency key to the taskId that claimed it, if any.
36
+ * The queue serves submit-time dedupe from its rehydrated in-memory
37
+ * index; this read exists for consumers that share a store across
38
+ * processes without holding the full record set.
39
+ */
40
+ lookupIdempotencyKey(key: string): Promise<string | undefined>;
41
+ /** Delete the named records — the retention-cap eviction path. */
42
+ remove(taskIds: readonly string[]): Promise<void>;
43
+ }
44
+ /**
45
+ * The persisted delegation state exists but cannot be parsed into
46
+ * records. Fail loud: silently starting empty over a corrupt journal
47
+ * would erase delegation history and re-run idempotent work. Opt into
48
+ * recovery explicitly via `FileDelegationStoreOptions.recoverCorrupt`
49
+ * (the bin maps `AGENT_RUNTIME_DELEGATION_STATE_RECOVER=1` onto it),
50
+ * which archives the corrupt file and starts fresh.
51
+ *
52
+ * @experimental
53
+ */
54
+ declare class DelegationStateCorruptError extends AgentEvalError {
55
+ constructor(message: string, options?: {
56
+ cause?: unknown;
57
+ });
58
+ }
59
+ /**
60
+ * A delegation-store read or write failed (filesystem error, store
61
+ * called before `loadAll`, ...). Once the queue observes one, it stops
62
+ * accepting new submissions — accepting work it cannot journal would
63
+ * silently demote durable mode to in-memory mode.
64
+ *
65
+ * @experimental
66
+ */
67
+ declare class DelegationPersistenceError extends AgentEvalError {
68
+ constructor(message: string, options?: {
69
+ cause?: unknown;
70
+ });
71
+ }
72
+ /** @experimental */
73
+ declare class InMemoryDelegationStore implements DelegationStore {
74
+ private readonly records;
75
+ loadAll(): Promise<DelegationRecord[]>;
76
+ upsert(record: DelegationRecord): Promise<void>;
77
+ lookupIdempotencyKey(key: string): Promise<string | undefined>;
78
+ remove(taskIds: readonly string[]): Promise<void>;
79
+ }
80
+ /** @experimental */
81
+ interface FileDelegationStoreOptions {
82
+ /** Absolute path of the JSON state file. Parent directories are created on first write. */
83
+ filePath: string;
84
+ /**
85
+ * When the state file exists but cannot be parsed, archive it to
86
+ * `<filePath>.corrupt-<timestamp>` and start empty instead of
87
+ * throwing `DelegationStateCorruptError`. Default false.
88
+ */
89
+ recoverCorrupt?: boolean;
90
+ }
91
+ /**
92
+ * JSON-file persistence for the delegation queue. Each write serializes
93
+ * the full record set and lands it atomically (write to a sibling tmp
94
+ * file, then `rename`), so readers never observe a torn file — a crash
95
+ * mid-write leaves the previous snapshot intact. Writes are serialized
96
+ * internally; concurrent `upsert`/`remove` calls cannot interleave.
97
+ *
98
+ * Built for the MCP server's scale (one stdio process, hundreds of
99
+ * records): full-snapshot writes keep the format trivially inspectable
100
+ * and corruption-detectable without a database dependency.
101
+ *
102
+ * @experimental
103
+ */
104
+ declare class FileDelegationStore implements DelegationStore {
105
+ private readonly filePath;
106
+ private readonly recoverCorrupt;
107
+ private readonly records;
108
+ private loaded;
109
+ private writeTail;
110
+ private tmpSeq;
111
+ constructor(options: FileDelegationStoreOptions);
112
+ loadAll(): Promise<DelegationRecord[]>;
113
+ upsert(record: DelegationRecord): Promise<void>;
114
+ lookupIdempotencyKey(key: string): Promise<string | undefined>;
115
+ remove(taskIds: readonly string[]): Promise<void>;
116
+ private assertLoaded;
117
+ private enqueueWrite;
118
+ private writeSnapshot;
119
+ }
120
+
121
+ /**
122
+ * @experimental
123
+ *
124
+ * MCP delegation tool surface — the typed inputs/outputs the product agent
125
+ * sees over the wire. These types are the contract; the JSON schemas under
126
+ * `tools/*` mirror them for the MCP `tools/list` advertisement.
127
+ *
128
+ * Async semantics: `delegate_code` + `delegate_research` return a `taskId`
129
+ * immediately. The product agent polls `delegation_status` until the task
130
+ * transitions to `completed` | `failed` | `cancelled`. `delegate_feedback`
131
+ * + `delegation_history` are synchronous reads / writes against the local
132
+ * task queue + feedback store.
133
+ */
134
+
135
+ /** @experimental */
136
+ type DelegationProfile = 'coder' | 'researcher' | 'ui-auditor';
137
+ /** @experimental */
138
+ type DelegationStatus = 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
139
+ /**
140
+ * Minimal `CoderTask` overrides exposed over the MCP wire. The full
141
+ * `CoderTask` carries fields the kernel synthesizes from `goal` +
142
+ * `repoRoot` — the agent only edits the few that materially gate
143
+ * validator behavior.
144
+ *
145
+ * @experimental
146
+ */
147
+ interface DelegateCodeConfig {
148
+ testCmd?: string;
149
+ typecheckCmd?: string;
150
+ forbiddenPaths?: string[];
151
+ maxDiffLines?: number;
152
+ }
153
+ /** @experimental */
154
+ interface DelegateCodeArgs {
155
+ /** Natural-language description of what the coder must accomplish. */
156
+ goal: string;
157
+ /** Absolute path inside the sandbox where the repo lives. */
158
+ repoRoot: string;
159
+ /** Optional free-form context the agent surfaces in the prompt prelude. */
160
+ contextHint?: string;
161
+ /**
162
+ * When > 1, dispatches `multiHarnessCoderFanout` across N harnesses
163
+ * (claude-code, codex, opencode-glm) and picks the highest-scoring
164
+ * passing patch. Default 1.
165
+ */
166
+ variants?: number;
167
+ /** Validator + prompt overrides the agent knows for this repo. */
168
+ config?: DelegateCodeConfig;
169
+ /** Multi-tenant scope (customer-id, workspace-id). */
170
+ namespace?: string;
171
+ }
172
+ /** @experimental */
173
+ interface DelegateCodeResult {
174
+ taskId: string;
175
+ /** Best-effort hint — coder loops can take minutes-to-hours. */
176
+ estimatedDurationMs?: number;
177
+ }
178
+ /** @experimental */
179
+ type ResearchSource = 'web' | 'corpus' | 'twitter' | 'github' | 'docs';
180
+ /** @experimental */
181
+ interface DelegateResearchConfig {
182
+ recencyWindow?: {
183
+ since?: string;
184
+ until?: string;
185
+ };
186
+ maxItems?: number;
187
+ minConfidence?: number;
188
+ }
189
+ /** @experimental */
190
+ interface DelegateResearchArgs {
191
+ question: string;
192
+ namespace: string;
193
+ scope?: string;
194
+ sources?: ResearchSource[];
195
+ variants?: number;
196
+ config?: DelegateResearchConfig;
197
+ }
198
+ /** @experimental */
199
+ interface DelegateResearchResult {
200
+ taskId: string;
201
+ estimatedDurationMs?: number;
202
+ }
203
+ /** @experimental */
204
+ interface FeedbackRefersTo {
205
+ kind: 'delegation' | 'artifact' | 'outcome';
206
+ /** For `'delegation'`, this is the taskId. */
207
+ ref: string;
208
+ }
209
+ /** @experimental */
210
+ interface FeedbackRating {
211
+ /** [0, 1]. */
212
+ score: number;
213
+ label?: 'good' | 'bad' | 'neutral' | 'mixed';
214
+ notes: string;
215
+ }
216
+ /** @experimental */
217
+ interface DelegateFeedbackArgs {
218
+ refersTo: FeedbackRefersTo;
219
+ rating: FeedbackRating;
220
+ by: 'agent' | 'user' | 'downstream-judge';
221
+ /** ISO timestamp; defaults to server clock when omitted. */
222
+ capturedAt?: string;
223
+ namespace?: string;
224
+ }
225
+ /** @experimental */
226
+ interface DelegateFeedbackResult {
227
+ recorded: true;
228
+ id: string;
229
+ }
230
+ /** @experimental */
231
+ interface DelegationStatusArgs {
232
+ taskId: string;
233
+ }
234
+ /** @experimental */
235
+ interface DelegationProgress {
236
+ iteration: number;
237
+ phase: string;
238
+ }
239
+ /** @experimental */
240
+ interface DelegationError {
241
+ message: string;
242
+ kind: string;
243
+ }
244
+ /**
245
+ * Polymorphic `result` field: `CoderOutput` when the underlying profile
246
+ * is `'coder'`, a structurally-typed research output when `'researcher'`.
247
+ * The MCP wire carries it as JSON either way.
248
+ *
249
+ * @experimental
250
+ */
251
+ type DelegationResultPayload = {
252
+ profile: 'coder';
253
+ output: CoderOutput;
254
+ } | {
255
+ profile: 'researcher';
256
+ output: ResearchOutputShape;
257
+ } | {
258
+ profile: 'ui-auditor';
259
+ output: UiAuditorDelegationOutput;
260
+ };
261
+ /**
262
+ * Wire-shape of a completed UI-audit delegation. The `findings` array
263
+ * contains every finding persisted to the workspace during the run,
264
+ * already enriched with `id` and `createdAt` by the writer. `workspaceDir`
265
+ * is the absolute path to the workspace; `indexFile` is the workspace-
266
+ * relative path to the regenerated index.md.
267
+ *
268
+ * @experimental
269
+ */
270
+ interface UiAuditorDelegationOutput {
271
+ workspaceDir: string;
272
+ indexFile: string;
273
+ findings: UiFinding[];
274
+ /** Total iterations the loop ran for this delegation. */
275
+ iterations: number;
276
+ }
277
+ /** @experimental */
278
+ type UiAuditLensFilter = readonly UiLens[];
279
+ /** Optional per-route capture spec the agent surfaces over the wire. */
280
+ interface DelegateUiAuditRoute {
281
+ /** Stable route name (used in screenshot filenames + finding metadata). */
282
+ name: string;
283
+ /** Fully-qualified URL. */
284
+ url: string;
285
+ /** Viewports to capture at. Defaults to `[{ width: 1280, height: 800 }]`. */
286
+ viewports?: readonly {
287
+ width: number;
288
+ height: number;
289
+ }[];
290
+ /** Default false. Full-page captures for the broad lenses. */
291
+ fullPage?: boolean;
292
+ /** Selector to wait for before capture. */
293
+ waitFor?: string;
294
+ }
295
+ /** @experimental */
296
+ interface DelegateUiAuditConfig {
297
+ /**
298
+ * Lenses to iterate. Default: every lens except `'other'`. Order is
299
+ * preserved — the driver iterates lens-by-lens.
300
+ */
301
+ lenses?: UiAuditLensFilter;
302
+ /** Maximum total iterations across all (lens × route) pairs. Default 33 (11 lenses × 3 routes). */
303
+ maxIterations?: number;
304
+ /** Maximum concurrent iterations within a single plan() round. Default 2. */
305
+ maxConcurrency?: number;
306
+ /** Free-form product context surfaced to the judge. */
307
+ productContext?: string;
308
+ }
309
+ /** @experimental */
310
+ interface DelegateUiAuditArgs {
311
+ /** Workspace root for the audit (absolute path). */
312
+ workspaceDir: string;
313
+ /** Routes to audit. Must be non-empty. */
314
+ routes: readonly DelegateUiAuditRoute[];
315
+ /** Multi-tenant scope. */
316
+ namespace?: string;
317
+ config?: DelegateUiAuditConfig;
318
+ }
319
+ /** @experimental */
320
+ interface DelegateUiAuditResult {
321
+ taskId: string;
322
+ estimatedDurationMs?: number;
323
+ }
324
+ /**
325
+ * Loose shape of a research output over the wire — the substrate cannot
326
+ * import the `ResearchOutput` type from agent-knowledge without inducing
327
+ * a dependency cycle, so the MCP layer treats it structurally.
328
+ *
329
+ * @experimental
330
+ */
331
+ interface ResearchOutputShape {
332
+ items: unknown[];
333
+ citations: unknown[];
334
+ proposedWrites: unknown[];
335
+ gaps?: string[];
336
+ notes?: string;
337
+ [key: string]: unknown;
338
+ }
339
+ /** @experimental */
340
+ interface DelegationStatusResult {
341
+ taskId: string;
342
+ profile: DelegationProfile;
343
+ status: DelegationStatus;
344
+ progress?: DelegationProgress;
345
+ result?: DelegationResultPayload;
346
+ error?: DelegationError;
347
+ costUsd?: number;
348
+ startedAt: string;
349
+ completedAt?: string;
350
+ }
351
+ /** @experimental */
352
+ interface DelegationHistoryArgs {
353
+ namespace?: string;
354
+ profile?: DelegationProfile;
355
+ /** ISO date — only delegations started at-or-after `since` are returned. */
356
+ since?: string;
357
+ /** Default 50. Hard cap 500. */
358
+ limit?: number;
359
+ }
360
+ /** @experimental */
361
+ interface DelegationFeedbackSnapshot {
362
+ id: string;
363
+ score: number;
364
+ label?: FeedbackRating['label'];
365
+ by: DelegateFeedbackArgs['by'];
366
+ notes: string;
367
+ capturedAt: string;
368
+ }
369
+ /** @experimental */
370
+ interface DelegationHistoryEntry {
371
+ taskId: string;
372
+ profile: DelegationProfile;
373
+ namespace?: string;
374
+ args: DelegateCodeArgs | DelegateResearchArgs | DelegateUiAuditArgs;
375
+ status: DelegationStatus;
376
+ feedback?: DelegationFeedbackSnapshot[];
377
+ costUsd?: number;
378
+ startedAt: string;
379
+ completedAt?: string;
380
+ }
381
+ /** @experimental */
382
+ interface DelegationHistoryResult {
383
+ delegations: DelegationHistoryEntry[];
384
+ }
385
+
386
+ /**
387
+ * @experimental
388
+ *
389
+ * State machine for async MCP delegations:
390
+ *
391
+ * pending → running → completed | failed
392
+ * ↘ cancelled (from any non-terminal state via cancel())
393
+ *
394
+ * Each `submit` returns a `taskId` immediately and kicks the work off in the
395
+ * background. The work function receives an `AbortSignal` the queue fires
396
+ * when `cancel(taskId)` is called. The queue does NOT supervise runtime
397
+ * timeouts — the underlying `runLoop` driver / sandbox imposes those.
398
+ *
399
+ * Idempotency: callers may supply an `idempotencyKey` (hash of the input).
400
+ * A duplicate `submit` with a known key returns the existing task instead of
401
+ * starting a new one. Mutated input → different key → different task.
402
+ *
403
+ * Durability: the working set lives in memory (reads stay synchronous) and
404
+ * every record mutation is journaled through a `DelegationStore`. The default
405
+ * `InMemoryDelegationStore` keeps today's semantics — a process restart drops
406
+ * all state. Construct via `DelegationTaskQueue.restore({ store })` with a
407
+ * `FileDelegationStore` to reload prior records on startup: terminal records
408
+ * stay queryable, in-flight records either re-attach through the
409
+ * `resumeDelegate` seam (when they carry a `detachedSessionRef`) or fail
410
+ * loud with a driver-restart error so `delegation_status` tells the truth.
411
+ */
412
+
413
+ type AnyDelegateArgs = DelegateCodeArgs | DelegateResearchArgs | DelegateUiAuditArgs;
414
+ /**
415
+ * Must be JSON-safe end to end (`args`, `result`, `error`, `feedback`) —
416
+ * persistent stores round-trip records through `JSON.stringify`.
417
+ *
418
+ * @experimental
419
+ */
420
+ interface DelegationRecord {
421
+ taskId: string;
422
+ profile: DelegationProfile;
423
+ namespace?: string;
424
+ args: AnyDelegateArgs;
425
+ status: DelegationStatus;
426
+ progress?: DelegationProgress;
427
+ result?: DelegationResultPayload;
428
+ error?: DelegationError;
429
+ costUsd?: number;
430
+ startedAt: string;
431
+ completedAt?: string;
432
+ /** Sha-prefix hash of the canonical input — used for idempotency lookup. */
433
+ idempotencyKey?: string;
434
+ /**
435
+ * Caller-generated deterministic id of a detached run (e.g. the sandbox
436
+ * session id a single-tick driver resumes by). Presence is what makes a
437
+ * restored in-flight record resumable via `resumeDelegate`; without it a
438
+ * restart settles the record as failed.
439
+ */
440
+ detachedSessionRef?: string;
441
+ /** Feedback events keyed by this delegation's taskId. */
442
+ feedback: DelegationFeedbackSnapshot[];
443
+ }
444
+ /** @experimental */
445
+ interface SubmitInput<Args extends AnyDelegateArgs> {
446
+ profile: DelegationProfile;
447
+ args: Args;
448
+ namespace?: string;
449
+ idempotencyKey?: string;
450
+ /**
451
+ * Records the detached-run resume key on the new record. The submitted
452
+ * `run` function still executes in-process exactly as without it — the
453
+ * ref only matters after a restart, when `DelegationTaskQueue.restore`
454
+ * hands it to the `resumeDelegate` seam instead of failing the record.
455
+ */
456
+ detachedSessionRef?: string;
457
+ /**
458
+ * Runs the underlying delegation. The queue passes a fresh `AbortSignal`
459
+ * and a `report` channel for incremental progress updates. The function
460
+ * MUST resolve with the typed `DelegationResultPayload['output']`; the
461
+ * queue wraps it with the profile tag.
462
+ */
463
+ run: (ctx: DelegationRunContext) => Promise<DelegationResultPayload['output']>;
464
+ }
465
+ /** @experimental Context handed to a `SubmitInput.run` function. */
466
+ interface DelegationRunContext {
467
+ signal: AbortSignal;
468
+ report(progress: DelegationProgress): void;
469
+ /** The `detachedSessionRef` recorded at submit, when one was supplied. */
470
+ detachedSessionRef?: string;
471
+ /**
472
+ * Replace the record's detached-run resume key — the detached dispatch path
473
+ * calls this once the sandbox id is known so the persisted ref names a
474
+ * resolvable box. Ignored after the record settles (a cancel racing the
475
+ * rebind is legitimate; the ref no longer matters then). Throws on an empty
476
+ * ref — erasing the resume key would silently make the record unresumable.
477
+ */
478
+ updateDetachedSessionRef(ref: string): void;
479
+ }
480
+ /** @experimental */
481
+ interface SubmitOutput {
482
+ taskId: string;
483
+ /** True when a prior matching `idempotencyKey` returned an existing record. */
484
+ reused: boolean;
485
+ }
486
+ /**
487
+ * One observation of a detached run, mapped 1:1 from a single-tick driver
488
+ * (e.g. the sandbox SDK's `driveTurn`, which reports
489
+ * completed | running | failed per pass). `running` schedules another tick
490
+ * after `intervalMs`; `completed` / `failed` settle the record.
491
+ *
492
+ * @experimental
493
+ */
494
+ type DelegationResumeTick = {
495
+ state: 'running';
496
+ } | {
497
+ state: 'completed';
498
+ output: DelegationResultPayload['output'];
499
+ costUsd?: number;
500
+ } | {
501
+ state: 'failed';
502
+ error: DelegationError;
503
+ };
504
+ /** @experimental */
505
+ interface DelegationResumeContext {
506
+ /** Fired by `cancel(taskId)`; the driver should stop the remote run when it can. */
507
+ signal: AbortSignal;
508
+ report(progress: DelegationProgress): void;
509
+ }
510
+ /**
511
+ * Re-attaches restored in-flight records to their detached runs. The queue
512
+ * calls `tick` repeatedly — it never awaits a whole run — so the driver can
513
+ * be a thin wrapper over a one-pass primitive: resolve the run named by
514
+ * `detachedSessionRef`, advance/poll it once, report where it stands. A
515
+ * thrown error settles the record as failed; `failed` ticks are treated as
516
+ * terminal and are not retried.
517
+ *
518
+ * @experimental
519
+ */
520
+ interface DelegationResumeDriver {
521
+ tick(task: {
522
+ record: DelegationRecord;
523
+ detachedSessionRef: string;
524
+ }, ctx: DelegationResumeContext): Promise<DelegationResumeTick>;
525
+ /** Delay between `running` ticks, in milliseconds. Default 5000. */
526
+ intervalMs?: number;
527
+ }
528
+ /** @experimental */
529
+ interface DelegationTaskQueueOptions {
530
+ /** ID generator override; default `randomTaskId`. */
531
+ generateId?: () => string;
532
+ /** Clock override; default `() => new Date().toISOString()`. */
533
+ now?: () => string;
534
+ /**
535
+ * Journal for record mutations and the `restore()` load source. Default
536
+ * `InMemoryDelegationStore` — observably identical to an unjournaled
537
+ * queue. Pass a `FileDelegationStore` through
538
+ * `DelegationTaskQueue.restore` for state that survives a restart;
539
+ * constructing with `new` never loads prior state.
540
+ */
541
+ store?: DelegationStore;
542
+ /** Resume seam for restored in-flight records that carry a `detachedSessionRef`. */
543
+ resumeDelegate?: DelegationResumeDriver;
544
+ /**
545
+ * Maximum number of terminal (completed | failed | cancelled) records
546
+ * retained; the oldest (by `completedAt`) are evicted from memory and
547
+ * store once the cap is exceeded. Default unbounded.
548
+ */
549
+ maxTerminalRecords?: number;
550
+ /**
551
+ * Observes the first store failure. After it fires, the queue refuses
552
+ * new submissions and `flush()` rejects with the same error. Default:
553
+ * rethrow on a microtask — an unhandled crash — because silently
554
+ * degrading durable mode to memory-only would lie to the caller.
555
+ */
556
+ onPersistError?: (error: DelegationPersistenceError) => void;
557
+ }
558
+ /** @experimental */
559
+ declare class DelegationTaskQueue {
560
+ private readonly records;
561
+ private readonly controllers;
562
+ private readonly byIdempotencyKey;
563
+ private readonly generateId;
564
+ private readonly now;
565
+ private readonly store;
566
+ private readonly resumeDelegate?;
567
+ private readonly maxTerminalRecords;
568
+ private readonly onPersistError;
569
+ private persistTail;
570
+ private persistFailure;
571
+ constructor(options?: DelegationTaskQueueOptions);
572
+ /**
573
+ * Construct a queue from previously-persisted state. Loads every record
574
+ * from `options.store`, rebuilds the idempotency index (so a re-submitted
575
+ * identical task returns the prior taskId and its terminal state), then:
576
+ *
577
+ * - terminal records stay queryable via `status()` / `history()`
578
+ * - in-flight records with a `detachedSessionRef` re-attach through
579
+ * `options.resumeDelegate` and report `running`
580
+ * - other in-flight records settle as failed — their driver died with
581
+ * the previous process and the result is unrecoverable
582
+ *
583
+ * The retention cap applies to the loaded set as well.
584
+ */
585
+ static restore(options?: DelegationTaskQueueOptions): Promise<DelegationTaskQueue>;
586
+ /**
587
+ * Kick off a delegation in the background. Returns immediately. The
588
+ * `taskId` is queryable via `status` once this method returns. Throws
589
+ * the recorded `DelegationPersistenceError` once the store has failed —
590
+ * the queue does not accept work it cannot journal.
591
+ */
592
+ submit<Args extends AnyDelegateArgs>(input: SubmitInput<Args>): SubmitOutput;
593
+ /**
594
+ * Snapshot the current state of a delegation. Returns `undefined` for
595
+ * unknown ids so callers can distinguish missing from terminal.
596
+ */
597
+ status(taskId: string): DelegationStatusResult | undefined;
598
+ /**
599
+ * Abort an in-flight delegation. Returns `false` if the task is unknown
600
+ * or already terminal. The underlying `run` function MUST honor the
601
+ * abort signal for the cancel to take effect; the queue marks the
602
+ * record `cancelled` regardless so a misbehaving runner cannot pin the
603
+ * UI on `running` forever.
604
+ */
605
+ cancel(taskId: string): boolean;
606
+ /**
607
+ * Append a feedback event to the matching delegation. Returns `false`
608
+ * when `ref` does not name a known taskId — the caller should still
609
+ * record the feedback through a different surface (artifact/outcome
610
+ * kinds are not queue-bound).
611
+ */
612
+ attachFeedback(taskId: string, snapshot: DelegationFeedbackSnapshot): boolean;
613
+ /**
614
+ * Query the recorded delegations. Returns entries newest-first (by
615
+ * `startedAt`), truncated to `limit`.
616
+ */
617
+ history(args?: DelegationHistoryArgs): DelegationHistoryEntry[];
618
+ /**
619
+ * Await every journal write issued so far. Rejects with the recorded
620
+ * `DelegationPersistenceError` when any of them failed. Call before
621
+ * handing the store's backing file to another process.
622
+ */
623
+ flush(): Promise<void>;
624
+ /** Test-only — number of in-flight (non-terminal) records. */
625
+ inflightCount(): number;
626
+ private execute;
627
+ private rehydrate;
628
+ private startResume;
629
+ private driveResume;
630
+ private persist;
631
+ private persistRemoval;
632
+ private failPersistence;
633
+ private enforceRetention;
634
+ }
635
+ /**
636
+ * Best-effort stable hash for use as `idempotencyKey`. Not cryptographic;
637
+ * collisions only affect dedupe, never correctness.
638
+ *
639
+ * @experimental
640
+ */
641
+ declare function hashIdempotencyInput(value: unknown): string;
642
+
643
+ /**
644
+ * @experimental
645
+ *
646
+ * Detached delegation turns over the sandbox SDK's `driveTurn` primitive.
647
+ *
648
+ * Two halves of one story:
649
+ *
650
+ * - {@link runDetachedTurn} — the dispatch side. A single-session delegate
651
+ * (single-variant coder / researcher) acquires a box, binds the sandbox id
652
+ * into the record's `detachedSessionRef`, then advances the turn with
653
+ * repeated `driveTurn` ticks instead of holding a live SSE stream. The
654
+ * session id is deterministic and supplied at submit time, so a process
655
+ * crash between ticks loses nothing — the turn keeps running in the box.
656
+ *
657
+ * - {@link createDriveTurnResumeDriver} — the resume side. A
658
+ * `DelegationResumeDriver` that re-attaches restored in-flight records to
659
+ * their detached runs: parse the record's ref, resolve the box, advance the
660
+ * turn one `driveTurn` pass per `tick()`, and map the SDK's three states
661
+ * (`completed | running | failed`) onto `DelegationResumeTick`.
662
+ *
663
+ * Both sides type the box structurally ({@link DriveTurnCapableBox}) so tests
664
+ * inject fakes and the module never requires the sandbox SDK at runtime — the
665
+ * SDK stays an optional peer, exactly like the executors' `SandboxClient` seam.
666
+ *
667
+ * Tradeoffs of detached mode (why it is opt-in, not the default): a detached
668
+ * turn yields one terminal payload instead of a live event stream, so per-token
669
+ * loop trace events and kernel token/cost aggregation are not produced for that
670
+ * turn. Multi-variant fanout stays on the streaming `runLoop` path — N
671
+ * concurrent sessions cannot be expressed as one resume key, and winner
672
+ * selection needs every candidate.
673
+ */
674
+
675
+ /**
676
+ * Structural mirror of the sandbox SDK's `TurnDriveResult` (>= 0.6).
677
+ * Discriminated on `state`; `failed` is terminal and deterministic per the
678
+ * SDK contract — re-invoking with the same ids returns the same outcome.
679
+ *
680
+ * @experimental
681
+ */
682
+ type DriveTurnTick = {
683
+ state: 'completed';
684
+ text: string;
685
+ result: Record<string, unknown>;
686
+ } | {
687
+ state: 'running';
688
+ startedAt?: Date;
689
+ elapsedMs?: number;
690
+ } | {
691
+ state: 'failed';
692
+ error: string;
693
+ };
694
+ /**
695
+ * The box surface detached turns need. `SandboxInstance`
696
+ * (`@tangle-network/sandbox` >= 0.6) satisfies it structurally; tests pass
697
+ * in-memory fakes. `_sessionCancel` is the SDK's remote-cancellation surface —
698
+ * optional here because older SDKs / fakes may not expose it; when present it
699
+ * is invoked on abort so the remote run actually stops.
700
+ *
701
+ * @experimental
702
+ */
703
+ interface DriveTurnCapableBox {
704
+ driveTurn(message: string, opts: {
705
+ sessionId: string;
706
+ turnId?: string;
707
+ wallCapMs?: number;
708
+ }): Promise<DriveTurnTick>;
709
+ _sessionCancel?(id: string): Promise<void>;
710
+ }
711
+ /**
712
+ * Decoded `DelegationRecord.detachedSessionRef`. `sandboxId` is absent between
713
+ * submit and box acquisition — a record restored in that window is not
714
+ * resumable (there is no box to resume on) and the resume driver fails it
715
+ * loud rather than dispatching onto a guessed box.
716
+ *
717
+ * @experimental
718
+ */
719
+ interface DetachedSessionRefParts {
720
+ sessionId: string;
721
+ sandboxId?: string;
722
+ }
723
+ /**
724
+ * Encode ref parts into the JSON-safe string stored on the record:
725
+ * `session=<id>` before the box exists, `sandbox=<id>;session=<id>` once
726
+ * bound. Ids must not contain the `;`/`=` delimiters.
727
+ *
728
+ * @experimental
729
+ */
730
+ declare function formatDetachedSessionRef(parts: DetachedSessionRefParts): string;
731
+ /** @experimental Inverse of {@link formatDetachedSessionRef}; throws `ValidationError` on malformed input. */
732
+ declare function parseDetachedSessionRef(raw: string): DetachedSessionRefParts;
733
+ /** @experimental The terminal payload of a finished detached turn. */
734
+ interface DetachedTurn {
735
+ /** Final assistant text. */
736
+ text: string;
737
+ /** The SDK's cached AgentExecutionResult-shape record for the turn. */
738
+ result: Record<string, unknown>;
739
+ }
740
+ /**
741
+ * Synthesize the terminal event array a detached turn settles through. Shaped
742
+ * so the existing event-stream output adapters (coder, researcher) parse it:
743
+ * `data.result` for adapters that read a structured terminal record, `data.text`
744
+ * for adapters that scan assistant text for the fenced result block.
745
+ *
746
+ * @experimental
747
+ */
748
+ declare function detachedTurnEvents(sessionId: string, turn: DetachedTurn): SandboxEvent[];
749
+ /** @experimental */
750
+ interface RunDetachedTurnOptions {
751
+ /** Sandbox client used to acquire the box (the delegate's executor client). */
752
+ client: SandboxClient;
753
+ /** Profile + overrides for box acquisition — same spec the streaming path uses. */
754
+ spec: AgentRunSpec<unknown>;
755
+ /** The full turn prompt; consumed by `driveTurn`'s dispatch leg. */
756
+ prompt: string;
757
+ /** Deterministic resume key, minted at submit time (`parseDetachedSessionRef(ref).sessionId`). */
758
+ sessionId: string;
759
+ /**
760
+ * Called once the box exists, with its sandbox id. Callers persist
761
+ * `formatDetachedSessionRef({ sandboxId, sessionId })` onto the record here so
762
+ * a restart can resolve the box again.
763
+ */
764
+ bindSandbox(sandboxId: string): void;
765
+ signal: AbortSignal;
766
+ report(progress: DelegationProgress): void;
767
+ /** Delay between `running` ticks (ms). Default 5000. */
768
+ tickIntervalMs?: number;
769
+ /** Wall-clock cap forwarded to `driveTurn` — the SDK cancels and fails a session past it. */
770
+ wallCapMs?: number;
771
+ }
772
+ /**
773
+ * Dispatch one detached turn and advance it to a terminal state with
774
+ * `driveTurn` ticks. The first tick dispatches (idempotent on `sessionId`);
775
+ * subsequent ticks poll. On abort the remote session is cancelled via
776
+ * `_sessionCancel` when the box exposes it. The box is torn down on every
777
+ * in-process exit path (success, failure, abort) — only a process death skips
778
+ * teardown, which is exactly the case the resume driver re-attaches to.
779
+ *
780
+ * @experimental
781
+ */
782
+ declare function runDetachedTurn(options: RunDetachedTurnOptions): Promise<DetachedTurn>;
783
+ /** @experimental */
784
+ interface DriveTurnResumeDriverOptions {
785
+ /**
786
+ * Resolve the live box owning a detached session. The bin wires this to the
787
+ * sandbox client's `get(sandboxId)`; throw when the box no longer exists —
788
+ * a thrown tick settles the record as failed, which is the truth.
789
+ */
790
+ resolveSandbox(sandboxId: string): Promise<DriveTurnCapableBox>;
791
+ /**
792
+ * Rebuild the turn prompt from the persisted record. Only consumed by
793
+ * `driveTurn`'s dispatch leg — i.e. when the previous process died after
794
+ * binding the box but before the session was dispatched. Must reproduce the
795
+ * prompt the delegate would have sent.
796
+ */
797
+ buildMessage(record: DelegationRecord): string;
798
+ /**
799
+ * Map a completed turn onto the delegation's typed output payload (parse +
800
+ * validate per profile). Throw when the resumed result does not pass the
801
+ * profile's gate — the queue settles the record as failed with that error.
802
+ */
803
+ settleOutput(turn: DetachedTurn, record: DelegationRecord, ctx: {
804
+ signal: AbortSignal;
805
+ }): Promise<DelegationResultPayload['output']> | DelegationResultPayload['output'];
806
+ /** Delay between `running` ticks (ms). Default 5000. */
807
+ intervalMs?: number;
808
+ /** Wall-clock cap forwarded to `driveTurn` on every tick. */
809
+ wallCapMs?: number;
810
+ }
811
+ /**
812
+ * Build the `driveTurn`-backed {@link DelegationResumeDriver}. Each `tick()`
813
+ * is one settle/poll/dispatch pass:
814
+ *
815
+ * - ref without a sandbox binding → `failed` (`DetachedSessionUnboundError`):
816
+ * the previous process died before a box existed; there is nothing to resume.
817
+ * - `driveTurn` `completed` → `settleOutput` → `completed` tick.
818
+ * - `running` → progress via `ctx.report`, `running` tick (queue re-ticks
819
+ * after `intervalMs`).
820
+ * - `failed` → `failed` tick (`DetachedTurnFailedError`) — terminal per the
821
+ * SDK's deterministic-failure contract.
822
+ *
823
+ * Abort: the queue stops ticking once `cancel()` flips the record, so remote
824
+ * cancellation is hooked onto `ctx.signal` (once per task) and fires
825
+ * `_sessionCancel` when the SDK surface exposes it. The driver never deletes
826
+ * boxes — it cannot know whether `sandboxId` is a disposable sibling or a
827
+ * fleet machine, and destroying a fleet machine would be unrecoverable.
828
+ *
829
+ * @experimental
830
+ */
831
+ declare function createDriveTurnResumeDriver(options: DriveTurnResumeDriverOptions): DelegationResumeDriver;
832
+
833
+ /**
834
+ * @experimental
835
+ *
836
+ * Delegation executors — the layer between MCP delegates and the sandbox
837
+ * substrate. Each executor exposes a {@link SandboxClient} the kernel
838
+ * consumes plus a placement tag so the trace pipeline can correlate workers
839
+ * with their physical placement.
840
+ *
841
+ * Two implementations ship in-box:
842
+ *
843
+ * - {@link createSiblingSandboxExecutor} — every delegation spawns a fresh
844
+ * sandbox sibling to the caller. Default when the MCP server runs as a
845
+ * standalone CLI mounted outside a fleet.
846
+ *
847
+ * - {@link createFleetWorkspaceExecutor} — delegations dispatch onto machines
848
+ * in the caller's existing fleet so worker diffs land directly on the
849
+ * caller's filesystem (the fleet's shared workspace). Selected when the
850
+ * parent sandbox passes `TANGLE_FLEET_ID` into the MCP server's env.
851
+ */
852
+
853
+ /** @experimental */
854
+ interface DelegationExecutor {
855
+ /** Sandbox client the kernel calls. Returned with `describePlacement` set. */
856
+ readonly client: SandboxClient;
857
+ /** Best-effort one-liner used in stderr boot logs and diagnostics. */
858
+ describe(): string;
859
+ /**
860
+ * Where delegated work physically runs. `sibling` and `fleet` placements are
861
+ * session-backed (boxes expose `driveTurn`, so detached dispatch + resume
862
+ * apply); `in-process` spawns local harness CLIs with no sandbox session to
863
+ * detach. Optional so consumer-implemented executors stay source-compatible;
864
+ * absent means "unknown" and detached dispatch is not enabled for it.
865
+ */
866
+ readonly placement?: 'sibling' | 'fleet' | 'in-process';
867
+ }
868
+ /** @experimental */
869
+ interface SiblingSandboxExecutorOptions {
870
+ client: SandboxClient;
871
+ }
872
+ /**
873
+ * Wrap a raw sandbox SDK client so the kernel emits
874
+ * `loop.iteration.dispatch` events with `{ placement: 'sibling', sandboxId }`.
875
+ *
876
+ * The returned client `.create()` delegates to the underlying client; the
877
+ * only added behavior is a `describePlacement` tag the kernel reads.
878
+ *
879
+ * @experimental
880
+ */
881
+ declare function createSiblingSandboxExecutor(options: SiblingSandboxExecutorOptions): DelegationExecutor;
882
+ /**
883
+ * Minimal `SandboxFleet` surface the fleet executor calls. Declared
884
+ * structurally so tests can pass an in-memory stub without instantiating the
885
+ * sandbox SDK.
886
+ *
887
+ * @experimental
888
+ */
889
+ interface FleetHandle {
890
+ readonly fleetId: string;
891
+ /** Machine ids in dispatch-eligible order. The executor round-robins. */
892
+ readonly ids: ReadonlyArray<string>;
893
+ /** Resolve a machine id to its `SandboxInstance` — that machine is mounted
894
+ * on the fleet's shared workspace, so any diff the worker writes lands on
895
+ * every other fleet machine's filesystem too. */
896
+ sandbox(machineId: string): Promise<SandboxInstance>;
897
+ }
898
+ /** @experimental */
899
+ interface FleetWorkspaceExecutorOptions {
900
+ fleet: FleetHandle;
901
+ /**
902
+ * Override the machine-selection policy. Default = round-robin across
903
+ * `fleet.ids`, skipping the optional `excludeMachineIds` set (typically the
904
+ * coordinator machine the MCP server is running on).
905
+ */
906
+ selectMachine?: (call: {
907
+ callIndex: number;
908
+ ids: ReadonlyArray<string>;
909
+ }) => string;
910
+ /**
911
+ * Machine ids to skip during default round-robin. Set to the caller's own
912
+ * machineId so workers don't compete with the orchestrator on the same VM.
913
+ */
914
+ excludeMachineIds?: ReadonlyArray<string>;
915
+ }
916
+ /**
917
+ * Build an executor that resolves each delegated iteration to an existing
918
+ * machine in `fleet`. The fleet's shared-workspace policy means the worker
919
+ * machine sees the caller's filesystem — diffs land in-place with no
920
+ * cross-sandbox copy step.
921
+ *
922
+ * @experimental
923
+ */
924
+ declare function createFleetWorkspaceExecutor(options: FleetWorkspaceExecutorOptions): DelegationExecutor;
925
+
926
+ /** @experimental */
927
+ interface DelegateRunCtx {
928
+ signal: AbortSignal;
929
+ report(progress: DelegationProgress): void;
930
+ /**
931
+ * Detached-run resume key recorded on the queue record at submit time
932
+ * (`formatDetachedSessionRef`). Present only when the submit path requested
933
+ * detached dispatch — its presence is what routes a session-backed delegate
934
+ * onto the `driveTurn` tick path instead of holding a stream.
935
+ */
936
+ detachedSessionRef?: string;
937
+ /** Rebind the record's resume key (e.g. once the sandbox id is known). */
938
+ updateDetachedSessionRef?(ref: string): void;
939
+ }
940
+ /** @experimental */
941
+ type CoderDelegate = (args: DelegateCodeArgs, ctx: DelegateRunCtx) => Promise<CoderOutput>;
942
+ /** @experimental */
943
+ type ResearcherDelegate = (args: DelegateResearchArgs, ctx: DelegateRunCtx) => Promise<ResearchOutputShape>;
944
+ /**
945
+ * UI-auditor delegate — fully consumer-injected. agent-runtime ships no
946
+ * default factory because the inputs are workspace path + judge function
947
+ * + (optionally) a `SandboxClient`, and the judge is the consumer's
948
+ * model seam. See `createInProcessUiAuditClient` + `uiAuditorProfile` in
949
+ * `@tangle-network/agent-runtime/profiles` for the canonical wiring.
950
+ *
951
+ * @experimental
952
+ */
953
+ type UiAuditorDelegate = (args: DelegateUiAuditArgs, ctx: DelegateRunCtx) => Promise<UiAuditorDelegationOutput>;
954
+ /** @experimental Structured review verdict over a coder candidate. */
955
+ interface CoderReview {
956
+ /** Gate: only approved candidates are eligible to win. */
957
+ approved: boolean;
958
+ /** Reviewer's recommendation — surfaced in traces. */
959
+ recommendation: 'ship' | 'approve-with-nits' | 'changes-requested' | 'reject';
960
+ /** Readiness 0..1, used by the `highest-readiness` winner-selection strategy. */
961
+ readiness: number;
962
+ notes?: string;
963
+ }
964
+ /**
965
+ * @experimental
966
+ *
967
+ * Optional adversarial reviewer over a coder candidate that already passed
968
+ * mechanical validation (tests/typecheck/forbidden/diff/no-op/secrets). Folded
969
+ * from the ai-trading-blueprint delegation MCP: a candidate is only eligible to
970
+ * win if the reviewer approves it. The reviewer is the consumer's seam — an LLM
971
+ * judge, a `pnpm review` command, anything returning a `CoderReview`.
972
+ */
973
+ type CoderReviewer = (output: CoderOutput, task: CoderTask, ctx: {
974
+ signal: AbortSignal;
975
+ }) => Promise<CoderReview> | CoderReview;
976
+ /**
977
+ * @experimental Winner-selection strategy among validated (+ reviewed)
978
+ * candidates. `highest-readiness` requires a `reviewer`. Default `highest-score`
979
+ * (the kernel's behavior — preserves backward compatibility).
980
+ */
981
+ type CoderWinnerSelection = 'highest-score' | 'smallest-diff' | 'highest-readiness' | 'first-approved';
982
+ /** @experimental */
983
+ interface CreateDefaultCoderDelegateOptions {
984
+ /**
985
+ * Execution placement. Pass a {@link DelegationExecutor} (sibling or fleet)
986
+ * to control where worker iterations land. `sandboxClient` is a
987
+ * convenience shorthand that wraps the client in a sibling executor — pass
988
+ * one or the other, not both.
989
+ */
990
+ executor?: DelegationExecutor;
991
+ /**
992
+ * Convenience shorthand for sibling placement. Equivalent to
993
+ * `executor: createSiblingSandboxExecutor({ client: sandboxClient })`.
994
+ */
995
+ sandboxClient?: SandboxClient;
996
+ /** Backend harness for the single-coder path. Default comes from `coderProfile`. */
997
+ harness?: string;
998
+ /** Model override for the single-coder path. */
999
+ model?: string;
1000
+ /** Default `['claude-code', 'codex', 'opencode/zai-coding-plan/glm-5.1']` when variants > 1. */
1001
+ fanoutHarnesses?: string[];
1002
+ /** Optional per-harness model override for `variants > 1`. */
1003
+ fanoutModels?: (string | undefined)[];
1004
+ /** Hard cap on the kernel's per-batch concurrency. Default 4. */
1005
+ maxConcurrency?: number;
1006
+ /**
1007
+ * Optional adversarial reviewer. When set, a candidate must pass mechanical
1008
+ * validation AND `reviewer.approved` to be eligible to win — empty/secret/
1009
+ * test-failing patches are already gone; this catches the "compiles + passes
1010
+ * but wrong/unsafe" class the deterministic validator can't see.
1011
+ */
1012
+ reviewer?: CoderReviewer;
1013
+ /** Winner-selection strategy among eligible candidates. Default `highest-score`. */
1014
+ winnerSelection?: CoderWinnerSelection;
1015
+ /**
1016
+ * Loop trace emitter forwarded into every delegated `runLoop`. Wire
1017
+ * `createPropagatingTraceEmitter(readTraceContextFromEnv())` here (the bin
1018
+ * does) so delegated build-loops export their topology spans to the OTLP /
1019
+ * Tangle Intelligence sink when `OTEL_EXPORTER_OTLP_ENDPOINT` is set — and
1020
+ * are a cheap no-op when it isn't. Configurable by construction.
1021
+ *
1022
+ * Detached single-variant turns (taken when `ctx.detachedSessionRef` is set)
1023
+ * bypass `runLoop` and therefore emit no loop trace events for that turn.
1024
+ */
1025
+ traceEmitter?: LoopTraceEmitter;
1026
+ /** Tick cadence (ms) for the detached single-variant path. Default 5000. */
1027
+ detachedTickIntervalMs?: number;
1028
+ /** Wall-clock cap (ms) forwarded to `driveTurn` for detached turns. */
1029
+ detachedWallCapMs?: number;
1030
+ }
1031
+ /**
1032
+ * Build a coder delegate that drives `runLoop` against the project's
1033
+ * sandbox client + coder profile. When `args.variants > 1` it switches
1034
+ * to the multi-harness fanout topology.
1035
+ *
1036
+ * @experimental
1037
+ */
1038
+ declare function createDefaultCoderDelegate(options: CreateDefaultCoderDelegateOptions): CoderDelegate;
1039
+ /**
1040
+ * Canonical `DelegateCodeArgs` → `CoderTask` mapping — the single source for
1041
+ * the delegate's live dispatch AND the resume driver's settle/message
1042
+ * rebuilding, so a resumed record reproduces exactly the task the original
1043
+ * process dispatched.
1044
+ *
1045
+ * @experimental
1046
+ */
1047
+ declare function coderTaskFromArgs(args: DelegateCodeArgs): CoderTask;
1048
+ /** @experimental */
1049
+ interface SettleDetachedCoderTurnOptions {
1050
+ task: CoderTask;
1051
+ /** Session id of the detached turn — used as the synthesized event id. */
1052
+ sessionId: string;
1053
+ signal: AbortSignal;
1054
+ harness?: string;
1055
+ model?: string;
1056
+ /** Same gate as the streaming path: an unapproved candidate cannot win. */
1057
+ reviewer?: CoderReviewer;
1058
+ }
1059
+ /**
1060
+ * Settle a completed detached coder turn through the same gate the streaming
1061
+ * path applies: parse the terminal payload with the coder output adapter,
1062
+ * run the mechanical validator (tests/typecheck/forbidden/diff/no-op/secrets),
1063
+ * then the optional reviewer. Throws when nothing survives — a resumed or
1064
+ * detached run must not return an unvalidated patch.
1065
+ *
1066
+ * @experimental
1067
+ */
1068
+ declare function settleDetachedCoderTurn(turn: DetachedTurn, options: SettleDetachedCoderTurnOptions): Promise<CoderOutput>;
1069
+
1070
+ /**
1071
+ * @experimental
1072
+ *
1073
+ * `createKbGate` — the valid-only knowledge-base growth gate, distilled from
1074
+ * physim's KB-research subsystem. A research-in-a-loop delegate (or any KB
1075
+ * writer) runs candidate facts through this before persisting, so the KB grows
1076
+ * with ONLY grounded facts — hallucinated, unsourced, or laundered claims are
1077
+ * vetoed at the gate.
1078
+ *
1079
+ * Fail-closed by construction: every judge must `accept`; the FIRST veto wins
1080
+ * and the fact is rejected. The non-negotiable floor (always on, can't be
1081
+ * disabled) is the **passage-present guard** — a fact's `verbatimPassage` MUST
1082
+ * literally appear in its `sourceText`. That single check kills the dominant
1083
+ * failure mode (a confident claim decoupled from any real source).
1084
+ *
1085
+ * Pure + dependency-free: it operates on fact candidates, not on a store, so it
1086
+ * composes with `@tangle-network/agent-knowledge` or any persistence layer
1087
+ * without importing it. The remediation policy (correct-on-veto vs
1088
+ * escalate-as-unverified) is the caller's — this returns the verdict; it never
1089
+ * drops a fact silently.
1090
+ */
1091
+ /** @experimental A fact proposed for the KB, with its grounding. */
1092
+ interface FactCandidate {
1093
+ /** The atomic claim text. */
1094
+ claim: string;
1095
+ /** Optional extracted value (number or string) the claim asserts. */
1096
+ value?: string | number;
1097
+ /** Verbatim span lifted from the source that backs the claim. */
1098
+ verbatimPassage: string;
1099
+ /** The raw source text the passage must be grounded in. */
1100
+ sourceText: string;
1101
+ /** Where the fact claims to come from — checked for circular/self citations. */
1102
+ citation?: string;
1103
+ }
1104
+ /** @experimental */
1105
+ interface FactJudgeVerdict {
1106
+ accept: boolean;
1107
+ reason?: string;
1108
+ }
1109
+ /** @experimental A pluggable fact validator. Throw is NOT allowed — return a
1110
+ * verdict; a thrown judge is a programmer error, not a veto. */
1111
+ interface FactJudge {
1112
+ name: string;
1113
+ judge(candidate: FactCandidate): FactJudgeVerdict | Promise<FactJudgeVerdict>;
1114
+ }
1115
+ /** @experimental */
1116
+ interface KbGateResult {
1117
+ accepted: boolean;
1118
+ /** Name of the judge that vetoed; undefined when accepted. */
1119
+ vetoedBy?: string;
1120
+ reason?: string;
1121
+ }
1122
+ /** @experimental */
1123
+ interface CreateKbGateOptions {
1124
+ /** Extra judges appended after the built-in floor (e.g. an LLM judge). */
1125
+ judges?: FactJudge[];
1126
+ /** Minimum verbatim-passage length. Default 12 — kills empty/stub passages. */
1127
+ minPassageChars?: number;
1128
+ /**
1129
+ * Citation tokens that denote a SELF-generated artifact (e.g. `'spec'`,
1130
+ * `'cad_params'`, `'requirements'`). A citation naming one is circular
1131
+ * (laundering) — the fact cites a derived artifact, not a real source.
1132
+ * Default `[]` (no circular check unless the consumer declares its kinds).
1133
+ */
1134
+ selfArtifactKinds?: string[];
1135
+ }
1136
+ /**
1137
+ * @experimental
1138
+ *
1139
+ * Build a fail-closed KB gate. The returned function runs the built-in floor
1140
+ * (passage-non-empty → passage-present → value-in-passage → no-circular-citation)
1141
+ * then any consumer judges, returning on the first veto.
1142
+ */
1143
+ declare function createKbGate(options?: CreateKbGateOptions): (candidate: FactCandidate) => Promise<KbGateResult>;
1144
+
1145
+ export { type FeedbackRating as $, type DelegationError as A, type DelegationHistoryEntry as B, type CoderReviewer as C, type DelegateCodeArgs as D, DelegationPersistenceError as E, type FactCandidate as F, type DelegationProfile as G, type DelegationProgress as H, type DelegationRecord as I, type DelegationResultPayload as J, type DelegationResumeContext as K, type DelegationResumeDriver as L, type DelegationResumeTick as M, type DelegationRunContext as N, DelegationStateCorruptError as O, type DelegationStatus as P, type DelegationStore as Q, type ResearcherDelegate as R, type DelegationTaskQueueOptions as S, type DetachedSessionRefParts as T, type UiAuditorDelegate as U, type DetachedTurn as V, type DriveTurnCapableBox as W, type DriveTurnResumeDriverOptions as X, type DriveTurnTick as Y, type FactJudge as Z, type FactJudgeVerdict as _, type CoderWinnerSelection as a, type FeedbackRefersTo as a0, FileDelegationStore as a1, type FileDelegationStoreOptions as a2, type FleetWorkspaceExecutorOptions as a3, InMemoryDelegationStore as a4, type KbGateResult as a5, type ResearchOutputShape as a6, type RunDetachedTurnOptions as a7, type SettleDetachedCoderTurnOptions as a8, type SiblingSandboxExecutorOptions as a9, type SubmitInput as aa, type SubmitOutput as ab, type UiAuditorDelegationOutput as ac, coderTaskFromArgs as ad, createDefaultCoderDelegate as ae, createDriveTurnResumeDriver as af, createFleetWorkspaceExecutor as ag, createKbGate as ah, createSiblingSandboxExecutor as ai, detachedTurnEvents as aj, formatDetachedSessionRef as ak, hashIdempotencyInput as al, parseDetachedSessionRef as am, runDetachedTurn as an, settleDetachedCoderTurn as ao, type CreateKbGateOptions as b, type FleetHandle as c, type DelegationExecutor as d, type DelegateFeedbackArgs as e, type DelegationFeedbackSnapshot as f, DelegationTaskQueue as g, type CoderDelegate as h, type DelegateCodeResult as i, type DelegateFeedbackResult as j, type ResearchSource as k, type DelegateResearchArgs as l, type DelegateResearchResult as m, type DelegateUiAuditArgs as n, type DelegateUiAuditResult as o, type DelegationHistoryResult as p, type DelegationHistoryArgs as q, type DelegationStatusResult as r, type DelegationStatusArgs as s, type CoderReview as t, type CreateDefaultCoderDelegateOptions as u, type DelegateCodeConfig as v, type DelegateResearchConfig as w, type DelegateRunCtx as x, type DelegateUiAuditConfig as y, type DelegateUiAuditRoute as z };