@kodax-ai/kodax 0.7.40 → 0.7.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/dist/chunks/{chunk-NDNILSTR.js → chunk-5TFLMGER.js} +1 -1
  3. package/dist/chunks/{chunk-FAVPT4P7.js → chunk-6OB4AJOM.js} +1 -1
  4. package/dist/chunks/chunk-HYWVRTFA.js +1233 -0
  5. package/dist/chunks/chunk-SX2IS5JP.js +16 -0
  6. package/dist/chunks/chunk-ZPJPNLBK.js +462 -0
  7. package/dist/chunks/{compaction-config-A7XZ6H5Y.js → compaction-config-LT5PEXPT.js} +1 -1
  8. package/dist/chunks/{construction-bootstrap-OFPUZTXQ.js → construction-bootstrap-HBCWJFHC.js} +1 -1
  9. package/dist/chunks/dist-V3BS2NKB.js +2 -0
  10. package/dist/chunks/{utils-DFMYJUTE.js → utils-FAFUQJ2A.js} +1 -1
  11. package/dist/index.d.ts +232 -7
  12. package/dist/index.js +2 -2
  13. package/dist/kodax_cli.js +922 -912
  14. package/dist/sdk-agent.d.ts +1459 -10
  15. package/dist/sdk-agent.js +1 -1
  16. package/dist/sdk-coding.d.ts +4543 -14
  17. package/dist/sdk-coding.js +1 -1
  18. package/dist/sdk-llm.d.ts +209 -10
  19. package/dist/sdk-repl.d.ts +2694 -13
  20. package/dist/sdk-repl.js +1 -1
  21. package/dist/sdk-skills.d.ts +487 -11
  22. package/dist/types-chunks/bash-prefix-extractor.d-B2iliwdi.d.ts +2432 -0
  23. package/dist/types-chunks/capability.d-BxNgd1-c.d.ts +368 -0
  24. package/dist/types-chunks/cost-tracker.d-C4dMlQuV.d.ts +342 -0
  25. package/dist/types-chunks/history-cleanup.d-q1vAvCss.d.ts +1266 -0
  26. package/dist/types-chunks/instance-discovery.d-DZhp77vb.d.ts +1217 -0
  27. package/dist/types-chunks/resolver.d-BwD6TKz7.d.ts +262 -0
  28. package/dist/types-chunks/storage.d-Bv9T99Qu.d.ts +584 -0
  29. package/dist/types-chunks/types.d-C5mHR87z.d.ts +119 -0
  30. package/package.json +8 -3
  31. package/dist/acp_events.d.ts +0 -109
  32. package/dist/acp_logger.d.ts +0 -20
  33. package/dist/acp_server.d.ts +0 -92
  34. package/dist/chunks/chunk-CLS57NPX.js +0 -460
  35. package/dist/chunks/chunk-QZEDWITG.js +0 -1226
  36. package/dist/chunks/chunk-Z5EBDA6R.js +0 -15
  37. package/dist/chunks/dist-OTUF22DA.js +0 -2
  38. package/dist/cli_commands.d.ts +0 -17
  39. package/dist/cli_option_helpers.d.ts +0 -49
  40. package/dist/cli_option_helpers.test.d.ts +0 -1
  41. package/dist/constructed_cli.d.ts +0 -82
  42. package/dist/constructed_cli.test.d.ts +0 -1
  43. package/dist/kodax_cli.d.ts +0 -7
  44. package/dist/self_modify_cli.d.ts +0 -81
  45. package/dist/self_modify_cli.test.d.ts +0 -9
  46. package/dist/skill_cli.d.ts +0 -15
  47. package/dist/skill_cli.test.d.ts +0 -1
@@ -1,15 +1,1464 @@
1
+ import { A as Agent, H as Handoff, $ as RunnerToolCall, a2 as RunnerToolResult, a6 as Span, a as AgentMessage, C as ChildTaskRegistry, D as DiscoveredInstance, a4 as SessionMeta, a5 as SessionStateSnapshot, aa as StateWriterFs, k as InstanceDiscoveryFs, a9 as StateWriter } from './types-chunks/instance-discovery.d-DZhp77vb.js';
2
+ export { b as AgentMiddlewareDeclaration, c as AgentReasoningProfile, d as AgentTool, e as CurrentTodoSummary, f as DiscoveryOptions, G as Guardrail, g as GuardrailBlockedError, h as GuardrailContext, i as GuardrailEscalateError, j as GuardrailVerdict, I as InputGuardrail, K as KodaXCompactMemoryProgress, l as KodaXCompactMemorySeed, m as KodaXExtensionSessionRecord, n as KodaXExtensionSessionState, o as KodaXExtensionStore, p as KodaXExtensionStoreEntry, q as KodaXJsonValue, r as KodaXSessionArchiveMarkerEntry, s as KodaXSessionArtifactLedgerEntry, t as KodaXSessionBranchSummaryEntry, u as KodaXSessionCompactionEntry, v as KodaXSessionData, w as KodaXSessionEntry, x as KodaXSessionEntryBase, y as KodaXSessionLabelEntry, z as KodaXSessionLineage, B as KodaXSessionMessageEntry, E as KodaXSessionMeta, F as KodaXSessionNavigationOptions, J as KodaXSessionRuntimeInfo, L as KodaXSessionScope, M as KodaXSessionStorage, N as KodaXSessionTreeNode, O as KodaXSessionUiHistoryItem, P as KodaXSessionUiHistoryItemType, Q as KodaXSessionWorkspaceKind, R as MAX_TOOL_LOOP_ITERATIONS, S as OutputGuardrail, T as PersistedSessionState, U as ReasoningDepth, V as RecentlyModifiedFile, W as RequestTaskStopOptions, X as RequestTaskStopResult, Y as RunnableTool, Z as RunnerLlmResult, _ as RunnerLlmReturn, a0 as RunnerToolContext, a1 as RunnerToolObserver, a3 as SessionErrorMetadata, ab as StateWriterOptions, ac as TaskAbortRegistry, ad as ToolBeforeOutcome, ae as ToolGuardrail, af as buildAssistantMessageFromLlmResult, ag as buildToolResultMessage, ah as collectGuardrails, ai as createAgent, aj as createHandoff, ak as createStateWriter, al as discoverInstances, am as executeRunnerToolCall, an as isRunnableTool, ao as isRunnerLlmResult, ap as registerChildTask, aq as requestTaskStop, ar as runInputGuardrails, as as runOutputGuardrails, at as runToolAfterGuardrails, au as runToolBeforeGuardrails } from './types-chunks/instance-discovery.d-DZhp77vb.js';
3
+ import { d as AgentManifest, M as ManifestPatch, k as InvariantId, Q as QualityInvariant } from './types-chunks/history-cleanup.d-q1vAvCss.js';
4
+ export { A as AdmissionAuditOptions, a as AdmissionCtx, b as AdmissionVerdict, c as AdmittedHandle, C as CompactionContext, e as CompactionEntry, f as CompactionEntryPayload, g as CompactionPolicy, D as DEFAULT_SYSTEM_CAP, h as DefaultSummaryCompaction, i as DefaultSummaryCompactionOptions, j as Deliverable, I as InMemorySessionOptions, l as InvariantResult, m as InvariantSession, K as KODAX_API_MIN_INTERVAL, n as KODAX_DEFAULT_TIMEOUT, o as KODAX_HARD_TIMEOUT, p as KODAX_MAX_INCOMPLETE_RETRIES, q as KODAX_MAX_MAXTOKENS_RETRIES, r as KODAX_MAX_RETRIES, s as KODAX_MAX_TOKENS, t as KODAX_RETRY_BASE_DELAY, u as KODAX_STAGGER_DELAY, v as MessageEntry, O as ObserveCtx, P as PROMISE_PATTERN, w as PolicyCompactionResult, x as PresetDispatcher, y as PresetTracingContext, R as ReadonlyMutationTracker, z as ReadonlyRecorder, B as RunEvent, E as RunOptions, F as RunResult, G as Runner, H as RunnerEvent, S as Session, J as SessionDispatchResult, L as SessionEntry, N as SessionExtension, T as SessionForkOptions, U as SystemCap, V as TerminalCtx, W as ToolCapability, X as ToolPermission, _ as _resetAdmittedAgentBindings, Y as _resetPresetDispatchers, Z as buildSystemPrompt, $ as cleanupIncompleteToolCalls, a0 as countTokens, a1 as createInMemorySession, a2 as createInvariantSessionForAgent, a3 as detectInstructionsInjection, a4 as estimateTokens, a5 as extractAssistantTextFromMessage, a6 as getAdmittedAgentBindings, a7 as registerPresetDispatcher, a8 as runAdmissionAudit, a9 as setAdmittedAgentBindings, aa as validateAndFixToolHistory } from './types-chunks/history-cleanup.d-q1vAvCss.js';
5
+ import { o as KodaXMessage } from './types-chunks/capability.d-BxNgd1-c.js';
6
+ export { C as CapabilityKind, a as CapabilityProvider, b as CapabilityResult, g as KodaXAssuranceIntent, i as KodaXContentBlock, k as KodaXExecutionMode, l as KodaXExecutionPattern, n as KodaXImageBlock, q as KodaXMutationSurface, t as KodaXProviderConfig, B as KodaXProviderStreamOptions, F as KodaXReasoningCapability, G as KodaXReasoningMode, I as KodaXReasoningRequest, J as KodaXRedactedThinkingBlock, M as KodaXRiskLevel, N as KodaXStreamResult, O as KodaXTaskActionability, P as KodaXTaskBudgetOverrides, Q as KodaXTaskComplexity, R as KodaXTaskFamily, S as KodaXTaskRoutingDecision, T as KodaXTaskType, U as KodaXTaskWorkIntent, V as KodaXTextBlock, W as KodaXThinkingBlock, X as KodaXThinkingBudgetMap, Y as KodaXThinkingDepth, Z as KodaXTokenUsage, _ as KodaXToolDefinition, $ as KodaXToolResultBlock, a0 as KodaXToolUseBlock } from './types-chunks/capability.d-BxNgd1-c.js';
7
+ import { Q as QueueEventListener, b as QueuedMessage, E as EnqueueInput, D as DequeueFilter, a as MessagePriority, M as MessageMode } from './types-chunks/types.d-C5mHR87z.js';
8
+
1
9
  /**
2
- * SDK subpath entry`@kodax-ai/kodax/agent`
10
+ * Runner Handoff HelpersFEATURE_084 Shard 4 (v0.7.26).
3
11
  *
4
- * Re-exports the entire `@kodax-ai/agent` public API so SDK consumers
5
- * can pull agent primitives directly without going through the broader
6
- * `runKodaX` surface.
12
+ * When a tool result carries `metadata.handoffTarget`, the Runner should
13
+ * switch ownership to the target Agent declared in
14
+ * `currentAgent.handoffs`. This module provides pure helpers for that
15
+ * transition:
7
16
  *
8
- * Usage:
9
- * ```ts
10
- * import { Runner } from '@kodax-ai/kodax/agent';
11
- * ```
17
+ * - `detectHandoffSignal(currentAgent, toolResults)` — find the first
18
+ * tool result that carries a matching handoff and return the resolved
19
+ * Handoff + target.
20
+ * - `replaceSystemMessage(transcript, newAgent)` — swap the leading
21
+ * system message so the next LLM turn sees the new agent's
22
+ * instructions.
23
+ * - `emitHandoffSpan(parentSpan, from, to, ...)` — emit the
24
+ * `HandoffSpan` (FEATURE_083 span kind).
12
25
  *
13
- * See docs/ADR.md ADR-024 for the SDK formalization decision.
26
+ * Guardrail scoping for Shard 4: input / output guardrails are **run-scoped**
27
+ * — they use the starting agent's declarations and run once at start/end of
28
+ * the overall run. They do NOT re-run on handoff. Tool guardrails apply to
29
+ * every tool invocation regardless of which agent is calling. This keeps
30
+ * the mental model simple; later shards may refine.
14
31
  */
