odai-dsh-agent 0.2.8 → 0.2.9
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/LICENSE +21 -21
- package/README.md +140 -140
- package/build/bin/odai-dsh-agent.mjs +12 -12
- package/build/src/installer.d.mts.map +1 -1
- package/build/src/installer.mjs +3 -26
- package/build/src/installer.mjs.map +1 -1
- package/package.json +70 -70
- package/preset/odai/agent.cordis.yml +261 -261
- package/preset/odai/preset.yml +3 -3
- package/preset/odai/runtime/lifecycle-runtime.d.mts.map +1 -1
- package/preset/odai/runtime/lifecycle-runtime.mjs +21 -5
- package/preset/odai/runtime/lifecycle-runtime.mjs.map +1 -1
- package/preset/odai/runtime/prompt-runtime.d.mts.map +1 -1
- package/preset/odai/runtime/prompt-runtime.mjs +14 -1
- package/preset/odai/runtime/prompt-runtime.mjs.map +1 -1
- package/preset/odai/runtime/role-overlays.mjs +11 -11
- package/preset/odai/runtime/role-overlays.mjs.map +1 -1
- package/preset/odai/runtime/router.d.mts.map +1 -1
- package/preset/odai/runtime/router.mjs +1 -0
- package/preset/odai/runtime/router.mjs.map +1 -1
- package/preset/odai/runtime/routing-context.d.mts +4 -0
- package/preset/odai/runtime/routing-context.d.mts.map +1 -1
- package/preset/odai/runtime/routing-context.mjs +97 -19
- package/preset/odai/runtime/routing-context.mjs.map +1 -1
- package/preset/odai/skills/odai/agents/openai.yaml +4 -4
- package/preset/odai/skills/odai/assets/claude-agents/agent.md +11 -11
- package/preset/odai/skills/odai/assets/codex-agents/config.toml +11 -11
- package/preset/odai/skills/odai/assets/codex-agents/role.toml +5 -5
- package/preset/odai/skills/odai/assets/copilot-agents/agent.md +13 -13
- package/preset/odai/skills/odai/assets/hooks-policy.example.json +24 -24
- package/preset/odai/skills/odai/assets/routing-roles/controller.md +15 -15
- package/preset/odai/skills/odai/assets/routing-roles/executor.md +11 -11
- package/preset/odai/skills/odai/assets/routing-roles/frontend.md +7 -7
- package/preset/odai/skills/odai/assets/routing-roles/planner.md +29 -29
- package/preset/odai/skills/odai/assets/routing-roles/researcher.md +6 -6
- package/preset/odai/skills/odai/assets/routing-roles/reviewer.md +11 -11
- package/preset/odai/skills/odai/assets/task-state.md +14 -14
- package/preset/odai/skills/odai/manifest.json +37 -37
- package/preset/odai/skills/odai/references/craft.md +54 -54
- package/preset/odai/skills/odai/references/dao.md +30 -30
- package/preset/odai/skills/odai/references/leverage.md +53 -53
- package/preset/odai/skills/odai/references/planning.md +82 -82
- package/preset/odai/skills/odai/references/support.md +36 -36
- package/preset/odai/skills/odai/references/verification.md +23 -23
- package/preset/odai/skills/odai/scripts/build-hooks.mjs +273 -273
- package/preset/odai/skills/odai/scripts/build-routing.mjs +274 -274
- package/preset/odai/skills/odai/scripts/install-routing.mjs +397 -397
- package/preset/odai/skills/odai/scripts/odai-hook.mjs +457 -457
- package/preset/odai/skills/odai/scripts/run-role.mjs +336 -336
- package/preset/odai/skills/odai/scripts/run-routing.mjs +1154 -1154
- package/preset/odai/skills/odai/scripts/verify-routing.mjs +103 -103
|
@@ -1,103 +1,103 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
4
|
-
import { homedir } from "node:os";
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
import process from "node:process";
|
|
7
|
-
import { createHash } from "node:crypto";
|
|
8
|
-
import { fileURLToPath } from "node:url";
|
|
9
|
-
|
|
10
|
-
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
-
const args = parseArgs(process.argv.slice(2));
|
|
12
|
-
const mappings = new Map([["odai_researcher", "researcher"], ["odai_planner", "planner"], ["odai_executor", "executor"], ["odai_reviewer", "reviewer"], ["odai_frontend", "frontend"]]);
|
|
13
|
-
|
|
14
|
-
if (args.help) {
|
|
15
|
-
console.log(`Usage:
|
|
16
|
-
node odai-verify-routing.mjs --host codex --project <directory> \\
|
|
17
|
-
--role <odai_researcher|odai_planner|odai_executor|odai_reviewer|odai_frontend> --after <ISO timestamp> \\
|
|
18
|
-
[--agent-path </root/task>] [--sessions <directory>]
|
|
19
|
-
|
|
20
|
-
只读取近期 Codex rollout metadata,把实际角色、模型与推理档和 odai 托管清单对账。`);
|
|
21
|
-
process.exit(0);
|
|
22
|
-
}
|
|
23
|
-
if (args.host !== "codex") fail(`不支持的 host:${args.host || "(missing)"}`);
|
|
24
|
-
if (!mappings.has(args.role)) fail("--role 必须是 odai_researcher、odai_planner、odai_executor、odai_reviewer 或 odai_frontend");
|
|
25
|
-
if (!args.project || !args.after) fail("缺少 --project 或 --after");
|
|
26
|
-
const after = Date.parse(args.after);
|
|
27
|
-
if (!Number.isFinite(after)) fail(`无效的 --after:${args.after}`);
|
|
28
|
-
|
|
29
|
-
const project = path.resolve(args.project);
|
|
30
|
-
const manifestFile = path.join(scriptDir, "odai-routing.json");
|
|
31
|
-
if (!existsSync(manifestFile)) fail(`缺少路由清单:${manifestFile}`);
|
|
32
|
-
const manifest = JSON.parse(readFileSync(manifestFile, "utf8"));
|
|
33
|
-
if (manifest.id !== "odai-routing-installation" || manifest.host !== "codex") fail("路由清单无效");
|
|
34
|
-
for (const [relative, expected] of Object.entries(manifest.files || {})) {
|
|
35
|
-
const target = path.join(scriptDir, relative);
|
|
36
|
-
if (!existsSync(target) || sha256(readFileSync(target)) !== expected) fail(`路由托管文件缺失或已变化:${relative}`);
|
|
37
|
-
}
|
|
38
|
-
const expected = manifest.mapping?.[mappings.get(args.role)];
|
|
39
|
-
if (!expected?.model) fail("路由清单缺少角色模型映射");
|
|
40
|
-
|
|
41
|
-
const sessions = path.resolve(args.sessions || path.join(process.env.CODEX_HOME || path.join(homedir(), ".codex"), "sessions"));
|
|
42
|
-
if (!existsSync(sessions)) fail(`Codex sessions 不存在:${sessions}`);
|
|
43
|
-
const candidates = [];
|
|
44
|
-
for (const file of recentJsonl(sessions, after - 5000)) {
|
|
45
|
-
const metadata = readMetadata(file);
|
|
46
|
-
const session = metadata.session;
|
|
47
|
-
const source = session?.source?.subagent?.thread_spawn;
|
|
48
|
-
if (!source || session.agent_role !== args.role || path.resolve(session.cwd || "") !== project) continue;
|
|
49
|
-
const started = Date.parse(session.timestamp || "");
|
|
50
|
-
if (!Number.isFinite(started) || started < after) continue;
|
|
51
|
-
if (args.agentPath && source.agent_path !== args.agentPath) continue;
|
|
52
|
-
if (metadata.context) candidates.push({ session, context: metadata.context, source });
|
|
53
|
-
}
|
|
54
|
-
if (candidates.length !== 1) fail(candidates.length ? `找到 ${candidates.length} 个候选子会话;请用 --agent-path 消除歧义` : "未找到满足边界的子会话运行时记录");
|
|
55
|
-
const match = candidates[0];
|
|
56
|
-
const actualModel = match.context.model;
|
|
57
|
-
const actualEffort = match.context.effort || match.context.collaboration_mode?.settings?.reasoning_effort || null;
|
|
58
|
-
if (actualModel !== expected.model) fail(`模型不匹配:期望 ${expected.model},实际 ${actualModel || "(missing)"}`);
|
|
59
|
-
if (expected.reasoning_effort && actualEffort !== expected.reasoning_effort) fail(`推理档不匹配:期望 ${expected.reasoning_effort},实际 ${actualEffort || "(missing)"}`);
|
|
60
|
-
|
|
61
|
-
process.stdout.write(`${JSON.stringify({
|
|
62
|
-
verified: true, role: match.session.agent_role, model: actualModel, reasoning_effort: actualEffort,
|
|
63
|
-
agent_path: match.source.agent_path || null, child_thread_id: match.session.id,
|
|
64
|
-
parent_thread_id: match.session.parent_thread_id || null, project,
|
|
65
|
-
}, null, 2)}\n`);
|
|
66
|
-
|
|
67
|
-
function recentJsonl(root, threshold) {
|
|
68
|
-
const files = [];
|
|
69
|
-
const pending = [root];
|
|
70
|
-
while (pending.length) {
|
|
71
|
-
const directory = pending.pop();
|
|
72
|
-
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
73
|
-
const full = path.join(directory, entry.name);
|
|
74
|
-
if (entry.isDirectory()) pending.push(full);
|
|
75
|
-
else if (entry.isFile() && entry.name.endsWith(".jsonl") && statSync(full).mtimeMs >= threshold) files.push(full);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return files;
|
|
79
|
-
}
|
|
80
|
-
function readMetadata(file) {
|
|
81
|
-
let session = null; let context = null;
|
|
82
|
-
for (const line of readFileSync(file, "utf8").split("\n")) {
|
|
83
|
-
if (!line) continue;
|
|
84
|
-
let event; try { event = JSON.parse(line); } catch { continue; }
|
|
85
|
-
if (event.type === "session_meta") session = event.payload;
|
|
86
|
-
if (event.type === "turn_context") context = event.payload;
|
|
87
|
-
if (session && context) break;
|
|
88
|
-
}
|
|
89
|
-
return { session, context };
|
|
90
|
-
}
|
|
91
|
-
function parseArgs(values) {
|
|
92
|
-
const result = { host: "", project: "", role: "", after: "", agentPath: "", sessions: "", help: false };
|
|
93
|
-
const fields = new Map([["--host", "host"], ["--project", "project"], ["--role", "role"], ["--after", "after"], ["--agent-path", "agentPath"], ["--sessions", "sessions"]]);
|
|
94
|
-
for (let index = 0; index < values.length; index += 1) {
|
|
95
|
-
const value = values[index];
|
|
96
|
-
if (value === "--help" || value === "-h") result.help = true;
|
|
97
|
-
else if (fields.has(value)) { const next = values[++index] || ""; if (!next || next.startsWith("--")) fail(`${value} 需要一个值`); result[fields.get(value)] = next; }
|
|
98
|
-
else fail(`未知参数:${value}`);
|
|
99
|
-
}
|
|
100
|
-
return result;
|
|
101
|
-
}
|
|
102
|
-
function sha256(value) { return createHash("sha256").update(value).digest("hex"); }
|
|
103
|
-
function fail(message) { console.error(message); process.exit(1); }
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import process from "node:process";
|
|
7
|
+
import { createHash } from "node:crypto";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
|
|
10
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const args = parseArgs(process.argv.slice(2));
|
|
12
|
+
const mappings = new Map([["odai_researcher", "researcher"], ["odai_planner", "planner"], ["odai_executor", "executor"], ["odai_reviewer", "reviewer"], ["odai_frontend", "frontend"]]);
|
|
13
|
+
|
|
14
|
+
if (args.help) {
|
|
15
|
+
console.log(`Usage:
|
|
16
|
+
node odai-verify-routing.mjs --host codex --project <directory> \\
|
|
17
|
+
--role <odai_researcher|odai_planner|odai_executor|odai_reviewer|odai_frontend> --after <ISO timestamp> \\
|
|
18
|
+
[--agent-path </root/task>] [--sessions <directory>]
|
|
19
|
+
|
|
20
|
+
只读取近期 Codex rollout metadata,把实际角色、模型与推理档和 odai 托管清单对账。`);
|
|
21
|
+
process.exit(0);
|
|
22
|
+
}
|
|
23
|
+
if (args.host !== "codex") fail(`不支持的 host:${args.host || "(missing)"}`);
|
|
24
|
+
if (!mappings.has(args.role)) fail("--role 必须是 odai_researcher、odai_planner、odai_executor、odai_reviewer 或 odai_frontend");
|
|
25
|
+
if (!args.project || !args.after) fail("缺少 --project 或 --after");
|
|
26
|
+
const after = Date.parse(args.after);
|
|
27
|
+
if (!Number.isFinite(after)) fail(`无效的 --after:${args.after}`);
|
|
28
|
+
|
|
29
|
+
const project = path.resolve(args.project);
|
|
30
|
+
const manifestFile = path.join(scriptDir, "odai-routing.json");
|
|
31
|
+
if (!existsSync(manifestFile)) fail(`缺少路由清单:${manifestFile}`);
|
|
32
|
+
const manifest = JSON.parse(readFileSync(manifestFile, "utf8"));
|
|
33
|
+
if (manifest.id !== "odai-routing-installation" || manifest.host !== "codex") fail("路由清单无效");
|
|
34
|
+
for (const [relative, expected] of Object.entries(manifest.files || {})) {
|
|
35
|
+
const target = path.join(scriptDir, relative);
|
|
36
|
+
if (!existsSync(target) || sha256(readFileSync(target)) !== expected) fail(`路由托管文件缺失或已变化:${relative}`);
|
|
37
|
+
}
|
|
38
|
+
const expected = manifest.mapping?.[mappings.get(args.role)];
|
|
39
|
+
if (!expected?.model) fail("路由清单缺少角色模型映射");
|
|
40
|
+
|
|
41
|
+
const sessions = path.resolve(args.sessions || path.join(process.env.CODEX_HOME || path.join(homedir(), ".codex"), "sessions"));
|
|
42
|
+
if (!existsSync(sessions)) fail(`Codex sessions 不存在:${sessions}`);
|
|
43
|
+
const candidates = [];
|
|
44
|
+
for (const file of recentJsonl(sessions, after - 5000)) {
|
|
45
|
+
const metadata = readMetadata(file);
|
|
46
|
+
const session = metadata.session;
|
|
47
|
+
const source = session?.source?.subagent?.thread_spawn;
|
|
48
|
+
if (!source || session.agent_role !== args.role || path.resolve(session.cwd || "") !== project) continue;
|
|
49
|
+
const started = Date.parse(session.timestamp || "");
|
|
50
|
+
if (!Number.isFinite(started) || started < after) continue;
|
|
51
|
+
if (args.agentPath && source.agent_path !== args.agentPath) continue;
|
|
52
|
+
if (metadata.context) candidates.push({ session, context: metadata.context, source });
|
|
53
|
+
}
|
|
54
|
+
if (candidates.length !== 1) fail(candidates.length ? `找到 ${candidates.length} 个候选子会话;请用 --agent-path 消除歧义` : "未找到满足边界的子会话运行时记录");
|
|
55
|
+
const match = candidates[0];
|
|
56
|
+
const actualModel = match.context.model;
|
|
57
|
+
const actualEffort = match.context.effort || match.context.collaboration_mode?.settings?.reasoning_effort || null;
|
|
58
|
+
if (actualModel !== expected.model) fail(`模型不匹配:期望 ${expected.model},实际 ${actualModel || "(missing)"}`);
|
|
59
|
+
if (expected.reasoning_effort && actualEffort !== expected.reasoning_effort) fail(`推理档不匹配:期望 ${expected.reasoning_effort},实际 ${actualEffort || "(missing)"}`);
|
|
60
|
+
|
|
61
|
+
process.stdout.write(`${JSON.stringify({
|
|
62
|
+
verified: true, role: match.session.agent_role, model: actualModel, reasoning_effort: actualEffort,
|
|
63
|
+
agent_path: match.source.agent_path || null, child_thread_id: match.session.id,
|
|
64
|
+
parent_thread_id: match.session.parent_thread_id || null, project,
|
|
65
|
+
}, null, 2)}\n`);
|
|
66
|
+
|
|
67
|
+
function recentJsonl(root, threshold) {
|
|
68
|
+
const files = [];
|
|
69
|
+
const pending = [root];
|
|
70
|
+
while (pending.length) {
|
|
71
|
+
const directory = pending.pop();
|
|
72
|
+
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
73
|
+
const full = path.join(directory, entry.name);
|
|
74
|
+
if (entry.isDirectory()) pending.push(full);
|
|
75
|
+
else if (entry.isFile() && entry.name.endsWith(".jsonl") && statSync(full).mtimeMs >= threshold) files.push(full);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return files;
|
|
79
|
+
}
|
|
80
|
+
function readMetadata(file) {
|
|
81
|
+
let session = null; let context = null;
|
|
82
|
+
for (const line of readFileSync(file, "utf8").split("\n")) {
|
|
83
|
+
if (!line) continue;
|
|
84
|
+
let event; try { event = JSON.parse(line); } catch { continue; }
|
|
85
|
+
if (event.type === "session_meta") session = event.payload;
|
|
86
|
+
if (event.type === "turn_context") context = event.payload;
|
|
87
|
+
if (session && context) break;
|
|
88
|
+
}
|
|
89
|
+
return { session, context };
|
|
90
|
+
}
|
|
91
|
+
function parseArgs(values) {
|
|
92
|
+
const result = { host: "", project: "", role: "", after: "", agentPath: "", sessions: "", help: false };
|
|
93
|
+
const fields = new Map([["--host", "host"], ["--project", "project"], ["--role", "role"], ["--after", "after"], ["--agent-path", "agentPath"], ["--sessions", "sessions"]]);
|
|
94
|
+
for (let index = 0; index < values.length; index += 1) {
|
|
95
|
+
const value = values[index];
|
|
96
|
+
if (value === "--help" || value === "-h") result.help = true;
|
|
97
|
+
else if (fields.has(value)) { const next = values[++index] || ""; if (!next || next.startsWith("--")) fail(`${value} 需要一个值`); result[fields.get(value)] = next; }
|
|
98
|
+
else fail(`未知参数:${value}`);
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
function sha256(value) { return createHash("sha256").update(value).digest("hex"); }
|
|
103
|
+
function fail(message) { console.error(message); process.exit(1); }
|