@agentrouter-top/relay-dsh-plugin-codex 0.2.2-agentrouter.1
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/AGENTROUTER-FORK.md +7 -0
- package/LICENSE +21 -0
- package/README.md +419 -0
- package/README.zh.md +429 -0
- package/codex-activity-wire.mjs +16 -0
- package/cordis.patch.yml +5 -0
- package/docs/dsh-0.1.2-alpha.3.md +37 -0
- package/docs/images/dsh-new-session-backends.jpg +0 -0
- package/docs/reliability-acceptance.md +85 -0
- package/docs/reliability-spec.md +305 -0
- package/docs/spec/dsh-interaction-bridge.md +168 -0
- package/docs/spec/execution-presentation.md +103 -0
- package/dsh-client-compat.d.mts +4 -0
- package/dsh-client-compat.mjs +26 -0
- package/dsh-compat.mjs +92 -0
- package/lib/client.js +37202 -0
- package/lib/client.js.map +1 -0
- package/lib/host-plugin.js +5142 -0
- package/lib/host-plugin.js.map +1 -0
- package/lib/typert.host.js +16 -0
- package/lib/typert.host.js.map +1 -0
- package/package.json +157 -0
- package/presets/relay-codex/.relay-managed +1 -0
- package/presets/relay-codex/agent.cordis.yml +3 -0
- package/presets/relay-codex/preset.yml +3 -0
- package/scripts/repair-activity-history.mjs +123 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region typert.host.js
|
|
2
|
+
const TYPERT = {
|
|
3
|
+
package: "@agentrouter-top/relay-dsh-plugin-codex",
|
|
4
|
+
face: "host",
|
|
5
|
+
schemas: [],
|
|
6
|
+
invocations: [],
|
|
7
|
+
model: {
|
|
8
|
+
services: [],
|
|
9
|
+
events: [],
|
|
10
|
+
objects: []
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
//#endregion
|
|
14
|
+
export { TYPERT };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=typert.host.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typert.host.js","names":[],"sources":["../typert.host.js"],"sourcesContent":["export const TYPERT = { package: \"@agentrouter-top/relay-dsh-plugin-codex\", face: \"host\", schemas: [], invocations: [], model: { services: [], events: [], objects: [] } };\n"],"mappings":";AAAA,MAAa,SAAS;CAAE,SAAS;CAA2C,MAAM;CAAQ,SAAS,CAAC;CAAG,aAAa,CAAC;CAAG,OAAO;EAAE,UAAU,CAAC;EAAG,QAAQ,CAAC;EAAG,SAAS,CAAC;CAAE;AAAE"}
|
package/package.json
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agentrouter-top/relay-dsh-plugin-codex",
|
|
3
|
+
"version": "0.2.2-agentrouter.1",
|
|
4
|
+
"description": "Codex conversation backend for DeepSeek Harness, powered by the Codex App Server with approvals and DSH tool support. AgentRouter fork: generic child-process launch configuration and pinned Codex runtime.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"dsh-plugin",
|
|
7
|
+
"deepseek-harness",
|
|
8
|
+
"dsh"
|
|
9
|
+
],
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "lib/host-plugin.js",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/Maybank01/agentrouter-dsh-plugins.git",
|
|
16
|
+
"directory": "upstream/relay-codex"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://github.com/Maybank01/agentrouter-dsh-plugins",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/Maybank01/agentrouter-dsh-plugins/issues"
|
|
21
|
+
},
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=22.13"
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public",
|
|
27
|
+
"tag": "next",
|
|
28
|
+
"registry": "https://registry.npmjs.org"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsdown --config tsdown.config.ts",
|
|
32
|
+
"postbuild": "node normalize-build.mjs",
|
|
33
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
34
|
+
"test:unit": "node --test test/*.test.mjs",
|
|
35
|
+
"test:component": "vitest run test/*.spec.tsx",
|
|
36
|
+
"test": "npm run test:unit && npm run test:component",
|
|
37
|
+
"verify": "npm run typecheck && npm test && npm run build"
|
|
38
|
+
},
|
|
39
|
+
"exports": {
|
|
40
|
+
".": "./lib/host-plugin.js",
|
|
41
|
+
"./client": "./lib/client.js",
|
|
42
|
+
"./typert": "./lib/typert.host.js",
|
|
43
|
+
"./package.json": "./package.json"
|
|
44
|
+
},
|
|
45
|
+
"files": [
|
|
46
|
+
"README.zh.md",
|
|
47
|
+
"docs/images",
|
|
48
|
+
"docs/reliability-spec.md",
|
|
49
|
+
"docs/reliability-acceptance.md",
|
|
50
|
+
"docs/spec/dsh-interaction-bridge.md",
|
|
51
|
+
"docs/spec/execution-presentation.md",
|
|
52
|
+
"scripts/repair-activity-history.mjs",
|
|
53
|
+
"codex-activity-wire.mjs",
|
|
54
|
+
"cordis.patch.yml",
|
|
55
|
+
"presets",
|
|
56
|
+
"lib/client.js",
|
|
57
|
+
"lib/client.js.map",
|
|
58
|
+
"lib/host-plugin.js",
|
|
59
|
+
"lib/host-plugin.js.map",
|
|
60
|
+
"lib/typert.host.js",
|
|
61
|
+
"lib/typert.host.js.map",
|
|
62
|
+
"docs/dsh-0.1.2-alpha.3.md",
|
|
63
|
+
"dsh-compat.mjs",
|
|
64
|
+
"dsh-client-compat.mjs",
|
|
65
|
+
"dsh-client-compat.d.mts",
|
|
66
|
+
"AGENTROUTER-FORK.md"
|
|
67
|
+
],
|
|
68
|
+
"dsh": {
|
|
69
|
+
"client": {
|
|
70
|
+
"inject": [
|
|
71
|
+
"@deepseek-ai/dsh-client-connection",
|
|
72
|
+
"@deepseek-ai/dsh-client-locale",
|
|
73
|
+
"@deepseek-ai/dsh-client-ui-conversation",
|
|
74
|
+
"@deepseek-ai/dsh-client-ui-tool",
|
|
75
|
+
"@deepseek-ai/dsh-client-ui-primitives",
|
|
76
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
77
|
+
"@deepseek-ai/dsh-client-ui-sidebar",
|
|
78
|
+
"@deepseek-ai/dsh-client-ui-slots",
|
|
79
|
+
"@deepseek-ai/dsh-client-ui-theme",
|
|
80
|
+
"relay-dsh-plugin-session-import",
|
|
81
|
+
"@deepseek-ai/dsh-client-ui-renderer",
|
|
82
|
+
"@deepseek-ai/dsh-client-ui-session",
|
|
83
|
+
"@deepseek-ai/dsh-api-session-controller",
|
|
84
|
+
"@deepseek-ai/dsh-api-workspace-controller",
|
|
85
|
+
"@deepseek-ai/dsh-client-ui-chat",
|
|
86
|
+
"@deepseek-ai/dsh-client-ui-model-selection"
|
|
87
|
+
],
|
|
88
|
+
"platform": "web"
|
|
89
|
+
},
|
|
90
|
+
"bundle": {
|
|
91
|
+
"patch": "./cordis.patch.yml"
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
"peerDependencies": {
|
|
95
|
+
"@deepseek-ai/dsh-llm": ">=0.1.1-rc.2 <0.1.2-0 || >=0.1.2-alpha.2 <0.1.3-0",
|
|
96
|
+
"@deepseek-ai/dsh-session": ">=0.1.1-rc.2 <0.1.2-0 || >=0.1.2-alpha.2 <0.1.3-0",
|
|
97
|
+
"@deepseek-ai/dsh-typert-protocol": ">=0.1.1-rc.2 <0.1.2-0 || >=0.1.2-alpha.2 <0.1.3-0",
|
|
98
|
+
"react": "^18.2.0"
|
|
99
|
+
},
|
|
100
|
+
"peerDependenciesMeta": {
|
|
101
|
+
"@deepseek-ai/dsh-llm": {
|
|
102
|
+
"optional": true
|
|
103
|
+
},
|
|
104
|
+
"@deepseek-ai/dsh-session": {
|
|
105
|
+
"optional": true
|
|
106
|
+
},
|
|
107
|
+
"@deepseek-ai/dsh-typert-protocol": {
|
|
108
|
+
"optional": true
|
|
109
|
+
},
|
|
110
|
+
"react": {
|
|
111
|
+
"optional": true
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"dependencies": {
|
|
115
|
+
"@openai/codex": "0.153.4",
|
|
116
|
+
"lucide-react": "^1.37.0",
|
|
117
|
+
"relay-dsh-plugin-session-import": "0.2.2"
|
|
118
|
+
},
|
|
119
|
+
"devDependencies": {
|
|
120
|
+
"@testing-library/dom": "^10.4.1",
|
|
121
|
+
"@testing-library/react": "^16.3.2",
|
|
122
|
+
"@types/react": "~18.3.1",
|
|
123
|
+
"@types/react-dom": "^18.3.7",
|
|
124
|
+
"jsdom": "^29.1.1",
|
|
125
|
+
"lightningcss": "1.32.0",
|
|
126
|
+
"react": "^18.2.0",
|
|
127
|
+
"react-dom": "^18.3.1",
|
|
128
|
+
"tsdown": "0.22.2",
|
|
129
|
+
"typescript": "6.0.3",
|
|
130
|
+
"vitest": "^4.1.8",
|
|
131
|
+
"@deepseek-ai/cordis": "4.0.2",
|
|
132
|
+
"@deepseek-ai/dsh-llm": "0.1.2-rc.1",
|
|
133
|
+
"@deepseek-ai/dsh-session": "0.1.2-rc.1",
|
|
134
|
+
"@deepseek-ai/dsh-typert-protocol": "0.1.2-rc.1",
|
|
135
|
+
"@deepseek-ai/dsh-client-connection": "0.1.2-rc.1",
|
|
136
|
+
"@deepseek-ai/dsh-client-locale": "0.1.2-rc.1",
|
|
137
|
+
"@deepseek-ai/dsh-client-ui-conversation": "0.1.2-rc.1",
|
|
138
|
+
"@deepseek-ai/dsh-client-ui-tool": "0.1.2-rc.1",
|
|
139
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.1.2-rc.1",
|
|
140
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.1.2-rc.1",
|
|
141
|
+
"@deepseek-ai/dsh-client-ui-sidebar": "0.1.2-rc.1",
|
|
142
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.1.2-rc.1",
|
|
143
|
+
"@deepseek-ai/dsh-client-ui-theme": "0.1.2-rc.1",
|
|
144
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.1.2-rc.1",
|
|
145
|
+
"@deepseek-ai/dsh-client-ui-session": "0.1.2-rc.1",
|
|
146
|
+
"@deepseek-ai/dsh-api-session-controller": "0.1.2-rc.1",
|
|
147
|
+
"@deepseek-ai/dsh-api-workspace-controller": "0.1.2-rc.1",
|
|
148
|
+
"@deepseek-ai/dsh-client-ui-chat": "0.1.2-rc.1",
|
|
149
|
+
"@deepseek-ai/dsh-client-ui-model-selection": "0.1.2-rc.1"
|
|
150
|
+
},
|
|
151
|
+
"agentrouter": {
|
|
152
|
+
"upstream": "https://github.com/yangbobo2021/relay-dsh-plugin-codex.git",
|
|
153
|
+
"commit": "75918b3e06880df25375154321109f364124a544",
|
|
154
|
+
"launchConfigVersion": 1,
|
|
155
|
+
"codexRuntime": "0.153.4"
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Managed by Relay's DSH integration.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { copyFile, lstat, open, readFile, readdir, rename, rm } from "node:fs/promises";
|
|
4
|
+
import { dirname, join, resolve } from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
import * as zlib from "node:zlib";
|
|
7
|
+
import { readActivityPayload } from "../codex-activity-wire.mjs";
|
|
8
|
+
|
|
9
|
+
const LEGACY_TYPE = "relay-codex/activity";
|
|
10
|
+
const hash = bytes => createHash("sha256").update(bytes).digest("hex");
|
|
11
|
+
|
|
12
|
+
function decodeFrames(bytes) {
|
|
13
|
+
const frames = [];
|
|
14
|
+
let offset = 0;
|
|
15
|
+
while (offset < bytes.length) {
|
|
16
|
+
const decoded = zlib.zstdDecompressSync(bytes.subarray(offset), { info: true });
|
|
17
|
+
const consumed = decoded.engine.bytesWritten;
|
|
18
|
+
if (consumed <= 0) throw new Error("Invalid zstd frame length");
|
|
19
|
+
frames.push(decoded.buffer);
|
|
20
|
+
offset += consumed;
|
|
21
|
+
}
|
|
22
|
+
return Buffer.concat(frames);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Stop DSH before applying: the file hash check detects changes, not a live writer lock.
|
|
26
|
+
export async function repairActivityHistory(path, { write = false } = {}) {
|
|
27
|
+
const info = await lstat(path);
|
|
28
|
+
if (!info.isFile()) throw new Error("Expected a regular session log, not a symlink");
|
|
29
|
+
const compressed = path.endsWith(".jsonl.zstd");
|
|
30
|
+
if (!compressed && !path.endsWith(".jsonl")) throw new Error("Unsupported log suffix");
|
|
31
|
+
if (compressed && !zlib.zstdDecompressSync) throw new Error("Zstd repair requires Node with zstd support (22.15+)");
|
|
32
|
+
const original = await readFile(path);
|
|
33
|
+
const bytes = compressed ? decodeFrames(original) : original;
|
|
34
|
+
const plaintext = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
35
|
+
if (!plaintext.endsWith("\n")) throw new Error("Refusing a torn session log");
|
|
36
|
+
const lines = plaintext.slice(0, -1).split("\n");
|
|
37
|
+
const header = JSON.parse(lines[0]);
|
|
38
|
+
if (header.type !== "session" || header.version !== 0 || typeof header.id !== "string") {
|
|
39
|
+
throw new Error("Unsupported session header");
|
|
40
|
+
}
|
|
41
|
+
let changed = 0;
|
|
42
|
+
for (let i = 1; i < lines.length; i++) {
|
|
43
|
+
const event = JSON.parse(lines[i]);
|
|
44
|
+
if (event.type !== LEGACY_TYPE) continue;
|
|
45
|
+
if (!readActivityPayload(event.data) || !Number.isSafeInteger(event.seq) || event.seq < 0) {
|
|
46
|
+
throw new Error(`Refusing malformed legacy activity at record ${i}`);
|
|
47
|
+
}
|
|
48
|
+
if (event.ignorable === true) continue;
|
|
49
|
+
if (event.ignorable !== undefined) throw new Error(`Invalid ignorable marker at record ${i}`);
|
|
50
|
+
lines[i] = JSON.stringify({ ...event, ignorable: true });
|
|
51
|
+
changed++;
|
|
52
|
+
}
|
|
53
|
+
const result = { path, sessionId: header.id, changed, written: false, sha256: hash(original) };
|
|
54
|
+
if (!write || changed === 0) return result;
|
|
55
|
+
// DSH requires a standalone first zstd frame containing only the header.
|
|
56
|
+
const repaired = compressed
|
|
57
|
+
? Buffer.concat([
|
|
58
|
+
zlib.zstdCompressSync(Buffer.from(`${lines[0]}\n`)),
|
|
59
|
+
zlib.zstdCompressSync(Buffer.from(`${lines.slice(1).join("\n")}\n`)),
|
|
60
|
+
])
|
|
61
|
+
: Buffer.from(`${lines.join("\n")}\n`);
|
|
62
|
+
const backup = `${path}.before-codex-activity-${result.sha256}.bak`;
|
|
63
|
+
try { await copyFile(path, backup, constants.COPYFILE_EXCL) }
|
|
64
|
+
catch (error) { if (error.code !== "EEXIST") throw error }
|
|
65
|
+
if (hash(await readFile(backup)) !== result.sha256) throw new Error("Backup verification failed");
|
|
66
|
+
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
67
|
+
try {
|
|
68
|
+
const handle = await open(temporary, "wx", info.mode & 0o777);
|
|
69
|
+
try { await handle.writeFile(repaired); await handle.sync() } finally { await handle.close() }
|
|
70
|
+
if (hash(await readFile(path)) !== result.sha256) throw new Error("Session changed during repair; refusing replacement");
|
|
71
|
+
await rename(temporary, path);
|
|
72
|
+
const directory = await open(dirname(path), "r");
|
|
73
|
+
try { await directory.sync() } finally { await directory.close() }
|
|
74
|
+
} finally {
|
|
75
|
+
await rm(temporary, { force: true });
|
|
76
|
+
}
|
|
77
|
+
return { ...result, written: true, backup };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function scanActivityHistories(root) {
|
|
81
|
+
const info = await lstat(root);
|
|
82
|
+
if (!info.isDirectory()) throw new Error("Expected a sessions directory, not a symlink");
|
|
83
|
+
const report = { scanned: 0, affected: [], errors: [] };
|
|
84
|
+
async function visit(directory) {
|
|
85
|
+
for (const entry of await readdir(directory, { withFileTypes: true })) {
|
|
86
|
+
const path = join(directory, entry.name);
|
|
87
|
+
if (entry.isDirectory()) await visit(path);
|
|
88
|
+
else if (entry.isFile() && ["session.jsonl", "session.jsonl.zstd"].includes(entry.name)) {
|
|
89
|
+
report.scanned++;
|
|
90
|
+
try {
|
|
91
|
+
const result = await repairActivityHistory(path);
|
|
92
|
+
if (result.changed > 0) report.affected.push(result);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
report.errors.push({ path, error: error.message });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
await visit(root);
|
|
100
|
+
return report;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
|
|
104
|
+
const args = process.argv.slice(2);
|
|
105
|
+
const write = args[0] === "--write";
|
|
106
|
+
const paths = write ? args.slice(1) : args;
|
|
107
|
+
if (!paths.length) throw new Error("Usage: repair-activity-history.mjs [--write] <session.jsonl[.zstd]> ... | [--write] --root <sessions-directory> (stop DSH before --write)");
|
|
108
|
+
if (paths[0] === "--root") {
|
|
109
|
+
if (paths.length !== 2) throw new Error("--root requires exactly one sessions directory");
|
|
110
|
+
const root = resolve(paths[1]);
|
|
111
|
+
const report = await scanActivityHistories(root);
|
|
112
|
+
console.log(JSON.stringify(report));
|
|
113
|
+
if (report.errors.length > 0) throw new Error("Session scan incomplete; no batch repairs applied");
|
|
114
|
+
if (write) {
|
|
115
|
+
for (const { path } of report.affected) console.log(JSON.stringify(await repairActivityHistory(path, { write: true })));
|
|
116
|
+
const remaining = await scanActivityHistories(root);
|
|
117
|
+
console.log(JSON.stringify({ verification: remaining }));
|
|
118
|
+
if (remaining.errors.length || remaining.affected.length) throw new Error("Session verification incomplete");
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
for (const path of paths) console.log(JSON.stringify(await repairActivityHistory(resolve(path), { write })));
|
|
122
|
+
}
|
|
123
|
+
}
|