15
- export * from '@kodax-ai/agent';
32
+
33
+ interface HandoffSignal {
34
+ readonly from: Agent;
35
+ readonly to: Agent;
36
+ readonly handoff: Handoff;
37
+ /** Index of the tool result that triggered the handoff. */
38
+ readonly triggerIndex: number;
39
+ }
40
+ /**
41
+ * Find the first tool result with a `handoffTarget` metadata field that
42
+ * resolves to a declared handoff on `currentAgent`. Returns `undefined` if
43
+ * no result carries a handoff target or if the target isn't declared.
44
+ */
45
+ declare function detectHandoffSignal(currentAgent: Agent, _toolCalls: readonly RunnerToolCall[], toolResults: readonly RunnerToolResult[]): HandoffSignal | undefined;
46
+ /**
47
+ * Replace the leading system message with `newAgent`'s instructions so the
48
+ * next LLM turn runs under the new role. The rest of the transcript
49
+ * (user, assistant tool_use, tool_result blocks) is preserved verbatim so
50
+ * the new agent sees the full lead-up.
51
+ */
52
+ declare function replaceSystemMessage(transcript: readonly AgentMessage[], newAgent: Agent): AgentMessage[];
53
+ /**
54
+ * Emit a `HandoffSpan` as a child of the agent span. Ends immediately
55
+ * because a handoff is a point-in-time event (unlike agent/tool spans
56
+ * which wrap a duration).
57
+ */
58
+ declare function emitHandoffSpan(parentSpan: Span | null, from: Agent, to: Agent, handoffKind: 'continuation' | 'as-tool', description?: string): void;
59
+
60
+ /**
61
+ * FEATURE_101 (v0.7.31) admission runtime — patch reducer + invariant registry.
62
+ *
63
+ * Deterministic, side-effect-free helpers consumed by `Runner.admit()` (in
64
+ * `./runner.ts`, added in the next 1A.3 increment).
65
+ *
66
+ * - `applyManifestPatch(manifest, patch)`: produces a new manifest with the
67
+ * patch applied. Used when admission needs to clamp tools / budget /
68
+ * iterations to system caps.
69
+ * - `composePatches(patches)`: merges multiple patches (deterministic
70
+ * reducer; min wins for clamp values, union for collections).
71
+ * - `InvariantRegistry`: in-memory registry mapping `InvariantId` to
72
+ * `QualityInvariant` implementations. Open-ended — FEATURE_101 v1
73
+ * registers 7 closed-set invariants; FEATURE_106 registers 1 external
74
+ * invariant; future features may add more.
75
+ * - `resolveRequiredInvariants(role, toolScope, harnessTier)`: pure
76
+ * function returning the InvariantId set the admission layer enforces
77
+ * by default (caller's `manifest.declaredInvariants` is unioned on
78
+ * top — declared can only ADD, never remove from required).
79
+ *
80
+ * Pure data + pure functions. No I/O, no shared mutable state visible
81
+ * outside the registry's own opaque object. Tests live in
82
+ * `./admission-runtime.test.ts`.
83
+ */
84
+
85
+ /**
86
+ * Apply a patch to a manifest, returning a NEW manifest. Pure function;
87
+ * input is not mutated.
88
+ *
89
+ * Semantics per field:
90
+ *
91
+ * - `removeTools`: manifest.tools filtered by `(t) => !patch.removeTools.includes(t.name)`
92
+ * - `clampMaxBudget`: manifest.maxBudget = min(current, patch.clampMaxBudget) when current > clamp
93
+ * - `clampMaxIterations`: manifest.maxIterations = min(current, patch.clampMaxIterations) when current > clamp.
94
+ * v0.7.31.2 added `AgentManifest.maxIterations` as a first-class
95
+ * field (symmetric with maxBudget). Runner.run reads the post-
96
+ * clamp manifest through `getAdmittedAgentBindings` and takes
97
+ * min-wins against `RunOptions.maxToolLoopIterations`.
98
+ * - `addInvariants`: union into manifest.declaredInvariants
99
+ * - `notes`: ignored at the manifest level — surfaced
100
+ * through AdmissionVerdict.clampNotes
101
+ *
102
+ * The patch is monotone: tools can only be REMOVED (admission can shrink,
103
+ * never expand), budgets can only be CLAMPED DOWN, invariants can only be
104
+ * ADDED. This invariant is what makes "clamp severity is safe to apply
105
+ * automatically" — the manifest can't end up MORE permissive than what
106
+ * the LLM submitted.
107
+ */
108
+ declare function applyManifestPatch(manifest: AgentManifest, patch: ManifestPatch): AgentManifest;
109
+ /**
110
+ * Compose multiple patches into one. Order is preserved for `notes`; numeric
111
+ * clamp fields use min-wins (most restrictive); collections union.
112
+ *
113
+ * Used when several invariants each return clamp at admission time — the
114
+ * Runner accumulates patches and applies them in one pass.
115
+ */
116
+ declare function composePatches(patches: readonly ManifestPatch[]): ManifestPatch;
117
+ /**
118
+ * Register an invariant implementation. Throws if the id is already
119
+ * registered (overwrite would be a silent contract bug). Tests that
120
+ * need a fresh registry call `_resetInvariantRegistry()` first.
121
+ */
122
+ declare function registerInvariant(invariant: QualityInvariant): void;
123
+ /**
124
+ * Look up a registered invariant by id. Returns undefined when not
125
+ * registered — Runner.admit treats this as "skip this id silently"
126
+ * because the closed-set guarantee belongs to the registry caller, not
127
+ * to the runtime.
128
+ */
129
+ declare function getInvariant(id: InvariantId): QualityInvariant | undefined;
130
+ /**
131
+ * Snapshot of the currently-registered invariant ids. Read-only —
132
+ * mutations on the returned array do NOT affect the registry.
133
+ */
134
+ declare function listRegisteredInvariants(): readonly InvariantId[];
135
+ /**
136
+ * Reset the registry to empty. **Tests only** — production code should
137
+ * never invoke this. Module export name is prefixed with `_` to make
138
+ * accidental imports stand out.
139
+ */
140
+ declare function _resetInvariantRegistry(): void;
141
+ /**
142
+ * Per-role / per-tool-scope / per-harness-tier required invariant set.
143
+ *
144
+ * Pure function — same inputs always return the same set. The admission
145
+ * layer unions this with `manifest.declaredInvariants` to produce the
146
+ * effective set. Declared can only ADD (it's an LLM voluntary commitment
147
+ * on top of what the system requires).
148
+ *
149
+ * v1 default policy: every manifest gets the 7 admission-v1 closed-set
150
+ * invariants. role / toolScope / harnessTier are accepted but currently
151
+ * not used to differentiate — they're plumbed through so future
152
+ * refinements can specialize without breaking the API.
153
+ */
154
+ declare function resolveRequiredInvariants(_role: string, _toolScope: readonly string[], _harnessTier: string): readonly InvariantId[];
155
+ /**
156
+ * Effective invariant set for a manifest = union of required (system)
157
+ * and declared (LLM voluntary). Returned in stable order (required
158
+ * first, then declared additions in insertion order).
159
+ */
160
+ declare function resolveEffectiveInvariants(required: readonly InvariantId[], declared: readonly InvariantId[] | undefined): readonly InvariantId[];
161
+
162
+ /**
163
+ * FEATURE_101 v0.7.31.1 — admission metrics counters.
164
+ *
165
+ * Closes the "admission decoration" risk called out in
166
+ * docs/features/v0.7.31.md §dispatch eval 新增指标. v0.7.31 declared
167
+ * three metrics in the design (`admission_reject_after_retry_rate`,
168
+ * `admission_clamp_rate`, `invariant_violation_rate`) but never emitted
169
+ * them at runtime — leaving operators with no signal on whether
170
+ * admission was actually catching things or had become a no-op.
171
+ *
172
+ * The module exports an in-process counter table plus pure helpers to
173
+ * read / reset / compute rates. Counters increment from:
174
+ *
175
+ * - `runAdmissionAudit` on every verdict (ok / ok+clamp / reject /
176
+ * reject-final).
177
+ * - `InvariantSession.recordX` and `assertTerminal` on every
178
+ * observed violation.
179
+ *
180
+ * Reading the rates:
181
+ *
182
+ * - `admission_clamp_rate` = admitOkClamped / admitTotal
183
+ * - `admission_reject_after_retry_rate` = admitRejectFinal / admitTotal
184
+ * - `invariant_violation_rate` = invariantViolations / admitTotal
185
+ *
186
+ * Counters are process-local — exporters (Prometheus, OpenTelemetry,
187
+ * etc.) are expected to scrape `getAdmissionMetricsSnapshot()` on a
188
+ * cadence. `_resetAdmissionMetrics` is for tests; production never
189
+ * calls it.
190
+ */
191
+ interface AdmissionMetricsSnapshot {
192
+ readonly admitTotal: number;
193
+ readonly admitOk: number;
194
+ readonly admitOkClamped: number;
195
+ readonly admitReject: number;
196
+ readonly admitRejectFinal: number;
197
+ readonly invariantViolationsObserved: number;
198
+ readonly invariantViolationsTerminal: number;
199
+ readonly admissionClampRate: number;
200
+ readonly admissionRejectAfterRetryRate: number;
201
+ readonly invariantViolationRate: number;
202
+ }
203
+ /**
204
+ * Snapshot of current counters + computed rates. Returned object is a
205
+ * fresh copy — mutating it does NOT affect the live counters.
206
+ */
207
+ declare function getAdmissionMetricsSnapshot(): AdmissionMetricsSnapshot;
208
+ /**
209
+ * Test-only reset. Production code MUST NOT call this — the counters
210
+ * are designed to accumulate across the process lifetime.
211
+ */
212
+ declare function _resetAdmissionMetrics(): void;
213
+ /**
214
+ * Returns true when the admission debug flag is set in the environment.
215
+ * Recognises `'1'`, `'true'`, `'yes'`, `'on'` (case-insensitive).
216
+ * Falls through to false on unset / empty / any other value, so the
217
+ * default is silent.
218
+ */
219
+ declare function isAdmissionDebugEnabled(): boolean;
220
+
221
+ /**
222
+ * FEATURE_101 invariant: `evidenceTrail`.
223
+ *
224
+ * Mutations must leave evidence; the terminal deliverable verifies the
225
+ * evidence trail is complete.
226
+ *
227
+ * Hooks:
228
+ * - observe(mutation_recorded): no-op in v1 — the runtime mutation
229
+ * tracker already records the file. Reserved for future "did the
230
+ * evidence_added event arrive within N tool calls of the mutation"
231
+ * timing checks.
232
+ * - assertTerminal(deliverable): if the run produced any mutations
233
+ * (deliverable.mutationCount > 0), at least one evidence artifact
234
+ * must accompany them. Empty `evidenceArtifacts` for a mutating run
235
+ * is a reject — the deliverable is unauditable.
236
+ *
237
+ * The threshold is intentionally coarse (any > 0 works): per-file
238
+ * evidence is the @kodax-ai/coding mutation tracker's job, not the Layer A
239
+ * primitive's. We can tighten the rule once FEATURE_089 starts emitting
240
+ * structured mutation→evidence pairings.
241
+ */
242
+
243
+ declare const evidenceTrail: QualityInvariant;
244
+
245
+ /**
246
+ * FEATURE_101 invariant: `finalOwner`.
247
+ *
248
+ * Admit-only check: the manifest must designate a final owner. v1 heuristic:
249
+ *
250
+ * - manifest.name is non-empty (schema validation already enforces, but
251
+ * re-checking here keeps the invariant self-contained), and
252
+ * - if the manifest declares handoffs, the handoff graph (manifest +
253
+ * ctx.activatedAgents) must contain at least one terminal node — an
254
+ * agent with no outgoing handoffs. If every node has an outgoing
255
+ * handoff, the deliverable has no resting place; admit rejects.
256
+ *
257
+ * The check is intentionally lightweight: deeper graph properties (single
258
+ * sink, dominator analysis) are noise for v1 and would burn invariant
259
+ * budget on hypothetical multi-role topologies that don't ship in
260
+ * v0.7.31. We can sharpen the rule when FEATURE_089 starts emitting
261
+ * richer manifests.
262
+ *
263
+ * Pure function — no I/O, no shared mutable state.
264
+ */
265
+
266
+ declare const finalOwner: QualityInvariant;
267
+
268
+ /**
269
+ * FEATURE_101 invariant: `handoffLegality`.
270
+ *
271
+ * Admit-time DAG check: the handoff graph (manifest.handoffs + the
272
+ * transitive closure of ctx.activatedAgents.handoffs) must be acyclic.
273
+ * A cycle means the system can transfer ownership in a loop without
274
+ * ever terminating — admission rejects.
275
+ *
276
+ * v1 algorithm: iterative DFS with explicit white/gray/black colouring.
277
+ * Iterative (not recursive) because manifests with deep handoff chains
278
+ * could blow the JS stack on certain runtimes — handing the search a
279
+ * stack of our own keeps the bound predictable.
280
+ *
281
+ * Observe-time: no-op in v1. Cycle detection at admit time covers the
282
+ * static graph; runtime handoff traversal is a separate concern handled
283
+ * by the Runner's loop bound. We keep an `observe` stub registered so
284
+ * future versions can add per-event bookkeeping (e.g. detect "agent X
285
+ * already handed off this run, refusing second handoff") without a
286
+ * registry migration.
287
+ */
288
+
289
+ declare const handoffLegality: QualityInvariant;
290
+
291
+ /**
292
+ * Pure-new invariant implementations bundled with @kodax-ai/agent.
293
+ *
294
+ * The admission contract types live in `../admission.ts`; the registry
295
+ * runtime in `../admission-runtime.ts`. This module exports the three
296
+ * invariant declarations plus a `registerCoreInvariants()` helper that
297
+ * registers them in one call.
298
+ *
299
+ * Why this split:
300
+ * - These three (finalOwner, handoffLegality, evidenceTrail) are pure
301
+ * functions of the admission types — they have NO @kodax-ai/coding
302
+ * dependencies. Living in @kodax-ai/agent keeps the dependency direction
303
+ * clean and lets `Runner.admit` unit-test against real invariants
304
+ * without pulling the coding runtime into the test harness.
305
+ * - The four coupled invariants (budgetCeiling, toolPermission,
306
+ * boundedRevise, independentReview) wrap @kodax-ai/coding capabilities
307
+ * (mutation tracker, budget controller, ToolGuardrail tier resolver)
308
+ * and live in `@kodax-ai/coding/src/agent-runtime/invariants/`.
309
+ * - `harnessSelectionTiming` (FEATURE_106 external) was here in
310
+ * v0.7.31; v0.7.35.1 FEATURE_142 (A-R2) moved it back to
311
+ * `@kodax-ai/coding/src/agent-runtime/invariants/` because its body
312
+ * hardcoded `ctx.recorder.scout.payload.scout.confirmedHarness` —
313
+ * a coding-AMA Scout-role field reference (ADR-021).
314
+ *
315
+ * Registration is NOT side-effecting on import — consumers call
316
+ * `registerCoreInvariants()` explicitly so test isolation
317
+ * (`_resetInvariantRegistry()` followed by registering only the subset
318
+ * a test needs) stays predictable.
319
+ */
320
+
321
+ /**
322
+ * The three pure invariants @kodax-ai/agent ships, in registration order.
323
+ * Exposed as a constant so consumers can introspect the set without
324
+ * registering (e.g. dispatch-eval metric setup that wants id labels).
325
+ */
326
+ declare const CORE_INVARIANTS: readonly QualityInvariant[];
327
+ /**
328
+ * Register the three pure-new invariants on the shared runtime registry.
329
+ * Idempotent only when paired with `_resetInvariantRegistry()` first —
330
+ * `registerInvariant` itself throws on duplicate registration, which is
331
+ * the desired contract (silent overwrite would mask refactors).
332
+ */
333
+ declare function registerCoreInvariants(): void;
334
+
335
+ /**
336
+ * v0.7.35.1 FEATURE_145 — Agent config home, 3-tier resolution.
337
+ *
338
+ * Centralizes the user-config directory used to be hardcoded across
339
+ * ~30 sites in 6 packages as `path.join(os.homedir(), '.kodax', ...)`.
340
+ * That pattern had two problems:
341
+ *
342
+ * 1. **Drift**: each new caller in a future feature was a fresh
343
+ * hardcode site; nothing stopped a caller from using the wrong
344
+ * string (`'kodax'` instead of `'.kodax'`, etc.).
345
+ * 2. **Substrate consumer coupling**: when `@kodax-ai/agent` is reused
346
+ * by a downstream agent (e.g. `@kodax-ai/ops-agent`,
347
+ * `@kodax-ai/data-analysis-agent`), there was no way to redirect the
348
+ * runtime config dir — every derivative agent was forced to
349
+ * share the `~/.kodax/` namespace.
350
+ *
351
+ * The helper exposes a 3-tier priority chain:
352
+ *
353
+ * 1. **Programmatic override** via {@link setAgentConfigHome} —
354
+ * highest priority. Substrate consumers call this once at boot,
355
+ * before any subsystem reads the path.
356
+ * 2. **`KODAX_HOME` env var** — middle priority. Used by shell / CI /
357
+ * test isolation / multi-tenant shared machines. (Already honored
358
+ * historically by `@kodax-ai/llm/src/reasoning-overrides.ts`; this
359
+ * helper makes it the canonical path for all packages.)
360
+ * 3. **`~/.kodax/`** — lowest priority. Default for the standalone
361
+ * kodax CLI. With DI not set + env not set, the resolver returns
362
+ * the same byte sequence as the prior hardcoded
363
+ * `path.join(os.homedir(), '.kodax')` calls — so the migration
364
+ * from hardcoded sites to this helper is byte-equivalent for the
365
+ * existing user base.
366
+ *
367
+ * Why a process-level singleton (and not per-call DI):
368
+ * the ~30 fs callsites are buried in library helpers (construction /
369
+ * mcp catalog / oauth tokens / paste-cache etc.). Threading a
370
+ * `configHome` parameter through every helper would change ~50
371
+ * function signatures, and every caller would have to remember to
372
+ * thread it — a single forgotten thread silently falls back to
373
+ * default. Singleton matches the `process.env.NODE_ENV` pattern: a
374
+ * process really has a single config home (no legitimate use case
375
+ * for a process to interleave reads/writes against `~/.kodax/` AND
376
+ * `~/.opsagent/` simultaneously).
377
+ *
378
+ * NOT migrated:
379
+ * - `@kodax-ai/llm/src/reasoning-overrides.ts:49` keeps its inline
380
+ * `process.env.KODAX_HOME ?? path.join(os.homedir(), '.kodax')`
381
+ * fallback because moving it to this helper would create an
382
+ * `@kodax-ai/llm → @kodax-ai/agent` dependency cycle (agent already
383
+ * imports ai). The two implementations have identical observable
384
+ * behavior at the env / default tiers; the programmatic override
385
+ * tier doesn't apply to ai-layer code.
386
+ * - **Project-relative** `.kodax/` paths (e.g. `path.join(projectRoot,
387
+ * '.kodax', 'AGENTS.md')`) are NOT migrated — those name a
388
+ * different concept (per-project config) and use a different root.
389
+ * - **CWD-relative** subpath constants like `path.join('.kodax',
390
+ * 'constructed', '_audit.jsonl')` (joined with a project root by
391
+ * the caller) are likewise project-scoped and stay as-is.
392
+ */
393
+ /**
394
+ * Set the agent config home programmatically. Highest priority in
395
+ * {@link getAgentConfigHome}'s 3-tier chain.
396
+ *
397
+ * Substrate consumers (e.g. an agent built on top of `@kodax-ai/agent`)
398
+ * should call this once at process boot, before any subsystem reads
399
+ * the path. Pass `undefined` to reset (used in tests).
400
+ */
401
+ declare function setAgentConfigHome(path: string | undefined): void;
402
+ /**
403
+ * Resolve the agent runtime config home directory.
404
+ *
405
+ * Priority (high → low):
406
+ * 1. Programmatic override via {@link setAgentConfigHome}
407
+ * 2. `KODAX_HOME` env var
408
+ * 3. `~/.kodax` (hardcoded default)
409
+ */
410
+ declare function getAgentConfigHome(): string;
411
+ /**
412
+ * Resolve a sub-path under the agent config home.
413
+ *
414
+ * Equivalent to `path.join(getAgentConfigHome(), ...segments)` but
415
+ * shorter at every callsite (which is the entire point of the helper —
416
+ * 30 callsites of `path.join(os.homedir(), '.kodax', x, y)` collapse to
417
+ * 30 callsites of `getAgentConfigPath(x, y)`).
418
+ */
419
+ declare function getAgentConfigPath(...segments: string[]): string;
420
+
421
+ /**
422
+ * @kodax-ai/agent/messaging/queue — 2-tier agentId-scoped FIFO message queue.
423
+ *
424
+ * FEATURE_115 (v0.7.36).
425
+ *
426
+ * Invariants:
427
+ * - In-priority FIFO: messages of the same priority drain in enqueue order.
428
+ * - Cross-priority precedence: 'user' drains before 'background', regardless
429
+ * of enqueue order.
430
+ * - agentId routing: messages addressed to agentId X are only visible to
431
+ * consumers filtering for that exact agentId. undefined ≠ "any agent" —
432
+ * it specifically matches main-thread messages.
433
+ * - Not persistent: process restart loses queue state, by design (matches
434
+ * TodoStore semantics).
435
+ *
436
+ * The queue is process-global by default (`getMessageQueue()`); the class is
437
+ * also exported for tests / isolated downstream use.
438
+ */
439
+
440
+ declare class MessageQueue {
441
+ private messages;
442
+ private nextSeq;
443
+ /**
444
+ * FEATURE_159 (v0.7.40) — observable subscription set. Same pattern as
445
+ * Claude Code's `messageQueueManager.ts` `createSignal()` substrate,
446
+ * but the listener carries a structured `QueueEvent` so SDK
447
+ * observability consumers (logger, tracer, metrics) can react per-
448
+ * event without re-diffing snapshots. `useSyncExternalStore` consumers
449
+ * still work — they ignore the event argument.
450
+ *
451
+ * Cached `snapshotRef` keeps reference identity stable across reads
452
+ * when nothing changed — required by React 18's `useSyncExternalStore`
453
+ * to avoid render loops.
454
+ */
455
+ private listeners;
456
+ private snapshotRef;
457
+ private notify;
458
+ /**
459
+ * Subscribe to queue mutations. Compatible with React 18's
460
+ * `useSyncExternalStore(subscribe, getSnapshot)` — the hook passes a
461
+ * `() => void` callback, which is structurally assignable to the
462
+ * `QueueEventListener` parameter because TypeScript treats
463
+ * callback parameter discards as compatible. SDK consumers that
464
+ * declare a typed `(event: QueueEvent) => void` listener see the
465
+ * structured event.
466
+ *
467
+ * Listener is called synchronously after every mutation; returns an
468
+ * unsubscribe function.
469
+ */
470
+ subscribe: (listener: QueueEventListener) => (() => void);
471
+ /**
472
+ * Returns the current frozen queue snapshot. Reference identity is
473
+ * stable across reads when the queue has not mutated, which is the
474
+ * contract React's `useSyncExternalStore` relies on.
475
+ */
476
+ getSnapshot: () => readonly QueuedMessage[];
477
+ /** Returns the assigned id of the enqueued message. */
478
+ enqueue(input: EnqueueInput): string;
479
+ /**
480
+ * Drain matching messages, ordered by priority (user > background) then
481
+ * FIFO within each priority. Removes drained messages from the queue.
482
+ */
483
+ dequeue(filter: DequeueFilter): QueuedMessage[];
484
+ /**
485
+ * Peek at matching messages without removing them. Returns messages in
486
+ * the same priority + FIFO order that `dequeue(filter)` would return,
487
+ * so callers can inspect what the next drain would yield.
488
+ */
489
+ peek(filter: DequeueFilter): QueuedMessage[];
490
+ /** Total queue size across all priorities / agents. */
491
+ size(): number;
492
+ /** Count of messages matching the filter. */
493
+ count(filter: DequeueFilter): number;
494
+ /** True iff at least one message matches the filter. */
495
+ has(filter: DequeueFilter): boolean;
496
+ /** Remove all queued messages — used in tests / process abort scenarios. */
497
+ clear(): void;
498
+ }
499
+ /**
500
+ * Returns the process-global MessageQueue singleton, creating it lazily on
501
+ * first call. Use this for production wiring; instantiate `MessageQueue`
502
+ * directly in tests / isolated subsystems where a shared instance would
503
+ * cause cross-test pollution.
504
+ */
505
+ declare function getMessageQueue(): MessageQueue;
506
+ /**
507
+ * Test-only reset hook. Production code MUST NOT call this. Underscored
508
+ * prefix follows the same convention as `_resetInvariantRegistry` /
509
+ * `_resetAdmittedAgentBindings` / `_resetAdmissionMetrics`.
510
+ */
511
+ declare function _resetMessageQueueForTests(): void;
512
+
513
+ /**
514
+ * @kodax-ai/agent/messaging/drain — mid-turn drain decision (FEATURE_115).
515
+ *
516
+ * Mid-turn drain pulls queued messages between tool execution and the
517
+ * next LLM call. Default ceiling is `user` priority — user input
518
+ * interrupts the agent loop; background-priority messages
519
+ * (subagent task-notifications) wait for the next safe boundary.
520
+ *
521
+ * **FEATURE_155 v0.7.39 Slice C2 — yield-tool gate retired.** Before
522
+ * v0.7.39 the drain widened to background priority when the agent had
523
+ * just called `await_child_task` (FEATURE_119 Pattern B). With idle-
524
+ * yield (the default since Slice B1.D), `await_child_task` is gone and
525
+ * the runner's outer loop in
526
+ * `coding/src/task-engine/_internal/managed-task/idle-yield.ts` owns
527
+ * background-priority dequeue at the no-tool-calls exit. The mid-turn
528
+ * drain therefore stays at `user` priority — that's the only path
529
+ * where it makes sense to interrupt the agent without violating the
530
+ * tool_use/tool_result pairing contract. `YIELD_TOOL_NAMES` and the
531
+ * `lastTurnToolNames` parameter remain on the public surface as a
532
+ * placeholder for any FUTURE yield tool (e.g. an explicit `sleep`),
533
+ * but the set is empty so the gate is currently a no-op.
534
+ */
535
+
536
+ /**
537
+ * Tools that gate background-priority drain. Empty under FEATURE_155
538
+ * idle-yield (v0.7.39+) — see file header. Adding an entry should
539
+ * remain a deliberate decision: only tools that semantically
540
+ * represent the agent yielding control should unlock background
541
+ * priority, because background drain can interleave subagent
542
+ * results into the main conversation in unexpected places.
543
+ */
544
+ declare const YIELD_TOOL_NAMES: ReadonlySet<string>;
545
+ interface MaybeDrainMidTurnInput {
546
+ /** Tool names invoked during the most recent iteration's tool_use blocks. */
547
+ readonly lastTurnToolNames: readonly string[];
548
+ /** Current agent's id (`undefined` for the main thread). */
549
+ readonly agentId?: string;
550
+ /** Optional cap on the number of drained messages. */
551
+ readonly limit?: number;
552
+ }
553
+ /**
554
+ * Returns the priority ceiling that mid-turn drain should use given the
555
+ * tools the agent invoked in the most recent iteration.
556
+ *
557
+ * Under FEATURE_155 idle-yield (`YIELD_TOOL_NAMES` empty by default),
558
+ * this always returns `'user'` — background-priority messages are
559
+ * picked up by the runner-driven outer loop's `waitForWakeEvent` at
560
+ * the no-tool-calls exit, not by the mid-turn drain.
561
+ */
562
+ declare function midTurnDrainPriority(lastTurnToolNames: readonly string[]): MessagePriority;
563
+ /**
564
+ * Drain queued messages destined for `agentId` (main-thread by default)
565
+ * up to the priority ceiling decided by Sleep gating. Returns the drained
566
+ * messages in `dequeue` order (priority-first FIFO).
567
+ */
568
+ declare function maybeDrainMidTurn(input: MaybeDrainMidTurnInput): QueuedMessage[];
569
+ interface EnqueueChildTaskNotificationInput {
570
+ /**
571
+ * agentId of the parent / coordinator that should receive the
572
+ * notification. `undefined` targets the main thread.
573
+ */
574
+ readonly parentAgentId?: string;
575
+ /** Stable identifier of the completed child task (e.g. `child-...`). */
576
+ readonly taskId: string;
577
+ /** Human-readable summary appended after the task id banner. */
578
+ readonly summary: string;
579
+ }
580
+ /**
581
+ * Enqueue a `task-notification` message destined for the parent / main
582
+ * thread when a backgrounded child task finishes (FEATURE_119 Pattern B).
583
+ *
584
+ * Goes in at `priority: 'background'`. Under FEATURE_155 idle-yield
585
+ * (v0.7.39+), the runner's outer loop drains background priority at
586
+ * the no-tool-calls exit (`waitForWakeEvent` →
587
+ * `composeIdleYieldUserMessage`), so the notification surfaces in
588
+ * the agent's next-turn user message as a `<task-completed
589
+ * task_id="…">…</task-completed>` block. Pre-v0.7.39 a `yield tool`
590
+ * (`await_child_task`) gated mid-turn drain to background priority —
591
+ * that path is gone with the tool.
592
+ *
593
+ * Returns the enqueued message id (matches `MessageQueue.enqueue` contract).
594
+ */
595
+ declare function enqueueChildTaskNotification(input: EnqueueChildTaskNotificationInput): string;
596
+
597
+ /**
598
+ * Idle-yield primitives — async chat-while-waiting orchestration core.
599
+ *
600
+ * Originally shipped as `packages/coding/src/task-engine/_internal/
601
+ * managed-task/idle-yield.ts` (v0.7.39 Slices A1-C3, FEATURE_155). The
602
+ * v0.7.38 hotfix follow-up chain (Bug A-G) landed inside the same file.
603
+ * v0.7.39 FEATURE_120 Step 0 (this slice) lifts the module to
604
+ * `@kodax-ai/agent`'s `orchestration/` so any agent-flavor consumer
605
+ * outside KodaX's coding stack can reuse the same async fan-out
606
+ * wait-and-resume mechanic (ADR-021).
607
+ *
608
+ * The lifted module is **generic over the child-task result type**
609
+ * (`TChildResult`). Coding's `KodaXChildExecutionResult` shape stays in
610
+ * `@kodax-ai/coding`; only `taskId` + `error` are read here. The
611
+ * `result` field is opaque — `composeIdleYieldUserMessage` never
612
+ * inspects it (the fallback banner uses only `taskId` / error message).
613
+ *
614
+ * Replaces the blocking `await_child_task` semantics with a Claude-Code-
615
+ * style "agent turn ends idle, runner waits for the next external event"
616
+ * mechanism. When the agent has dispatched ≥1 children and has nothing
617
+ * else to do, it outputs a brief status line (no tool calls), and
618
+ * Runner.run returns. This module gives the runner layer the utilities
619
+ * it needs to interpret that exit and resume:
620
+ *
621
+ * 1. `detectIdleYield(...)` — synchronous predicate over the run's exit
622
+ * state. Returns true when the agent turn ended without an
623
+ * `emit_handoff` AND there are still child tasks the agent is
624
+ * expected to wait on. False on every other path so legacy
625
+ * semantics stay untouched.
626
+ *
627
+ * 2. `waitForWakeEvent(...)` — async race between child-task
628
+ * completions and the MessageQueue. Returns the first event so the
629
+ * runner layer knows what to splice into the next-turn context.
630
+ * Cooperative with `AbortSignal` so REPL Esc tears it down promptly.
631
+ *
632
+ * 3. `composeIdleYieldUserMessage(...)` — given a resolved
633
+ * `WakeEvent`, builds the synthetic user message that the runner
634
+ * should splice into the next `Runner.run` input.
635
+ *
636
+ * Bug A-G hotfix invariants preserved verbatim from the v0.7.38
637
+ * release:
638
+ *
639
+ * - Bug B / D / `hasEmittedTerminalVerdict` field: outer loop gates
640
+ * on terminal Evaluator verdict, NOT on legacy
641
+ * `managedProtocolPayloadRef.verdict`. The agent layer carries
642
+ * the boolean as a snapshot field; callers compute it.
643
+ * - Bug E / `hasPendingBackgroundMessages` field: fast-child race
644
+ * recovery — keep the loop alive when either the registry OR the
645
+ * queue still has undelivered work.
646
+ * - Bug F / abort listener cleanup: explicit
647
+ * `removeEventListener` in `settle()` even on non-abort wakes.
648
+ * - Bug A registry cleanup: NOT this module's responsibility — owned
649
+ * by `registerChildTask` in `task-registry.ts`.
650
+ */
651
+
652
+ /**
653
+ * Env-flag gate for the runner outer-loop wiring.
654
+ *
655
+ * **Slice C3 (v0.7.39) — flag retired as a runtime gate.** With
656
+ * `await_child_task` removed (Slice C1) there is no working "v0.7.38
657
+ * emulation" path: the prompt + banner always teach idle-yield, so
658
+ * gating only the outer loop would leave a flag-OFF deployment with
659
+ * agents that exit text-only but no resumer to wake them. The
660
+ * function is therefore hard-coded to `true` and exists only for
661
+ * import compatibility with the Slice A1/A2 callers and
662
+ * historical-test references; the env var has no effect.
663
+ */
664
+ declare function isIdleYieldEnabled(): boolean;
665
+ /**
666
+ * Count the `tool_use` blocks on the last assistant message of a Runner
667
+ * transcript. Used to populate `IdleYieldSnapshot.lastAssistantToolCallCount`
668
+ * — a 0 count is the marker for the no-tool-calls exit branch
669
+ * `Runner.run` uses to terminate its tool loop.
670
+ */
671
+ declare function countLastAssistantToolCalls(messages: readonly KodaXMessage[]): number;
672
+ /** Snapshot of the agent run's exit state, computed by the runner layer. */
673
+ interface IdleYieldSnapshot {
674
+ /**
675
+ * The last assistant message's tool-call count from the Runner.run
676
+ * transcript. Idle-yield is signalled when this is 0 (Runner
677
+ * exited via the no-tool-calls branch, not via a tool-driven
678
+ * handoff).
679
+ */
680
+ readonly lastAssistantToolCallCount: number;
681
+ /**
682
+ * Number of child tasks still in the registry when Runner.run
683
+ * returned. Reads `registry.size` at the boundary. Idle-yield only
684
+ * fires when this is > 0 OR `hasPendingBackgroundMessages` is true
685
+ * — otherwise there's nothing to wait for and the stop is a real
686
+ * terminal event.
687
+ */
688
+ readonly pendingChildTaskCount: number;
689
+ /**
690
+ * True if the run's managed-protocol payload has been populated
691
+ * with a handoff (typically `emit_handoff` for the worker→evaluator
692
+ * boundary). False = the run ended without a handoff. Idle-yield
693
+ * REQUIRES this to be false; otherwise the handoff target already
694
+ * owns the next step.
695
+ */
696
+ readonly hasEmittedHandoff: boolean;
697
+ /**
698
+ * v0.7.38 FEATURE_155 Bug B+D hotfix — true if the run's managed-
699
+ * protocol payload has been populated with a terminal verdict
700
+ * (`accept` / `blocked`; `revise` triggers a chain re-run, not
701
+ * idle-yield continuation). Without this gate the outer loop would
702
+ * keep re-entering `Runner.run` after a terminal verdict — wasting
703
+ * LLM turns on post-verdict child notifications. Idle-yield
704
+ * REQUIRES this to be false; same reasoning as `hasEmittedHandoff`
705
+ * but for the verdict side.
706
+ */
707
+ readonly hasEmittedTerminalVerdict: boolean;
708
+ /**
709
+ * v0.7.38 FEATURE_155 Bug E hotfix — true if the background-priority
710
+ * message queue still has undelivered banners destined for the
711
+ * caller agent. Set this alongside `pendingChildTaskCount` because
712
+ * of the **fast-child race**: the dispatch IIFE may enqueue a
713
+ * notification BEFORE its promise resolves, and the registry's
714
+ * `.finally(delete)` cleanup runs in the same microtask burst.
715
+ * When a child completes faster than the surrounding Runner.run
716
+ * iteration (e.g. a sub-second probe vs a multi-second LLM call),
717
+ * the registry entry is removed BEFORE the outer loop reads
718
+ * `pendingChildTaskCount` — making the snapshot see `0`, breaking
719
+ * the loop, and orphaning the banner in the background queue.
720
+ * With this field, the loop stays in the wait state whenever
721
+ * there's still something to deliver, regardless of which arm
722
+ * (registry or queue) carries it.
723
+ *
724
+ * Drained only by the outer loop's `composeIdleYieldUserMessage`
725
+ * call AFTER `waitForWakeEvent` returns. The mid-turn drain caps
726
+ * at `user` priority post-FEATURE_155, so this is the **only**
727
+ * consumer of background-priority messages — losing it strands
728
+ * the banner.
729
+ */
730
+ readonly hasPendingBackgroundMessages: boolean;
731
+ }
732
+ /**
733
+ * Pure predicate. True when the agent turn ended via the
734
+ * "no tool calls + still has pending children" path that idle-yield
735
+ * is designed to handle.
736
+ *
737
+ * The conjunction terms are deliberately independent — caller can mix
738
+ * in additional gating (e.g. a feature flag) without rewriting this.
739
+ * Returning false here means "treat the run as terminal / delegate to
740
+ * legacy semantics" and is the safe default. The current term set is:
741
+ * `lastAssistantToolCallCount`, `hasEmittedHandoff`,
742
+ * `hasEmittedTerminalVerdict`, and (`pendingChildTaskCount` OR
743
+ * `hasPendingBackgroundMessages`) — the last pair forms the wait-or-
744
+ * resume gate (fast-child race recovery; see field docs).
745
+ */
746
+ declare function detectIdleYield(snapshot: IdleYieldSnapshot): boolean;
747
+ /**
748
+ * FEATURE_167 (v0.7.41) — terminal-verdict-missing predicate.
749
+ *
750
+ * Companion to `detectIdleYield`, gating the OPPOSITE half of the
751
+ * outer-loop exit decision: when the inner Runner.run loop exits
752
+ * with no tool calls AFTER a handoff has fired but BEFORE the
753
+ * Evaluator has emitted a terminal verdict, the run is structurally
754
+ * INCOMPLETE — the audit step is missing. `detectIdleYield` correctly
755
+ * returns false in this state (the gate at line 174 short-circuits on
756
+ * `hasEmittedHandoff`), so the outer loop currently terminates with
757
+ * `recorder.verdict === undefined`. Downstream `deriveFinalStatus`
758
+ * then falls back to `signal:'COMPLETE'`, falsely reporting success
759
+ * for a failed audit (production session 20260515_185354).
760
+ *
761
+ * This predicate identifies that exact missing-verdict state so the
762
+ * outer loop can branch into a retry path (inject a "call emit_verdict"
763
+ * prompt and re-invoke the Evaluator) before falling through to a
764
+ * synthesized fallback verdict.
765
+ *
766
+ * **Disjoint from `detectIdleYield`** by construction:
767
+ *
768
+ * - `detectIdleYield` requires `!hasEmittedHandoff`
769
+ * - `detectMissingTerminalVerdict` requires `hasEmittedHandoff`
770
+ *
771
+ * Both also require `lastAssistantToolCallCount === 0`. Both gate on
772
+ * `!hasEmittedTerminalVerdict`. The two predicates partition the
773
+ * "Evaluator turn just exited text-only" space cleanly — no run can
774
+ * satisfy both.
775
+ *
776
+ * **Pending children take precedence over verdict retry.** Predicate
777
+ * is intentionally `pendingChildTaskCount <= 0 && !hasPendingBackgroundMessages`
778
+ * so that if Evaluator dispatched audit children and is correctly
779
+ * idle-yielding for them (FEATURE_155 Bug C discipline), the
780
+ * verdict-missing retry does NOT fire — `detectIdleYield` handles
781
+ * that path instead. Only when there's nothing left to wait on does
782
+ * the verdict-missing branch take over.
783
+ */
784
+ declare function detectMissingTerminalVerdict(snapshot: IdleYieldSnapshot): boolean;
785
+ /**
786
+ * Discriminated union surfacing the reason a wake completed.
787
+ *
788
+ * Generic over `TChildResult` so coding-flavor consumers can carry
789
+ * their `KodaXChildExecutionResult` shape through `child-completed`
790
+ * wakes without the agent layer naming the type. This module only
791
+ * reads `taskId` (for the fallback banner) and `error` — `result` is
792
+ * opaque pass-through.
793
+ */
794
+ type WakeEvent<TChildResult = unknown> = {
795
+ readonly kind: 'child-completed';
796
+ readonly taskId: string;
797
+ readonly result: TChildResult;
798
+ } | {
799
+ readonly kind: 'child-failed';
800
+ readonly taskId: string;
801
+ readonly error: Error;
802
+ } | {
803
+ readonly kind: 'messages-arrived';
804
+ readonly messages: readonly QueuedMessage[];
805
+ } | {
806
+ readonly kind: 'aborted';
807
+ };
808
+ interface WaitForWakeEventOptions<TChildResult = unknown> {
809
+ /**
810
+ * Live ChildTaskRegistry snapshot. The waiter wraps each entry's
811
+ * promise so the FIRST settling child wins the race.
812
+ *
813
+ * **NOTE**: the waiter does NOT delete entries on settlement — the
814
+ * registry's normal cleanup path (`registerChildTask`'s built-in
815
+ * `.finally` chain) owns deletion. Wrapping doesn't double-consume.
816
+ */
817
+ readonly registry: ChildTaskRegistry<TChildResult>;
818
+ /** Process-global message queue surface (FEATURE_115 substrate). */
819
+ readonly messageQueue: MessageQueue;
820
+ /**
821
+ * AgentId filter for queue dequeues. Use `undefined` to match
822
+ * main-thread messages (the standard queue scope).
823
+ */
824
+ readonly agentId: string | undefined;
825
+ /**
826
+ * Optional cancellation. When fired, the waiter resolves with
827
+ * `{ kind: 'aborted' }` and tears down its poll timer.
828
+ */
829
+ readonly abortSignal?: AbortSignal;
830
+ /**
831
+ * Queue poll interval. The MessageQueue is poll-based; this is the
832
+ * granularity at which user input becomes visible to the waiter.
833
+ * 100 ms keeps perceived REPL responsiveness < 1 frame at 60 fps
834
+ * for the human eye and stays well below typical LLM-call latency.
835
+ * Tests can pass smaller values (e.g. 10 ms) to keep them fast.
836
+ */
837
+ readonly pollIntervalMs?: number;
838
+ }
839
+ /**
840
+ * Race child completions against MessageQueue arrivals. Returns the
841
+ * first wake event. Guarantees:
842
+ *
843
+ * - Cleanup: the poll timer is cleared on resolution regardless of
844
+ * which arm wins.
845
+ * - At-most-once dequeue: when the queue arm wins, the messages it
846
+ * drained are returned to the caller AND removed from the queue
847
+ * (the caller is now responsible for splicing them into the
848
+ * agent's next-turn context).
849
+ * - Abort-safe: if `abortSignal` fires before any other event, the
850
+ * waiter resolves with `{ kind: 'aborted' }`. Already-settled
851
+ * child promises are NOT cancelled — the registry's owner handles
852
+ * them on the next turn.
853
+ *
854
+ * Caller responsibilities:
855
+ * - Pass the EXACT registry snapshot (not a copy) so subsequent
856
+ * dispatches the agent performs after wake are visible to the
857
+ * next `waitForWakeEvent` call.
858
+ * - Splice the returned messages / child result into the agent's
859
+ * next Runner.run input. The waiter does not itself construct
860
+ * synthetic user-message bytes — that's the runner-layer's job.
861
+ */
862
+ declare function waitForWakeEvent<TChildResult = unknown>(options: WaitForWakeEventOptions<TChildResult>): Promise<WakeEvent<TChildResult>>;
863
+ /**
864
+ * Compose the synthetic user message spliced after an agent idle-yield
865
+ * resume. The runner outer loop calls this with the resolved
866
+ * `WakeEvent` plus a function that drains any pending background
867
+ * messages (typically `() => getMessageQueue().dequeue(...)`).
868
+ *
869
+ * - `messages-arrived` wake: the queue arm already drained the
870
+ * QueuedMessage(s); pass their content through verbatim. Then
871
+ * drain anything that arrived between settle and this call (a
872
+ * tight race with the dispatch handler's enqueue is possible) and
873
+ * concatenate.
874
+ *
875
+ * - `child-completed` / `child-failed` wake: the dispatch handler's
876
+ * in-IIFE `enqueueChildTaskNotification` is a precondition of the
877
+ * promise settling, so the queue holds the canonical
878
+ * `<task-completed>` banner. Drain to capture it. If for any
879
+ * reason the banner is missing (defensive — a misbehaving
880
+ * dispatch path that resolved without enqueuing), synthesize a
881
+ * minimal one from the wake event so the agent still observes
882
+ * the resolution rather than silently looping again.
883
+ *
884
+ * - `aborted`: caller is expected to break out before reaching this
885
+ * helper. If reached anyway, returns `undefined` so the outer
886
+ * loop treats it as a terminal exit.
887
+ *
888
+ * Returns `undefined` when the wake yields no spliceable content —
889
+ * the outer loop treats that as a real terminal exit.
890
+ */
891
+ /**
892
+ * FEATURE_121 (v0.7.40) — Envelope aggregate budget enforcer.
893
+ *
894
+ * Pure `string[] → string[]` transform applied to drained envelope
895
+ * fragments before they're joined into a single synthetic user
896
+ * message. The coding layer provides the implementation that knows
897
+ * how to spill oversized fragments to disk and replace them with
898
+ * preview + path markers (see `@kodax-ai/coding`
899
+ * `createEnvelopeAggregateBudgetEnforcer`). The agent layer only
900
+ * sees this opaque function type — **no `@kodax-ai/coding` symbols
901
+ * may leak into this signature**, otherwise ADR-021 layer
902
+ * independence breaks and `@kodax/agent` cannot build standalone.
903
+ */
904
+ type EnvelopeAggregateEnforcer = (fragments: readonly string[]) => readonly string[] | Promise<readonly string[]>;
905
+ /**
906
+ * FEATURE_159 (v0.7.40) — mode-split synthetic.
907
+ *
908
+ * Pre-FEATURE_159: every wake-drained message was wrapped in a single
909
+ * `_synthetic: true` user message. This was correct for child-task
910
+ * notifications (the agent needs to see them; the human doesn't), but
911
+ * WRONG for user-typed prompts that arrived via chat-while-waiting —
912
+ * those got hidden from the transcript so users couldn't see their own
913
+ * messages echoed in the conversation history.
914
+ *
915
+ * Post-FEATURE_159: fragments are partitioned by `msg.mode`. Two
916
+ * separate messages may be emitted:
917
+ * 1. Synthetic banner (`_synthetic: true`) — concatenates
918
+ * `task-notification` + `system-reminder` content. Hidden from
919
+ * REPL display; the agent sees it as context. Spliced FIRST so it
920
+ * reads as the "tail of the prior turn" before the new prompt.
921
+ * 2. Real user message (no `_synthetic`) — concatenates `prompt`
922
+ * content from chat-while-waiting. Renders as a normal user
923
+ * bubble in transcript. Spliced AFTER the banner so the chain
924
+ * reads naturally as "previous turn outputs → user follow-up".
925
+ *
926
+ * Aggregate budget enforcer applies ONLY to the synthetic banner
927
+ * fragments (the side that can carry child-task envelopes of arbitrary
928
+ * size). User prompts pass through unchanged — the user's intent must
929
+ * never be silently truncated.
930
+ *
931
+ * Return type changed from `KodaXMessage | undefined` to
932
+ * `readonly KodaXMessage[]` (possibly empty). Callers must spread the
933
+ * result into their next-iteration input.
934
+ */
935
+ declare function composeIdleYieldUserMessage<TChildResult = unknown>(wakeEvent: WakeEvent<TChildResult>, drainBackgroundQueue: () => readonly QueuedMessage[], enforceAggregate?: EnvelopeAggregateEnforcer): Promise<readonly KodaXMessage[]>;
936
+
937
+ /**
938
+ * Generic outer loop for async fan-out + idle-yield resume.
939
+ *
940
+ * FEATURE_120 v0.7.39 Step 0c. Wraps the `while (true) { Runner.run; if
941
+ * (detectIdleYield) waitForWakeEvent + compose + resume; else break }`
942
+ * control flow so that any agent flavor consuming `@kodax-ai/agent` as
943
+ * a standalone framework can adopt the FEATURE_155 chat-while-waiting
944
+ * pattern without re-implementing the loop and re-discovering the
945
+ * v0.7.38 Bug A-G hotfix invariants.
946
+ *
947
+ * The wrapper does NOT take ownership of any agent-flavor-specific
948
+ * state — recorder access, role mapping, status-bar emission,
949
+ * checkpoint cleanup, chain-reset-on-resume — those flow in through
950
+ * callbacks. The wrapper owns only the universal pieces:
951
+ *
952
+ * 1. Iteration cap (default 64) — defensive against prompt bugs.
953
+ * 2. Per-iteration `runOnce` invocation (callee owns Runner.run
954
+ * options closure + error-path cleanup chain).
955
+ * 3. Snapshot computation via callback, then `detectIdleYield` gate.
956
+ * 4. Optional `onIdleWaiting` hook fired AFTER the gate passes but
957
+ * BEFORE the wake wait.
958
+ * 5. `waitForWakeEvent` with the registry / queue / abort plumbing.
959
+ * 6. `composeIdleYieldUserMessage` to splice synthetic input.
960
+ * 7. `resumeAgent` callback to pick the agent for the next iteration.
961
+ *
962
+ * Order is significant — see Risk R4 in v0.7.39 Phase 1c design notes.
963
+ * Tests in `runner-with-idle-yield.test.ts` pin every boundary.
964
+ *
965
+ * The wrapper has zero inbound dependency on coding-flavor types; all
966
+ * agent-flavor specifics flow through `TRunResult` / `TChildResult` /
967
+ * the callbacks (ADR-021).
968
+ */
969
+
970
+ /** Default iteration cap matches the legacy `IDLE_YIELD_MAX_ITERATIONS` constant. */
971
+ declare const DEFAULT_IDLE_YIELD_MAX_ITERATIONS = 64;
972
+ /**
973
+ * Minimal shape requirement on the run result the caller's `runOnce`
974
+ * returns. The wrapper reads `messages` to replay the transcript into
975
+ * the next iteration after a wake; everything else on the result is
976
+ * opaque pass-through.
977
+ */
978
+ interface RunWithIdleYieldRunResult {
979
+ readonly messages: readonly AgentMessage[];
980
+ }
981
+ interface RunWithIdleYieldOptions<TRunResult extends RunWithIdleYieldRunResult, TChildResult> {
982
+ /** Agent that executes the first iteration of the loop. */
983
+ readonly initialAgent: Agent;
984
+ /** Initial input messages for the first `Runner.run`. */
985
+ readonly initialInput: readonly AgentMessage[];
986
+ /**
987
+ * Per-iteration Runner invocation. Caller closes over its Runner
988
+ * options (llm, guardrails, toolObserver, maxToolLoopIterations,
989
+ * abortSignal, compactionHook, error-path cleanup). The wrapper
990
+ * passes the current `agent` and `input` and awaits the result.
991
+ *
992
+ * **Error-path note**: if `runOnce` rejects, the rejection
993
+ * propagates out of `runWithIdleYield` verbatim — wrapper does not
994
+ * swallow. Caller's `.catch` (if any) must run inside the
995
+ * `runOnce` closure to keep cleanup ordering observable.
996
+ */
997
+ readonly runOnce: (agent: Agent, input: readonly AgentMessage[]) => Promise<TRunResult>;
998
+ /**
999
+ * Compute the `IdleYieldSnapshot` after each `runOnce` returns.
1000
+ * Caller has access to the full run result + any external state
1001
+ * via closure (recorder, registry size, queue inspection).
1002
+ */
1003
+ readonly computeSnapshot: (runResult: TRunResult) => IdleYieldSnapshot;
1004
+ /**
1005
+ * Live child-task registry. Passed verbatim to `waitForWakeEvent`'s
1006
+ * `registry` arm. Mutations to this map between iterations are
1007
+ * visible to the next wake.
1008
+ */
1009
+ readonly registry: ChildTaskRegistry<TChildResult>;
1010
+ /** Process-global message queue. Passed verbatim to `waitForWakeEvent`. */
1011
+ readonly messageQueue: MessageQueue;
1012
+ /**
1013
+ * AgentId scope for the queue arm. `undefined` matches main-thread
1014
+ * messages (the default scope a parent agent's dispatch handler
1015
+ * targets).
1016
+ */
1017
+ readonly agentId: string | undefined;
1018
+ /** Optional cancellation. Tears the loop down on the next wait boundary. */
1019
+ readonly abortSignal?: AbortSignal;
1020
+ /**
1021
+ * Choose the agent for the next iteration after a wake. Caller has
1022
+ * access to the just-finished run result; coding-flavor consumers
1023
+ * typically return their `chain.worker` regardless of the result.
1024
+ */
1025
+ readonly resumeAgent: (runResult: TRunResult) => Agent;
1026
+ /**
1027
+ * Optional hook fired AFTER `detectIdleYield` returns true but BEFORE
1028
+ * `waitForWakeEvent` parks. Used by coding for FEATURE_156 status-bar
1029
+ * emission. The `currentAgent` arg is the agent that just ran (so
1030
+ * role lookups read the right name on every iteration).
1031
+ */
1032
+ readonly onIdleWaiting?: (currentAgent: Agent, runResult: TRunResult) => void;
1033
+ /**
1034
+ * Optional hook fired when the iteration cap is hit. Coding does not
1035
+ * currently log here (matches v0.7.38 behavior — silent break) but
1036
+ * the hook exists so SDK consumers can record the prompt-bug signal.
1037
+ */
1038
+ readonly onIterationCap?: () => void;
1039
+ /**
1040
+ * Defensive ceiling on outer-loop iterations. Default 64 matches the
1041
+ * legacy `IDLE_YIELD_MAX_ITERATIONS` constant. Set lower in tests to
1042
+ * exercise the cap branch quickly.
1043
+ */
1044
+ readonly maxIterations?: number;
1045
+ /**
1046
+ * FEATURE_121 (v0.7.40): optional aggregate budget enforcer for the
1047
+ * synthetic user message built from drained background banners. When
1048
+ * provided, it transforms the fragment array before they're joined
1049
+ * (per-banner per-`<task-completed>` summary chunks remain unchanged
1050
+ * by this wrapper — that's the caller's responsibility at enqueue
1051
+ * time via `applyToolResultGuardrail`). The aggregate hook only kicks
1052
+ * in when N banners' total exceeds the limit set by the coding-layer
1053
+ * implementation (default 200KB per claudecode parity).
1054
+ *
1055
+ * Type is `string[] → string[] | Promise<string[]>` so the agent
1056
+ * layer carries no `@kodax-ai/coding` symbols. See
1057
+ * `EnvelopeAggregateEnforcer` in idle-yield.ts.
1058
+ */
1059
+ readonly envelopeAggregateEnforcer?: EnvelopeAggregateEnforcer;
1060
+ }
1061
+ /**
1062
+ * Run the agent chain with idle-yield resume.
1063
+ *
1064
+ * On each iteration:
1065
+ * 1. Invoke `runOnce(currentAgent, currentInput)`.
1066
+ * 2. Increment the iteration counter. If it exceeds
1067
+ * `maxIterations`, call `onIterationCap?.()` and break.
1068
+ * 3. Compute the snapshot via `computeSnapshot(runResult)`.
1069
+ * 4. If `detectIdleYield(snapshot)` is false (run terminal or
1070
+ * handoff/verdict already emitted), break.
1071
+ * 5. Fire `onIdleWaiting?.(currentAgent, runResult)`.
1072
+ * 6. Park in `waitForWakeEvent({registry, messageQueue, agentId, abortSignal})`.
1073
+ * 7. If wake is `aborted`, break.
1074
+ * 8. Build synthetic user message via `composeIdleYieldUserMessage`.
1075
+ * If undefined (truly empty wake), break.
1076
+ * 9. Replay: `currentInput = [...runResult.messages, syntheticUserMessage]`,
1077
+ * `currentAgent = resumeAgent(runResult)`, loop.
1078
+ *
1079
+ * Returns the last `runResult` (the one that broke the loop).
1080
+ *
1081
+ * Bug A-G hotfix invariants preserved:
1082
+ * - Bug A (registry cleanup): owned by `registerChildTask` —
1083
+ * unaffected by this wrapper.
1084
+ * - Bug B/D (terminal-verdict + handoff gates): caller's
1085
+ * `computeSnapshot` must populate `hasEmittedTerminalVerdict`
1086
+ * and `hasEmittedHandoff` from the canonical recorder source.
1087
+ * - Bug E (fast-child race): caller's `computeSnapshot` must read
1088
+ * `hasPendingBackgroundMessages` alongside the registry size.
1089
+ * - Bug F (abort listener leak): owned by `waitForWakeEvent` —
1090
+ * unaffected by this wrapper.
1091
+ */
1092
+ declare function runWithIdleYield<TRunResult extends RunWithIdleYieldRunResult, TChildResult>(opts: RunWithIdleYieldOptions<TRunResult, TChildResult>): Promise<TRunResult>;
1093
+
1094
+ /**
1095
+ * Generic concurrency-bounded fan-out — `runFanOut`.
1096
+ *
1097
+ * FEATURE_120 v0.7.39 Step 0d (Option D scope). Lifts the truly
1098
+ * agent-flavor-agnostic part of `@kodax-ai/coding`'s `executeChildAgents`
1099
+ * orchestrator: bounded-concurrency `Promise.allSettled` + abort-pre-
1100
+ * check + structured progress events + cancelled-bundles tracking.
1101
+ *
1102
+ * What stays at `@kodax-ai/coding`'s `executeChildAgents`:
1103
+ * - `KodaXChildContextBundle` / `KodaXChildExecutionResult` shapes
1104
+ * - read vs write child differentiation
1105
+ * - `validateWriteBundles` role policy
1106
+ * - worktree isolation + cleanup
1107
+ * - briefing + `CHILD_AGENT_SYSTEM_PROMPT` injection
1108
+ *
1109
+ * What lives here:
1110
+ * - Bounded concurrency via private semaphore
1111
+ * - Pre-execution abort check (cancelled bundles collected)
1112
+ * - Promise.allSettled-style rejection capture
1113
+ * - Per-bundle structured progress events (`start` / `item-done` /
1114
+ * `item-failed`) with stable bundle reference + completed/total
1115
+ * counter via a second context argument
1116
+ *
1117
+ * The wrapper has zero inbound dependency on coding-flavor types —
1118
+ * `TBundle` and `TResult` are fully opaque to the module (ADR-021).
1119
+ */
1120
+ /** Discriminated union for `onProgress` event payloads. */
1121
+ type FanOutProgressEvent<TBundle, TResult> = {
1122
+ readonly kind: 'start';
1123
+ readonly bundle: TBundle;
1124
+ readonly bundleIndex: number;
1125
+ } | {
1126
+ readonly kind: 'item-done';
1127
+ readonly bundle: TBundle;
1128
+ readonly bundleIndex: number;
1129
+ readonly result: TResult;
1130
+ } | {
1131
+ readonly kind: 'item-failed';
1132
+ readonly bundle: TBundle;
1133
+ readonly bundleIndex: number;
1134
+ readonly error: Error;
1135
+ };
1136
+ interface RunFanOutOptions<TBundle, TResult> {
1137
+ /**
1138
+ * The bundles to execute. Order is preserved as the canonical
1139
+ * "bundle index" for progress events; result order is **completion
1140
+ * order**, not bundle order (mirror callers use the bundle reference
1141
+ * carried in each event or each result to attribute outcomes).
1142
+ */
1143
+ readonly bundles: readonly TBundle[];
1144
+ /**
1145
+ * Per-bundle executor. The wrapper invokes this after acquiring a
1146
+ * concurrency slot AND after passing the abort pre-check. If the
1147
+ * promise rejects, the rejection is captured in the result
1148
+ * `{status: 'rejected', bundle, reason}` and onProgress's
1149
+ * `item-failed` event fires; otherwise `{status: 'fulfilled', bundle,
1150
+ * value}` and `item-done` fires.
1151
+ */
1152
+ readonly runOne: (bundle: TBundle) => Promise<TResult>;
1153
+ /**
1154
+ * Maximum concurrent in-flight `runOne` invocations. Must be ≥ 1;
1155
+ * a value of 1 collapses the fan-out to strict serial execution.
1156
+ */
1157
+ readonly maxParallel: number;
1158
+ /**
1159
+ * Optional cancellation signal. Checked AFTER each semaphore acquire
1160
+ * but BEFORE invoking `runOne`. Bundles whose abort check fires are
1161
+ * added to `cancelled` and never see their `runOne` call. In-flight
1162
+ * `runOne` invocations are NOT interrupted — abort acts as a "stop
1163
+ * scheduling new work" signal.
1164
+ */
1165
+ readonly abortSignal?: AbortSignal;
1166
+ /**
1167
+ * Optional progress hook. The first argument is the event; the
1168
+ * second is a shared snapshot of `{completedCount, totalCount}`
1169
+ * captured at the moment the event fires (so callers don't need
1170
+ * to maintain their own counter).
1171
+ */
1172
+ readonly onProgress?: (event: FanOutProgressEvent<TBundle, TResult>, ctx: {
1173
+ readonly completedCount: number;
1174
+ readonly totalCount: number;
1175
+ }) => void;
1176
+ }
1177
+ /**
1178
+ * Per-bundle outcome carried in the final result. Mirrors
1179
+ * `PromiseSettledResult` but adds the originating `bundle` for easy
1180
+ * mapping back to caller-owned state.
1181
+ */
1182
+ type FanOutOutcome<TBundle, TResult> = {
1183
+ readonly status: 'fulfilled';
1184
+ readonly bundle: TBundle;
1185
+ readonly value: TResult;
1186
+ } | {
1187
+ readonly status: 'rejected';
1188
+ readonly bundle: TBundle;
1189
+ readonly reason: Error;
1190
+ };
1191
+ interface RunFanOutResult<TBundle, TResult> {
1192
+ /**
1193
+ * Per-bundle outcomes in **completion order** (NOT input bundle
1194
+ * order). Use the embedded `bundle` reference to map outcomes back
1195
+ * to caller state — index-based access is order-fragile.
1196
+ */
1197
+ readonly results: ReadonlyArray<FanOutOutcome<TBundle, TResult>>;
1198
+ /**
1199
+ * Bundles that were skipped due to abort firing before their
1200
+ * `runOne` call. Preserves input bundle reference so callers can
1201
+ * map back to their own per-bundle state.
1202
+ */
1203
+ readonly cancelled: readonly TBundle[];
1204
+ }
1205
+ /**
1206
+ * Run `runOne` over each bundle with bounded concurrency. Returns
1207
+ * after every bundle has either settled (`results`) or been skipped
1208
+ * due to abort (`cancelled`).
1209
+ *
1210
+ * Behavior:
1211
+ * - Empty `bundles` → returns `{results: [], cancelled: []}`
1212
+ * immediately without firing any events.
1213
+ * - `maxParallel ≥ bundles.length` collapses to native
1214
+ * `Promise.allSettled` semantics (no semaphore contention).
1215
+ * - `maxParallel === 1` collapses to strict serial execution.
1216
+ * - Each successful `runOne` fires `onProgress({kind: 'item-done',
1217
+ * ..., result})` AFTER `completedCount` is incremented (so the
1218
+ * `ctx.completedCount` in the event reflects "including this
1219
+ * one").
1220
+ * - Each failed `runOne` fires `onProgress({kind: 'item-failed',
1221
+ * ..., error})` with `completedCount` updated the same way —
1222
+ * the counter counts settled items regardless of success.
1223
+ * - Abort firing AFTER a bundle has entered `runOne` does NOT
1224
+ * cancel that bundle's promise — it completes normally and
1225
+ * joins `results`. Only bundles still waiting for a semaphore
1226
+ * slot (or those whose semaphore-acquired callback runs after
1227
+ * abort) get marked cancelled.
1228
+ */
1229
+ declare function runFanOut<TBundle, TResult>(opts: RunFanOutOptions<TBundle, TResult>): Promise<RunFanOutResult<TBundle, TResult>>;
1230
+
1231
+ /**
1232
+ * Generic cross-agent send-message router primitive.
1233
+ *
1234
+ * FEATURE_120 v0.7.39 Phase 2a (ADR-021). Coordinator-style agents need
1235
+ * to dispatch instructions to running peer/child agents whose execution
1236
+ * is gated by an agentId-scoped `MessageQueue`. This module owns the
1237
+ * minimal "validate target exists, enqueue addressed message, report
1238
+ * outcome" trio so the @kodax-ai/agent package can ship the substrate
1239
+ * without inheriting any coding-flavor framing.
1240
+ *
1241
+ * Caller responsibilities (NOT done here):
1242
+ * - Content framing (e.g., `<coordinator-instruction>` tag wrapping)
1243
+ * — this is a tool-layer / flavor concern.
1244
+ * - Priority choice — the caller decides whether a route is
1245
+ * `'user'` (interrupts) or `'background'` (queued for next yield).
1246
+ * - Mode choice — `'prompt'` for new instructions,
1247
+ * `'system-reminder'` for advisory metadata, etc.
1248
+ * - Sentinel target handling (e.g., broadcast `'*'`) — the router
1249
+ * rejects them as `unknown-target` because they aren't registry
1250
+ * keys; callers can intercept upstream if they need fan-out
1251
+ * semantics.
1252
+ *
1253
+ * What this primitive owns:
1254
+ * - Existence check against a `ReadonlyMap<string, unknown>`
1255
+ * registry — the value type is opaque; we only care about key
1256
+ * membership.
1257
+ * - Enqueue via `MessageQueue.enqueue` with `agentId` set to `to`.
1258
+ * - Structured result so callers can render success / error UX
1259
+ * without re-parsing strings.
1260
+ */
1261
+
1262
+ interface RouteMessageOptions {
1263
+ /** Target agentId. Must exist as a key in `registry`. */
1264
+ readonly to: string;
1265
+ /** Priority class — `'user'` interrupts, `'background'` waits. */
1266
+ readonly priority: MessagePriority;
1267
+ /** Message mode — caller selects based on intent. */
1268
+ readonly mode: MessageMode;
1269
+ /**
1270
+ * Pre-formatted message body. The router does NOT wrap this — any
1271
+ * framing (e.g. tags) must be applied by the caller before invoking.
1272
+ */
1273
+ readonly content: string;
1274
+ /**
1275
+ * Registry the target must appear in. The value type is opaque; the
1276
+ * router only calls `.has(to)`. Pass a `ChildTaskRegistry<T>` for the
1277
+ * standard child-task case, or any other `ReadonlyMap<string, ?>`.
1278
+ */
1279
+ readonly registry: ReadonlyMap<string, unknown>;
1280
+ /** Queue to enqueue into when the target is known. */
1281
+ readonly queue: MessageQueue;
1282
+ }
1283
+ type RouteMessageResult = {
1284
+ readonly ok: true;
1285
+ readonly messageId: string;
1286
+ } | {
1287
+ readonly ok: false;
1288
+ readonly reason: 'unknown-target';
1289
+ readonly to: string;
1290
+ };
1291
+ /**
1292
+ * Validate that `to` is registered, then enqueue an addressed message.
1293
+ *
1294
+ * Returns `{ok: true, messageId}` on success; `{ok: false, reason:
1295
+ * 'unknown-target', to}` when the target is missing from `registry`.
1296
+ * On failure the queue is not mutated.
1297
+ *
1298
+ * Synchronous because `MessageQueue.enqueue` is synchronous — the
1299
+ * routing decision is a pure function of registry membership.
1300
+ */
1301
+ declare function routeMessage(opts: RouteMessageOptions): RouteMessageResult;
1302
+
1303
+ /**
1304
+ * FEATURE_125 (v0.7.41) — Team Mode Layer 2b: system-prompt injection.
1305
+ *
1306
+ * Renders the `=== Other active KodaX sessions ===` block that the
1307
+ * coding-side system-prompt builder splices into the worker system
1308
+ * prompt when sibling instances are alive. Pure formatter — no fs,
1309
+ * no clock, no globals.
1310
+ *
1311
+ * Contract:
1312
+ * - Input: a list of `DiscoveredInstance` (from S2) + `nowMs`.
1313
+ * - Output: a string block (or empty string when input is empty).
1314
+ * - When the list is empty, returns `''` so the caller can splice
1315
+ * unconditionally without an `if (block)` branch.
1316
+ * - When the list is non-empty, the rendered block is bookended by
1317
+ * a single header line and a single coordination-guidance paragraph
1318
+ * so the LLM has clear context for the data it just received.
1319
+ *
1320
+ * The block deliberately does NOT prescribe a specific action (no
1321
+ * "you MUST avoid editing X"); it surfaces context and lets the LLM
1322
+ * self-decide per the FEATURE_125 LLM-First philosophy. The prompt
1323
+ * eval at S7 verifies LLM behavior under this block.
1324
+ */
1325
+
1326
+ interface RenderOptions {
1327
+ /**
1328
+ * Defaults to `Date.now()`. Tests pass a controllable clock.
1329
+ * Affects only the "(started N min ago)" / "(M min ago)" relative
1330
+ * timestamps; absolute fields are passed through verbatim.
1331
+ */
1332
+ readonly nowMs?: number;
1333
+ /**
1334
+ * Maximum number of sibling instances to render. Defaults to 5 —
1335
+ * if a user has >5 sessions open we summarize the rest with a
1336
+ * "+N more" hint rather than blowing up the system prompt. Per
1337
+ * EVAL_GUIDELINES, prompt context budget is finite; cap before
1338
+ * the LLM-call site (not after).
1339
+ */
1340
+ readonly maxRendered?: number;
1341
+ /**
1342
+ * Cap on `recentlyModifiedFiles` per peer. Defaults to 3 — keeps
1343
+ * the block scannable when one peer has touched many files.
1344
+ */
1345
+ readonly maxRecentFilesPerPeer?: number;
1346
+ }
1347
+ /**
1348
+ * Build the system-prompt block describing currently-active sibling
1349
+ * KodaX sessions. Pure function — same inputs always produce the same
1350
+ * output.
1351
+ *
1352
+ * Returns `''` (empty string) when `instances.length === 0`. Callers
1353
+ * splice with template-literal concatenation; the empty-string default
1354
+ * means no conditional is needed.
1355
+ */
1356
+ declare function buildOtherInstancesPromptBlock(instances: readonly DiscoveredInstance[], options?: RenderOptions): string;
1357
+
1358
+ /**
1359
+ * FEATURE_125 (v0.7.41) — Team Mode bootstrap helper.
1360
+ *
1361
+ * One-call entry point invoked from the REPL bootstrap (`kodax_cli.ts`
1362
+ * / `repl.ts`) once per process. Wires the four moving parts:
1363
+ *
1364
+ * 1. Reaps stale instance directories left by crashed peers
1365
+ * (acceptance criterion 7 in v0.7.41.md).
1366
+ * 2. Constructs the `StateWriter` (S1) using sensible defaults.
1367
+ * 3. Registers the writer in the process-level singleton (S3
1368
+ * consumers + tool-result paths use `getActiveTeamModeWriter`).
1369
+ * 4. Returns a small handle exposing the writer, a sibling-snapshot
1370
+ * function (used by the runner-driven adapter once per LLM
1371
+ * round), and an idempotent `shutdown()`.
1372
+ *
1373
+ * Disabled via `KODAX_DISABLE_MULTI_INSTANCE=1` — the helper returns
1374
+ * `null` and the singleton stays empty so every caller silently falls
1375
+ * back to solo-mode behavior. The env var is the project-spec escape
1376
+ * hatch for failure diagnosis; do NOT use it as a daily-driver opt-out.
1377
+ *
1378
+ * DI-clean: optional `fs`, `clock`, `instancesRoot` overrides so tests
1379
+ * can exercise the full lifecycle without touching `~/.kodax`.
1380
+ */
1381
+
1382
+ interface TeamModeBootstrapOptions {
1383
+ /** Static session meta. Required because cwd / startedAt are essential. */
1384
+ readonly meta: SessionMeta;
1385
+ /**
1386
+ * Initial published state. Defaults to `{ agentPhase: 'idle' }` —
1387
+ * callers typically update this immediately when work begins.
1388
+ */
1389
+ readonly initialState?: SessionStateSnapshot;
1390
+ /**
1391
+ * pid override; defaults to `process.pid`. Tests inject a stable
1392
+ * pid; production never passes this.
1393
+ */
1394
+ readonly pid?: number;
1395
+ /**
1396
+ * Override the instances root directory. Defaults to
1397
+ * `getAgentConfigPath('instances')` via the underlying
1398
+ * `createStateWriter` / `discoverInstances`. Tests pass a temp dir.
1399
+ */
1400
+ readonly instancesRoot?: string;
1401
+ /** Inject an in-memory fs for both the writer and the discovery scan. Tests only. */
1402
+ readonly fs?: StateWriterFs & InstanceDiscoveryFs;
1403
+ readonly clock?: () => number;
1404
+ readonly heartbeatIntervalMs?: number;
1405
+ /**
1406
+ * Pass `false` to skip the initial reap of stale peer directories.
1407
+ * Defaults to `true` — every session that bootstraps Team Mode is
1408
+ * also a candidate cleaner for crashed peers.
1409
+ */
1410
+ readonly reapStaleOnStart?: boolean;
1411
+ /** Forwarded to discoverInstances for per-instance failure logs. */
1412
+ readonly logger?: (message: string) => void;
1413
+ }
1414
+ interface TeamModeHandle {
1415
+ readonly writer: StateWriter;
1416
+ /**
1417
+ * Get the current sibling snapshot. Cheap (a single readdir + N
1418
+ * stat). Caller wires this into the runner-driven LLM-call prefix
1419
+ * so each round sees a fresh sibling list — Team Mode block is
1420
+ * intentionally NOT cached in the stable system-prompt prefix.
1421
+ */
1422
+ discoverSiblings(): DiscoveredInstance[];
1423
+ /**
1424
+ * Tear down the writer + clear the singleton. Idempotent. Safe to
1425
+ * call from a process-exit handler, an Ink unmount, or an explicit
1426
+ * `/exit` slash command.
1427
+ */
1428
+ shutdown(): Promise<void>;
1429
+ }
1430
+ declare function bootstrapTeamMode(options: TeamModeBootstrapOptions): TeamModeHandle | null;
1431
+
1432
+ /**
1433
+ * FEATURE_125 (v0.7.41) — Process-level singleton for Team Mode.
1434
+ *
1435
+ * One KodaX process owns at most one `StateWriter` instance. Consumers
1436
+ * scattered across the codebase (REPL bootstrap, tool execution
1437
+ * contexts, runner-driven adapter, todo store) need a shared handle
1438
+ * without threading the writer through every signature. This module
1439
+ * is the canonical accessor:
1440
+ *
1441
+ * - `setActiveTeamModeWriter(writer)` — REPL bootstrap calls this
1442
+ * once after `createStateWriter`.
1443
+ * - `getActiveTeamModeWriter()` — any module asks the singleton for
1444
+ * the live writer (or null when Team Mode is disabled / hasn't
1445
+ * bootstrapped yet).
1446
+ * - `updateActiveTeamMode(patch)` — convenience for "I want to
1447
+ * bump current_intent / active_files; do nothing if no writer".
1448
+ *
1449
+ * Mirrors the `activeExtensionRuntime` singleton pattern in
1450
+ * `packages/coding/src/extensions/runtime.ts` so familiarity is
1451
+ * preserved.
1452
+ */
1453
+
1454
+ declare function setActiveTeamModeWriter(writer: StateWriter | null): void;
1455
+ declare function getActiveTeamModeWriter(): StateWriter | null;
1456
+ /**
1457
+ * Convenience: if a writer is active, merge `patch` into its state +
1458
+ * flush. No-op when Team Mode is disabled (no writer was bootstrapped,
1459
+ * or `KODAX_DISABLE_MULTI_INSTANCE=1` short-circuited the bootstrap).
1460
+ */
1461
+ declare function updateActiveTeamMode(patch: Partial<SessionStateSnapshot>): void;
1462
+
1463
+ export { Agent, AgentManifest, AgentMessage, CORE_INVARIANTS, ChildTaskRegistry, DEFAULT_IDLE_YIELD_MAX_ITERATIONS, DequeueFilter, DiscoveredInstance, EnqueueInput, Handoff, InstanceDiscoveryFs, InvariantId, KodaXMessage, ManifestPatch, MessageMode, MessagePriority, MessageQueue, QualityInvariant, QueuedMessage, RunnerToolCall, RunnerToolResult, SessionMeta, SessionStateSnapshot, StateWriter, StateWriterFs, YIELD_TOOL_NAMES, _resetAdmissionMetrics, _resetInvariantRegistry, _resetMessageQueueForTests, applyManifestPatch, bootstrapTeamMode, buildOtherInstancesPromptBlock, composeIdleYieldUserMessage, composePatches, countLastAssistantToolCalls, detectHandoffSignal, detectIdleYield, detectMissingTerminalVerdict, emitHandoffSpan, enqueueChildTaskNotification, evidenceTrail, finalOwner, getActiveTeamModeWriter, getAdmissionMetricsSnapshot, getAgentConfigHome, getAgentConfigPath, getInvariant, getMessageQueue, handoffLegality, isAdmissionDebugEnabled, isIdleYieldEnabled, listRegisteredInvariants, maybeDrainMidTurn, midTurnDrainPriority, registerCoreInvariants, registerInvariant, replaceSystemMessage, resolveEffectiveInvariants, resolveRequiredInvariants, routeMessage, runFanOut, runWithIdleYield, setActiveTeamModeWriter, setAgentConfigHome, updateActiveTeamMode, waitForWakeEvent };
1464
+ export type { AdmissionMetricsSnapshot, EnqueueChildTaskNotificationInput, EnvelopeAggregateEnforcer, FanOutOutcome, FanOutProgressEvent, HandoffSignal, IdleYieldSnapshot, MaybeDrainMidTurnInput, RenderOptions, RouteMessageOptions, RouteMessageResult, RunFanOutOptions, RunFanOutResult, RunWithIdleYieldOptions, RunWithIdleYieldRunResult, TeamModeBootstrapOptions, TeamModeHandle, WaitForWakeEventOptions, WakeEvent };