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,388 +1,366 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import {
|
|
3
|
-
copyFileSync,
|
|
4
|
-
mkdirSync,
|
|
5
|
-
readFileSync,
|
|
6
|
-
renameSync,
|
|
7
|
-
rmSync,
|
|
8
|
-
writeFileSync,
|
|
9
|
-
} from "node:fs";
|
|
2
|
+
import { copyFileSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync, } from "node:fs";
|
|
10
3
|
import { homedir } from "node:os";
|
|
11
4
|
import { dirname, resolve } from "node:path";
|
|
12
|
-
|
|
13
5
|
import { requireModelRoute, sameModelRoute } from "./model-route.mjs";
|
|
14
6
|
import { acquireOwnedStoreLock } from "./store-lock.mjs";
|
|
15
|
-
|
|
7
|
+
import { isUnknownRecord } from "./runtime-types.mjs";
|
|
16
8
|
const STORE_SCHEMA_VERSION = 1;
|
|
17
9
|
const TARGET_FIELDS = new Set(["provider", "model", "reasoningEffort"]);
|
|
18
10
|
const STATE_PROTOCOL_SOURCE = Object.freeze({
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
11
|
+
kind: "plugin",
|
|
12
|
+
plugin: "odai-dsh-runtime",
|
|
13
|
+
form: "instructions",
|
|
22
14
|
});
|
|
23
|
-
|
|
24
15
|
export const COMPACTION_STATE_PROTOCOL = [
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
16
|
+
"Additional checkpoint integrity protocol:",
|
|
17
|
+
"- Treat facts explicitly marked CURRENT, corrected, or authoritative as the active state.",
|
|
18
|
+
"- Facts marked SUPERSEDED, REJECTED, obsolete, or historical may be retained only as rejected history; never describe them as current, latest, selected, or pending.",
|
|
19
|
+
"- Preserve opaque identifiers, paths, commands, status values, delimiters, and numeric values byte-for-byte when they determine continuation state.",
|
|
20
|
+
"- Before emitting the checkpoint, self-check that no rejected fact is framed as active and that no active value conflicts with another section.",
|
|
30
21
|
].join("\n");
|
|
31
|
-
|
|
32
22
|
export const COMPACTION_CONFIG_PROMPT = [
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
23
|
+
"## Odai compaction model configuration",
|
|
24
|
+
"When the user naturally asks to inspect, set, change, or reset the model or reasoning effort used to summarize compacted context, use odai_compaction_config.",
|
|
25
|
+
"Set a compaction target only when the user explicitly supplies both provider and model. Pass reasoningEffort only when the user explicitly supplies it. Never infer or silently choose any value. If the user changes only the effort for an existing persisted target, inspect and preserve that exact provider/model pair.",
|
|
26
|
+
"The default is inherit: compaction uses the current conversation route. A configured target changes only compaction-summary requests; it does not change the controller, researcher, planner, executor, reviewer, frontend, normal conversation, summary output budget, or cache-retention policy. An optional explicit reasoning effort is likewise scoped only to compaction summaries; omitting it preserves the existing reasoning inheritance and cross-model isolation behavior.",
|
|
27
|
+
"Configured targets also receive a provider-neutral checkpoint integrity protocol that keeps current facts above superseded or rejected history and preserves continuation-critical opaque values exactly.",
|
|
28
|
+
"Do not ask the user to edit YAML, JSON, managed Agent files, or Plugin configuration. The tool owns persistence. Set/remove changes apply to the next compaction request.",
|
|
29
|
+
"An invalid persisted store is visible through the tool while runtime requests safely inherit the conversation route. If the configured route fails or produces an incomplete summary, DSH must preserve the original history rather than landing a partial checkpoint.",
|
|
40
30
|
].join("\n");
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
return value;
|
|
31
|
+
export class CompactionModelStoreValidationError extends Error {
|
|
32
|
+
}
|
|
33
|
+
function errorCode(error) {
|
|
34
|
+
if (error === null || typeof error !== "object" || !("code" in error))
|
|
35
|
+
return undefined;
|
|
36
|
+
return typeof error.code === "string" ? error.code : undefined;
|
|
49
37
|
}
|
|
50
|
-
|
|
51
38
|
export function resolveCompactionTarget(value, field = "Odai compaction model target") {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
39
|
+
if (!isUnknownRecord(value))
|
|
40
|
+
throw new TypeError(`${field} must be an object`);
|
|
41
|
+
const unknownFields = Object.keys(value).filter((name) => !TARGET_FIELDS.has(name));
|
|
42
|
+
if (unknownFields.length > 0)
|
|
43
|
+
throw new TypeError(`${field} has unknown fields: ${unknownFields.join(", ")}`);
|
|
44
|
+
if (typeof value.provider !== "string" || value.provider.trim() === "") {
|
|
45
|
+
throw new TypeError(`${field}.provider must be a non-empty string`);
|
|
46
|
+
}
|
|
47
|
+
if (typeof value.model !== "string" || value.model.trim() === "") {
|
|
48
|
+
throw new TypeError(`${field}.model must be a non-empty string`);
|
|
49
|
+
}
|
|
50
|
+
if (value.reasoningEffort !== undefined
|
|
51
|
+
&& (typeof value.reasoningEffort !== "string" || value.reasoningEffort.trim() === "")) {
|
|
52
|
+
throw new TypeError(`${field}.reasoningEffort must be a non-empty string`);
|
|
53
|
+
}
|
|
54
|
+
return Object.freeze({
|
|
55
|
+
provider: value.provider.trim(),
|
|
56
|
+
model: value.model.trim(),
|
|
57
|
+
...(typeof value.reasoningEffort === "string" ? { reasoningEffort: value.reasoningEffort.trim() } : {}),
|
|
58
|
+
});
|
|
70
59
|
}
|
|
71
|
-
|
|
72
60
|
export function resolveCompactionConfigPath(configuredPath, env = process.env) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
61
|
+
if (configuredPath !== undefined) {
|
|
62
|
+
if (typeof configuredPath !== "string" || configuredPath.trim() === "") {
|
|
63
|
+
throw new TypeError("config.compaction.configPath must be a non-empty string");
|
|
64
|
+
}
|
|
65
|
+
return resolve(configuredPath.trim());
|
|
76
66
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
: resolve(homedir(), ".dsh");
|
|
82
|
-
return resolve(dshHome, "odai", "compaction.json");
|
|
67
|
+
const dshHome = typeof env.DSH_HOME === "string" && env.DSH_HOME.trim() !== ""
|
|
68
|
+
? resolve(env.DSH_HOME.trim())
|
|
69
|
+
: resolve(homedir(), ".dsh");
|
|
70
|
+
return resolve(dshHome, "odai", "compaction.json");
|
|
83
71
|
}
|
|
84
|
-
|
|
85
72
|
export function readCompactionModelStore(configPath) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
} catch (error) {
|
|
90
|
-
if (error?.code === "ENOENT") return Object.freeze({ schemaVersion: STORE_SCHEMA_VERSION });
|
|
91
|
-
throw new Error(`cannot read Odai compaction model configuration ${configPath}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
92
|
-
}
|
|
93
|
-
let parsed;
|
|
94
|
-
try {
|
|
95
|
-
parsed = JSON.parse(text);
|
|
96
|
-
} catch (error) {
|
|
97
|
-
throw new CompactionModelStoreValidationError(`Odai compaction model configuration ${configPath} is not valid JSON`, { cause: error });
|
|
98
|
-
}
|
|
99
|
-
try {
|
|
100
|
-
const store = assertPlainObject(parsed, `Odai compaction model configuration ${configPath}`);
|
|
101
|
-
const unknown = Object.keys(store).filter((field) => !["schemaVersion", "target"].includes(field));
|
|
102
|
-
if (unknown.length > 0) {
|
|
103
|
-
throw new TypeError(`Odai compaction model configuration ${configPath} has unknown fields: ${unknown.join(", ")}`);
|
|
73
|
+
let text;
|
|
74
|
+
try {
|
|
75
|
+
text = readFileSync(configPath, "utf8");
|
|
104
76
|
}
|
|
105
|
-
|
|
106
|
-
|
|
77
|
+
catch (error) {
|
|
78
|
+
if (errorCode(error) === "ENOENT")
|
|
79
|
+
return Object.freeze({ schemaVersion: STORE_SCHEMA_VERSION });
|
|
80
|
+
throw new Error(`cannot read Odai compaction model configuration ${configPath}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
81
|
+
}
|
|
82
|
+
let parsed;
|
|
83
|
+
try {
|
|
84
|
+
parsed = JSON.parse(text);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
throw new CompactionModelStoreValidationError(`Odai compaction model configuration ${configPath} is not valid JSON`, { cause: error });
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
if (!isUnknownRecord(parsed))
|
|
91
|
+
throw new TypeError(`Odai compaction model configuration ${configPath} must be an object`);
|
|
92
|
+
const unknownFields = Object.keys(parsed).filter((field) => !["schemaVersion", "target"].includes(field));
|
|
93
|
+
if (unknownFields.length > 0) {
|
|
94
|
+
throw new TypeError(`Odai compaction model configuration ${configPath} has unknown fields: ${unknownFields.join(", ")}`);
|
|
95
|
+
}
|
|
96
|
+
if (parsed.schemaVersion !== STORE_SCHEMA_VERSION) {
|
|
97
|
+
throw new TypeError(`Odai compaction model configuration ${configPath} has unsupported schemaVersion ${String(parsed.schemaVersion)}`);
|
|
98
|
+
}
|
|
99
|
+
if (parsed.target === undefined)
|
|
100
|
+
return Object.freeze({ schemaVersion: STORE_SCHEMA_VERSION });
|
|
101
|
+
return Object.freeze({
|
|
102
|
+
schemaVersion: STORE_SCHEMA_VERSION,
|
|
103
|
+
target: resolveCompactionTarget(parsed.target, `Odai compaction model configuration ${configPath}.target`),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
if (error instanceof CompactionModelStoreValidationError)
|
|
108
|
+
throw error;
|
|
109
|
+
throw new CompactionModelStoreValidationError(`Odai compaction model configuration ${configPath} failed validation: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
107
110
|
}
|
|
108
|
-
if (store.target === undefined) return Object.freeze({ schemaVersion: STORE_SCHEMA_VERSION });
|
|
109
|
-
return Object.freeze({
|
|
110
|
-
schemaVersion: STORE_SCHEMA_VERSION,
|
|
111
|
-
target: resolveCompactionTarget(store.target, `Odai compaction model configuration ${configPath}.target`),
|
|
112
|
-
});
|
|
113
|
-
} catch (error) {
|
|
114
|
-
if (error instanceof CompactionModelStoreValidationError) throw error;
|
|
115
|
-
throw new CompactionModelStoreValidationError(
|
|
116
|
-
`Odai compaction model configuration ${configPath} failed validation: ${error instanceof Error ? error.message : String(error)}`,
|
|
117
|
-
{ cause: error },
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
111
|
}
|
|
121
|
-
|
|
122
112
|
export function effectiveCompactionTarget(configPath) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
113
|
+
const stored = readCompactionModelStore(configPath);
|
|
114
|
+
return Object.freeze({
|
|
115
|
+
...(stored.target === undefined ? {} : { target: stored.target }),
|
|
116
|
+
source: stored.target === undefined ? "inherit" : "persisted",
|
|
117
|
+
});
|
|
128
118
|
}
|
|
129
|
-
|
|
130
119
|
export function applyCompactionTarget(options, target, sessions) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
120
|
+
if (options?.purpose !== "compaction" || target === undefined)
|
|
121
|
+
return false;
|
|
122
|
+
const currentRoute = options.sessionId === undefined
|
|
123
|
+
? undefined
|
|
124
|
+
: sessions?.get?.(options.sessionId)?.requestHeader?.()?.config;
|
|
125
|
+
const durableRouteKnown = typeof currentRoute?.provider === "string"
|
|
126
|
+
&& currentRoute.provider.length > 0
|
|
127
|
+
&& typeof currentRoute.model === "string"
|
|
128
|
+
&& currentRoute.model.length > 0;
|
|
129
|
+
const targetChangesDurableRoute = durableRouteKnown
|
|
130
|
+
&& (currentRoute.provider !== target.provider || currentRoute.model !== target.model);
|
|
131
|
+
const inheritedReasoning = targetChangesDurableRoute
|
|
132
|
+
&& typeof currentRoute.reasoningEffort === "string"
|
|
133
|
+
&& currentRoute.reasoningEffort.length > 0
|
|
134
|
+
&& options.reasoningEffort === currentRoute.reasoningEffort;
|
|
135
|
+
const changesRoute = options.provider !== target.provider || options.model !== target.model;
|
|
136
|
+
const changesReasoning = target.reasoningEffort !== undefined
|
|
137
|
+
&& options.reasoningEffort !== target.reasoningEffort;
|
|
138
|
+
if (!changesRoute && !inheritedReasoning && !changesReasoning)
|
|
139
|
+
return false;
|
|
140
|
+
if (!Object.isExtensible(options)) {
|
|
141
|
+
throw new Error("configured Odai compaction model cannot be applied to an immutable request");
|
|
142
|
+
}
|
|
143
|
+
if (target.reasoningEffort !== undefined)
|
|
144
|
+
options.reasoningEffort = target.reasoningEffort;
|
|
145
|
+
else if (inheritedReasoning)
|
|
146
|
+
delete options.reasoningEffort;
|
|
147
|
+
if (changesRoute) {
|
|
148
|
+
options.provider = target.provider;
|
|
149
|
+
options.model = target.model;
|
|
150
|
+
}
|
|
151
|
+
return true;
|
|
160
152
|
}
|
|
161
|
-
|
|
162
153
|
export function applyCompactionStateProtocol(options, target) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
message?.source?.kind === STATE_PROTOCOL_SOURCE.kind
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
154
|
+
if (options?.purpose !== "compaction" || target === undefined)
|
|
155
|
+
return false;
|
|
156
|
+
if (!Array.isArray(options.messages) || !Object.isExtensible(options)) {
|
|
157
|
+
throw new Error("configured Odai compaction model requires a mutable message envelope for checkpoint integrity");
|
|
158
|
+
}
|
|
159
|
+
const alreadyApplied = options.messages.some((message) => (message?.source?.kind === STATE_PROTOCOL_SOURCE.kind
|
|
160
|
+
&& message.source.plugin === STATE_PROTOCOL_SOURCE.plugin
|
|
161
|
+
&& message.source.form === STATE_PROTOCOL_SOURCE.form
|
|
162
|
+
&& Array.isArray(message.content)
|
|
163
|
+
&& message.content.some((block) => block?.type === "text" && block.text === COMPACTION_STATE_PROTOCOL)));
|
|
164
|
+
if (alreadyApplied)
|
|
165
|
+
return false;
|
|
166
|
+
options.messages = [...options.messages, {
|
|
167
|
+
id: randomUUID(),
|
|
168
|
+
role: "user",
|
|
169
|
+
content: [{ type: "text", text: COMPACTION_STATE_PROTOCOL }],
|
|
170
|
+
source: STATE_PROTOCOL_SOURCE,
|
|
171
|
+
}];
|
|
172
|
+
return true;
|
|
182
173
|
}
|
|
183
|
-
|
|
184
174
|
function writeCompactionModelStore(configPath, target) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
175
|
+
mkdirSync(dirname(configPath), { recursive: true, mode: 0o700 });
|
|
176
|
+
const temporary = `${configPath}.tmp-${process.pid}-${randomUUID()}`;
|
|
177
|
+
const value = `${JSON.stringify({ schemaVersion: STORE_SCHEMA_VERSION, target }, null, 2)}\n`;
|
|
178
|
+
try {
|
|
179
|
+
writeFileSync(temporary, value, { encoding: "utf8", mode: 0o600, flag: "wx" });
|
|
180
|
+
renameSync(temporary, configPath);
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
rmSync(temporary, { force: true });
|
|
184
|
+
}
|
|
194
185
|
}
|
|
195
|
-
|
|
196
186
|
function preserveInvalidStore(configPath) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
187
|
+
try {
|
|
188
|
+
renameSync(configPath, `${configPath}.invalid-${Date.now()}-${randomUUID()}`);
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
if (errorCode(error) === "ENOENT")
|
|
193
|
+
return false;
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
204
196
|
}
|
|
205
|
-
|
|
206
197
|
export function invalidatePersistedCompactionTarget(configPath, expectedTarget) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
198
|
+
const releaseLock = acquireOwnedStoreLock(configPath, "Odai compaction model configuration");
|
|
199
|
+
try {
|
|
200
|
+
const current = readCompactionModelStore(configPath);
|
|
201
|
+
if (!sameModelRoute(current.target, expectedTarget)) {
|
|
202
|
+
return Object.freeze({ invalidated: false, reason: "target-changed-or-not-persisted" });
|
|
203
|
+
}
|
|
204
|
+
if (!current.target)
|
|
205
|
+
return Object.freeze({ invalidated: false, reason: "target-changed-or-not-persisted" });
|
|
206
|
+
const backupPath = `${configPath}.invalidated-${Date.now()}-${randomUUID()}`;
|
|
207
|
+
copyFileSync(configPath, backupPath);
|
|
208
|
+
rmSync(configPath, { force: true });
|
|
209
|
+
return Object.freeze({ invalidated: true, backupPath, target: current.target });
|
|
210
|
+
}
|
|
211
|
+
finally {
|
|
212
|
+
releaseLock();
|
|
212
213
|
}
|
|
213
|
-
const backupPath = `${configPath}.invalidated-${Date.now()}-${randomUUID()}`;
|
|
214
|
-
copyFileSync(configPath, backupPath);
|
|
215
|
-
rmSync(configPath, { force: true });
|
|
216
|
-
return Object.freeze({ invalidated: true, backupPath, target: current.target });
|
|
217
|
-
} finally {
|
|
218
|
-
releaseLock();
|
|
219
|
-
}
|
|
220
214
|
}
|
|
221
|
-
|
|
222
215
|
function resultFor(configPath, action, selection, recoveredInvalidStore = false) {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
216
|
+
return {
|
|
217
|
+
action,
|
|
218
|
+
configPath,
|
|
219
|
+
source: selection.source,
|
|
220
|
+
...(selection.target === undefined ? {} : { target: selection.target }),
|
|
221
|
+
requiresNextCompaction: action !== "show",
|
|
222
|
+
...(selection.invalidStore ? { invalidStore: true } : {}),
|
|
223
|
+
...(recoveredInvalidStore ? { recoveredInvalidStore: true } : {}),
|
|
224
|
+
};
|
|
232
225
|
}
|
|
233
|
-
|
|
234
226
|
export function createCompactionConfigTool(configPath, options = {}) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
type: "object",
|
|
255
|
-
additionalProperties: false,
|
|
256
|
-
required: ["action"],
|
|
257
|
-
properties: {
|
|
258
|
-
action: {
|
|
259
|
-
type: "string",
|
|
260
|
-
enum: ["show", "set", "remove"],
|
|
261
|
-
description: "Show the effective target, set a user-specified provider/model and optional reasoning effort, or remove it to restore inheritance.",
|
|
262
|
-
},
|
|
263
|
-
provider: {
|
|
264
|
-
type: "string",
|
|
265
|
-
description: "Provider id explicitly supplied by the user; required for set.",
|
|
266
|
-
},
|
|
267
|
-
model: {
|
|
268
|
-
type: "string",
|
|
269
|
-
description: "Model id explicitly supplied by the user; required for set.",
|
|
270
|
-
},
|
|
271
|
-
reasoningEffort: {
|
|
272
|
-
type: "string",
|
|
273
|
-
description: "Optional compaction reasoning effort explicitly supplied by the user.",
|
|
274
|
-
},
|
|
275
|
-
},
|
|
276
|
-
},
|
|
277
|
-
output: {
|
|
278
|
-
schema: {
|
|
279
|
-
type: "object",
|
|
280
|
-
additionalProperties: false,
|
|
281
|
-
required: ["action", "configPath", "source", "requiresNextCompaction"],
|
|
282
|
-
properties: {
|
|
283
|
-
action: { type: "string", enum: ["show", "set", "remove"] },
|
|
284
|
-
configPath: { type: "string" },
|
|
285
|
-
source: { type: "string", enum: ["inherit", "persisted"] },
|
|
286
|
-
target: {
|
|
227
|
+
const onConfigured = typeof options.onConfigured === "function" ? options.onConfigured : () => { };
|
|
228
|
+
const resolveCallConfig = typeof options.resolveCallConfig === "function" ? options.resolveCallConfig : undefined;
|
|
229
|
+
const isChild = typeof options.isChild === "function"
|
|
230
|
+
? options.isChild
|
|
231
|
+
: (agent) => {
|
|
232
|
+
const header = agent?.session?.header;
|
|
233
|
+
return header?.origin === "subagent"
|
|
234
|
+
|| (Number.isSafeInteger(header?.delegationDepth) && (header?.delegationDepth ?? 0) > 0);
|
|
235
|
+
};
|
|
236
|
+
return {
|
|
237
|
+
name: "odai_compaction_config",
|
|
238
|
+
description: [
|
|
239
|
+
"Inspect, set, or remove the shared Odai compaction-summary target.",
|
|
240
|
+
"Use only when the user naturally asks about the compaction model/reasoning effort or explicitly supplies the provider and model to set. Never select a provider, model, or reasoning effort on the user's behalf.",
|
|
241
|
+
"The default is inherit. A persisted target changes only future compaction-summary requests; controller and responsibility routes, output budget, and cache retention stay unchanged. An optional user-specified reasoning effort applies only to those summaries.",
|
|
242
|
+
"Persisted targets also receive a provider-neutral integrity protocol that keeps current facts above superseded history and preserves continuation-critical opaque values exactly.",
|
|
243
|
+
"An invalid store is reported while runtime requests safely inherit. If the configured route or summary fails, DSH preserves the original history. Changes apply from the next compaction request.",
|
|
244
|
+
].join(" "),
|
|
245
|
+
parameters: {
|
|
287
246
|
type: "object",
|
|
288
247
|
additionalProperties: false,
|
|
289
|
-
required: ["
|
|
248
|
+
required: ["action"],
|
|
290
249
|
properties: {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
250
|
+
action: { type: "string", enum: ["show", "set", "remove"], description: "Show the effective target, set a user-specified provider/model and optional reasoning effort, or remove it to restore inheritance." },
|
|
251
|
+
provider: { type: "string", description: "Provider id explicitly supplied by the user; required for set." },
|
|
252
|
+
model: { type: "string", description: "Model id explicitly supplied by the user; required for set." },
|
|
253
|
+
reasoningEffort: { type: "string", description: "Optional compaction reasoning effort explicitly supplied by the user." },
|
|
294
254
|
},
|
|
295
|
-
},
|
|
296
|
-
requiresNextCompaction: { type: "boolean" },
|
|
297
|
-
invalidStore: { type: "boolean" },
|
|
298
|
-
recoveredInvalidStore: { type: "boolean" },
|
|
299
255
|
},
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
256
|
+
output: {
|
|
257
|
+
schema: {
|
|
258
|
+
type: "object",
|
|
259
|
+
additionalProperties: false,
|
|
260
|
+
required: ["action", "configPath", "source", "requiresNextCompaction"],
|
|
261
|
+
properties: {
|
|
262
|
+
action: { type: "string", enum: ["show", "set", "remove"] },
|
|
263
|
+
configPath: { type: "string" },
|
|
264
|
+
source: { type: "string", enum: ["inherit", "persisted"] },
|
|
265
|
+
target: { type: "object", additionalProperties: false, required: ["provider", "model"], properties: { provider: { type: "string" }, model: { type: "string" }, reasoningEffort: { type: "string" } } },
|
|
266
|
+
requiresNextCompaction: { type: "boolean" },
|
|
267
|
+
invalidStore: { type: "boolean" },
|
|
268
|
+
recoveredInvalidStore: { type: "boolean" },
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
render(_arguments, value) {
|
|
272
|
+
const target = value.target
|
|
273
|
+
? `${value.target.provider}/${value.target.model} (reasoning: ${value.target.reasoningEffort ?? "not configured"})`
|
|
274
|
+
: "inherit the current conversation model and reasoning";
|
|
275
|
+
return [{
|
|
276
|
+
type: "text",
|
|
277
|
+
text: [
|
|
278
|
+
`${value.action === "show" ? "Current" : "Updated"} Odai compaction target (${value.source}): ${target}.`,
|
|
279
|
+
" This affects compaction summaries only; other model routes, the summary budget, and cache retention are unchanged.",
|
|
280
|
+
...(value.invalidStore ? [" The persisted store is invalid, so runtime requests currently inherit; set or remove the target to repair it."] : []),
|
|
281
|
+
...(value.recoveredInvalidStore ? [" An invalid prior store was preserved before the configuration was repaired or reset."] : []),
|
|
282
|
+
...(value.requiresNextCompaction ? [" The change applies from the next compaction request."] : []),
|
|
283
|
+
].join(""),
|
|
284
|
+
}];
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
execute(arguments_, execution) {
|
|
288
|
+
if (!execution.agent)
|
|
289
|
+
throw new Error("odai_compaction_config requires an owning agent session");
|
|
290
|
+
const agent = execution.agent;
|
|
291
|
+
if (isChild(agent))
|
|
292
|
+
throw new Error("child agents may not change Odai compaction model configuration");
|
|
293
|
+
if (!isUnknownRecord(arguments_))
|
|
294
|
+
throw new TypeError("arguments must be an object");
|
|
295
|
+
const unknownFields = Object.keys(arguments_).filter((field) => !["action", "provider", "model", "reasoningEffort"].includes(field));
|
|
296
|
+
if (unknownFields.length > 0)
|
|
297
|
+
throw new TypeError(`unknown arguments: ${unknownFields.join(", ")}`);
|
|
298
|
+
const action = arguments_.action;
|
|
299
|
+
if (action !== "show" && action !== "set" && action !== "remove")
|
|
300
|
+
throw new TypeError("action must be show, set, or remove");
|
|
301
|
+
if (action === "show") {
|
|
302
|
+
if (arguments_.provider !== undefined || arguments_.model !== undefined || arguments_.reasoningEffort !== undefined) {
|
|
303
|
+
throw new TypeError("provider, model, and reasoningEffort must be omitted for show");
|
|
304
|
+
}
|
|
305
|
+
let selection;
|
|
306
|
+
try {
|
|
307
|
+
selection = effectiveCompactionTarget(configPath);
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
if (!(error instanceof CompactionModelStoreValidationError))
|
|
311
|
+
throw error;
|
|
312
|
+
selection = Object.freeze({ source: "inherit", invalidStore: true });
|
|
313
|
+
}
|
|
314
|
+
return Promise.resolve(resultFor(configPath, "show", selection));
|
|
358
315
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
316
|
+
if (action === "remove"
|
|
317
|
+
&& (arguments_.provider !== undefined || arguments_.model !== undefined || arguments_.reasoningEffort !== undefined)) {
|
|
318
|
+
throw new TypeError("provider, model, and reasoningEffort must be omitted for remove");
|
|
319
|
+
}
|
|
320
|
+
const proposed = action === "set"
|
|
321
|
+
? resolveCompactionTarget({
|
|
322
|
+
provider: arguments_.provider,
|
|
323
|
+
model: arguments_.model,
|
|
324
|
+
...(arguments_.reasoningEffort === undefined ? {} : { reasoningEffort: arguments_.reasoningEffort }),
|
|
325
|
+
})
|
|
326
|
+
: undefined;
|
|
327
|
+
const commit = (validationStatus) => {
|
|
328
|
+
const releaseLock = acquireOwnedStoreLock(configPath, "Odai compaction model configuration");
|
|
329
|
+
try {
|
|
330
|
+
let recoveredInvalidStore = false;
|
|
331
|
+
try {
|
|
332
|
+
readCompactionModelStore(configPath);
|
|
333
|
+
}
|
|
334
|
+
catch (error) {
|
|
335
|
+
if (!(error instanceof CompactionModelStoreValidationError)) {
|
|
336
|
+
throw new Error("Odai compaction model configuration could not be read safely; no changes were made", { cause: error });
|
|
337
|
+
}
|
|
338
|
+
recoveredInvalidStore = preserveInvalidStore(configPath);
|
|
339
|
+
}
|
|
340
|
+
if (action === "set" && proposed)
|
|
341
|
+
writeCompactionModelStore(configPath, proposed);
|
|
342
|
+
else
|
|
343
|
+
rmSync(configPath, { force: true });
|
|
344
|
+
const selection = action === "set" && proposed
|
|
345
|
+
? Object.freeze({ target: proposed, source: "persisted" })
|
|
346
|
+
: Object.freeze({ source: "inherit" });
|
|
347
|
+
onConfigured(agent, {
|
|
348
|
+
action,
|
|
349
|
+
...(selection.target === undefined ? {} : { target: selection.target }),
|
|
350
|
+
...(validationStatus === "verified" ? { validationStatus } : {}),
|
|
351
|
+
...(recoveredInvalidStore ? { recoveredInvalidStore: true } : {}),
|
|
352
|
+
});
|
|
353
|
+
return resultFor(configPath, action, selection, recoveredInvalidStore);
|
|
354
|
+
}
|
|
355
|
+
finally {
|
|
356
|
+
releaseLock();
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
if (action === "set" && proposed) {
|
|
360
|
+
return requireModelRoute(resolveCallConfig, proposed, execution.signal, "compaction model route").then((validation) => commit(validation.status));
|
|
361
|
+
}
|
|
362
|
+
return Promise.resolve(commit());
|
|
363
|
+
},
|
|
364
|
+
};
|
|
388
365
|
}
|
|
366
|
+
//# sourceMappingURL=compaction-config.mjs.map
|