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,360 +1,334 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import {
|
|
3
|
-
mkdirSync,
|
|
4
|
-
readFileSync,
|
|
5
|
-
renameSync,
|
|
6
|
-
rmSync,
|
|
7
|
-
writeFileSync,
|
|
8
|
-
} from "node:fs";
|
|
2
|
+
import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync, } from "node:fs";
|
|
9
3
|
import { homedir } from "node:os";
|
|
10
4
|
import { dirname, resolve } from "node:path";
|
|
11
|
-
|
|
12
5
|
import { acquireOwnedStoreLock } from "./store-lock.mjs";
|
|
13
|
-
|
|
6
|
+
import { isUnknownRecord } from "./runtime-types.mjs";
|
|
14
7
|
const STORE_SCHEMA_VERSION = 1;
|
|
15
8
|
const POLICY_FIELDS = new Set(["concise", "maxTokens"]);
|
|
16
9
|
const OUTPUT_MODES = new Set(["normal", "concise", "economy"]);
|
|
17
10
|
const DEFAULT_ECONOMY_MAX_TOKENS = 500;
|
|
18
11
|
export const DEFAULT_OUTPUT_POLICY = Object.freeze({ concise: true });
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
return value;
|
|
12
|
+
export class OutputPolicyStoreValidationError extends Error {
|
|
13
|
+
}
|
|
14
|
+
function errorCode(error) {
|
|
15
|
+
if (error === null || typeof error !== "object" || !("code" in error))
|
|
16
|
+
return undefined;
|
|
17
|
+
return typeof error.code === "string" ? error.code : undefined;
|
|
27
18
|
}
|
|
28
|
-
|
|
29
19
|
export function resolveOutputPolicy(value, field = "Odai output policy") {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
20
|
+
if (!isUnknownRecord(value))
|
|
21
|
+
throw new TypeError(`${field} must be an object`);
|
|
22
|
+
const unknownFields = Object.keys(value).filter((name) => !POLICY_FIELDS.has(name));
|
|
23
|
+
if (unknownFields.length > 0)
|
|
24
|
+
throw new TypeError(`${field} has unknown fields: ${unknownFields.join(", ")}`);
|
|
25
|
+
if (!Object.hasOwn(value, "concise") || typeof value.concise !== "boolean") {
|
|
26
|
+
throw new TypeError(`${field}.concise must be an own boolean property`);
|
|
27
|
+
}
|
|
28
|
+
if (value.maxTokens !== undefined
|
|
29
|
+
&& (typeof value.maxTokens !== "number" || !Number.isSafeInteger(value.maxTokens) || value.maxTokens <= 0)) {
|
|
30
|
+
throw new TypeError(`${field}.maxTokens must be a positive integer`);
|
|
31
|
+
}
|
|
32
|
+
return Object.freeze({
|
|
33
|
+
concise: value.concise,
|
|
34
|
+
...(typeof value.maxTokens === "number" ? { maxTokens: value.maxTokens } : {}),
|
|
35
|
+
});
|
|
44
36
|
}
|
|
45
|
-
|
|
46
37
|
export function resolveOutputConfigPath(configuredPath, env = process.env) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
38
|
+
if (configuredPath !== undefined) {
|
|
39
|
+
if (typeof configuredPath !== "string" || configuredPath.trim() === "") {
|
|
40
|
+
throw new TypeError("config.output.configPath must be a non-empty string");
|
|
41
|
+
}
|
|
42
|
+
return resolve(configuredPath.trim());
|
|
50
43
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
: resolve(homedir(), ".dsh");
|
|
56
|
-
return resolve(dshHome, "odai", "output.json");
|
|
44
|
+
const dshHome = typeof env.DSH_HOME === "string" && env.DSH_HOME.trim() !== ""
|
|
45
|
+
? resolve(env.DSH_HOME.trim())
|
|
46
|
+
: resolve(homedir(), ".dsh");
|
|
47
|
+
return resolve(dshHome, "odai", "output.json");
|
|
57
48
|
}
|
|
58
|
-
|
|
59
49
|
export function readOutputPolicyStore(configPath) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
} catch (error) {
|
|
64
|
-
if (error?.code === "ENOENT") return Object.freeze({ schemaVersion: STORE_SCHEMA_VERSION });
|
|
65
|
-
throw new Error(`cannot read Odai output configuration ${configPath}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
66
|
-
}
|
|
67
|
-
let parsed;
|
|
68
|
-
try {
|
|
69
|
-
parsed = JSON.parse(text);
|
|
70
|
-
} catch (error) {
|
|
71
|
-
throw new OutputPolicyStoreValidationError(`Odai output configuration ${configPath} is not valid JSON`, { cause: error });
|
|
72
|
-
}
|
|
73
|
-
try {
|
|
74
|
-
const store = assertPlainObject(parsed, `Odai output configuration ${configPath}`);
|
|
75
|
-
const unknown = Object.keys(store).filter((field) => !["schemaVersion", "policy"].includes(field));
|
|
76
|
-
if (unknown.length > 0) {
|
|
77
|
-
throw new TypeError(`Odai output configuration ${configPath} has unknown fields: ${unknown.join(", ")}`);
|
|
50
|
+
let text;
|
|
51
|
+
try {
|
|
52
|
+
text = readFileSync(configPath, "utf8");
|
|
78
53
|
}
|
|
79
|
-
|
|
80
|
-
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (errorCode(error) === "ENOENT")
|
|
56
|
+
return Object.freeze({ schemaVersion: STORE_SCHEMA_VERSION });
|
|
57
|
+
throw new Error(`cannot read Odai output configuration ${configPath}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
58
|
+
}
|
|
59
|
+
let parsed;
|
|
60
|
+
try {
|
|
61
|
+
parsed = JSON.parse(text);
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
throw new OutputPolicyStoreValidationError(`Odai output configuration ${configPath} is not valid JSON`, { cause: error });
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
if (!isUnknownRecord(parsed))
|
|
68
|
+
throw new TypeError(`Odai output configuration ${configPath} must be an object`);
|
|
69
|
+
const unknownFields = Object.keys(parsed).filter((field) => !["schemaVersion", "policy"].includes(field));
|
|
70
|
+
if (unknownFields.length > 0) {
|
|
71
|
+
throw new TypeError(`Odai output configuration ${configPath} has unknown fields: ${unknownFields.join(", ")}`);
|
|
72
|
+
}
|
|
73
|
+
if (parsed.schemaVersion !== STORE_SCHEMA_VERSION) {
|
|
74
|
+
throw new TypeError(`Odai output configuration ${configPath} has unsupported schemaVersion ${String(parsed.schemaVersion)}`);
|
|
75
|
+
}
|
|
76
|
+
if (parsed.policy === undefined)
|
|
77
|
+
return Object.freeze({ schemaVersion: STORE_SCHEMA_VERSION });
|
|
78
|
+
return Object.freeze({
|
|
79
|
+
schemaVersion: STORE_SCHEMA_VERSION,
|
|
80
|
+
policy: resolveOutputPolicy(parsed.policy, `Odai output configuration ${configPath}.policy`),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (error instanceof OutputPolicyStoreValidationError)
|
|
85
|
+
throw error;
|
|
86
|
+
throw new OutputPolicyStoreValidationError(`Odai output configuration ${configPath} failed validation: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
81
87
|
}
|
|
82
|
-
if (store.policy === undefined) return Object.freeze({ schemaVersion: STORE_SCHEMA_VERSION });
|
|
83
|
-
return Object.freeze({
|
|
84
|
-
schemaVersion: STORE_SCHEMA_VERSION,
|
|
85
|
-
policy: resolveOutputPolicy(store.policy, `Odai output configuration ${configPath}.policy`),
|
|
86
|
-
});
|
|
87
|
-
} catch (error) {
|
|
88
|
-
if (error instanceof OutputPolicyStoreValidationError) throw error;
|
|
89
|
-
throw new OutputPolicyStoreValidationError(
|
|
90
|
-
`Odai output configuration ${configPath} failed validation: ${error instanceof Error ? error.message : String(error)}`,
|
|
91
|
-
{ cause: error },
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
88
|
}
|
|
95
|
-
|
|
96
89
|
export function effectiveOutputPolicy(configPath) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
90
|
+
const stored = readOutputPolicyStore(configPath);
|
|
91
|
+
return Object.freeze({
|
|
92
|
+
policy: stored.policy ?? DEFAULT_OUTPUT_POLICY,
|
|
93
|
+
source: stored.policy ? "persisted" : "default",
|
|
94
|
+
});
|
|
102
95
|
}
|
|
103
|
-
|
|
104
96
|
export const IN_PLACE_OUTPUT_RESPONSIBILITIES = Object.freeze(["planner", "executor", "frontend"]);
|
|
105
|
-
|
|
106
97
|
function resolveResponsibilityOutputBudget(policy, route) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
return Object.freeze({ source: "unbounded-by-odai" });
|
|
98
|
+
if (route.maxTokens !== undefined) {
|
|
99
|
+
return Object.freeze({ source: "responsibility-override", maxTokens: route.maxTokens });
|
|
100
|
+
}
|
|
101
|
+
if (policy?.maxTokens !== undefined) {
|
|
102
|
+
return Object.freeze({
|
|
103
|
+
source: "controller-policy",
|
|
104
|
+
maxTokens: policy.maxTokens,
|
|
105
|
+
warning: "responsibility-inherits-controller-ceiling",
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return Object.freeze({ source: "unbounded-by-odai" });
|
|
121
109
|
}
|
|
122
|
-
|
|
123
110
|
export function resolveInPlaceResponsibilityOutputBudgets(policy, routes) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
111
|
+
if (!isUnknownRecord(routes))
|
|
112
|
+
return undefined;
|
|
113
|
+
const entries = [];
|
|
114
|
+
for (const responsibility of IN_PLACE_OUTPUT_RESPONSIBILITIES) {
|
|
115
|
+
const route = routes[responsibility];
|
|
116
|
+
if (isUnknownRecord(route))
|
|
117
|
+
entries.push([responsibility, resolveResponsibilityOutputBudget(policy, route)]);
|
|
118
|
+
}
|
|
119
|
+
const budgets = Object.fromEntries(entries);
|
|
120
|
+
return Object.keys(budgets).length > 0 ? Object.freeze(budgets) : undefined;
|
|
132
121
|
}
|
|
133
|
-
|
|
134
122
|
function writeOutputPolicyStore(configPath, policy) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
123
|
+
mkdirSync(dirname(configPath), { recursive: true, mode: 0o700 });
|
|
124
|
+
const temporary = `${configPath}.tmp-${process.pid}-${randomUUID()}`;
|
|
125
|
+
const value = `${JSON.stringify({ schemaVersion: STORE_SCHEMA_VERSION, policy }, null, 2)}\n`;
|
|
126
|
+
try {
|
|
127
|
+
writeFileSync(temporary, value, { encoding: "utf8", mode: 0o600, flag: "wx" });
|
|
128
|
+
renameSync(temporary, configPath);
|
|
129
|
+
}
|
|
130
|
+
finally {
|
|
131
|
+
rmSync(temporary, { force: true });
|
|
132
|
+
}
|
|
144
133
|
}
|
|
145
|
-
|
|
146
134
|
function preserveInvalidStore(configPath) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
135
|
+
try {
|
|
136
|
+
renameSync(configPath, `${configPath}.invalid-${Date.now()}-${randomUUID()}`);
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
if (errorCode(error) === "ENOENT")
|
|
141
|
+
return false;
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
154
144
|
}
|
|
155
|
-
|
|
156
145
|
function resultFor(configPath, action, selection, recoveredInvalidStore = false, responsibilityRoutes) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
146
|
+
const responsibilityBudgets = resolveInPlaceResponsibilityOutputBudgets(selection.policy, responsibilityRoutes);
|
|
147
|
+
return {
|
|
148
|
+
action,
|
|
149
|
+
configPath,
|
|
150
|
+
policy: selection.policy,
|
|
151
|
+
source: selection.source,
|
|
152
|
+
requiresNextTurn: action !== "show",
|
|
153
|
+
...(responsibilityBudgets ? { responsibilityBudgets } : {}),
|
|
154
|
+
...(recoveredInvalidStore ? { recoveredInvalidStore: true } : {}),
|
|
155
|
+
};
|
|
167
156
|
}
|
|
168
|
-
|
|
169
157
|
export function renderOutputPolicyPrompt(policy) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
158
|
+
if (!policy.concise && policy.maxTokens === undefined)
|
|
159
|
+
return "";
|
|
160
|
+
return [
|
|
161
|
+
"## Odai controller output policy",
|
|
162
|
+
...(policy.concise ? [
|
|
163
|
+
"Keep the final user-facing response concise. Include only the result, decisive evidence, unresolved items, and necessary next action; omit routine process narration and repeated context unless the user explicitly asks for detail.",
|
|
164
|
+
] : []),
|
|
165
|
+
...(policy.maxTokens === undefined ? [] : [
|
|
166
|
+
`Each controller model request carries a provider output ceiling request of ${policy.maxTokens} tokens, which may include reasoning. Provider enforcement is not guaranteed; prioritize completion and finish before the requested ceiling.`,
|
|
167
|
+
]),
|
|
168
|
+
"This policy applies only to controller requests and the final user-facing response. It never reduces child-agent, compaction, checkpoint, or other internal context budgets. A user-configured in-place responsibility maxTokens explicitly overrides this ceiling only inside that routed planner, executor, or frontend scope; the runtime records that exception.",
|
|
169
|
+
"The policy changes presentation and the requested controller budget only; it never permits omitting required results, evidence, risks, blockers, or verification.",
|
|
170
|
+
].join("\n");
|
|
182
171
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
172
|
+
function resolveSetPolicy(arguments_) {
|
|
173
|
+
if (arguments_.mode === undefined) {
|
|
174
|
+
if (arguments_.concise === false && arguments_.maxTokens !== undefined) {
|
|
175
|
+
throw new TypeError("maxTokens requires concise=true or mode=economy");
|
|
176
|
+
}
|
|
177
|
+
return resolveOutputPolicy({
|
|
178
|
+
concise: arguments_.concise,
|
|
179
|
+
...(arguments_.maxTokens === undefined ? {} : { maxTokens: arguments_.maxTokens }),
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
if (typeof arguments_.mode !== "string" || !OUTPUT_MODES.has(arguments_.mode)) {
|
|
183
|
+
throw new TypeError("mode must be normal, concise, or economy");
|
|
184
|
+
}
|
|
185
|
+
if (arguments_.concise !== undefined) {
|
|
186
|
+
throw new TypeError("concise must be omitted when mode is supplied");
|
|
187
|
+
}
|
|
188
|
+
if (arguments_.mode !== "economy" && arguments_.maxTokens !== undefined) {
|
|
189
|
+
throw new TypeError("maxTokens is accepted only with economy mode");
|
|
188
190
|
}
|
|
189
191
|
return resolveOutputPolicy({
|
|
190
|
-
|
|
191
|
-
|
|
192
|
+
concise: arguments_.mode !== "normal",
|
|
193
|
+
...(arguments_.mode === "economy"
|
|
194
|
+
? { maxTokens: arguments_.maxTokens ?? DEFAULT_ECONOMY_MAX_TOKENS }
|
|
195
|
+
: {}),
|
|
192
196
|
});
|
|
193
|
-
}
|
|
194
|
-
if (!OUTPUT_MODES.has(args.mode)) {
|
|
195
|
-
throw new TypeError("mode must be normal, concise, or economy");
|
|
196
|
-
}
|
|
197
|
-
if (args.concise !== undefined) {
|
|
198
|
-
throw new TypeError("concise must be omitted when mode is supplied");
|
|
199
|
-
}
|
|
200
|
-
if (args.mode !== "economy" && args.maxTokens !== undefined) {
|
|
201
|
-
throw new TypeError("maxTokens is accepted only with economy mode");
|
|
202
|
-
}
|
|
203
|
-
return resolveOutputPolicy({
|
|
204
|
-
concise: args.mode !== "normal",
|
|
205
|
-
...(args.mode === "economy"
|
|
206
|
-
? { maxTokens: args.maxTokens ?? DEFAULT_ECONOMY_MAX_TOKENS }
|
|
207
|
-
: {}),
|
|
208
|
-
});
|
|
209
197
|
}
|
|
210
|
-
|
|
211
198
|
export function createOutputConfigTool(configPath, options = {}) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
type: "object",
|
|
231
|
-
additionalProperties: false,
|
|
232
|
-
required: ["action"],
|
|
233
|
-
properties: {
|
|
234
|
-
action: {
|
|
235
|
-
type: "string",
|
|
236
|
-
enum: ["show", "set", "remove"],
|
|
237
|
-
},
|
|
238
|
-
mode: {
|
|
239
|
-
type: "string",
|
|
240
|
-
enum: ["normal", "concise", "economy"],
|
|
241
|
-
description: "Preferred for set; economy defaults maxTokens to 500 unless the user supplies another positive value.",
|
|
242
|
-
},
|
|
243
|
-
concise: {
|
|
244
|
-
type: "boolean",
|
|
245
|
-
description: "Backward-compatible alternative for set; omit when mode is supplied.",
|
|
246
|
-
},
|
|
247
|
-
maxTokens: {
|
|
248
|
-
type: "integer",
|
|
249
|
-
description: "Optional positive replacement for economy mode's default 500-token provider request ceiling.",
|
|
250
|
-
},
|
|
251
|
-
},
|
|
252
|
-
},
|
|
253
|
-
output: {
|
|
254
|
-
schema: {
|
|
255
|
-
type: "object",
|
|
256
|
-
additionalProperties: false,
|
|
257
|
-
required: ["action", "configPath", "policy", "source", "requiresNextTurn"],
|
|
258
|
-
properties: {
|
|
259
|
-
action: { type: "string", enum: ["show", "set", "remove"] },
|
|
260
|
-
configPath: { type: "string" },
|
|
261
|
-
policy: {
|
|
199
|
+
const onConfigured = typeof options.onConfigured === "function" ? options.onConfigured : () => { };
|
|
200
|
+
const responsibilityRoutesFor = typeof options.responsibilityRoutesFor === "function" ? options.responsibilityRoutesFor : () => undefined;
|
|
201
|
+
const isChild = typeof options.isChild === "function"
|
|
202
|
+
? options.isChild
|
|
203
|
+
: (agent) => {
|
|
204
|
+
const header = agent?.session?.header;
|
|
205
|
+
return header?.origin === "subagent"
|
|
206
|
+
|| (Number.isSafeInteger(header?.delegationDepth) && (header?.delegationDepth ?? 0) > 0);
|
|
207
|
+
};
|
|
208
|
+
return {
|
|
209
|
+
name: "odai_output_config",
|
|
210
|
+
description: [
|
|
211
|
+
"Inspect, set, or remove the shared Odai controller output policy; the package default is soft concise.",
|
|
212
|
+
"Normal mode is concise=false with no maxTokens; soft concise is concise=true with no maxTokens; economy mode is concise=true with a user-adjustable maxTokens request ceiling and defaults to 500 only when the user names economy without another value.",
|
|
213
|
+
"Use only when the user explicitly requests an output mode or supplies a custom maxTokens; never invent a non-default custom value. Remove restores soft concise.",
|
|
214
|
+
"Changes start next user turn. maxTokens is a provider request ceiling, not locally enforced; providers may include reasoning, exceed or ignore it, or truncate responses. Child-agent, compaction, checkpoint, and other internal budgets stay unchanged.",
|
|
215
|
+
].join(" "),
|
|
216
|
+
parameters: {
|
|
262
217
|
type: "object",
|
|
263
218
|
additionalProperties: false,
|
|
264
|
-
required: ["
|
|
219
|
+
required: ["action"],
|
|
265
220
|
properties: {
|
|
266
|
-
|
|
267
|
-
|
|
221
|
+
action: { type: "string", enum: ["show", "set", "remove"] },
|
|
222
|
+
mode: { type: "string", enum: ["normal", "concise", "economy"], description: "Preferred for set; economy defaults maxTokens to 500 unless the user supplies another positive value." },
|
|
223
|
+
concise: { type: "boolean", description: "Backward-compatible alternative for set; omit when mode is supplied." },
|
|
224
|
+
maxTokens: { type: "integer", description: "Optional positive replacement for economy mode's default 500-token provider request ceiling." },
|
|
268
225
|
},
|
|
269
|
-
},
|
|
270
|
-
source: { type: "string", enum: ["default", "persisted"] },
|
|
271
|
-
requiresNextTurn: { type: "boolean" },
|
|
272
|
-
recoveredInvalidStore: { type: "boolean" },
|
|
273
|
-
responsibilityBudgets: {
|
|
274
|
-
type: "object",
|
|
275
|
-
additionalProperties: false,
|
|
276
|
-
properties: Object.fromEntries(IN_PLACE_OUTPUT_RESPONSIBILITIES.map((responsibility) => [responsibility, {
|
|
277
|
-
type: "object",
|
|
278
|
-
additionalProperties: false,
|
|
279
|
-
required: ["source"],
|
|
280
|
-
properties: {
|
|
281
|
-
source: { type: "string", enum: ["responsibility-override", "controller-policy", "unbounded-by-odai"] },
|
|
282
|
-
maxTokens: { type: "integer" },
|
|
283
|
-
warning: { type: "string", enum: ["responsibility-inherits-controller-ceiling"] },
|
|
284
|
-
},
|
|
285
|
-
}])),
|
|
286
|
-
},
|
|
287
226
|
},
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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
|
-
|
|
358
|
-
|
|
359
|
-
|
|
227
|
+
output: {
|
|
228
|
+
schema: {
|
|
229
|
+
type: "object",
|
|
230
|
+
additionalProperties: false,
|
|
231
|
+
required: ["action", "configPath", "policy", "source", "requiresNextTurn"],
|
|
232
|
+
properties: {
|
|
233
|
+
action: { type: "string", enum: ["show", "set", "remove"] },
|
|
234
|
+
configPath: { type: "string" },
|
|
235
|
+
policy: { type: "object", additionalProperties: false, required: ["concise"], properties: { concise: { type: "boolean" }, maxTokens: { type: "integer" } } },
|
|
236
|
+
source: { type: "string", enum: ["default", "persisted"] },
|
|
237
|
+
requiresNextTurn: { type: "boolean" },
|
|
238
|
+
recoveredInvalidStore: { type: "boolean" },
|
|
239
|
+
responsibilityBudgets: {
|
|
240
|
+
type: "object",
|
|
241
|
+
additionalProperties: false,
|
|
242
|
+
properties: Object.fromEntries(IN_PLACE_OUTPUT_RESPONSIBILITIES.map((responsibility) => [responsibility, {
|
|
243
|
+
type: "object",
|
|
244
|
+
additionalProperties: false,
|
|
245
|
+
required: ["source"],
|
|
246
|
+
properties: {
|
|
247
|
+
source: { type: "string", enum: ["responsibility-override", "controller-policy", "unbounded-by-odai"] },
|
|
248
|
+
maxTokens: { type: "integer" },
|
|
249
|
+
warning: { type: "string", enum: ["responsibility-inherits-controller-ceiling"] },
|
|
250
|
+
},
|
|
251
|
+
}])),
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
render(_arguments, value) {
|
|
256
|
+
const settings = [
|
|
257
|
+
`concise=${value.policy.concise ? "on" : "off"}`,
|
|
258
|
+
...(value.policy.maxTokens === undefined ? [] : [`maxTokens=${value.policy.maxTokens}`]),
|
|
259
|
+
].join(", ");
|
|
260
|
+
const responsibilityBudgets = Object.entries(value.responsibilityBudgets ?? {});
|
|
261
|
+
const configuredResponsibilities = responsibilityBudgets.map(([responsibility, budget]) => (`${responsibility}=${budget.maxTokens === undefined ? "no Odai maxTokens" : `maxTokens=${budget.maxTokens}`} [${budget.source}]`));
|
|
262
|
+
const inheritedWarnings = responsibilityBudgets
|
|
263
|
+
.filter(([, budget]) => budget.warning === "responsibility-inherits-controller-ceiling")
|
|
264
|
+
.map(([responsibility]) => ` Warning: ${responsibility} has no explicit maxTokens and inherits the controller ceiling when routed in-place; providers may count reasoning and truncate substantial work.`);
|
|
265
|
+
return [{
|
|
266
|
+
type: "text",
|
|
267
|
+
text: [
|
|
268
|
+
`${value.action === "show" ? "Current" : "Updated"} Odai controller output policy (${value.source}): ${settings}.`,
|
|
269
|
+
...(value.policy.maxTokens === undefined ? [] : [" maxTokens is sent as a provider request ceiling; strict provider compliance is not guaranteed and must be checked from usage."]),
|
|
270
|
+
...(configuredResponsibilities.length > 0 ? [` In-place responsibility ceilings: ${configuredResponsibilities.join("; ")}.`] : []),
|
|
271
|
+
...inheritedWarnings,
|
|
272
|
+
...(value.recoveredInvalidStore ? [" An invalid prior store was preserved and replaced."] : []),
|
|
273
|
+
...(value.requiresNextTurn ? [" The change applies from the next user turn."] : []),
|
|
274
|
+
].join(""),
|
|
275
|
+
}];
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
execute(arguments_, execution) {
|
|
279
|
+
if (!execution.agent)
|
|
280
|
+
throw new Error("odai_output_config requires an owning agent session");
|
|
281
|
+
if (isChild(execution.agent))
|
|
282
|
+
throw new Error("child agents may not change Odai output configuration");
|
|
283
|
+
if (!isUnknownRecord(arguments_))
|
|
284
|
+
throw new TypeError("arguments must be an object");
|
|
285
|
+
const unknownFields = Object.keys(arguments_).filter((field) => !["action", "mode", "concise", "maxTokens"].includes(field));
|
|
286
|
+
if (unknownFields.length > 0)
|
|
287
|
+
throw new TypeError(`unknown arguments: ${unknownFields.join(", ")}`);
|
|
288
|
+
const action = arguments_.action;
|
|
289
|
+
if (action !== "show" && action !== "set" && action !== "remove")
|
|
290
|
+
throw new TypeError("action must be show, set, or remove");
|
|
291
|
+
if (action === "show") {
|
|
292
|
+
if (arguments_.mode !== undefined || arguments_.concise !== undefined || arguments_.maxTokens !== undefined) {
|
|
293
|
+
throw new TypeError("mode, concise, and maxTokens must be omitted for show");
|
|
294
|
+
}
|
|
295
|
+
return Promise.resolve(resultFor(configPath, "show", effectiveOutputPolicy(configPath), false, responsibilityRoutesFor()));
|
|
296
|
+
}
|
|
297
|
+
if (action === "remove"
|
|
298
|
+
&& (arguments_.mode !== undefined || arguments_.concise !== undefined || arguments_.maxTokens !== undefined)) {
|
|
299
|
+
throw new TypeError("mode, concise, and maxTokens must be omitted for remove");
|
|
300
|
+
}
|
|
301
|
+
const proposed = action === "set" ? resolveSetPolicy(arguments_) : undefined;
|
|
302
|
+
const releaseLock = acquireOwnedStoreLock(configPath, "Odai output configuration");
|
|
303
|
+
try {
|
|
304
|
+
let recoveredInvalidStore = false;
|
|
305
|
+
try {
|
|
306
|
+
readOutputPolicyStore(configPath);
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
if (!(error instanceof OutputPolicyStoreValidationError)) {
|
|
310
|
+
throw new Error("Odai output configuration could not be read safely; no changes were made", { cause: error });
|
|
311
|
+
}
|
|
312
|
+
recoveredInvalidStore = preserveInvalidStore(configPath);
|
|
313
|
+
}
|
|
314
|
+
if (action === "set" && proposed)
|
|
315
|
+
writeOutputPolicyStore(configPath, proposed);
|
|
316
|
+
else
|
|
317
|
+
rmSync(configPath, { force: true });
|
|
318
|
+
const selection = action === "set" && proposed
|
|
319
|
+
? Object.freeze({ policy: proposed, source: "persisted" })
|
|
320
|
+
: Object.freeze({ policy: DEFAULT_OUTPUT_POLICY, source: "default" });
|
|
321
|
+
onConfigured(execution.agent, {
|
|
322
|
+
action,
|
|
323
|
+
policy: selection.policy,
|
|
324
|
+
...(recoveredInvalidStore ? { recoveredInvalidStore: true } : {}),
|
|
325
|
+
});
|
|
326
|
+
return Promise.resolve(resultFor(configPath, action, selection, recoveredInvalidStore, responsibilityRoutesFor()));
|
|
327
|
+
}
|
|
328
|
+
finally {
|
|
329
|
+
releaseLock();
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
};
|
|
360
333
|
}
|
|
334
|
+
//# sourceMappingURL=output-config.mjs.map
|