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,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `BridgeStore` — the bridge's centralized session index over SQLite.
|
|
3
|
+
*
|
|
4
|
+
* Owns the schema, the query interface (list / byDshId / byOmpId / byFile /
|
|
5
|
+
* upsert / prune + UI setters), and the connection lifecycle (WAL, integrity
|
|
6
|
+
* check). The driver behind it is abstracted by `./adapter.js`; this class
|
|
7
|
+
* only talks to the normalized `SqliteDb` surface.
|
|
8
|
+
*/
|
|
9
|
+
import { openDb } from "./adapter.js";
|
|
10
|
+
import { SCHEMA_SQL } from "./schema.js";
|
|
11
|
+
/** The fallback workspace id for sessions whose cwd is missing/corrupt (D8). */
|
|
12
|
+
export const UNGROUPED_WORKSPACE = "ungrouped";
|
|
13
|
+
const SESSIONS_COLUMNS = [
|
|
14
|
+
"omp_session_id",
|
|
15
|
+
"dsh_session_id",
|
|
16
|
+
"session_file",
|
|
17
|
+
"cwd",
|
|
18
|
+
"title",
|
|
19
|
+
"created_at",
|
|
20
|
+
"last_modified_at",
|
|
21
|
+
"transcript_size",
|
|
22
|
+
"model_provider",
|
|
23
|
+
"model_id",
|
|
24
|
+
"agent_preset",
|
|
25
|
+
"permission_preset",
|
|
26
|
+
"forked_from",
|
|
27
|
+
"archived",
|
|
28
|
+
"last_visited_at",
|
|
29
|
+
];
|
|
30
|
+
const UPSERT_SQL = `INSERT INTO sessions (${SESSIONS_COLUMNS.join(", ")})
|
|
31
|
+
VALUES (${SESSIONS_COLUMNS.map(() => "?").join(", ")})
|
|
32
|
+
ON CONFLICT(omp_session_id) DO UPDATE SET
|
|
33
|
+
dsh_session_id = excluded.dsh_session_id,
|
|
34
|
+
session_file = excluded.session_file,
|
|
35
|
+
cwd = excluded.cwd,
|
|
36
|
+
title = excluded.title,
|
|
37
|
+
created_at = excluded.created_at,
|
|
38
|
+
last_modified_at = excluded.last_modified_at,
|
|
39
|
+
transcript_size = excluded.transcript_size,
|
|
40
|
+
model_provider = excluded.model_provider,
|
|
41
|
+
model_id = excluded.model_id,
|
|
42
|
+
agent_preset = excluded.agent_preset,
|
|
43
|
+
permission_preset = excluded.permission_preset,
|
|
44
|
+
forked_from = excluded.forked_from,
|
|
45
|
+
archived = excluded.archived,
|
|
46
|
+
last_visited_at = excluded.last_visited_at`;
|
|
47
|
+
/** Parse a stored JSON array, failing soft to `undefined`. */
|
|
48
|
+
function parseOrder(raw) {
|
|
49
|
+
if (raw === null)
|
|
50
|
+
return undefined;
|
|
51
|
+
try {
|
|
52
|
+
const value = JSON.parse(raw);
|
|
53
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string") ? value : undefined;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** Order a flat row list per workspace under manual mode (D9). */
|
|
60
|
+
function orderManual(rows, workspaces) {
|
|
61
|
+
const byCwd = new Map();
|
|
62
|
+
for (const row of rows) {
|
|
63
|
+
const key = row.cwd ?? UNGROUPED_WORKSPACE;
|
|
64
|
+
const group = byCwd.get(key);
|
|
65
|
+
if (group === undefined)
|
|
66
|
+
byCwd.set(key, [row]);
|
|
67
|
+
else
|
|
68
|
+
group.push(row);
|
|
69
|
+
}
|
|
70
|
+
const orderFor = new Map(workspaces.map((w) => [w.workspace_id, parseOrder(w.manual_order)]));
|
|
71
|
+
const ordered = [];
|
|
72
|
+
for (const [cwd, group] of byCwd) {
|
|
73
|
+
const manual = orderFor.get(cwd);
|
|
74
|
+
if (manual !== undefined && manual.length > 0) {
|
|
75
|
+
const rank = new Map(manual.map((id, index) => [id, index]));
|
|
76
|
+
group.sort((a, b) => {
|
|
77
|
+
const ai = rank.get(a.dsh_session_id) ?? Number.MAX_SAFE_INTEGER;
|
|
78
|
+
const bi = rank.get(b.dsh_session_id) ?? Number.MAX_SAFE_INTEGER;
|
|
79
|
+
return ai !== bi ? ai - bi : b.last_modified_at - a.last_modified_at;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
group.sort((a, b) => b.last_modified_at - a.last_modified_at);
|
|
84
|
+
}
|
|
85
|
+
ordered.push(...group);
|
|
86
|
+
}
|
|
87
|
+
return ordered;
|
|
88
|
+
}
|
|
89
|
+
export class BridgeStore {
|
|
90
|
+
db;
|
|
91
|
+
constructor(db) {
|
|
92
|
+
this.db = db;
|
|
93
|
+
}
|
|
94
|
+
/** Open (WAL + schema + integrity check). Throws on a corrupt/invalid database. */
|
|
95
|
+
static open(path) {
|
|
96
|
+
const db = openDb(path);
|
|
97
|
+
db.exec("PRAGMA journal_mode=WAL");
|
|
98
|
+
db.exec(SCHEMA_SQL);
|
|
99
|
+
const check = db.prepare("PRAGMA integrity_check").all();
|
|
100
|
+
const ok = check.length === 1 && check[0]?.["integrity_check"] === "ok";
|
|
101
|
+
if (!ok) {
|
|
102
|
+
db.close();
|
|
103
|
+
throw new Error(`bridge-store integrity check failed: ${JSON.stringify(check)}`);
|
|
104
|
+
}
|
|
105
|
+
return new BridgeStore(db);
|
|
106
|
+
}
|
|
107
|
+
close() {
|
|
108
|
+
this.db.close();
|
|
109
|
+
}
|
|
110
|
+
all(sql, ...params) {
|
|
111
|
+
return this.db.prepare(sql).all(...params);
|
|
112
|
+
}
|
|
113
|
+
get(sql, ...params) {
|
|
114
|
+
return this.db.prepare(sql).get(...params);
|
|
115
|
+
}
|
|
116
|
+
run(sql, ...params) {
|
|
117
|
+
this.db.prepare(sql).run(...params);
|
|
118
|
+
}
|
|
119
|
+
/** All session rows, default `last_modified_at DESC`, manual mode per-workspace order. */
|
|
120
|
+
list() {
|
|
121
|
+
const rows = this.all("SELECT * FROM sessions ORDER BY last_modified_at DESC");
|
|
122
|
+
if (this.getUiState("sort_mode") !== "manual")
|
|
123
|
+
return rows;
|
|
124
|
+
return orderManual(rows, this.workspaces());
|
|
125
|
+
}
|
|
126
|
+
/** All workspace rows. */
|
|
127
|
+
workspaces() {
|
|
128
|
+
return this.all("SELECT * FROM workspaces");
|
|
129
|
+
}
|
|
130
|
+
byDshId(dshSessionId) {
|
|
131
|
+
return this.get("SELECT * FROM sessions WHERE dsh_session_id = ?", dshSessionId);
|
|
132
|
+
}
|
|
133
|
+
byOmpId(ompSessionId) {
|
|
134
|
+
return this.get("SELECT * FROM sessions WHERE omp_session_id = ?", ompSessionId);
|
|
135
|
+
}
|
|
136
|
+
byFile(sessionFile) {
|
|
137
|
+
return this.get("SELECT * FROM sessions WHERE session_file = ?", sessionFile);
|
|
138
|
+
}
|
|
139
|
+
/** Full-row upsert, keyed by `omp_session_id`. */
|
|
140
|
+
upsert(row) {
|
|
141
|
+
this.run(UPSERT_SQL, row.omp_session_id, row.dsh_session_id, row.session_file, row.cwd, row.title, row.created_at, row.last_modified_at, row.transcript_size, row.model_provider, row.model_id, row.agent_preset, row.permission_preset, row.forked_from, row.archived, row.last_visited_at);
|
|
142
|
+
}
|
|
143
|
+
/** Drop every row whose session file is not in `keepFiles`. */
|
|
144
|
+
prune(keepFiles) {
|
|
145
|
+
const files = this.all("SELECT session_file FROM sessions");
|
|
146
|
+
const del = this.db.prepare("DELETE FROM sessions WHERE session_file = ?");
|
|
147
|
+
for (const { session_file } of files) {
|
|
148
|
+
if (!keepFiles.has(session_file))
|
|
149
|
+
del.run(session_file);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/** Migration only: overwrite the DSH pairing for one OMP session. */
|
|
153
|
+
setDshId(ompSessionId, dshSessionId) {
|
|
154
|
+
this.run("UPDATE sessions SET dsh_session_id = ? WHERE omp_session_id = ?", dshSessionId, ompSessionId);
|
|
155
|
+
}
|
|
156
|
+
setPermissionPreset(ompSessionId, preset) {
|
|
157
|
+
this.run("UPDATE sessions SET permission_preset = ? WHERE omp_session_id = ?", preset, ompSessionId);
|
|
158
|
+
}
|
|
159
|
+
setArchived(dshSessionId, archived) {
|
|
160
|
+
this.run("UPDATE sessions SET archived = ? WHERE dsh_session_id = ?", archived ? 1 : 0, dshSessionId);
|
|
161
|
+
}
|
|
162
|
+
setForkedFrom(dshSessionId, forkedFrom) {
|
|
163
|
+
this.run("UPDATE sessions SET forked_from = ? WHERE dsh_session_id = ?", forkedFrom, dshSessionId);
|
|
164
|
+
}
|
|
165
|
+
touchVisited(dshSessionId, at) {
|
|
166
|
+
this.run("UPDATE sessions SET last_visited_at = ? WHERE dsh_session_id = ?", at, dshSessionId);
|
|
167
|
+
}
|
|
168
|
+
/** Persist a workspace's manual ordering (upsert the workspace row first). */
|
|
169
|
+
setWorkspaceOrder(workspaceId, order) {
|
|
170
|
+
this.run("INSERT INTO workspaces (workspace_id, title, manual_order) VALUES (?, NULL, ?) ON CONFLICT(workspace_id) DO UPDATE SET manual_order = excluded.manual_order", workspaceId, JSON.stringify(order));
|
|
171
|
+
}
|
|
172
|
+
/** Ensure the `ungrouped` sentinel workspace row exists (D8). */
|
|
173
|
+
ensureUngroupedWorkspace() {
|
|
174
|
+
this.run("INSERT INTO workspaces (workspace_id, title, manual_order) VALUES (?, NULL, NULL) ON CONFLICT(workspace_id) DO NOTHING", UNGROUPED_WORKSPACE);
|
|
175
|
+
}
|
|
176
|
+
getUiState(key) {
|
|
177
|
+
return this.get("SELECT value FROM ui_state WHERE key = ?", key)?.value ?? undefined;
|
|
178
|
+
}
|
|
179
|
+
setUiState(key, value) {
|
|
180
|
+
this.run("INSERT INTO ui_state (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value", key, value);
|
|
181
|
+
}
|
|
182
|
+
/** The tracked OMP default model (`modelRoles.default`), or undefined when unset. */
|
|
183
|
+
getOmpDefaultModel() {
|
|
184
|
+
const raw = this.getUiState("omp_default_model");
|
|
185
|
+
if (raw === undefined)
|
|
186
|
+
return undefined;
|
|
187
|
+
try {
|
|
188
|
+
const parsed = JSON.parse(raw);
|
|
189
|
+
if (typeof parsed.provider === "string" && typeof parsed.model === "string") {
|
|
190
|
+
return { provider: parsed.provider, model: parsed.model };
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
/** Persist the tracked OMP default model. */
|
|
199
|
+
setOmpDefaultModel(provider, model) {
|
|
200
|
+
this.setUiState("omp_default_model", JSON.stringify({ provider, model }));
|
|
201
|
+
}
|
|
202
|
+
/** Event-driven model update after a live model switch (D5.3). */
|
|
203
|
+
updateModel(dshSessionId, provider, model) {
|
|
204
|
+
this.run("UPDATE sessions SET model_provider = ?, model_id = ? WHERE dsh_session_id = ?", provider, model, dshSessionId);
|
|
205
|
+
}
|
|
206
|
+
/** Event-driven stat refresh on teardown/cold transition (D5.3). */
|
|
207
|
+
updateStat(dshSessionId, size, mtimeMs) {
|
|
208
|
+
this.run("UPDATE sessions SET transcript_size = ?, last_modified_at = ? WHERE dsh_session_id = ?", size, mtimeMs, dshSessionId);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge store lifecycle: DB path resolution, open/close, and the module
|
|
3
|
+
* singleton the rest of the bridge reaches through {@link getBridgeStore}.
|
|
4
|
+
*/
|
|
5
|
+
import { mkdirSync } from "node:fs";
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
import { dirname, join, resolve } from "node:path";
|
|
8
|
+
import { OMP_SESSIONS_ROOT } from "../omp-store.js";
|
|
9
|
+
import { BridgeStore } from "./db.js";
|
|
10
|
+
import { prepareIndex } from "./reconcile.js";
|
|
11
|
+
let store;
|
|
12
|
+
/**
|
|
13
|
+
* Resolve the index path: `OMP_BRIDGE_DB` (absolute) or the profile's own
|
|
14
|
+
* state dir (`$DSH_HOME/bridge-store.sqlite`). Throws on a relative/invalid
|
|
15
|
+
* path, or one that falls inside OMP's native store (D7: OMP scans that dir
|
|
16
|
+
* and would treat the `.sqlite` as a foreign session file).
|
|
17
|
+
*
|
|
18
|
+
* `$DSH_HOME` also anchors dsh's home-level singleton settings document
|
|
19
|
+
* (`$DSH_HOME/settings.yaml`), which this bridge never reads; the profile
|
|
20
|
+
* isolates that document via its own `settings.path` patch (settings-under-
|
|
21
|
+
* profile), independent of this store.
|
|
22
|
+
*/
|
|
23
|
+
export function resolveBridgeDbPath() {
|
|
24
|
+
const explicit = process.env.OMP_BRIDGE_DB;
|
|
25
|
+
if (explicit !== undefined && explicit !== "" && !explicit.startsWith("/")) {
|
|
26
|
+
throw new Error(`OMP_BRIDGE_DB must be an absolute path: ${explicit}`);
|
|
27
|
+
}
|
|
28
|
+
const path = resolve(explicit !== undefined && explicit !== ""
|
|
29
|
+
? explicit
|
|
30
|
+
: join(process.env.DSH_HOME ?? join(homedir(), ".omp", "dsh"), "bridge-store.sqlite"));
|
|
31
|
+
const root = resolve(OMP_SESSIONS_ROOT);
|
|
32
|
+
if (path === root || path.startsWith(`${root}/`)) {
|
|
33
|
+
throw new Error(`bridge store must not live under OMP_SESSIONS_ROOT (${root}): ${path}`);
|
|
34
|
+
}
|
|
35
|
+
return path;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Open the store, run the boot warm pass, then the one-time migration. The
|
|
39
|
+
* warm pass must complete before the bridge serves traffic (D5.1): the first
|
|
40
|
+
* landing already reads real titles/models from the index.
|
|
41
|
+
*/
|
|
42
|
+
export function initBridgeStore(path) {
|
|
43
|
+
if (store !== undefined)
|
|
44
|
+
return store;
|
|
45
|
+
const dbPath = path ?? resolveBridgeDbPath();
|
|
46
|
+
mkdirSync(dirname(dbPath), { recursive: true });
|
|
47
|
+
store = BridgeStore.open(dbPath);
|
|
48
|
+
prepareIndex(store);
|
|
49
|
+
return store;
|
|
50
|
+
}
|
|
51
|
+
/** The live store, or undefined when not initialized (tests, no DB). */
|
|
52
|
+
export function getBridgeStore() {
|
|
53
|
+
return store;
|
|
54
|
+
}
|
|
55
|
+
/** Close and release the singleton (teardown). */
|
|
56
|
+
export function closeBridgeStore() {
|
|
57
|
+
store?.close();
|
|
58
|
+
store = undefined;
|
|
59
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge session index — dsh↔omp identity mapping only.
|
|
3
|
+
*
|
|
4
|
+
* dsh-shape-session-log: the index is no longer a mirror of OMP's native
|
|
5
|
+
* store. The dsh session log is of record, and this index holds ONLY the
|
|
6
|
+
* `dsh_session_id ↔ omp_session_id ↔ session_file` mapping written at first
|
|
7
|
+
* spawn (plus the workspace-attach source for the UI). The union-era OMP-store
|
|
8
|
+
* scan, stat reconcile, prune, and the webui.json migration are all retired:
|
|
9
|
+
* pruning by an OMP-store scan deleted these mappings and broke resume.
|
|
10
|
+
*/
|
|
11
|
+
import { statSync } from "node:fs";
|
|
12
|
+
/**
|
|
13
|
+
* Boot the index before anything reads session state: ensure the ungrouped
|
|
14
|
+
* fallback workspace exists. Idempotent.
|
|
15
|
+
*/
|
|
16
|
+
export function prepareIndex(store) {
|
|
17
|
+
store.ensureUngroupedWorkspace();
|
|
18
|
+
}
|
|
19
|
+
/** Event-driven INSERT at first spawn: record the identity mapping + preset. */
|
|
20
|
+
export function upsertCreated(store, input) {
|
|
21
|
+
let size = 0;
|
|
22
|
+
let mtimeMs = Date.now();
|
|
23
|
+
try {
|
|
24
|
+
const stat = statSync(input.sessionFile);
|
|
25
|
+
size = stat.size;
|
|
26
|
+
mtimeMs = stat.mtimeMs;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// File not yet flushable — the next boot pass fills stat lazily.
|
|
30
|
+
}
|
|
31
|
+
store.upsert({
|
|
32
|
+
omp_session_id: input.ompSessionId,
|
|
33
|
+
dsh_session_id: input.dshSessionId,
|
|
34
|
+
session_file: input.sessionFile,
|
|
35
|
+
cwd: input.cwd ?? null,
|
|
36
|
+
title: null,
|
|
37
|
+
// v2 header identity: the row's created_at MUST equal the live session
|
|
38
|
+
// header's createdAt, or session-query's cross-observation header check
|
|
39
|
+
// (assertSessionHeadersCompatible) throws SOURCE_CONFLICT for the id.
|
|
40
|
+
created_at: input.createdAt ?? Date.now(),
|
|
41
|
+
last_modified_at: mtimeMs,
|
|
42
|
+
transcript_size: size,
|
|
43
|
+
model_provider: null,
|
|
44
|
+
model_id: null,
|
|
45
|
+
agent_preset: "omp",
|
|
46
|
+
permission_preset: input.preset ?? null,
|
|
47
|
+
forked_from: null,
|
|
48
|
+
archived: 0,
|
|
49
|
+
last_visited_at: null,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite schema for the bridge's centralized session index.
|
|
3
|
+
*
|
|
4
|
+
* One `sessions` table carries mapping + metadata + UI fields 1:1 (D2); two
|
|
5
|
+
* aux tables carry workspace grouping/ordering and singleton UI state. The
|
|
6
|
+
* schema is idempotent (`IF NOT EXISTS`), applied on every open.
|
|
7
|
+
*/
|
|
8
|
+
export const SCHEMA_SQL = `
|
|
9
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
10
|
+
omp_session_id TEXT PRIMARY KEY, -- OMP uuidv7 (session header id)
|
|
11
|
+
dsh_session_id TEXT NOT NULL UNIQUE, -- DSH-facing id (real or derived)
|
|
12
|
+
session_file TEXT NOT NULL UNIQUE, -- transcript absolute path
|
|
13
|
+
cwd TEXT, -- working directory (NULL → ungrouped)
|
|
14
|
+
title TEXT, -- display title (latest wins)
|
|
15
|
+
created_at INTEGER NOT NULL, -- epoch ms
|
|
16
|
+
last_modified_at INTEGER NOT NULL, -- transcript mtime (default sort key)
|
|
17
|
+
transcript_size INTEGER NOT NULL, -- bytes (reconcile change detection)
|
|
18
|
+
model_provider TEXT, -- last model provider (complete, D6)
|
|
19
|
+
model_id TEXT, -- last model id
|
|
20
|
+
agent_preset TEXT, -- DSH composition id (always "omp")
|
|
21
|
+
permission_preset TEXT, -- approval preset (danger-full-access/workspace-write/read-only)
|
|
22
|
+
forked_from TEXT, -- fork source dsh_session_id (NULL = not a fork)
|
|
23
|
+
archived INTEGER NOT NULL DEFAULT 0, -- archive flag (default list excludes)
|
|
24
|
+
last_visited_at INTEGER -- last view epoch ms (refresh priority, D10)
|
|
25
|
+
);
|
|
26
|
+
CREATE INDEX IF NOT EXISTS sessions_cwd ON sessions(cwd);
|
|
27
|
+
CREATE INDEX IF NOT EXISTS sessions_mtime ON sessions(last_modified_at DESC);
|
|
28
|
+
CREATE INDEX IF NOT EXISTS sessions_visit ON sessions(last_visited_at DESC);
|
|
29
|
+
|
|
30
|
+
CREATE TABLE IF NOT EXISTS workspaces (
|
|
31
|
+
workspace_id TEXT PRIMARY KEY, -- = cwd path; 'ungrouped' sentinel (D8)
|
|
32
|
+
title TEXT,
|
|
33
|
+
manual_order TEXT -- JSON array of dsh_session_id (manual mode, D9)
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
CREATE TABLE IF NOT EXISTS ui_state (
|
|
37
|
+
key TEXT PRIMARY KEY,
|
|
38
|
+
value TEXT -- e.g. sort_mode = 'last_updated' | 'manual'
|
|
39
|
+
);
|
|
40
|
+
`;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* aw.agent-adapter-omp — the adapter's world plugin (spec S2): activation IS
|
|
3
|
+
* roster membership.
|
|
4
|
+
*
|
|
5
|
+
* AW-B (DL1–DL5): the world is spawned as a sibling ROOT context with NO
|
|
6
|
+
* listener (see `worldMountPatches`) and mounts itself under `/<label>` through
|
|
7
|
+
* the hub:
|
|
8
|
+
*
|
|
9
|
+
* 1. ctx0 publishes the host mount (real webServer instance) before spawn.
|
|
10
|
+
* 2. the world's entry row provides the virtual `webServer` in the world root,
|
|
11
|
+
* translating every route its plugins declare into `/<label>+path`.
|
|
12
|
+
* 3. the hub's carrier answers `/<label>/api` + `/<label>/api/remote.mux` in
|
|
13
|
+
* ctx0's auth domain and dispatches into this world's gateway instance.
|
|
14
|
+
*/
|
|
15
|
+
import { mkdirSync } from 'node:fs';
|
|
16
|
+
import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
import { spawnWorld, registerAgent, setReady, registerForeignTarget, registerHostMount, mountWorld, worldMountPatches, provisionWorldProfile, } from '../../agent-hub/dist/index.js';
|
|
19
|
+
export const name = 'aw.agent-adapter-omp';
|
|
20
|
+
/** Runtime key — also the mount label. */
|
|
21
|
+
const KEY = 'omp';
|
|
22
|
+
const ANCHOR = process.env.SUPERD_DSH_ANCHOR
|
|
23
|
+
?? '/home/u1/.local/lib/node_modules/@deepseek-ai/dsh/package.json';
|
|
24
|
+
export function apply(ctx) {
|
|
25
|
+
registerAgent({ key: KEY, label: 'OMP', ready: false });
|
|
26
|
+
// Harness resolver (env → ~/.dsh): a consumer install has no DSH_HOME and
|
|
27
|
+
// no cwd contract — deriving the world root from cwd spawns worlds into a
|
|
28
|
+
// garbage tree or throws inside apply() and kills the whole host boot.
|
|
29
|
+
const root = resolveDshHome();
|
|
30
|
+
// S7 nested home: every runtime owns one root under `agents/<label>`, and the
|
|
31
|
+
// world's DSH data lives INSIDE it (sessions / settings / workspaces), so no
|
|
32
|
+
// two runtimes ever share a session list. The foreign app's own store sits in
|
|
33
|
+
// the same root (adapter-owned app home, see AW-C0).
|
|
34
|
+
const worldHome = join(root, 'agents', KEY);
|
|
35
|
+
mkdirSync(join(worldHome, 'profiles'), { recursive: true });
|
|
36
|
+
// Publish story: a foreign install has no line bootstrap, so the nested
|
|
37
|
+
// profile + @pgmi-builds links are provisioned HERE (idempotent; dev lines
|
|
38
|
+
// keep parity — smoke.mjs wrote the same layout, and AW_BARE_BASE still
|
|
39
|
+
// overrides the resolved base).
|
|
40
|
+
const bareBase = provisionWorldProfile({
|
|
41
|
+
key: KEY,
|
|
42
|
+
adapterPkg: '@pgmi-builds/agent-adapter-omp',
|
|
43
|
+
worldHome,
|
|
44
|
+
});
|
|
45
|
+
const world = (async () => {
|
|
46
|
+
// Wait for ctx0's listening stack: the mount needs the real webServer and
|
|
47
|
+
// the browser-trust fence from the live connection service.
|
|
48
|
+
const host = await new Promise((resolve) => {
|
|
49
|
+
ctx.inject(['webServer', 'connection'], (hostCtx) => { resolve(hostCtx); });
|
|
50
|
+
});
|
|
51
|
+
const real = host['webServer'];
|
|
52
|
+
const connection = host['connection'];
|
|
53
|
+
const dropHostMount = registerHostMount({
|
|
54
|
+
key: KEY,
|
|
55
|
+
labelPath: `/${KEY}`,
|
|
56
|
+
real,
|
|
57
|
+
// ctx0's index gate, applied by the mount to this world's pages and assets:
|
|
58
|
+
// the world is reached in-ctx and owns no listener, so ctx0 decides auth.
|
|
59
|
+
authorize: (req, res) => connection.authorizeIndex(req, res),
|
|
60
|
+
});
|
|
61
|
+
const ctxW = await spawnWorld({
|
|
62
|
+
appName: `aw-${KEY}`,
|
|
63
|
+
profileName: 'web',
|
|
64
|
+
installAnchor: ANCHOR,
|
|
65
|
+
home: worldHome,
|
|
66
|
+
dataHome: worldHome,
|
|
67
|
+
// Adapter-local bundle names resolve from the profile's own
|
|
68
|
+
// node_modules (heal owns @deepseek-ai there; @pgmi-builds links are
|
|
69
|
+
// provisioned by provisionWorldProfile above, or by the line bootstrap
|
|
70
|
+
// when the adapter package was not resolvable — env wins over both).
|
|
71
|
+
bareModuleBaseUrl: process.env.AW_BARE_BASE ?? bareBase ?? undefined,
|
|
72
|
+
extraPatches: worldMountPatches(KEY),
|
|
73
|
+
});
|
|
74
|
+
const gateway = ctxW.get('typertGateway');
|
|
75
|
+
registerForeignTarget({ key: KEY, gateway });
|
|
76
|
+
const mounted = mountWorld({
|
|
77
|
+
real,
|
|
78
|
+
label: KEY,
|
|
79
|
+
gateway,
|
|
80
|
+
requestRejection: (request) => connection
|
|
81
|
+
.requestRejection(request),
|
|
82
|
+
});
|
|
83
|
+
setReady(KEY, true);
|
|
84
|
+
ctx.effect?.(() => () => {
|
|
85
|
+
mounted.dispose();
|
|
86
|
+
dropHostMount();
|
|
87
|
+
}, `aw.agent-adapter-${KEY}: mount`);
|
|
88
|
+
return ctxW;
|
|
89
|
+
})().catch((cause) => {
|
|
90
|
+
// A failed world degrades to a not-ready roster row; a rejected provided
|
|
91
|
+
// promise would fail the entire host boot (loader entry apply).
|
|
92
|
+
console.error(`[super-dsh] ${KEY} world failed:`, cause);
|
|
93
|
+
return null;
|
|
94
|
+
});
|
|
95
|
+
ctx.provide(`aw.world.${KEY}`, world);
|
|
96
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pgmi-builds/agent-adapter-omp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "OMP Web: OMP provider for DeepSeek Harness (dsh) — an AgentFactory that embeds OMP via the @oh-my-pi/pi-coding-agent SDK (Bun sidecar) and bridges OMP's session surface into Dash Agent/Session contracts.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./dist/index.js",
|
|
8
|
+
"./client": "./lib/client/index.js",
|
|
9
|
+
"./world": "./dist/world-plugin.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"lib",
|
|
14
|
+
"sidecar",
|
|
15
|
+
"cordis.patch.yml",
|
|
16
|
+
"README.md"
|
|
17
|
+
],
|
|
18
|
+
"dsh": {
|
|
19
|
+
"bundle": {
|
|
20
|
+
"patch": "./cordis.patch.yml"
|
|
21
|
+
},
|
|
22
|
+
"client": {
|
|
23
|
+
"inject": [
|
|
24
|
+
"@deepseek-ai/dsh-client-ui-layout"
|
|
25
|
+
],
|
|
26
|
+
"platform": "web"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc -p tsconfig.json",
|
|
31
|
+
"build-client": "tsx scripts/build-client.ts",
|
|
32
|
+
"test": "node --test test/*.test.mjs"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@oh-my-pi/pi-coding-agent": "^18.1.14",
|
|
36
|
+
"bun": "^1.4.2",
|
|
37
|
+
"@pgmi-builds/agent-hub": "file:../agent-hub"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
41
|
+
"@deepseek-ai/dsh-agent": "0.1.5-rc.2",
|
|
42
|
+
"@deepseek-ai/dsh-agent-presets": "0.1.5-rc.2",
|
|
43
|
+
"@deepseek-ai/dsh-llm": "0.1.5-rc.2",
|
|
44
|
+
"@deepseek-ai/dsh-scope": "0.1.5-rc.2",
|
|
45
|
+
"@deepseek-ai/dsh-session": "0.1.5-rc.2",
|
|
46
|
+
"@deepseek-ai/dsh-session-persistence": "0.1.5-rc.2",
|
|
47
|
+
"@deepseek-ai/dsh-typert-protocol": "0.1.5-rc.2",
|
|
48
|
+
"@deepseek-ai/dsh-home-paths": "0.1.5-rc.2"
|
|
49
|
+
},
|
|
50
|
+
"peerDependenciesMeta": {
|
|
51
|
+
"@deepseek-ai/cordis": {
|
|
52
|
+
"optional": true
|
|
53
|
+
},
|
|
54
|
+
"@deepseek-ai/dsh-agent": {
|
|
55
|
+
"optional": true
|
|
56
|
+
},
|
|
57
|
+
"@deepseek-ai/dsh-agent-presets": {
|
|
58
|
+
"optional": true
|
|
59
|
+
},
|
|
60
|
+
"@deepseek-ai/dsh-llm": {
|
|
61
|
+
"optional": true
|
|
62
|
+
},
|
|
63
|
+
"@deepseek-ai/dsh-scope": {
|
|
64
|
+
"optional": true
|
|
65
|
+
},
|
|
66
|
+
"@deepseek-ai/dsh-session": {
|
|
67
|
+
"optional": true
|
|
68
|
+
},
|
|
69
|
+
"@deepseek-ai/dsh-session-persistence": {
|
|
70
|
+
"optional": true
|
|
71
|
+
},
|
|
72
|
+
"@deepseek-ai/dsh-typert-protocol": {
|
|
73
|
+
"optional": true
|
|
74
|
+
},
|
|
75
|
+
"@deepseek-ai/dsh-home-paths": {
|
|
76
|
+
"optional": true
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"devDependencies": {
|
|
80
|
+
"@types/node": "^26.2.0",
|
|
81
|
+
"tsdown": "^0.15.12",
|
|
82
|
+
"tsx": "^4.23.13",
|
|
83
|
+
"typescript": "^5.6.0"
|
|
84
|
+
},
|
|
85
|
+
"publishConfig": {
|
|
86
|
+
"access": "public"
|
|
87
|
+
},
|
|
88
|
+
"license": "MIT",
|
|
89
|
+
"main": "dist/index.js",
|
|
90
|
+
"repository": {
|
|
91
|
+
"type": "git",
|
|
92
|
+
"url": "git+https://github.com/pgmi-builds/omp-web.git"
|
|
93
|
+
},
|
|
94
|
+
"engines": {
|
|
95
|
+
"node": ">=22.18"
|
|
96
|
+
}
|
|
97
|
+
}
|