@tangle-network/agent-runtime 0.105.0 → 0.106.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.
- package/README.md +2 -2
- package/dist/activation-CRjVfOiy.js +203 -0
- package/dist/activation-CRjVfOiy.js.map +1 -0
- package/dist/activation-DyWB0K6E.d.ts +98 -0
- package/dist/agent.d.ts +216 -234
- package/dist/agent.js +615 -547
- package/dist/agent.js.map +1 -1
- package/dist/analyst-loop-DGdtVENg.js +517 -0
- package/dist/analyst-loop-DGdtVENg.js.map +1 -0
- package/dist/analyst-loop.d.ts +10 -43
- package/dist/analyst-loop.js +2 -11
- package/dist/backends-Dy8NujB4.js +713 -0
- package/dist/backends-Dy8NujB4.js.map +1 -0
- package/dist/candidate-execution/index.d.ts +4 -192
- package/dist/candidate-execution/index.js +5 -66
- package/dist/candidate-execution-DqMbv1rU.js +488 -0
- package/dist/candidate-execution-DqMbv1rU.js.map +1 -0
- package/dist/coder-yhVWbdWc.js +40 -0
- package/dist/coder-yhVWbdWc.js.map +1 -0
- package/dist/conversation-XucG_K3l.js +1265 -0
- package/dist/conversation-XucG_K3l.js.map +1 -0
- package/dist/conversation.d.ts +2 -804
- package/dist/conversation.js +2 -55
- package/dist/environment-provider-CfLUmKmf.js +1168 -0
- package/dist/environment-provider-CfLUmKmf.js.map +1 -0
- package/dist/environment-provider-IUGU3epE.d.ts +964 -0
- package/dist/environment-provider.d.ts +2 -78
- package/dist/environment-provider.js +2 -18
- package/dist/errors-DEAvWQPy.js +111 -0
- package/dist/errors-DEAvWQPy.js.map +1 -0
- package/dist/improvement-adapter-Cr5TBSji.d.ts +166 -0
- package/dist/improvement-cycle-C095kS_g.js +2624 -0
- package/dist/improvement-cycle-C095kS_g.js.map +1 -0
- package/dist/index-BGdxAwfO.d.ts +838 -0
- package/dist/index-C-FYUuFG.d.ts +193 -0
- package/dist/index-CbNM40xG.d.ts +140 -0
- package/dist/index-DruSKs3Z.d.ts +7224 -0
- package/dist/index-Efjb3nrQ.d.ts +671 -0
- package/dist/index.d.ts +14 -1058
- package/dist/index.js +2106 -2082
- package/dist/index.js.map +1 -1
- package/dist/intelligence.d.ts +768 -889
- package/dist/intelligence.js +1466 -1367
- package/dist/intelligence.js.map +1 -1
- package/dist/{kb-gate-CwHO0vz6.d.ts → kb-gate-C8z2juK8.d.ts} +33 -31
- package/dist/kb-gate-DpaSwXVx.js +80 -0
- package/dist/kb-gate-DpaSwXVx.js.map +1 -0
- package/dist/knowledge-Ku-mPP4f.js +433 -0
- package/dist/knowledge-Ku-mPP4f.js.map +1 -0
- package/dist/knowledge.d.ts +2 -150
- package/dist/knowledge.js +2 -40
- package/dist/local-harness-Dh8PJ0ot.d.ts +455 -0
- package/dist/loop-runner-bin-BXsFEUcf.d.ts +162 -0
- package/dist/loop-runner-bin-BwjQ1NfH.js +268 -0
- package/dist/loop-runner-bin-BwjQ1NfH.js.map +1 -0
- package/dist/loop-runner-bin.d.ts +2 -15
- package/dist/loop-runner-bin.js +2 -24
- package/dist/loops.d.ts +5 -4164
- package/dist/loops.js +8 -419
- package/dist/mcp/bin.d.ts +1 -1
- package/dist/mcp/bin.js +116 -146
- package/dist/mcp/bin.js.map +1 -1
- package/dist/mcp/index.d.ts +295 -436
- package/dist/mcp/index.js +1242 -1177
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/memory-bin.d.ts +1 -1
- package/dist/mcp/memory-bin.js +37 -16
- package/dist/mcp/memory-bin.js.map +1 -1
- package/dist/memory-server-DL6cE2Ag.js +234 -0
- package/dist/memory-server-DL6cE2Ag.js.map +1 -0
- package/dist/model-resolution-Btd9iIKV.js +98 -0
- package/dist/model-resolution-Btd9iIKV.js.map +1 -0
- package/dist/openai-tools-B68JaOCx.d.ts +24 -0
- package/dist/openai-tools-CKLy1C7M.js +44 -0
- package/dist/openai-tools-CKLy1C7M.js.map +1 -0
- package/dist/otel-export-D34IQW1C.js +843 -0
- package/dist/otel-export-D34IQW1C.js.map +1 -0
- package/dist/platform.d.ts +188 -185
- package/dist/platform.js +185 -218
- package/dist/platform.js.map +1 -1
- package/dist/prepare-B53sPV7M.js +4292 -0
- package/dist/prepare-B53sPV7M.js.map +1 -0
- package/dist/primeintellect/index.d.ts +149 -145
- package/dist/primeintellect/index.js +626 -1003
- package/dist/primeintellect/index.js.map +1 -1
- package/dist/profiles.d.ts +296 -490
- package/dist/profiles.js +722 -749
- package/dist/profiles.js.map +1 -1
- package/dist/protected-model-port-BLPH5VVv.js +722 -0
- package/dist/protected-model-port-BLPH5VVv.js.map +1 -0
- package/dist/protected-model-port-BP6Z4eau.d.ts +283 -0
- package/dist/protected-redaction--F3v1oo8.js +200 -0
- package/dist/protected-redaction--F3v1oo8.js.map +1 -0
- package/dist/redact-D-u-rrcn.js +82 -0
- package/dist/redact-D-u-rrcn.js.map +1 -0
- package/dist/redact-kbIOjfjM.d.ts +452 -0
- package/dist/researcher-CoVqNhfI.js +433 -0
- package/dist/researcher-CoVqNhfI.js.map +1 -0
- package/dist/runtime-BG0BziaP.js +5408 -0
- package/dist/runtime-BG0BziaP.js.map +1 -0
- package/dist/sandbox-events-DeI5xX8P.js +316 -0
- package/dist/sandbox-events-DeI5xX8P.js.map +1 -0
- package/dist/spawn-journal-CwPvKUTa.js +456 -0
- package/dist/spawn-journal-CwPvKUTa.js.map +1 -0
- package/dist/structural-rollout-IXUEplky.js +1445 -0
- package/dist/structural-rollout-IXUEplky.js.map +1 -0
- package/dist/substrate-B0TYNrXn.js +27 -0
- package/dist/substrate-B0TYNrXn.js.map +1 -0
- package/dist/{substrate-Dyiqmd3o.d.ts → substrate-BcnuSHXm.d.ts} +57 -54
- package/dist/supervise-BiRutHS9.js +7321 -0
- package/dist/supervise-BiRutHS9.js.map +1 -0
- package/dist/supervisor-DTKhF-RV.js +3826 -0
- package/dist/supervisor-DTKhF-RV.js.map +1 -0
- package/dist/testing.d.ts +4 -3
- package/dist/testing.js +2478 -2637
- package/dist/testing.js.map +1 -1
- package/dist/tool-server-RcWgLIsL.js +141 -0
- package/dist/tool-server-RcWgLIsL.js.map +1 -0
- package/dist/trace-propagation-CJJC7SVB.js +57 -0
- package/dist/trace-propagation-CJJC7SVB.js.map +1 -0
- package/dist/types-BaIpb0XT.d.ts +492 -0
- package/dist/types-BevOjfTY.d.ts +765 -0
- package/dist/types-C9j4qg6l.d.ts +500 -0
- package/dist/types-Cja5OTGh.d.ts +190 -0
- package/dist/util-MVgdwuIS.js +145 -0
- package/dist/util-MVgdwuIS.js.map +1 -0
- package/dist/workspace-archive-BosQiotA.js +575 -0
- package/dist/workspace-archive-BosQiotA.js.map +1 -0
- package/package.json +43 -56
- package/skills/build-with-agent-runtime/SKILL.md +1 -0
- package/dist/activation-Cm-9MMX1.d.ts +0 -69
- package/dist/analyst-loop.js.map +0 -1
- package/dist/backends-BO4Jqoe2.d.ts +0 -134
- package/dist/candidate-execution/index.js.map +0 -1
- package/dist/chunk-2KGAN2HM.js +0 -1256
- package/dist/chunk-2KGAN2HM.js.map +0 -1
- package/dist/chunk-2VZYRB55.js +0 -834
- package/dist/chunk-2VZYRB55.js.map +0 -1
- package/dist/chunk-3MDZX7YU.js +0 -125
- package/dist/chunk-3MDZX7YU.js.map +0 -1
- package/dist/chunk-5IBQAPVB.js +0 -29
- package/dist/chunk-5IBQAPVB.js.map +0 -1
- package/dist/chunk-5QOB7H74.js +0 -387
- package/dist/chunk-5QOB7H74.js.map +0 -1
- package/dist/chunk-C5RTIBNZ.js +0 -48
- package/dist/chunk-C5RTIBNZ.js.map +0 -1
- package/dist/chunk-CTRA64LY.js +0 -680
- package/dist/chunk-CTRA64LY.js.map +0 -1
- package/dist/chunk-DPEUKJRO.js +0 -27
- package/dist/chunk-DPEUKJRO.js.map +0 -1
- package/dist/chunk-FD2MBMOH.js +0 -494
- package/dist/chunk-FD2MBMOH.js.map +0 -1
- package/dist/chunk-H5QPIZNX.js +0 -907
- package/dist/chunk-H5QPIZNX.js.map +0 -1
- package/dist/chunk-HLKC4UYB.js +0 -3514
- package/dist/chunk-HLKC4UYB.js.map +0 -1
- package/dist/chunk-HNP72PNU.js +0 -192
- package/dist/chunk-HNP72PNU.js.map +0 -1
- package/dist/chunk-ISPWRSEC.js +0 -408
- package/dist/chunk-ISPWRSEC.js.map +0 -1
- package/dist/chunk-KRBFHMV6.js +0 -1228
- package/dist/chunk-KRBFHMV6.js.map +0 -1
- package/dist/chunk-M22PL4GH.js +0 -171
- package/dist/chunk-M22PL4GH.js.map +0 -1
- package/dist/chunk-NURIQBJQ.js +0 -72
- package/dist/chunk-NURIQBJQ.js.map +0 -1
- package/dist/chunk-OPVWXJ2H.js +0 -75
- package/dist/chunk-OPVWXJ2H.js.map +0 -1
- package/dist/chunk-PZZKQVQV.js +0 -52
- package/dist/chunk-PZZKQVQV.js.map +0 -1
- package/dist/chunk-Q2JSAVQ3.js +0 -671
- package/dist/chunk-Q2JSAVQ3.js.map +0 -1
- package/dist/chunk-RDOAVVHY.js +0 -699
- package/dist/chunk-RDOAVVHY.js.map +0 -1
- package/dist/chunk-SGKPNBXE.js +0 -60
- package/dist/chunk-SGKPNBXE.js.map +0 -1
- package/dist/chunk-SMQXZGLZ.js +0 -351
- package/dist/chunk-SMQXZGLZ.js.map +0 -1
- package/dist/chunk-UHEZW5BU.js +0 -2918
- package/dist/chunk-UHEZW5BU.js.map +0 -1
- package/dist/chunk-UPWGXKXB.js +0 -254
- package/dist/chunk-UPWGXKXB.js.map +0 -1
- package/dist/chunk-VISA6CI3.js +0 -200
- package/dist/chunk-VISA6CI3.js.map +0 -1
- package/dist/chunk-WMTCUOQL.js +0 -474
- package/dist/chunk-WMTCUOQL.js.map +0 -1
- package/dist/chunk-WRTOVNN4.js +0 -4728
- package/dist/chunk-WRTOVNN4.js.map +0 -1
- package/dist/chunk-WSTRQZYQ.js +0 -5576
- package/dist/chunk-WSTRQZYQ.js.map +0 -1
- package/dist/chunk-XBG2W2VW.js +0 -6434
- package/dist/chunk-XBG2W2VW.js.map +0 -1
- package/dist/chunk-YEJR7IXO.js +0 -68
- package/dist/chunk-YEJR7IXO.js.map +0 -1
- package/dist/chunk-YJZA2BIK.js +0 -1338
- package/dist/chunk-YJZA2BIK.js.map +0 -1
- package/dist/completion-gate-BMy5LGoP.d.ts +0 -256
- package/dist/conversation.js.map +0 -1
- package/dist/coordination-BZZSVYpZ.d.ts +0 -2570
- package/dist/environment-provider.js.map +0 -1
- package/dist/improvement-adapter-HAZz-7vK.d.ts +0 -203
- package/dist/knowledge.js.map +0 -1
- package/dist/local-harness-BDNcl6jI.d.ts +0 -175
- package/dist/loop-runner-bin-68aoM9-7.d.ts +0 -199
- package/dist/loop-runner-bin.js.map +0 -1
- package/dist/loops.js.map +0 -1
- package/dist/openai-tools-fnj6SRVg.d.ts +0 -46
- package/dist/protected-model-port-B4t-OQjL.d.ts +0 -271
- package/dist/redact-BuFjxBUJ.d.ts +0 -481
- package/dist/sanitize-DEbPNtyI.d.ts +0 -328
- package/dist/structural-rollout-DEh6CEsa.d.ts +0 -446
- package/dist/supervise-4_48wwvy.d.ts +0 -379
- package/dist/types-BCoemcXU.d.ts +0 -802
- package/dist/types-BwoZWq-i.d.ts +0 -510
- package/dist/types-Cbp72qyu.d.ts +0 -491
- package/dist/types-CmYCMbFT.d.ts +0 -203
- package/dist/types-CvNwMzQt.d.ts +0 -957
- package/dist/worktree-fanout-DxhAWr5Z.d.ts +0 -999
|
@@ -1,2570 +0,0 @@
|
|
|
1
|
-
import { R as Runtime, E as ExecutorFactory, U as UsageEvent, k as ExecutorProgress, T as TraceSource, g as Spend, i as ExecutorRegistry, A as Agent, S as Scope, b as ResultBlobStore, B as Budget } from './types-CvNwMzQt.js';
|
|
2
|
-
import { StreamingDetector, DetectorSignal, ToolSpan, AgentEvalError } from '@tangle-network/agent-eval';
|
|
3
|
-
import { AgentProfile as AgentProfile$1 } from '@tangle-network/agent-interface';
|
|
4
|
-
import { a as UiLens, U as UiFinding, C as CoderTask } from './substrate-Dyiqmd3o.js';
|
|
5
|
-
import { a as SandboxClient, E as ExecCtx, t as LoopTraceEmitter, c as LoopTraceEvent, A as AgentRunSpec } from './types-BCoemcXU.js';
|
|
6
|
-
import { AgentProfile, BackendType, SandboxEvent, SandboxInstance } from '@tangle-network/sandbox';
|
|
7
|
-
import { b as ToolSpec, c as RuntimeTelemetryOptions, R as RouterConfig } from './sanitize-DEbPNtyI.js';
|
|
8
|
-
import { R as RuntimeStreamEvent } from './types-BwoZWq-i.js';
|
|
9
|
-
import { G as GitRunner, a as WorktreeCheckRunner, D as DeliverableSpec } from './completion-gate-BMy5LGoP.js';
|
|
10
|
-
import { L as LocalHarness } from './local-harness-BDNcl6jI.js';
|
|
11
|
-
import { ProviderExecutorOptions, AgentEnvironmentProviderRegistry } from './environment-provider.js';
|
|
12
|
-
import { AgentEnvironmentProvider } from '@tangle-network/agent-interface/environment-provider';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
*
|
|
16
|
-
* `piExecutor` — pi wrapped behind `Executor`, NOT forked.
|
|
17
|
-
*
|
|
18
|
-
* pi already implements everything a steerable worker needs, and implements it well: a queued
|
|
19
|
-
* steering channel delivered between turns, follow-ups, abort, compaction, session persistence
|
|
20
|
-
* with fork/branch, and an out-of-process line-delimited JSON protocol over stdin/stdout
|
|
21
|
-
* (`pi --mode rpc`). Reimplementing any of that here would mean owning a turn loop someone else
|
|
22
|
-
* already maintains. So this module is a thin protocol adapter, and every capability maps onto a
|
|
23
|
-
* verb pi already has:
|
|
24
|
-
*
|
|
25
|
-
* `execute` → `prompt`, draining pi's event stream until `agent_end`
|
|
26
|
-
* `deliver` → `prompt` with `streamingBehavior` — pi owns the queue, we do not
|
|
27
|
-
* `teardown` → `abort`, then close stdin and reap the process
|
|
28
|
-
* `progress` → pi's `tool_execution_start`/`_end` + `turn_end` events, plus `get_state`'s
|
|
29
|
-
* `pendingMessageCount` mirrored locally so the read stays synchronous
|
|
30
|
-
* `traceSource` → the same tool events decoded into the shared `ToolSpan` currency
|
|
31
|
-
* `resultArtifact` → the last assistant text collected off the stream
|
|
32
|
-
*
|
|
33
|
-
* It is registered through the DOCUMENTED extension point (`ExecutorRegistry.register('pi', …)`),
|
|
34
|
-
* so nothing in the resolver switches on it and a consumer can replace it wholesale.
|
|
35
|
-
*
|
|
36
|
-
* agent-runtime does NOT take a dependency on pi. The wire shapes (`RpcCommand`, `AgentEvent`)
|
|
37
|
-
* are read structurally off JSON lines, so a pi that adds commands stays compatible and a pi that
|
|
38
|
-
* is not installed simply fails loud at spawn instead of at import.
|
|
39
|
-
*
|
|
40
|
-
* Usage accounting: pi reports token usage on its assistant messages when the provider supplies
|
|
41
|
-
* it. Nothing is fabricated — a turn whose usage pi does not report contributes an `iteration`
|
|
42
|
-
* event and zero tokens, exactly like the other honest executors.
|
|
43
|
-
*
|
|
44
|
-
* @experimental
|
|
45
|
-
*/
|
|
46
|
-
|
|
47
|
-
/** The runtime name `piExecutor` registers under. */
|
|
48
|
-
declare const PI_RUNTIME: Runtime;
|
|
49
|
-
/** Seam key the registry threads a `PiSeam` through (`ExecutorContext.seams['pi']`). */
|
|
50
|
-
declare const piSeamKey = "pi";
|
|
51
|
-
/** How to launch pi in its out-of-process RPC mode, and how long to wait on it. */
|
|
52
|
-
interface PiSeam {
|
|
53
|
-
/** The pi executable (default `'pi'`). Anything on PATH or an absolute path. */
|
|
54
|
-
bin?: string;
|
|
55
|
-
/** Extra args appended after `--mode rpc`. `--provider` / `--model` are added from `model`. */
|
|
56
|
-
args?: ReadonlyArray<string>;
|
|
57
|
-
/** `provider/model` or just `model` — split on the first `/` into pi's two flags. */
|
|
58
|
-
model?: string;
|
|
59
|
-
cwd?: string;
|
|
60
|
-
env?: Record<string, string>;
|
|
61
|
-
/** Wall-clock ceiling for one `prompt` (the wait for `agent_end`). Omit = no timeout. */
|
|
62
|
-
turnTimeoutMs?: number;
|
|
63
|
-
/** Newest-last activity window `progress()` reports. Default 12. */
|
|
64
|
-
activityWindow?: number;
|
|
65
|
-
}
|
|
66
|
-
/** Build the `Executor` for one pi worker. Registered as runtime `'pi'`. */
|
|
67
|
-
declare const piExecutor: ExecutorFactory<unknown>;
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
*
|
|
71
|
-
* The worker-side receive end of the down-leg: a per-worker inbox an executor exposes as
|
|
72
|
-
* `Executor.deliver`. The driver's `steer_agent` / `answer_question` land here,
|
|
73
|
-
* and the worker's agent loop drains them at two points (Drew's two delivery modes):
|
|
74
|
-
*
|
|
75
|
-
* - QUEUED (default): the message accumulates and is FLUSHED at the next step boundary — folded
|
|
76
|
-
* into the conversation before the next think. A worker is also forced to flush BEFORE it may
|
|
77
|
-
* settle, so it can never finish while a steer/answer it never read is still pending.
|
|
78
|
-
* - FORCEFUL (`interrupt: true`): trips `freshInterrupt()`'s signal so the loop can abort its
|
|
79
|
-
* in-flight turn immediately, then re-plan with the message folded in — breaking the worker out
|
|
80
|
-
* of a wrong path mid-task instead of waiting for it to finish the step.
|
|
81
|
-
*
|
|
82
|
-
* `deliver` never throws — a malformed message is ignored, per the `Executor.deliver` contract.
|
|
83
|
-
*
|
|
84
|
-
* @experimental
|
|
85
|
-
*/
|
|
86
|
-
interface InboxMessage {
|
|
87
|
-
readonly kind: 'steer' | 'answer';
|
|
88
|
-
readonly text: string;
|
|
89
|
-
/** Forceful messages abort the in-flight turn; queued ones wait for the boundary flush. */
|
|
90
|
-
readonly interrupt: boolean;
|
|
91
|
-
/** Present for an `answer` — the question id it resolves. */
|
|
92
|
-
readonly questionId?: string;
|
|
93
|
-
}
|
|
94
|
-
interface Inbox {
|
|
95
|
-
/** The `Executor.deliver` implementation — accept a raw down-message from `Scope.send`. */
|
|
96
|
-
deliver(msg: unknown): void;
|
|
97
|
-
/** Remove and return all pending messages (the flush). */
|
|
98
|
-
drain(): InboxMessage[];
|
|
99
|
-
pending(): number;
|
|
100
|
-
/** Open a fresh per-turn interrupt signal; a later forceful `deliver` aborts it. The loop links
|
|
101
|
-
* this into the signal it passes to its inference call, then re-plans when it fires. */
|
|
102
|
-
freshInterrupt(): AbortSignal;
|
|
103
|
-
/** Render drained messages as ONE operator turn to fold into the worker's conversation. */
|
|
104
|
-
fold(messages: ReadonlyArray<InboxMessage>): string;
|
|
105
|
-
}
|
|
106
|
-
/** Create the worker-side inbox for the down-leg: the driver's `steer_agent` / `answer_question` messages queue here and the worker's loop drains them at step boundaries and before settle. */
|
|
107
|
-
declare function createInbox(): Inbox;
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
*
|
|
111
|
-
* The STEERABLE sandbox worker: one box, one server-side session, MANY turns — so a message
|
|
112
|
-
* from the driver has a boundary to be folded into.
|
|
113
|
-
*
|
|
114
|
-
* The default cloud worker was built on `singleShotDriver`: one `runAgentRounds` shot, no turn
|
|
115
|
-
* boundary, and no `Executor.deliver`. `Scope.send` therefore returned `false` for it and
|
|
116
|
-
* `steer_agent` reported `delivered:false` on every call — steering the DEFAULT worker was
|
|
117
|
-
* mechanically impossible, not merely unused. This module is the missing continuation loop.
|
|
118
|
-
*
|
|
119
|
-
* Structure deliberately mirrors `streamBridgeSession` (the cli-bridge equivalent that already
|
|
120
|
-
* works) so both steerable runtimes have ONE shape:
|
|
121
|
-
*
|
|
122
|
-
* - turn 0 sends the task through `SandboxLineage.start` (fresh box, minted session id);
|
|
123
|
-
* - each later turn fires ONLY when the inbox has a steer/answer to fold, and goes through
|
|
124
|
-
* `SandboxLineage.continue` — the SAME box and the SAME server-side session, so the prompt
|
|
125
|
-
* carries only the new instruction and the worker keeps everything it had already learned;
|
|
126
|
-
* - a worker may not settle while a delivered steer is unread: the loop drains once more
|
|
127
|
-
* before breaking, which is what makes `steer_agent` a promise rather than a hint;
|
|
128
|
-
* - a forceful (`interrupt:true`) steer aborts the in-flight turn so the worker re-plans
|
|
129
|
-
* immediately instead of finishing a path the supervisor already rejected.
|
|
130
|
-
*
|
|
131
|
-
* It also produces the LIVE signal a supervisor steers FROM: every sandbox event stamps
|
|
132
|
-
* activity, tool parts are decoded into the shared `ToolSpan` currency for the online detector
|
|
133
|
-
* panel, and `progress()` answers turn / tool / idle / pending-steer questions synchronously.
|
|
134
|
-
*
|
|
135
|
-
* Reports REAL usage only — token and cost numbers come from the sandbox events themselves.
|
|
136
|
-
*
|
|
137
|
-
* @experimental
|
|
138
|
-
*/
|
|
139
|
-
|
|
140
|
-
/** Ceiling on continuation turns. Turn 0 is the task; every later turn is a folded steer, so
|
|
141
|
-
* this bounds how many times a supervisor may redirect ONE worker before it must respawn. */
|
|
142
|
-
declare const DEFAULT_SANDBOX_STEERING_MAX_TURNS = 24;
|
|
143
|
-
/** Opt-in configuration for the steerable sandbox worker (`SandboxSeam.steering`). Absent, the
|
|
144
|
-
* sandbox executor keeps its historical single-shot `runAgentRounds` composition verbatim. */
|
|
145
|
-
interface SandboxSteeringOptions {
|
|
146
|
-
/** Max turns for one worker (turn 0 + folded steers). Default {@link DEFAULT_SANDBOX_STEERING_MAX_TURNS}. */
|
|
147
|
-
readonly maxTurns?: number;
|
|
148
|
-
/** How many recent tool/turn notes `progress()` reports. Default 12. */
|
|
149
|
-
readonly activityWindow?: number;
|
|
150
|
-
/** Per-turn wall-clock ceiling; the turn's stream is aborted when it elapses. */
|
|
151
|
-
readonly turnTimeoutMs?: number;
|
|
152
|
-
}
|
|
153
|
-
/** What the steerable session exposes to its executor: the usage stream plus the live reads. */
|
|
154
|
-
interface SteerableSandboxSession {
|
|
155
|
-
/** Drive the worker to settlement. `signal` is the spawn-scoped abort handed to `execute`. */
|
|
156
|
-
stream(task: unknown, signal: AbortSignal): AsyncIterable<UsageEvent>;
|
|
157
|
-
progress(): ExecutorProgress;
|
|
158
|
-
traceSource(): TraceSource;
|
|
159
|
-
artifact(): {
|
|
160
|
-
outRef: string;
|
|
161
|
-
out: unknown;
|
|
162
|
-
spent: Spend;
|
|
163
|
-
} | undefined;
|
|
164
|
-
teardown(): Promise<void>;
|
|
165
|
-
}
|
|
166
|
-
interface SteerableSandboxArgs {
|
|
167
|
-
readonly controller: AbortController;
|
|
168
|
-
readonly profile: AgentProfile;
|
|
169
|
-
readonly harness: BackendType;
|
|
170
|
-
readonly sandboxClient: SandboxClient;
|
|
171
|
-
readonly inbox: Inbox;
|
|
172
|
-
readonly taskToPrompt: (task: unknown) => string;
|
|
173
|
-
readonly options?: SandboxSteeringOptions;
|
|
174
|
-
readonly loopCtx?: Partial<Omit<ExecCtx, 'sandboxClient' | 'signal'>>;
|
|
175
|
-
readonly contentRef: (prefix: string, value: unknown) => string;
|
|
176
|
-
readonly now?: () => number;
|
|
177
|
-
}
|
|
178
|
-
/** One steerable sandbox worker. The returned session is inert until `stream()` is drained. */
|
|
179
|
-
declare function createSteerableSandboxSession(args: SteerableSandboxArgs): SteerableSandboxSession;
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
*
|
|
183
|
-
* The leaf runtime — the built-in `Executor` IMPLEMENTATIONS behind the ONE
|
|
184
|
-
* open interface frozen in `./types`, plus the open resolver/registry that maps
|
|
185
|
-
* an `AgentSpec` to one of them OR accepts a bring-your-own executor verbatim.
|
|
186
|
-
*
|
|
187
|
-
* The interface is the extension point, not a closed `inline|sandbox|cli` union:
|
|
188
|
-
* - router/inline : a direct OpenAI-compatible Router call, no box (one-shot).
|
|
189
|
-
* - sandbox : COMPOSES the existing `runAgentRounds` kernel as a single-task
|
|
190
|
-
* leaf and surfaces its token/cost usage as `UsageEvent`s;
|
|
191
|
-
* forwards PR #150's optional `lineage` passthrough WITHOUT
|
|
192
|
-
* reinventing checkpoint/fork (streaming).
|
|
193
|
-
* - cli : a Halo/RLM subprocess; `budgetExempt` (no token accounting),
|
|
194
|
-
* excluded from the equal-k arms by construction (streaming).
|
|
195
|
-
* Every metered runtime reports through the SAME normalized `UsageEvent` channel
|
|
196
|
-
* so the conserved budget pool meters them identically. A user's own agent is
|
|
197
|
-
* first-class the moment it implements `Executor` — register it by name or
|
|
198
|
-
* pass it as `AgentSpec.executor`.
|
|
199
|
-
*
|
|
200
|
-
* Layering: `estimateCost`/`isModelPriced` are substrate primitives from
|
|
201
|
-
* `@tangle-network/agent-eval`; `runAgentRounds`/`acquireSandbox` are runtime kernels
|
|
202
|
-
* from this package. No per-vendor adapters live here.
|
|
203
|
-
*
|
|
204
|
-
* @experimental
|
|
205
|
-
*/
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Router/inline connection seam. A direct OpenAI-compatible Router endpoint —
|
|
209
|
-
* the cheapest leaf, no box, no tools. `model` overrides the profile's model
|
|
210
|
-
* hint when present; otherwise the profile's `model.default` is required.
|
|
211
|
-
*/
|
|
212
|
-
interface RouterSeam {
|
|
213
|
-
routerBaseUrl: string;
|
|
214
|
-
routerKey: string;
|
|
215
|
-
model?: string;
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* Sandbox executor seam. The `sandboxClient` the composed `runAgentRounds` creates
|
|
219
|
-
* boxes through, plus the optional trace/run/lineage wiring forwarded into the
|
|
220
|
-
* loop. `lineage` is opaque here (PR #150's `RunAgentRoundsOptions.lineage`): forwarded
|
|
221
|
-
* forward-compatibly, never inspected — this executor does NOT reinvent
|
|
222
|
-
* checkpoint/fork.
|
|
223
|
-
*/
|
|
224
|
-
interface SandboxSeam {
|
|
225
|
-
sandboxClient: SandboxClient;
|
|
226
|
-
/** Forwarded into the composed `runAgentRounds`'s `ctx` (trace emitter, run handle, etc.). */
|
|
227
|
-
loopCtx?: Partial<Omit<ExecCtx, 'sandboxClient' | 'signal'>>;
|
|
228
|
-
/** PR #150 `RunAgentRoundsOptions.lineage` passthrough — opaque; forwarded, not parsed. */
|
|
229
|
-
lineage?: unknown;
|
|
230
|
-
/** Hard cap on the composed loop's iterations. The budget pool reserves against
|
|
231
|
-
* the spawn `Budget.maxIterations`; this is the leaf's own ceiling. Default 1. */
|
|
232
|
-
maxIterations?: number;
|
|
233
|
-
/**
|
|
234
|
-
* OPT-IN: run this worker as a multi-turn, STEERABLE session instead of the historical
|
|
235
|
-
* single-shot `runAgentRounds` composition. Setting it gives the sandbox worker an `Executor.deliver`
|
|
236
|
-
* inbox (so `Scope.send` / `steer_agent` actually reach it), a live tool-activity trace, and a
|
|
237
|
-
* `progress()` read — turning the default cloud worker from something a supervisor can only
|
|
238
|
-
* wait on into something it can watch and correct.
|
|
239
|
-
*
|
|
240
|
-
* Absent, nothing changes: the same `runAgentRounds` leaf, no inbox, `steer_agent` still reports
|
|
241
|
-
* `delivered:false`. Opt-in because a steerable worker holds ONE box across several turns,
|
|
242
|
-
* which is a different resource profile from a fire-and-forget shot.
|
|
243
|
-
*/
|
|
244
|
-
steering?: SandboxSteeringOptions;
|
|
245
|
-
}
|
|
246
|
-
/** CLI subprocess seam. `bin` + `args` describe the Halo/RLM process to spawn. */
|
|
247
|
-
interface CliSeam {
|
|
248
|
-
bin: string;
|
|
249
|
-
args?: string[];
|
|
250
|
-
/** Extra environment for the subprocess (merged over `process.env`). */
|
|
251
|
-
env?: Record<string, string>;
|
|
252
|
-
/** Working directory for the subprocess. */
|
|
253
|
-
cwd?: string;
|
|
254
|
-
}
|
|
255
|
-
/**
|
|
256
|
-
* cli-worktree seam. A supervisor-authored `AgentProfile` driving a local coding-harness CLI
|
|
257
|
-
* (claude / codex / opencode) on its own git worktree — the leaf `createWorktreeCliExecutor`
|
|
258
|
-
* named as data. `harness` + `repoRoot` + `taskPrompt` are required; the authored
|
|
259
|
-
* `profile.prompt.systemPrompt` + `profile.model.default` reach the harness via the §1.5
|
|
260
|
-
* `harnessInvocation` mapper. Everything else mirrors `WorktreeCliExecutorOptions`.
|
|
261
|
-
*/
|
|
262
|
-
interface CliWorktreeSeam {
|
|
263
|
-
repoRoot: string;
|
|
264
|
-
/** Local CLI harness transport. Omit when `bridge` is set. */
|
|
265
|
-
harness?: LocalHarness;
|
|
266
|
-
taskPrompt: string;
|
|
267
|
-
runId?: string;
|
|
268
|
-
baseRef?: string;
|
|
269
|
-
harnessTimeoutMs?: number;
|
|
270
|
-
/** Isolated, network-off Codex execution with terminal JSONL usage capture. */
|
|
271
|
-
codexReproducible?: boolean;
|
|
272
|
-
/** Absolute host paths denied to reproducible Codex. */
|
|
273
|
-
codexReadDeniedPaths?: ReadonlyArray<string>;
|
|
274
|
-
testCmd?: string;
|
|
275
|
-
typecheckCmd?: string;
|
|
276
|
-
checkTimeoutMs?: number;
|
|
277
|
-
checkOutputCap?: number;
|
|
278
|
-
budgetExempt?: boolean;
|
|
279
|
-
/** Live cli-bridge transport inside the worktree. When set, the worktree leaf accepts
|
|
280
|
-
* `deliver()` messages and resumes the same bridge session in this worktree cwd. */
|
|
281
|
-
bridge?: CliWorktreeBridgeSeam;
|
|
282
|
-
/** Test seam — forwarded to worktree helpers. */
|
|
283
|
-
runGit?: GitRunner;
|
|
284
|
-
/** Test seam — forwarded to verification checks. */
|
|
285
|
-
runCommand?: WorktreeCheckRunner;
|
|
286
|
-
}
|
|
287
|
-
interface CliWorktreeBridgeSeam {
|
|
288
|
-
bridgeUrl: string;
|
|
289
|
-
bridgeBearer: string;
|
|
290
|
-
/** Bridge model/harness id. Defaults to the profile's model hint when omitted. */
|
|
291
|
-
model?: string;
|
|
292
|
-
agentProfile?: Record<string, unknown>;
|
|
293
|
-
timeoutMs?: number;
|
|
294
|
-
/** Stable cli-bridge session id. Defaults to `bridge-worktree-${runId}`. */
|
|
295
|
-
sessionId?: string;
|
|
296
|
-
maxTurns?: number;
|
|
297
|
-
}
|
|
298
|
-
/**
|
|
299
|
-
* cli-bridge seam. A local OpenAI-compatible bridge that fronts harness CLIs
|
|
300
|
-
* (claude-code / opencode / kimi / pi) behind one HTTP surface; `model` doubles
|
|
301
|
-
* as the harness selector (e.g. `claude-code/sonnet`, `opencode/<provider>/<model>`).
|
|
302
|
-
* `agentProfile` is the bridge-dialect profile (metadata.disallowedTools, mcp)
|
|
303
|
-
* forwarded verbatim per request — how an arm disables native tools or injects
|
|
304
|
-
* a provider search MCP.
|
|
305
|
-
*
|
|
306
|
-
* The executor opens a RESUMABLE cli-bridge session — structurally identical to the
|
|
307
|
-
* sandbox executor's persistent box, just local. `sessionId` is the stable
|
|
308
|
-
* caller-owned id cli-bridge maps to the harness's internal conversation id; a
|
|
309
|
-
* follow-up steer/resume on the SAME id continues the SAME harness session (opencode
|
|
310
|
-
* `-s`, claude `--resume`, …). Omit it and the executor mints a stable one per spawn.
|
|
311
|
-
*/
|
|
312
|
-
interface BridgeSeam {
|
|
313
|
-
bridgeUrl: string;
|
|
314
|
-
bridgeBearer: string;
|
|
315
|
-
model: string;
|
|
316
|
-
/** Optional working directory forwarded to cli-bridge and persisted with the session. */
|
|
317
|
-
cwd?: string;
|
|
318
|
-
agentProfile?: Record<string, unknown>;
|
|
319
|
-
timeoutMs?: number;
|
|
320
|
-
/** Stable, caller-owned cli-bridge session id for harness-side resume. Defaults
|
|
321
|
-
* to a freshly minted per-spawn id so each worker is its own resumable session. */
|
|
322
|
-
sessionId?: string;
|
|
323
|
-
/** Per-resume-turn inference cap before the worker settles on its last output.
|
|
324
|
-
* Mirrors `routerToolsInlineExecutor.maxTurns`; default 200 (runaway backstop). */
|
|
325
|
-
maxTurns?: number;
|
|
326
|
-
}
|
|
327
|
-
/** Generic environment provider executor config. External packages implement
|
|
328
|
-
* `AgentEnvironmentProvider`; this built-in wrapper lets `createExecutor`
|
|
329
|
-
* consume them as backend data while preserving the existing usage channel. */
|
|
330
|
-
interface ProviderSeam extends ProviderExecutorOptions {
|
|
331
|
-
provider: AgentEnvironmentProvider | string;
|
|
332
|
-
registry?: AgentEnvironmentProviderRegistry;
|
|
333
|
-
}
|
|
334
|
-
/**
|
|
335
|
-
* Router seam WITH tool use — the tool-using router backend. Same direct
|
|
336
|
-
* OpenAI-compatible endpoint as `RouterSeam`, but each turn passes `tools`; when
|
|
337
|
-
* the model emits tool_calls they run via `executeToolCall` ON THIS HOST and the
|
|
338
|
-
* results fold back as `tool` messages, repeating until the model answers without
|
|
339
|
-
* a tool or `maxTurns` is hit. A real agentic loop, OFF-BOX — no sandbox, so it
|
|
340
|
-
* is unaffected by a box's egress allowlist. One turn = one completion = the
|
|
341
|
-
* equal-compute unit. `executeToolCall` receives the task so per-task tool
|
|
342
|
-
* surfaces (e.g. a gym keyed by task) can dispatch correctly.
|
|
343
|
-
*/
|
|
344
|
-
interface RouterToolsSeam {
|
|
345
|
-
routerBaseUrl: string;
|
|
346
|
-
routerKey: string;
|
|
347
|
-
model?: string;
|
|
348
|
-
tools: ReadonlyArray<ToolSpec>;
|
|
349
|
-
executeToolCall: (name: string, args: Record<string, unknown>, task: unknown) => Promise<string>;
|
|
350
|
-
/** Online observer of each tool step — the seam a `DetectorMonitor` taps to watch the live pipe
|
|
351
|
-
* (raise a `finding` when the worker loops/errors). Called after every tool call resolves, with
|
|
352
|
-
* real per-call wall-clock (`startedAt`/`endedAt`/`durationMs`) so a push `TraceSource` can carry
|
|
353
|
-
* non-zero span durations onto the unified timeline. */
|
|
354
|
-
onToolStep?: (step: {
|
|
355
|
-
toolName: string;
|
|
356
|
-
args: Record<string, unknown>;
|
|
357
|
-
status: 'ok' | 'error';
|
|
358
|
-
startedAt?: number;
|
|
359
|
-
endedAt?: number;
|
|
360
|
-
durationMs?: number;
|
|
361
|
-
}) => void;
|
|
362
|
-
/** Max inference turns. Default 200 (runaway backstop — set far above any
|
|
363
|
-
* legitimate workflow). For tighter per-workflow limits use a cost budget
|
|
364
|
-
* or wall-clock deadline at the call site. */
|
|
365
|
-
maxTurns?: number;
|
|
366
|
-
}
|
|
367
|
-
/**
|
|
368
|
-
* The leaf `createWorktreeCliExecutor` as a backend-as-data factory: a supervisor-authored
|
|
369
|
-
* `AgentProfile` driving claude / codex / opencode on its own worktree. `budgetExempt` like
|
|
370
|
-
* the other CLI leaves; the authored systemPrompt + model reach the harness via §1.5.
|
|
371
|
-
*/
|
|
372
|
-
declare const cliWorktreeExecutor: ExecutorFactory<unknown>;
|
|
373
|
-
/**
|
|
374
|
-
* Config for {@link createExecutor}: the backend is DATA — the cost dial a profile,
|
|
375
|
-
* an experiment config, or a replay journal can name — not an import choice. Each
|
|
376
|
-
* variant carries its backend's seam (router/router-tools/bridge/cli/cli-worktree/sandbox).
|
|
377
|
-
*/
|
|
378
|
-
type ExecutorConfig = ({
|
|
379
|
-
backend: 'router';
|
|
380
|
-
} & RouterSeam) | ({
|
|
381
|
-
backend: 'router-tools';
|
|
382
|
-
} & RouterToolsSeam) | ({
|
|
383
|
-
backend: 'bridge';
|
|
384
|
-
} & BridgeSeam) | ({
|
|
385
|
-
backend: 'cli';
|
|
386
|
-
} & CliSeam) | ({
|
|
387
|
-
backend: 'cli-worktree';
|
|
388
|
-
} & CliWorktreeSeam) | ({
|
|
389
|
-
backend: 'provider';
|
|
390
|
-
} & ProviderSeam) | ({
|
|
391
|
-
backend: 'pi';
|
|
392
|
-
} & PiSeam) | ({
|
|
393
|
-
backend: 'sandbox';
|
|
394
|
-
harness?: BackendType;
|
|
395
|
-
} & SandboxSeam);
|
|
396
|
-
/**
|
|
397
|
-
* The single built-in executor factory. Picks a leaf backend by data (`config.backend`),
|
|
398
|
-
* injects the matching seam, and delegates to that backend's built-in implementation.
|
|
399
|
-
* The `Executor` port stays OPEN: bring-your-own agents implement `Executor` directly
|
|
400
|
-
* and never pass through here. Use this (or `createExecutorRegistry`) instead of a
|
|
401
|
-
* per-vendor adapter or a closed `inline|sandbox|cli` switch — those bypass the
|
|
402
|
-
* `UsageEvent` reporting channel.
|
|
403
|
-
*/
|
|
404
|
-
declare function createExecutor(config: ExecutorConfig): ExecutorFactory<unknown>;
|
|
405
|
-
/**
|
|
406
|
-
* The open resolver/registry. Pre-registers the three built-ins under their
|
|
407
|
-
* runtime tags (`'router'`, `'sandbox'`, `'cli'`) and accepts `register(name,
|
|
408
|
-
* factory)` for any additional runtime — and a BYO `AgentSpec.executor` resolves
|
|
409
|
-
* without touching the registry at all. NOT a closed switch; registration + BYO
|
|
410
|
-
* ARE the extension points.
|
|
411
|
-
*
|
|
412
|
-
* `resolve` precedence (frozen in `ExecutorRegistry`): a BYO `spec.executor` →
|
|
413
|
-
* `harness === null` → the `'router'` factory; else a registered factory for the
|
|
414
|
-
* harness-derived runtime (`'sandbox'` for any `BackendType`); else fail loud.
|
|
415
|
-
*/
|
|
416
|
-
declare function createExecutorRegistry(): ExecutorRegistry;
|
|
417
|
-
|
|
418
|
-
/**
|
|
419
|
-
*
|
|
420
|
-
* The ONLINE analyst: watch a `TraceSource` and fold each tool span through agent-eval's published
|
|
421
|
-
* streaming detector kernel (`repeatedActionDetector`/`errorStreakDetector` — the SAME kernel the
|
|
422
|
-
* control loop folds), firing `onSignal` the moment a worker loops or error-storms. Substrate-
|
|
423
|
-
* agnostic: it consumes spans from any source (owned router/bridge loop OR a sandbox box session),
|
|
424
|
-
* never the raw tool seam. Detection logic + the failure taxonomy live in agent-eval; not reimplemented.
|
|
425
|
-
*
|
|
426
|
-
* @experimental
|
|
427
|
-
*/
|
|
428
|
-
|
|
429
|
-
interface WatchTraceOptions {
|
|
430
|
-
/** The detectors to run online. Defaults to a stuck-loop + error-streak panel. */
|
|
431
|
-
readonly detectors?: ReadonlyArray<StreamingDetector>;
|
|
432
|
-
/** Fired for each signal a detector raises — the seam that raises a `finding` on the bus. */
|
|
433
|
-
readonly onSignal?: (signal: DetectorSignal, span: ToolSpan) => void | Promise<void>;
|
|
434
|
-
}
|
|
435
|
-
/** The default online panel for a tool-call pipe: a worker repeating the same call, or hammering
|
|
436
|
-
* consecutive errors. (No-progress needs a domain progress-probe, so it is opt-in, not default.)
|
|
437
|
-
*
|
|
438
|
-
* Coverage note: `repeated-action` works for EVERY harness (it needs only tool name + args, which
|
|
439
|
-
* every adapter provides). `error-streak` needs per-call status — opencode carries it inline
|
|
440
|
-
* (`state.status`, VALIDATED live), but claude-code/codex tool-call parts do NOT (their errors live
|
|
441
|
-
* in separate result blocks not yet decoded), so error-streak is silent for those until result-block
|
|
442
|
-
* decoding is added + live-validated. It is in the panel because it is correct where status exists. */
|
|
443
|
-
declare function defaultToolDetectors(): StreamingDetector[];
|
|
444
|
-
/** Subscribe to a `TraceSource` and run the streaming detectors over its live spans. Returns an
|
|
445
|
-
* unsubscribe. A defensive `argHash` failure (circular args) never throws out of the side-channel. */
|
|
446
|
-
declare function watchTrace(source: TraceSource, opts?: WatchTraceOptions): () => void;
|
|
447
|
-
|
|
448
|
-
/**
|
|
449
|
-
*
|
|
450
|
-
* The child→parent message bus: the ONE pipe carrying every message a worker, sub-driver, or
|
|
451
|
-
* analyst sends up to the driver — settled outputs, questions, and trace-analyst findings. It
|
|
452
|
-
* unifies channels that were ad-hoc before (the settled-worker cursor, the ask-parent question
|
|
453
|
-
* channel, and analyst results) into a single typed primitive with two lanes:
|
|
454
|
-
*
|
|
455
|
-
* - PASS-THROUGH (`subscribe`): every published event reaches subscribers immediately — the
|
|
456
|
-
* express lane for online steering and live observation (a UI, a hook, the parent's box).
|
|
457
|
-
* - STANDBY (`pull`): events also queue so the driver consumes them on its own cadence. The queue
|
|
458
|
-
* is PRIORITY-ordered: a higher-`priority` event (a blocking question) is bumped ahead of
|
|
459
|
-
* queued settles/findings so the driver sees it first; ties resolve FIFO by publish order.
|
|
460
|
-
*
|
|
461
|
-
* Observability is first-class (A++): every event is stamped with a monotonic `seq` and wall-clock
|
|
462
|
-
* `at`, the full ordered `history()` is retained as an audit/replay trail, and `stats()` exposes
|
|
463
|
-
* published/pulled counts by kind. Subscribers receive the stamped record, not a bare event.
|
|
464
|
-
*
|
|
465
|
-
* The interface is transport-agnostic on purpose. Same box → this in-process queue. Cross box →
|
|
466
|
-
* the SAME publish/pull/subscribe surface backed by a durable mailbox on the parent's box (children
|
|
467
|
-
* POST events with at-least-once retry; payloads are blob refs so the event stays small). Consumers
|
|
468
|
-
* depend only on this interface, so distribution is a transport swap, never an architecture change.
|
|
469
|
-
*
|
|
470
|
-
* @experimental
|
|
471
|
-
*/
|
|
472
|
-
/** Every bus event is a discriminated union member keyed by `type`. */
|
|
473
|
-
interface BusEvent {
|
|
474
|
-
readonly type: string;
|
|
475
|
-
}
|
|
476
|
-
/** A published event stamped for ordering and observability. `seq` is the monotonic publish index;
|
|
477
|
-
* `priority` drives pull order (higher = bumped ahead); `at` is the wall-clock publish time (ms). */
|
|
478
|
-
interface BusRecord<E extends BusEvent> {
|
|
479
|
-
readonly seq: number;
|
|
480
|
-
readonly at: number;
|
|
481
|
-
readonly priority: number;
|
|
482
|
-
readonly event: E;
|
|
483
|
-
}
|
|
484
|
-
interface PublishOptions {
|
|
485
|
-
/** Higher = pulled ahead of lower-priority queued events (default 0). A blocking question sets
|
|
486
|
-
* this so it bumps to the front of the driver's inbox. */
|
|
487
|
-
readonly priority?: number;
|
|
488
|
-
/** Whether the event enters the pull queue (default true). Set `false` for record-only events —
|
|
489
|
-
* the parent→child down-leg (steer / answer / resume): they belong in `history()` and reach
|
|
490
|
-
* `subscribe` observers, but the parent must never `pull` its own outbound message back. */
|
|
491
|
-
readonly queue?: boolean;
|
|
492
|
-
}
|
|
493
|
-
interface BusStats {
|
|
494
|
-
readonly published: number;
|
|
495
|
-
readonly pulled: number;
|
|
496
|
-
/** Count published per event `type`. */
|
|
497
|
-
readonly byKind: Readonly<Record<string, number>>;
|
|
498
|
-
}
|
|
499
|
-
interface EventBus<E extends BusEvent> {
|
|
500
|
-
/** Stamp + queue the event, then deliver the stamped record to every subscriber in order.
|
|
501
|
-
* Returns the stamped record. */
|
|
502
|
-
publish(event: E, opts?: PublishOptions): Promise<BusRecord<E>>;
|
|
503
|
-
/** Remove and return the highest-priority QUEUED event whose type is in `kinds` (any if omitted),
|
|
504
|
-
* ties broken FIFO by `seq`; `undefined` when nothing matches. */
|
|
505
|
-
pull(kinds?: ReadonlyArray<E['type']>): E | undefined;
|
|
506
|
-
/** Register a pass-through handler; it receives the stamped record of every event published after
|
|
507
|
-
* registration. Returns an unsubscribe fn. */
|
|
508
|
-
subscribe(handler: (record: BusRecord<E>) => void | Promise<void>): () => void;
|
|
509
|
-
/** Count of queued, not-yet-pulled events (filtered by `kinds` when given). */
|
|
510
|
-
pending(kinds?: ReadonlyArray<E['type']>): number;
|
|
511
|
-
/** The full ordered log of every event ever published (the audit/replay trail). */
|
|
512
|
-
history(): ReadonlyArray<BusRecord<E>>;
|
|
513
|
-
/** Throughput counters for observability dashboards. */
|
|
514
|
-
stats(): BusStats;
|
|
515
|
-
}
|
|
516
|
-
/** Create the child→parent coordination bus: one typed pipe for settled outputs, questions, and analyst findings, with a priority-ordered pull queue and a pass-through subscribe lane. */
|
|
517
|
-
declare function createEventBus<E extends BusEvent>(now?: () => number): EventBus<E>;
|
|
518
|
-
|
|
519
|
-
/**
|
|
520
|
-
*
|
|
521
|
-
* Sandbox-session coder decode layer. The sandbox-session delegate (`./delegates`) and the
|
|
522
|
-
* cross-restart resume driver run the in-box harness over a `SandboxClient` and need to
|
|
523
|
-
* (a) build an `AgentRunSpec` from the caller-authored (or minimal model-only default) worker
|
|
524
|
-
* profile, (b) decode the harness event stream into a structured `CoderOutput`, and (c) gate it with
|
|
525
|
-
* the shared mechanical checks. This sandbox-session path is kept separate from the generic recursive
|
|
526
|
-
* path: `worktreeFanout` instead settles the raw `WorktreePatchArtifact` and gates via
|
|
527
|
-
* `patchDelivered`. Prefer `worktreeFanout` / `worktreeLoopRunner` for NEW local-repo coding.
|
|
528
|
-
*
|
|
529
|
-
* The decode tolerates two `result`-event shapes:
|
|
530
|
-
* 1. the in-process executor's raw worktree-harness result (`{ branch, patch, stats, checks }`),
|
|
531
|
-
* projected onto `CoderOutput`; and
|
|
532
|
-
* 2. an LLM-emitted JSON block (`{ branch, patch, testResult, typecheckResult, diffStats }`),
|
|
533
|
-
* lifted onto `data.result` or scanned out of the assistant transcript (any harness shape).
|
|
534
|
-
*
|
|
535
|
-
* @experimental
|
|
536
|
-
*/
|
|
537
|
-
|
|
538
|
-
/** @experimental The structured coder result the sandbox-session path decodes + gates. */
|
|
539
|
-
interface CoderOutput {
|
|
540
|
-
/** Branch the agent wrote the patch on. */
|
|
541
|
-
branch: string;
|
|
542
|
-
/** Unified diff (`git diff <base>..HEAD`). */
|
|
543
|
-
patch: string;
|
|
544
|
-
testResult: {
|
|
545
|
-
passed: boolean;
|
|
546
|
-
output: string;
|
|
547
|
-
};
|
|
548
|
-
typecheckResult: {
|
|
549
|
-
passed: boolean;
|
|
550
|
-
output: string;
|
|
551
|
-
};
|
|
552
|
-
diffStats: {
|
|
553
|
-
filesChanged: number;
|
|
554
|
-
insertions: number;
|
|
555
|
-
deletions: number;
|
|
556
|
-
};
|
|
557
|
-
/** Optional reviewer commentary surfaced by the agent. */
|
|
558
|
-
reviewerNotes?: string;
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
/**
|
|
562
|
-
*
|
|
563
|
-
* Persistence port for the MCP delegation queue.
|
|
564
|
-
*
|
|
565
|
-
* `DelegationTaskQueue` keeps its working set in memory (status/history
|
|
566
|
-
* reads stay synchronous) and journals every record mutation through a
|
|
567
|
-
* `DelegationStore`. `DelegationTaskQueue.restore({ store })` is the load
|
|
568
|
-
* path: it reads the full record set once at construction and rehydrates
|
|
569
|
-
* the queue from it. After that the store only sees writes.
|
|
570
|
-
*
|
|
571
|
-
* Records MUST be JSON-safe — `FileDelegationStore` round-trips them
|
|
572
|
-
* through `JSON.stringify`/`JSON.parse`, so a `Date`, `Map`, or function
|
|
573
|
-
* smuggled into `args`/`result` would corrupt the journal.
|
|
574
|
-
*
|
|
575
|
-
* @experimental
|
|
576
|
-
*/
|
|
577
|
-
|
|
578
|
-
/** @experimental */
|
|
579
|
-
interface DelegationStore {
|
|
580
|
-
/**
|
|
581
|
-
* Read every persisted record. Called once, by
|
|
582
|
-
* `DelegationTaskQueue.restore`, before any write. A missing backing
|
|
583
|
-
* file is an empty store; an unparseable one throws
|
|
584
|
-
* `DelegationStateCorruptError`.
|
|
585
|
-
*/
|
|
586
|
-
loadAll(): Promise<DelegationRecord[]>;
|
|
587
|
-
/** Insert or replace the record keyed by `record.taskId`. */
|
|
588
|
-
upsert(record: DelegationRecord): Promise<void>;
|
|
589
|
-
/**
|
|
590
|
-
* Resolve an idempotency key to the taskId that claimed it, if any.
|
|
591
|
-
* The queue serves submit-time dedupe from its rehydrated in-memory
|
|
592
|
-
* index; this read exists for consumers that share a store across
|
|
593
|
-
* processes without holding the full record set.
|
|
594
|
-
*/
|
|
595
|
-
lookupIdempotencyKey(key: string): Promise<string | undefined>;
|
|
596
|
-
/** Delete the named records — the retention-cap eviction path. */
|
|
597
|
-
remove(taskIds: readonly string[]): Promise<void>;
|
|
598
|
-
}
|
|
599
|
-
/**
|
|
600
|
-
* The persisted delegation state exists but cannot be parsed into
|
|
601
|
-
* records. Fail loud: silently starting empty over a corrupt journal
|
|
602
|
-
* would erase delegation history and re-run idempotent work. Opt into
|
|
603
|
-
* recovery explicitly via `FileDelegationStoreOptions.recoverCorrupt`
|
|
604
|
-
* (the bin maps `AGENT_RUNTIME_DELEGATION_STATE_RECOVER=1` onto it),
|
|
605
|
-
* which archives the corrupt file and starts fresh.
|
|
606
|
-
*
|
|
607
|
-
* @experimental
|
|
608
|
-
*/
|
|
609
|
-
declare class DelegationStateCorruptError extends AgentEvalError {
|
|
610
|
-
constructor(message: string, options?: {
|
|
611
|
-
cause?: unknown;
|
|
612
|
-
});
|
|
613
|
-
}
|
|
614
|
-
/**
|
|
615
|
-
* A delegation-store read or write failed (filesystem error, store
|
|
616
|
-
* called before `loadAll`, ...). Once the queue observes one, it stops
|
|
617
|
-
* accepting new submissions — accepting work it cannot journal would
|
|
618
|
-
* silently demote durable mode to in-memory mode.
|
|
619
|
-
*
|
|
620
|
-
* @experimental
|
|
621
|
-
*/
|
|
622
|
-
declare class DelegationPersistenceError extends AgentEvalError {
|
|
623
|
-
constructor(message: string, options?: {
|
|
624
|
-
cause?: unknown;
|
|
625
|
-
});
|
|
626
|
-
}
|
|
627
|
-
/** In-memory `DelegationStore` — suitable for single-process use and tests. @experimental */
|
|
628
|
-
declare class InMemoryDelegationStore implements DelegationStore {
|
|
629
|
-
private readonly records;
|
|
630
|
-
loadAll(): Promise<DelegationRecord[]>;
|
|
631
|
-
upsert(record: DelegationRecord): Promise<void>;
|
|
632
|
-
lookupIdempotencyKey(key: string): Promise<string | undefined>;
|
|
633
|
-
remove(taskIds: readonly string[]): Promise<void>;
|
|
634
|
-
}
|
|
635
|
-
/** @experimental */
|
|
636
|
-
interface FileDelegationStoreOptions {
|
|
637
|
-
/** Absolute path of the JSON state file. Parent directories are created on first write. */
|
|
638
|
-
filePath: string;
|
|
639
|
-
/**
|
|
640
|
-
* When the state file exists but cannot be parsed, archive it to
|
|
641
|
-
* `<filePath>.corrupt-<timestamp>` and start empty instead of
|
|
642
|
-
* throwing `DelegationStateCorruptError`. Default false.
|
|
643
|
-
*/
|
|
644
|
-
recoverCorrupt?: boolean;
|
|
645
|
-
}
|
|
646
|
-
/**
|
|
647
|
-
* JSON-file persistence for the delegation queue. Each write serializes
|
|
648
|
-
* the full record set and lands it atomically (write to a sibling tmp
|
|
649
|
-
* file, then `rename`), so readers never observe a torn file — a crash
|
|
650
|
-
* mid-write leaves the previous snapshot intact. Writes are serialized
|
|
651
|
-
* internally; concurrent `upsert`/`remove` calls cannot interleave.
|
|
652
|
-
*
|
|
653
|
-
* Built for the MCP server's scale (one stdio process, hundreds of
|
|
654
|
-
* records): full-snapshot writes keep the format trivially inspectable
|
|
655
|
-
* and corruption-detectable without a database dependency.
|
|
656
|
-
*
|
|
657
|
-
* @experimental
|
|
658
|
-
*/
|
|
659
|
-
declare class FileDelegationStore implements DelegationStore {
|
|
660
|
-
private readonly filePath;
|
|
661
|
-
private readonly recoverCorrupt;
|
|
662
|
-
private readonly records;
|
|
663
|
-
private loaded;
|
|
664
|
-
private writeTail;
|
|
665
|
-
private tmpSeq;
|
|
666
|
-
constructor(options: FileDelegationStoreOptions);
|
|
667
|
-
loadAll(): Promise<DelegationRecord[]>;
|
|
668
|
-
upsert(record: DelegationRecord): Promise<void>;
|
|
669
|
-
lookupIdempotencyKey(key: string): Promise<string | undefined>;
|
|
670
|
-
remove(taskIds: readonly string[]): Promise<void>;
|
|
671
|
-
private assertLoaded;
|
|
672
|
-
private enqueueWrite;
|
|
673
|
-
private writeSnapshot;
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
/**
|
|
677
|
-
*
|
|
678
|
-
* Compact loop-trace tee for the delegation journal.
|
|
679
|
-
*
|
|
680
|
-
* The OTEL exporter ({@link createPropagatingTraceEmitter}) is a no-op
|
|
681
|
-
* without `OTEL_EXPORTER_OTLP_ENDPOINT`, which leaves delegated work streams
|
|
682
|
-
* dark in practice. This module derives the same loop → round → branch span
|
|
683
|
-
* tree (via the shared {@link buildLoopSpanNodes} builder) into a small,
|
|
684
|
-
* JSON-safe shape persisted directly on the `DelegationRecord` — observable
|
|
685
|
-
* through `delegation_status` with no collector infrastructure. Both sinks
|
|
686
|
-
* coexist: the OTEL export path is unchanged.
|
|
687
|
-
*
|
|
688
|
-
* Payload discipline: a record's trace is hard-capped (spans + serialized
|
|
689
|
-
* bytes). Past the cap the OLDEST spans are dropped and the record carries a
|
|
690
|
-
* `traceTruncated: true` marker — truncation is never silent.
|
|
691
|
-
*
|
|
692
|
-
* @experimental
|
|
693
|
-
*/
|
|
694
|
-
|
|
695
|
-
/**
|
|
696
|
-
* One span of a delegation's compact trace. Flat (parent linkage by id), all
|
|
697
|
-
* values JSON-safe scalars — `FileDelegationStore` round-trips records
|
|
698
|
-
* through `JSON.stringify`. `meta` carries the span's attributes (GenAI
|
|
699
|
-
* semconv keys + `tangle.loop.*` extensions) exactly as the OTEL sink emits
|
|
700
|
-
* them, so a consumer can re-export journal traces losslessly.
|
|
701
|
-
*
|
|
702
|
-
* @experimental
|
|
703
|
-
*/
|
|
704
|
-
interface DelegationTraceSpan {
|
|
705
|
-
spanId: string;
|
|
706
|
-
/** Absent on the tree root. */
|
|
707
|
-
parentSpanId?: string;
|
|
708
|
-
/** `'loop'` | `'loop.round'` | `'loop.iteration'` (or a sink-specific name). */
|
|
709
|
-
name: string;
|
|
710
|
-
/** Topology level: loop root, plan round, or iteration branch. */
|
|
711
|
-
kind: 'loop' | 'round' | 'branch';
|
|
712
|
-
startMs: number;
|
|
713
|
-
endMs: number;
|
|
714
|
-
meta?: Record<string, string | number | boolean>;
|
|
715
|
-
}
|
|
716
|
-
/** Default cap on spans retained per delegation record. @experimental */
|
|
717
|
-
declare const DELEGATION_TRACE_MAX_SPANS = 512;
|
|
718
|
-
/** Default cap on the serialized trace payload per record, in bytes. @experimental */
|
|
719
|
-
declare const DELEGATION_TRACE_MAX_BYTES: number;
|
|
720
|
-
/** @experimental */
|
|
721
|
-
interface DelegationTraceCaps {
|
|
722
|
-
/** Default {@link DELEGATION_TRACE_MAX_SPANS}. */
|
|
723
|
-
maxSpans?: number;
|
|
724
|
-
/** Default {@link DELEGATION_TRACE_MAX_BYTES}. Approximate — measured as the
|
|
725
|
-
* sum of per-span `JSON.stringify` lengths. */
|
|
726
|
-
maxBytes?: number;
|
|
727
|
-
}
|
|
728
|
-
/** @experimental */
|
|
729
|
-
interface CappedDelegationTrace {
|
|
730
|
-
trace: DelegationTraceSpan[];
|
|
731
|
-
/** True when oldest spans were dropped to honor the caps. */
|
|
732
|
-
truncated: boolean;
|
|
733
|
-
}
|
|
734
|
-
/**
|
|
735
|
-
* Derive the compact span tree for ONE loop run from its buffered
|
|
736
|
-
* `LoopTraceEvent` stream. Same reconstruction as the OTEL exporter
|
|
737
|
-
* ({@link buildLoopSpanNodes}); tolerates partial streams.
|
|
738
|
-
*
|
|
739
|
-
* @experimental
|
|
740
|
-
*/
|
|
741
|
-
declare function buildDelegationTraceSpans(events: ReadonlyArray<LoopTraceEvent>): DelegationTraceSpan[];
|
|
742
|
-
/**
|
|
743
|
-
* Enforce the trace caps over an ordered (oldest-first) span list. Drops the
|
|
744
|
-
* OLDEST spans first and reports `truncated: true` when anything was dropped;
|
|
745
|
-
* the newest span always survives, so a non-empty input never caps to empty.
|
|
746
|
-
* Dropping a parent may orphan surviving children's `parentSpanId` references
|
|
747
|
-
* — acceptable for the flat journal shape; consumers treat unresolved parents
|
|
748
|
-
* as roots.
|
|
749
|
-
*
|
|
750
|
-
* @experimental
|
|
751
|
-
*/
|
|
752
|
-
declare function capDelegationTrace(spans: ReadonlyArray<DelegationTraceSpan>, caps?: DelegationTraceCaps): CappedDelegationTrace;
|
|
753
|
-
/**
|
|
754
|
-
* Per-delegation trace collector. Buffers `LoopTraceEvent`s per runId
|
|
755
|
-
* (mirroring the OTEL emitter's buffering) and hands the derived compact
|
|
756
|
-
* spans to `onSpans` when a run reaches `loop.ended`. `settle()` drains runs
|
|
757
|
-
* that never ended — a hard-aborted loop still leaves its partial tree in the
|
|
758
|
-
* journal, unlike the OTEL path which drops it.
|
|
759
|
-
*
|
|
760
|
-
* @experimental
|
|
761
|
-
*/
|
|
762
|
-
interface DelegationTraceCollector {
|
|
763
|
-
emitter: LoopTraceEmitter;
|
|
764
|
-
/** Flush buffered events of runs that never reached `loop.ended`. */
|
|
765
|
-
settle(): void;
|
|
766
|
-
}
|
|
767
|
-
/** Build a `DelegationTraceCollector` that buffers loop-trace events and converts them to spans on settle. @experimental */
|
|
768
|
-
declare function createDelegationTraceCollector(onSpans: (spans: DelegationTraceSpan[]) => void): DelegationTraceCollector;
|
|
769
|
-
/**
|
|
770
|
-
* Fan one `LoopTraceEvent` stream into several emitters — e.g. the
|
|
771
|
-
* process-wide OTEL exporter AND the per-delegation journal collector.
|
|
772
|
-
* `undefined` entries are skipped; returns `undefined` when nothing is left
|
|
773
|
-
* so callers keep the kernel's "no emitter, no events" fast path.
|
|
774
|
-
*
|
|
775
|
-
* @experimental
|
|
776
|
-
*/
|
|
777
|
-
declare function composeLoopTraceEmitters(...emitters: ReadonlyArray<LoopTraceEmitter | undefined>): LoopTraceEmitter | undefined;
|
|
778
|
-
|
|
779
|
-
/**
|
|
780
|
-
* OTEL span exporter — streams LoopTraceEvents to an OTLP/HTTP collector.
|
|
781
|
-
*
|
|
782
|
-
* Reads OTEL_EXPORTER_OTLP_ENDPOINT + OTEL_EXPORTER_OTLP_HEADERS from env
|
|
783
|
-
* when no explicit config is given. Keeps the runtime dep-free from
|
|
784
|
-
* @opentelemetry/sdk-trace-base — minimal OTLP/JSON serializer.
|
|
785
|
-
*
|
|
786
|
-
* The exporter accepts both raw OtelSpan objects and LoopTraceEvents
|
|
787
|
-
* (which get converted to OTLP spans automatically).
|
|
788
|
-
*/
|
|
789
|
-
|
|
790
|
-
interface OtelExportConfig {
|
|
791
|
-
/** OTLP endpoint. Reads OTEL_EXPORTER_OTLP_ENDPOINT env by default. */
|
|
792
|
-
endpoint?: string;
|
|
793
|
-
/** OTLP headers. Reads OTEL_EXPORTER_OTLP_HEADERS env by default. */
|
|
794
|
-
headers?: Record<string, string>;
|
|
795
|
-
/** Batch size before flush. Default 64. */
|
|
796
|
-
batchSize?: number;
|
|
797
|
-
/** Flush interval ms. Default 5000. */
|
|
798
|
-
flushIntervalMs?: number;
|
|
799
|
-
/** Resource attributes stamped on every export. */
|
|
800
|
-
resourceAttributes?: Record<string, string | number | boolean>;
|
|
801
|
-
/** Service name. Default 'agent-runtime'. */
|
|
802
|
-
serviceName?: string;
|
|
803
|
-
}
|
|
804
|
-
interface OtelExporter {
|
|
805
|
-
/** Export a span. */
|
|
806
|
-
exportSpan(span: OtelSpan): void;
|
|
807
|
-
/** Force flush pending spans. */
|
|
808
|
-
flush(): Promise<void>;
|
|
809
|
-
/** Shutdown cleanly. */
|
|
810
|
-
shutdown(): Promise<void>;
|
|
811
|
-
}
|
|
812
|
-
interface OtelSpan {
|
|
813
|
-
traceId: string;
|
|
814
|
-
spanId: string;
|
|
815
|
-
parentSpanId?: string;
|
|
816
|
-
name: string;
|
|
817
|
-
kind?: number;
|
|
818
|
-
startTimeUnixNano: string;
|
|
819
|
-
endTimeUnixNano: string;
|
|
820
|
-
attributes?: OtelAttribute[];
|
|
821
|
-
status?: {
|
|
822
|
-
code: number;
|
|
823
|
-
message?: string;
|
|
824
|
-
};
|
|
825
|
-
}
|
|
826
|
-
interface OtelAttribute {
|
|
827
|
-
key: string;
|
|
828
|
-
value: {
|
|
829
|
-
stringValue?: string;
|
|
830
|
-
intValue?: string;
|
|
831
|
-
doubleValue?: number;
|
|
832
|
-
boolValue?: boolean;
|
|
833
|
-
};
|
|
834
|
-
}
|
|
835
|
-
/**
|
|
836
|
-
* Create an OTEL exporter. Returns undefined when no endpoint is configured.
|
|
837
|
-
*/
|
|
838
|
-
declare function createOtelExporter(config?: OtelExportConfig): OtelExporter | undefined;
|
|
839
|
-
/**
|
|
840
|
-
* Convert a LoopTraceEvent into an OtelSpan for export.
|
|
841
|
-
*/
|
|
842
|
-
declare function loopEventToOtelSpan(event: {
|
|
843
|
-
kind: string;
|
|
844
|
-
runId: string;
|
|
845
|
-
timestamp: number;
|
|
846
|
-
payload: object;
|
|
847
|
-
}, traceId: string, parentSpanId?: string): OtelSpan;
|
|
848
|
-
interface RuntimeEventOtelOptions extends RuntimeTelemetryOptions {
|
|
849
|
-
/** Final customer redactor applied after the schema-aware runtime sanitizer. */
|
|
850
|
-
redact?: (value: unknown) => unknown;
|
|
851
|
-
}
|
|
852
|
-
/** Convert normalized runtime events into lossless, redacted child spans. */
|
|
853
|
-
declare function buildRuntimeEventOtelSpans(events: ReadonlyArray<RuntimeStreamEvent>, traceId: string, parentSpanId?: string, options?: RuntimeEventOtelOptions): OtelSpan[];
|
|
854
|
-
/**
|
|
855
|
-
* Sink-neutral node in a reconstructed loop span tree. The root node's
|
|
856
|
-
* `parentSpanId` is `undefined` — sinks decide how to parent it (the OTEL
|
|
857
|
-
* mapper attaches the inherited delegation span; the delegation journal
|
|
858
|
-
* leaves it as the tree root).
|
|
859
|
-
*/
|
|
860
|
-
interface LoopSpanNode {
|
|
861
|
-
spanId: string;
|
|
862
|
-
parentSpanId?: string;
|
|
863
|
-
/** `'loop'` | `'loop.round'` | `'loop.iteration'`. */
|
|
864
|
-
name: string;
|
|
865
|
-
/** Topology level: loop root, plan round, or iteration branch. */
|
|
866
|
-
kind: 'loop' | 'round' | 'branch';
|
|
867
|
-
startMs: number;
|
|
868
|
-
endMs: number;
|
|
869
|
-
attrs: Record<string, string | number | boolean>;
|
|
870
|
-
/** True when the iteration carried an error — maps to OTEL status code 2. */
|
|
871
|
-
error: boolean;
|
|
872
|
-
}
|
|
873
|
-
/**
|
|
874
|
-
* Build a nested, real-duration OTLP span tree for ONE loop run from its full
|
|
875
|
-
* ordered `LoopTraceEvent` stream. Unlike `loopEventToOtelSpan` (one flat,
|
|
876
|
-
* zero-duration span per event), this reconstructs the topology hierarchy a
|
|
877
|
-
* GenAI trace viewer renders natively:
|
|
878
|
-
*
|
|
879
|
-
* loop (invoke_workflow)
|
|
880
|
-
* └─ loop.round[k] (invoke_workflow) ← tangle.loop.move.{kind,width,rationale}
|
|
881
|
-
* ├─ loop.iteration[i] (invoke_agent) ← gen_ai.agent.name + usage + verdict + placement
|
|
882
|
-
* └─ …
|
|
883
|
-
*
|
|
884
|
-
* Attributes follow the current GenAI semconv (`gen_ai.*`) where they apply and
|
|
885
|
-
* a namespaced `tangle.loop.*` / `tangle.cost.usd` extension for topology /
|
|
886
|
-
* verdict / placement / cost (not yet standardized). Pure: feed it a buffered
|
|
887
|
-
* per-runId event array (e.g. flushed on `loop.ended`) and export the result.
|
|
888
|
-
*/
|
|
889
|
-
declare function buildLoopOtelSpans(events: ReadonlyArray<{
|
|
890
|
-
kind: string;
|
|
891
|
-
runId: string;
|
|
892
|
-
timestamp: number;
|
|
893
|
-
payload: object;
|
|
894
|
-
}>, traceId: string, rootParentSpanId?: string): OtelSpan[];
|
|
895
|
-
/**
|
|
896
|
-
* Sink-neutral core behind {@link buildLoopOtelSpans}: reconstruct the
|
|
897
|
-
* loop → round → branch span tree from one run's ordered `LoopTraceEvent`
|
|
898
|
-
* stream. Consumed by the OTEL mapper above and by the MCP delegation
|
|
899
|
-
* journal's compact trace tee — one topology reconstruction, two sinks.
|
|
900
|
-
* Tolerates partial streams (a run that never reached `loop.ended` closes
|
|
901
|
-
* at the last observed event's timestamp).
|
|
902
|
-
*/
|
|
903
|
-
declare function buildLoopSpanNodes(events: ReadonlyArray<{
|
|
904
|
-
kind: string;
|
|
905
|
-
runId: string;
|
|
906
|
-
timestamp: number;
|
|
907
|
-
payload: object;
|
|
908
|
-
}>): LoopSpanNode[];
|
|
909
|
-
/** Wire version the eval-runs ingest enforces (X-Tangle-Wire-Version + body). */
|
|
910
|
-
declare const INTELLIGENCE_WIRE_VERSION = "2026-05-26.v1";
|
|
911
|
-
interface EvalRunGeneration {
|
|
912
|
-
/** 0-based ordinal of this generation within the run (required by ingest). */
|
|
913
|
-
index: number;
|
|
914
|
-
/** Identity of the proposed surface change (content-addressed hash). */
|
|
915
|
-
surfaceHash: string;
|
|
916
|
-
/** Arbitrary provenance for this generation (rationale, evidence, source). */
|
|
917
|
-
surface?: unknown;
|
|
918
|
-
/** Per-scenario results; empty until the generation is measured. */
|
|
919
|
-
cells?: unknown[];
|
|
920
|
-
/** Mean composite score (0 when unmeasured — pair with labels.measured). */
|
|
921
|
-
compositeMean: number;
|
|
922
|
-
costUsd: number;
|
|
923
|
-
durationMs: number;
|
|
924
|
-
}
|
|
925
|
-
interface EvalRunEvent {
|
|
926
|
-
runId: string;
|
|
927
|
-
runDir: string;
|
|
928
|
-
/** ISO timestamp. */
|
|
929
|
-
timestamp: string;
|
|
930
|
-
status: 'started' | 'baseline-complete' | 'generation-complete' | 'gate-decided' | 'finished' | 'errored';
|
|
931
|
-
labels?: Record<string, string>;
|
|
932
|
-
baseline?: EvalRunGeneration;
|
|
933
|
-
generations?: EvalRunGeneration[];
|
|
934
|
-
gateDecision?: 'ship' | 'hold' | 'need_more_work' | 'model_ceiling' | 'arch_ceiling';
|
|
935
|
-
holdoutLift?: number;
|
|
936
|
-
totalCostUsd: number;
|
|
937
|
-
totalDurationMs: number;
|
|
938
|
-
errorMessage?: string;
|
|
939
|
-
}
|
|
940
|
-
interface EvalRunsExportConfig {
|
|
941
|
-
/** Bearer key — tenant is resolved server-side from it. Reads TANGLE_API_KEY. */
|
|
942
|
-
apiKey?: string;
|
|
943
|
-
/** Intelligence base. Reads TANGLE_INTELLIGENCE_URL env, else prod. */
|
|
944
|
-
base?: string;
|
|
945
|
-
/** Idempotency-Key header (e.g. the runId) — safe retries + upsert. */
|
|
946
|
-
idempotencyKey?: string;
|
|
947
|
-
}
|
|
948
|
-
interface EvalRunsExportResult {
|
|
949
|
-
ok: boolean;
|
|
950
|
-
status: number;
|
|
951
|
-
accepted: number;
|
|
952
|
-
rejected: Array<{
|
|
953
|
-
index: number;
|
|
954
|
-
reason: string;
|
|
955
|
-
}>;
|
|
956
|
-
}
|
|
957
|
-
/**
|
|
958
|
-
* Ship self-improvement eval-run events to Tangle Intelligence. Unlike the
|
|
959
|
-
* best-effort span exporter, this RESOLVES with the ingest verdict (accepted /
|
|
960
|
-
* rejected per event) so a consumer's loop can assert its provenance landed.
|
|
961
|
-
* Throws only on a missing key or network failure.
|
|
962
|
-
*/
|
|
963
|
-
declare function exportEvalRuns(events: EvalRunEvent[], config?: EvalRunsExportConfig): Promise<EvalRunsExportResult>;
|
|
964
|
-
|
|
965
|
-
/**
|
|
966
|
-
*
|
|
967
|
-
* Trace context propagation for MCP subprocess.
|
|
968
|
-
*
|
|
969
|
-
* When the MCP server is launched as a child process by a sandbox harness,
|
|
970
|
-
* the parent passes trace context via environment variables:
|
|
971
|
-
*
|
|
972
|
-
* TRACE_ID=<current-run-trace-id>
|
|
973
|
-
* PARENT_SPAN_ID=<span-that-dispatched-the-delegation>
|
|
974
|
-
*
|
|
975
|
-
* The MCP server reads these at startup and uses them as the root of its
|
|
976
|
-
* internal trace tree. All spans emitted by `runAgentRounds` invocations inside
|
|
977
|
-
* the MCP are children of the parent's delegation span.
|
|
978
|
-
*
|
|
979
|
-
* When these env vars are absent, the MCP generates a fresh trace root —
|
|
980
|
-
* the server operates standalone without trace joining.
|
|
981
|
-
*
|
|
982
|
-
* @experimental
|
|
983
|
-
*/
|
|
984
|
-
|
|
985
|
-
interface TraceContext {
|
|
986
|
-
/** Trace id inherited from the parent process, or a fresh one. */
|
|
987
|
-
traceId: string;
|
|
988
|
-
/** Parent span id from the delegation that launched this MCP server. */
|
|
989
|
-
parentSpanId?: string;
|
|
990
|
-
}
|
|
991
|
-
/**
|
|
992
|
-
* Read trace context from the process environment.
|
|
993
|
-
* Returns a context with inherited ids or a freshly generated root.
|
|
994
|
-
*/
|
|
995
|
-
declare function readTraceContextFromEnv(): TraceContext;
|
|
996
|
-
/**
|
|
997
|
-
* Create a LoopTraceEmitter that:
|
|
998
|
-
* 1. Parents all spans under the inherited PARENT_SPAN_ID.
|
|
999
|
-
* 2. Exports spans to OTEL when OTEL_EXPORTER_OTLP_ENDPOINT is set.
|
|
1000
|
-
*
|
|
1001
|
-
* Returns both the emitter and the optional exporter handle for shutdown.
|
|
1002
|
-
*/
|
|
1003
|
-
declare function createPropagatingTraceEmitter(ctx: TraceContext): {
|
|
1004
|
-
emitter: LoopTraceEmitter;
|
|
1005
|
-
exporter: OtelExporter | undefined;
|
|
1006
|
-
context: TraceContext;
|
|
1007
|
-
};
|
|
1008
|
-
/**
|
|
1009
|
-
* Build env vars to pass to a child MCP subprocess so it inherits the
|
|
1010
|
-
* current trace context.
|
|
1011
|
-
*/
|
|
1012
|
-
declare function traceContextToEnv(ctx: TraceContext): Record<string, string>;
|
|
1013
|
-
|
|
1014
|
-
/**
|
|
1015
|
-
*
|
|
1016
|
-
* MCP delegation tool surface — the typed inputs/outputs the product agent
|
|
1017
|
-
* sees over the wire. These types are the contract; the JSON schemas under
|
|
1018
|
-
* `tools/*` mirror them for the MCP `tools/list` advertisement.
|
|
1019
|
-
*
|
|
1020
|
-
* Async semantics: `delegate_ui_audit` returns a `taskId` immediately. The
|
|
1021
|
-
* product agent polls `delegation_status` until the task transitions to
|
|
1022
|
-
* `completed` | `failed` | `cancelled`. `delegate_feedback` +
|
|
1023
|
-
* `delegation_history` are synchronous reads / writes against the local
|
|
1024
|
-
* task queue + feedback store.
|
|
1025
|
-
*
|
|
1026
|
-
* @experimental
|
|
1027
|
-
*/
|
|
1028
|
-
|
|
1029
|
-
/** @experimental */
|
|
1030
|
-
type DelegationProfile = 'coder' | 'researcher' | 'ui-auditor';
|
|
1031
|
-
/** @experimental */
|
|
1032
|
-
type DelegationStatus = 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
|
|
1033
|
-
/**
|
|
1034
|
-
* Minimal `CoderTask` overrides exposed over the MCP wire. The full
|
|
1035
|
-
* `CoderTask` carries fields the kernel synthesizes from `goal` +
|
|
1036
|
-
* `repoRoot` — the agent only edits the few that materially gate
|
|
1037
|
-
* validator behavior.
|
|
1038
|
-
*
|
|
1039
|
-
* @experimental
|
|
1040
|
-
*/
|
|
1041
|
-
interface DelegateCodeConfig {
|
|
1042
|
-
testCmd?: string;
|
|
1043
|
-
typecheckCmd?: string;
|
|
1044
|
-
forbiddenPaths?: string[];
|
|
1045
|
-
maxDiffLines?: number;
|
|
1046
|
-
}
|
|
1047
|
-
/** @experimental */
|
|
1048
|
-
interface DelegateCodeArgs {
|
|
1049
|
-
/** Natural-language description of what the coder must accomplish. */
|
|
1050
|
-
goal: string;
|
|
1051
|
-
/** Absolute path inside the sandbox where the repo lives. */
|
|
1052
|
-
repoRoot: string;
|
|
1053
|
-
/** Optional free-form context the agent surfaces in the prompt prelude. */
|
|
1054
|
-
contextHint?: string;
|
|
1055
|
-
/**
|
|
1056
|
-
* When > 1, dispatches `multiHarnessCoderFanout` across N harnesses
|
|
1057
|
-
* (claude-code, codex, opencode-glm) and picks the highest-scoring
|
|
1058
|
-
* passing patch. Default 1.
|
|
1059
|
-
*/
|
|
1060
|
-
variants?: number;
|
|
1061
|
-
/** Validator + prompt overrides the agent knows for this repo. */
|
|
1062
|
-
config?: DelegateCodeConfig;
|
|
1063
|
-
/** Multi-tenant scope (customer-id, workspace-id). */
|
|
1064
|
-
namespace?: string;
|
|
1065
|
-
}
|
|
1066
|
-
/** @experimental */
|
|
1067
|
-
interface DelegateCodeResult {
|
|
1068
|
-
taskId: string;
|
|
1069
|
-
/** Best-effort hint — coder loops can take minutes-to-hours. */
|
|
1070
|
-
estimatedDurationMs?: number;
|
|
1071
|
-
}
|
|
1072
|
-
/** @experimental */
|
|
1073
|
-
type ResearchSource = 'web' | 'corpus' | 'twitter' | 'github' | 'docs';
|
|
1074
|
-
/** @experimental */
|
|
1075
|
-
interface DelegateResearchConfig {
|
|
1076
|
-
recencyWindow?: {
|
|
1077
|
-
since?: string;
|
|
1078
|
-
until?: string;
|
|
1079
|
-
};
|
|
1080
|
-
maxItems?: number;
|
|
1081
|
-
minConfidence?: number;
|
|
1082
|
-
}
|
|
1083
|
-
/** @experimental */
|
|
1084
|
-
interface DelegateResearchArgs {
|
|
1085
|
-
question: string;
|
|
1086
|
-
namespace: string;
|
|
1087
|
-
scope?: string;
|
|
1088
|
-
sources?: ResearchSource[];
|
|
1089
|
-
variants?: number;
|
|
1090
|
-
config?: DelegateResearchConfig;
|
|
1091
|
-
}
|
|
1092
|
-
/** @experimental */
|
|
1093
|
-
interface DelegateResearchResult {
|
|
1094
|
-
taskId: string;
|
|
1095
|
-
estimatedDurationMs?: number;
|
|
1096
|
-
}
|
|
1097
|
-
/** @experimental */
|
|
1098
|
-
interface FeedbackRefersTo {
|
|
1099
|
-
kind: 'delegation' | 'artifact' | 'outcome';
|
|
1100
|
-
/** For `'delegation'`, this is the taskId. */
|
|
1101
|
-
ref: string;
|
|
1102
|
-
}
|
|
1103
|
-
/** @experimental */
|
|
1104
|
-
interface FeedbackRating {
|
|
1105
|
-
/** [0, 1]. */
|
|
1106
|
-
score: number;
|
|
1107
|
-
label?: 'good' | 'bad' | 'neutral' | 'mixed';
|
|
1108
|
-
notes: string;
|
|
1109
|
-
}
|
|
1110
|
-
/** @experimental */
|
|
1111
|
-
interface DelegateFeedbackArgs {
|
|
1112
|
-
refersTo: FeedbackRefersTo;
|
|
1113
|
-
rating: FeedbackRating;
|
|
1114
|
-
by: 'agent' | 'user' | 'downstream-judge';
|
|
1115
|
-
/** ISO timestamp; defaults to server clock when omitted. */
|
|
1116
|
-
capturedAt?: string;
|
|
1117
|
-
namespace?: string;
|
|
1118
|
-
}
|
|
1119
|
-
/** @experimental */
|
|
1120
|
-
interface DelegateFeedbackResult {
|
|
1121
|
-
recorded: true;
|
|
1122
|
-
id: string;
|
|
1123
|
-
}
|
|
1124
|
-
/** @experimental */
|
|
1125
|
-
interface DelegationStatusArgs {
|
|
1126
|
-
taskId: string;
|
|
1127
|
-
/**
|
|
1128
|
-
* Return the delegation's compact loop-trace span tree alongside the
|
|
1129
|
-
* status. Default false — status polls stay light; opt in when you need
|
|
1130
|
-
* the topology (which iterations ran, where they were placed, what each
|
|
1131
|
-
* cost) rather than just the state machine.
|
|
1132
|
-
*/
|
|
1133
|
-
includeTrace?: boolean;
|
|
1134
|
-
}
|
|
1135
|
-
/** @experimental */
|
|
1136
|
-
interface DelegationProgress {
|
|
1137
|
-
iteration: number;
|
|
1138
|
-
phase: string;
|
|
1139
|
-
}
|
|
1140
|
-
/** @experimental */
|
|
1141
|
-
interface DelegationError {
|
|
1142
|
-
message: string;
|
|
1143
|
-
kind: string;
|
|
1144
|
-
}
|
|
1145
|
-
/**
|
|
1146
|
-
* Polymorphic `result` field: `CoderOutput` when the underlying profile
|
|
1147
|
-
* is `'coder'`, a structurally-typed research output when `'researcher'`.
|
|
1148
|
-
* The MCP wire carries it as JSON either way.
|
|
1149
|
-
*
|
|
1150
|
-
* @experimental
|
|
1151
|
-
*/
|
|
1152
|
-
type DelegationResultPayload = {
|
|
1153
|
-
profile: 'coder';
|
|
1154
|
-
output: CoderOutput;
|
|
1155
|
-
} | {
|
|
1156
|
-
profile: 'researcher';
|
|
1157
|
-
output: ResearchOutputShape;
|
|
1158
|
-
} | {
|
|
1159
|
-
profile: 'ui-auditor';
|
|
1160
|
-
output: UiAuditorDelegationOutput;
|
|
1161
|
-
};
|
|
1162
|
-
/**
|
|
1163
|
-
* Wire-shape of a completed UI-audit delegation. The `findings` array
|
|
1164
|
-
* contains every finding persisted to the workspace during the run,
|
|
1165
|
-
* already enriched with `id` and `createdAt` by the writer. `workspaceDir`
|
|
1166
|
-
* is the absolute path to the workspace; `indexFile` is the workspace-
|
|
1167
|
-
* relative path to the regenerated index.md.
|
|
1168
|
-
*
|
|
1169
|
-
* @experimental
|
|
1170
|
-
*/
|
|
1171
|
-
interface UiAuditorDelegationOutput {
|
|
1172
|
-
workspaceDir: string;
|
|
1173
|
-
indexFile: string;
|
|
1174
|
-
findings: UiFinding[];
|
|
1175
|
-
/** Total iterations the loop ran for this delegation. */
|
|
1176
|
-
iterations: number;
|
|
1177
|
-
}
|
|
1178
|
-
/** @experimental */
|
|
1179
|
-
type UiAuditLensFilter = readonly UiLens[];
|
|
1180
|
-
/** Optional per-route capture spec the agent surfaces over the wire. */
|
|
1181
|
-
interface DelegateUiAuditRoute {
|
|
1182
|
-
/** Stable route name (used in screenshot filenames + finding metadata). */
|
|
1183
|
-
name: string;
|
|
1184
|
-
/** Fully-qualified URL. */
|
|
1185
|
-
url: string;
|
|
1186
|
-
/** Viewports to capture at. Defaults to `[{ width: 1280, height: 800 }]`. */
|
|
1187
|
-
viewports?: readonly {
|
|
1188
|
-
width: number;
|
|
1189
|
-
height: number;
|
|
1190
|
-
}[];
|
|
1191
|
-
/** Default false. Full-page captures for the broad lenses. */
|
|
1192
|
-
fullPage?: boolean;
|
|
1193
|
-
/** Selector to wait for before capture. */
|
|
1194
|
-
waitFor?: string;
|
|
1195
|
-
}
|
|
1196
|
-
/** @experimental */
|
|
1197
|
-
interface DelegateUiAuditConfig {
|
|
1198
|
-
/**
|
|
1199
|
-
* Lenses to iterate. Default: every lens except `'other'`. Order is
|
|
1200
|
-
* preserved — the driver iterates lens-by-lens.
|
|
1201
|
-
*/
|
|
1202
|
-
lenses?: UiAuditLensFilter;
|
|
1203
|
-
/** Maximum total iterations across all (lens × route) pairs. Default 33 (11 lenses × 3 routes). */
|
|
1204
|
-
maxIterations?: number;
|
|
1205
|
-
/** Maximum concurrent iterations within a single plan() round. Default 2. */
|
|
1206
|
-
maxConcurrency?: number;
|
|
1207
|
-
/** Free-form product context surfaced to the judge. */
|
|
1208
|
-
productContext?: string;
|
|
1209
|
-
}
|
|
1210
|
-
/** @experimental */
|
|
1211
|
-
interface DelegateUiAuditArgs {
|
|
1212
|
-
/** Workspace root for the audit (absolute path). */
|
|
1213
|
-
workspaceDir: string;
|
|
1214
|
-
/** Routes to audit. Must be non-empty. */
|
|
1215
|
-
routes: readonly DelegateUiAuditRoute[];
|
|
1216
|
-
/** Multi-tenant scope. */
|
|
1217
|
-
namespace?: string;
|
|
1218
|
-
config?: DelegateUiAuditConfig;
|
|
1219
|
-
}
|
|
1220
|
-
/** @experimental */
|
|
1221
|
-
interface DelegateUiAuditResult {
|
|
1222
|
-
taskId: string;
|
|
1223
|
-
estimatedDurationMs?: number;
|
|
1224
|
-
}
|
|
1225
|
-
/**
|
|
1226
|
-
* Provider-neutral research output carried over the MCP boundary. The MCP
|
|
1227
|
-
* layer accepts this structural shape instead of coupling its wire contract to
|
|
1228
|
-
* one research implementation.
|
|
1229
|
-
*
|
|
1230
|
-
* @experimental
|
|
1231
|
-
*/
|
|
1232
|
-
interface ResearchOutputShape {
|
|
1233
|
-
items: unknown[];
|
|
1234
|
-
citations: unknown[];
|
|
1235
|
-
proposedWrites: unknown[];
|
|
1236
|
-
gaps?: string[];
|
|
1237
|
-
notes?: string;
|
|
1238
|
-
[key: string]: unknown;
|
|
1239
|
-
}
|
|
1240
|
-
/** @experimental */
|
|
1241
|
-
interface DelegationStatusResult {
|
|
1242
|
-
taskId: string;
|
|
1243
|
-
profile: DelegationProfile;
|
|
1244
|
-
status: DelegationStatus;
|
|
1245
|
-
progress?: DelegationProgress;
|
|
1246
|
-
result?: DelegationResultPayload;
|
|
1247
|
-
error?: DelegationError;
|
|
1248
|
-
costUsd?: number;
|
|
1249
|
-
startedAt: string;
|
|
1250
|
-
completedAt?: string;
|
|
1251
|
-
/** Compact loop-trace span tree; present only when `includeTrace: true` was passed and spans were recorded. */
|
|
1252
|
-
trace?: DelegationTraceSpan[];
|
|
1253
|
-
/** Present when oldest trace spans were dropped to honor the trace caps. */
|
|
1254
|
-
traceTruncated?: true;
|
|
1255
|
-
/** Inherited trace identity recorded at submit — join key into the caller's trace. */
|
|
1256
|
-
traceId?: string;
|
|
1257
|
-
/** Caller span that dispatched the delegation, when one was inherited. */
|
|
1258
|
-
parentSpanId?: string;
|
|
1259
|
-
}
|
|
1260
|
-
/** @experimental */
|
|
1261
|
-
interface DelegationHistoryArgs {
|
|
1262
|
-
namespace?: string;
|
|
1263
|
-
profile?: DelegationProfile;
|
|
1264
|
-
/** ISO date — only delegations started at-or-after `since` are returned. */
|
|
1265
|
-
since?: string;
|
|
1266
|
-
/** Default 50. Hard cap 500. */
|
|
1267
|
-
limit?: number;
|
|
1268
|
-
}
|
|
1269
|
-
/** @experimental */
|
|
1270
|
-
interface DelegationFeedbackSnapshot {
|
|
1271
|
-
id: string;
|
|
1272
|
-
score: number;
|
|
1273
|
-
label?: FeedbackRating['label'];
|
|
1274
|
-
by: DelegateFeedbackArgs['by'];
|
|
1275
|
-
notes: string;
|
|
1276
|
-
capturedAt: string;
|
|
1277
|
-
}
|
|
1278
|
-
/** @experimental */
|
|
1279
|
-
interface DelegationHistoryEntry {
|
|
1280
|
-
taskId: string;
|
|
1281
|
-
profile: DelegationProfile;
|
|
1282
|
-
namespace?: string;
|
|
1283
|
-
args: DelegateCodeArgs | DelegateResearchArgs | DelegateUiAuditArgs;
|
|
1284
|
-
status: DelegationStatus;
|
|
1285
|
-
feedback?: DelegationFeedbackSnapshot[];
|
|
1286
|
-
costUsd?: number;
|
|
1287
|
-
startedAt: string;
|
|
1288
|
-
completedAt?: string;
|
|
1289
|
-
/**
|
|
1290
|
-
* True when the record carries a journaled loop trace. History stays
|
|
1291
|
-
* light by design — fetch the spans via
|
|
1292
|
-
* `delegation_status { taskId, includeTrace: true }`.
|
|
1293
|
-
*/
|
|
1294
|
-
hasTrace: boolean;
|
|
1295
|
-
/** Inherited trace identity recorded at submit — join key into the caller's trace. */
|
|
1296
|
-
traceId?: string;
|
|
1297
|
-
}
|
|
1298
|
-
/** @experimental */
|
|
1299
|
-
interface DelegationHistoryResult {
|
|
1300
|
-
delegations: DelegationHistoryEntry[];
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
/**
|
|
1304
|
-
*
|
|
1305
|
-
* State machine for async MCP delegations:
|
|
1306
|
-
*
|
|
1307
|
-
* pending → running → completed | failed
|
|
1308
|
-
* ↘ cancelled (from any non-terminal state via cancel())
|
|
1309
|
-
*
|
|
1310
|
-
* Each `submit` returns a `taskId` immediately and kicks the work off in the
|
|
1311
|
-
* background. The work function receives an `AbortSignal` the queue fires
|
|
1312
|
-
* when `cancel(taskId)` is called. The queue does NOT supervise runtime
|
|
1313
|
-
* timeouts — the underlying `runAgentRounds` driver / sandbox imposes those.
|
|
1314
|
-
*
|
|
1315
|
-
* Idempotency: callers may supply an `idempotencyKey` (hash of the input).
|
|
1316
|
-
* A duplicate `submit` with a known key returns the existing task instead of
|
|
1317
|
-
* starting a new one. Mutated input → different key → different task.
|
|
1318
|
-
*
|
|
1319
|
-
* Durability: the working set lives in memory (reads stay synchronous) and
|
|
1320
|
-
* every record mutation is journaled through a `DelegationStore`. The default
|
|
1321
|
-
* `InMemoryDelegationStore` keeps today's semantics — a process restart drops
|
|
1322
|
-
* all state. Construct via `DelegationTaskQueue.restore({ store })` with a
|
|
1323
|
-
* `FileDelegationStore` to reload prior records on startup: terminal records
|
|
1324
|
-
* stay queryable, in-flight records either re-attach through the
|
|
1325
|
-
* `resumeDelegate` seam (when they carry a `detachedSessionRef`) or fail
|
|
1326
|
-
* loud with a driver-restart error so `delegation_status` tells the truth.
|
|
1327
|
-
*
|
|
1328
|
-
* @experimental
|
|
1329
|
-
*/
|
|
1330
|
-
|
|
1331
|
-
type AnyDelegateArgs = DelegateCodeArgs | DelegateResearchArgs | DelegateUiAuditArgs;
|
|
1332
|
-
/**
|
|
1333
|
-
* Must be JSON-safe end to end (`args`, `result`, `error`, `feedback`) —
|
|
1334
|
-
* persistent stores round-trip records through `JSON.stringify`.
|
|
1335
|
-
*
|
|
1336
|
-
* @experimental
|
|
1337
|
-
*/
|
|
1338
|
-
interface DelegationRecord {
|
|
1339
|
-
taskId: string;
|
|
1340
|
-
profile: DelegationProfile;
|
|
1341
|
-
namespace?: string;
|
|
1342
|
-
args: AnyDelegateArgs;
|
|
1343
|
-
status: DelegationStatus;
|
|
1344
|
-
progress?: DelegationProgress;
|
|
1345
|
-
result?: DelegationResultPayload;
|
|
1346
|
-
error?: DelegationError;
|
|
1347
|
-
costUsd?: number;
|
|
1348
|
-
startedAt: string;
|
|
1349
|
-
completedAt?: string;
|
|
1350
|
-
/** Sha-prefix hash of the canonical input — used for idempotency lookup. */
|
|
1351
|
-
idempotencyKey?: string;
|
|
1352
|
-
/**
|
|
1353
|
-
* Caller-generated deterministic id of a detached run (e.g. the sandbox
|
|
1354
|
-
* session id a single-tick driver resumes by). Presence is what makes a
|
|
1355
|
-
* restored in-flight record resumable via `resumeDelegate`; without it a
|
|
1356
|
-
* restart settles the record as failed.
|
|
1357
|
-
*/
|
|
1358
|
-
detachedSessionRef?: string;
|
|
1359
|
-
/** Feedback events keyed by this delegation's taskId. */
|
|
1360
|
-
feedback: DelegationFeedbackSnapshot[];
|
|
1361
|
-
/**
|
|
1362
|
-
* Compact loop-trace span tree teed from the delegation's run, oldest
|
|
1363
|
-
* spans first. Appended when a delegated loop reaches `loop.ended` and
|
|
1364
|
-
* settled (partial buffers included) at the terminal transition. Capped
|
|
1365
|
-
* via `capDelegationTrace` — see `traceTruncated`.
|
|
1366
|
-
*/
|
|
1367
|
-
trace?: DelegationTraceSpan[];
|
|
1368
|
-
/** Present when oldest trace spans were dropped to honor the trace caps. */
|
|
1369
|
-
traceTruncated?: true;
|
|
1370
|
-
/**
|
|
1371
|
-
* Inherited trace identity (the queue's `traceContext` at submit time —
|
|
1372
|
-
* typically `readTraceContextFromEnv()`), distinct from the span payload:
|
|
1373
|
-
* a journal consumer joins records into the parent trace by these ids
|
|
1374
|
-
* without parsing spans. Restored records keep their persisted identity.
|
|
1375
|
-
*/
|
|
1376
|
-
traceId?: string;
|
|
1377
|
-
/** Caller span that dispatched the delegation, when one was inherited. */
|
|
1378
|
-
parentSpanId?: string;
|
|
1379
|
-
}
|
|
1380
|
-
/** @experimental */
|
|
1381
|
-
interface SubmitInput<Args extends AnyDelegateArgs> {
|
|
1382
|
-
profile: DelegationProfile;
|
|
1383
|
-
args: Args;
|
|
1384
|
-
namespace?: string;
|
|
1385
|
-
idempotencyKey?: string;
|
|
1386
|
-
/**
|
|
1387
|
-
* Records the detached-run resume key on the new record. The submitted
|
|
1388
|
-
* `run` function still executes in-process exactly as without it — the
|
|
1389
|
-
* ref only matters after a restart, when `DelegationTaskQueue.restore`
|
|
1390
|
-
* hands it to the `resumeDelegate` seam instead of failing the record.
|
|
1391
|
-
*/
|
|
1392
|
-
detachedSessionRef?: string;
|
|
1393
|
-
/**
|
|
1394
|
-
* Runs the underlying delegation. The queue passes a fresh `AbortSignal`
|
|
1395
|
-
* and a `report` channel for incremental progress updates. The function
|
|
1396
|
-
* MUST resolve with the typed `DelegationResultPayload['output']`; the
|
|
1397
|
-
* queue wraps it with the profile tag.
|
|
1398
|
-
*/
|
|
1399
|
-
run: (ctx: DelegationRunContext) => Promise<DelegationResultPayload['output']>;
|
|
1400
|
-
}
|
|
1401
|
-
/** @experimental Context handed to a `SubmitInput.run` function. */
|
|
1402
|
-
interface DelegationRunContext {
|
|
1403
|
-
signal: AbortSignal;
|
|
1404
|
-
report(progress: DelegationProgress): void;
|
|
1405
|
-
/** The `detachedSessionRef` recorded at submit, when one was supplied. */
|
|
1406
|
-
detachedSessionRef?: string;
|
|
1407
|
-
/**
|
|
1408
|
-
* Replace the record's detached-run resume key — the detached dispatch path
|
|
1409
|
-
* calls this once the sandbox id is known so the persisted ref names a
|
|
1410
|
-
* resolvable box. Ignored after the record settles (a cancel racing the
|
|
1411
|
-
* rebind is legitimate; the ref no longer matters then). Throws on an empty
|
|
1412
|
-
* ref — erasing the resume key would silently make the record unresumable.
|
|
1413
|
-
*/
|
|
1414
|
-
updateDetachedSessionRef(ref: string): void;
|
|
1415
|
-
/**
|
|
1416
|
-
* Per-delegation loop-trace sink, always provided by the queue. Events
|
|
1417
|
-
* emitted here are journaled onto the record as a compact span tree
|
|
1418
|
-
* (`record.trace`) when each loop run ends and at the delegation's
|
|
1419
|
-
* terminal transition. Delegates forward it into their `runAgentRounds` ctx,
|
|
1420
|
-
* composed with any process-wide OTEL emitter
|
|
1421
|
-
* (`composeLoopTraceEmitters`). Optional in the type so consumer-built
|
|
1422
|
-
* contexts stay source-compatible.
|
|
1423
|
-
*/
|
|
1424
|
-
traceEmitter?: LoopTraceEmitter;
|
|
1425
|
-
}
|
|
1426
|
-
/** @experimental */
|
|
1427
|
-
interface SubmitOutput {
|
|
1428
|
-
taskId: string;
|
|
1429
|
-
/** True when a prior matching `idempotencyKey` returned an existing record. */
|
|
1430
|
-
reused: boolean;
|
|
1431
|
-
}
|
|
1432
|
-
/**
|
|
1433
|
-
* One observation of a detached run, mapped 1:1 from a single-tick driver
|
|
1434
|
-
* (e.g. the sandbox SDK's `driveTurn`, which reports
|
|
1435
|
-
* completed | running | failed per pass). `running` schedules another tick
|
|
1436
|
-
* after `intervalMs`; `completed` / `failed` settle the record.
|
|
1437
|
-
*
|
|
1438
|
-
* @experimental
|
|
1439
|
-
*/
|
|
1440
|
-
type DelegationResumeTick = {
|
|
1441
|
-
state: 'running';
|
|
1442
|
-
} | {
|
|
1443
|
-
state: 'completed';
|
|
1444
|
-
output: DelegationResultPayload['output'];
|
|
1445
|
-
costUsd?: number;
|
|
1446
|
-
} | {
|
|
1447
|
-
state: 'failed';
|
|
1448
|
-
error: DelegationError;
|
|
1449
|
-
};
|
|
1450
|
-
/** @experimental */
|
|
1451
|
-
interface DelegationResumeContext {
|
|
1452
|
-
/** Fired by `cancel(taskId)`; the driver should stop the remote run when it can. */
|
|
1453
|
-
signal: AbortSignal;
|
|
1454
|
-
report(progress: DelegationProgress): void;
|
|
1455
|
-
}
|
|
1456
|
-
/**
|
|
1457
|
-
* Re-attaches restored in-flight records to their detached runs. The queue
|
|
1458
|
-
* calls `tick` repeatedly — it never awaits a whole run — so the driver can
|
|
1459
|
-
* be a thin wrapper over a one-pass primitive: resolve the run named by
|
|
1460
|
-
* `detachedSessionRef`, advance/poll it once, report where it stands. A
|
|
1461
|
-
* thrown error settles the record as failed; `failed` ticks are treated as
|
|
1462
|
-
* terminal and are not retried.
|
|
1463
|
-
*
|
|
1464
|
-
* @experimental
|
|
1465
|
-
*/
|
|
1466
|
-
interface DelegationResumeDriver {
|
|
1467
|
-
tick(task: {
|
|
1468
|
-
record: DelegationRecord;
|
|
1469
|
-
detachedSessionRef: string;
|
|
1470
|
-
}, ctx: DelegationResumeContext): Promise<DelegationResumeTick>;
|
|
1471
|
-
/** Delay between `running` ticks, in milliseconds. Default 5000. */
|
|
1472
|
-
intervalMs?: number;
|
|
1473
|
-
}
|
|
1474
|
-
/** @experimental */
|
|
1475
|
-
interface DelegationTaskQueueOptions {
|
|
1476
|
-
/** ID generator override; default `randomTaskId`. */
|
|
1477
|
-
generateId?: () => string;
|
|
1478
|
-
/** Clock override; default `() => new Date().toISOString()`. */
|
|
1479
|
-
now?: () => string;
|
|
1480
|
-
/**
|
|
1481
|
-
* Journal for record mutations and the `restore()` load source. Default
|
|
1482
|
-
* `InMemoryDelegationStore` — observably identical to an unjournaled
|
|
1483
|
-
* queue. Pass a `FileDelegationStore` through
|
|
1484
|
-
* `DelegationTaskQueue.restore` for state that survives a restart;
|
|
1485
|
-
* constructing with `new` never loads prior state.
|
|
1486
|
-
*/
|
|
1487
|
-
store?: DelegationStore;
|
|
1488
|
-
/** Resume seam for restored in-flight records that carry a `detachedSessionRef`. */
|
|
1489
|
-
resumeDelegate?: DelegationResumeDriver;
|
|
1490
|
-
/**
|
|
1491
|
-
* Maximum number of terminal (completed | failed | cancelled) records
|
|
1492
|
-
* retained; the oldest (by `completedAt`) are evicted from memory and
|
|
1493
|
-
* store once the cap is exceeded. Default unbounded.
|
|
1494
|
-
*/
|
|
1495
|
-
maxTerminalRecords?: number;
|
|
1496
|
-
/**
|
|
1497
|
-
* Observes the first store failure. After it fires, the queue refuses
|
|
1498
|
-
* new submissions and `flush()` rejects with the same error. Default:
|
|
1499
|
-
* rethrow on a microtask — an unhandled crash — because silently
|
|
1500
|
-
* degrading durable mode to memory-only would lie to the caller.
|
|
1501
|
-
*/
|
|
1502
|
-
onPersistError?: (error: DelegationPersistenceError) => void;
|
|
1503
|
-
/**
|
|
1504
|
-
* Inherited trace identity stamped on every submitted record
|
|
1505
|
-
* (`traceId` / `parentSpanId`). The bin passes
|
|
1506
|
-
* `readTraceContextFromEnv()` so journal consumers can join delegation
|
|
1507
|
-
* records into the caller's trace. Restored records keep the identity
|
|
1508
|
-
* they were persisted with.
|
|
1509
|
-
*/
|
|
1510
|
-
traceContext?: TraceContext;
|
|
1511
|
-
}
|
|
1512
|
-
/** In-process queue for async delegation tasks — submit, cancel, poll status, and read history. @experimental */
|
|
1513
|
-
declare class DelegationTaskQueue {
|
|
1514
|
-
private readonly records;
|
|
1515
|
-
private readonly controllers;
|
|
1516
|
-
private readonly byIdempotencyKey;
|
|
1517
|
-
private readonly generateId;
|
|
1518
|
-
private readonly now;
|
|
1519
|
-
private readonly store;
|
|
1520
|
-
private readonly resumeDelegate?;
|
|
1521
|
-
private readonly maxTerminalRecords;
|
|
1522
|
-
private readonly onPersistError;
|
|
1523
|
-
private readonly traceContext;
|
|
1524
|
-
private persistTail;
|
|
1525
|
-
private persistFailure;
|
|
1526
|
-
constructor(options?: DelegationTaskQueueOptions);
|
|
1527
|
-
/**
|
|
1528
|
-
* Construct a queue from previously-persisted state. Loads every record
|
|
1529
|
-
* from `options.store`, rebuilds the idempotency index (so a re-submitted
|
|
1530
|
-
* identical task returns the prior taskId and its terminal state), then:
|
|
1531
|
-
*
|
|
1532
|
-
* - terminal records stay queryable via `status()` / `history()`
|
|
1533
|
-
* - in-flight records with a `detachedSessionRef` re-attach through
|
|
1534
|
-
* `options.resumeDelegate` and report `running`
|
|
1535
|
-
* - other in-flight records settle as failed — their driver died with
|
|
1536
|
-
* the previous process and the result is unrecoverable
|
|
1537
|
-
*
|
|
1538
|
-
* The retention cap applies to the loaded set as well.
|
|
1539
|
-
*/
|
|
1540
|
-
static restore(options?: DelegationTaskQueueOptions): Promise<DelegationTaskQueue>;
|
|
1541
|
-
/**
|
|
1542
|
-
* Kick off a delegation in the background. Returns immediately. The
|
|
1543
|
-
* `taskId` is queryable via `status` once this method returns. Throws
|
|
1544
|
-
* the recorded `DelegationPersistenceError` once the store has failed —
|
|
1545
|
-
* the queue does not accept work it cannot journal.
|
|
1546
|
-
*/
|
|
1547
|
-
submit<Args extends AnyDelegateArgs>(input: SubmitInput<Args>): SubmitOutput;
|
|
1548
|
-
/**
|
|
1549
|
-
* Snapshot the current state of a delegation. Returns `undefined` for
|
|
1550
|
-
* unknown ids so callers can distinguish missing from terminal.
|
|
1551
|
-
* `includeTrace` attaches the journaled loop-trace span tree — off by
|
|
1552
|
-
* default so status polls stay light.
|
|
1553
|
-
*/
|
|
1554
|
-
status(taskId: string, opts?: {
|
|
1555
|
-
includeTrace?: boolean;
|
|
1556
|
-
}): DelegationStatusResult | undefined;
|
|
1557
|
-
/**
|
|
1558
|
-
* Abort an in-flight delegation. Returns `false` if the task is unknown
|
|
1559
|
-
* or already terminal. The underlying `run` function MUST honor the
|
|
1560
|
-
* abort signal for the cancel to take effect; the queue marks the
|
|
1561
|
-
* record `cancelled` regardless so a misbehaving runner cannot pin the
|
|
1562
|
-
* UI on `running` forever.
|
|
1563
|
-
*/
|
|
1564
|
-
cancel(taskId: string): boolean;
|
|
1565
|
-
/**
|
|
1566
|
-
* Append a feedback event to the matching delegation. Returns `false`
|
|
1567
|
-
* when `ref` does not name a known taskId — the caller should still
|
|
1568
|
-
* record the feedback through a different surface (artifact/outcome
|
|
1569
|
-
* kinds are not queue-bound).
|
|
1570
|
-
*/
|
|
1571
|
-
attachFeedback(taskId: string, snapshot: DelegationFeedbackSnapshot): boolean;
|
|
1572
|
-
/**
|
|
1573
|
-
* Query the recorded delegations. Returns entries newest-first (by
|
|
1574
|
-
* `startedAt`), truncated to `limit`.
|
|
1575
|
-
*/
|
|
1576
|
-
history(args?: DelegationHistoryArgs): DelegationHistoryEntry[];
|
|
1577
|
-
/**
|
|
1578
|
-
* Await every journal write issued so far. Rejects with the recorded
|
|
1579
|
-
* `DelegationPersistenceError` when any of them failed. Call before
|
|
1580
|
-
* handing the store's backing file to another process.
|
|
1581
|
-
*/
|
|
1582
|
-
flush(): Promise<void>;
|
|
1583
|
-
/** Test-only — number of in-flight (non-terminal) records. */
|
|
1584
|
-
inflightCount(): number;
|
|
1585
|
-
private execute;
|
|
1586
|
-
private appendTrace;
|
|
1587
|
-
private rehydrate;
|
|
1588
|
-
private startResume;
|
|
1589
|
-
private driveResume;
|
|
1590
|
-
/**
|
|
1591
|
-
* Journal the resumed segment of a detached run as one compact span. The
|
|
1592
|
-
* resume driver re-attaches after a process restart, so the original
|
|
1593
|
-
* process's loop events are gone — this span records the post-restart
|
|
1594
|
-
* observation window (re-attach → terminal tick) under the
|
|
1595
|
-
* `'detached-resume'` driver tag, keeping restored delegations observable
|
|
1596
|
-
* in the journal alongside trace-carrying live runs.
|
|
1597
|
-
*/
|
|
1598
|
-
private appendResumeSpan;
|
|
1599
|
-
private persist;
|
|
1600
|
-
private persistRemoval;
|
|
1601
|
-
private failPersistence;
|
|
1602
|
-
private enforceRetention;
|
|
1603
|
-
}
|
|
1604
|
-
/**
|
|
1605
|
-
* Best-effort stable hash for use as `idempotencyKey`. Not cryptographic;
|
|
1606
|
-
* collisions only affect dedupe, never correctness.
|
|
1607
|
-
*
|
|
1608
|
-
* @experimental
|
|
1609
|
-
*/
|
|
1610
|
-
declare function hashIdempotencyInput(value: unknown): string;
|
|
1611
|
-
|
|
1612
|
-
/**
|
|
1613
|
-
*
|
|
1614
|
-
* Detached delegation turns over the sandbox SDK's `driveTurn` primitive.
|
|
1615
|
-
*
|
|
1616
|
-
* Two halves of one story:
|
|
1617
|
-
*
|
|
1618
|
-
* - {@link runDetachedTurn} — the dispatch side. A single-session delegate
|
|
1619
|
-
* (single-variant coder / researcher) acquires a box, binds the sandbox id
|
|
1620
|
-
* into the record's `detachedSessionRef`, then advances the turn with
|
|
1621
|
-
* repeated `driveTurn` ticks instead of holding a live SSE stream. The
|
|
1622
|
-
* session id is deterministic and supplied at submit time, so a process
|
|
1623
|
-
* crash between ticks loses nothing — the turn keeps running in the box.
|
|
1624
|
-
*
|
|
1625
|
-
* - {@link createDetachedTurnResumeDriver} — the resume side. A
|
|
1626
|
-
* `DelegationResumeDriver` that re-attaches restored in-flight records to
|
|
1627
|
-
* their detached runs: parse the record's ref, resolve the box, advance the
|
|
1628
|
-
* turn one `driveTurn` pass per `tick()`, and map the SDK's three states
|
|
1629
|
-
* (`completed | running | failed`) onto `DelegationResumeTick`.
|
|
1630
|
-
*
|
|
1631
|
-
* Both sides type the box structurally ({@link DriveTurnCapableBox}) so tests
|
|
1632
|
-
* inject fakes and the module never requires the sandbox SDK at runtime — the
|
|
1633
|
-
* SDK stays an optional peer, exactly like the executors' `SandboxClient` seam.
|
|
1634
|
-
*
|
|
1635
|
-
* Tradeoffs of detached mode (why it is opt-in, not the default): a detached
|
|
1636
|
-
* turn yields one terminal payload instead of a live event stream, so kernel
|
|
1637
|
-
* token/cost aggregation is not produced for that turn. The trace sinks still
|
|
1638
|
-
* observe detached work — `runDetachedTurn` synthesizes a single-iteration
|
|
1639
|
-
* loop event stream (see `RunDetachedTurnOptions.traceEmitter`) so the span
|
|
1640
|
-
* topology joins the inherited trace context, with cost/tokens reported as 0
|
|
1641
|
-
* under the `'detached-turn'` driver tag. Multi-variant fanout stays on the
|
|
1642
|
-
* streaming `runAgentRounds` path — N concurrent sessions cannot be expressed as one
|
|
1643
|
-
* resume key, and winner selection needs every candidate.
|
|
1644
|
-
*
|
|
1645
|
-
* @experimental
|
|
1646
|
-
*/
|
|
1647
|
-
|
|
1648
|
-
/**
|
|
1649
|
-
* Structural mirror of the sandbox SDK's `TurnDriveResult` (>= 0.6).
|
|
1650
|
-
* Discriminated on `state`; `failed` is terminal and deterministic per the
|
|
1651
|
-
* SDK contract — re-invoking with the same ids returns the same outcome.
|
|
1652
|
-
*
|
|
1653
|
-
* @experimental
|
|
1654
|
-
*/
|
|
1655
|
-
type DriveTurnTick = {
|
|
1656
|
-
state: 'completed';
|
|
1657
|
-
text: string;
|
|
1658
|
-
result: Record<string, unknown>;
|
|
1659
|
-
} | {
|
|
1660
|
-
state: 'running';
|
|
1661
|
-
startedAt?: Date;
|
|
1662
|
-
elapsedMs?: number;
|
|
1663
|
-
} | {
|
|
1664
|
-
state: 'failed';
|
|
1665
|
-
error: string;
|
|
1666
|
-
};
|
|
1667
|
-
/**
|
|
1668
|
-
* The box surface detached turns need. `SandboxInstance`
|
|
1669
|
-
* (`@tangle-network/sandbox` >= 0.6) satisfies it structurally; tests pass
|
|
1670
|
-
* in-memory fakes. `_sessionCancel` is the SDK's remote-cancellation surface —
|
|
1671
|
-
* optional here because older SDKs / fakes may not expose it; when present it
|
|
1672
|
-
* is invoked on abort so the remote run actually stops.
|
|
1673
|
-
*
|
|
1674
|
-
* @experimental
|
|
1675
|
-
*/
|
|
1676
|
-
interface DriveTurnCapableBox {
|
|
1677
|
-
driveTurn(message: string, opts: {
|
|
1678
|
-
sessionId: string;
|
|
1679
|
-
turnId?: string;
|
|
1680
|
-
wallCapMs?: number;
|
|
1681
|
-
}): Promise<DriveTurnTick>;
|
|
1682
|
-
_sessionCancel?(id: string): Promise<void>;
|
|
1683
|
-
}
|
|
1684
|
-
/**
|
|
1685
|
-
* Decoded `DelegationRecord.detachedSessionRef`. `sandboxId` is absent between
|
|
1686
|
-
* submit and box acquisition — a record restored in that window is not
|
|
1687
|
-
* resumable (there is no box to resume on) and the resume driver fails it
|
|
1688
|
-
* loud rather than dispatching onto a guessed box.
|
|
1689
|
-
*
|
|
1690
|
-
* @experimental
|
|
1691
|
-
*/
|
|
1692
|
-
interface DetachedSessionRefParts {
|
|
1693
|
-
sessionId: string;
|
|
1694
|
-
sandboxId?: string;
|
|
1695
|
-
}
|
|
1696
|
-
/**
|
|
1697
|
-
* Encode ref parts into the JSON-safe string stored on the record:
|
|
1698
|
-
* `session=<id>` before the box exists, `sandbox=<id>;session=<id>` once
|
|
1699
|
-
* bound. Ids must not contain the `;`/`=` delimiters.
|
|
1700
|
-
*
|
|
1701
|
-
* @experimental
|
|
1702
|
-
*/
|
|
1703
|
-
declare function formatDetachedSessionRef(parts: DetachedSessionRefParts): string;
|
|
1704
|
-
/** Parse a `detachedSessionRef` string back to parts; throws `ValidationError` on malformed input. @experimental */
|
|
1705
|
-
declare function parseDetachedSessionRef(raw: string): DetachedSessionRefParts;
|
|
1706
|
-
/** @experimental The terminal payload of a finished detached turn. */
|
|
1707
|
-
interface DetachedTurn {
|
|
1708
|
-
/** Final assistant text. */
|
|
1709
|
-
text: string;
|
|
1710
|
-
/** The SDK's cached AgentExecutionResult-shape record for the turn. */
|
|
1711
|
-
result: Record<string, unknown>;
|
|
1712
|
-
}
|
|
1713
|
-
/**
|
|
1714
|
-
* Synthesize the terminal event array a detached turn settles through. Shaped
|
|
1715
|
-
* so the existing event-stream output adapters (coder, researcher) parse it:
|
|
1716
|
-
* `data.result` for adapters that read a structured terminal record, `data.text`
|
|
1717
|
-
* for adapters that scan assistant text for the fenced result block.
|
|
1718
|
-
*
|
|
1719
|
-
* @experimental
|
|
1720
|
-
*/
|
|
1721
|
-
declare function detachedTurnEvents(sessionId: string, turn: DetachedTurn): SandboxEvent[];
|
|
1722
|
-
/** @experimental */
|
|
1723
|
-
interface RunDetachedTurnOptions {
|
|
1724
|
-
/** Sandbox client used to acquire the box (the delegate's executor client). */
|
|
1725
|
-
client: SandboxClient;
|
|
1726
|
-
/** Profile + overrides for box acquisition — same spec the streaming path uses. */
|
|
1727
|
-
spec: AgentRunSpec<unknown>;
|
|
1728
|
-
/** The full turn prompt; consumed by `driveTurn`'s dispatch leg. */
|
|
1729
|
-
prompt: string;
|
|
1730
|
-
/** Deterministic resume key, minted at submit time (`parseDetachedSessionRef(ref).sessionId`). */
|
|
1731
|
-
sessionId: string;
|
|
1732
|
-
/**
|
|
1733
|
-
* Called once the box exists, with its sandbox id. Callers persist
|
|
1734
|
-
* `formatDetachedSessionRef({ sandboxId, sessionId })` onto the record here so
|
|
1735
|
-
* a restart can resolve the box again.
|
|
1736
|
-
*/
|
|
1737
|
-
bindSandbox(sandboxId: string): void;
|
|
1738
|
-
signal: AbortSignal;
|
|
1739
|
-
report(progress: DelegationProgress): void;
|
|
1740
|
-
/** Delay between `running` ticks (ms). Default 5000. */
|
|
1741
|
-
tickIntervalMs?: number;
|
|
1742
|
-
/** Wall-clock cap forwarded to `driveTurn` — the SDK cancels and fails a session past it. */
|
|
1743
|
-
wallCapMs?: number;
|
|
1744
|
-
/**
|
|
1745
|
-
* Loop-trace sink. When set, the detached turn synthesizes a
|
|
1746
|
-
* single-iteration loop span tree (`runId` = `sessionId`, driver
|
|
1747
|
-
* `'detached-turn'`) so trace-context inheritance survives the detached
|
|
1748
|
-
* path — the same events the streaming `runAgentRounds` path would emit, minus
|
|
1749
|
-
* per-token telemetry: `driveTurn` yields one terminal payload, so token
|
|
1750
|
-
* and cost figures are structurally unavailable and reported as 0 under
|
|
1751
|
-
* this driver tag.
|
|
1752
|
-
*/
|
|
1753
|
-
traceEmitter?: LoopTraceEmitter;
|
|
1754
|
-
/** Physical placement stamped on the synthesized dispatch event. Default `'sibling'`. */
|
|
1755
|
-
placement?: 'sibling' | 'fleet';
|
|
1756
|
-
}
|
|
1757
|
-
/**
|
|
1758
|
-
* Dispatch one detached turn and advance it to a terminal state with
|
|
1759
|
-
* `driveTurn` ticks. The first tick dispatches (idempotent on `sessionId`);
|
|
1760
|
-
* subsequent ticks poll. On abort the remote session is cancelled via
|
|
1761
|
-
* `_sessionCancel` when the box exposes it. The box is torn down on every
|
|
1762
|
-
* in-process exit path (success, failure, abort) — only a process death skips
|
|
1763
|
-
* teardown, which is exactly the case the resume driver re-attaches to.
|
|
1764
|
-
*
|
|
1765
|
-
* @experimental
|
|
1766
|
-
*/
|
|
1767
|
-
declare function runDetachedTurn(options: RunDetachedTurnOptions): Promise<DetachedTurn>;
|
|
1768
|
-
/** @experimental */
|
|
1769
|
-
interface DetachedTurnResumeDriverOptions {
|
|
1770
|
-
/**
|
|
1771
|
-
* Resolve the live box owning a detached session. The bin wires this to the
|
|
1772
|
-
* sandbox client's `get(sandboxId)`; throw when the box no longer exists —
|
|
1773
|
-
* a thrown tick settles the record as failed, which is the truth.
|
|
1774
|
-
*/
|
|
1775
|
-
resolveSandbox(sandboxId: string): Promise<DriveTurnCapableBox>;
|
|
1776
|
-
/**
|
|
1777
|
-
* Rebuild the turn prompt from the persisted record. Only consumed by
|
|
1778
|
-
* `driveTurn`'s dispatch leg — i.e. when the previous process died after
|
|
1779
|
-
* binding the box but before the session was dispatched. Must reproduce the
|
|
1780
|
-
* prompt the delegate would have sent.
|
|
1781
|
-
*/
|
|
1782
|
-
buildMessage(record: DelegationRecord): string;
|
|
1783
|
-
/**
|
|
1784
|
-
* Map a completed turn onto the delegation's typed output payload (parse +
|
|
1785
|
-
* validate per profile). Throw when the resumed result does not pass the
|
|
1786
|
-
* profile's gate — the queue settles the record as failed with that error.
|
|
1787
|
-
*/
|
|
1788
|
-
settleOutput(turn: DetachedTurn, record: DelegationRecord, ctx: {
|
|
1789
|
-
signal: AbortSignal;
|
|
1790
|
-
}): Promise<DelegationResultPayload['output']> | DelegationResultPayload['output'];
|
|
1791
|
-
/** Delay between `running` ticks (ms). Default 5000. */
|
|
1792
|
-
intervalMs?: number;
|
|
1793
|
-
/** Wall-clock cap forwarded to `driveTurn` on every tick. */
|
|
1794
|
-
wallCapMs?: number;
|
|
1795
|
-
}
|
|
1796
|
-
/**
|
|
1797
|
-
* Build the `driveTurn`-backed {@link DelegationResumeDriver}. Each `tick()`
|
|
1798
|
-
* is one settle/poll/dispatch pass:
|
|
1799
|
-
*
|
|
1800
|
-
* - ref without a sandbox binding → `failed` (`DetachedSessionUnboundError`):
|
|
1801
|
-
* the previous process died before a box existed; there is nothing to resume.
|
|
1802
|
-
* - `driveTurn` `completed` → `settleOutput` → `completed` tick.
|
|
1803
|
-
* - `running` → progress via `ctx.report`, `running` tick (queue re-ticks
|
|
1804
|
-
* after `intervalMs`).
|
|
1805
|
-
* - `failed` → `failed` tick (`DetachedTurnFailedError`) — terminal per the
|
|
1806
|
-
* SDK's deterministic-failure contract.
|
|
1807
|
-
*
|
|
1808
|
-
* Abort: the queue stops ticking once `cancel()` flips the record, so remote
|
|
1809
|
-
* cancellation is hooked onto `ctx.signal` (once per task) and fires
|
|
1810
|
-
* `_sessionCancel` when the SDK surface exposes it. The driver never deletes
|
|
1811
|
-
* boxes — it cannot know whether `sandboxId` is a disposable sibling or a
|
|
1812
|
-
* fleet machine, and destroying a fleet machine would be unrecoverable.
|
|
1813
|
-
*
|
|
1814
|
-
* @experimental
|
|
1815
|
-
*/
|
|
1816
|
-
declare function createDetachedTurnResumeDriver(options: DetachedTurnResumeDriverOptions): DelegationResumeDriver;
|
|
1817
|
-
|
|
1818
|
-
/**
|
|
1819
|
-
*
|
|
1820
|
-
* Delegation executors — the layer between MCP delegates and the sandbox
|
|
1821
|
-
* substrate. Each executor exposes a {@link SandboxClient} the kernel
|
|
1822
|
-
* consumes plus a placement tag so the trace pipeline can correlate workers
|
|
1823
|
-
* with their physical placement.
|
|
1824
|
-
*
|
|
1825
|
-
* Two implementations ship in-box:
|
|
1826
|
-
*
|
|
1827
|
-
* - {@link createSiblingSandboxExecutor} — every delegation spawns a fresh
|
|
1828
|
-
* sandbox sibling to the caller. Default when the MCP server runs as a
|
|
1829
|
-
* standalone CLI mounted outside a fleet.
|
|
1830
|
-
*
|
|
1831
|
-
* - {@link createFleetWorkspaceExecutor} — delegations dispatch onto machines
|
|
1832
|
-
* in the caller's existing fleet so worker diffs land directly on the
|
|
1833
|
-
* caller's filesystem (the fleet's shared workspace). Selected when the
|
|
1834
|
-
* parent sandbox passes `TANGLE_FLEET_ID` into the MCP server's env.
|
|
1835
|
-
*
|
|
1836
|
-
* @experimental
|
|
1837
|
-
*/
|
|
1838
|
-
|
|
1839
|
-
/** @experimental */
|
|
1840
|
-
interface DelegationExecutor {
|
|
1841
|
-
/** Sandbox client the kernel calls. Returned with `describePlacement` set. */
|
|
1842
|
-
readonly client: SandboxClient;
|
|
1843
|
-
/** Best-effort one-liner used in stderr boot logs and diagnostics. */
|
|
1844
|
-
describe(): string;
|
|
1845
|
-
/**
|
|
1846
|
-
* Where delegated work physically runs. `sibling` and `fleet` placements are
|
|
1847
|
-
* session-backed (boxes expose `driveTurn`, so detached dispatch + resume
|
|
1848
|
-
* apply); `in-process` spawns local harness CLIs with no sandbox session to
|
|
1849
|
-
* detach. Optional so consumer-implemented executors stay source-compatible;
|
|
1850
|
-
* absent means "unknown" and detached dispatch is not enabled for it.
|
|
1851
|
-
*/
|
|
1852
|
-
readonly placement?: 'sibling' | 'fleet' | 'in-process';
|
|
1853
|
-
}
|
|
1854
|
-
/** @experimental */
|
|
1855
|
-
interface SiblingSandboxExecutorOptions {
|
|
1856
|
-
client: SandboxClient;
|
|
1857
|
-
}
|
|
1858
|
-
/**
|
|
1859
|
-
* Wrap a raw sandbox SDK client so the kernel emits
|
|
1860
|
-
* `loop.iteration.dispatch` events with `{ placement: 'sibling', sandboxId }`.
|
|
1861
|
-
*
|
|
1862
|
-
* The returned client `.create()` delegates to the underlying client; the
|
|
1863
|
-
* only added behavior is a `describePlacement` tag the kernel reads.
|
|
1864
|
-
*
|
|
1865
|
-
* @experimental
|
|
1866
|
-
*/
|
|
1867
|
-
declare function createSiblingSandboxExecutor(options: SiblingSandboxExecutorOptions): DelegationExecutor;
|
|
1868
|
-
/**
|
|
1869
|
-
* Minimal `SandboxFleet` surface the fleet executor calls. Declared
|
|
1870
|
-
* structurally so tests can pass an in-memory stub without instantiating the
|
|
1871
|
-
* sandbox SDK.
|
|
1872
|
-
*
|
|
1873
|
-
* @experimental
|
|
1874
|
-
*/
|
|
1875
|
-
interface FleetHandle {
|
|
1876
|
-
readonly fleetId: string;
|
|
1877
|
-
/** Machine ids in dispatch-eligible order. The executor round-robins. */
|
|
1878
|
-
readonly ids: ReadonlyArray<string>;
|
|
1879
|
-
/** Resolve a machine id to its `SandboxInstance` — that machine is mounted
|
|
1880
|
-
* on the fleet's shared workspace, so any diff the worker writes lands on
|
|
1881
|
-
* every other fleet machine's filesystem too. */
|
|
1882
|
-
sandbox(machineId: string): Promise<SandboxInstance>;
|
|
1883
|
-
}
|
|
1884
|
-
/** @experimental */
|
|
1885
|
-
interface FleetWorkspaceExecutorOptions {
|
|
1886
|
-
fleet: FleetHandle;
|
|
1887
|
-
/**
|
|
1888
|
-
* Override the machine-selection policy. Default = round-robin across
|
|
1889
|
-
* `fleet.ids`, skipping the optional `excludeMachineIds` set (typically the
|
|
1890
|
-
* coordinator machine the MCP server is running on).
|
|
1891
|
-
*/
|
|
1892
|
-
selectMachine?: (call: {
|
|
1893
|
-
callIndex: number;
|
|
1894
|
-
ids: ReadonlyArray<string>;
|
|
1895
|
-
}) => string;
|
|
1896
|
-
/**
|
|
1897
|
-
* Machine ids to skip during default round-robin. Set to the caller's own
|
|
1898
|
-
* machineId so workers don't compete with the orchestrator on the same VM.
|
|
1899
|
-
*/
|
|
1900
|
-
excludeMachineIds?: ReadonlyArray<string>;
|
|
1901
|
-
}
|
|
1902
|
-
/**
|
|
1903
|
-
* Build an executor that resolves each delegated iteration to an existing
|
|
1904
|
-
* machine in `fleet`. The fleet's shared-workspace policy means the worker
|
|
1905
|
-
* machine sees the caller's filesystem — diffs land in-place with no
|
|
1906
|
-
* cross-sandbox copy step.
|
|
1907
|
-
*
|
|
1908
|
-
* @experimental
|
|
1909
|
-
*/
|
|
1910
|
-
declare function createFleetWorkspaceExecutor(options: FleetWorkspaceExecutorOptions): DelegationExecutor;
|
|
1911
|
-
|
|
1912
|
-
/**
|
|
1913
|
-
*
|
|
1914
|
-
* `detachedSessionDelegate` — the sandbox-session coder delegate: a closure that drives `runAgentRounds`
|
|
1915
|
-
* against a `SandboxClient` + a caller-supplied (or minimal model-only default) worker profile, to a
|
|
1916
|
-
* mechanically-validated `CoderOutput`. The caller invokes the returned delegate directly with its
|
|
1917
|
-
* coder args; when wired into a durable queue it also settles cross-restart-resumed records.
|
|
1918
|
-
*
|
|
1919
|
-
* Delegation vs COORDINATION (`../runtime/supervise/coordination-mcp.ts`): this delegate runs a
|
|
1920
|
-
* coding task INSIDE the agent's OWN sandbox environment — a sibling box on its own `SandboxClient`,
|
|
1921
|
-
* fresh branch on its repo. It is NOT backend-pluggable. To instead SPAWN + live-drive workers in a
|
|
1922
|
-
* CHOSEN backend (sandbox OR cli-bridge, via `createExecutor({ backend })`) with observe/steer/resume
|
|
1923
|
-
* + recursion, use `delegate()` / the coordination MCP.
|
|
1924
|
-
*
|
|
1925
|
-
* The worker profile is a parameter the caller supplies (§1.5: the system authors profiles). When
|
|
1926
|
-
* none is passed, a minimal model-only default profile is materialized in `./detached-coder` — no
|
|
1927
|
-
* hardcoded skills or tools. For NEW local-repo coding use `worktreeFanout` / `worktreeLoopRunner`
|
|
1928
|
-
* (author one `AgentProfile` per harness → `createWorktreeCliExecutor` leaves → `gateOnDeliverable`).
|
|
1929
|
-
*
|
|
1930
|
-
* @experimental
|
|
1931
|
-
*/
|
|
1932
|
-
|
|
1933
|
-
/** @experimental */
|
|
1934
|
-
interface DelegateRunCtx {
|
|
1935
|
-
signal: AbortSignal;
|
|
1936
|
-
report(progress: DelegationProgress): void;
|
|
1937
|
-
/**
|
|
1938
|
-
* Detached-run resume key recorded on the queue record at submit time
|
|
1939
|
-
* (`formatDetachedSessionRef`). Present only when the submit path requested
|
|
1940
|
-
* detached dispatch — its presence is what routes a session-backed delegate
|
|
1941
|
-
* onto the `driveTurn` tick path instead of holding a stream.
|
|
1942
|
-
*/
|
|
1943
|
-
detachedSessionRef?: string;
|
|
1944
|
-
/** Rebind the record's resume key (e.g. once the sandbox id is known). */
|
|
1945
|
-
updateDetachedSessionRef?(ref: string): void;
|
|
1946
|
-
/**
|
|
1947
|
-
* Per-delegation trace sink supplied by the queue — loop events emitted
|
|
1948
|
-
* here land on the delegation record as a compact span tree. Delegates
|
|
1949
|
-
* compose it with their configured OTEL emitter so both sinks observe
|
|
1950
|
-
* the same stream.
|
|
1951
|
-
*/
|
|
1952
|
-
traceEmitter?: LoopTraceEmitter;
|
|
1953
|
-
}
|
|
1954
|
-
/** @experimental The coder delegate closure — given the coder args + run context, drives the
|
|
1955
|
-
* sandbox-session coder path to a validated `CoderOutput`. `detachedSessionDelegate` is the
|
|
1956
|
-
* built-in implementation; the queue invokes one of these per coder delegation. */
|
|
1957
|
-
type CoderDelegate = (args: DelegateCodeArgs, ctx: DelegateRunCtx) => Promise<CoderOutput>;
|
|
1958
|
-
/**
|
|
1959
|
-
* UI-auditor delegate — fully consumer-injected. agent-runtime ships no
|
|
1960
|
-
* default factory because the inputs are workspace path + judge function
|
|
1961
|
-
* + (optionally) a `SandboxClient`, and the judge is the consumer's
|
|
1962
|
-
* model seam. See `createInProcessUiAuditClient` + `uiAuditorProfile` in
|
|
1963
|
-
* `@tangle-network/agent-runtime/profiles` for the canonical wiring.
|
|
1964
|
-
*
|
|
1965
|
-
* @experimental
|
|
1966
|
-
*/
|
|
1967
|
-
type UiAuditorDelegate = (args: DelegateUiAuditArgs, ctx: DelegateRunCtx) => Promise<UiAuditorDelegationOutput>;
|
|
1968
|
-
/** @experimental Structured review verdict over a coder candidate. */
|
|
1969
|
-
interface CoderReview {
|
|
1970
|
-
/** Gate: only approved candidates are eligible to win. */
|
|
1971
|
-
approved: boolean;
|
|
1972
|
-
/** Reviewer's recommendation — surfaced in traces. */
|
|
1973
|
-
recommendation: 'ship' | 'approve-with-nits' | 'changes-requested' | 'reject';
|
|
1974
|
-
/** Readiness 0..1, used by the `highest-readiness` winner-selection strategy. */
|
|
1975
|
-
readiness: number;
|
|
1976
|
-
notes?: string;
|
|
1977
|
-
}
|
|
1978
|
-
/**
|
|
1979
|
-
*
|
|
1980
|
-
* Optional adversarial reviewer over a coder candidate that already passed
|
|
1981
|
-
* mechanical validation (tests/typecheck/forbidden/diff/no-op/secrets). Folded
|
|
1982
|
-
* from the ai-trading-blueprint delegation MCP: a candidate is only eligible to
|
|
1983
|
-
* win if the reviewer approves it. The reviewer is the consumer's seam — an LLM
|
|
1984
|
-
* judge, a `pnpm review` command, anything returning a `CoderReview`.
|
|
1985
|
-
*
|
|
1986
|
-
* @experimental
|
|
1987
|
-
*/
|
|
1988
|
-
type CoderReviewer = (output: CoderOutput, task: CoderTask, ctx: {
|
|
1989
|
-
signal: AbortSignal;
|
|
1990
|
-
}) => Promise<CoderReview> | CoderReview;
|
|
1991
|
-
/**
|
|
1992
|
-
* @experimental Winner-selection strategy among validated (+ reviewed) candidates on the
|
|
1993
|
-
* sandbox-session path. The base strategies (`highest-score` / `smallest-diff` /
|
|
1994
|
-
* `first-approved`) delegate to the shared `selectValidWinner`; `highest-readiness` is the
|
|
1995
|
-
* reviewer-only strategy this path keeps that the generic selector does not express. Default
|
|
1996
|
-
* `highest-score`.
|
|
1997
|
-
*/
|
|
1998
|
-
type DetachedWinnerSelection = 'highest-score' | 'smallest-diff' | 'highest-readiness' | 'first-approved';
|
|
1999
|
-
/** @experimental */
|
|
2000
|
-
interface DetachedSessionDelegateOptions {
|
|
2001
|
-
/**
|
|
2002
|
-
* Execution placement. Pass a {@link DelegationExecutor} (sibling or fleet)
|
|
2003
|
-
* to control where worker iterations land. `sandboxClient` is a
|
|
2004
|
-
* convenience shorthand that wraps the client in a sibling executor — pass
|
|
2005
|
-
* one or the other, not both.
|
|
2006
|
-
*/
|
|
2007
|
-
executor?: DelegationExecutor;
|
|
2008
|
-
/**
|
|
2009
|
-
* Convenience shorthand for sibling placement. Equivalent to
|
|
2010
|
-
* `executor: createSiblingSandboxExecutor({ client: sandboxClient })`.
|
|
2011
|
-
*/
|
|
2012
|
-
sandboxClient?: SandboxClient;
|
|
2013
|
-
/**
|
|
2014
|
-
* The worker's authored `AgentProfile` (§1.5: the system authors profiles). Spread onto the
|
|
2015
|
-
* sandbox-session run spec → `runAgentRounds` → the executor's `harnessInvocation`, so the harness runs
|
|
2016
|
-
* under the caller's stance. Omit to use a minimal model-only default (no hardcoded skills/tools);
|
|
2017
|
-
* `harness` / `model` / `systemPrompt` below are convenience overrides layered onto whichever
|
|
2018
|
-
* profile is used.
|
|
2019
|
-
*/
|
|
2020
|
-
workerProfile?: AgentProfile$1;
|
|
2021
|
-
/** Backend harness for the single-coder path (sets `metadata.backendType`). Default `claude-code`. */
|
|
2022
|
-
harness?: string;
|
|
2023
|
-
/** Model override for the single-coder path. */
|
|
2024
|
-
model?: string;
|
|
2025
|
-
/**
|
|
2026
|
-
* The worker's authored system prompt (§1.5). Flows onto the run spec's
|
|
2027
|
-
* `profile.prompt.systemPrompt` → through `runAgentRounds` → the executor's `harnessInvocation`, so the
|
|
2028
|
-
* harness runs under this stance. Omit to keep the profile's own prompt.
|
|
2029
|
-
*/
|
|
2030
|
-
systemPrompt?: string;
|
|
2031
|
-
/** Default `['claude-code', 'codex', 'opencode/zai-coding-plan/glm-5.1']` when variants > 1. */
|
|
2032
|
-
fanoutHarnesses?: string[];
|
|
2033
|
-
/** Optional per-harness model override for `variants > 1`. */
|
|
2034
|
-
fanoutModels?: (string | undefined)[];
|
|
2035
|
-
/** Hard cap on the kernel's per-batch concurrency. Default 4. */
|
|
2036
|
-
maxConcurrency?: number;
|
|
2037
|
-
/**
|
|
2038
|
-
* Optional adversarial reviewer. When set, a candidate must pass mechanical
|
|
2039
|
-
* validation AND `reviewer.approved` to be eligible to win — empty/secret/
|
|
2040
|
-
* test-failing patches are already gone; this catches the "compiles + passes
|
|
2041
|
-
* but wrong/unsafe" class the deterministic validator can't see.
|
|
2042
|
-
*/
|
|
2043
|
-
reviewer?: CoderReviewer;
|
|
2044
|
-
/** Winner-selection strategy among eligible candidates. Default `highest-score`. */
|
|
2045
|
-
winnerSelection?: DetachedWinnerSelection;
|
|
2046
|
-
/**
|
|
2047
|
-
* Loop trace emitter forwarded into every delegated `runAgentRounds`. Wire
|
|
2048
|
-
* `createPropagatingTraceEmitter(readTraceContextFromEnv())` here (the bin
|
|
2049
|
-
* does) so delegated build-loops export their topology spans to the OTLP /
|
|
2050
|
-
* Tangle Intelligence sink when `OTEL_EXPORTER_OTLP_ENDPOINT` is set — and
|
|
2051
|
-
* are a cheap no-op when it isn't. Configurable by construction.
|
|
2052
|
-
*
|
|
2053
|
-
* Detached single-variant turns (taken when `ctx.detachedSessionRef` is set)
|
|
2054
|
-
* bypass `runAgentRounds`; `runDetachedTurn` synthesizes a single-iteration loop
|
|
2055
|
-
* event stream for them so this emitter observes detached work too.
|
|
2056
|
-
*/
|
|
2057
|
-
traceEmitter?: LoopTraceEmitter;
|
|
2058
|
-
/** Tick cadence (ms) for the detached single-variant path. Default 5000. */
|
|
2059
|
-
detachedTickIntervalMs?: number;
|
|
2060
|
-
/** Wall-clock cap (ms) forwarded to `driveTurn` for detached turns. */
|
|
2061
|
-
detachedWallCapMs?: number;
|
|
2062
|
-
}
|
|
2063
|
-
/**
|
|
2064
|
-
* Build the sandbox-session coder delegate. It drives `runAgentRounds` against the project's
|
|
2065
|
-
* sandbox client + coder profile; when `args.variants > 1` it switches to the multi-harness fanout
|
|
2066
|
-
* topology.
|
|
2067
|
-
*
|
|
2068
|
-
* This is the SANDBOX-SESSION coder path: workers run the in-box harness via the
|
|
2069
|
-
* `SandboxClient`'s `streamPrompt`, and single-variant turns can dispatch DETACHED
|
|
2070
|
-
* (driveTurn ticks) so a durable queue resumes them across an MCP restart — a substrate
|
|
2071
|
-
* the recursive worktree-CLI leaf does not yet have a journal-replay equivalent for.
|
|
2072
|
-
*
|
|
2073
|
-
* For NEW local-repo coding use `worktreeFanout` / `worktreeLoopRunner` (author an `AgentProfile`
|
|
2074
|
-
* per harness → `createWorktreeCliExecutor` leaves → `gateOnDeliverable`). This delegate runs
|
|
2075
|
-
* held-stream by default and only its OPTIONAL cross-restart resume (the `driveTurn` tick) is opt-in
|
|
2076
|
-
* behind `MCP_ENABLE_DETACHED_RESUME`.
|
|
2077
|
-
*
|
|
2078
|
-
* @experimental
|
|
2079
|
-
*/
|
|
2080
|
-
declare function detachedSessionDelegate(options: DetachedSessionDelegateOptions): CoderDelegate;
|
|
2081
|
-
/**
|
|
2082
|
-
* Canonical `DelegateCodeArgs` → `CoderTask` mapping — the single source for
|
|
2083
|
-
* the delegate's live dispatch AND the resume driver's settle/message
|
|
2084
|
-
* rebuilding, so a resumed record reproduces exactly the task the original
|
|
2085
|
-
* process dispatched.
|
|
2086
|
-
*
|
|
2087
|
-
* @experimental
|
|
2088
|
-
*/
|
|
2089
|
-
declare function coderTaskFromArgs(args: DelegateCodeArgs): CoderTask;
|
|
2090
|
-
/** @experimental */
|
|
2091
|
-
interface SettleDetachedCoderTurnOptions {
|
|
2092
|
-
task: CoderTask;
|
|
2093
|
-
/** Session id of the detached turn — used as the synthesized event id. */
|
|
2094
|
-
sessionId: string;
|
|
2095
|
-
signal: AbortSignal;
|
|
2096
|
-
harness?: string;
|
|
2097
|
-
model?: string;
|
|
2098
|
-
/** Same gate as the streaming path: an unapproved candidate cannot win. */
|
|
2099
|
-
reviewer?: CoderReviewer;
|
|
2100
|
-
}
|
|
2101
|
-
/**
|
|
2102
|
-
* Settle a completed detached coder turn through the same gate the streaming
|
|
2103
|
-
* path applies: parse the terminal payload with the coder output adapter,
|
|
2104
|
-
* run the mechanical validator (tests/typecheck/forbidden/diff/no-op/secrets),
|
|
2105
|
-
* then the optional reviewer. Throws when nothing survives — a resumed or
|
|
2106
|
-
* detached run must not return an unvalidated patch.
|
|
2107
|
-
*
|
|
2108
|
-
* SCOPE NOTE (detached/resume): the detached `driveTurn`-tick + cross-restart resume path is
|
|
2109
|
-
* bound to the `runAgentRounds` + sandbox-session substrate. The recursive `Scope`/worktree-CLI leaf has
|
|
2110
|
-
* journal→replay but no driveTurn-over-a-detached-sandbox-session equivalent yet, so resume is NOT
|
|
2111
|
-
* advertised on the generic `worktreeFanout` path. This helper (with `coderTaskFromArgs` and
|
|
2112
|
-
* `createDetachedTurnResumeDriver`) stays as the resume seam `bin.ts` wires for in-flight records.
|
|
2113
|
-
*
|
|
2114
|
-
* @experimental
|
|
2115
|
-
*/
|
|
2116
|
-
declare function settleDetachedCoderTurn(turn: DetachedTurn, options: SettleDetachedCoderTurnOptions): Promise<CoderOutput>;
|
|
2117
|
-
|
|
2118
|
-
/**
|
|
2119
|
-
*
|
|
2120
|
-
* Feedback persistence surface for the MCP layer.
|
|
2121
|
-
*
|
|
2122
|
-
* Feedback storage is product policy, so the MCP layer depends on this narrow
|
|
2123
|
-
* interface instead of choosing a knowledge store. The default implementation
|
|
2124
|
-
* is in-memory; consumers wire their own durable adapter via
|
|
2125
|
-
* `createMcpServer({ feedbackStore })`.
|
|
2126
|
-
*
|
|
2127
|
-
* Feedback events are append-only: every rating is a new event with a
|
|
2128
|
-
* fresh id, even when the same delegation is rated multiple times. The
|
|
2129
|
-
* caller decides how to roll up scores downstream.
|
|
2130
|
-
*
|
|
2131
|
-
* @experimental
|
|
2132
|
-
*/
|
|
2133
|
-
|
|
2134
|
-
/** @experimental */
|
|
2135
|
-
interface FeedbackEvent {
|
|
2136
|
-
id: string;
|
|
2137
|
-
refersTo: DelegateFeedbackArgs['refersTo'];
|
|
2138
|
-
rating: DelegateFeedbackArgs['rating'];
|
|
2139
|
-
by: DelegateFeedbackArgs['by'];
|
|
2140
|
-
capturedAt: string;
|
|
2141
|
-
namespace?: string;
|
|
2142
|
-
}
|
|
2143
|
-
/** @experimental */
|
|
2144
|
-
interface FeedbackStore {
|
|
2145
|
-
/** Append a new event. Never dedupes — every rating is its own event. */
|
|
2146
|
-
put(event: FeedbackEvent): Promise<void>;
|
|
2147
|
-
/**
|
|
2148
|
-
* List events filtered by `namespace`. When `namespace` is omitted, list
|
|
2149
|
-
* across all namespaces. Returns events in insertion order.
|
|
2150
|
-
*/
|
|
2151
|
-
list(filter?: {
|
|
2152
|
-
namespace?: string;
|
|
2153
|
-
refersToRef?: string;
|
|
2154
|
-
}): Promise<FeedbackEvent[]>;
|
|
2155
|
-
}
|
|
2156
|
-
/** In-memory `FeedbackStore` — suitable for single-process use and tests. @experimental */
|
|
2157
|
-
declare class InMemoryFeedbackStore implements FeedbackStore {
|
|
2158
|
-
private readonly events;
|
|
2159
|
-
put(event: FeedbackEvent): Promise<void>;
|
|
2160
|
-
list(filter?: {
|
|
2161
|
-
namespace?: string;
|
|
2162
|
-
refersToRef?: string;
|
|
2163
|
-
}): Promise<FeedbackEvent[]>;
|
|
2164
|
-
}
|
|
2165
|
-
/**
|
|
2166
|
-
* Project a `FeedbackEvent` down to the snapshot shape carried on
|
|
2167
|
-
* `delegation_history` entries.
|
|
2168
|
-
*
|
|
2169
|
-
* @experimental
|
|
2170
|
-
*/
|
|
2171
|
-
declare function eventToSnapshot(event: FeedbackEvent): DelegationFeedbackSnapshot;
|
|
2172
|
-
|
|
2173
|
-
/**
|
|
2174
|
-
*
|
|
2175
|
-
* `delegate` MCP tool — the ONE generic delegation verb, the agent-facing front door to
|
|
2176
|
-
* `delegate()` / `supervise()`. The agent hands it an INTENT (what it wants done); a default
|
|
2177
|
-
* authoring supervisor decomposes the intent and AUTHORS the worker profile it needs — there is no
|
|
2178
|
-
* hardcoded coder/researcher profile, so one verb covers code, research, and anything else.
|
|
2179
|
-
*
|
|
2180
|
-
* `delegate` is SYNCHRONOUS: it awaits the full supervised run and returns the delivered output
|
|
2181
|
-
* TOGETHER WITH `spentTotal` — the conserved cost of the whole delegation (`iterations` / `tokens` /
|
|
2182
|
-
* `usd` / `ms`), so the caller always learns what the delegation actually spent.
|
|
2183
|
-
*
|
|
2184
|
-
* The supervisor's substrate (its brain `router`, the worker `backend`, the completion `deliverable`)
|
|
2185
|
-
* is INJECTED at server construction — never an agent-supplied arg. The agent supplies only the
|
|
2186
|
-
* intent (+ an optional per-call `model` / `runId`).
|
|
2187
|
-
*
|
|
2188
|
-
* @experimental
|
|
2189
|
-
*/
|
|
2190
|
-
|
|
2191
|
-
/** MCP tool name for the `delegate` generic-delegation tool. @experimental */
|
|
2192
|
-
declare const DELEGATE_TOOL_NAME = "delegate";
|
|
2193
|
-
/** Human-readable description of the `delegate` MCP tool, injected into the tool manifest. @experimental */
|
|
2194
|
-
declare const DELEGATE_DESCRIPTION: string;
|
|
2195
|
-
/** JSON Schema for `delegate` tool arguments (`intent` + optional `model` and `runId`). @experimental */
|
|
2196
|
-
declare const DELEGATE_INPUT_SCHEMA: {
|
|
2197
|
-
readonly type: "object";
|
|
2198
|
-
readonly properties: {
|
|
2199
|
-
readonly intent: {
|
|
2200
|
-
readonly type: "string";
|
|
2201
|
-
readonly description: "What you want accomplished, as an outcome. The supervisor authors the worker.";
|
|
2202
|
-
};
|
|
2203
|
-
readonly model: {
|
|
2204
|
-
readonly type: "string";
|
|
2205
|
-
readonly description: "Optional per-call override for the supervisor brain model.";
|
|
2206
|
-
};
|
|
2207
|
-
readonly runId: {
|
|
2208
|
-
readonly type: "string";
|
|
2209
|
-
readonly description: "Optional trace-correlation id for this delegation.";
|
|
2210
|
-
};
|
|
2211
|
-
};
|
|
2212
|
-
readonly required: readonly ["intent"];
|
|
2213
|
-
readonly additionalProperties: false;
|
|
2214
|
-
};
|
|
2215
|
-
/** Parsed `delegate` tool arguments. */
|
|
2216
|
-
interface DelegateArgs {
|
|
2217
|
-
intent: string;
|
|
2218
|
-
model?: string;
|
|
2219
|
-
runId?: string;
|
|
2220
|
-
}
|
|
2221
|
-
/** Parse and validate raw MCP tool input into typed `DelegateArgs`; throws `TypeError` on bad input. @experimental */
|
|
2222
|
-
declare function validateDelegateArgs(raw: unknown): DelegateArgs;
|
|
2223
|
-
/** The synchronous result the `delegate` tool returns to the calling agent: the delivered output (or
|
|
2224
|
-
* the no-winner reason) PLUS the conserved spend of the whole delegation. */
|
|
2225
|
-
type DelegateResult = {
|
|
2226
|
-
status: 'winner';
|
|
2227
|
-
out: unknown;
|
|
2228
|
-
outRef: string;
|
|
2229
|
-
spentTotal: Spend;
|
|
2230
|
-
} | {
|
|
2231
|
-
status: 'no-winner';
|
|
2232
|
-
reason: string;
|
|
2233
|
-
spentTotal: Spend;
|
|
2234
|
-
};
|
|
2235
|
-
/** @experimental */
|
|
2236
|
-
interface DelegateHandlerOptions {
|
|
2237
|
-
/** The supervisor brain's router substrate (REQUIRED — the default supervisor is router-brained). */
|
|
2238
|
-
router: RouterConfig;
|
|
2239
|
-
/** WHERE the authored workers run. Required for `supervise()` to spawn anything. */
|
|
2240
|
-
backend: ExecutorConfig;
|
|
2241
|
-
/** The completion oracle the authored workers settle against (settled ⟺ delivered). */
|
|
2242
|
-
deliverable?: DeliverableSpec;
|
|
2243
|
-
/** Default supervisor brain model when a call omits `model`. */
|
|
2244
|
-
model?: string;
|
|
2245
|
-
/** Restrict the run to this subset of models. */
|
|
2246
|
-
allowedModels?: readonly string[];
|
|
2247
|
-
}
|
|
2248
|
-
/**
|
|
2249
|
-
* Build the `delegate` tool handler. Closes over the injected supervisor substrate (`router` /
|
|
2250
|
-
* `backend` / `deliverable`); each call routes the agent's intent to `delegate()` and returns the
|
|
2251
|
-
* delivered output with its conserved cost.
|
|
2252
|
-
*/
|
|
2253
|
-
declare function createDelegateHandler(options: DelegateHandlerOptions): (raw: unknown) => Promise<DelegateResult>;
|
|
2254
|
-
|
|
2255
|
-
/**
|
|
2256
|
-
*
|
|
2257
|
-
* Stdio JSON-RPC MCP server exposing the delegation tools to sandbox
|
|
2258
|
-
* coding-harness agents (claude-code, codex, opencode, ...): the generic
|
|
2259
|
-
* `delegate` verb plus the queue-bound `delegate_feedback`,
|
|
2260
|
-
* `delegation_status`, and `delegation_history`. `delegate_ui_audit` is served
|
|
2261
|
-
* when a `uiAuditorDelegate` is wired.
|
|
2262
|
-
*
|
|
2263
|
-
* The server is transport-bound but topology-free: tool execution is
|
|
2264
|
-
* delegated to handler functions composed from a queue, a feedback
|
|
2265
|
-
* store, and the wired run delegates. Consumers wire those at
|
|
2266
|
-
* construction time. The `agent-runtime-mcp` bin serves the generic
|
|
2267
|
-
* `delegate` verb over a real sandbox client when `MCP_ENABLE_DELEGATE=1`.
|
|
2268
|
-
*
|
|
2269
|
-
* Wire protocol: line-delimited JSON-RPC 2.0 over stdio. Each line is
|
|
2270
|
-
* one request; each response is one line. `tools/list` and `tools/call`
|
|
2271
|
-
* mirror the MCP 2024-11-05 spec; we do not pull in
|
|
2272
|
-
* `@modelcontextprotocol/sdk` to keep the dependency footprint zero.
|
|
2273
|
-
*
|
|
2274
|
-
* @experimental
|
|
2275
|
-
*/
|
|
2276
|
-
|
|
2277
|
-
/** @experimental */
|
|
2278
|
-
interface McpServerOptions {
|
|
2279
|
-
/**
|
|
2280
|
-
* Required to enable `delegate` — the ONE generic delegation verb. Inject the supervisor
|
|
2281
|
-
* substrate: its brain `router`, the worker `backend`, and the completion `deliverable`. The
|
|
2282
|
-
* supervisor AUTHORS its own worker from the agent's intent, so there is no worker profile to
|
|
2283
|
-
* wire here.
|
|
2284
|
-
*/
|
|
2285
|
-
delegateSupervisor?: DelegateHandlerOptions;
|
|
2286
|
-
/**
|
|
2287
|
-
* Required to enable delegate_ui_audit. Wire one that closes over your
|
|
2288
|
-
* `runAgentRounds` + `uiAuditorProfile` + a `SandboxClient` (the
|
|
2289
|
-
* canonical in-process choice is `createInProcessUiAuditClient` from
|
|
2290
|
-
* `@tangle-network/agent-runtime/profiles`) + your vision judge.
|
|
2291
|
-
*/
|
|
2292
|
-
uiAuditorDelegate?: UiAuditorDelegate;
|
|
2293
|
-
/** Override the default in-memory feedback store. */
|
|
2294
|
-
feedbackStore?: FeedbackStore;
|
|
2295
|
-
/** Override the default in-memory task queue. */
|
|
2296
|
-
queue?: DelegationTaskQueue;
|
|
2297
|
-
/**
|
|
2298
|
-
* Extra tools to serve alongside the delegation tools, for example
|
|
2299
|
-
* `createCoordinationTools(...).tools`. Registered after the built-ins; a
|
|
2300
|
-
* duplicate name throws so delegation tools cannot be shadowed silently.
|
|
2301
|
-
*/
|
|
2302
|
-
extraTools?: McpToolDescriptor[];
|
|
2303
|
-
/**
|
|
2304
|
-
* Inherited trace identity (`readTraceContextFromEnv()`) stamped on every
|
|
2305
|
-
* record the DEFAULT queue creates. Ignored when `queue` is supplied —
|
|
2306
|
-
* pass `traceContext` to that queue's constructor instead.
|
|
2307
|
-
*/
|
|
2308
|
-
traceContext?: TraceContext;
|
|
2309
|
-
/** Server display name surfaced via `initialize`. Default `'agent-runtime-mcp'`. */
|
|
2310
|
-
serverName?: string;
|
|
2311
|
-
/** Server version surfaced via `initialize`. Default = the package version baked at build time. */
|
|
2312
|
-
serverVersion?: string;
|
|
2313
|
-
}
|
|
2314
|
-
/** @experimental */
|
|
2315
|
-
interface McpToolDescriptor {
|
|
2316
|
-
name: string;
|
|
2317
|
-
description: string;
|
|
2318
|
-
inputSchema: Record<string, unknown>;
|
|
2319
|
-
handler: (raw: unknown) => Promise<unknown>;
|
|
2320
|
-
}
|
|
2321
|
-
/** @experimental */
|
|
2322
|
-
interface McpServer {
|
|
2323
|
-
/** Tools currently registered (depend on which delegates were wired). */
|
|
2324
|
-
readonly tools: ReadonlyMap<string, McpToolDescriptor>;
|
|
2325
|
-
/** The underlying queue — exposed so tests can introspect it. */
|
|
2326
|
-
readonly queue: DelegationTaskQueue;
|
|
2327
|
-
/** The feedback store — exposed for the same reason. */
|
|
2328
|
-
readonly feedbackStore: FeedbackStore;
|
|
2329
|
-
/** Handle a single parsed JSON-RPC message. Returns the response object (or `null` for notifications). */
|
|
2330
|
-
handle(message: JsonRpcMessage): Promise<JsonRpcResponse | null>;
|
|
2331
|
-
/** Drive the server on a stdio-shaped transport until `stop()` is called. */
|
|
2332
|
-
serve(transport?: McpTransport): Promise<void>;
|
|
2333
|
-
/** Stop a `serve` call. Subsequent requests are rejected. */
|
|
2334
|
-
stop(): void;
|
|
2335
|
-
}
|
|
2336
|
-
/** @experimental */
|
|
2337
|
-
interface McpTransport {
|
|
2338
|
-
input: NodeJS.ReadableStream;
|
|
2339
|
-
output: NodeJS.WritableStream;
|
|
2340
|
-
}
|
|
2341
|
-
/** @experimental */
|
|
2342
|
-
interface JsonRpcMessage {
|
|
2343
|
-
jsonrpc: '2.0';
|
|
2344
|
-
id?: number | string | null;
|
|
2345
|
-
method: string;
|
|
2346
|
-
params?: unknown;
|
|
2347
|
-
}
|
|
2348
|
-
/** @experimental */
|
|
2349
|
-
interface JsonRpcResponse {
|
|
2350
|
-
jsonrpc: '2.0';
|
|
2351
|
-
id: number | string | null;
|
|
2352
|
-
result?: unknown;
|
|
2353
|
-
error?: {
|
|
2354
|
-
code: number;
|
|
2355
|
-
message: string;
|
|
2356
|
-
data?: unknown;
|
|
2357
|
-
};
|
|
2358
|
-
}
|
|
2359
|
-
/**
|
|
2360
|
-
* Stdio JSON-RPC MCP server exposing the delegation tools (`delegate`, `delegate_feedback`, `delegation_status`, `delegation_history`, optional `delegate_ui_audit`) to sandbox coding-harness agents.
|
|
2361
|
-
*
|
|
2362
|
-
* @experimental
|
|
2363
|
-
*/
|
|
2364
|
-
declare function createMcpServer(options?: McpServerOptions): McpServer;
|
|
2365
|
-
/**
|
|
2366
|
-
* In-process pair of `Readable` + `Writable` streams suitable for driving
|
|
2367
|
-
* `server.serve(...)` from a test. Returns the agent-side stream (the
|
|
2368
|
-
* client writes to it) and the server-side stream (the test reads from it).
|
|
2369
|
-
*
|
|
2370
|
-
* @experimental
|
|
2371
|
-
*/
|
|
2372
|
-
declare function createInProcessTransport(): {
|
|
2373
|
-
transport: McpTransport;
|
|
2374
|
-
clientWrite(line: string): void;
|
|
2375
|
-
clientClose(): void;
|
|
2376
|
-
readServer(): Promise<JsonRpcResponse[]>;
|
|
2377
|
-
};
|
|
2378
|
-
|
|
2379
|
-
/**
|
|
2380
|
-
*
|
|
2381
|
-
* MCP binding for a live `Scope`. A sandbox driver gets the same small verbs
|
|
2382
|
-
* the in-process driver has: spawn, observe, await, steer, ask/answer, analyze,
|
|
2383
|
-
* and stop. Settled outputs remain Scope artifacts; product code can project
|
|
2384
|
-
* them into any UI/report envelope it needs.
|
|
2385
|
-
*
|
|
2386
|
-
* @experimental
|
|
2387
|
-
*/
|
|
2388
|
-
|
|
2389
|
-
/** A worker the driver has drained via `await_event`. */
|
|
2390
|
-
interface SettledWorker {
|
|
2391
|
-
readonly id: string;
|
|
2392
|
-
readonly status: 'done' | 'down';
|
|
2393
|
-
readonly score?: number;
|
|
2394
|
-
readonly valid?: boolean;
|
|
2395
|
-
readonly outRef?: string;
|
|
2396
|
-
readonly reason?: string;
|
|
2397
|
-
/** Epoch ms the ledger recorded this settlement — the resolution a progress-based stop rule
|
|
2398
|
-
* needs to answer "how long since anything landed?" without inventing a timestamp at read
|
|
2399
|
-
* time. Stamped when the cursor yields the settlement, not when a reader first looks. */
|
|
2400
|
-
readonly settledAt?: number;
|
|
2401
|
-
}
|
|
2402
|
-
type QuestionLevel = 'worker' | 'driver' | 'loop';
|
|
2403
|
-
type QuestionUrgency = 'continue-without' | 'blocks-step' | 'blocks-run';
|
|
2404
|
-
interface QuestionOption {
|
|
2405
|
-
readonly label: string;
|
|
2406
|
-
readonly tradeoff: string;
|
|
2407
|
-
}
|
|
2408
|
-
interface Question {
|
|
2409
|
-
readonly id: string;
|
|
2410
|
-
readonly from: string;
|
|
2411
|
-
readonly level: QuestionLevel;
|
|
2412
|
-
readonly question: string;
|
|
2413
|
-
readonly reason: string;
|
|
2414
|
-
readonly urgency: QuestionUrgency;
|
|
2415
|
-
readonly options?: ReadonlyArray<QuestionOption>;
|
|
2416
|
-
}
|
|
2417
|
-
type QuestionDecision = {
|
|
2418
|
-
readonly kind: 'answer';
|
|
2419
|
-
readonly answer: string;
|
|
2420
|
-
readonly by: string;
|
|
2421
|
-
} | {
|
|
2422
|
-
readonly kind: 'defer';
|
|
2423
|
-
readonly reason: string;
|
|
2424
|
-
} | {
|
|
2425
|
-
readonly kind: 'escalate';
|
|
2426
|
-
readonly to: 'parent' | 'user' | string;
|
|
2427
|
-
readonly reason: string;
|
|
2428
|
-
};
|
|
2429
|
-
interface QuestionRecord extends Question {
|
|
2430
|
-
readonly status: 'open' | 'answered' | 'deferred' | 'escalated';
|
|
2431
|
-
readonly decision?: QuestionDecision;
|
|
2432
|
-
readonly openedAt: number;
|
|
2433
|
-
}
|
|
2434
|
-
type QuestionPolicy = 'auto' | 'mustDecide' | 'bubble' | 'failClosed';
|
|
2435
|
-
interface AnalystRegistry {
|
|
2436
|
-
readonly kinds: ReadonlyArray<{
|
|
2437
|
-
id: string;
|
|
2438
|
-
description: string;
|
|
2439
|
-
area: string;
|
|
2440
|
-
}>;
|
|
2441
|
-
readonly run: (kindId: string, trace: unknown) => Promise<unknown>;
|
|
2442
|
-
}
|
|
2443
|
-
/** A trace-analyst result re-entered as a message on the bus (the `finding` event kind). */
|
|
2444
|
-
interface AnalystFindingEvent {
|
|
2445
|
-
readonly fromWorker: string;
|
|
2446
|
-
readonly analyst: string;
|
|
2447
|
-
readonly findings: unknown;
|
|
2448
|
-
}
|
|
2449
|
-
/** A parent→child message (the down-leg): recorded for observability, delivered via the child inbox,
|
|
2450
|
-
* never pulled back by the parent. `delivered` mirrors whether the live child accepted it. */
|
|
2451
|
-
interface DownMessageEvent {
|
|
2452
|
-
readonly toWorker: string;
|
|
2453
|
-
readonly instruction: string;
|
|
2454
|
-
readonly delivered: boolean;
|
|
2455
|
-
}
|
|
2456
|
-
/** Every message on the one typed pipe. UP (child→parent): question / settled / finding — queued for
|
|
2457
|
-
* the driver to `pull`. DOWN (parent→child): steer / answer — record-only (history + subscribers),
|
|
2458
|
-
* routed to the child inbox. New kinds are additive. */
|
|
2459
|
-
type CoordinationEvent = {
|
|
2460
|
-
readonly type: 'question';
|
|
2461
|
-
readonly question: QuestionRecord;
|
|
2462
|
-
} | {
|
|
2463
|
-
readonly type: 'settled';
|
|
2464
|
-
readonly worker: SettledWorker;
|
|
2465
|
-
} | {
|
|
2466
|
-
readonly type: 'finding';
|
|
2467
|
-
readonly finding: AnalystFindingEvent;
|
|
2468
|
-
} | {
|
|
2469
|
-
readonly type: 'steer';
|
|
2470
|
-
readonly down: DownMessageEvent;
|
|
2471
|
-
} | {
|
|
2472
|
-
readonly type: 'answer';
|
|
2473
|
-
readonly down: DownMessageEvent;
|
|
2474
|
-
readonly questionId: string;
|
|
2475
|
-
};
|
|
2476
|
-
type MakeWorkerAgent = (profile: unknown) => Agent<unknown, unknown>;
|
|
2477
|
-
interface CoordinationToolsOptions {
|
|
2478
|
-
readonly scope: Scope<unknown>;
|
|
2479
|
-
readonly blobs: ResultBlobStore;
|
|
2480
|
-
readonly makeWorkerAgent: MakeWorkerAgent;
|
|
2481
|
-
readonly perWorker: Budget;
|
|
2482
|
-
readonly analysts?: AnalystRegistry;
|
|
2483
|
-
readonly onEvent?: (event: CoordinationEvent) => void | Promise<void>;
|
|
2484
|
-
readonly questionPolicy?: QuestionPolicy;
|
|
2485
|
-
/** Analyst kind ids to run AUTOMATICALLY when a worker settles `done` (the analyst-on-settle
|
|
2486
|
-
* hook). Each result is published as a `finding` event on the bus — pass-through to subscribers
|
|
2487
|
-
* and queued for the driver to pull via `await_event`. Omit/empty = no auto-analysis (default;
|
|
2488
|
-
* the driver can still run lenses on demand via `run_analyst`). Requires `analysts`. */
|
|
2489
|
-
readonly analyzeOnSettle?: ReadonlyArray<string>;
|
|
2490
|
-
/** Hard cap on how many workers may be LIVE (spawned but not yet settled) at once. `spawn_agent`
|
|
2491
|
-
* counts the scope's non-terminal nodes and fails closed (`error: 'max-live-workers'`) BEFORE
|
|
2492
|
-
* reserving from the pool when the cap is already met — a concurrency fence on top of the
|
|
2493
|
-
* conserved-budget fence (the pool bounds total work; this bounds simultaneous work, e.g. live
|
|
2494
|
-
* sandboxes/boxes). Omit or `<= 0` = no cap (the prior behavior; the pool stays the only fence). */
|
|
2495
|
-
readonly maxLiveWorkers?: number;
|
|
2496
|
-
/** Max wall-clock ms a single `await_event` call may block waiting on a live worker to settle
|
|
2497
|
-
* before it returns a non-error `{ pending: true, live }` snapshot and lets the caller re-poll.
|
|
2498
|
-
* The underlying `scope.next()` blocks for the WHOLE (multi-minute) worker run; over a remote MCP
|
|
2499
|
-
* transport that block outlives the client's per-request timeout, so an unbounded await surfaces
|
|
2500
|
-
* to the supervisor as a hard tool ERROR on every call — the exact failure that leaves it flying
|
|
2501
|
-
* blind. Bounding the wait converts that error into a re-pollable liveness signal. The background
|
|
2502
|
-
* drain keeps running, so a settlement that lands after the bound is published to the bus and
|
|
2503
|
-
* pulled by the next call — nothing is lost. Omit = {@link DEFAULT_AWAIT_EVENT_TIMEOUT_MS}; `<= 0`
|
|
2504
|
-
* restores the prior UNBOUNDED block (only safe for in-process drivers with no transport timeout). */
|
|
2505
|
-
readonly awaitTimeoutMs?: number;
|
|
2506
|
-
/**
|
|
2507
|
-
* OPT-IN: run the ONLINE detector panel over each spawned worker's live tool trace and raise a
|
|
2508
|
-
* `finding` on the bus the moment a detector fires — so the driver learns "this worker is
|
|
2509
|
-
* looping" mid-run, from `await_event`, instead of at settle.
|
|
2510
|
-
*
|
|
2511
|
-
* This closes the `watchTrace` → `raiseFinding` wire whose own docstring already described it
|
|
2512
|
-
* ("the seam an ONLINE detector uses to tell the driver 'this worker is looping/erroring' the
|
|
2513
|
-
* moment it happens") but which nothing connected. Workers whose executor exposes no
|
|
2514
|
-
* `traceSource` are simply not watched; nothing fails.
|
|
2515
|
-
*
|
|
2516
|
-
* Omit = no online watching (the settle-time analysts are unaffected).
|
|
2517
|
-
*/
|
|
2518
|
-
readonly watchWorkers?: WorkerWatchOptions;
|
|
2519
|
-
/**
|
|
2520
|
-
* How long a worker may go without metered activity before `observe_agent` reports it as
|
|
2521
|
-
* `stalled`. A derived read at observation time, never a background watchdog — nothing is
|
|
2522
|
-
* killed or retried. Omit = the runtime default.
|
|
2523
|
-
*/
|
|
2524
|
-
readonly stallAfterMs?: number;
|
|
2525
|
-
}
|
|
2526
|
-
/** Online-detector wiring for spawned workers (`CoordinationToolsOptions.watchWorkers`). */
|
|
2527
|
-
interface WorkerWatchOptions {
|
|
2528
|
-
/** Detector panel; omit for the default stuck-loop + error-streak pair. */
|
|
2529
|
-
readonly detectors?: WatchTraceOptions['detectors'];
|
|
2530
|
-
/** Raise at most this many findings per worker, so one pathological worker cannot flood the
|
|
2531
|
-
* driver's inbox with the same signal every span. Default 3; `<= 0` = unlimited. */
|
|
2532
|
-
readonly maxFindingsPerWorker?: number;
|
|
2533
|
-
}
|
|
2534
|
-
/**
|
|
2535
|
-
* The supervisor-side toolbox returned by {@link createCoordinationTools}: the MCP tool
|
|
2536
|
-
* descriptors a driver `AgentProfile` calls to spawn, steer, observe, and settle workers
|
|
2537
|
-
* over a live `Scope`, plus the typed accessors (`settled`/`questions`/`history`/`stats`/
|
|
2538
|
-
* `raiseFinding`) for the bidirectional coordination bus. This is the live, backend-of-your-
|
|
2539
|
-
* choice, steerable counterpart to the one-shot own-sandbox delegation MCP.
|
|
2540
|
-
*/
|
|
2541
|
-
interface CoordinationTools {
|
|
2542
|
-
readonly tools: McpToolDescriptor[];
|
|
2543
|
-
isStopped(): boolean;
|
|
2544
|
-
stopReason(): string | undefined;
|
|
2545
|
-
settled(): ReadonlyArray<SettledWorker>;
|
|
2546
|
-
questions(): ReadonlyArray<QuestionRecord>;
|
|
2547
|
-
/** The full ordered log of every bus event — UP (settled / question / finding) and DOWN
|
|
2548
|
-
* (steer / answer) — the observability audit + replay trail. Each record carries seq,
|
|
2549
|
-
* timestamp, and priority. */
|
|
2550
|
-
history(): ReadonlyArray<BusRecord<CoordinationEvent>>;
|
|
2551
|
-
/** Bus throughput counters (published / pulled / by-kind) for live dashboards. */
|
|
2552
|
-
stats(): BusStats;
|
|
2553
|
-
/** Raise a `finding` on the bus from outside the settle hook — the seam an ONLINE detector
|
|
2554
|
-
* (mid-run, on the worker pipe) uses to tell the driver "this worker is looping/erroring" the
|
|
2555
|
-
* moment it happens, instead of only at settle. Queued for `await_event` + pass-through. */
|
|
2556
|
-
raiseFinding(finding: AnalystFindingEvent): Promise<void>;
|
|
2557
|
-
/**
|
|
2558
|
-
* Post-loop drain: pull every ALREADY-settled, unpulled child into the ledger (publishing each
|
|
2559
|
-
* as a `settled` bus event for the audit trail) WITHOUT awaiting live children. The driver
|
|
2560
|
-
* calls this once its brain loop ends, so a delivered child the brain never awaited still
|
|
2561
|
-
* reaches `finalizeBestDelivered` — a gate-verified delivery must never be lost to the
|
|
2562
|
-
* driver's pull discipline. Analyst-on-settle hooks do NOT fire here (the driver has stopped;
|
|
2563
|
-
* nobody is left to read a finding, and analysts spend real compute). Returns the count.
|
|
2564
|
-
*/
|
|
2565
|
-
drainResolved(): Promise<number>;
|
|
2566
|
-
}
|
|
2567
|
-
/** Build the driver's MCP tools over a live scope. */
|
|
2568
|
-
declare function createCoordinationTools(opts: CoordinationToolsOptions): CoordinationTools;
|
|
2569
|
-
|
|
2570
|
-
export { DelegationStateCorruptError as $, type AnalystRegistry as A, type DelegateResearchArgs as B, type CappedDelegationTrace as C, type DelegationExecutor as D, type ExecutorConfig as E, type FleetHandle as F, type DelegateResearchConfig as G, type DelegateResearchResult as H, type DelegateResult as I, type DelegateRunCtx as J, type DelegateUiAuditConfig as K, type DelegateUiAuditRoute as L, type MakeWorkerAgent as M, type DelegationError as N, type DelegationFeedbackSnapshot as O, type DelegationHistoryEntry as P, DelegationPersistenceError as Q, type DelegationProfile as R, type DelegationProgress as S, type DelegationRecord as T, type UiAuditorDelegate as U, type DelegationResultPayload as V, type WorkerWatchOptions as W, type DelegationResumeContext as X, type DelegationResumeDriver as Y, type DelegationResumeTick as Z, type DelegationRunContext as _, DelegationTaskQueue as a, traceContextToEnv as a$, type DelegationStatus as a0, type DelegationStore as a1, type DelegationTaskQueueOptions as a2, type DelegationTraceCaps as a3, type DelegationTraceCollector as a4, type DelegationTraceSpan as a5, type DetachedSessionDelegateOptions as a6, type DetachedSessionRefParts as a7, type DetachedTurn as a8, type DetachedTurnResumeDriverOptions as a9, type SiblingSandboxExecutorOptions as aA, type SubmitInput as aB, type SubmitOutput as aC, type TraceContext as aD, type UiAuditorDelegationOutput as aE, buildDelegationTraceSpans as aF, capDelegationTrace as aG, coderTaskFromArgs as aH, composeLoopTraceEmitters as aI, createCoordinationTools as aJ, createDelegateHandler as aK, createDelegationTraceCollector as aL, createDetachedTurnResumeDriver as aM, createFleetWorkspaceExecutor as aN, createInProcessTransport as aO, createMcpServer as aP, createPropagatingTraceEmitter as aQ, createSiblingSandboxExecutor as aR, detachedSessionDelegate as aS, detachedTurnEvents as aT, eventToSnapshot as aU, formatDetachedSessionRef as aV, hashIdempotencyInput as aW, parseDetachedSessionRef as aX, readTraceContextFromEnv as aY, runDetachedTurn as aZ, settleDetachedCoderTurn as a_, type DetachedWinnerSelection as aa, type DriveTurnCapableBox as ab, type DriveTurnTick as ac, type FeedbackEvent as ad, type FeedbackRating as ae, type FeedbackRefersTo as af, FileDelegationStore as ag, type FileDelegationStoreOptions as ah, type FleetWorkspaceExecutorOptions as ai, InMemoryDelegationStore as aj, InMemoryFeedbackStore as ak, type JsonRpcMessage as al, type JsonRpcResponse as am, type McpServer as an, type McpServerOptions as ao, type McpToolDescriptor as ap, type McpTransport as aq, type Question as ar, type QuestionDecision as as, type QuestionPolicy as at, type QuestionRecord as au, type ResearchOutputShape as av, type ResearchSource as aw, type RunDetachedTurnOptions as ax, type SettleDetachedCoderTurnOptions as ay, type SettledWorker as az, type FeedbackStore as b, validateDelegateArgs as b0, type EvalRunEvent as b1, type EvalRunGeneration as b2, type EvalRunsExportConfig as b3, type EvalRunsExportResult as b4, INTELLIGENCE_WIRE_VERSION as b5, type LoopSpanNode as b6, type OtelAttribute as b7, type OtelExportConfig as b8, type OtelExporter as b9, createInbox as bA, createSteerableSandboxSession as bB, defaultToolDetectors as bC, piExecutor as bD, piSeamKey as bE, watchTrace as bF, type OtelSpan as ba, type RuntimeEventOtelOptions as bb, buildLoopOtelSpans as bc, buildLoopSpanNodes as bd, buildRuntimeEventOtelSpans as be, createOtelExporter as bf, exportEvalRuns as bg, loopEventToOtelSpan as bh, type BusEvent as bi, type BusRecord as bj, type BusStats as bk, DEFAULT_SANDBOX_STEERING_MAX_TURNS as bl, type EventBus as bm, type Inbox as bn, type InboxMessage as bo, PI_RUNTIME as bp, type PiSeam as bq, type ProviderSeam as br, type PublishOptions as bs, type SandboxSteeringOptions as bt, type SteerableSandboxSession as bu, type WatchTraceOptions as bv, cliWorktreeExecutor as bw, createEventBus as bx, createExecutor as by, createExecutorRegistry as bz, type DelegateFeedbackResult as c, type DelegateFeedbackArgs as d, type DelegateUiAuditArgs as e, type DelegateUiAuditResult as f, type DelegationHistoryResult as g, type DelegationHistoryArgs as h, type DelegationStatusResult as i, type DelegationStatusArgs as j, type CoderDelegate as k, type CoderReview as l, type CoderReviewer as m, type CoordinationEvent as n, type CoordinationTools as o, type CoordinationToolsOptions as p, DELEGATE_DESCRIPTION as q, DELEGATE_INPUT_SCHEMA as r, DELEGATE_TOOL_NAME as s, DELEGATION_TRACE_MAX_BYTES as t, DELEGATION_TRACE_MAX_SPANS as u, type DelegateArgs as v, type DelegateCodeArgs as w, type DelegateCodeConfig as x, type DelegateCodeResult as y, type DelegateHandlerOptions as z };
|