odai-dsh-agent 0.2.5 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/build/bin/odai-dsh-agent.d.mts +3 -0
- package/build/bin/odai-dsh-agent.d.mts.map +1 -0
- package/build/bin/odai-dsh-agent.mjs +114 -0
- package/build/bin/odai-dsh-agent.mjs.map +1 -0
- package/build/src/dsh-version.d.mts +16 -0
- package/build/src/dsh-version.d.mts.map +1 -0
- package/build/src/dsh-version.mjs +20 -0
- package/build/src/dsh-version.mjs.map +1 -0
- package/build/src/installer.d.mts +46 -0
- package/build/src/installer.d.mts.map +1 -0
- package/build/src/installer.mjs +398 -0
- package/build/src/installer.mjs.map +1 -0
- package/package.json +13 -12
- package/preset/odai/runtime/compaction-config.d.mts +66 -0
- package/preset/odai/runtime/compaction-config.d.mts.map +1 -0
- package/preset/odai/runtime/compaction-config.mjs +320 -342
- package/preset/odai/runtime/compaction-config.mjs.map +1 -0
- package/preset/odai/runtime/compaction-runtime.d.mts +68 -0
- package/preset/odai/runtime/compaction-runtime.d.mts.map +1 -0
- package/preset/odai/runtime/compaction-runtime.mjs +157 -0
- package/preset/odai/runtime/compaction-runtime.mjs.map +1 -0
- package/preset/odai/runtime/context-activation.d.mts +31 -0
- package/preset/odai/runtime/context-activation.d.mts.map +1 -0
- package/preset/odai/runtime/context-activation.mjs +55 -49
- package/preset/odai/runtime/context-activation.mjs.map +1 -0
- package/preset/odai/runtime/context-capability.d.mts +16 -0
- package/preset/odai/runtime/context-capability.d.mts.map +1 -0
- package/preset/odai/runtime/context-capability.mjs +79 -74
- package/preset/odai/runtime/context-capability.mjs.map +1 -0
- package/preset/odai/runtime/governance.d.mts +30 -0
- package/preset/odai/runtime/governance.d.mts.map +1 -0
- package/preset/odai/runtime/governance.mjs +126 -113
- package/preset/odai/runtime/governance.mjs.map +1 -0
- package/preset/odai/runtime/human-care.d.mts +13 -0
- package/preset/odai/runtime/human-care.d.mts.map +1 -0
- package/preset/odai/runtime/human-care.mjs +45 -42
- package/preset/odai/runtime/human-care.mjs.map +1 -0
- package/preset/odai/runtime/human-safety-continuity-store.d.mts +43 -0
- package/preset/odai/runtime/human-safety-continuity-store.d.mts.map +1 -0
- package/preset/odai/runtime/human-safety-continuity-store.mjs +193 -215
- package/preset/odai/runtime/human-safety-continuity-store.mjs.map +1 -0
- package/preset/odai/runtime/human-safety-continuity.d.mts +27 -0
- package/preset/odai/runtime/human-safety-continuity.d.mts.map +1 -0
- package/preset/odai/runtime/human-safety-continuity.mjs +205 -216
- package/preset/odai/runtime/human-safety-continuity.mjs.map +1 -0
- package/preset/odai/runtime/human-safety.d.mts +14 -0
- package/preset/odai/runtime/human-safety.d.mts.map +1 -0
- package/preset/odai/runtime/human-safety.mjs +50 -47
- package/preset/odai/runtime/human-safety.mjs.map +1 -0
- package/preset/odai/runtime/index.d.mts +8 -0
- package/preset/odai/runtime/index.d.mts.map +1 -0
- package/preset/odai/runtime/index.mjs +190 -2473
- package/preset/odai/runtime/index.mjs.map +1 -0
- package/preset/odai/runtime/lifecycle-runtime.d.mts +83 -0
- package/preset/odai/runtime/lifecycle-runtime.d.mts.map +1 -0
- package/preset/odai/runtime/lifecycle-runtime.mjs +1140 -0
- package/preset/odai/runtime/lifecycle-runtime.mjs.map +1 -0
- package/preset/odai/runtime/model-route.d.mts +22 -0
- package/preset/odai/runtime/model-route.d.mts.map +1 -0
- package/preset/odai/runtime/model-route.mjs +68 -60
- package/preset/odai/runtime/model-route.mjs.map +1 -0
- package/preset/odai/runtime/output-config.d.mts +54 -0
- package/preset/odai/runtime/output-config.d.mts.map +1 -0
- package/preset/odai/runtime/output-config.mjs +289 -315
- package/preset/odai/runtime/output-config.mjs.map +1 -0
- package/preset/odai/runtime/output-policy-state.d.mts +3 -0
- package/preset/odai/runtime/output-policy-state.d.mts.map +1 -0
- package/preset/odai/runtime/output-policy-state.mjs +41 -29
- package/preset/odai/runtime/output-policy-state.mjs.map +1 -0
- package/preset/odai/runtime/prompt-runtime.d.mts +55 -0
- package/preset/odai/runtime/prompt-runtime.d.mts.map +1 -0
- package/preset/odai/runtime/prompt-runtime.mjs +303 -0
- package/preset/odai/runtime/prompt-runtime.mjs.map +1 -0
- package/preset/odai/runtime/research-packet.d.mts +30 -0
- package/preset/odai/runtime/research-packet.d.mts.map +1 -0
- package/preset/odai/runtime/research-packet.mjs +116 -117
- package/preset/odai/runtime/research-packet.mjs.map +1 -0
- package/preset/odai/runtime/responsibility-gap.d.mts +26 -0
- package/preset/odai/runtime/responsibility-gap.d.mts.map +1 -0
- package/preset/odai/runtime/responsibility-gap.mjs +93 -85
- package/preset/odai/runtime/responsibility-gap.mjs.map +1 -0
- package/preset/odai/runtime/responsibility-scope.d.mts +69 -0
- package/preset/odai/runtime/responsibility-scope.d.mts.map +1 -0
- package/preset/odai/runtime/responsibility-scope.mjs +199 -171
- package/preset/odai/runtime/responsibility-scope.mjs.map +1 -0
- package/preset/odai/runtime/role-overlays.d.mts +5 -0
- package/preset/odai/runtime/role-overlays.d.mts.map +1 -0
- package/preset/odai/runtime/role-overlays.mjs +15 -14
- package/preset/odai/runtime/role-overlays.mjs.map +1 -0
- package/preset/odai/runtime/route-card.d.mts +42 -0
- package/preset/odai/runtime/route-card.d.mts.map +1 -0
- package/preset/odai/runtime/route-card.mjs +158 -146
- package/preset/odai/runtime/route-card.mjs.map +1 -0
- package/preset/odai/runtime/router.d.mts +80 -0
- package/preset/odai/runtime/router.d.mts.map +1 -0
- package/preset/odai/runtime/router.mjs +509 -559
- package/preset/odai/runtime/router.mjs.map +1 -0
- package/preset/odai/runtime/routing-config.d.mts +76 -0
- package/preset/odai/runtime/routing-config.d.mts.map +1 -0
- package/preset/odai/runtime/routing-config.mjs +373 -438
- package/preset/odai/runtime/routing-config.mjs.map +1 -0
- package/preset/odai/runtime/routing-context.d.mts +64 -0
- package/preset/odai/runtime/routing-context.d.mts.map +1 -0
- package/preset/odai/runtime/routing-context.mjs +398 -277
- package/preset/odai/runtime/routing-context.mjs.map +1 -0
- package/preset/odai/runtime/runtime-config.d.mts +24 -0
- package/preset/odai/runtime/runtime-config.d.mts.map +1 -0
- package/preset/odai/runtime/runtime-config.mjs +181 -0
- package/preset/odai/runtime/runtime-config.mjs.map +1 -0
- package/preset/odai/runtime/runtime-support.d.mts +91 -0
- package/preset/odai/runtime/runtime-support.d.mts.map +1 -0
- package/preset/odai/runtime/runtime-support.mjs +371 -0
- package/preset/odai/runtime/runtime-support.mjs.map +1 -0
- package/preset/odai/runtime/runtime-types.d.mts +237 -0
- package/preset/odai/runtime/runtime-types.d.mts.map +1 -0
- package/preset/odai/runtime/runtime-types.mjs +4 -0
- package/preset/odai/runtime/runtime-types.mjs.map +1 -0
- package/preset/odai/runtime/semantic-memory-store.d.mts +110 -0
- package/preset/odai/runtime/semantic-memory-store.d.mts.map +1 -0
- package/preset/odai/runtime/semantic-memory-store.mjs +271 -385
- package/preset/odai/runtime/semantic-memory-store.mjs.map +1 -0
- package/preset/odai/runtime/semantic-memory.d.mts +88 -0
- package/preset/odai/runtime/semantic-memory.d.mts.map +1 -0
- package/preset/odai/runtime/semantic-memory.mjs +790 -750
- package/preset/odai/runtime/semantic-memory.mjs.map +1 -0
- package/preset/odai/runtime/session-compat.d.mts +31 -0
- package/preset/odai/runtime/session-compat.d.mts.map +1 -0
- package/preset/odai/runtime/session-compat.mjs +301 -328
- package/preset/odai/runtime/session-compat.mjs.map +1 -0
- package/preset/odai/runtime/session-evidence.d.mts +25 -0
- package/preset/odai/runtime/session-evidence.d.mts.map +1 -0
- package/preset/odai/runtime/session-evidence.mjs +309 -287
- package/preset/odai/runtime/session-evidence.mjs.map +1 -0
- package/preset/odai/runtime/skill-bundle.d.mts +53 -0
- package/preset/odai/runtime/skill-bundle.d.mts.map +1 -0
- package/preset/odai/runtime/skill-bundle.mjs +286 -276
- package/preset/odai/runtime/skill-bundle.mjs.map +1 -0
- package/preset/odai/runtime/skill-evolution.d.mts +112 -0
- package/preset/odai/runtime/skill-evolution.d.mts.map +1 -0
- package/preset/odai/runtime/skill-evolution.mjs +1287 -1261
- package/preset/odai/runtime/skill-evolution.mjs.map +1 -0
- package/preset/odai/runtime/skill-selection-state.d.mts +5 -0
- package/preset/odai/runtime/skill-selection-state.d.mts.map +1 -0
- package/preset/odai/runtime/skill-selection-state.mjs +59 -43
- package/preset/odai/runtime/skill-selection-state.mjs.map +1 -0
- package/preset/odai/runtime/skill-selector.d.mts +28 -0
- package/preset/odai/runtime/skill-selector.d.mts.map +1 -0
- package/preset/odai/runtime/skill-selector.mjs +100 -109
- package/preset/odai/runtime/skill-selector.mjs.map +1 -0
- package/preset/odai/runtime/skill-source-config.d.mts +32 -0
- package/preset/odai/runtime/skill-source-config.d.mts.map +1 -0
- package/preset/odai/runtime/skill-source-config.mjs +200 -210
- package/preset/odai/runtime/skill-source-config.mjs.map +1 -0
- package/preset/odai/runtime/store-lock.d.mts +2 -0
- package/preset/odai/runtime/store-lock.d.mts.map +1 -0
- package/preset/odai/runtime/store-lock.mjs +92 -87
- package/preset/odai/runtime/store-lock.mjs.map +1 -0
- package/preset/odai/runtime/tool-runtime.d.mts +43 -0
- package/preset/odai/runtime/tool-runtime.d.mts.map +1 -0
- package/preset/odai/runtime/tool-runtime.mjs +221 -0
- package/preset/odai/runtime/tool-runtime.mjs.map +1 -0
- package/bin/odai-dsh-agent.mjs +0 -108
- package/scripts/verify-agent-load.mjs +0 -260
- package/src/dsh-version.mjs +0 -24
- package/src/installer.mjs +0 -385
|
@@ -1,2493 +1,210 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
classifyImplementationAuthorization,
|
|
8
|
-
classifyResponsibilityInterruptionText,
|
|
9
|
-
decideResearchPrefetch,
|
|
10
|
-
decideRoute,
|
|
11
|
-
extractLatestUserText,
|
|
12
|
-
extractRoutingText,
|
|
13
|
-
renderDelegationPrompt,
|
|
14
|
-
renderMissingRouteConfigNotice,
|
|
15
|
-
renderRouteFailureNotice,
|
|
16
|
-
renderRouteNotice,
|
|
17
|
-
requiresFailClosedProtection,
|
|
18
|
-
} from "./router.mjs";
|
|
19
|
-
import {
|
|
20
|
-
activeRouteProtection,
|
|
21
|
-
createChildToolGuard,
|
|
22
|
-
createRouteProtectionGuard,
|
|
23
|
-
isSubagent,
|
|
24
|
-
summarizeToolResult,
|
|
25
|
-
} from "./governance.mjs";
|
|
26
|
-
import {
|
|
27
|
-
CONFIGURABLE_ROLES,
|
|
28
|
-
ROUTING_CONFIG_PROMPT,
|
|
29
|
-
createRoutingConfigTool,
|
|
30
|
-
effectiveRoutingSnapshot,
|
|
31
|
-
invalidatePersistedRoleRoute,
|
|
32
|
-
resolveRoleRoute,
|
|
33
|
-
resolveRoutingConfigPath,
|
|
34
|
-
} from "./routing-config.mjs";
|
|
35
|
-
import {
|
|
36
|
-
DEFAULT_OUTPUT_POLICY,
|
|
37
|
-
createOutputConfigTool,
|
|
38
|
-
effectiveOutputPolicy,
|
|
39
|
-
renderOutputPolicyPrompt,
|
|
40
|
-
resolveOutputConfigPath,
|
|
41
|
-
} from "./output-config.mjs";
|
|
42
|
-
import { selectSharedOutputPolicyForTurn } from "./output-policy-state.mjs";
|
|
43
|
-
import {
|
|
44
|
-
COMPACTION_CONFIG_PROMPT,
|
|
45
|
-
applyCompactionStateProtocol,
|
|
46
|
-
applyCompactionTarget,
|
|
47
|
-
createCompactionConfigTool,
|
|
48
|
-
effectiveCompactionTarget,
|
|
49
|
-
invalidatePersistedCompactionTarget,
|
|
50
|
-
resolveCompactionConfigPath,
|
|
51
|
-
} from "./compaction-config.mjs";
|
|
52
|
-
import { createSessionEvidence, resolveSessionEvidenceRoot } from "./session-evidence.mjs";
|
|
1
|
+
import { applyCompactionStateProtocol, applyCompactionTarget, effectiveCompactionTarget, invalidatePersistedCompactionTarget, } from "./compaction-config.mjs";
|
|
2
|
+
import { installCompactionRuntime } from "./compaction-runtime.mjs";
|
|
3
|
+
import { installLifecycleRuntime } from "./lifecycle-runtime.mjs";
|
|
53
4
|
import { classifyModelRouteFailure, probeModelRoute } from "./model-route.mjs";
|
|
54
|
-
import {
|
|
55
|
-
import {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
} from "./
|
|
62
|
-
import {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
import {
|
|
68
|
-
claimResponsibilityScope,
|
|
69
|
-
createResponsibilityScope,
|
|
70
|
-
latestDanglingResponsibilityScope,
|
|
71
|
-
latestStoppedResponsibilityScope,
|
|
72
|
-
pendingResponsibilityInterruption,
|
|
73
|
-
pendingResponsibilityScopeRestoration,
|
|
74
|
-
responsibilityScopeClaimedEvent,
|
|
75
|
-
responsibilityScopeOwnsRequest,
|
|
76
|
-
responsibilityScopeStartedEvent,
|
|
77
|
-
responsibilityScopeStopReason,
|
|
78
|
-
responsibilityScopeStoppedEvent,
|
|
79
|
-
} from "./responsibility-scope.mjs";
|
|
80
|
-
import { HUMAN_CARE_REFERENCE_PATH, createHumanCareTool } from "./human-care.mjs";
|
|
81
|
-
import { HUMAN_SAFETY_REFERENCE_PATH, createHumanSafetyTool } from "./human-safety.mjs";
|
|
82
|
-
import {
|
|
83
|
-
HUMAN_SAFETY_CONTINUITY_PROMPT,
|
|
84
|
-
createHumanSafetyContinuityTool,
|
|
85
|
-
renderHumanSafetyContinuitySection,
|
|
86
|
-
} from "./human-safety-continuity.mjs";
|
|
87
|
-
import {
|
|
88
|
-
readHumanSafetyContinuityStore,
|
|
89
|
-
resolveHumanSafetyContinuityStorePath,
|
|
90
|
-
} from "./human-safety-continuity-store.mjs";
|
|
91
|
-
import {
|
|
92
|
-
ROUTE_CARD_PROMPT,
|
|
93
|
-
activeRouteCard,
|
|
94
|
-
createRouteCardTool,
|
|
95
|
-
routeCardById,
|
|
96
|
-
unsettledRouteCard,
|
|
97
|
-
} from "./route-card.mjs";
|
|
98
|
-
import { buildRoleContextPacket, renderRoleContextPacket } from "./routing-context.mjs";
|
|
99
|
-
import {
|
|
100
|
-
parseResearchPacket,
|
|
101
|
-
renderResearchPacket,
|
|
102
|
-
verifyResearchPacketSources,
|
|
103
|
-
} from "./research-packet.mjs";
|
|
104
|
-
import { dshRoleContract } from "./role-overlays.mjs";
|
|
105
|
-
import {
|
|
106
|
-
SKILL_SOURCE_CONFIG_PROMPT,
|
|
107
|
-
createSkillSourceConfigTool,
|
|
108
|
-
effectiveSkillSource,
|
|
109
|
-
resolveSkillSourceConfigPath,
|
|
110
|
-
} from "./skill-source-config.mjs";
|
|
111
|
-
import {
|
|
112
|
-
ODAI_RUNTIME_CONTRACT,
|
|
113
|
-
SKILL_SOURCE_MODES,
|
|
114
|
-
loadSkillBundle,
|
|
115
|
-
readSkillBundleFile,
|
|
116
|
-
} from "./skill-bundle.mjs";
|
|
117
|
-
import { resolveSkillSelection } from "./skill-selector.mjs";
|
|
118
|
-
import {
|
|
119
|
-
currentAgentTurn,
|
|
120
|
-
selectSharedSkillForTurn,
|
|
121
|
-
sharedSkillSelection,
|
|
122
|
-
} from "./skill-selection-state.mjs";
|
|
123
|
-
import {
|
|
124
|
-
applySkillEvolutionSelection,
|
|
125
|
-
createSkillEvolutionTool,
|
|
126
|
-
resolveSkillEvolutionRoot,
|
|
127
|
-
skillEvolutionDisabled,
|
|
128
|
-
} from "./skill-evolution.mjs";
|
|
129
|
-
import {
|
|
130
|
-
MEMORY_PROMPT,
|
|
131
|
-
captureAutomaticMemories,
|
|
132
|
-
claimSemanticMemoryTurn,
|
|
133
|
-
createSemanticMemoryTool,
|
|
134
|
-
latestDirectUserMessage,
|
|
135
|
-
memoryPacketMessage,
|
|
136
|
-
renderSemanticMemoryPacket,
|
|
137
|
-
retrieveSemanticMemories,
|
|
138
|
-
} from "./semantic-memory.mjs";
|
|
139
|
-
import {
|
|
140
|
-
MEMORY_MODES,
|
|
141
|
-
effectiveMemorySettings,
|
|
142
|
-
resolveMemoryStorePath,
|
|
143
|
-
} from "./semantic-memory-store.mjs";
|
|
144
|
-
|
|
145
|
-
export const name = "odai-dsh-runtime";
|
|
5
|
+
import { createPromptRuntime } from "./prompt-runtime.mjs";
|
|
6
|
+
import { latestDanglingResponsibilityScope, responsibilityScopeStoppedEvent, } from "./responsibility-scope.mjs";
|
|
7
|
+
import { classifyPendingReviewerText, extractLatestUserText } from "./router.mjs";
|
|
8
|
+
import { invalidatePersistedRoleRoute } from "./routing-config.mjs";
|
|
9
|
+
import { inheritCompactionReasoning, resolveConfig, resolveSkillPath, } from "./runtime-config.mjs";
|
|
10
|
+
import { RUNTIME_NAME, loggerFor, routeFromConfig, runRoutedRole, sameRequestModelRoute, } from "./runtime-support.mjs";
|
|
11
|
+
import { createSessionEvidence, resolveSessionEvidenceRoot } from "./session-evidence.mjs";
|
|
12
|
+
import { currentAgentTurn } from "./skill-selection-state.mjs";
|
|
13
|
+
import { latestDirectUserMessage } from "./semantic-memory.mjs";
|
|
14
|
+
import { installToolRuntime } from "./tool-runtime.mjs";
|
|
15
|
+
import { resolveHumanSafetyContinuityStorePath } from "./human-safety-continuity-store.mjs";
|
|
16
|
+
export const name = RUNTIME_NAME;
|
|
17
|
+
export { inheritCompactionReasoning, resolveConfig, resolveSkillPath, runRoutedRole };
|
|
146
18
|
export const inject = ["systemPrompt", "tools", "subagents", "sessions", "llm"];
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if (value === undefined) return {};
|
|
156
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
157
|
-
throw new TypeError(`${field} must be an object`);
|
|
158
|
-
}
|
|
159
|
-
return value;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export function resolveConfig(rawConfig = {}) {
|
|
163
|
-
const raw = assertPlainObject(rawConfig, "config");
|
|
164
|
-
const routing = assertPlainObject(raw.routing, "config.routing");
|
|
165
|
-
const roles = assertPlainObject(routing.roles, "config.routing.roles");
|
|
166
|
-
const governance = assertPlainObject(raw.governance, "config.governance");
|
|
167
|
-
const output = assertPlainObject(raw.output, "config.output");
|
|
168
|
-
const compaction = assertPlainObject(raw.compaction, "config.compaction");
|
|
169
|
-
const memory = assertPlainObject(raw.memory, "config.memory");
|
|
170
|
-
const mode = routing.mode ?? DEFAULT_ROUTING_MODE;
|
|
171
|
-
const provider = routing.provider ?? "spawn";
|
|
172
|
-
const maxInputChars = routing.maxInputChars ?? 12_000;
|
|
173
|
-
const configPath = resolveRoutingConfigPath(routing.configPath);
|
|
174
|
-
const additionalDeniedTools = governance.additionalDeniedTools ?? [];
|
|
175
|
-
const skillSource = governance.skillSource ?? "bundled";
|
|
176
|
-
const skillConfigPath = resolveSkillSourceConfigPath(governance.skillConfigPath);
|
|
177
|
-
const evolutionRoot = resolveSkillEvolutionRoot(governance.evolutionRoot);
|
|
178
|
-
const outputConfigPath = resolveOutputConfigPath(output.configPath);
|
|
179
|
-
const compactionConfigPath = resolveCompactionConfigPath(compaction.configPath);
|
|
180
|
-
const memoryStorePath = resolveMemoryStorePath(memory.storePath);
|
|
181
|
-
const memoryMode = memory.mode ?? "auto";
|
|
182
|
-
const memoryMaxRetrieved = memory.maxRetrieved ?? 6;
|
|
183
|
-
const compactionCacheRetention = compaction.cacheRetention
|
|
184
|
-
?? process.env.ODAI_COMPACTION_CACHE_RETENTION
|
|
185
|
-
?? "provider-default";
|
|
186
|
-
const unknownRoles = Object.keys(roles).filter((role) => !ROUTED_ROLES.includes(role));
|
|
187
|
-
const unknownOutputFields = Object.keys(output).filter((field) => field !== "configPath");
|
|
188
|
-
const unknownCompactionFields = Object.keys(compaction).filter((field) => !["cacheRetention", "configPath"].includes(field));
|
|
189
|
-
const unknownMemoryFields = Object.keys(memory).filter((field) => !["mode", "storePath", "maxRetrieved"].includes(field));
|
|
190
|
-
|
|
191
|
-
if (!ROUTING_MODES.has(mode)) {
|
|
192
|
-
throw new TypeError("config.routing.mode must be off, observe, auto, or execute");
|
|
193
|
-
}
|
|
194
|
-
if (typeof provider !== "string" || provider.trim() === "") {
|
|
195
|
-
throw new TypeError("config.routing.provider must be a non-empty string");
|
|
196
|
-
}
|
|
197
|
-
if (!Number.isSafeInteger(maxInputChars) || maxInputChars < 256) {
|
|
198
|
-
throw new TypeError("config.routing.maxInputChars must be an integer of at least 256");
|
|
199
|
-
}
|
|
200
|
-
if (unknownRoles.length > 0) {
|
|
201
|
-
throw new TypeError(`config.routing.roles has unknown roles: ${unknownRoles.join(", ")}`);
|
|
202
|
-
}
|
|
203
|
-
if (unknownOutputFields.length > 0) {
|
|
204
|
-
throw new TypeError(`config.output has unknown fields: ${unknownOutputFields.join(", ")}`);
|
|
205
|
-
}
|
|
206
|
-
if (unknownCompactionFields.length > 0) {
|
|
207
|
-
throw new TypeError(`config.compaction has unknown fields: ${unknownCompactionFields.join(", ")}`);
|
|
208
|
-
}
|
|
209
|
-
if (unknownMemoryFields.length > 0) {
|
|
210
|
-
throw new TypeError(`config.memory has unknown fields: ${unknownMemoryFields.join(", ")}`);
|
|
211
|
-
}
|
|
212
|
-
if (!MEMORY_MODES.includes(memoryMode)) {
|
|
213
|
-
throw new TypeError("config.memory.mode must be auto or off");
|
|
214
|
-
}
|
|
215
|
-
if (!Number.isSafeInteger(memoryMaxRetrieved) || memoryMaxRetrieved < 1 || memoryMaxRetrieved > 12) {
|
|
216
|
-
throw new TypeError("config.memory.maxRetrieved must be an integer from 1 to 12");
|
|
217
|
-
}
|
|
218
|
-
if (!COMPACTION_CACHE_RETENTIONS.has(compactionCacheRetention)) {
|
|
219
|
-
throw new TypeError("config.compaction.cacheRetention must be provider-default, short, long, or none");
|
|
220
|
-
}
|
|
221
|
-
if (!Array.isArray(additionalDeniedTools)
|
|
222
|
-
|| additionalDeniedTools.some((tool) => typeof tool !== "string" || tool.trim() === "")) {
|
|
223
|
-
throw new TypeError("config.governance.additionalDeniedTools must be an array of non-empty strings");
|
|
224
|
-
}
|
|
225
|
-
if (!SKILL_SOURCE_MODES.includes(skillSource)) {
|
|
226
|
-
throw new TypeError("config.governance.skillSource must be bundled, auto, or user");
|
|
227
|
-
}
|
|
228
|
-
if (raw.skillPath !== undefined && (typeof raw.skillPath !== "string" || raw.skillPath.trim() === "")) {
|
|
229
|
-
throw new TypeError("config.skillPath must be a non-empty string");
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
return Object.freeze({
|
|
233
|
-
skillPath: raw.skillPath,
|
|
234
|
-
routing: Object.freeze({
|
|
235
|
-
mode,
|
|
236
|
-
provider: provider.trim(),
|
|
237
|
-
maxInputChars,
|
|
238
|
-
configPath,
|
|
239
|
-
roles: Object.freeze(Object.fromEntries(ROUTED_ROLES.map((role) => [
|
|
240
|
-
role,
|
|
241
|
-
resolveRoleRoute(roles[role], role),
|
|
242
|
-
]))),
|
|
243
|
-
}),
|
|
244
|
-
governance: Object.freeze({
|
|
245
|
-
additionalDeniedTools: Object.freeze(additionalDeniedTools.map((tool) => tool.trim())),
|
|
246
|
-
skillSource,
|
|
247
|
-
skillConfigPath,
|
|
248
|
-
evolutionRoot,
|
|
249
|
-
}),
|
|
250
|
-
output: Object.freeze({ configPath: outputConfigPath }),
|
|
251
|
-
compaction: Object.freeze({
|
|
252
|
-
cacheRetention: compactionCacheRetention,
|
|
253
|
-
configPath: compactionConfigPath,
|
|
254
|
-
}),
|
|
255
|
-
memory: Object.freeze({
|
|
256
|
-
mode: memoryMode,
|
|
257
|
-
storePath: memoryStorePath,
|
|
258
|
-
maxRetrieved: memoryMaxRetrieved,
|
|
259
|
-
}),
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/** Mutate exact-route compaction options; returns true only when an option changed. */
|
|
264
|
-
export function inheritCompactionReasoning(options, sessions, cacheRetention = "provider-default") {
|
|
265
|
-
if (options?.purpose !== "compaction"
|
|
266
|
-
|| options.sessionId === undefined
|
|
267
|
-
|| !Object.isExtensible(options)) {
|
|
268
|
-
return false;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const route = sessions?.get?.(options.sessionId)?.requestHeader?.()?.config;
|
|
272
|
-
if (route?.provider !== options.provider
|
|
273
|
-
|| route?.model !== options.model
|
|
274
|
-
|| typeof route.reasoningEffort !== "string"
|
|
275
|
-
|| route.reasoningEffort.length === 0) {
|
|
276
|
-
return false;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
let changed = false;
|
|
280
|
-
if (options.reasoningEffort === undefined) {
|
|
281
|
-
options.reasoningEffort = route.reasoningEffort;
|
|
282
|
-
changed = true;
|
|
283
|
-
}
|
|
284
|
-
if (options.cacheRetention === undefined && cacheRetention !== "provider-default") {
|
|
285
|
-
options.cacheRetention = cacheRetention;
|
|
286
|
-
changed = true;
|
|
287
|
-
}
|
|
288
|
-
return changed;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
export function resolveSkillPath(configuredPath, env = process.env) {
|
|
292
|
-
const explicit = configuredPath ?? env.ODAI_SKILL_PATH;
|
|
293
|
-
if (explicit !== undefined) {
|
|
294
|
-
const path = resolve(explicit);
|
|
295
|
-
if (!existsSync(path)) throw new Error(`explicit Odai canonical skill not found: ${path}`);
|
|
296
|
-
return path;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const candidates = [
|
|
300
|
-
resolve(PLUGIN_DIR, "skills/odai/SKILL.md"),
|
|
301
|
-
resolve(PLUGIN_DIR, "../../skills/odai/SKILL.md"),
|
|
302
|
-
];
|
|
303
|
-
const found = candidates.find((candidate) => existsSync(candidate));
|
|
304
|
-
if (found) return found;
|
|
305
|
-
throw new Error(`Odai bundled canonical skill not found; checked: ${candidates.join(", ")}`);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
export function loadRoleContracts(skillPath) {
|
|
309
|
-
return loadSkillBundle(skillPath).roleContracts;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
function deepFreeze(value) {
|
|
313
|
-
if (value === null || typeof value !== "object" || Object.isFrozen(value)) return value;
|
|
314
|
-
for (const child of Object.values(value)) deepFreeze(child);
|
|
315
|
-
return Object.freeze(value);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
function pluginMessage(text, summary, extraBlocks = []) {
|
|
319
|
-
return deepFreeze({
|
|
320
|
-
id: randomUUID(),
|
|
321
|
-
role: "user",
|
|
322
|
-
content: [{ type: "text", text }, ...extraBlocks],
|
|
323
|
-
source: {
|
|
324
|
-
kind: "plugin",
|
|
325
|
-
plugin: name,
|
|
326
|
-
form: "notice",
|
|
327
|
-
summary: summary.length <= 120 ? summary : `${summary.slice(0, 119)}…`,
|
|
328
|
-
},
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
function renderOutputLimitInterruptionNotice(interruption) {
|
|
333
|
-
const route = interruption.effectiveRoute ?? interruption.requestedRoute;
|
|
334
|
-
return [
|
|
335
|
-
"Odai verified output-limit interruption",
|
|
336
|
-
`responsibility: ${interruption.responsibility}`,
|
|
337
|
-
`interrupted scope: ${interruption.scopeId}`,
|
|
338
|
-
`provider finish reason: ${interruption.reason}`,
|
|
339
|
-
...(route ? [`effective route: ${route.provider}/${route.model}`] : []),
|
|
340
|
-
...(interruption.effectiveMaxTokens === undefined ? [] : [`effective maxTokens: ${interruption.effectiveMaxTokens}`]),
|
|
341
|
-
...(interruption.outputTokens === undefined ? [] : [`observed outputTokens: ${interruption.outputTokens}`]),
|
|
342
|
-
"This proves the responsibility output was interrupted, not that its task completed. Explain the verified cause without guessing. Resume only after an explicit user continuation.",
|
|
343
|
-
].join("\n");
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
function loggerFor(ctx) {
|
|
347
|
-
if (typeof ctx.logger !== "function") return { info() {}, warn() {} };
|
|
348
|
-
return ctx.logger(name);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
function outputText(blocks) {
|
|
352
|
-
if (!Array.isArray(blocks)) return "";
|
|
353
|
-
return blocks
|
|
354
|
-
.filter((block) => block?.type === "text" && typeof block.text === "string")
|
|
355
|
-
.map((block) => block.text)
|
|
356
|
-
.join("\n")
|
|
357
|
-
.trim();
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
function renderResearchTaskContract(taskText) {
|
|
361
|
-
return [
|
|
362
|
-
"Decision-blocking factual question: Determine whether the user's causal claim is supported and which existing repository facts govern the safety of the requested high-impact change.",
|
|
363
|
-
"Allowed source scope: the current project root only. Use repository-relative paths and read-only source tools; do not inspect parent, sibling, user, or unrelated directories.",
|
|
364
|
-
"Authority and freshness: label each current-checkout source by its actual role (for example runtime configuration, implementation, test, incident record, or documentation). Do not invent an authority hierarchy; report unresolved conflicts and missing freshness evidence as unknowns.",
|
|
365
|
-
"Stop condition: return the smallest packet with 2-6 source-backed facts from at least two files, or stop with the missing evidence boundary. Do not select a route or continue after additional reading cannot change this factual question.",
|
|
366
|
-
"For every fact, excerpt must exactly equal the complete cited source line after trimming leading and trailing whitespace.",
|
|
367
|
-
"",
|
|
368
|
-
"Original user request:",
|
|
369
|
-
taskText,
|
|
370
|
-
].join("\n");
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
function roleAgentOptions(roleRoute) {
|
|
374
|
-
if (!roleRoute) return undefined;
|
|
375
|
-
return {
|
|
376
|
-
provider: roleRoute.provider,
|
|
377
|
-
model: roleRoute.model,
|
|
378
|
-
...(roleRoute.maxTokens === undefined ? {} : { maxTokens: roleRoute.maxTokens }),
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
function sameRequestModelRoute(left, right) {
|
|
383
|
-
return Boolean(left && right
|
|
384
|
-
&& left.provider === right.provider
|
|
385
|
-
&& left.model === right.model
|
|
386
|
-
&& left.reasoningEffort === right.reasoningEffort);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
function currentAgentStep(agent) {
|
|
390
|
-
const events = agent?.session?.events;
|
|
391
|
-
if (!Array.isArray(events)) return undefined;
|
|
392
|
-
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
393
|
-
if (events[index]?.type === "step/start" && Number.isSafeInteger(events[index].data?.step)) return events[index].data.step;
|
|
394
|
-
}
|
|
395
|
-
return undefined;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
function routeFromConfig(config) {
|
|
399
|
-
if (typeof config?.provider !== "string" || typeof config?.model !== "string") return undefined;
|
|
400
|
-
return Object.freeze({
|
|
401
|
-
provider: config.provider,
|
|
402
|
-
model: config.model,
|
|
403
|
-
...(config.reasoningEffort === undefined ? {} : { reasoningEffort: config.reasoningEffort }),
|
|
404
|
-
...(config.maxTokens === undefined ? {} : { maxTokens: config.maxTokens }),
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
function latestRequestRoute(localAgent) {
|
|
409
|
-
const events = localAgent?.session?.events;
|
|
410
|
-
if (!Array.isArray(events)) return undefined;
|
|
411
|
-
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
412
|
-
const event = events[index];
|
|
413
|
-
if (event?.type !== "request/header") continue;
|
|
414
|
-
return routeFromConfig(event.data?.header?.config);
|
|
415
|
-
}
|
|
416
|
-
return undefined;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
function routeMismatchFor(expected, actual, subject) {
|
|
420
|
-
if (!expected) return undefined;
|
|
421
|
-
if (!actual) return `${subject} request/header did not expose an actual model route`;
|
|
422
|
-
for (const field of ["provider", "model", "reasoningEffort", "maxTokens"]) {
|
|
423
|
-
if (expected[field] !== undefined && expected[field] !== actual[field]) {
|
|
424
|
-
return `${subject} ${field} mismatch: expected ${expected[field]}, got ${actual[field] ?? "<absent>"}`;
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
return undefined;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
function routeMismatch(expected, actual) {
|
|
431
|
-
return routeMismatchFor(expected, actual, "child");
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
export async function runRoutedRole({ subagents, provider, decision, taskText, roleContract, agent, signal, roleRoute }) {
|
|
435
|
-
let run;
|
|
436
|
-
let outcome;
|
|
437
|
-
try {
|
|
438
|
-
run = await subagents.start(provider, {
|
|
439
|
-
label: `odai-${decision.role}`,
|
|
440
|
-
prompt: [{ type: "text", text: renderDelegationPrompt(decision, taskText, roleContract) }],
|
|
441
|
-
parent: agent,
|
|
442
|
-
signal,
|
|
443
|
-
maxDepth: 1,
|
|
444
|
-
...(roleRoute ? { agentOptions: roleAgentOptions(roleRoute) } : {}),
|
|
445
|
-
});
|
|
446
|
-
const result = await run.result;
|
|
447
|
-
const actualRoute = latestRequestRoute(run.localAgent);
|
|
448
|
-
const mismatch = routeMismatch(roleRoute, actualRoute);
|
|
449
|
-
const routeReceiptStatus = !actualRoute ? "unverified" : mismatch ? "mismatch" : "applied";
|
|
450
|
-
const routeEvidence = Object.freeze({
|
|
451
|
-
routeReceiptStatus,
|
|
452
|
-
...(mismatch ? { routeReceiptError: mismatch } : {}),
|
|
453
|
-
...(actualRoute ? { actualRoute } : {}),
|
|
454
|
-
});
|
|
455
|
-
if (result.stopReason !== "completed") {
|
|
456
|
-
outcome = Object.freeze({
|
|
457
|
-
status: "fallback",
|
|
458
|
-
stopReason: result.stopReason,
|
|
459
|
-
output: result.output ?? [],
|
|
460
|
-
...routeEvidence,
|
|
461
|
-
});
|
|
462
|
-
} else if (mismatch) {
|
|
463
|
-
outcome = Object.freeze({
|
|
464
|
-
status: "fallback",
|
|
465
|
-
stopReason: "route-unverified",
|
|
466
|
-
output: [],
|
|
467
|
-
error: mismatch,
|
|
468
|
-
...routeEvidence,
|
|
469
|
-
});
|
|
470
|
-
} else if (!outputText(result.output)) {
|
|
471
|
-
outcome = Object.freeze({
|
|
472
|
-
status: "fallback",
|
|
473
|
-
stopReason: "route-empty-output",
|
|
474
|
-
output: [],
|
|
475
|
-
error: "child completed without textual evidence",
|
|
476
|
-
taskError: "child completed without textual evidence",
|
|
477
|
-
...routeEvidence,
|
|
478
|
-
});
|
|
479
|
-
} else {
|
|
480
|
-
outcome = Object.freeze({
|
|
481
|
-
status: "completed",
|
|
482
|
-
stopReason: result.stopReason,
|
|
483
|
-
output: result.output ?? [],
|
|
484
|
-
...routeEvidence,
|
|
485
|
-
});
|
|
486
|
-
}
|
|
487
|
-
} catch (error) {
|
|
488
|
-
const taskError = error instanceof Error ? error.message : String(error);
|
|
489
|
-
outcome = Object.freeze({
|
|
490
|
-
status: "fallback",
|
|
491
|
-
stopReason: "infrastructure-error",
|
|
492
|
-
output: [],
|
|
493
|
-
routeReceiptStatus: "unverified",
|
|
494
|
-
error: taskError,
|
|
495
|
-
taskError,
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
if (run) {
|
|
500
|
-
try {
|
|
501
|
-
await run.dispose();
|
|
502
|
-
} catch (error) {
|
|
503
|
-
const disposeError = error instanceof Error ? error.message : String(error);
|
|
504
|
-
const cleanupTaskError = outcome?.taskError
|
|
505
|
-
? `${outcome.taskError}; provider cleanup failed: ${disposeError}`
|
|
506
|
-
: `provider cleanup failed: ${disposeError}`;
|
|
507
|
-
return Object.freeze({
|
|
508
|
-
status: "fallback",
|
|
509
|
-
stopReason: "infrastructure-error",
|
|
510
|
-
output: [],
|
|
511
|
-
routeReceiptStatus: outcome?.routeReceiptStatus ?? "unverified",
|
|
512
|
-
...(outcome?.routeReceiptError ? { routeReceiptError: outcome.routeReceiptError } : {}),
|
|
513
|
-
...(outcome?.actualRoute ? { actualRoute: outcome.actualRoute } : {}),
|
|
514
|
-
error: outcome?.error
|
|
515
|
-
? `${outcome.error}; provider cleanup failed: ${disposeError}`
|
|
516
|
-
: cleanupTaskError,
|
|
517
|
-
taskError: cleanupTaskError,
|
|
518
|
-
});
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
return outcome;
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
function canonicalPrompt(selection) {
|
|
526
|
-
const { bundle } = selection;
|
|
527
|
-
const fallback = selection.status === "fallback"
|
|
528
|
-
? `Selection fallback: ${selection.reasonCode}${selection.detail ? ` (${selection.detail})` : ""}.`
|
|
529
|
-
: undefined;
|
|
530
|
-
const evolution = selection.evolution?.status === "active"
|
|
531
|
-
? `User evolution: generation ${selection.evolution.generationId}; base digest ${selection.evolution.baseDigest}; current upstream digest ${selection.evolution.upstreamDigest}; rebase required: ${String(selection.evolution.rebaseRequired)}.`
|
|
532
|
-
: undefined;
|
|
533
|
-
return [
|
|
534
|
-
"## odai canonical governance",
|
|
535
|
-
`Canonical source: ${bundle.source} (${bundle.provider})`,
|
|
536
|
-
`Canonical skill: ${bundle.manifest.skillVersion}; runtime contract: ${bundle.manifest.runtimeContract}; digest: ${bundle.digest}.`,
|
|
537
|
-
...(evolution ? [evolution] : []),
|
|
538
|
-
...(fallback ? [fallback] : []),
|
|
539
|
-
"Apply this governance to every request. Keep the controller as the final delivery owner; use another role only for a real independent gap with observable net benefit.",
|
|
540
|
-
"Odai governance is already loaded by this runtime; do not call the skill tool or read SKILL.md to load odai again.",
|
|
541
|
-
"",
|
|
542
|
-
bundle.skillText,
|
|
543
|
-
].join("\n");
|
|
19
|
+
function isResponsibilityGapProposal(data) {
|
|
20
|
+
return typeof data.responsibility === "string"
|
|
21
|
+
&& ["researcher", "planner", "executor", "reviewer", "frontend", "user"].includes(data.responsibility)
|
|
22
|
+
&& typeof data.gap === "string"
|
|
23
|
+
&& Array.isArray(data.evidenceRefs)
|
|
24
|
+
&& data.evidenceRefs.every((value) => typeof value === "string")
|
|
25
|
+
&& typeof data.expectedChange === "string"
|
|
26
|
+
&& typeof data.stateDigest === "string";
|
|
544
27
|
}
|
|
545
|
-
|
|
546
|
-
function renderEffectiveRoutingContext(snapshotState) {
|
|
547
|
-
if (snapshotState.error) {
|
|
548
|
-
return [
|
|
549
|
-
"Current effective responsibility mappings: unavailable because the saved routing configuration is invalid.",
|
|
550
|
-
`Configuration error: ${snapshotState.detail}`,
|
|
551
|
-
"Use odai_routing_config only when the user asks to inspect or repair it; do not infer any route.",
|
|
552
|
-
].join("\n");
|
|
553
|
-
}
|
|
554
|
-
const mappings = CONFIGURABLE_ROLES.flatMap((role) => {
|
|
555
|
-
const route = snapshotState.snapshot.roles[role];
|
|
556
|
-
if (!route) return [];
|
|
557
|
-
const options = [
|
|
558
|
-
...(route.reasoningEffort ? [`reasoningEffort=${route.reasoningEffort}`] : []),
|
|
559
|
-
...(route.maxTokens ? [`maxTokens=${route.maxTokens}`] : []),
|
|
560
|
-
];
|
|
561
|
-
return [`${role}=${route.provider}/${route.model}${options.length > 0 ? ` (${options.join(", ")})` : ""} [${snapshotState.snapshot.sources[role]}]`];
|
|
562
|
-
});
|
|
563
|
-
return [
|
|
564
|
-
`Current effective responsibility mappings (runtime-owned; supersedes conversation summaries): ${mappings.join("; ") || "none"}.`,
|
|
565
|
-
"These are route targets, not evidence that a responsibility ran. Only an actual route receipt proves use; a generic subagent does not.",
|
|
566
|
-
].join("\n");
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
function latestRouteReceipt(events) {
|
|
570
|
-
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
571
|
-
const event = events[index];
|
|
572
|
-
const data = event?.data;
|
|
573
|
-
if (!data?.routeSource || !Number.isSafeInteger(data.turn) || !Number.isSafeInteger(data.step)) continue;
|
|
574
|
-
if (event.type === "odai/route-fallback") {
|
|
575
|
-
return Object.freeze({
|
|
576
|
-
turn: data.turn,
|
|
577
|
-
step: data.step,
|
|
578
|
-
responsibility: data.responsibility,
|
|
579
|
-
...(data.responsibilityScopeId ? { responsibilityScopeId: data.responsibilityScopeId } : {}),
|
|
580
|
-
...(data.routeCardId ? { routeCardId: data.routeCardId } : {}),
|
|
581
|
-
status: "fallback",
|
|
582
|
-
taskStatus: "fallback",
|
|
583
|
-
routeMode: data.routeMode,
|
|
584
|
-
routeSource: data.routeSource,
|
|
585
|
-
fallbackUsed: true,
|
|
586
|
-
requestedRoute: data.requestedRoute,
|
|
587
|
-
...(data.fallbackRoute ? { actualRoute: data.fallbackRoute } : {}),
|
|
588
|
-
...(data.error ? { error: data.error } : {}),
|
|
589
|
-
});
|
|
590
|
-
}
|
|
591
|
-
if (event.type === "odai/route-applied") {
|
|
592
|
-
return Object.freeze({
|
|
593
|
-
turn: data.turn,
|
|
594
|
-
step: data.step,
|
|
595
|
-
responsibility: data.responsibility,
|
|
596
|
-
...(data.responsibilityScopeId ? { responsibilityScopeId: data.responsibilityScopeId } : {}),
|
|
597
|
-
...(data.routeCardId ? { routeCardId: data.routeCardId } : {}),
|
|
598
|
-
status: data.status,
|
|
599
|
-
routeMode: data.routeMode,
|
|
600
|
-
routeSource: data.routeSource,
|
|
601
|
-
fallbackUsed: data.fallbackUsed,
|
|
602
|
-
requestedRoute: data.requestedRoute,
|
|
603
|
-
...(data.actualRoute ? { actualRoute: data.actualRoute } : {}),
|
|
604
|
-
...(data.stopReason ? { stopReason: data.stopReason } : {}),
|
|
605
|
-
...(data.error ? { error: data.error } : {}),
|
|
606
|
-
});
|
|
607
|
-
}
|
|
608
|
-
if (["odai/route-result", "odai/research-result"].includes(event.type)) {
|
|
609
|
-
const routeReceiptStatus = data.routeReceiptStatus ?? "unverified";
|
|
610
|
-
return Object.freeze({
|
|
611
|
-
turn: data.turn,
|
|
612
|
-
step: data.step,
|
|
613
|
-
responsibility: data.role,
|
|
614
|
-
status: routeReceiptStatus,
|
|
615
|
-
taskStatus: data.status,
|
|
616
|
-
routeMode: "child",
|
|
617
|
-
routeSource: data.routeSource,
|
|
618
|
-
fallbackUsed: routeReceiptStatus !== "applied",
|
|
619
|
-
requestedRoute: data.requestedRoute,
|
|
620
|
-
...(data.actualRoute ? { actualRoute: data.actualRoute } : {}),
|
|
621
|
-
...(data.routeReceiptError ? { error: data.routeReceiptError } : {}),
|
|
622
|
-
...(data.stopReason ? { taskStopReason: data.stopReason } : {}),
|
|
623
|
-
...(data.error ? { taskError: data.error } : {}),
|
|
624
|
-
});
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
return undefined;
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
function routedRoleOf(agent) {
|
|
631
|
-
const events = agent?.session?.events;
|
|
632
|
-
if (!Array.isArray(events)) return undefined;
|
|
633
|
-
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
634
|
-
const event = events[index];
|
|
635
|
-
if (event?.type !== "subagent/descriptor") continue;
|
|
636
|
-
const label = event.data?.label;
|
|
637
|
-
if (typeof label !== "string") return undefined;
|
|
638
|
-
const match = /^odai-(researcher|planner|executor|reviewer|frontend)(?:$|[\s:])/u.exec(label.trim());
|
|
639
|
-
return match && ROUTED_ROLES.includes(match[1]) ? match[1] : undefined;
|
|
640
|
-
}
|
|
641
|
-
return undefined;
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
const ODAI_ADAPTIVE_TOOL_NAMES = new Set([...ODAI_CONTEXTUAL_TOOL_NAMES, ...ODAI_CORE_TOOL_NAMES]);
|
|
645
|
-
|
|
646
|
-
function reconcileAdaptiveToolSchemas(assembly, activeNames, executableSchemas = []) {
|
|
647
|
-
if (!assembly || !Array.isArray(assembly.tools)) return assembly;
|
|
648
|
-
const active = new Set(activeNames);
|
|
649
|
-
const executableByName = new Map(
|
|
650
|
-
(Array.isArray(executableSchemas) ? executableSchemas : [])
|
|
651
|
-
.filter((tool) => active.has(tool?.name))
|
|
652
|
-
.map((tool) => [tool.name, tool]),
|
|
653
|
-
);
|
|
654
|
-
const tools = assembly.tools.filter((tool) => !ODAI_ADAPTIVE_TOOL_NAMES.has(tool?.name)
|
|
655
|
-
|| (active.has(tool.name) && executableByName.has(tool.name)));
|
|
656
|
-
const present = new Set(tools.map((tool) => tool?.name));
|
|
657
|
-
const missing = activeNames
|
|
658
|
-
.filter((name) => !present.has(name) && executableByName.has(name))
|
|
659
|
-
.map((name) => executableByName.get(name));
|
|
660
|
-
if (missing.length > 0) {
|
|
661
|
-
const lastAdaptiveIndex = tools.reduce(
|
|
662
|
-
(last, tool, index) => ODAI_ADAPTIVE_TOOL_NAMES.has(tool?.name) ? index : last,
|
|
663
|
-
-1,
|
|
664
|
-
);
|
|
665
|
-
tools.splice(lastAdaptiveIndex < 0 ? tools.length : lastAdaptiveIndex + 1, 0, ...missing);
|
|
666
|
-
}
|
|
667
|
-
return tools.length === assembly.tools.length && missing.length === 0 ? assembly : { ...assembly, tools };
|
|
668
|
-
}
|
|
669
|
-
|
|
670
28
|
export function apply(ctx, rawConfig) {
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
try {
|
|
680
|
-
evidence.append(agent, type, data);
|
|
681
|
-
} catch (error) {
|
|
682
|
-
logger.warn(`failed to record ${type}: ${error instanceof Error ? error.message : String(error)}`);
|
|
683
|
-
}
|
|
684
|
-
};
|
|
685
|
-
const hasSessionEvent = (agent, type, predicate) => evidence.has(agent, type, predicate);
|
|
686
|
-
const selectCompactionTarget = () => {
|
|
687
|
-
try {
|
|
688
|
-
return effectiveCompactionTarget(config.compaction.configPath);
|
|
689
|
-
} catch (error) {
|
|
690
|
-
logger.warn(`Odai compaction model configuration is invalid; inheriting the conversation route: ${error instanceof Error ? error.message : String(error)}`);
|
|
691
|
-
return Object.freeze({ source: "inherit", status: "fallback", reasonCode: "compaction-config-invalid" });
|
|
692
|
-
}
|
|
693
|
-
};
|
|
694
|
-
const compactionFallbackRequests = new WeakSet();
|
|
695
|
-
ctx.on("llm/stream", (options, next) => {
|
|
696
|
-
if (options?.purpose !== "compaction" || compactionFallbackRequests.has(options)) return next();
|
|
697
|
-
const selection = selectCompactionTarget();
|
|
698
|
-
const downstream = next();
|
|
699
|
-
const streamLike = downstream && typeof downstream[Symbol.asyncIterator] === "function";
|
|
700
|
-
if (!selection.target || !streamLike) {
|
|
701
|
-
applyCompactionTarget(options, selection.target, ctx.sessions);
|
|
702
|
-
applyCompactionStateProtocol(options, selection.target);
|
|
703
|
-
inheritCompactionReasoning(options, ctx.sessions, config.compaction.cacheRetention);
|
|
704
|
-
return downstream;
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
const original = Object.freeze({ ...options, messages: options.messages });
|
|
708
|
-
const session = options.sessionId === undefined ? undefined : ctx.sessions?.get?.(options.sessionId);
|
|
709
|
-
const record = (data) => {
|
|
710
|
-
if (session) appendEvent({ session }, "odai/compaction-route", data);
|
|
711
|
-
};
|
|
712
|
-
const restoreOriginal = () => {
|
|
713
|
-
for (const key of Object.keys(options)) {
|
|
714
|
-
if (!Object.hasOwn(original, key)) delete options[key];
|
|
715
|
-
}
|
|
716
|
-
Object.assign(options, original);
|
|
717
|
-
};
|
|
718
|
-
return (async function* configuredCompactionRoute() {
|
|
719
|
-
const validation = await probeModelRoute(
|
|
720
|
-
(candidate, signal) => ctx.llm.resolveCallConfig(candidate, signal),
|
|
721
|
-
selection.target,
|
|
722
|
-
options.signal,
|
|
723
|
-
);
|
|
724
|
-
if (validation.status === "rejected") {
|
|
725
|
-
let invalidation = { invalidated: false };
|
|
726
|
-
if (validation.failure.kind === "deterministic" && selection.source === "persisted") {
|
|
727
|
-
try {
|
|
728
|
-
invalidation = invalidatePersistedCompactionTarget(config.compaction.configPath, selection.target);
|
|
729
|
-
} catch (error) {
|
|
730
|
-
invalidation = { invalidated: false, error: error instanceof Error ? error.message : String(error) };
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
record({
|
|
734
|
-
status: "fallback",
|
|
735
|
-
requestedRoute: selection.target,
|
|
736
|
-
fallbackRoute: routeFromConfig(original),
|
|
737
|
-
fallbackUsed: true,
|
|
738
|
-
failureKind: validation.failure.kind,
|
|
739
|
-
errorCode: validation.failure.code,
|
|
740
|
-
error: validation.failure.message,
|
|
741
|
-
invalidated: invalidation.invalidated,
|
|
742
|
-
...(invalidation.backupPath ? { backupPath: invalidation.backupPath } : {}),
|
|
743
|
-
...(invalidation.error ? { cleanupError: invalidation.error } : {}),
|
|
744
|
-
});
|
|
745
|
-
inheritCompactionReasoning(options, ctx.sessions, config.compaction.cacheRetention);
|
|
746
|
-
for await (const chunk of downstream) yield chunk;
|
|
747
|
-
return;
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
applyCompactionTarget(options, selection.target, ctx.sessions);
|
|
751
|
-
applyCompactionStateProtocol(options, selection.target);
|
|
752
|
-
inheritCompactionReasoning(options, ctx.sessions, config.compaction.cacheRetention);
|
|
753
|
-
const buffered = [];
|
|
754
|
-
let terminalFailure;
|
|
755
|
-
for await (const chunk of downstream) {
|
|
756
|
-
buffered.push(chunk);
|
|
757
|
-
if (chunk?.type === "finish" && ["error", "aborted"].includes(chunk.reason?.kind)) {
|
|
758
|
-
terminalFailure = chunk.reason.failure;
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
if (!terminalFailure) {
|
|
762
|
-
record({
|
|
763
|
-
status: "applied",
|
|
764
|
-
requestedRoute: selection.target,
|
|
765
|
-
effectiveRoute: routeFromConfig(options),
|
|
766
|
-
fallbackUsed: false,
|
|
767
|
-
});
|
|
768
|
-
for (const chunk of buffered) yield chunk;
|
|
769
|
-
return;
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
const failure = classifyModelRouteFailure(terminalFailure);
|
|
773
|
-
if (failure.kind === "cancelled" || options.signal?.aborted) {
|
|
774
|
-
record({
|
|
775
|
-
status: "failed",
|
|
776
|
-
requestedRoute: selection.target,
|
|
777
|
-
fallbackUsed: false,
|
|
778
|
-
failureKind: failure.kind,
|
|
779
|
-
errorCode: failure.code,
|
|
780
|
-
error: failure.message,
|
|
781
|
-
});
|
|
782
|
-
for (const chunk of buffered) yield chunk;
|
|
783
|
-
return;
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
if (sameRequestModelRoute(original, options)) {
|
|
787
|
-
record({
|
|
788
|
-
status: "failed",
|
|
789
|
-
requestedRoute: selection.target,
|
|
790
|
-
effectiveRoute: routeFromConfig(options),
|
|
791
|
-
fallbackUsed: false,
|
|
792
|
-
failureKind: failure.kind,
|
|
793
|
-
errorCode: failure.code,
|
|
794
|
-
error: failure.message,
|
|
795
|
-
stopReason: "configured-and-inherited-routes-match",
|
|
796
|
-
});
|
|
797
|
-
for (const chunk of buffered) yield chunk;
|
|
798
|
-
return;
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
let invalidation = { invalidated: false };
|
|
802
|
-
if (failure.kind === "deterministic" && selection.source === "persisted") {
|
|
29
|
+
const config = resolveConfig(rawConfig);
|
|
30
|
+
const logger = loggerFor(ctx);
|
|
31
|
+
const humanSafetyContinuityStorePath = resolveHumanSafetyContinuityStorePath();
|
|
32
|
+
const evidence = createSessionEvidence({
|
|
33
|
+
root: resolveSessionEvidenceRoot(config.routing.configPath),
|
|
34
|
+
logger,
|
|
35
|
+
});
|
|
36
|
+
const appendEvent = (agent, type, data) => {
|
|
803
37
|
try {
|
|
804
|
-
|
|
805
|
-
} catch (error) {
|
|
806
|
-
invalidation = { invalidated: false, error: error instanceof Error ? error.message : String(error) };
|
|
38
|
+
evidence.append(agent, type, data);
|
|
807
39
|
}
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
const fallbackOptions = { ...original, messages: original.messages };
|
|
811
|
-
inheritCompactionReasoning(fallbackOptions, ctx.sessions, config.compaction.cacheRetention);
|
|
812
|
-
compactionFallbackRequests.add(fallbackOptions);
|
|
813
|
-
record({
|
|
814
|
-
status: "fallback",
|
|
815
|
-
requestedRoute: selection.target,
|
|
816
|
-
fallbackRoute: routeFromConfig(fallbackOptions),
|
|
817
|
-
fallbackUsed: true,
|
|
818
|
-
failureKind: failure.kind,
|
|
819
|
-
errorCode: failure.code,
|
|
820
|
-
error: failure.message,
|
|
821
|
-
invalidated: invalidation.invalidated,
|
|
822
|
-
...(invalidation.backupPath ? { backupPath: invalidation.backupPath } : {}),
|
|
823
|
-
...(invalidation.error ? { cleanupError: invalidation.error } : {}),
|
|
824
|
-
});
|
|
825
|
-
for await (const chunk of ctx.llm.stream(fallbackOptions)) yield chunk;
|
|
826
|
-
})();
|
|
827
|
-
});
|
|
828
|
-
const skillPath = resolveSkillPath(config.skillPath);
|
|
829
|
-
const explicitSkillPath = config.skillPath !== undefined
|
|
830
|
-
|| (typeof process.env.ODAI_SKILL_PATH === "string" && process.env.ODAI_SKILL_PATH.trim() !== "");
|
|
831
|
-
const bundled = loadSkillBundle(skillPath, {
|
|
832
|
-
source: explicitSkillPath ? "path" : "bundled",
|
|
833
|
-
provider: explicitSkillPath ? "odai-explicit-path" : "odai-dsh-runtime",
|
|
834
|
-
});
|
|
835
|
-
if (bundled.manifest.runtimeContract !== ODAI_RUNTIME_CONTRACT) {
|
|
836
|
-
throw new Error(`Odai canonical runtimeContract ${bundled.manifest.runtimeContract} is incompatible with this runtime`);
|
|
837
|
-
}
|
|
838
|
-
const baseSelection = Object.freeze({
|
|
839
|
-
mode: explicitSkillPath ? "path" : "bundled",
|
|
840
|
-
status: "selected",
|
|
841
|
-
reasonCode: explicitSkillPath ? "explicit-path" : "bundled-configured",
|
|
842
|
-
bundle: bundled,
|
|
843
|
-
rejections: Object.freeze([]),
|
|
844
|
-
});
|
|
845
|
-
const evolutionDisabled = explicitSkillPath || skillEvolutionDisabled();
|
|
846
|
-
|
|
847
|
-
ctx.systemPrompt.section({
|
|
848
|
-
name: "odai:canonical-governance",
|
|
849
|
-
order: -20,
|
|
850
|
-
text: canonicalPrompt(baseSelection),
|
|
851
|
-
});
|
|
852
|
-
ctx.systemPrompt.section({
|
|
853
|
-
name: "odai:routing-configuration",
|
|
854
|
-
order: -19,
|
|
855
|
-
text: ROUTING_CONFIG_PROMPT,
|
|
856
|
-
});
|
|
857
|
-
ctx.systemPrompt.section({
|
|
858
|
-
name: "odai:human-safety-continuity",
|
|
859
|
-
order: -18.875,
|
|
860
|
-
text: HUMAN_SAFETY_CONTINUITY_PROMPT,
|
|
861
|
-
});
|
|
862
|
-
ctx.systemPrompt.section({
|
|
863
|
-
name: "odai:responsibility-gap",
|
|
864
|
-
order: -18.75,
|
|
865
|
-
text: RESPONSIBILITY_GAP_PROMPT,
|
|
866
|
-
});
|
|
867
|
-
ctx.systemPrompt.section({
|
|
868
|
-
name: "odai:route-card",
|
|
869
|
-
order: -18.5,
|
|
870
|
-
text: ROUTE_CARD_PROMPT,
|
|
871
|
-
});
|
|
872
|
-
ctx.systemPrompt.section({
|
|
873
|
-
name: "odai:skill-source-configuration",
|
|
874
|
-
order: -18,
|
|
875
|
-
text: SKILL_SOURCE_CONFIG_PROMPT,
|
|
876
|
-
});
|
|
877
|
-
ctx.systemPrompt.section({
|
|
878
|
-
name: "odai:controller-output-policy",
|
|
879
|
-
order: -17,
|
|
880
|
-
text: "",
|
|
881
|
-
});
|
|
882
|
-
ctx.systemPrompt.section({
|
|
883
|
-
name: "odai:compaction-model-configuration",
|
|
884
|
-
order: -16,
|
|
885
|
-
text: COMPACTION_CONFIG_PROMPT,
|
|
886
|
-
});
|
|
887
|
-
ctx.systemPrompt.section({
|
|
888
|
-
name: "odai:semantic-memory",
|
|
889
|
-
order: -15,
|
|
890
|
-
text: MEMORY_PROMPT,
|
|
891
|
-
});
|
|
892
|
-
|
|
893
|
-
const optionalSkillRegistry = () => {
|
|
894
|
-
try {
|
|
895
|
-
if (ctx.skills && typeof ctx.skills.get === "function") return ctx.skills;
|
|
896
|
-
} catch {}
|
|
897
|
-
try {
|
|
898
|
-
const service = typeof ctx.get === "function" ? ctx.get("skills") : undefined;
|
|
899
|
-
return service && typeof service.get === "function" ? service : undefined;
|
|
900
|
-
} catch {
|
|
901
|
-
return undefined;
|
|
902
|
-
}
|
|
903
|
-
};
|
|
904
|
-
const selectUpstreamForAgent = async (agent, context) => {
|
|
905
|
-
if (explicitSkillPath) return baseSelection;
|
|
906
|
-
let mode;
|
|
907
|
-
try {
|
|
908
|
-
mode = effectiveSkillSource(config.governance.skillConfigPath, config.governance.skillSource);
|
|
909
|
-
} catch (error) {
|
|
910
|
-
return Object.freeze({
|
|
911
|
-
...baseSelection,
|
|
912
|
-
mode: "bundled",
|
|
913
|
-
status: "fallback",
|
|
914
|
-
reasonCode: "source-config-invalid",
|
|
915
|
-
detail: error instanceof Error ? error.message : String(error),
|
|
916
|
-
});
|
|
917
|
-
}
|
|
918
|
-
return resolveSkillSelection({
|
|
919
|
-
mode,
|
|
920
|
-
bundled,
|
|
921
|
-
cwd: agent.session?.header?.cwd,
|
|
922
|
-
scope: context.scope,
|
|
923
|
-
signal: context.signal,
|
|
924
|
-
skills: optionalSkillRegistry(),
|
|
925
|
-
});
|
|
926
|
-
};
|
|
927
|
-
const selectForAgent = async (agent, context) => {
|
|
928
|
-
const upstream = await selectUpstreamForAgent(agent, context);
|
|
929
|
-
return applySkillEvolutionSelection(upstream, config.governance.evolutionRoot, { disabled: evolutionDisabled });
|
|
930
|
-
};
|
|
931
|
-
const selectOutputForAgent = () => {
|
|
932
|
-
try {
|
|
933
|
-
return effectiveOutputPolicy(config.output.configPath);
|
|
934
|
-
} catch (error) {
|
|
935
|
-
logger.warn(`Odai output configuration is invalid; using the default policy: ${error instanceof Error ? error.message : String(error)}`);
|
|
936
|
-
return Object.freeze({
|
|
937
|
-
policy: DEFAULT_OUTPUT_POLICY,
|
|
938
|
-
source: "default",
|
|
939
|
-
status: "fallback",
|
|
940
|
-
reasonCode: "output-config-invalid",
|
|
941
|
-
});
|
|
942
|
-
}
|
|
943
|
-
};
|
|
944
|
-
const memorySettingsSnapshots = new WeakMap();
|
|
945
|
-
const memorySettingsFor = (agent, turn = currentAgentTurn(agent)) => {
|
|
946
|
-
const cached = memorySettingsSnapshots.get(agent);
|
|
947
|
-
if (cached && cached.turn === turn) return cached.settings;
|
|
948
|
-
let settings;
|
|
949
|
-
try {
|
|
950
|
-
settings = effectiveMemorySettings(config.memory.storePath, { mode: config.memory.mode });
|
|
951
|
-
} catch (error) {
|
|
952
|
-
logger.warn(`Odai semantic memory is unavailable; capture and retrieval are disabled for this turn: ${error instanceof Error ? error.message : String(error)}`);
|
|
953
|
-
settings = Object.freeze({ mode: "off", source: "invalid-store" });
|
|
954
|
-
}
|
|
955
|
-
memorySettingsSnapshots.set(agent, { turn, settings });
|
|
956
|
-
return settings;
|
|
957
|
-
};
|
|
958
|
-
const contextActivationFor = (agent, text, turn = currentAgentTurn(agent)) => activateRequestedCapabilities(
|
|
959
|
-
classifyContextActivation(text),
|
|
960
|
-
requestedContextCapabilities(evidence.events(agent), turn),
|
|
961
|
-
);
|
|
962
|
-
const routingSnapshots = new WeakMap();
|
|
963
|
-
const routingSnapshotFor = (agent, turn = currentAgentTurn(agent)) => {
|
|
964
|
-
const cached = routingSnapshots.get(agent);
|
|
965
|
-
if (cached && cached.turn === turn) return cached.state;
|
|
966
|
-
let state;
|
|
967
|
-
try {
|
|
968
|
-
state = Object.freeze({
|
|
969
|
-
snapshot: effectiveRoutingSnapshot(config.routing.configPath, config.routing.roles),
|
|
970
|
-
});
|
|
971
|
-
} catch (error) {
|
|
972
|
-
state = Object.freeze({
|
|
973
|
-
error: "persisted Odai routing configuration failed validation",
|
|
974
|
-
detail: error instanceof Error ? error.message : String(error),
|
|
975
|
-
});
|
|
976
|
-
}
|
|
977
|
-
routingSnapshots.set(agent, { turn, state });
|
|
978
|
-
return state;
|
|
979
|
-
};
|
|
980
|
-
ctx.on("system-prompt/assemble", async (assembly, context, next) => {
|
|
981
|
-
const agent = context.agent;
|
|
982
|
-
if (!agent) return next();
|
|
983
|
-
const turn = currentAgentTurn(agent);
|
|
984
|
-
const childSession = isSubagentSession(agent);
|
|
985
|
-
const directMessage = latestDirectUserMessage(agent);
|
|
986
|
-
const activation = contextActivationFor(agent, directMessage ? extractLatestUserText([directMessage]) : "", turn);
|
|
987
|
-
const proposedStep = (currentAgentStep(agent) ?? 0) + 1;
|
|
988
|
-
const exposureEvents = evidence.events(agent);
|
|
989
|
-
const routeCardNeeded = !childSession && (Boolean(activeRouteCard(exposureEvents))
|
|
990
|
-
|| ["planner", "executor"].includes(pendingResponsibilityGap(agent, turn, proposedStep)?.responsibility)
|
|
991
|
-
|| pendingResponsibilityInterruption(exposureEvents)?.responsibility === "executor");
|
|
992
|
-
// DSH builds assembly.tools before this middleware runs. Filter that snapshot and the execution registry together.
|
|
993
|
-
const activeToolNames = syncToolExposure(agent, activation, { turn, step: proposedStep, routeCard: routeCardNeeded });
|
|
994
|
-
const executableSchemas = typeof ctx.tools.schemas === "function" ? ctx.tools.schemas(agent) : [];
|
|
995
|
-
const visibleAssembly = reconcileAdaptiveToolSchemas(assembly, activeToolNames, executableSchemas);
|
|
996
|
-
if (visibleAssembly !== assembly) assembly.tools = visibleAssembly.tools;
|
|
997
|
-
|
|
998
|
-
const downstream = await next();
|
|
999
|
-
const finalExecutableSchemas = typeof ctx.tools.schemas === "function" ? ctx.tools.schemas(agent) : [];
|
|
1000
|
-
const reconciledDownstream = reconcileAdaptiveToolSchemas(downstream, activeToolNames, finalExecutableSchemas);
|
|
1001
|
-
const selection = await selectSharedSkillForTurn(agent, () => selectForAgent(agent, context));
|
|
1002
|
-
const outputSelection = childSession
|
|
1003
|
-
? Object.freeze({ policy: DEFAULT_OUTPUT_POLICY, source: "default" })
|
|
1004
|
-
: await selectSharedOutputPolicyForTurn(agent, turn, selectOutputForAgent);
|
|
1005
|
-
const selectionEvidence = {
|
|
1006
|
-
...(turn === undefined ? {} : { turn }),
|
|
1007
|
-
requestedMode: selection.mode,
|
|
1008
|
-
status: selection.status,
|
|
1009
|
-
reasonCode: selection.reasonCode,
|
|
1010
|
-
effectiveSource: selection.bundle.source,
|
|
1011
|
-
skillVersion: selection.bundle.manifest.skillVersion,
|
|
1012
|
-
runtimeContract: selection.bundle.manifest.runtimeContract,
|
|
1013
|
-
digest: selection.bundle.digest,
|
|
1014
|
-
rejections: selection.rejections.map(({ source, reasonCode }) => ({ source, reasonCode })),
|
|
1015
|
-
...(selection.evolution?.generationId ? {
|
|
1016
|
-
evolution: {
|
|
1017
|
-
status: selection.evolution.status,
|
|
1018
|
-
generationId: selection.evolution.generationId,
|
|
1019
|
-
...(selection.evolution.baseDigest ? { baseDigest: selection.evolution.baseDigest } : {}),
|
|
1020
|
-
...(selection.evolution.upstreamDigest ? { upstreamDigest: selection.evolution.upstreamDigest } : {}),
|
|
1021
|
-
...(selection.evolution.rebaseRequired === undefined ? {} : { rebaseRequired: selection.evolution.rebaseRequired }),
|
|
1022
|
-
},
|
|
1023
|
-
} : {}),
|
|
1024
|
-
};
|
|
1025
|
-
if (!hasSessionEvent(agent, "odai/skill-selected", (data) => data?.turn === turn && data?.digest === selection.bundle.digest)) {
|
|
1026
|
-
appendEvent(agent, "odai/skill-selected", selectionEvidence);
|
|
1027
|
-
}
|
|
1028
|
-
if (selection.status === "fallback") {
|
|
1029
|
-
logger.warn(`Odai skill source ${selection.mode} fell back to bundled governance (${selection.reasonCode})`);
|
|
1030
|
-
}
|
|
1031
|
-
const outputPrompt = renderOutputPolicyPrompt(outputSelection.policy);
|
|
1032
|
-
const routingPrompt = !activation.routingConfig
|
|
1033
|
-
? ""
|
|
1034
|
-
: childSession
|
|
1035
|
-
? ROUTING_CONFIG_PROMPT
|
|
1036
|
-
: `${ROUTING_CONFIG_PROMPT}\n\n${renderEffectiveRoutingContext(routingSnapshotFor(agent, turn))}`;
|
|
1037
|
-
let continuityRecordPrompt;
|
|
1038
|
-
if (!childSession && (activation.care || activation.safety || activation.continuity)) {
|
|
1039
|
-
try {
|
|
1040
|
-
continuityRecordPrompt = renderHumanSafetyContinuitySection(
|
|
1041
|
-
readHumanSafetyContinuityStore(humanSafetyContinuityStorePath),
|
|
1042
|
-
);
|
|
1043
|
-
} catch (error) {
|
|
1044
|
-
logger.warn(`Odai human-safety continuity is unavailable for this turn: ${error instanceof Error ? error.message : String(error)}`);
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
const continuityPrompt = [activation.continuity ? HUMAN_SAFETY_CONTINUITY_PROMPT : undefined, continuityRecordPrompt]
|
|
1048
|
-
.filter(Boolean)
|
|
1049
|
-
.join("\n\n");
|
|
1050
|
-
if (outputPrompt && !hasSessionEvent(agent, "odai/output-policy-selected", (data) => data?.turn === turn)) {
|
|
1051
|
-
appendEvent(agent, "odai/output-policy-selected", {
|
|
1052
|
-
...(turn === undefined ? {} : { turn }),
|
|
1053
|
-
source: outputSelection.source,
|
|
1054
|
-
policy: outputSelection.policy,
|
|
1055
|
-
});
|
|
1056
|
-
}
|
|
1057
|
-
return {
|
|
1058
|
-
...reconciledDownstream,
|
|
1059
|
-
sections: reconciledDownstream.sections.map((section) => {
|
|
1060
|
-
if (section.name === "odai:canonical-governance") return { ...section, text: canonicalPrompt(selection) };
|
|
1061
|
-
if (section.name === "odai:routing-configuration") return { ...section, text: routingPrompt };
|
|
1062
|
-
if (section.name === "odai:human-safety-continuity") return { ...section, text: continuityPrompt };
|
|
1063
|
-
if (section.name === "odai:responsibility-gap") return { ...section, text: childSession ? "" : RESPONSIBILITY_GAP_PROMPT };
|
|
1064
|
-
if (section.name === "odai:route-card") return { ...section, text: routeCardNeeded ? ROUTE_CARD_PROMPT : "" };
|
|
1065
|
-
if (section.name === "odai:skill-source-configuration") return { ...section, text: activation.skillSource ? SKILL_SOURCE_CONFIG_PROMPT : "" };
|
|
1066
|
-
if (section.name === "odai:controller-output-policy") return { ...section, text: outputPrompt };
|
|
1067
|
-
if (section.name === "odai:compaction-model-configuration") return { ...section, text: activation.compactionConfig ? COMPACTION_CONFIG_PROMPT : "" };
|
|
1068
|
-
if (section.name === "odai:semantic-memory") return { ...section, text: activation.memory ? MEMORY_PROMPT : "" };
|
|
1069
|
-
return section;
|
|
1070
|
-
}),
|
|
1071
|
-
};
|
|
1072
|
-
});
|
|
1073
|
-
|
|
1074
|
-
const routeProtections = new WeakMap();
|
|
1075
|
-
const responsibilityScopes = new WeakMap();
|
|
1076
|
-
const responsibilityScopeOwners = new WeakMap();
|
|
1077
|
-
const pendingRouteReceipts = new WeakMap();
|
|
1078
|
-
const pendingScopeRestorations = new WeakMap();
|
|
1079
|
-
const outputUsageBySession = new WeakMap();
|
|
1080
|
-
const stopResponsibilityScope = (agent, reason, position = {}) => {
|
|
1081
|
-
const scope = responsibilityScopes.get(agent);
|
|
1082
|
-
if (!scope || (position.scopeId && position.scopeId !== scope.id)) return undefined;
|
|
1083
|
-
responsibilityScopes.delete(agent);
|
|
1084
|
-
const protection = routeProtections.get(agent);
|
|
1085
|
-
if (protection?.scopeId === scope.id) routeProtections.delete(agent);
|
|
1086
|
-
appendEvent(agent, "odai/route-protection-released", {
|
|
1087
|
-
scopeId: scope.id,
|
|
1088
|
-
turn: scope.turn,
|
|
1089
|
-
reason,
|
|
1090
|
-
});
|
|
1091
|
-
appendEvent(agent, "odai/responsibility-scope-stopped", responsibilityScopeStoppedEvent(scope, reason, position));
|
|
1092
|
-
return scope;
|
|
1093
|
-
};
|
|
1094
|
-
const stopDanglingResponsibilityScope = (agent, reason) => {
|
|
1095
|
-
if (responsibilityScopes.has(agent)) return undefined;
|
|
1096
|
-
const dangling = latestDanglingResponsibilityScope(evidence.events(agent));
|
|
1097
|
-
if (!dangling) return undefined;
|
|
1098
|
-
appendEvent(agent, "odai/route-protection-released", {
|
|
1099
|
-
scopeId: dangling.scopeId,
|
|
1100
|
-
turn: dangling.turn,
|
|
1101
|
-
reason,
|
|
1102
|
-
});
|
|
1103
|
-
appendEvent(agent, "odai/responsibility-scope-stopped", {
|
|
1104
|
-
...dangling,
|
|
1105
|
-
reason,
|
|
1106
|
-
});
|
|
1107
|
-
return dangling;
|
|
1108
|
-
};
|
|
1109
|
-
const configuredRole = (agent, role, turn = currentAgentTurn(agent)) => {
|
|
1110
|
-
const state = routingSnapshotFor(agent, turn);
|
|
1111
|
-
if (state.error) return state;
|
|
1112
|
-
return {
|
|
1113
|
-
route: state.snapshot.roles[role],
|
|
1114
|
-
source: state.snapshot.sources[role],
|
|
1115
|
-
};
|
|
1116
|
-
};
|
|
1117
|
-
const pendingResponsibilityGap = (agent, turn, step) => {
|
|
1118
|
-
const consumed = new Set();
|
|
1119
|
-
const events = evidence.events(agent);
|
|
1120
|
-
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
1121
|
-
const event = events[index];
|
|
1122
|
-
if (event.data?.turn !== turn) continue;
|
|
1123
|
-
if (event.type === "odai/responsibility-gap-consumed") {
|
|
1124
|
-
consumed.add(event.data.stateDigest);
|
|
1125
|
-
continue;
|
|
1126
|
-
}
|
|
1127
|
-
if (event.type !== "odai/responsibility-gap"
|
|
1128
|
-
|| !Number.isSafeInteger(event.data?.step)
|
|
1129
|
-
|| event.data.step >= step
|
|
1130
|
-
|| consumed.has(event.data.stateDigest)) continue;
|
|
1131
|
-
return event.data;
|
|
1132
|
-
}
|
|
1133
|
-
return undefined;
|
|
1134
|
-
};
|
|
1135
|
-
const invalidateFailedRoleRoute = (agent, role, route, source, failure, position = {}) => {
|
|
1136
|
-
let invalidation = Object.freeze({ invalidated: false, reason: "not-deterministic-or-not-persisted" });
|
|
1137
|
-
if (failure.kind === "deterministic" && source === "persisted-mapping") {
|
|
1138
|
-
try {
|
|
1139
|
-
invalidation = invalidatePersistedRoleRoute(config.routing.configPath, role, route);
|
|
1140
|
-
} catch (error) {
|
|
1141
|
-
invalidation = Object.freeze({
|
|
1142
|
-
invalidated: false,
|
|
1143
|
-
reason: "cleanup-failed",
|
|
1144
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1145
|
-
});
|
|
1146
|
-
}
|
|
1147
|
-
}
|
|
1148
|
-
appendEvent(agent, "odai/route-health", {
|
|
1149
|
-
...position,
|
|
1150
|
-
responsibility: role,
|
|
1151
|
-
routeSource: source,
|
|
1152
|
-
requestedRoute: route,
|
|
1153
|
-
status: failure.kind === "deterministic" ? "invalid" : "unhealthy",
|
|
1154
|
-
failureKind: failure.kind,
|
|
1155
|
-
errorCode: failure.code,
|
|
1156
|
-
error: failure.message,
|
|
1157
|
-
invalidated: invalidation.invalidated,
|
|
1158
|
-
...(invalidation.backupPath ? { backupPath: invalidation.backupPath } : {}),
|
|
1159
|
-
...(invalidation.reason ? { cleanupReason: invalidation.reason } : {}),
|
|
1160
|
-
...(invalidation.error ? { cleanupError: invalidation.error } : {}),
|
|
1161
|
-
});
|
|
1162
|
-
return invalidation;
|
|
1163
|
-
};
|
|
1164
|
-
const onDenied = (execution, reason) => {
|
|
1165
|
-
appendEvent(execution.agent, "odai/governance-denied", {
|
|
1166
|
-
callId: String(execution.callId),
|
|
1167
|
-
tool: execution.name,
|
|
1168
|
-
reason,
|
|
1169
|
-
}, logger);
|
|
1170
|
-
};
|
|
1171
|
-
const childGuard = createChildToolGuard({
|
|
1172
|
-
additionalDeniedTools: config.governance.additionalDeniedTools,
|
|
1173
|
-
onDenied,
|
|
1174
|
-
});
|
|
1175
|
-
const routeProtectionGuard = createRouteProtectionGuard({
|
|
1176
|
-
additionalDeniedTools: config.governance.additionalDeniedTools,
|
|
1177
|
-
onDenied,
|
|
1178
|
-
protectionFor(agent) {
|
|
1179
|
-
if (config.routing.mode === "off") return undefined;
|
|
1180
|
-
return routeProtections.get(agent) ?? activeRouteProtection(agent, evidence.events(agent));
|
|
1181
|
-
},
|
|
1182
|
-
});
|
|
1183
|
-
ctx.tools.register(createContextCapabilityTool({
|
|
1184
|
-
isChild: isSubagent,
|
|
1185
|
-
onRequested(agent, capability) {
|
|
1186
|
-
const turn = currentAgentTurn(agent);
|
|
1187
|
-
const step = currentAgentStep(agent);
|
|
1188
|
-
appendEvent(agent, "odai/context-capability-requested", {
|
|
1189
|
-
...(turn === undefined ? {} : { turn }),
|
|
1190
|
-
...(step === undefined ? {} : { step }),
|
|
1191
|
-
capability,
|
|
1192
|
-
});
|
|
1193
|
-
},
|
|
1194
|
-
}));
|
|
1195
|
-
ctx.tools.register(createRoutingConfigTool(config.routing.configPath, {
|
|
1196
|
-
configuredRoles: config.routing.roles,
|
|
1197
|
-
resolveCallConfig(route, signal) {
|
|
1198
|
-
return ctx.llm.resolveCallConfig(route, signal);
|
|
1199
|
-
},
|
|
1200
|
-
latestRouteFor(agent) {
|
|
1201
|
-
return latestRouteReceipt(evidence.events(agent));
|
|
1202
|
-
},
|
|
1203
|
-
outputPolicyFor() {
|
|
1204
|
-
return selectOutputForAgent().policy;
|
|
1205
|
-
},
|
|
1206
|
-
onConfigured(agent, data) {
|
|
1207
|
-
appendEvent(agent, "odai/routing-configured", data);
|
|
1208
|
-
},
|
|
1209
|
-
}));
|
|
1210
|
-
ctx.tools.register(createHumanCareTool({
|
|
1211
|
-
isChild: isSubagent,
|
|
1212
|
-
contractFor(agent) {
|
|
1213
|
-
const bundle = sharedSkillSelection(agent)?.bundle ?? bundled;
|
|
1214
|
-
return readSkillBundleFile(bundle, HUMAN_CARE_REFERENCE_PATH).toString("utf8");
|
|
1215
|
-
},
|
|
1216
|
-
}));
|
|
1217
|
-
ctx.tools.register(createHumanSafetyTool({
|
|
1218
|
-
isChild: isSubagent,
|
|
1219
|
-
contractFor(agent) {
|
|
1220
|
-
const bundle = sharedSkillSelection(agent)?.bundle ?? bundled;
|
|
1221
|
-
return readSkillBundleFile(bundle, HUMAN_SAFETY_REFERENCE_PATH).toString("utf8");
|
|
1222
|
-
},
|
|
1223
|
-
}));
|
|
1224
|
-
ctx.tools.register(createResponsibilityGapTool({
|
|
1225
|
-
isChild: isSubagent,
|
|
1226
|
-
onProposed(agent, proposal) {
|
|
1227
|
-
const turn = currentAgentTurn(agent);
|
|
1228
|
-
const step = currentAgentStep(agent);
|
|
1229
|
-
appendEvent(agent, "odai/responsibility-gap", {
|
|
1230
|
-
...(turn === undefined ? {} : { turn }),
|
|
1231
|
-
...(step === undefined ? {} : { step }),
|
|
1232
|
-
...proposal,
|
|
1233
|
-
});
|
|
1234
|
-
},
|
|
1235
|
-
}));
|
|
1236
|
-
ctx.tools.register(createRouteCardTool({
|
|
1237
|
-
activeFor(agent) {
|
|
1238
|
-
return activeRouteCard(evidence.events(agent));
|
|
1239
|
-
},
|
|
1240
|
-
unsettledFor(agent) {
|
|
1241
|
-
return unsettledRouteCard(evidence.events(agent));
|
|
1242
|
-
},
|
|
1243
|
-
authorizationFor(agent) {
|
|
1244
|
-
const message = latestDirectUserMessage(agent);
|
|
1245
|
-
if (!message) return Object.freeze({ status: "unknown" });
|
|
1246
|
-
return Object.freeze({
|
|
1247
|
-
...classifyImplementationAuthorization(extractLatestUserText([message])),
|
|
1248
|
-
userMessageId: message.id,
|
|
1249
|
-
});
|
|
1250
|
-
},
|
|
1251
|
-
onFrozen(agent, card) {
|
|
1252
|
-
appendEvent(agent, "odai/route-card-frozen", { card });
|
|
1253
|
-
},
|
|
1254
|
-
onCleared(agent, cardId) {
|
|
1255
|
-
appendEvent(agent, "odai/route-card-cleared", { cardId });
|
|
1256
|
-
},
|
|
1257
|
-
}));
|
|
1258
|
-
ctx.tools.register(createSkillSourceConfigTool(
|
|
1259
|
-
config.governance.skillConfigPath,
|
|
1260
|
-
config.governance.skillSource,
|
|
1261
|
-
{
|
|
1262
|
-
explicitPath: explicitSkillPath,
|
|
1263
|
-
onConfigured(agent, data) {
|
|
1264
|
-
appendEvent(agent, "odai/skill-source-configured", data);
|
|
1265
|
-
},
|
|
1266
|
-
},
|
|
1267
|
-
));
|
|
1268
|
-
ctx.tools.register(createSkillEvolutionTool(config.governance.evolutionRoot, {
|
|
1269
|
-
disabled: evolutionDisabled,
|
|
1270
|
-
currentSelectionFor(agent) {
|
|
1271
|
-
return sharedSkillSelection(agent)
|
|
1272
|
-
?? applySkillEvolutionSelection(baseSelection, config.governance.evolutionRoot, { disabled: evolutionDisabled });
|
|
1273
|
-
},
|
|
1274
|
-
onChanged(agent, data) {
|
|
1275
|
-
appendEvent(agent, `odai/evolution-${data.action}`, data);
|
|
1276
|
-
},
|
|
1277
|
-
}));
|
|
1278
|
-
ctx.tools.register(createOutputConfigTool(config.output.configPath, {
|
|
1279
|
-
isChild: isSubagent,
|
|
1280
|
-
responsibilityRoutesFor() {
|
|
1281
|
-
try {
|
|
1282
|
-
return effectiveRoutingSnapshot(config.routing.configPath, config.routing.roles).roles;
|
|
1283
|
-
} catch {
|
|
1284
|
-
return undefined;
|
|
1285
|
-
}
|
|
1286
|
-
},
|
|
1287
|
-
onConfigured(agent, data) {
|
|
1288
|
-
appendEvent(agent, "odai/output-configured", data);
|
|
1289
|
-
},
|
|
1290
|
-
}));
|
|
1291
|
-
ctx.tools.register(createCompactionConfigTool(config.compaction.configPath, {
|
|
1292
|
-
isChild: isSubagent,
|
|
1293
|
-
resolveCallConfig(route, signal) {
|
|
1294
|
-
return ctx.llm.resolveCallConfig(route, signal);
|
|
1295
|
-
},
|
|
1296
|
-
onConfigured(agent, data) {
|
|
1297
|
-
appendEvent(agent, "odai/compaction-configured", data);
|
|
1298
|
-
},
|
|
1299
|
-
}));
|
|
1300
|
-
ctx.tools.register(createSemanticMemoryTool(config.memory.storePath, {
|
|
1301
|
-
configuredMode: config.memory.mode,
|
|
1302
|
-
onChanged(agent, data) {
|
|
1303
|
-
appendEvent(agent, "odai/memory-changed", data);
|
|
1304
|
-
},
|
|
1305
|
-
}));
|
|
1306
|
-
ctx.tools.register(createHumanSafetyContinuityTool({
|
|
1307
|
-
storePath: humanSafetyContinuityStorePath,
|
|
1308
|
-
directUserTextFor(agent) {
|
|
1309
|
-
const message = latestDirectUserMessage(agent);
|
|
1310
|
-
return message ? extractLatestUserText([message]) : "";
|
|
1311
|
-
},
|
|
1312
|
-
onChanged(agent, data) {
|
|
1313
|
-
appendEvent(agent, "odai/human-safety-continuity-changed", data);
|
|
1314
|
-
},
|
|
1315
|
-
}));
|
|
1316
|
-
ctx.tools.guard((execution) => childGuard(execution) ?? routeProtectionGuard(execution));
|
|
1317
|
-
|
|
1318
|
-
const toolExposureStates = new WeakMap();
|
|
1319
|
-
const syncToolExposure = (agent, activation, options = {}) => {
|
|
1320
|
-
const child = isSubagentSession(agent);
|
|
1321
|
-
const activeNames = activeOdaiToolNames(activation, { child, routeCard: options.routeCard === true });
|
|
1322
|
-
const deniedNames = [
|
|
1323
|
-
...inactiveOdaiToolNames(activeNames),
|
|
1324
|
-
...(child ? ODAI_CORE_TOOL_NAMES : []),
|
|
1325
|
-
];
|
|
1326
|
-
const key = deniedNames.join("\u0000");
|
|
1327
|
-
const previous = toolExposureStates.get(agent);
|
|
1328
|
-
if (previous?.key === key || ["unsupported", "fallback"].includes(previous?.key)) return activeNames;
|
|
1329
|
-
previous?.dispose?.();
|
|
1330
|
-
const restrict = agent?.ctx?.tools?.restrict;
|
|
1331
|
-
if (typeof restrict !== "function") {
|
|
1332
|
-
toolExposureStates.set(agent, Object.freeze({ key: "unsupported", dispose: undefined }));
|
|
1333
|
-
return activeNames;
|
|
1334
|
-
}
|
|
1335
|
-
try {
|
|
1336
|
-
const dispose = deniedNames.length > 0
|
|
1337
|
-
? restrict.call(agent.ctx.tools, { deny: deniedNames })
|
|
1338
|
-
: undefined;
|
|
1339
|
-
toolExposureStates.set(agent, Object.freeze({ key, dispose }));
|
|
1340
|
-
appendEvent(agent, "odai/tool-exposure-selected", {
|
|
1341
|
-
...(options.turn === undefined ? {} : { turn: options.turn }),
|
|
1342
|
-
...(options.step === undefined ? {} : { step: options.step }),
|
|
1343
|
-
mode: "adaptive",
|
|
1344
|
-
activeTools: activeNames,
|
|
1345
|
-
});
|
|
1346
|
-
} catch (error) {
|
|
1347
|
-
logger.warn(`Odai adaptive execution restriction is unavailable; prompt schemas remain limited while the executable catalog stays complete: ${error instanceof Error ? error.message : String(error)}`);
|
|
1348
|
-
toolExposureStates.set(agent, Object.freeze({ key: "fallback", dispose: undefined }));
|
|
1349
|
-
}
|
|
1350
|
-
return activeNames;
|
|
1351
|
-
};
|
|
1352
|
-
|
|
1353
|
-
ctx.on("tools/result", (execution, result) => {
|
|
1354
|
-
if (!execution.agent) return;
|
|
1355
|
-
const summary = summarizeToolResult(execution, result);
|
|
1356
|
-
if (hasSessionEvent(execution.agent, "odai/tool-observed", (data) => data?.callId === summary.callId)) return;
|
|
1357
|
-
appendEvent(execution.agent, "odai/tool-observed", summary, logger);
|
|
1358
|
-
});
|
|
1359
|
-
|
|
1360
|
-
ctx.on("agent/request", async ({ agent, turn, step, signal }, next) => {
|
|
1361
|
-
let proposed = await next();
|
|
1362
|
-
const childRole = routedRoleOf(agent);
|
|
1363
|
-
const childRoleState = childRole ? configuredRole(agent, childRole, turn) : undefined;
|
|
1364
|
-
if (childRole && !childRoleState.route) {
|
|
1365
|
-
throw new Error(childRoleState.error
|
|
1366
|
-
? `Odai ${childRole} child route is unavailable: ${childRoleState.detail}`
|
|
1367
|
-
: `Odai ${childRole} child route is not configured`);
|
|
1368
|
-
}
|
|
1369
|
-
let scope = responsibilityScopes.get(agent);
|
|
1370
|
-
if (scope && !responsibilityScopeOwnsRequest(scope, turn, step)) {
|
|
1371
|
-
stopResponsibilityScope(agent, "ownership-boundary", { step });
|
|
1372
|
-
scope = undefined;
|
|
1373
|
-
}
|
|
1374
|
-
if (!childRole && !scope) {
|
|
1375
|
-
const restoration = pendingResponsibilityScopeRestoration(evidence.events(agent));
|
|
1376
|
-
if (restoration && sameRequestModelRoute(proposed, restoration.temporaryRoute)) {
|
|
1377
|
-
const { reasoningEffort: _temporaryEffort, maxTokens: _temporaryMaxTokens, ...withoutTemporaryRoute } = proposed;
|
|
1378
|
-
proposed = Object.freeze({ ...withoutTemporaryRoute, ...restoration.baseRoute });
|
|
1379
|
-
appendEvent(agent, "odai/responsibility-scope-restoration-requested", {
|
|
1380
|
-
scopeId: restoration.scopeId,
|
|
1381
|
-
turn,
|
|
1382
|
-
step,
|
|
1383
|
-
role: restoration.role,
|
|
1384
|
-
requestedRoute: restoration.baseRoute,
|
|
1385
|
-
});
|
|
1386
|
-
if (agent?.session) {
|
|
1387
|
-
pendingScopeRestorations.set(agent.session, Object.freeze({
|
|
1388
|
-
agent,
|
|
1389
|
-
scopeId: restoration.scopeId,
|
|
1390
|
-
turn,
|
|
1391
|
-
step,
|
|
1392
|
-
role: restoration.role,
|
|
1393
|
-
expectedRoute: restoration.baseRoute,
|
|
1394
|
-
}));
|
|
40
|
+
catch (error) {
|
|
41
|
+
logger.warn(`failed to record ${type}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1395
42
|
}
|
|
1396
|
-
}
|
|
1397
|
-
}
|
|
1398
|
-
const upgradeRole = scope?.role;
|
|
1399
|
-
let roleRoute = childRole
|
|
1400
|
-
? childRoleState.route
|
|
1401
|
-
: scope
|
|
1402
|
-
? scope.route
|
|
1403
|
-
: undefined;
|
|
1404
|
-
const routeSource = childRole ? childRoleState?.source : scope?.source;
|
|
1405
|
-
let routeMode = childRole ? "child" : sameRequestModelRoute(proposed, roleRoute) ? "inline" : "same-turn";
|
|
1406
|
-
let scopedResponsibilityMaxTokens = upgradeRole ? roleRoute?.maxTokens : undefined;
|
|
1407
|
-
const finalize = (finalRequest) => {
|
|
1408
|
-
if (roleRoute && agent?.session && Number.isSafeInteger(turn) && Number.isSafeInteger(step)) {
|
|
1409
|
-
const expectedRoute = roleRoute;
|
|
1410
|
-
const receiptScope = responsibilityScopeOwnsRequest(responsibilityScopes.get(agent), turn, step)
|
|
1411
|
-
? responsibilityScopes.get(agent)
|
|
1412
|
-
: scope;
|
|
1413
|
-
pendingRouteReceipts.set(agent.session, Object.freeze({
|
|
1414
|
-
agent,
|
|
1415
|
-
turn,
|
|
1416
|
-
step,
|
|
1417
|
-
responsibility: childRole ?? upgradeRole,
|
|
1418
|
-
routeMode,
|
|
1419
|
-
routeSource,
|
|
1420
|
-
...(receiptScope?.id ? { responsibilityScopeId: receiptScope.id } : {}),
|
|
1421
|
-
...(receiptScope?.cardId ? { routeCardId: receiptScope.cardId } : {}),
|
|
1422
|
-
...(receiptScope?.resumeOfScopeId ? { resumeOfScopeId: receiptScope.resumeOfScopeId } : {}),
|
|
1423
|
-
requestedRoute: expectedRoute,
|
|
1424
|
-
expectedRoute,
|
|
1425
|
-
}));
|
|
1426
|
-
}
|
|
1427
|
-
return finalRequest;
|
|
1428
43
|
};
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
baseRoute: proposed,
|
|
1445
|
-
temporaryRoute: request,
|
|
1446
|
-
routeMode,
|
|
1447
|
-
});
|
|
1448
|
-
responsibilityScopes.set(agent, scope);
|
|
1449
|
-
appendEvent(agent, "odai/responsibility-scope-claimed", responsibilityScopeClaimedEvent(scope));
|
|
1450
|
-
}
|
|
1451
|
-
const validation = scope?.routeValidated
|
|
1452
|
-
? Object.freeze({ status: "verified" })
|
|
1453
|
-
: await probeModelRoute(
|
|
1454
|
-
(candidate, candidateSignal) => ctx.llm.resolveCallConfig(candidate, candidateSignal),
|
|
1455
|
-
request,
|
|
1456
|
-
signal,
|
|
1457
|
-
);
|
|
1458
|
-
if (validation.status === "rejected") {
|
|
1459
|
-
const responsibility = childRole ?? upgradeRole;
|
|
1460
|
-
const invalidation = invalidateFailedRoleRoute(
|
|
1461
|
-
agent,
|
|
1462
|
-
responsibility,
|
|
1463
|
-
roleRoute,
|
|
1464
|
-
routeSource,
|
|
1465
|
-
validation.failure,
|
|
1466
|
-
{ turn, step },
|
|
1467
|
-
);
|
|
1468
|
-
appendEvent(agent, "odai/route-fallback", {
|
|
1469
|
-
turn,
|
|
1470
|
-
step,
|
|
1471
|
-
responsibility,
|
|
1472
|
-
...(scope?.id ? { responsibilityScopeId: scope.id } : {}),
|
|
1473
|
-
...(scope?.cardId ? { routeCardId: scope.cardId } : {}),
|
|
1474
|
-
routeMode,
|
|
1475
|
-
routeSource,
|
|
1476
|
-
requestedRoute: roleRoute,
|
|
1477
|
-
fallbackUsed: true,
|
|
1478
|
-
fallbackRoute: routeFromConfig(proposed),
|
|
1479
|
-
failureKind: validation.failure.kind,
|
|
1480
|
-
errorCode: validation.failure.code,
|
|
1481
|
-
error: validation.failure.message,
|
|
1482
|
-
invalidated: invalidation.invalidated,
|
|
1483
|
-
});
|
|
1484
|
-
if (scope?.cardId) {
|
|
1485
|
-
appendEvent(agent, "odai/route-card-claim-released", {
|
|
1486
|
-
cardId: scope.cardId,
|
|
1487
|
-
turn,
|
|
1488
|
-
step,
|
|
1489
|
-
reason: "route-validation-failed",
|
|
1490
|
-
});
|
|
1491
|
-
}
|
|
1492
|
-
if (childRole) {
|
|
1493
|
-
const error = new Error(`Odai ${childRole} route failed validation: ${validation.failure.code}: ${validation.failure.message}`);
|
|
1494
|
-
error.code = validation.failure.code;
|
|
1495
|
-
error.routeFailureKind = validation.failure.kind;
|
|
1496
|
-
throw error;
|
|
1497
|
-
}
|
|
1498
|
-
stopResponsibilityScope(agent, "route-validation-failed", { step });
|
|
1499
|
-
if (scope?.decision && requiresFailClosedProtection(scope.decision)) {
|
|
1500
|
-
protectController(agent, turn, step, scope.decision, "route-validation", validation.failure.message);
|
|
1501
|
-
}
|
|
1502
|
-
roleRoute = undefined;
|
|
1503
|
-
routeMode = "same-turn";
|
|
1504
|
-
scopedResponsibilityMaxTokens = undefined;
|
|
1505
|
-
request = proposed;
|
|
1506
|
-
}
|
|
1507
|
-
}
|
|
1508
|
-
if (childRole || isSubagentSession(agent)) return finalize(request);
|
|
1509
|
-
|
|
1510
|
-
const outputSelection = await selectSharedOutputPolicyForTurn(agent, turn, selectOutputForAgent);
|
|
1511
|
-
const configuredMaxTokens = outputSelection.policy.maxTokens;
|
|
1512
|
-
if (scopedResponsibilityMaxTokens !== undefined) {
|
|
1513
|
-
if (!hasSessionEvent(agent, "odai/output-budget-overridden", (data) => data?.turn === turn && data?.step === step)) {
|
|
1514
|
-
appendEvent(agent, "odai/output-budget-overridden", {
|
|
1515
|
-
turn,
|
|
1516
|
-
...(step === undefined ? {} : { step }),
|
|
1517
|
-
responsibility: upgradeRole,
|
|
1518
|
-
responsibilityMaxTokens: scopedResponsibilityMaxTokens,
|
|
1519
|
-
...(configuredMaxTokens === undefined ? {} : { configuredControllerMaxTokens: configuredMaxTokens }),
|
|
1520
|
-
effectiveMaxTokens: scopedResponsibilityMaxTokens,
|
|
1521
|
-
budgetSource: "responsibility-override",
|
|
1522
|
-
semantics: "explicit-responsibility-override",
|
|
1523
|
-
});
|
|
1524
|
-
}
|
|
1525
|
-
return finalize(Object.freeze({ ...request, maxTokens: scopedResponsibilityMaxTokens }));
|
|
1526
|
-
}
|
|
1527
|
-
if (configuredMaxTokens === undefined) return finalize(request);
|
|
1528
|
-
const priorMaxTokens = request.maxTokens;
|
|
1529
|
-
const effectiveMaxTokens = priorMaxTokens === undefined
|
|
1530
|
-
? configuredMaxTokens
|
|
1531
|
-
: Math.min(priorMaxTokens, configuredMaxTokens);
|
|
1532
|
-
const budgetSource = priorMaxTokens !== undefined && priorMaxTokens < configuredMaxTokens
|
|
1533
|
-
? "preexisting-request-ceiling"
|
|
1534
|
-
: "controller-policy";
|
|
1535
|
-
if (!hasSessionEvent(agent, "odai/output-budget-applied", (data) => data?.turn === turn && data?.step === step)) {
|
|
1536
|
-
appendEvent(agent, "odai/output-budget-applied", {
|
|
1537
|
-
turn,
|
|
1538
|
-
...(step === undefined ? {} : { step }),
|
|
1539
|
-
...(upgradeRole === undefined ? {} : { responsibility: upgradeRole }),
|
|
1540
|
-
configuredMaxTokens,
|
|
1541
|
-
...(priorMaxTokens === undefined ? {} : { priorMaxTokens }),
|
|
1542
|
-
effectiveMaxTokens,
|
|
1543
|
-
budgetSource,
|
|
1544
|
-
semantics: "provider-request-ceiling",
|
|
1545
|
-
});
|
|
1546
|
-
}
|
|
1547
|
-
return finalize(Object.freeze({ ...request, maxTokens: effectiveMaxTokens }));
|
|
1548
|
-
}, { prepend: true });
|
|
1549
|
-
|
|
1550
|
-
const routeFallbackAttempts = new WeakMap();
|
|
1551
|
-
ctx.on("agent/request-error", async ({ agent, turn, step, provider, failure, signal }, next) => {
|
|
1552
|
-
const childRole = routedRoleOf(agent);
|
|
1553
|
-
const scope = responsibilityScopes.get(agent);
|
|
1554
|
-
if (!childRole && scope && (signal.aborted || failure?.code === "CONTEXT_WINDOW_EXCEEDED")) {
|
|
1555
|
-
if (agent?.session) pendingRouteReceipts.delete(agent.session);
|
|
1556
|
-
stopResponsibilityScope(agent, signal.aborted ? "request-aborted" : "context-window-exceeded", { step });
|
|
1557
|
-
return next();
|
|
1558
|
-
}
|
|
1559
|
-
if (signal.aborted || failure?.code === "CONTEXT_WINDOW_EXCEEDED") return next();
|
|
1560
|
-
const active = childRole
|
|
1561
|
-
? { role: childRole, ...configuredRole(agent, childRole, turn) }
|
|
1562
|
-
: responsibilityScopeOwnsRequest(scope, turn, step)
|
|
1563
|
-
? scope
|
|
1564
|
-
: undefined;
|
|
1565
|
-
if (!active?.route || provider !== active.route.provider) return next();
|
|
1566
|
-
|
|
1567
|
-
const classified = classifyModelRouteFailure(failure);
|
|
1568
|
-
const invalidation = invalidateFailedRoleRoute(
|
|
1569
|
-
agent,
|
|
1570
|
-
active.role,
|
|
1571
|
-
active.route,
|
|
1572
|
-
active.source,
|
|
1573
|
-
classified,
|
|
1574
|
-
{ turn, step },
|
|
1575
|
-
);
|
|
1576
|
-
appendEvent(agent, "odai/route-fallback", {
|
|
1577
|
-
turn,
|
|
1578
|
-
step,
|
|
1579
|
-
responsibility: active.role,
|
|
1580
|
-
...(active.id ? { responsibilityScopeId: active.id } : {}),
|
|
1581
|
-
...(active.cardId ? { routeCardId: active.cardId } : {}),
|
|
1582
|
-
routeMode: childRole ? "child" : "same-turn",
|
|
1583
|
-
routeSource: active.source,
|
|
1584
|
-
requestedRoute: active.route,
|
|
1585
|
-
fallbackUsed: !childRole,
|
|
1586
|
-
fallbackRoute: childRole ? undefined : active.baseRoute ?? routeFromConfig(agent?.options),
|
|
1587
|
-
failureKind: classified.kind,
|
|
1588
|
-
errorCode: classified.code,
|
|
1589
|
-
error: classified.message,
|
|
1590
|
-
invalidated: invalidation.invalidated,
|
|
44
|
+
const hasSessionEvent = (agent, type, predicate) => evidence.has(agent, type, predicate);
|
|
45
|
+
installCompactionRuntime({
|
|
46
|
+
appendEvent,
|
|
47
|
+
applyCompactionStateProtocol,
|
|
48
|
+
applyCompactionTarget,
|
|
49
|
+
classifyModelRouteFailure,
|
|
50
|
+
config,
|
|
51
|
+
ctx,
|
|
52
|
+
effectiveCompactionTarget,
|
|
53
|
+
inheritCompactionReasoning,
|
|
54
|
+
invalidatePersistedCompactionTarget,
|
|
55
|
+
logger,
|
|
56
|
+
probeModelRoute,
|
|
57
|
+
routeFromConfig,
|
|
58
|
+
sameRequestModelRoute,
|
|
1591
59
|
});
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
if (childRole) return next();
|
|
1601
|
-
if (agent?.session) pendingRouteReceipts.delete(agent.session);
|
|
1602
|
-
stopResponsibilityScope(agent, classified.kind === "cancelled" ? "request-cancelled" : "route-request-failed", { step });
|
|
1603
|
-
if (scope?.decision && requiresFailClosedProtection(scope.decision)) {
|
|
1604
|
-
protectController(agent, turn, step, scope.decision, "route-request-failure", classified.message);
|
|
1605
|
-
}
|
|
1606
|
-
if (classified.kind === "cancelled") return next();
|
|
1607
|
-
|
|
1608
|
-
let attempts = routeFallbackAttempts.get(agent);
|
|
1609
|
-
if (!attempts) {
|
|
1610
|
-
attempts = new Set();
|
|
1611
|
-
routeFallbackAttempts.set(agent, attempts);
|
|
1612
|
-
}
|
|
1613
|
-
const key = `${turn}:${step}`;
|
|
1614
|
-
if (attempts.has(key)) return next();
|
|
1615
|
-
attempts.add(key);
|
|
1616
|
-
return { kind: "retry" };
|
|
1617
|
-
});
|
|
1618
|
-
|
|
1619
|
-
const protectController = (agent, turn, step, decision, source, failure, scopeId) => {
|
|
1620
|
-
const protection = Object.freeze({
|
|
1621
|
-
turn,
|
|
1622
|
-
step,
|
|
1623
|
-
mode: "read-only",
|
|
1624
|
-
reasonCode: decision.reasonCode,
|
|
1625
|
-
source,
|
|
1626
|
-
...(failure ? { failure } : {}),
|
|
1627
|
-
...(scopeId ? { scopeId } : {}),
|
|
60
|
+
const promptRuntime = createPromptRuntime({
|
|
61
|
+
appendEvent,
|
|
62
|
+
config,
|
|
63
|
+
ctx,
|
|
64
|
+
evidence,
|
|
65
|
+
hasSessionEvent,
|
|
66
|
+
humanSafetyContinuityStorePath,
|
|
67
|
+
logger,
|
|
1628
68
|
});
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
const
|
|
1635
|
-
const
|
|
1636
|
-
const
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
if (event?.type === "assistant/chunk" && event.data?.chunk?.type === "usage") {
|
|
1649
|
-
outputUsageBySession.set(session, {
|
|
1650
|
-
turn: event.data.turn,
|
|
1651
|
-
step: event.data.step,
|
|
1652
|
-
usage: event.data.chunk.usage,
|
|
1653
|
-
});
|
|
1654
|
-
} else if (event?.type === "assistant/message" && event.data?.usage) {
|
|
1655
|
-
outputUsageBySession.set(session, {
|
|
1656
|
-
turn: event.data.turn,
|
|
1657
|
-
step: event.data.step,
|
|
1658
|
-
usage: event.data.usage,
|
|
1659
|
-
});
|
|
1660
|
-
}
|
|
1661
|
-
|
|
1662
|
-
if (event?.type === "turn/end") {
|
|
1663
|
-
const usage = outputUsageBySession.get(session);
|
|
1664
|
-
if (event.data?.reason?.kind === "max-tokens" && owner) {
|
|
1665
|
-
const events = evidence.events(owner);
|
|
1666
|
-
const stopped = latestStoppedResponsibilityScope(events, event.data?.turn);
|
|
1667
|
-
const receipt = stopped && events.findLast((candidate) => (
|
|
1668
|
-
candidate?.type === "odai/route-applied"
|
|
1669
|
-
&& candidate.data?.status === "applied"
|
|
1670
|
-
&& candidate.data?.responsibilityScopeId === stopped.scopeId
|
|
1671
|
-
))?.data;
|
|
1672
|
-
const routeCard = stopped?.routeCardId ? routeCardById(events, stopped.routeCardId) : undefined;
|
|
1673
|
-
const observedOutputTokens = usage?.turn === stopped?.turn
|
|
1674
|
-
&& usage?.step === stopped?.stopStep
|
|
1675
|
-
&& Number.isSafeInteger(usage.usage?.outputTokens)
|
|
1676
|
-
? usage.usage.outputTokens
|
|
1677
|
-
: undefined;
|
|
1678
|
-
if (stopped?.reason === "terminal-response"
|
|
1679
|
-
&& Number.isSafeInteger(stopped.stopStep)
|
|
1680
|
-
&& receipt?.step === stopped.stopStep
|
|
1681
|
-
&& Number.isSafeInteger(observedOutputTokens)
|
|
1682
|
-
&& ["planner", "executor", "frontend"].includes(receipt.responsibility)
|
|
1683
|
-
&& (receipt.responsibility !== "executor" || routeCard)) {
|
|
1684
|
-
const effectiveRoute = receipt.actualRoute ?? receipt.requestedRoute;
|
|
1685
|
-
appendEvent(owner, "odai/responsibility-interrupted", {
|
|
1686
|
-
scopeId: stopped.scopeId,
|
|
1687
|
-
turn: stopped.turn,
|
|
1688
|
-
step: stopped.stopStep ?? stopped.startStep,
|
|
1689
|
-
responsibility: receipt.responsibility,
|
|
1690
|
-
reason: "max-tokens",
|
|
1691
|
-
routeMode: receipt.routeMode,
|
|
1692
|
-
routeSource: receipt.routeSource,
|
|
1693
|
-
requestedRoute: receipt.requestedRoute,
|
|
1694
|
-
...(effectiveRoute ? { effectiveRoute } : {}),
|
|
1695
|
-
...(effectiveRoute?.maxTokens === undefined ? {} : { effectiveMaxTokens: effectiveRoute.maxTokens }),
|
|
1696
|
-
outputTokens: observedOutputTokens,
|
|
1697
|
-
...(stopped.routeCardId ? { routeCardId: stopped.routeCardId } : {}),
|
|
1698
|
-
...(routeCard ? { routeCard } : {}),
|
|
1699
|
-
}, logger);
|
|
1700
|
-
}
|
|
1701
|
-
}
|
|
1702
|
-
outputUsageBySession.delete(session);
|
|
1703
|
-
}
|
|
1704
|
-
|
|
1705
|
-
const pendingRestoration = pendingScopeRestorations.get(session);
|
|
1706
|
-
if (pendingRestoration && event?.type === "turn/end" && event.data?.turn === pendingRestoration.turn) {
|
|
1707
|
-
appendEvent(pendingRestoration.agent, "odai/responsibility-scope-restored", {
|
|
1708
|
-
scopeId: pendingRestoration.scopeId,
|
|
1709
|
-
turn: pendingRestoration.turn,
|
|
1710
|
-
step: pendingRestoration.step,
|
|
1711
|
-
role: pendingRestoration.role,
|
|
1712
|
-
status: "unverified",
|
|
1713
|
-
requestedRoute: pendingRestoration.expectedRoute,
|
|
1714
|
-
stopReason: "no-effective-request",
|
|
1715
|
-
});
|
|
1716
|
-
pendingScopeRestorations.delete(session);
|
|
1717
|
-
}
|
|
1718
|
-
if (pendingRestoration
|
|
1719
|
-
&& ["request/header", "assistant/chunk", "assistant/message"].includes(event?.type)
|
|
1720
|
-
&& eventMatchesRequestPosition(pendingRestoration)) {
|
|
1721
|
-
let actualRoute;
|
|
1722
|
-
try {
|
|
1723
|
-
actualRoute = event.type === "request/header"
|
|
1724
|
-
? routeFromConfig(event.data?.header?.config)
|
|
1725
|
-
: routeFromConfig(session.requestHeader?.()?.config);
|
|
1726
|
-
} catch {}
|
|
1727
|
-
if (actualRoute || event.type !== "request/header") {
|
|
1728
|
-
const mismatch = routeMismatchFor(pendingRestoration.expectedRoute, actualRoute, "base-route restoration");
|
|
1729
|
-
appendEvent(pendingRestoration.agent, "odai/responsibility-scope-restored", {
|
|
1730
|
-
scopeId: pendingRestoration.scopeId,
|
|
1731
|
-
turn: pendingRestoration.turn,
|
|
1732
|
-
step: pendingRestoration.step,
|
|
1733
|
-
role: pendingRestoration.role,
|
|
1734
|
-
status: mismatch ? "mismatch" : "applied",
|
|
1735
|
-
requestedRoute: pendingRestoration.expectedRoute,
|
|
1736
|
-
...(actualRoute ? { actualRoute } : {}),
|
|
1737
|
-
...(mismatch ? { error: mismatch } : {}),
|
|
69
|
+
const { baseSelection, bundled, evolutionDisabled, explicitSkillPath, memorySettingsFor, routingSnapshotFor, selectOutputForAgent, skillPath, } = promptRuntime;
|
|
70
|
+
const routeProtections = new WeakMap();
|
|
71
|
+
const responsibilityScopes = new WeakMap();
|
|
72
|
+
const responsibilityScopeOwners = new WeakMap();
|
|
73
|
+
const pendingRouteReceipts = new WeakMap();
|
|
74
|
+
const pendingScopeRestorations = new WeakMap();
|
|
75
|
+
const outputUsageBySession = new WeakMap();
|
|
76
|
+
const stopResponsibilityScope = (agent, reason, position = {}) => {
|
|
77
|
+
const scope = responsibilityScopes.get(agent);
|
|
78
|
+
if (!scope || (position.scopeId && position.scopeId !== scope.id))
|
|
79
|
+
return undefined;
|
|
80
|
+
responsibilityScopes.delete(agent);
|
|
81
|
+
const protection = routeProtections.get(agent);
|
|
82
|
+
if (protection?.scopeId === scope.id)
|
|
83
|
+
routeProtections.delete(agent);
|
|
84
|
+
appendEvent(agent, "odai/route-protection-released", {
|
|
85
|
+
scopeId: scope.id,
|
|
86
|
+
turn: scope.turn,
|
|
87
|
+
reason,
|
|
1738
88
|
});
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
}
|
|
1753
|
-
|
|
1754
|
-
const pending = pendingRouteReceipts.get(session);
|
|
1755
|
-
if (!pending) return;
|
|
1756
|
-
if (event?.type === "turn/end" && event.data?.turn === pending.turn) {
|
|
1757
|
-
appendEvent(pending.agent, "odai/route-applied", {
|
|
1758
|
-
turn: pending.turn,
|
|
1759
|
-
step: pending.step,
|
|
1760
|
-
responsibility: pending.responsibility,
|
|
1761
|
-
...(pending.responsibilityScopeId ? { responsibilityScopeId: pending.responsibilityScopeId } : {}),
|
|
1762
|
-
...(pending.routeCardId ? { routeCardId: pending.routeCardId } : {}),
|
|
1763
|
-
status: "unverified",
|
|
1764
|
-
routeMode: pending.routeMode,
|
|
1765
|
-
routeSource: pending.routeSource,
|
|
1766
|
-
fallbackUsed: true,
|
|
1767
|
-
requestedRoute: pending.requestedRoute,
|
|
1768
|
-
stopReason: "no-effective-request",
|
|
1769
|
-
});
|
|
1770
|
-
if (pending.routeCardId) {
|
|
1771
|
-
appendEvent(pending.agent, "odai/route-card-claim-released", {
|
|
1772
|
-
cardId: pending.routeCardId,
|
|
1773
|
-
turn: pending.turn,
|
|
1774
|
-
step: pending.step,
|
|
1775
|
-
reason: "no-effective-request",
|
|
89
|
+
appendEvent(agent, "odai/responsibility-scope-stopped", responsibilityScopeStoppedEvent(scope, reason, position));
|
|
90
|
+
return scope;
|
|
91
|
+
};
|
|
92
|
+
const stopDanglingResponsibilityScope = (agent, reason) => {
|
|
93
|
+
if (responsibilityScopes.has(agent))
|
|
94
|
+
return undefined;
|
|
95
|
+
const dangling = latestDanglingResponsibilityScope(evidence.events(agent));
|
|
96
|
+
if (!dangling)
|
|
97
|
+
return undefined;
|
|
98
|
+
appendEvent(agent, "odai/route-protection-released", {
|
|
99
|
+
scopeId: dangling.scopeId,
|
|
100
|
+
turn: dangling.turn,
|
|
101
|
+
reason,
|
|
1776
102
|
});
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
step: pending.step,
|
|
1781
|
-
});
|
|
1782
|
-
pendingRouteReceipts.delete(session);
|
|
1783
|
-
return;
|
|
1784
|
-
}
|
|
1785
|
-
if (!["request/header", "assistant/chunk", "assistant/message"].includes(event?.type)) return;
|
|
1786
|
-
if (!eventMatchesRequestPosition(pending)) return;
|
|
1787
|
-
let actualRoute;
|
|
1788
|
-
try {
|
|
1789
|
-
actualRoute = event.type === "request/header"
|
|
1790
|
-
? routeFromConfig(event.data?.header?.config)
|
|
1791
|
-
: routeFromConfig(session.requestHeader?.()?.config);
|
|
1792
|
-
} catch {}
|
|
1793
|
-
if (!actualRoute && event.type === "request/header") return;
|
|
1794
|
-
const mismatch = routeMismatchFor(pending.expectedRoute, actualRoute, pending.routeMode);
|
|
1795
|
-
appendEvent(pending.agent, "odai/route-applied", {
|
|
1796
|
-
turn: pending.turn,
|
|
1797
|
-
step: pending.step,
|
|
1798
|
-
responsibility: pending.responsibility,
|
|
1799
|
-
...(pending.responsibilityScopeId ? { responsibilityScopeId: pending.responsibilityScopeId } : {}),
|
|
1800
|
-
...(pending.routeCardId ? { routeCardId: pending.routeCardId } : {}),
|
|
1801
|
-
status: mismatch ? "mismatch" : "applied",
|
|
1802
|
-
routeMode: pending.routeMode,
|
|
1803
|
-
routeSource: pending.routeSource,
|
|
1804
|
-
fallbackUsed: Boolean(mismatch),
|
|
1805
|
-
requestedRoute: pending.requestedRoute,
|
|
1806
|
-
...(actualRoute ? { actualRoute } : {}),
|
|
1807
|
-
...(mismatch ? { stopReason: "route-mismatch", error: mismatch } : {}),
|
|
1808
|
-
});
|
|
1809
|
-
if (mismatch && pending.routeMode === "same-turn") {
|
|
1810
|
-
stopResponsibilityScope(pending.agent, "route-mismatch", {
|
|
1811
|
-
scopeId: pending.responsibilityScopeId,
|
|
1812
|
-
step: pending.step,
|
|
1813
|
-
});
|
|
1814
|
-
}
|
|
1815
|
-
if (!mismatch && pending.resumeOfScopeId) {
|
|
1816
|
-
appendEvent(pending.agent, "odai/responsibility-interruption-consumed", {
|
|
1817
|
-
scopeId: pending.resumeOfScopeId,
|
|
1818
|
-
turn: pending.turn,
|
|
1819
|
-
step: pending.step,
|
|
1820
|
-
responsibility: pending.responsibility,
|
|
1821
|
-
resumedScopeId: pending.responsibilityScopeId,
|
|
1822
|
-
});
|
|
1823
|
-
}
|
|
1824
|
-
if (pending.routeCardId) {
|
|
1825
|
-
appendEvent(pending.agent, mismatch ? "odai/route-card-claim-released" : "odai/route-card-consumed", {
|
|
1826
|
-
cardId: pending.routeCardId,
|
|
1827
|
-
turn: pending.turn,
|
|
1828
|
-
step: pending.step,
|
|
1829
|
-
...(mismatch ? { reason: "route-mismatch" } : { receiptStatus: "applied" }),
|
|
1830
|
-
});
|
|
1831
|
-
}
|
|
1832
|
-
pendingRouteReceipts.delete(session);
|
|
1833
|
-
if (mismatch && pending.routeMode === "same-turn") {
|
|
1834
|
-
protectController(
|
|
1835
|
-
pending.agent,
|
|
1836
|
-
pending.turn,
|
|
1837
|
-
pending.step,
|
|
1838
|
-
{ reasonCode: `${pending.responsibility.toUpperCase()}_ROUTE_MISMATCH` },
|
|
1839
|
-
"route-mismatch",
|
|
1840
|
-
mismatch,
|
|
1841
|
-
);
|
|
1842
|
-
}
|
|
1843
|
-
});
|
|
1844
|
-
|
|
1845
|
-
ctx.on("agent/turn-stopping", ({ agent, turn }) => {
|
|
1846
|
-
stopResponsibilityScope(agent, "turn-stopping");
|
|
1847
|
-
const role = routedRoleOf(agent);
|
|
1848
|
-
if (!role) return;
|
|
1849
|
-
const receipts = evidence.events(agent)
|
|
1850
|
-
.filter((event) => event.type === "odai/route-applied"
|
|
1851
|
-
&& event.data?.turn === turn
|
|
1852
|
-
&& event.data?.responsibility === role
|
|
1853
|
-
&& event.data?.routeMode === "child")
|
|
1854
|
-
.map((event) => event.data);
|
|
1855
|
-
const failed = receipts.find((receipt) => receipt.status !== "applied");
|
|
1856
|
-
if (receipts.length > 0 && !failed) return;
|
|
1857
|
-
const detail = failed?.error ?? failed?.stopReason ?? "no verified child route receipt";
|
|
1858
|
-
throw new Error(`Odai ${role} child route was not verified: ${detail}`);
|
|
1859
|
-
});
|
|
1860
|
-
|
|
1861
|
-
{
|
|
1862
|
-
const routedSteps = new WeakMap();
|
|
1863
|
-
ctx.on("agent/pre-step", async ({ agent, turn, step, signal }, next) => {
|
|
1864
|
-
const subagentSession = isSubagentSession(agent);
|
|
1865
|
-
if (!subagentSession) {
|
|
1866
|
-
if (step === 1) {
|
|
1867
|
-
stopResponsibilityScope(agent, "new-turn");
|
|
1868
|
-
routeProtections.delete(agent);
|
|
1869
|
-
}
|
|
1870
|
-
stopDanglingResponsibilityScope(agent, "runtime-resume");
|
|
1871
|
-
}
|
|
1872
|
-
let downstream = await next();
|
|
1873
|
-
if (downstream.kind === "reject" || signal.aborted) return downstream;
|
|
1874
|
-
const responsibilityGap = subagentSession ? undefined : pendingResponsibilityGap(agent, turn, step);
|
|
1875
|
-
const authenticatedDirectMessage = latestDirectUserMessage(agent, undefined, { turn });
|
|
1876
|
-
const suppliedDirectMessages = Array.isArray(downstream.messages)
|
|
1877
|
-
? downstream.messages.filter((message) => message?.role === "user" && message?.source?.kind === "user")
|
|
1878
|
-
: [];
|
|
1879
|
-
const directMessage = latestDirectUserMessage(agent, suppliedDirectMessages, { turn });
|
|
1880
|
-
const directText = directMessage ? extractLatestUserText([directMessage]) : "";
|
|
1881
|
-
const authenticatedDirectText = authenticatedDirectMessage
|
|
1882
|
-
? extractLatestUserText([authenticatedDirectMessage])
|
|
1883
|
-
: "";
|
|
1884
|
-
const responsibilityEvents = subagentSession ? [] : evidence.events(agent);
|
|
1885
|
-
const interruption = !subagentSession && step === 1
|
|
1886
|
-
? pendingResponsibilityInterruption(responsibilityEvents)
|
|
1887
|
-
: undefined;
|
|
1888
|
-
let responsibilityContinuation;
|
|
1889
|
-
let interruptionNotice;
|
|
1890
|
-
if (interruption && authenticatedDirectMessage) {
|
|
1891
|
-
const disposition = classifyResponsibilityInterruptionText(authenticatedDirectText);
|
|
1892
|
-
if (disposition === "continue" && directMessage) {
|
|
1893
|
-
responsibilityContinuation = Object.freeze({ ...interruption, continuationText: authenticatedDirectText });
|
|
1894
|
-
appendEvent(agent, "odai/responsibility-interruption-resume-requested", {
|
|
1895
|
-
scopeId: interruption.scopeId,
|
|
1896
|
-
turn,
|
|
1897
|
-
step,
|
|
1898
|
-
responsibility: interruption.responsibility,
|
|
1899
|
-
});
|
|
1900
|
-
} else if (disposition === "preserve") {
|
|
1901
|
-
if (directMessage) {
|
|
1902
|
-
interruptionNotice = pluginMessage(
|
|
1903
|
-
renderOutputLimitInterruptionNotice(interruption),
|
|
1904
|
-
`odai verified ${interruption.responsibility} output-limit interruption`,
|
|
1905
|
-
);
|
|
1906
|
-
}
|
|
1907
|
-
appendEvent(agent, "odai/responsibility-interruption-preserved", {
|
|
1908
|
-
scopeId: interruption.scopeId,
|
|
1909
|
-
turn,
|
|
1910
|
-
step,
|
|
1911
|
-
responsibility: interruption.responsibility,
|
|
1912
|
-
reason: "output-limit-diagnostic",
|
|
1913
|
-
});
|
|
1914
|
-
} else if (disposition === "clear") {
|
|
1915
|
-
appendEvent(agent, "odai/responsibility-interruption-cleared", {
|
|
1916
|
-
scopeId: interruption.scopeId,
|
|
1917
|
-
turn,
|
|
1918
|
-
step,
|
|
1919
|
-
responsibility: interruption.responsibility,
|
|
1920
|
-
reason: "superseded-by-user-task",
|
|
1921
|
-
});
|
|
1922
|
-
}
|
|
1923
|
-
}
|
|
1924
|
-
if (interruptionNotice) {
|
|
1925
|
-
downstream = { ...downstream, messages: [...downstream.messages, interruptionNotice] };
|
|
1926
|
-
}
|
|
1927
|
-
if (subagentSession) return downstream;
|
|
1928
|
-
if (step !== 1 && !responsibilityGap) return downstream;
|
|
1929
|
-
|
|
1930
|
-
if (step === 1 && claimSemanticMemoryTurn(agent, turn, step)) {
|
|
1931
|
-
const settings = memorySettingsFor(agent, turn);
|
|
1932
|
-
const message = directMessage;
|
|
1933
|
-
const query = extractRoutingText(downstream.messages, agent?.session?.events).slice(0, config.routing.maxInputChars);
|
|
1934
|
-
let retrieved = [];
|
|
1935
|
-
let captured = [];
|
|
1936
|
-
let error;
|
|
1937
|
-
if (settings.mode === "auto" && message) {
|
|
1938
|
-
try {
|
|
1939
|
-
retrieved = retrieveSemanticMemories({
|
|
1940
|
-
storePath: config.memory.storePath,
|
|
1941
|
-
query,
|
|
1942
|
-
cwd: agent?.session?.header?.cwd,
|
|
1943
|
-
limit: config.memory.maxRetrieved,
|
|
1944
|
-
});
|
|
1945
|
-
captured = captureAutomaticMemories({
|
|
1946
|
-
storePath: config.memory.storePath,
|
|
1947
|
-
mode: settings.mode,
|
|
1948
|
-
agent,
|
|
1949
|
-
message,
|
|
1950
|
-
turn,
|
|
1951
|
-
cwd: agent?.session?.header?.cwd,
|
|
1952
|
-
});
|
|
1953
|
-
} catch (memoryError) {
|
|
1954
|
-
error = memoryError instanceof Error ? memoryError.message : String(memoryError);
|
|
1955
|
-
logger.warn(`Odai semantic memory processing failed closed for this turn: ${error}`);
|
|
1956
|
-
retrieved = [];
|
|
1957
|
-
captured = [];
|
|
1958
|
-
}
|
|
1959
|
-
}
|
|
1960
|
-
const captureEvidence = captured.filter((result) => result.changed);
|
|
1961
|
-
if (retrieved.length > 0 || captureEvidence.length > 0 || error) {
|
|
1962
|
-
appendEvent(agent, "odai/memory-processed", {
|
|
1963
|
-
turn,
|
|
1964
|
-
step,
|
|
1965
|
-
mode: settings.mode,
|
|
1966
|
-
source: settings.source,
|
|
1967
|
-
retrievedIds: retrieved.map((entry) => entry.id),
|
|
1968
|
-
captures: captureEvidence.map((result) => ({
|
|
1969
|
-
changed: true,
|
|
1970
|
-
reasonCode: result.reasonCode,
|
|
1971
|
-
...(result.id ? { id: result.id } : {}),
|
|
1972
|
-
...(result.status ? { status: result.status } : {}),
|
|
1973
|
-
...(result.scope ? { scope: result.scope } : {}),
|
|
1974
|
-
})),
|
|
1975
|
-
...(error ? { status: "fallback", error: "memory-store-unavailable" } : { status: "completed" }),
|
|
1976
|
-
}, logger);
|
|
1977
|
-
}
|
|
1978
|
-
const packet = renderSemanticMemoryPacket(retrieved);
|
|
1979
|
-
if (packet) {
|
|
1980
|
-
downstream = {
|
|
1981
|
-
...downstream,
|
|
1982
|
-
messages: [...downstream.messages, memoryPacketMessage(packet)],
|
|
1983
|
-
};
|
|
1984
|
-
}
|
|
1985
|
-
}
|
|
1986
|
-
|
|
1987
|
-
if (config.routing.mode === "off") return downstream;
|
|
1988
|
-
if (hasSessionEvent(agent, "odai/route-decided", (data) => data?.turn === turn && data?.step === step)) {
|
|
1989
|
-
return downstream;
|
|
1990
|
-
}
|
|
1991
|
-
|
|
1992
|
-
let routed = routedSteps.get(agent);
|
|
1993
|
-
if (!routed) {
|
|
1994
|
-
routed = new Set();
|
|
1995
|
-
routedSteps.set(agent, routed);
|
|
1996
|
-
}
|
|
1997
|
-
const routeKey = `${turn}:${step}`;
|
|
1998
|
-
if (routed.has(routeKey)) return downstream;
|
|
1999
|
-
routed.add(routeKey);
|
|
2000
|
-
|
|
2001
|
-
const taskText = extractRoutingText(downstream.messages, agent?.session?.events).slice(0, config.routing.maxInputChars);
|
|
2002
|
-
let routedDownstream = downstream;
|
|
2003
|
-
let researchPacketText = "";
|
|
2004
|
-
const researchDecision = decideResearchPrefetch({ text: taskText, proposal: responsibilityGap });
|
|
2005
|
-
if (researchDecision.action === "delegate") {
|
|
2006
|
-
appendEvent(agent, "odai/research-decided", {
|
|
2007
|
-
turn,
|
|
2008
|
-
step,
|
|
2009
|
-
role: "researcher",
|
|
2010
|
-
action: "delegate",
|
|
2011
|
-
mode: config.routing.mode,
|
|
2012
|
-
reasonCode: researchDecision.reasonCode,
|
|
2013
|
-
signals: researchDecision.signals,
|
|
2014
|
-
}, logger);
|
|
2015
|
-
if (config.routing.mode === "observe") {
|
|
2016
|
-
appendEvent(agent, "odai/research-result", {
|
|
2017
|
-
turn,
|
|
2018
|
-
step,
|
|
2019
|
-
role: "researcher",
|
|
2020
|
-
status: "observed",
|
|
2021
|
-
stopReason: "observe-mode",
|
|
2022
|
-
}, logger);
|
|
2023
|
-
} else {
|
|
2024
|
-
const researchState = configuredRole(agent, "researcher", turn);
|
|
2025
|
-
const researchRoute = researchState.route;
|
|
2026
|
-
if (!researchRoute) {
|
|
2027
|
-
appendEvent(agent, "odai/research-result", {
|
|
2028
|
-
turn,
|
|
2029
|
-
step,
|
|
2030
|
-
role: "researcher",
|
|
2031
|
-
status: "fallback",
|
|
2032
|
-
stopReason: researchState.error ? "route-config-invalid" : "route-config-missing",
|
|
2033
|
-
...(researchState.error ? { error: researchState.detail } : {}),
|
|
2034
|
-
}, logger);
|
|
2035
|
-
} else {
|
|
2036
|
-
const researchBundle = sharedSkillSelection(agent, turn)?.bundle ?? bundled;
|
|
2037
|
-
const researchContract = dshRoleContract(
|
|
2038
|
-
"researcher",
|
|
2039
|
-
researchBundle.roleContracts.researcher,
|
|
2040
|
-
researchBundle.referenceContracts,
|
|
2041
|
-
);
|
|
2042
|
-
const result = ctx.subagents?.start
|
|
2043
|
-
? await runRoutedRole({
|
|
2044
|
-
subagents: ctx.subagents,
|
|
2045
|
-
provider: config.routing.provider,
|
|
2046
|
-
decision: researchDecision,
|
|
2047
|
-
taskText: renderResearchTaskContract(taskText),
|
|
2048
|
-
roleContract: researchContract,
|
|
2049
|
-
agent,
|
|
2050
|
-
signal,
|
|
2051
|
-
roleRoute: researchRoute,
|
|
2052
|
-
})
|
|
2053
|
-
: Object.freeze({
|
|
2054
|
-
status: "fallback",
|
|
2055
|
-
stopReason: "infrastructure-error",
|
|
2056
|
-
output: [],
|
|
2057
|
-
error: "dsh subagents service unavailable",
|
|
2058
|
-
});
|
|
2059
|
-
let packet;
|
|
2060
|
-
let packetError;
|
|
2061
|
-
if (result.status === "completed") {
|
|
2062
|
-
try {
|
|
2063
|
-
packet = verifyResearchPacketSources(
|
|
2064
|
-
parseResearchPacket(outputText(result.output)),
|
|
2065
|
-
agent?.session?.header?.cwd,
|
|
2066
|
-
);
|
|
2067
|
-
} catch (error) {
|
|
2068
|
-
packetError = error instanceof Error ? error.message : String(error);
|
|
2069
|
-
}
|
|
2070
|
-
}
|
|
2071
|
-
const completed = result.status === "completed" && packet !== undefined;
|
|
2072
|
-
appendEvent(agent, "odai/research-result", {
|
|
2073
|
-
turn,
|
|
2074
|
-
step,
|
|
2075
|
-
role: "researcher",
|
|
2076
|
-
status: completed ? "completed" : "fallback",
|
|
2077
|
-
stopReason: completed ? result.stopReason : (packetError ? "packet-invalid" : result.stopReason),
|
|
2078
|
-
routeSource: researchState.source,
|
|
2079
|
-
fallbackUsed: !completed,
|
|
2080
|
-
routeReceiptStatus: result.routeReceiptStatus,
|
|
2081
|
-
requestedRoute: researchRoute,
|
|
2082
|
-
...(result.routeReceiptError ? { routeReceiptError: result.routeReceiptError } : {}),
|
|
2083
|
-
...(result.actualRoute ? { actualRoute: result.actualRoute } : {}),
|
|
2084
|
-
...(packet ? { packetDigest: packet.digest, sourceCount: packet.sourceCount } : {}),
|
|
2085
|
-
...(packetError ? { error: packetError } : result.taskError ? { error: result.taskError } : {}),
|
|
2086
|
-
}, logger);
|
|
2087
|
-
if (completed) {
|
|
2088
|
-
researchPacketText = renderResearchPacket(packet);
|
|
2089
|
-
routedDownstream = {
|
|
2090
|
-
...downstream,
|
|
2091
|
-
messages: [
|
|
2092
|
-
...routedDownstream.messages,
|
|
2093
|
-
pluginMessage(
|
|
2094
|
-
researchPacketText,
|
|
2095
|
-
`odai completed researcher evidence compression (${packet.sourceCount} sources)`,
|
|
2096
|
-
),
|
|
2097
|
-
],
|
|
2098
|
-
};
|
|
2099
|
-
}
|
|
2100
|
-
}
|
|
2101
|
-
}
|
|
2102
|
-
}
|
|
2103
|
-
|
|
2104
|
-
const frozenCard = responsibilityContinuation?.routeCard ?? activeRouteCard(evidence.events(agent));
|
|
2105
|
-
let decision = decideRoute({
|
|
2106
|
-
text: taskText,
|
|
2107
|
-
routeCard: frozenCard,
|
|
2108
|
-
proposal: responsibilityGap,
|
|
2109
|
-
interruption: responsibilityContinuation,
|
|
2110
|
-
});
|
|
2111
|
-
let routeRole = decision.targetRole ?? decision.role;
|
|
2112
|
-
const roleTaskText = researchPacketText ? `${taskText}\n\n${researchPacketText}` : taskText;
|
|
2113
|
-
let roleContext = decision.action === "direct"
|
|
2114
|
-
? undefined
|
|
2115
|
-
: buildRoleContextPacket(agent, routeRole, roleTaskText);
|
|
2116
|
-
let localReviewerCoverage;
|
|
2117
|
-
if (config.routing.mode === "auto"
|
|
2118
|
-
&& routeRole === "reviewer"
|
|
2119
|
-
&& decision.action === "delegate"
|
|
2120
|
-
&& !roleContext.sufficient) {
|
|
2121
|
-
localReviewerCoverage = roleContext.coverage;
|
|
2122
|
-
decision = Object.freeze({
|
|
2123
|
-
...decision,
|
|
2124
|
-
role: "controller",
|
|
2125
|
-
mode: "direct",
|
|
2126
|
-
action: "direct",
|
|
2127
|
-
targetRole: "reviewer",
|
|
2128
|
-
signals: Object.freeze([...decision.signals, "review-evidence-packet-missing", "controller-local-review"]),
|
|
103
|
+
appendEvent(agent, "odai/responsibility-scope-stopped", {
|
|
104
|
+
...dangling,
|
|
105
|
+
reason,
|
|
2129
106
|
});
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
turn
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
action: decision.action,
|
|
2137
|
-
...(decision.targetRole ? { targetRole: decision.targetRole } : {}),
|
|
2138
|
-
mode: config.routing.mode,
|
|
2139
|
-
reasonCode: decision.reasonCode,
|
|
2140
|
-
signals: decision.signals,
|
|
2141
|
-
...(responsibilityGap ? {
|
|
2142
|
-
stateDigest: responsibilityGap.stateDigest,
|
|
2143
|
-
gap: responsibilityGap.gap,
|
|
2144
|
-
evidenceRefs: responsibilityGap.evidenceRefs,
|
|
2145
|
-
expectedChange: responsibilityGap.expectedChange,
|
|
2146
|
-
} : {}),
|
|
2147
|
-
...(decision.considerations ? { considerations: decision.considerations } : {}),
|
|
2148
|
-
}, logger);
|
|
2149
|
-
if (responsibilityGap) {
|
|
2150
|
-
appendEvent(agent, "odai/responsibility-gap-consumed", {
|
|
2151
|
-
turn,
|
|
2152
|
-
step,
|
|
2153
|
-
responsibility: responsibilityGap.responsibility,
|
|
2154
|
-
stateDigest: responsibilityGap.stateDigest,
|
|
2155
|
-
routeAction: decision.action,
|
|
2156
|
-
reasonCode: decision.reasonCode,
|
|
2157
|
-
}, logger);
|
|
2158
|
-
}
|
|
2159
|
-
|
|
2160
|
-
if (decision.action === "direct") {
|
|
2161
|
-
if (!localReviewerCoverage) return routedDownstream;
|
|
2162
|
-
appendEvent(agent, "odai/route-context", {
|
|
2163
|
-
turn,
|
|
2164
|
-
step,
|
|
2165
|
-
role: "reviewer",
|
|
2166
|
-
mode: "controller-local",
|
|
2167
|
-
digest: roleContext.digest,
|
|
2168
|
-
evidenceCount: roleContext.evidenceCount,
|
|
2169
|
-
toolEvidenceCount: roleContext.toolEvidenceCount,
|
|
2170
|
-
acceptanceCount: localReviewerCoverage.acceptanceCount,
|
|
2171
|
-
diffCount: localReviewerCoverage.diffCount,
|
|
2172
|
-
testCount: localReviewerCoverage.testCount,
|
|
2173
|
-
truncated: roleContext.truncated,
|
|
2174
|
-
sufficient: false,
|
|
2175
|
-
}, logger);
|
|
2176
|
-
appendEvent(agent, "odai/route-result", {
|
|
2177
|
-
turn,
|
|
2178
|
-
step,
|
|
2179
|
-
role: "reviewer",
|
|
2180
|
-
action: "direct",
|
|
2181
|
-
status: "fallback",
|
|
2182
|
-
stopReason: "evidence-packet-missing",
|
|
2183
|
-
independent: false,
|
|
2184
|
-
}, logger);
|
|
107
|
+
return dangling;
|
|
108
|
+
};
|
|
109
|
+
const configuredRole = (agent, role, turn = currentAgentTurn(agent)) => {
|
|
110
|
+
const state = routingSnapshotFor(agent, turn);
|
|
111
|
+
if (state.error || !state.snapshot)
|
|
112
|
+
return state;
|
|
2185
113
|
return {
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
...routedDownstream.messages,
|
|
2189
|
-
pluginMessage(
|
|
2190
|
-
[
|
|
2191
|
-
`An independent reviewer was not started because the bounded packet is incomplete (${JSON.stringify(localReviewerCoverage)}).`,
|
|
2192
|
-
"Remain on the current controller route and continue the authorized task. Gather project-available requirements, acceptance conditions, diff, tests, and matching tool evidence before resubmitting a changed reviewer gap.",
|
|
2193
|
-
"A controller-local read-only check may guide the work, but it is not independent acceptance. Do not stop solely to ask the user for review artifacts the project can produce, and do not claim the reviewer approved release.",
|
|
2194
|
-
].join("\n"),
|
|
2195
|
-
"odai reviewer evidence is incomplete; controller continues locally",
|
|
2196
|
-
),
|
|
2197
|
-
],
|
|
114
|
+
route: state.snapshot.roles[role],
|
|
115
|
+
source: state.snapshot.sources[role],
|
|
2198
116
|
};
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
117
|
+
};
|
|
118
|
+
const pendingResponsibilityGap = (agent, turn, step) => {
|
|
119
|
+
const events = evidence.events(agent);
|
|
120
|
+
const consumed = new Set(events.flatMap((event) => (event.type === "odai/responsibility-gap-consumed" ? [event.data?.stateDigest] : [])));
|
|
121
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
122
|
+
const event = events[index];
|
|
123
|
+
if (event.data?.turn !== turn
|
|
124
|
+
|| event.type !== "odai/responsibility-gap"
|
|
125
|
+
|| typeof event.data?.step !== "number"
|
|
126
|
+
|| !Number.isSafeInteger(event.data.step)
|
|
127
|
+
|| event.data.step >= step
|
|
128
|
+
|| consumed.has(event.data.stateDigest)
|
|
129
|
+
|| !isResponsibilityGapProposal(event.data))
|
|
130
|
+
continue;
|
|
131
|
+
return event.data;
|
|
132
|
+
}
|
|
133
|
+
if (turn === undefined)
|
|
134
|
+
return undefined;
|
|
135
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
136
|
+
const event = events[index];
|
|
137
|
+
if (event.type !== "odai/responsibility-gap"
|
|
138
|
+
|| event.data?.responsibility !== "reviewer"
|
|
139
|
+
|| typeof event.data.turn !== "number"
|
|
140
|
+
|| event.data.turn >= turn
|
|
141
|
+
|| consumed.has(event.data.stateDigest)
|
|
142
|
+
|| !isResponsibilityGapProposal(event.data))
|
|
143
|
+
continue;
|
|
144
|
+
const deferred = events.slice(index + 1).some((candidate) => (candidate.type === "odai/responsibility-gap-deferred"
|
|
145
|
+
&& candidate.data?.stateDigest === event.data.stateDigest));
|
|
146
|
+
if (!deferred)
|
|
147
|
+
continue;
|
|
148
|
+
const message = latestDirectUserMessage(agent);
|
|
149
|
+
const transition = message
|
|
150
|
+
? classifyPendingReviewerText(extractLatestUserText([message]))
|
|
151
|
+
: "dormant";
|
|
152
|
+
if (transition === "continue")
|
|
153
|
+
return event.data;
|
|
154
|
+
if (transition === "supersede") {
|
|
155
|
+
appendEvent(agent, "odai/responsibility-gap-consumed", {
|
|
156
|
+
turn,
|
|
157
|
+
step,
|
|
158
|
+
responsibility: "reviewer",
|
|
159
|
+
stateDigest: event.data.stateDigest,
|
|
160
|
+
reason: "SUPERSEDED_BY_DIRECT_USER_TASK",
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return undefined;
|
|
2204
164
|
}
|
|
2205
|
-
return
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
const invalidConfig = Boolean(roleState.error);
|
|
2221
|
-
appendEvent(agent, "odai/route-config-missing", {
|
|
2222
|
-
turn,
|
|
2223
|
-
step,
|
|
2224
|
-
role: routeRole,
|
|
2225
|
-
action: decision.action,
|
|
2226
|
-
mode: config.routing.mode,
|
|
2227
|
-
status: invalidConfig ? "invalid" : "unconfigured",
|
|
2228
|
-
...(invalidConfig ? { error: roleState.detail } : {}),
|
|
2229
|
-
}, logger);
|
|
2230
|
-
if (requiresFailClosedProtection(decision)) {
|
|
2231
|
-
protectController(
|
|
2232
|
-
agent,
|
|
2233
|
-
turn,
|
|
2234
|
-
step,
|
|
2235
|
-
decision,
|
|
2236
|
-
invalidConfig ? "route-config-invalid" : "route-config-missing",
|
|
2237
|
-
);
|
|
165
|
+
return undefined;
|
|
166
|
+
};
|
|
167
|
+
const invalidateFailedRoleRoute = (agent, role, route, source, failure, position = {}) => {
|
|
168
|
+
let invalidation = Object.freeze({ invalidated: false, reason: "not-deterministic-or-not-persisted" });
|
|
169
|
+
if (failure.kind === "deterministic" && source === "persisted-mapping") {
|
|
170
|
+
try {
|
|
171
|
+
invalidation = invalidatePersistedRoleRoute(config.routing.configPath, role, route);
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
invalidation = Object.freeze({
|
|
175
|
+
invalidated: false,
|
|
176
|
+
reason: "cleanup-failed",
|
|
177
|
+
error: error instanceof Error ? error.message : String(error),
|
|
178
|
+
});
|
|
179
|
+
}
|
|
2238
180
|
}
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
],
|
|
2249
|
-
};
|
|
2250
|
-
}
|
|
2251
|
-
|
|
2252
|
-
const roleBundle = sharedSkillSelection(agent, turn)?.bundle ?? bundled;
|
|
2253
|
-
const canonicalRoleContract = roleBundle.roleContracts[routeRole];
|
|
2254
|
-
const roleContract = dshRoleContract(routeRole, canonicalRoleContract, roleBundle.referenceContracts);
|
|
2255
|
-
let rolePreflightVerified = false;
|
|
2256
|
-
if (routeRole === "frontend" && decision.action === "upgrade") {
|
|
2257
|
-
const health = await probeModelRoute(
|
|
2258
|
-
(candidate, candidateSignal) => ctx.llm.resolveCallConfig(candidate, candidateSignal),
|
|
2259
|
-
roleRoute,
|
|
2260
|
-
signal,
|
|
2261
|
-
);
|
|
2262
|
-
if (health.status === "rejected") {
|
|
2263
|
-
const invalidation = invalidateFailedRoleRoute(
|
|
2264
|
-
agent,
|
|
2265
|
-
routeRole,
|
|
2266
|
-
roleRoute,
|
|
2267
|
-
roleState.source,
|
|
2268
|
-
health.failure,
|
|
2269
|
-
{ turn, step, position: "pre-step" },
|
|
2270
|
-
);
|
|
2271
|
-
appendEvent(agent, "odai/route-result", {
|
|
2272
|
-
turn,
|
|
2273
|
-
step,
|
|
2274
|
-
role: routeRole,
|
|
2275
|
-
action: "upgrade",
|
|
2276
|
-
status: "fallback",
|
|
2277
|
-
stopReason: "route-preflight-failed",
|
|
2278
|
-
routeSource: roleState.source,
|
|
2279
|
-
fallbackUsed: true,
|
|
2280
|
-
requestedRoute: roleRoute,
|
|
2281
|
-
failureKind: health.failure.kind,
|
|
2282
|
-
error: health.failure.message,
|
|
181
|
+
appendEvent(agent, "odai/route-health", {
|
|
182
|
+
...position,
|
|
183
|
+
responsibility: role,
|
|
184
|
+
routeSource: source,
|
|
185
|
+
requestedRoute: route,
|
|
186
|
+
status: failure.kind === "deterministic" ? "invalid" : "unhealthy",
|
|
187
|
+
failureKind: failure.kind,
|
|
188
|
+
errorCode: failure.code,
|
|
189
|
+
error: failure.message,
|
|
2283
190
|
invalidated: invalidation.invalidated,
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
messages: [
|
|
2288
|
-
...routedDownstream.messages,
|
|
2289
|
-
pluginMessage(
|
|
2290
|
-
[
|
|
2291
|
-
`The configured frontend route failed preflight (${health.failure.kind}: ${health.failure.message}).`,
|
|
2292
|
-
"Continue locally as the current controller for this turn using the canonical frontend and craft contract below. Do not claim the configured frontend responsibility ran; no routed receipt exists.",
|
|
2293
|
-
"",
|
|
2294
|
-
"frontend local-fallback responsibility contract:",
|
|
2295
|
-
roleContract,
|
|
2296
|
-
].join("\n"),
|
|
2297
|
-
"odai frontend route unavailable; explicit local fallback",
|
|
2298
|
-
),
|
|
2299
|
-
],
|
|
2300
|
-
};
|
|
2301
|
-
}
|
|
2302
|
-
rolePreflightVerified = health.status === "verified";
|
|
2303
|
-
}
|
|
2304
|
-
roleContext ??= buildRoleContextPacket(agent, routeRole, taskText);
|
|
2305
|
-
const inPlaceUpgrade = decision.action === "upgrade"
|
|
2306
|
-
&& (config.routing.mode === "auto" || ["executor", "frontend"].includes(routeRole));
|
|
2307
|
-
const contextMode = inPlaceUpgrade ? "same-turn" : "bounded-packet";
|
|
2308
|
-
appendEvent(agent, "odai/route-context", {
|
|
2309
|
-
turn,
|
|
2310
|
-
step,
|
|
2311
|
-
role: routeRole,
|
|
2312
|
-
mode: contextMode,
|
|
2313
|
-
digest: roleContext.digest,
|
|
2314
|
-
evidenceCount: roleContext.evidenceCount,
|
|
2315
|
-
toolEvidenceCount: roleContext.toolEvidenceCount,
|
|
2316
|
-
acceptanceCount: roleContext.coverage.acceptanceCount,
|
|
2317
|
-
diffCount: roleContext.coverage.diffCount,
|
|
2318
|
-
testCount: roleContext.coverage.testCount,
|
|
2319
|
-
truncated: roleContext.truncated,
|
|
2320
|
-
sufficient: roleContext.sufficient,
|
|
2321
|
-
}, logger);
|
|
2322
|
-
|
|
2323
|
-
if (routeRole === "reviewer" && decision.action === "delegate" && !roleContext.sufficient) {
|
|
2324
|
-
appendEvent(agent, "odai/route-result", {
|
|
2325
|
-
turn,
|
|
2326
|
-
step,
|
|
2327
|
-
role: "reviewer",
|
|
2328
|
-
action: "delegate",
|
|
2329
|
-
status: "fallback",
|
|
2330
|
-
stopReason: "evidence-packet-missing",
|
|
2331
|
-
contextDigest: roleContext.digest,
|
|
2332
|
-
}, logger);
|
|
2333
|
-
return {
|
|
2334
|
-
kind: "enter",
|
|
2335
|
-
messages: [
|
|
2336
|
-
...routedDownstream.messages,
|
|
2337
|
-
pluginMessage(
|
|
2338
|
-
`odai reviewer child was not started because the bounded packet is incomplete (${JSON.stringify(roleContext.coverage)}). Gather requirements, acceptance conditions, actual diff, tests, and matching tool evidence first; do not claim independent acceptance.`,
|
|
2339
|
-
"odai reviewer evidence packet is incomplete",
|
|
2340
|
-
),
|
|
2341
|
-
],
|
|
2342
|
-
};
|
|
2343
|
-
}
|
|
2344
|
-
|
|
2345
|
-
if (inPlaceUpgrade) {
|
|
2346
|
-
stopResponsibilityScope(agent, "superseded", { step });
|
|
2347
|
-
const responsibilityScope = createResponsibilityScope({
|
|
2348
|
-
turn,
|
|
2349
|
-
startStep: step,
|
|
2350
|
-
role: routeRole,
|
|
2351
|
-
route: roleRoute,
|
|
2352
|
-
source: roleState.source,
|
|
2353
|
-
decision,
|
|
2354
|
-
routeValidated: rolePreflightVerified,
|
|
2355
|
-
...(routeRole === "executor" && frozenCard ? { cardId: frozenCard.id } : {}),
|
|
2356
|
-
...(responsibilityContinuation ? { resumeOfScopeId: responsibilityContinuation.scopeId } : {}),
|
|
191
|
+
...(invalidation.backupPath ? { backupPath: invalidation.backupPath } : {}),
|
|
192
|
+
...(invalidation.reason ? { cleanupReason: invalidation.reason } : {}),
|
|
193
|
+
...(invalidation.error ? { cleanupError: invalidation.error } : {}),
|
|
2357
194
|
});
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
appendEvent(agent, "odai/route-upgrade", {
|
|
2373
|
-
turn,
|
|
2374
|
-
step,
|
|
2375
|
-
role: decision.role,
|
|
2376
|
-
targetRole: routeRole,
|
|
2377
|
-
status: "requested",
|
|
2378
|
-
responsibilityScopeId: responsibilityScope.id,
|
|
2379
|
-
...(responsibilityContinuation ? { resumeOfScopeId: responsibilityContinuation.scopeId } : {}),
|
|
2380
|
-
continuationPolicy: responsibilityScope.continuationPolicy,
|
|
2381
|
-
stopPolicy: responsibilityScope.stopPolicy,
|
|
2382
|
-
routeSource: roleState.source,
|
|
2383
|
-
requestedRoute: roleRoute,
|
|
2384
|
-
contextDigest: roleContext.digest,
|
|
2385
|
-
contextMode,
|
|
2386
|
-
...(routeRole === "reviewer" ? { independent: false } : {}),
|
|
2387
|
-
}, logger);
|
|
2388
|
-
const contextBoundary = routeRole === "reviewer"
|
|
2389
|
-
? `The bounded packet is not independently reviewable (${JSON.stringify(roleContext.coverage)}). Perform a same-turn read-only check and do not claim independent acceptance.`
|
|
2390
|
-
: "Retain the current controller conversation and workspace context; do not reconstruct it through a child handoff.";
|
|
2391
|
-
return {
|
|
2392
|
-
kind: "enter",
|
|
2393
|
-
messages: [
|
|
2394
|
-
...routedDownstream.messages,
|
|
2395
|
-
pluginMessage(
|
|
2396
|
-
[
|
|
2397
|
-
renderRouteNotice(decision, config.routing.mode, roleRoute),
|
|
2398
|
-
"",
|
|
2399
|
-
`Context digest: sha256:${roleContext.digest}`,
|
|
2400
|
-
contextBoundary,
|
|
2401
|
-
"",
|
|
2402
|
-
`${routeRole} responsibility contract:`,
|
|
2403
|
-
roleContract,
|
|
2404
|
-
...(routeRole === "executor" && frozenCard
|
|
2405
|
-
? ["", "Frozen route card:", JSON.stringify(frozenCard, null, 2)]
|
|
2406
|
-
: []),
|
|
2407
|
-
].join("\n"),
|
|
2408
|
-
`odai upgraded controller route (${decision.reasonCode})`,
|
|
2409
|
-
),
|
|
2410
|
-
],
|
|
2411
|
-
};
|
|
2412
|
-
}
|
|
2413
|
-
|
|
2414
|
-
const delegationDecision = decision.role === routeRole
|
|
2415
|
-
? decision
|
|
2416
|
-
: Object.freeze({ ...decision, role: routeRole, mode: "delegate", action: "delegate" });
|
|
2417
|
-
const result = ctx.subagents?.start
|
|
2418
|
-
? await runRoutedRole({
|
|
2419
|
-
subagents: ctx.subagents,
|
|
2420
|
-
provider: config.routing.provider,
|
|
2421
|
-
decision: delegationDecision,
|
|
2422
|
-
taskText: renderRoleContextPacket(roleContext),
|
|
2423
|
-
roleContract,
|
|
2424
|
-
agent,
|
|
2425
|
-
signal,
|
|
2426
|
-
roleRoute,
|
|
2427
|
-
})
|
|
2428
|
-
: Object.freeze({
|
|
2429
|
-
status: "fallback",
|
|
2430
|
-
stopReason: "infrastructure-error",
|
|
2431
|
-
output: [],
|
|
2432
|
-
error: "dsh subagents service unavailable",
|
|
2433
|
-
});
|
|
2434
|
-
appendEvent(agent, "odai/route-result", {
|
|
2435
|
-
turn,
|
|
2436
|
-
step,
|
|
2437
|
-
role: routeRole,
|
|
2438
|
-
action: "delegate",
|
|
2439
|
-
status: result.status,
|
|
2440
|
-
stopReason: result.stopReason,
|
|
2441
|
-
routeSource: roleState.source,
|
|
2442
|
-
fallbackUsed: result.status !== "completed",
|
|
2443
|
-
routeReceiptStatus: result.routeReceiptStatus,
|
|
2444
|
-
requestedRoute: roleRoute,
|
|
2445
|
-
contextDigest: roleContext.digest,
|
|
2446
|
-
...(result.routeReceiptError ? { routeReceiptError: result.routeReceiptError } : {}),
|
|
2447
|
-
...(result.actualRoute ? { actualRoute: result.actualRoute } : {}),
|
|
2448
|
-
...(result.taskError ? { error: result.taskError } : {}),
|
|
2449
|
-
}, logger);
|
|
2450
|
-
|
|
2451
|
-
if (result.status === "completed") {
|
|
2452
|
-
const childText = outputText(result.output);
|
|
2453
|
-
const heading = renderRouteNotice(delegationDecision, config.routing.mode, result.actualRoute);
|
|
2454
|
-
return {
|
|
2455
|
-
kind: "enter",
|
|
2456
|
-
messages: [
|
|
2457
|
-
...routedDownstream.messages,
|
|
2458
|
-
pluginMessage(
|
|
2459
|
-
childText ? `${heading}\ncontext digest: sha256:${roleContext.digest}\n\n${routeRole} output:\n${childText}` : heading,
|
|
2460
|
-
`odai completed ${routeRole} route`,
|
|
2461
|
-
result.output.filter((block) => block?.type !== "text"),
|
|
2462
|
-
),
|
|
2463
|
-
],
|
|
2464
|
-
};
|
|
2465
|
-
}
|
|
2466
|
-
|
|
2467
|
-
const failure = result.error ?? result.stopReason;
|
|
2468
|
-
if (requiresFailClosedProtection(delegationDecision)) {
|
|
2469
|
-
protectController(agent, turn, step, delegationDecision, "route-failure", failure);
|
|
2470
|
-
}
|
|
2471
|
-
return {
|
|
2472
|
-
kind: "enter",
|
|
2473
|
-
messages: [
|
|
2474
|
-
...routedDownstream.messages,
|
|
2475
|
-
pluginMessage(
|
|
2476
|
-
renderRouteFailureNotice(delegationDecision, failure),
|
|
2477
|
-
requiresFailClosedProtection(delegationDecision)
|
|
2478
|
-
? `odai blocked high-impact ${routeRole} fallback`
|
|
2479
|
-
: `odai fell back from ${routeRole} route`,
|
|
2480
|
-
),
|
|
2481
|
-
],
|
|
2482
|
-
};
|
|
2483
|
-
}, { prepend: true });
|
|
2484
|
-
}
|
|
2485
|
-
|
|
2486
|
-
logger.info(`loaded canonical governance ${bundled.manifest.skillVersion} from ${skillPath}; skillSource=${explicitSkillPath ? "path" : config.governance.skillSource}; routing=${config.routing.mode}`);
|
|
2487
|
-
}
|
|
2488
|
-
|
|
2489
|
-
function isSubagentSession(agent) {
|
|
2490
|
-
const header = agent?.session?.header;
|
|
2491
|
-
return header?.origin === "subagent"
|
|
2492
|
-
|| (Number.isSafeInteger(header?.delegationDepth) && header.delegationDepth > 0);
|
|
195
|
+
return invalidation;
|
|
196
|
+
};
|
|
197
|
+
installToolRuntime({
|
|
198
|
+
appendEvent, baseSelection, bundled, config, ctx, evidence, evolutionDisabled, explicitSkillPath,
|
|
199
|
+
hasSessionEvent, humanSafetyContinuityStorePath, logger, pendingResponsibilityGap, promptRuntime,
|
|
200
|
+
routeProtections, selectOutputForAgent,
|
|
201
|
+
});
|
|
202
|
+
installLifecycleRuntime({
|
|
203
|
+
appendEvent, bundled, config, configuredRole, ctx, evidence, hasSessionEvent, invalidateFailedRoleRoute,
|
|
204
|
+
logger, memorySettingsFor, outputUsageBySession, pendingResponsibilityGap, pendingRouteReceipts,
|
|
205
|
+
pendingScopeRestorations, responsibilityScopeOwners, responsibilityScopes, routeProtections,
|
|
206
|
+
selectOutputForAgent, stopDanglingResponsibilityScope, stopResponsibilityScope,
|
|
207
|
+
});
|
|
208
|
+
logger.info(`loaded canonical governance ${bundled.manifest.skillVersion} from ${skillPath}; skillSource=${explicitSkillPath ? "path" : config.governance.skillSource}; routing=${config.routing.mode}`);
|
|
2493
209
|
}
|
|
210
|
+
//# sourceMappingURL=index.mjs.map
|