@kodax-ai/kodax 0.7.40 → 0.7.42

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