super-dsh 0.1.0 → 0.1.2
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/agent-agy/bridge/agy_bridge.py +250 -0
- package/agent-agy/bridge/gemini_openai_server.mjs +98 -0
- package/agent-agy/cordis.patch.yml +49 -0
- package/agent-agy/dist/adapter.d.ts +15 -0
- package/agent-agy/dist/adapter.js +42 -0
- package/agent-agy/dist/agent-preset-agy.d.ts +47 -0
- package/agent-agy/dist/agent-preset-agy.js +169 -0
- package/agent-agy/dist/agent-preset-projection.d.ts +31 -0
- package/agent-agy/dist/agent-preset-projection.js +33 -0
- package/agent-agy/dist/agent.d.ts +137 -0
- package/agent-agy/dist/agent.js +493 -0
- package/agent-agy/dist/agy-cli-client.d.ts +137 -0
- package/agent-agy/dist/agy-cli-client.js +301 -0
- package/agent-agy/dist/agy-client.d.ts +160 -0
- package/agent-agy/dist/agy-client.js +252 -0
- package/agent-agy/dist/agy-sessions.d.ts +24 -0
- package/agent-agy/dist/agy-sessions.js +116 -0
- package/agent-agy/dist/agy-store.d.ts +39 -0
- package/agent-agy/dist/agy-store.js +83 -0
- package/agent-agy/dist/inbox.d.ts +26 -0
- package/agent-agy/dist/inbox.js +53 -0
- package/agent-agy/dist/index.d.ts +80 -0
- package/agent-agy/dist/index.js +505 -0
- package/agent-agy/dist/models.d.ts +41 -0
- package/agent-agy/dist/models.js +74 -0
- package/agent-agy/dist/world-plugin.d.ts +9 -0
- package/agent-agy/dist/world-plugin.js +97 -0
- package/agent-agy/package.json +64 -0
- package/agent-claude/cordis.patch.yml +42 -0
- package/agent-claude/dist/adapter.js +62 -0
- package/agent-claude/dist/agent-preset-claude.js +169 -0
- package/agent-claude/dist/agent-preset-projection.js +30 -0
- package/agent-claude/dist/agent.js +1271 -0
- package/agent-claude/dist/claude-client.js +357 -0
- package/agent-claude/dist/claude-events.js +223 -0
- package/agent-claude/dist/claude-home.js +29 -0
- package/agent-claude/dist/commands.js +182 -0
- package/agent-claude/dist/content.js +134 -0
- package/agent-claude/dist/inbox.js +53 -0
- package/agent-claude/dist/index.js +555 -0
- package/agent-claude/dist/input-queue.js +64 -0
- package/agent-claude/dist/interaction.js +271 -0
- package/agent-claude/dist/models.js +158 -0
- package/agent-claude/dist/permission.js +120 -0
- package/agent-claude/dist/session-id.js +20 -0
- package/agent-claude/dist/session-map.js +165 -0
- package/agent-claude/dist/world-plugin.js +14 -0
- package/agent-claude/package.json +74 -0
- package/agent-codex/README.md +74 -0
- package/agent-codex/cordis.patch.yml +77 -0
- package/agent-codex/dist/adapter.js +55 -0
- package/agent-codex/dist/agent-preset-codex.js +171 -0
- package/agent-codex/dist/agent-preset-projection.js +37 -0
- package/agent-codex/dist/agent.js +1056 -0
- package/agent-codex/dist/app-home.js +65 -0
- package/agent-codex/dist/codex-client.js +469 -0
- package/agent-codex/dist/codex-events.js +297 -0
- package/agent-codex/dist/codex-store.js +190 -0
- package/agent-codex/dist/inbox.js +53 -0
- package/agent-codex/dist/index.js +605 -0
- package/agent-codex/dist/models.js +152 -0
- package/agent-codex/dist/permission.js +71 -0
- package/agent-codex/dist/session-map.js +170 -0
- package/agent-codex/dist/spike.js +46 -0
- package/agent-codex/dist/world-plugin.js +96 -0
- package/agent-codex/package.json +78 -0
- package/agent-hermes/README.md +144 -0
- package/agent-hermes/cordis.patch.yml +83 -0
- package/agent-hermes/dist/adapter.js +83 -0
- package/agent-hermes/dist/agent-preset-hermes.js +171 -0
- package/agent-hermes/dist/agent-preset-projection.js +34 -0
- package/agent-hermes/dist/agent.js +1068 -0
- package/agent-hermes/dist/hermes-client.js +953 -0
- package/agent-hermes/dist/hermes-events.js +289 -0
- package/agent-hermes/dist/hermes-store.js +158 -0
- package/agent-hermes/dist/inbox.js +53 -0
- package/agent-hermes/dist/index.js +541 -0
- package/agent-hermes/dist/models.js +324 -0
- package/agent-hermes/dist/permission.js +60 -0
- package/agent-hermes/dist/world-plugin.js +101 -0
- package/agent-hermes/package.json +76 -0
- package/agent-hub/cordis.patch.yml +12 -0
- package/agent-hub/dist/agent-roster.d.ts +34 -0
- package/agent-hub/dist/agent-roster.js +27 -0
- package/agent-hub/dist/carrier.d.ts +43 -0
- package/agent-hub/dist/carrier.js +241 -0
- package/agent-hub/dist/client-shim.d.ts +28 -0
- package/agent-hub/dist/client-shim.js +251 -0
- package/agent-hub/dist/envelope.d.ts +54 -0
- package/agent-hub/dist/envelope.js +92 -0
- package/agent-hub/dist/gateway.d.ts +28 -0
- package/agent-hub/dist/gateway.js +18 -0
- package/agent-hub/dist/index-pass.d.ts +22 -0
- package/agent-hub/dist/index-pass.js +47 -0
- package/agent-hub/dist/index.d.ts +29 -0
- package/agent-hub/dist/index.js +29 -0
- package/agent-hub/dist/labels.d.ts +45 -0
- package/agent-hub/dist/labels.js +83 -0
- package/agent-hub/dist/ownership.d.ts +37 -0
- package/agent-hub/dist/ownership.js +54 -0
- package/agent-hub/dist/roster.d.ts +28 -0
- package/agent-hub/dist/roster.js +38 -0
- package/agent-hub/dist/rpc.d.ts +38 -0
- package/agent-hub/dist/rpc.js +52 -0
- package/agent-hub/dist/spawn-world.d.ts +46 -0
- package/agent-hub/dist/spawn-world.js +72 -0
- package/agent-hub/dist/targets.d.ts +33 -0
- package/agent-hub/dist/targets.js +30 -0
- package/agent-hub/dist/world-entry.d.ts +31 -0
- package/agent-hub/dist/world-entry.js +51 -0
- package/agent-hub/dist/world-host.d.ts +69 -0
- package/agent-hub/dist/world-host.js +44 -0
- package/agent-hub/dist/world-join.d.ts +16 -0
- package/agent-hub/dist/world-join.js +122 -0
- package/agent-hub/dist/world-mount.d.ts +52 -0
- package/agent-hub/dist/world-mount.js +52 -0
- package/agent-hub/dist/world-mux.d.ts +72 -0
- package/agent-hub/dist/world-mux.js +224 -0
- package/agent-hub/dist/world-provision.d.ts +20 -0
- package/agent-hub/dist/world-provision.js +122 -0
- package/agent-hub/dist/world-web-server.d.ts +162 -0
- package/agent-hub/dist/world-web-server.js +248 -0
- package/agent-hub/lib/client/index.js +242 -0
- package/agent-hub/package.json +65 -0
- package/agent-omp/README.md +77 -0
- package/agent-omp/cordis.patch.yml +92 -0
- package/agent-omp/dist/adapter.js +36 -0
- package/agent-omp/dist/agent-preset-omp.js +171 -0
- package/agent-omp/dist/agent-preset-projection.js +36 -0
- package/agent-omp/dist/agent.js +1145 -0
- package/agent-omp/dist/app-home.js +86 -0
- package/agent-omp/dist/discovery.js +116 -0
- package/agent-omp/dist/inbox.js +53 -0
- package/agent-omp/dist/index.js +574 -0
- package/agent-omp/dist/knobs.js +50 -0
- package/agent-omp/dist/lazy-rpc.js +161 -0
- package/agent-omp/dist/mobile/gesture.js +148 -0
- package/agent-omp/dist/mobile/zoom-guard.js +294 -0
- package/agent-omp/dist/mobile-boot.js +38 -0
- package/agent-omp/dist/models.js +299 -0
- package/agent-omp/dist/omp-cli.js +147 -0
- package/agent-omp/dist/omp-disk-discovery.js +178 -0
- package/agent-omp/dist/omp-store.js +518 -0
- package/agent-omp/dist/pairing.js +45 -0
- package/agent-omp/dist/permission.js +96 -0
- package/agent-omp/dist/protocol.js +13 -0
- package/agent-omp/dist/rpc-types.js +1 -0
- package/agent-omp/dist/sdk-client.js +378 -0
- package/agent-omp/dist/sidecar-client.js +195 -0
- package/agent-omp/dist/store/adapter.js +47 -0
- package/agent-omp/dist/store/db.js +210 -0
- package/agent-omp/dist/store/index.js +59 -0
- package/agent-omp/dist/store/reconcile.js +51 -0
- package/agent-omp/dist/store/schema.js +40 -0
- package/agent-omp/dist/world-plugin.js +96 -0
- package/agent-omp/package.json +97 -0
- package/agent-omp/sidecar/main.ts +481 -0
- package/agent-pi/README.md +67 -0
- package/agent-pi/cordis.patch.yml +80 -0
- package/agent-pi/dist/adapter.js +53 -0
- package/agent-pi/dist/agent-preset-pi.js +169 -0
- package/agent-pi/dist/agent-preset-projection.js +29 -0
- package/agent-pi/dist/agent.js +949 -0
- package/agent-pi/dist/inbox.js +53 -0
- package/agent-pi/dist/index.js +595 -0
- package/agent-pi/dist/knobs.js +24 -0
- package/agent-pi/dist/models.js +200 -0
- package/agent-pi/dist/permission.js +66 -0
- package/agent-pi/dist/pi-client.js +224 -0
- package/agent-pi/dist/pi-events.js +155 -0
- package/agent-pi/dist/pi-home.js +55 -0
- package/agent-pi/dist/safe-json.js +25 -0
- package/agent-pi/dist/session-map.js +119 -0
- package/agent-pi/dist/world-plugin.js +97 -0
- package/agent-pi/package.json +77 -0
- package/cordis.patch.yml +22 -19
- package/package.json +88 -25
- package/client.js +0 -4
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSH-session ↔ Claude-session identity + permission-mode record (the "mapping
|
|
3
|
+
* only" model, 2026-09-16; mirrors `agent-codex/src/session-map.ts`).
|
|
4
|
+
*
|
|
5
|
+
* Route A anchors the Claude session id deterministically from the DSH id's
|
|
6
|
+
* UUID tail, so the *id* itself survives restarts with no map. What the event
|
|
7
|
+
* log cannot round-trip is the **effective Claude permission mode**: the extra
|
|
8
|
+
* tiers (`plan` / `auto` / `dontAsk`) are never folded into `permission/preset`
|
|
9
|
+
* by `permissionEventsFor`, and `presetFromEvents` skips a `preset:"plan"`
|
|
10
|
+
* event — so a resumed session cannot recover its live mode from the log. The
|
|
11
|
+
* mode is therefore persisted HERE, in the same record that stores the
|
|
12
|
+
* `claudeSessionId`, and `resume` reads it back and re-applies it through
|
|
13
|
+
* `client.setPermissionMode(mode)`.
|
|
14
|
+
*
|
|
15
|
+
* The record is an adapter-owned JSON file at the world's DSH-home root (NOT
|
|
16
|
+
* inside the Claude app home — the app home is the CLI's native `~/.claude`
|
|
17
|
+
* since the 2026-09-17 home ruling; DSH bookkeeping never lives in the
|
|
18
|
+
* runtime's native tree):
|
|
19
|
+
*
|
|
20
|
+
* `<worldHome>/dsh-sessions.json` (`<DSH home>/agents/claude/dsh-sessions.json`)
|
|
21
|
+
*
|
|
22
|
+
* Shape: `{ "<dshSessionId>": { claudeSessionId, claudeMode, cwd, createdAt,
|
|
23
|
+
* preset } }`. Writes are atomic (temp file + rename); reads are fail-soft
|
|
24
|
+
* (malformed JSON reads as empty) and never throw, so a corrupt map degrades
|
|
25
|
+
* to "unknown session" rather than breaking every operation.
|
|
26
|
+
*/
|
|
27
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
28
|
+
import { dirname, join, resolve } from "node:path";
|
|
29
|
+
import { assertNotProdHome } from "./claude-home.js";
|
|
30
|
+
/**
|
|
31
|
+
* The adapter's DSH-side state dir — the world's DSH home root
|
|
32
|
+
* (`<dshHome>/agents/claude`), never the Claude app home. Precedence:
|
|
33
|
+
* explicit world home (boot-provided `dshHomePath`), then `$DSH_HOME` (a hub
|
|
34
|
+
* home, so the world layout nests `agents/claude`), then the line's
|
|
35
|
+
* historical test-home default. Prod-guarded.
|
|
36
|
+
*/
|
|
37
|
+
export function resolveClaudeStateDir(home) {
|
|
38
|
+
if (home !== undefined) {
|
|
39
|
+
const dir = resolve(home);
|
|
40
|
+
assertNotProdHome(dir, "claude state dir");
|
|
41
|
+
return dir;
|
|
42
|
+
}
|
|
43
|
+
const fallbackHome = resolve(process.env.DSH_HOME ?? join(process.cwd(), ".tests", "aw"));
|
|
44
|
+
const dir = join(fallbackHome, "agents", "claude");
|
|
45
|
+
assertNotProdHome(dir, "claude state dir");
|
|
46
|
+
return dir;
|
|
47
|
+
}
|
|
48
|
+
/** The map file's path at the world DSH-home root. */
|
|
49
|
+
export function sessionMapPath(home) {
|
|
50
|
+
return join(resolveClaudeStateDir(home), "dsh-sessions.json");
|
|
51
|
+
}
|
|
52
|
+
/** Read the map. Fail-soft: a missing or malformed file reads as `{}`. */
|
|
53
|
+
export function readSessionMap(home) {
|
|
54
|
+
let raw;
|
|
55
|
+
try {
|
|
56
|
+
raw = readFileSync(sessionMapPath(home), "utf8");
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
let parsed;
|
|
62
|
+
try {
|
|
63
|
+
parsed = JSON.parse(raw);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return {};
|
|
67
|
+
}
|
|
68
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
|
|
69
|
+
return {};
|
|
70
|
+
const out = {};
|
|
71
|
+
for (const [id, value] of Object.entries(parsed)) {
|
|
72
|
+
const record = coerceRecord(value);
|
|
73
|
+
if (record !== undefined)
|
|
74
|
+
out[id] = record;
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
/** Validate one entry; malformed entries are dropped (fail-soft). */
|
|
79
|
+
function coerceRecord(value) {
|
|
80
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
81
|
+
return undefined;
|
|
82
|
+
const raw = value;
|
|
83
|
+
const cwd = raw["cwd"];
|
|
84
|
+
if (typeof cwd !== "string" || cwd === "")
|
|
85
|
+
return undefined;
|
|
86
|
+
const claudeSessionId = raw["claudeSessionId"];
|
|
87
|
+
const claudeMode = raw["claudeMode"];
|
|
88
|
+
const preset = raw["preset"];
|
|
89
|
+
const createdAt = raw["createdAt"];
|
|
90
|
+
return {
|
|
91
|
+
claudeSessionId: typeof claudeSessionId === "string" && claudeSessionId !== "" ? claudeSessionId : null,
|
|
92
|
+
claudeMode: typeof claudeMode === "string" && claudeMode !== "" ? claudeMode : null,
|
|
93
|
+
cwd,
|
|
94
|
+
createdAt: typeof createdAt === "number" && Number.isFinite(createdAt) ? createdAt : 0,
|
|
95
|
+
preset: typeof preset === "string" && preset !== "" ? preset : null,
|
|
96
|
+
...(raw["resumable"] === false ? { resumable: false } : {}),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/** One session's record, or `undefined` when the id is unknown. */
|
|
100
|
+
export function sessionRecord(home, dshSessionId) {
|
|
101
|
+
return readSessionMap(home)[dshSessionId];
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Insert or merge one session's record and persist the map atomically (temp
|
|
105
|
+
* file + rename, same directory). Merge semantics: an omitted `patch` field
|
|
106
|
+
* keeps the stored value; `claudeSessionId: null` explicitly records "not yet
|
|
107
|
+
* resolved". Throws only when the write itself fails — the caller owns that
|
|
108
|
+
* decision (create treats an unwritable map as fatal).
|
|
109
|
+
*/
|
|
110
|
+
export function upsertSession(home, dshSessionId, patch) {
|
|
111
|
+
const path = sessionMapPath(home);
|
|
112
|
+
const map = readSessionMap(home);
|
|
113
|
+
const previous = map[dshSessionId];
|
|
114
|
+
const next = {
|
|
115
|
+
claudeSessionId: patch.claudeSessionId !== undefined ? patch.claudeSessionId : previous?.claudeSessionId ?? null,
|
|
116
|
+
claudeMode: patch.claudeMode !== undefined ? patch.claudeMode : previous?.claudeMode ?? null,
|
|
117
|
+
cwd: patch.cwd !== undefined ? patch.cwd : previous?.cwd ?? "",
|
|
118
|
+
createdAt: patch.createdAt !== undefined ? patch.createdAt : previous?.createdAt ?? Date.now(),
|
|
119
|
+
preset: patch.preset !== undefined ? patch.preset : previous?.preset ?? null,
|
|
120
|
+
// Sticky refusal: only an explicit `true` clears a recorded false.
|
|
121
|
+
...(patch.resumable === true ? {} : patch.resumable === false || previous?.resumable === false ? { resumable: false } : {}),
|
|
122
|
+
};
|
|
123
|
+
map[dshSessionId] = next;
|
|
124
|
+
const dir = dirname(path);
|
|
125
|
+
mkdirSync(dir, { recursive: true });
|
|
126
|
+
const temp = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
127
|
+
writeFileSync(temp, `${JSON.stringify(map, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
128
|
+
try {
|
|
129
|
+
renameSync(temp, path);
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
try {
|
|
133
|
+
rmSync(temp, { force: true });
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// The rename failure is primary.
|
|
137
|
+
}
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
return next;
|
|
141
|
+
}
|
|
142
|
+
/** Drop one session's record (idempotent; failure to persist rethrows). */
|
|
143
|
+
export function forgetSession(home, dshSessionId) {
|
|
144
|
+
const path = sessionMapPath(home);
|
|
145
|
+
if (!existsSync(path))
|
|
146
|
+
return;
|
|
147
|
+
const map = readSessionMap(home);
|
|
148
|
+
if (map[dshSessionId] === undefined)
|
|
149
|
+
return;
|
|
150
|
+
delete map[dshSessionId];
|
|
151
|
+
const temp = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
152
|
+
writeFileSync(temp, `${JSON.stringify(map, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
153
|
+
try {
|
|
154
|
+
renameSync(temp, path);
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
try {
|
|
158
|
+
rmSync(temp, { force: true });
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
// The rename failure is primary.
|
|
162
|
+
}
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `./world` entry point for a future hub-side packaging step.
|
|
3
|
+
*
|
|
4
|
+
* Ruling R2 (2026-09-16): this adapter ships and is verified STANDALONE only.
|
|
5
|
+
* Joining the agent-worlds hub is the hub side's job — it packages this entry
|
|
6
|
+
* with its own spawn/roster wiring. Nothing here starts a process or reads the
|
|
7
|
+
* hub; it only exposes the canonical provider row name.
|
|
8
|
+
*/
|
|
9
|
+
export const name = 'aw.agent-adapter-claude-world';
|
|
10
|
+
export const providerRow = '@pgmi-builds/agent-adapter-claude';
|
|
11
|
+
export function apply() {
|
|
12
|
+
// Intentionally inert: the standalone bundle patch (cordis.patch.yml) is the
|
|
13
|
+
// sole mounting path in V1. The hub packages its own insert row via `providerRow`.
|
|
14
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pgmi-builds/agent-adapter-claude",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Claude Code provider for DeepSeek Harness (dsh) — an AgentFactory that bridges the host Claude Code CLI through the official @anthropic-ai/claude-agent-sdk into the Dash Agent/Session contracts (Agent Worlds, standalone form).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./dist/index.js",
|
|
8
|
+
"./world": "./dist/world-plugin.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"cordis.patch.yml"
|
|
13
|
+
],
|
|
14
|
+
"dsh": {
|
|
15
|
+
"bundle": {
|
|
16
|
+
"patch": "./cordis.patch.yml"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc -p tsconfig.json",
|
|
21
|
+
"test": "node --test test/*.test.mjs"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.263"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
28
|
+
"@deepseek-ai/dsh-agent": "0.1.5-rc.2",
|
|
29
|
+
"@deepseek-ai/dsh-commands": "0.1.5-rc.2",
|
|
30
|
+
"@deepseek-ai/dsh-llm": "0.1.5-rc.2",
|
|
31
|
+
"@deepseek-ai/dsh-session": "0.1.5-rc.2",
|
|
32
|
+
"@deepseek-ai/dsh-session-persistence": "0.1.5-rc.2",
|
|
33
|
+
"@deepseek-ai/dsh-user-approval": "0.1.5-rc.2",
|
|
34
|
+
"@deepseek-ai/dsh-user-questions": "0.1.5-rc.2"
|
|
35
|
+
},
|
|
36
|
+
"peerDependenciesMeta": {
|
|
37
|
+
"@deepseek-ai/cordis": {
|
|
38
|
+
"optional": true
|
|
39
|
+
},
|
|
40
|
+
"@deepseek-ai/dsh-agent": {
|
|
41
|
+
"optional": true
|
|
42
|
+
},
|
|
43
|
+
"@deepseek-ai/dsh-commands": {
|
|
44
|
+
"optional": true
|
|
45
|
+
},
|
|
46
|
+
"@deepseek-ai/dsh-llm": {
|
|
47
|
+
"optional": true
|
|
48
|
+
},
|
|
49
|
+
"@deepseek-ai/dsh-session": {
|
|
50
|
+
"optional": true
|
|
51
|
+
},
|
|
52
|
+
"@deepseek-ai/dsh-session-persistence": {
|
|
53
|
+
"optional": true
|
|
54
|
+
},
|
|
55
|
+
"@deepseek-ai/dsh-user-approval": {
|
|
56
|
+
"optional": true
|
|
57
|
+
},
|
|
58
|
+
"@deepseek-ai/dsh-user-questions": {
|
|
59
|
+
"optional": true
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@types/node": "^26.2.0",
|
|
64
|
+
"typescript": "^5.6.0"
|
|
65
|
+
},
|
|
66
|
+
"publishConfig": {
|
|
67
|
+
"access": "public"
|
|
68
|
+
},
|
|
69
|
+
"license": "MIT",
|
|
70
|
+
"main": "dist/index.js",
|
|
71
|
+
"engines": {
|
|
72
|
+
"node": ">=22.18"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# @pgmi-builds/agent-adapter-codex
|
|
2
|
+
|
|
3
|
+
superd 外壳下的 **Codex adapter**(agent-worlds 线,AW-E):把 Codex CLI 经由
|
|
4
|
+
`@openai/codex-sdk` 接进 DSH —— 会话、事件、模型目录、持久化全部按 DSH 语义呈现,
|
|
5
|
+
WebUI 消费的是 DSH 服务提供的数据面。
|
|
6
|
+
|
|
7
|
+
> 通用规则(定位 / 两形态 / 测试准则 / 接线域 / per-agent 定制权 / home 与 profile 布局)
|
|
8
|
+
> 见上级目录的 `agent-adapter-dev-rules.md` —— 改本包前先读它。
|
|
9
|
+
|
|
10
|
+
## 它是什么
|
|
11
|
+
|
|
12
|
+
- 一个**普通 dsh plugin bundle**:`package.json` 的 `dsh.bundle.patch` 指向本目录的
|
|
13
|
+
`cordis.patch.yml`(挂 codex provider、关掉它替代的原生行、重写权限三档表、pin 应用内目录选择器)。
|
|
14
|
+
- 一个 **AgentFactory**:`src/index.ts` 里的 `CodexProvider` 在 `ctx.agents` 注册工厂,
|
|
15
|
+
每个会话一个 `CodexSdkClient`(真 `@openai/codex-sdk` 线程);`src/agent.ts` 把 Codex 线程事件
|
|
16
|
+
投影成 DSH 会话事件。
|
|
17
|
+
- **不包含** 任何 hub 耦合:世界挂载由 hub 侧驱动(AW-B 用 `@pgmi-builds/agent-hub/world` 的
|
|
18
|
+
mount carrier)。本包自己就足以点亮一个完整的 web app。
|
|
19
|
+
|
|
20
|
+
## 单独跑起来(首选开发/验收形态)
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# 在仓根执行;端口默认 4988,LAN 走只绑 LAN IP 的 socat relay
|
|
24
|
+
AW_APP_PORT=4989 bash super-dsh/test/start-codex-app.sh
|
|
25
|
+
# 脚本会打印 loopback / LAN token URL;停止:
|
|
26
|
+
systemctl --user stop aw-codex-app-<port>-test aw-codex-app-<port>-relay
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
环境变量:`AW_APP_PORT`(端口)、`AW_APP_HOME`(默认 `$WT/.tests`)、
|
|
30
|
+
`SUPERD_LAN_HOST`(默认 `192.168.31.130`)、`CODEX_TRACE=1`(provider/agent 追踪)。
|
|
31
|
+
|
|
32
|
+
## Home 与 profile 布局(user 裁定)
|
|
33
|
+
|
|
34
|
+
| 位置 | 内容 |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `$DSH_HOME`(测试 = `<repo>/.tests`) | 测试 DSH home |
|
|
37
|
+
| `$DSH_HOME/agents/codex` | **Codex app home**:`config.toml` / `auth.json` / 模型 catalog(由 `scripts/setup-codex-home.mjs` 从 `~/.codex` **单向**拷入)+ Codex 自己的 rollout/state |
|
|
38
|
+
| `$DSH_HOME/profiles/codex` | 本 app 的 dsh profile(bundles + `cordis.patch.yml`) |
|
|
39
|
+
| `$DSH_HOME/sessions/...` | **DSH 会话日志**(上游 `session-persistence-jsonl`,zstd)——list/replay 都由此提供 |
|
|
40
|
+
|
|
41
|
+
**禁止**自造 per-app home(如 `.tests/codex-app`)。
|
|
42
|
+
|
|
43
|
+
## 关键实现点
|
|
44
|
+
|
|
45
|
+
- **session 身份 = 一对一映射**:DSH session id 是权威;Codex thread id 在其物化后写入
|
|
46
|
+
`<codexHome>/dsh-sessions.json`(`src/session-map.ts`)。resume 只经该映射重挂线程,查不到即
|
|
47
|
+
fail-closed。**不自建 rollout 扫描做 list,也不自解析 JSONL 做 replay。**
|
|
48
|
+
- **DSH-native 持久化**:工厂按上游 agent-loop 的方式持有写通道(create `persistence.create` /
|
|
49
|
+
resume `persistence.open(id,'write')` → `read` → `interruptedTurnClosers` → `prepare`),
|
|
50
|
+
发布点刷未存后缀,dispose 时 `handle.close()` 排空。
|
|
51
|
+
- **每会话路由钉住**:工厂在 create/resume 时同步钉住本会话路由(追加 `model/selection` +
|
|
52
|
+
`installModelSelection`),**不依赖全局默认模型的落盘时序**(共享 test home 里 settings 默认可能属别线)。
|
|
53
|
+
- **事件映射**:`system/message`(Codex 系统提示,rollout HEAD `base_instructions`)、
|
|
54
|
+
`request/context`(provider/model/contextWindow)、`assistant/message`(reasoning 用
|
|
55
|
+
`{type:'reasoning',text}`)、`tool/call`(原始 JSON 串)/`tool/result`、`step/start|end`、`turn/start|end`。
|
|
56
|
+
|
|
57
|
+
## 测试
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm run build # tsc strict
|
|
61
|
+
DSH_HOME=<repo>/.tests \
|
|
62
|
+
SUPERD_DSH_ANCHOR=<repo>/upstream/deepseek-harness/package.json \
|
|
63
|
+
node --test test/*.test.mjs
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
live 门控:`AW_CODEX_LIVE=1`(会真跑一次 Codex turn)。
|
|
67
|
+
|
|
68
|
+
## 已知边界(V1)
|
|
69
|
+
|
|
70
|
+
- 审批仅 launch-only 预设(SDK 无运行时审批;app-server 线才有 `*/requestApproval`);
|
|
71
|
+
- 无 steer / 无运行时换模型(下一轮生效)/ usage 滞后一轮 / 无 fork;
|
|
72
|
+
- `compaction/*`、`todo/write`、`subagent/*` 未接(见规则文档 §9.4 backlog);
|
|
73
|
+
- **AW-B world 挂载移植**:本包已无 world-plugin;若要在 AW-B 的 mount carrier 下作为世界被挂载,
|
|
74
|
+
由 hub 侧驱动(移植为跟踪项)。
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# @pgmi-builds/agent-adapter-codex bundle patch (AW-E SDK line, omp-web form).
|
|
2
|
+
# This file is the `dsh.bundle.patch` layer of the adapter package: when the
|
|
3
|
+
# codex world profile mounts the bundle, the boot merges THIS patch — mount
|
|
4
|
+
# the Codex provider plus disable the native components it replaces.
|
|
5
|
+
#
|
|
6
|
+
# Profile-specific values are deliberately NOT here: `webserver.port` (port
|
|
7
|
+
# choice) belongs to the world/profile patch layer (the aw-codex-world
|
|
8
|
+
# fixture carries the 127.0.0.1:0 listener posture).
|
|
9
|
+
|
|
10
|
+
# Mount the Codex provider: a Cordis Service that registers an AgentFactory
|
|
11
|
+
# on ctx.agents. The provider drives @openai/codex-sdk threads (one
|
|
12
|
+
# CodexSdkClient per session) plus resume (--resume-thread); the model
|
|
13
|
+
# selector is served by a Codex-backed LlmAdapter reading the codex home's
|
|
14
|
+
# config.toml + model_catalog_json.
|
|
15
|
+
- insert:
|
|
16
|
+
- id: codex-provider
|
|
17
|
+
name: '@pgmi-builds/agent-adapter-codex'
|
|
18
|
+
|
|
19
|
+
# Codex owns the live agent transcript (the rollout JSONL); disable the
|
|
20
|
+
# built-in loop so it does not double-run alongside the SDK thread.
|
|
21
|
+
- id: agent-loop
|
|
22
|
+
disabled: true
|
|
23
|
+
|
|
24
|
+
# The native routes register deepseek-official / pi-ai providers; the Codex
|
|
25
|
+
# provider registers its own adapter (the single `codex` route) on ctx.llm.
|
|
26
|
+
# Leaving them mounted would advertise native models alongside Codex's.
|
|
27
|
+
- id: llm-deepseek
|
|
28
|
+
disabled: true
|
|
29
|
+
- id: llm-pi-ai
|
|
30
|
+
disabled: true
|
|
31
|
+
|
|
32
|
+
# Codex is single-mode: the SDK thread drives tools itself, so no Dash preset
|
|
33
|
+
# composition is needed (the provider ships the fixed single-preset roster).
|
|
34
|
+
- id: agent-presets
|
|
35
|
+
disabled: true
|
|
36
|
+
|
|
37
|
+
# The upstream jsonl persistence row STAYS MOUNTED (2026-09-16): the provider
|
|
38
|
+
# writes the DSH session log through it (create/open/append/close), so
|
|
39
|
+
# list/read/replay/workspace grouping are ordinary DSH services over that log.
|
|
40
|
+
# The adapter no longer provides its own persistence.
|
|
41
|
+
|
|
42
|
+
# Codex has no runtime approval control — the adapter maps the preset onto
|
|
43
|
+
# the launch-only --approval-mode flag (3:3 table in src/permission.ts).
|
|
44
|
+
# Default to danger-full-access. Restate the full 3-preset table: a patch
|
|
45
|
+
# with only defaultPreset silently drops the bundle's 3-preset table to the
|
|
46
|
+
# plugin's built-in 2, breaking the 3:3 Dash<->Codex pairing.
|
|
47
|
+
- id: permission
|
|
48
|
+
config:
|
|
49
|
+
presets:
|
|
50
|
+
read-only:
|
|
51
|
+
sandbox: read-only
|
|
52
|
+
approval: ask
|
|
53
|
+
workspace-write:
|
|
54
|
+
sandbox: workspace-write
|
|
55
|
+
approval: ask
|
|
56
|
+
danger-full-access:
|
|
57
|
+
sandbox: danger-full-access
|
|
58
|
+
approval: never
|
|
59
|
+
defaultPreset: danger-full-access
|
|
60
|
+
|
|
61
|
+
# Directory picker: the stock `directory-picker-auto` is never a simple alias —
|
|
62
|
+
# it RESOLVES the interaction once per boot and mounts BOTH faces through the
|
|
63
|
+
# loader: the host backend package AND the matching CLIENT SURFACE package
|
|
64
|
+
# (`dsh-client-ui-directory-picker-<kind>`). The client surface is what
|
|
65
|
+
# occupies the workspace picker's directory-flow hole; with the hole empty the
|
|
66
|
+
# client hides the "add workspace" entry entirely (WorkspacePicker: `flowAvailable
|
|
67
|
+
# ? [ADD_WORKSPACE] : []`). NEVER just `disabled: true` this row — that removed
|
|
68
|
+
# the button. Pin the in-app browser by mounting both faces of `browse`
|
|
69
|
+
# ourselves (backend first: the surface's browser half drives the capability the
|
|
70
|
+
# backend registers) and unmounting the auto row.
|
|
71
|
+
- insert:
|
|
72
|
+
- id: directory-picker-browse
|
|
73
|
+
name: '@deepseek-ai/dsh-host-directory-picker-browse'
|
|
74
|
+
- id: directory-picker-browse-surface
|
|
75
|
+
name: '@deepseek-ai/dsh-client-ui-directory-picker-browse'
|
|
76
|
+
- id: directory-picker
|
|
77
|
+
disabled: true
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex-backed `LlmAdapter` registered on `ctx.llm` by the Codex provider.
|
|
3
|
+
*
|
|
4
|
+
* The browser model selector never touches the Codex agent directly: it is an
|
|
5
|
+
* RPC round-trip through the apiproxy, which reads `ctx.llm.listProviders /
|
|
6
|
+
* listModels / resolveModel` to build the catalog. This adapter answers those
|
|
7
|
+
* queries from the Codex home's real catalog (`models.ts`: config.toml +
|
|
8
|
+
* model_catalog_json) under the single provider route `codex` — never the
|
|
9
|
+
* native deepseek-official / pi-ai routes, which the profile disables.
|
|
10
|
+
*
|
|
11
|
+
* `stream` is never dispatched: Codex owns generation through its SDK thread
|
|
12
|
+
* (`CodexAgent`), so the adapter only serves catalog/metadata queries. It
|
|
13
|
+
* throws rather than fabricating a wire route the harness would misuse.
|
|
14
|
+
*/
|
|
15
|
+
import { LlmAdapter, LlmError, } from "@deepseek-ai/dsh-llm";
|
|
16
|
+
import { readCodexModelCatalog } from "./models.js";
|
|
17
|
+
/** The one provider route every Codex model is served under. */
|
|
18
|
+
export const CODEX_PROVIDER_ID = "codex";
|
|
19
|
+
export class CodexLlmAdapter extends LlmAdapter {
|
|
20
|
+
home;
|
|
21
|
+
/**
|
|
22
|
+
* `home` = the codex app home — the CLI's native `~/.codex` (2026-09-17
|
|
23
|
+
* home ruling), resolved by the provider via resolveCodexHome() so catalog
|
|
24
|
+
* reads never depend on construction-time context.
|
|
25
|
+
*/
|
|
26
|
+
constructor(home) {
|
|
27
|
+
super();
|
|
28
|
+
this.home = home;
|
|
29
|
+
}
|
|
30
|
+
providerInfo(_provider) {
|
|
31
|
+
return { id: CODEX_PROVIDER_ID, name: readCodexModelCatalog(this.home).provider ?? "Codex" };
|
|
32
|
+
}
|
|
33
|
+
async listModels(_provider) {
|
|
34
|
+
return readCodexModelCatalog(this.home).models.map((model) => ({
|
|
35
|
+
provider: CODEX_PROVIDER_ID,
|
|
36
|
+
id: model.id,
|
|
37
|
+
name: model.label,
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
async resolveModel(_provider, model) {
|
|
41
|
+
const found = readCodexModelCatalog(this.home).models.find((candidate) => candidate.id === model);
|
|
42
|
+
if (found === undefined) {
|
|
43
|
+
throw new LlmError(`Codex provider "${CODEX_PROVIDER_ID}" does not serve model "${model}"`, "MODEL_NOT_FOUND");
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
provider: CODEX_PROVIDER_ID,
|
|
47
|
+
id: found.id,
|
|
48
|
+
name: found.label,
|
|
49
|
+
...(found.contextWindow !== undefined && found.contextWindow > 0 ? { context: { contextWindow: found.contextWindow } } : {}),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
stream(_options) {
|
|
53
|
+
throw new LlmError("the Codex adapter does not stream: Codex owns generation through its SDK agent", "UNSUPPORTED_STREAM");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
2
|
+
var useValue = arguments.length > 2;
|
|
3
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
4
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
5
|
+
}
|
|
6
|
+
return useValue ? value : void 0;
|
|
7
|
+
};
|
|
8
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
9
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
10
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
11
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
12
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
13
|
+
var _, done = false;
|
|
14
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
15
|
+
var context = {};
|
|
16
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
17
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
18
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
19
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
20
|
+
if (kind === "accessor") {
|
|
21
|
+
if (result === void 0) continue;
|
|
22
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
23
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
24
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
25
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
26
|
+
}
|
|
27
|
+
else if (_ = accept(result)) {
|
|
28
|
+
if (kind === "field") initializers.unshift(_);
|
|
29
|
+
else descriptor[key] = _;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
33
|
+
done = true;
|
|
34
|
+
};
|
|
35
|
+
import { Remote, RemoteError, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
|
|
36
|
+
const CODEX_PRESET = Object.freeze({
|
|
37
|
+
id: "codex",
|
|
38
|
+
trust: "system",
|
|
39
|
+
path: "",
|
|
40
|
+
name: "Codex",
|
|
41
|
+
description: "Codex agent via the agent-codex adapter",
|
|
42
|
+
});
|
|
43
|
+
const COMPOSITION_TEXT = [
|
|
44
|
+
"# Codex (fixed roster)",
|
|
45
|
+
"",
|
|
46
|
+
"This deployment runs exactly one agent mode: the Codex provider bridges Dash",
|
|
47
|
+
"sessions to a native Codex agent over the @openai/codex-sdk. There is no",
|
|
48
|
+
"per-session plugin composition to configure, so this preset mounts nothing.",
|
|
49
|
+
"",
|
|
50
|
+
"rows: []",
|
|
51
|
+
"",
|
|
52
|
+
].join("\n");
|
|
53
|
+
/** The standing scope key every `codex` session reads presenters through. */
|
|
54
|
+
const STANDING_KEY = {};
|
|
55
|
+
let SingleCodexPresetRoster = (() => {
|
|
56
|
+
let _classSuper = TypertRemoteService;
|
|
57
|
+
let _instanceExtraInitializers = [];
|
|
58
|
+
let _remoteList_decorators;
|
|
59
|
+
let _remoteRead_decorators;
|
|
60
|
+
let _remoteCopy_decorators;
|
|
61
|
+
let _remoteDelete_decorators;
|
|
62
|
+
let _remoteSelect_decorators;
|
|
63
|
+
return class SingleCodexPresetRoster extends _classSuper {
|
|
64
|
+
static {
|
|
65
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
66
|
+
_remoteList_decorators = [Remote("list")];
|
|
67
|
+
_remoteRead_decorators = [Remote("read")];
|
|
68
|
+
_remoteCopy_decorators = [Remote("copy")];
|
|
69
|
+
_remoteDelete_decorators = [Remote("deletePreset")];
|
|
70
|
+
_remoteSelect_decorators = [Remote("select")];
|
|
71
|
+
__esDecorate(this, null, _remoteList_decorators, { kind: "method", name: "remoteList", static: false, private: false, access: { has: obj => "remoteList" in obj, get: obj => obj.remoteList }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
72
|
+
__esDecorate(this, null, _remoteRead_decorators, { kind: "method", name: "remoteRead", static: false, private: false, access: { has: obj => "remoteRead" in obj, get: obj => obj.remoteRead }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
73
|
+
__esDecorate(this, null, _remoteCopy_decorators, { kind: "method", name: "remoteCopy", static: false, private: false, access: { has: obj => "remoteCopy" in obj, get: obj => obj.remoteCopy }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
74
|
+
__esDecorate(this, null, _remoteDelete_decorators, { kind: "method", name: "remoteDelete", static: false, private: false, access: { has: obj => "remoteDelete" in obj, get: obj => obj.remoteDelete }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
75
|
+
__esDecorate(this, null, _remoteSelect_decorators, { kind: "method", name: "remoteSelect", static: false, private: false, access: { has: obj => "remoteSelect" in obj, get: obj => obj.remoteSelect }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
76
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
77
|
+
}
|
|
78
|
+
constructor(ctx) {
|
|
79
|
+
super(ctx, "agentPresets");
|
|
80
|
+
__runInitializers(this, _instanceExtraInitializers);
|
|
81
|
+
}
|
|
82
|
+
get defaultId() {
|
|
83
|
+
return CODEX_PRESET.id;
|
|
84
|
+
}
|
|
85
|
+
get roots() {
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
get authorable() {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
async list() {
|
|
92
|
+
return [CODEX_PRESET];
|
|
93
|
+
}
|
|
94
|
+
async resolve(id) {
|
|
95
|
+
if (id === undefined || id === CODEX_PRESET.id)
|
|
96
|
+
return CODEX_PRESET;
|
|
97
|
+
throw new RemoteError("agent-preset/not-found", `agent-presets: preset "${id}" not found (available: codex)`, { agentPreset: id, available: [CODEX_PRESET.id] });
|
|
98
|
+
}
|
|
99
|
+
async mount(_agentCtx, id) {
|
|
100
|
+
return this.resolve(id);
|
|
101
|
+
}
|
|
102
|
+
async recompose(_agentCtx, id) {
|
|
103
|
+
return this.resolve(id);
|
|
104
|
+
}
|
|
105
|
+
async read(id) {
|
|
106
|
+
await this.resolve(id);
|
|
107
|
+
return COMPOSITION_TEXT;
|
|
108
|
+
}
|
|
109
|
+
// ── Remote RPC surface (alpha: agentPresets routes come from @Remote) ────
|
|
110
|
+
// The stock dsh-host-apiproxy mapped cordis methods to routes; alpha serves
|
|
111
|
+
// them from @Remote decorators on a TypertRemoteService. A plain Service
|
|
112
|
+
// registers no routes, which is why Settings → Agent Preset 404'd.
|
|
113
|
+
async remoteList() {
|
|
114
|
+
if (process.env.CODEX_TRACE === "1")
|
|
115
|
+
process.stderr.write("[codex-roster] remoteList INVOKED\n");
|
|
116
|
+
return {
|
|
117
|
+
presets: [
|
|
118
|
+
{
|
|
119
|
+
id: CODEX_PRESET.id,
|
|
120
|
+
trust: CODEX_PRESET.trust,
|
|
121
|
+
isDefault: true,
|
|
122
|
+
name: CODEX_PRESET.name,
|
|
123
|
+
description: CODEX_PRESET.description,
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
authorable: this.authorable,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
async remoteRead(agentPreset) {
|
|
130
|
+
const preset = await this.resolve(agentPreset);
|
|
131
|
+
return {
|
|
132
|
+
agentPreset: preset.id,
|
|
133
|
+
trust: preset.trust,
|
|
134
|
+
content: await this.read(preset.id),
|
|
135
|
+
name: preset.name,
|
|
136
|
+
description: preset.description,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
async remoteCopy(from, id, name) {
|
|
140
|
+
await this.copy(from, id, name);
|
|
141
|
+
}
|
|
142
|
+
async remoteDelete(id) {
|
|
143
|
+
await this.remove(id);
|
|
144
|
+
}
|
|
145
|
+
/** Fixed roster: one preset, so selecting it is a no-op ack; anything else is unknown. */
|
|
146
|
+
async remoteSelect(agent, agentPreset) {
|
|
147
|
+
const preset = await this.resolve(agentPreset);
|
|
148
|
+
return preset.id;
|
|
149
|
+
}
|
|
150
|
+
async copy(_from, id, _name) {
|
|
151
|
+
throw new RemoteError("agent-preset/read-only", `agent-presets: preset "${id}" cannot be written: the Codex roster is fixed and ships exactly one preset`, { agentPreset: id, reason: "the Codex roster is fixed and ships exactly one preset" });
|
|
152
|
+
}
|
|
153
|
+
async remove(id) {
|
|
154
|
+
throw new RemoteError("agent-preset/read-only", `agent-presets: preset "${id}" cannot be written: the Codex roster is fixed and ships exactly one preset`, { agentPreset: id, reason: "the Codex roster is fixed and ships exactly one preset" });
|
|
155
|
+
}
|
|
156
|
+
serviceFor(_agent, _name) {
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
composeFrom(_agentCtx, _parentCtx) {
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
composedPreset(_agentCtx) {
|
|
163
|
+
return CODEX_PRESET.id;
|
|
164
|
+
}
|
|
165
|
+
async standingKeyFor(id) {
|
|
166
|
+
await this.resolve(id);
|
|
167
|
+
return STANDING_KEY;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
})();
|
|
171
|
+
export { SingleCodexPresetRoster };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `agentPreset` session projection, owned by the adapter.
|
|
3
|
+
*
|
|
4
|
+
* Upstream registers this fold from the `dsh-agent-presets` package, which our
|
|
5
|
+
* patch disables (the Codex provider replaces preset composition wholesale).
|
|
6
|
+
* But the Web UI depends on the projection regardless: the preset hero chip
|
|
7
|
+
* renders only when `session.projectionValues.agentPreset` resolves
|
|
8
|
+
* (`presetOf` in ui-agent-preset/seat-store.ts), and the session-header label
|
|
9
|
+
* reads it too. Without a registrant the projection is absent for every
|
|
10
|
+
* adapter session and both surfaces stay blank — roster or no roster (that
|
|
11
|
+
* was the "no mode" bug on the omp line: the roster service answered
|
|
12
|
+
* agentPresets/list 200, yet nothing drove the per-session projection the
|
|
13
|
+
* chip gates on).
|
|
14
|
+
*
|
|
15
|
+
* Same fold as upstream (packages/preset/agent-presets/src/session.ts):
|
|
16
|
+
* initialized from the creation header, advanced by `agent-preset/selected`
|
|
17
|
+
* events, which this adapter synthesizes (`"codex"`) on every session it
|
|
18
|
+
* prepares. Registered under the provider's own fiber via
|
|
19
|
+
* `ctx.inject(['sessionProjections'])`, so the key lives exactly as long as
|
|
20
|
+
* the provider is mounted.
|
|
21
|
+
*/
|
|
22
|
+
import { z } from "zod";
|
|
23
|
+
const agentPresetSchema = z.union([z.string(), z.null()]);
|
|
24
|
+
/** Current Session preset, initialized from its header and advanced by selection events. */
|
|
25
|
+
export const codexAgentPresetProjection = {
|
|
26
|
+
key: "agentPreset",
|
|
27
|
+
stateSchema: agentPresetSchema,
|
|
28
|
+
// adapter-codex is single-preset: default every session to "codex" so the
|
|
29
|
+
// header chip resolves for scan-native (never-resumed) sessions too,
|
|
30
|
+
// instead of only those whose log carries an `agent-preset/selected` event.
|
|
31
|
+
init: (header) => header.agentPreset ?? "codex",
|
|
32
|
+
apply: (state, event) => event.type === "agent-preset/selected"
|
|
33
|
+
? event.data.agentPreset
|
|
34
|
+
: state,
|
|
35
|
+
wire: { viewSchema: agentPresetSchema, view: (state) => state },
|
|
36
|
+
stateVersion: 1,
|
|
37
|
+
};
|