super-dsh 0.1.6 → 0.1.10-e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -3
- package/agent-agy/cordis.patch.yml +7 -4
- package/agent-agy/dist/agent.d.ts +12 -11
- package/agent-agy/dist/agy-auth-gateway.d.ts +124 -0
- package/agent-agy/dist/agy-auth-gateway.js +233 -0
- package/agent-agy/dist/agy-auth.d.ts +153 -0
- package/agent-agy/dist/agy-auth.js +179 -0
- package/agent-agy/dist/agy-cli-client.d.ts +39 -9
- package/agent-agy/dist/agy-cli-client.js +82 -18
- package/agent-agy/dist/agy-store.d.ts +8 -0
- package/agent-agy/dist/index.d.ts +46 -16
- package/agent-agy/dist/index.js +82 -56
- package/agent-agy/dist/models.d.ts +70 -0
- package/agent-agy/dist/models.js +102 -3
- package/agent-agy/package.json +7 -5
- package/agent-agy-sdk/cordis.patch.yml +58 -0
- package/agent-agy-sdk/dist/adapter.d.ts +15 -0
- package/agent-agy-sdk/dist/adapter.js +42 -0
- package/agent-agy-sdk/dist/agent-preset-agy.d.ts +27 -0
- package/agent-agy-sdk/dist/agent-preset-agy.js +32 -0
- package/agent-agy-sdk/dist/agent.d.ts +138 -0
- package/agent-agy-sdk/dist/agent.js +494 -0
- package/{agent-agy → agent-agy-sdk}/dist/agy-client.d.ts +26 -6
- package/{agent-agy → agent-agy-sdk}/dist/agy-client.js +43 -25
- package/agent-agy-sdk/dist/agy-sessions.d.ts +25 -0
- package/agent-agy-sdk/dist/agy-sessions.js +107 -0
- package/agent-agy-sdk/dist/agy-store.d.ts +39 -0
- package/agent-agy-sdk/dist/agy-store.js +83 -0
- package/agent-agy-sdk/dist/inbox.d.ts +26 -0
- package/agent-agy-sdk/dist/inbox.js +53 -0
- package/agent-agy-sdk/dist/index.d.ts +93 -0
- package/agent-agy-sdk/dist/index.js +476 -0
- package/agent-agy-sdk/dist/models.d.ts +41 -0
- package/agent-agy-sdk/dist/models.js +74 -0
- package/agent-agy-sdk/dist/sdk-venv.d.ts +76 -0
- package/agent-agy-sdk/dist/sdk-venv.js +173 -0
- package/agent-agy-sdk/dist/world-plugin.d.ts +10 -0
- package/agent-agy-sdk/dist/world-plugin.js +82 -0
- package/agent-agy-sdk/package.json +64 -0
- package/agent-claude/dist/adapter.js +106 -31
- package/agent-claude/dist/agent.js +154 -17
- package/agent-claude/dist/claude-client.js +84 -43
- package/agent-claude/dist/claude-launch.js +101 -0
- package/agent-claude/dist/claude-native-config.js +169 -0
- package/agent-claude/dist/claude-provider-gateway.js +340 -0
- package/agent-claude/dist/claude-providers.js +391 -0
- package/agent-claude/dist/claude-route.js +152 -0
- package/agent-claude/dist/index.js +214 -37
- package/agent-claude/dist/models.js +14 -0
- package/agent-claude/dist/sdk-runtime.js +320 -0
- package/agent-claude/package.json +6 -4
- package/agent-codex/README.md +141 -36
- package/agent-codex/cordis.patch.yml +14 -10
- package/agent-codex/dist/adapter.js +64 -28
- package/agent-codex/dist/agent.js +255 -81
- package/agent-codex/dist/app-server-approvals.js +165 -0
- package/agent-codex/dist/app-server-client.js +723 -0
- package/agent-codex/dist/app-server-events.js +432 -0
- package/agent-codex/dist/app-server-protocol.js +95 -0
- package/agent-codex/dist/app-server-wire.js +299 -0
- package/agent-codex/dist/codex-launch.js +78 -0
- package/agent-codex/dist/codex-native-config.js +392 -0
- package/agent-codex/dist/codex-provider-gateway.js +402 -0
- package/agent-codex/dist/codex-providers.js +376 -0
- package/agent-codex/dist/codex-route.js +86 -0
- package/agent-codex/dist/index.js +256 -115
- package/agent-codex/dist/permission.js +75 -1
- package/agent-codex/package.json +1 -2
- package/agent-codex-sdk/README.md +79 -0
- package/agent-codex-sdk/cordis.patch.yml +81 -0
- package/agent-codex-sdk/dist/adapter.js +91 -0
- package/agent-codex-sdk/dist/agent-preset-codex.js +32 -0
- package/agent-codex-sdk/dist/agent.js +1085 -0
- package/agent-codex-sdk/dist/codex-native-config.js +392 -0
- package/agent-codex-sdk/dist/codex-provider-gateway.js +402 -0
- package/agent-codex-sdk/dist/codex-providers.js +376 -0
- package/agent-codex-sdk/dist/codex-route.js +86 -0
- package/{agent-codex/dist/codex-client.js → agent-codex-sdk/dist/codex-sdk-client.js} +253 -32
- package/agent-codex-sdk/dist/codex-sdk-launch.js +92 -0
- package/agent-codex-sdk/dist/codex-store.js +180 -0
- package/agent-codex-sdk/dist/inbox.js +53 -0
- package/agent-codex-sdk/dist/index.js +739 -0
- package/agent-codex-sdk/dist/permission.js +71 -0
- package/agent-codex-sdk/dist/sdk-runtime.js +307 -0
- package/agent-codex-sdk/dist/session-map.js +167 -0
- package/agent-codex-sdk/dist/world-plugin.js +93 -0
- package/agent-codex-sdk/package.json +79 -0
- package/agent-codex-sdk/scripts/fetch-sdk-runtime.mjs +57 -0
- package/agent-omp/README.md +72 -55
- package/agent-omp/dist/agent.js +36 -11
- package/agent-omp/dist/index.js +92 -52
- package/agent-omp/dist/lazy-rpc.js +11 -4
- package/agent-omp/dist/omp-cli.js +114 -97
- package/agent-omp/dist/omp-store.js +12 -4
- package/agent-omp/dist/permission.js +70 -5
- package/agent-omp/dist/rpc-client.js +409 -0
- package/agent-omp/dist/world-plugin.js +36 -3
- package/agent-omp/package.json +2 -6
- package/agent-omp-sdk/README.md +103 -0
- package/agent-omp-sdk/cordis.patch.yml +108 -0
- package/agent-omp-sdk/dist/adapter.d.ts +21 -0
- package/agent-omp-sdk/dist/adapter.js +36 -0
- package/agent-omp-sdk/dist/agent-preset-omp.d.ts +31 -0
- package/agent-omp-sdk/dist/agent-preset-omp.js +32 -0
- package/agent-omp-sdk/dist/agent.d.ts +64 -0
- package/agent-omp-sdk/dist/agent.js +1178 -0
- package/agent-omp-sdk/dist/discovery.d.ts +57 -0
- package/agent-omp-sdk/dist/discovery.js +116 -0
- package/agent-omp-sdk/dist/inbox.d.ts +28 -0
- package/agent-omp-sdk/dist/inbox.js +53 -0
- package/agent-omp-sdk/dist/index.d.ts +60 -0
- package/agent-omp-sdk/dist/index.js +632 -0
- package/agent-omp-sdk/dist/knobs.d.ts +34 -0
- package/agent-omp-sdk/dist/knobs.js +50 -0
- package/agent-omp-sdk/dist/lazy-rpc.d.ts +98 -0
- package/agent-omp-sdk/dist/lazy-rpc.js +178 -0
- package/agent-omp-sdk/dist/mobile/zoom-guard.d.ts +187 -0
- package/agent-omp-sdk/dist/mobile/zoom-guard.js +294 -0
- package/agent-omp-sdk/dist/mobile-boot.d.ts +49 -0
- package/agent-omp-sdk/dist/mobile-boot.js +38 -0
- package/agent-omp-sdk/dist/models.d.ts +39 -0
- package/agent-omp-sdk/dist/models.js +299 -0
- package/agent-omp-sdk/dist/omp-cli.d.ts +67 -0
- package/agent-omp-sdk/dist/omp-cli.js +164 -0
- package/agent-omp-sdk/dist/omp-disk-discovery.d.ts +27 -0
- package/agent-omp-sdk/dist/omp-disk-discovery.js +178 -0
- package/agent-omp-sdk/dist/omp-executable.d.ts +2 -0
- package/agent-omp-sdk/dist/omp-executable.js +30 -0
- package/agent-omp-sdk/dist/omp-store.d.ts +134 -0
- package/agent-omp-sdk/dist/omp-store.js +526 -0
- package/agent-omp-sdk/dist/pairing.d.ts +7 -0
- package/agent-omp-sdk/dist/pairing.js +45 -0
- package/agent-omp-sdk/dist/permission.d.ts +138 -0
- package/agent-omp-sdk/dist/permission.js +161 -0
- package/agent-omp-sdk/dist/protocol.d.ts +189 -0
- package/agent-omp-sdk/dist/rpc-types.d.ts +77 -0
- package/agent-omp-sdk/dist/rpc-types.js +1 -0
- package/agent-omp-sdk/dist/sdk-client.d.ts +160 -0
- package/{agent-omp → agent-omp-sdk}/dist/sdk-client.js +108 -56
- package/agent-omp-sdk/dist/sdk-runtime.d.ts +211 -0
- package/agent-omp-sdk/dist/sdk-runtime.js +418 -0
- package/agent-omp-sdk/dist/sidecar-client.d.ts +87 -0
- package/agent-omp-sdk/dist/sidecar-client.js +197 -0
- package/agent-omp-sdk/dist/store/adapter.d.ts +17 -0
- package/agent-omp-sdk/dist/store/adapter.js +47 -0
- package/agent-omp-sdk/dist/store/db.d.ts +70 -0
- package/agent-omp-sdk/dist/store/db.js +210 -0
- package/agent-omp-sdk/dist/store/index.d.ts +23 -0
- package/agent-omp-sdk/dist/store/index.js +59 -0
- package/agent-omp-sdk/dist/store/reconcile.d.ts +15 -0
- package/agent-omp-sdk/dist/store/reconcile.js +51 -0
- package/agent-omp-sdk/dist/store/schema.d.ts +8 -0
- package/agent-omp-sdk/dist/store/schema.js +40 -0
- package/agent-omp-sdk/dist/transport.d.ts +49 -0
- package/agent-omp-sdk/dist/transport.js +35 -0
- package/agent-omp-sdk/dist/world-home.d.ts +4 -0
- package/agent-omp-sdk/dist/world-home.js +23 -0
- package/agent-omp-sdk/dist/world-plugin.d.ts +10 -0
- package/agent-omp-sdk/dist/world-plugin.js +137 -0
- package/agent-omp-sdk/package.json +87 -0
- package/client/index.js +1883 -287
- package/cordis.patch.yml +80 -0
- package/package.json +23 -9
- package/superd-hub/dist/a2a-delivery.d.ts +189 -0
- package/superd-hub/dist/a2a-delivery.js +240 -0
- package/superd-hub/dist/a2a-host.d.ts +76 -0
- package/superd-hub/dist/a2a-host.js +145 -0
- package/superd-hub/dist/a2a-ingest.d.ts +31 -0
- package/superd-hub/dist/a2a-ingest.js +636 -0
- package/superd-hub/dist/a2a-log.d.ts +17 -0
- package/superd-hub/dist/a2a-log.js +42 -0
- package/superd-hub/dist/a2a-row.d.ts +65 -0
- package/superd-hub/dist/a2a-row.js +162 -0
- package/superd-hub/dist/a2a-service.d.ts +357 -0
- package/superd-hub/dist/a2a-service.js +980 -0
- package/superd-hub/dist/a2a-wire.d.ts +220 -0
- package/superd-hub/dist/a2a-wire.js +305 -0
- package/superd-hub/dist/agent-ids.d.ts +10 -0
- package/superd-hub/dist/agent-ids.js +68 -0
- package/superd-hub/dist/agent-roster.d.ts +29 -18
- package/superd-hub/dist/agent-roster.js +54 -15
- package/superd-hub/dist/agent-status.d.ts +119 -0
- package/superd-hub/dist/agent-status.js +386 -0
- package/superd-hub/dist/client/agent-settings.d.ts +210 -0
- package/superd-hub/dist/client/agent-settings.js +300 -0
- package/superd-hub/dist/client/mesh-api.d.ts +134 -0
- package/superd-hub/dist/client/mesh-api.js +219 -0
- package/superd-hub/dist/client/mesh-groups.d.ts +63 -0
- package/superd-hub/dist/client/mesh-groups.js +66 -0
- package/superd-hub/dist/client/roster.d.ts +48 -0
- package/superd-hub/dist/client/roster.js +102 -0
- package/superd-hub/dist/client/runtime-settings-api.d.ts +76 -0
- package/superd-hub/dist/client/runtime-settings-api.js +137 -0
- package/superd-hub/dist/client/switch.d.ts +165 -0
- package/superd-hub/dist/client/switch.js +295 -0
- package/superd-hub/dist/exec-pty-channel.d.ts +58 -0
- package/superd-hub/dist/exec-pty-channel.js +162 -0
- package/superd-hub/dist/exec-pty-framing.d.ts +32 -0
- package/superd-hub/dist/exec-pty-framing.js +81 -0
- package/superd-hub/dist/exec-pty-rpc.d.ts +21 -0
- package/superd-hub/dist/exec-pty-rpc.js +128 -0
- package/superd-hub/dist/exec-pty-session.d.ts +76 -0
- package/superd-hub/dist/exec-pty-session.js +234 -0
- package/superd-hub/dist/exec.d.ts +56 -0
- package/superd-hub/dist/exec.js +225 -0
- package/superd-hub/dist/gateway.d.ts +39 -2
- package/superd-hub/dist/gateway.js +62 -3
- package/superd-hub/dist/hub-tool.d.ts +174 -0
- package/superd-hub/dist/hub-tool.js +464 -0
- package/superd-hub/dist/index.d.ts +15 -1
- package/superd-hub/dist/index.js +16 -0
- package/superd-hub/dist/machines.d.ts +45 -0
- package/superd-hub/dist/machines.js +125 -0
- package/superd-hub/dist/mesh-federate.d.ts +138 -0
- package/superd-hub/dist/mesh-federate.js +223 -0
- package/superd-hub/dist/mesh-gateway.d.ts +67 -0
- package/superd-hub/dist/mesh-gateway.js +408 -0
- package/superd-hub/dist/mesh-link.d.ts +274 -0
- package/superd-hub/dist/mesh-link.js +441 -0
- package/superd-hub/dist/mesh-rpc.d.ts +123 -0
- package/superd-hub/dist/mesh-rpc.js +250 -0
- package/superd-hub/dist/mux-client.d.ts +71 -0
- package/superd-hub/dist/mux-client.js +281 -0
- package/superd-hub/dist/pack-version.d.ts +2 -0
- package/superd-hub/dist/pack-version.js +30 -0
- package/superd-hub/dist/roster.d.ts +6 -0
- package/superd-hub/dist/rpc.d.ts +18 -2
- package/superd-hub/dist/rpc.js +34 -4
- package/superd-hub/dist/spawn-world.js +3 -1
- package/superd-hub/dist/switch-core.d.ts +62 -25
- package/superd-hub/dist/switch-core.js +53 -30
- package/superd-hub/dist/system-head.d.ts +22 -12
- package/superd-hub/dist/system-head.js +100 -84
- package/superd-hub/dist/wire-endpoints.d.ts +264 -0
- package/superd-hub/dist/wire-endpoints.js +180 -0
- package/superd-hub/lib/client/index.js +1883 -287
- package/superd-hub/package.json +11 -2
- package/agent-agy/dist/agent-preset-projection.d.ts +0 -31
- package/agent-agy/dist/agent-preset-projection.js +0 -33
- package/agent-claude/dist/agent-preset-projection.js +0 -30
- package/agent-codex/dist/agent-preset-projection.js +0 -37
- package/agent-codex/dist/app-home.js +0 -65
- package/agent-codex/dist/models.js +0 -152
- package/agent-codex/dist/spike.js +0 -46
- package/agent-hermes/dist/agent-preset-projection.js +0 -34
- package/agent-omp/dist/agent-preset-projection.js +0 -36
- package/agent-omp/dist/app-home.js +0 -86
- package/agent-omp/dist/sidecar-client.js +0 -195
- package/agent-pi/dist/agent-preset-projection.js +0 -29
- package/superd-hub/dist/client-shim.d.ts +0 -28
- package/superd-hub/dist/client-shim.js +0 -251
- package/superd-hub/dist/index-pass.d.ts +0 -22
- package/superd-hub/dist/index-pass.js +0 -47
- package/superd-hub/dist/labels.d.ts +0 -45
- package/superd-hub/dist/labels.js +0 -83
- /package/{agent-agy → agent-agy-sdk}/bridge/agy_bridge.py +0 -0
- /package/{agent-agy → agent-agy-sdk}/bridge/gemini_openai_server.mjs +0 -0
- /package/{agent-codex/dist/codex-events.js → agent-codex-sdk/dist/codex-sdk-events.js} +0 -0
- /package/{agent-omp → agent-omp-sdk}/dist/protocol.js +0 -0
- /package/{agent-omp → agent-omp-sdk}/sidecar/main.ts +0 -0
package/agent-omp/dist/index.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* dsh-omp-provider — OMP provider plugin for DeepSeek Harness.
|
|
3
3
|
*
|
|
4
|
-
* Replaces the built-in agent loop with an `AgentFactory` that
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Replaces the built-in agent loop with an `AgentFactory` that spawns
|
|
5
|
+
* `omp --mode rpc` per session (RPC transport: `LazyOmpRpc` → `OmpRpcClient`)
|
|
6
|
+
* and bridges its event stream into the Dash Agent/Session contracts. The OMP
|
|
7
|
+
* runtime is the operator's own `omp` binary — the adapter bundles no OMP copy.
|
|
8
|
+
* Mirrors `@deepseek-ai/dsh-agent-loop`'s creation transaction
|
|
9
9
|
* (prepare → setup → publish) so the session + agent publish as one ordered
|
|
10
10
|
* lifecycle.
|
|
11
11
|
*
|
|
12
12
|
* Phase 3 adds resume:
|
|
13
13
|
* - `createAgent` records the Dash→OMP session identity in `OmpSessionIndex`
|
|
14
14
|
* (OMP owns the transcript, Dash owns the live identity).
|
|
15
|
-
* - `resume` re-attaches the OMP
|
|
15
|
+
* - `resume` re-attaches the OMP session file through the RPC child (`--resume`) and restores
|
|
16
16
|
* the persisted Dash session log (`sessionPersistence.prepare`), so the
|
|
17
17
|
* Web UI's cold listing/history/follow-up path (`session.list`,
|
|
18
18
|
* `session.history`, `session.prompt` → `agents.resume`) reopens the
|
|
@@ -33,7 +33,7 @@ import { readOmpDefaultModelFromConfig, sessionHeaderId } from "./omp-store.js";
|
|
|
33
33
|
import { ompModelRoles, ompSetModelRoles } from "./omp-cli.js";
|
|
34
34
|
import { loadOmpModels, ompProviderIds, refreshOmpModelsCli } from "./models.js";
|
|
35
35
|
import { OMP_NATIVE_HOME, STORAGE_RECONCILE_INTERVAL_MS } from "./knobs.js";
|
|
36
|
-
import { defaultPermissionPreset, envApprovalMode,
|
|
36
|
+
import { defaultPermissionPreset, envApprovalMode, resolveSpawnPermission } from "./permission.js";
|
|
37
37
|
import { closeBridgeStore, getBridgeStore, initBridgeStore } from "./store/index.js";
|
|
38
38
|
import { prepareIndex, upsertCreated } from "./store/reconcile.js";
|
|
39
39
|
import { installMobileBootScript } from "./mobile-boot.js";
|
|
@@ -55,6 +55,28 @@ const trace = (...parts) => {
|
|
|
55
55
|
if (process.env.OMP_TRACE === "1")
|
|
56
56
|
process.stderr.write(`[omp-provider ${Date.now() % 1_000_000}] ${parts.join(" ")}\n`);
|
|
57
57
|
};
|
|
58
|
+
/**
|
|
59
|
+
* Whether any live OMP session currently holds an `omp --mode rpc` child.
|
|
60
|
+
*
|
|
61
|
+
* The reconcile tick used to read the catalog through the shared sidecar's
|
|
62
|
+
* live gate; with the RPC transport the equivalent fact is "a session's
|
|
63
|
+
* transport child exists". `refreshOmpModelsCli()` is a real
|
|
64
|
+
* `omp models --json` subprocess, so this gate is load-bearing (AGENTS.md
|
|
65
|
+
* §五·五: the periodic tick never materializes an `omp`). Sessions count only
|
|
66
|
+
* once they actually spawned — a draft session holds a `LazyOmpRpc` with no
|
|
67
|
+
* child yet, and an idle daemon's tick must pull ZERO children.
|
|
68
|
+
*
|
|
69
|
+
* Exported beside the plugin (the cordis surface itself is unchanged) so the
|
|
70
|
+
* no-sidecar guard can pin this gate's truth table behaviourally — a shape
|
|
71
|
+
* regex alone cannot see an empty input set.
|
|
72
|
+
*/
|
|
73
|
+
export function liveOmpSessionExists(sessions) {
|
|
74
|
+
for (const rpc of sessions) {
|
|
75
|
+
if (rpc.spawned)
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
58
80
|
export class OmpProvider extends Service {
|
|
59
81
|
/**
|
|
60
82
|
* `agentDefaultModel` is load-bearing, not decorative: the harness owns the
|
|
@@ -71,6 +93,12 @@ export class OmpProvider extends Service {
|
|
|
71
93
|
/** Stops the supervisor's follow loop on provider teardown. */
|
|
72
94
|
/** Live RPC-backed agents by dash id, for avoidance hand-off. */
|
|
73
95
|
heldAgents = new Map();
|
|
96
|
+
/**
|
|
97
|
+
* Live RPC wrappers by dash id — the reconcile tick's liveness gate (AGENTS.md
|
|
98
|
+
* §五·五): the catalog refresh runs only while one of these holds an
|
|
99
|
+
* `omp --mode rpc` child (`spawned`). Entries are dropped with the handle.
|
|
100
|
+
*/
|
|
101
|
+
liveSessions = new Set();
|
|
74
102
|
/**
|
|
75
103
|
* Persist the default selection into the settings section.
|
|
76
104
|
*
|
|
@@ -99,9 +127,10 @@ export class OmpProvider extends Service {
|
|
|
99
127
|
super(ctx, "ompProvider");
|
|
100
128
|
this.runtime = { ctx };
|
|
101
129
|
ctx.effect(() => ctx.agents.setFactory(this), "ompProvider.setFactory()");
|
|
102
|
-
// Native-home ruling (2026-09-17): the
|
|
103
|
-
// native OMP home (`~/.omp`; $OMP_HOME overrides in tests) — no
|
|
104
|
-
// bootstrap, no config seeding. The world DSH home only holds DSH
|
|
130
|
+
// Native-home ruling (2026-09-17): the spawned `omp` child runs on the
|
|
131
|
+
// operator's native OMP home (`~/.omp`; $OMP_HOME overrides in tests) — no
|
|
132
|
+
// app-home bootstrap, no config seeding. The world DSH home only holds DSH
|
|
133
|
+
// state.
|
|
105
134
|
this.#registerModelCatalog();
|
|
106
135
|
// Boot the centralized index BEFORE any service reads session state: warm
|
|
107
136
|
// pass + migration, so the first landing sees real titles/models (D5.1).
|
|
@@ -136,9 +165,9 @@ export class OmpProvider extends Service {
|
|
|
136
165
|
*/
|
|
137
166
|
async #syncModelDefaultTick() {
|
|
138
167
|
const service = this.runtime.ctx.get("agentDefaultModel");
|
|
139
|
-
// Source of truth is OMP's config.yml ON DISK, never the
|
|
140
|
-
//
|
|
141
|
-
// `ctx.agentDefaultModel` immediately (archived default-model contract).
|
|
168
|
+
// Source of truth is OMP's config.yml ON DISK, never the live transport:
|
|
169
|
+
// the `omp` child is cold at boot and this tick must publish OMP's default
|
|
170
|
+
// to `ctx.agentDefaultModel` immediately (archived default-model contract).
|
|
142
171
|
const ompParsed = readOmpDefaultModelFromConfig();
|
|
143
172
|
const ompKey = ompParsed === undefined ? undefined : `${ompParsed.provider}/${ompParsed.model}`;
|
|
144
173
|
const dsh = service?.currentSelection?.();
|
|
@@ -205,7 +234,12 @@ export class OmpProvider extends Service {
|
|
|
205
234
|
if (store !== undefined)
|
|
206
235
|
prepareIndex(store);
|
|
207
236
|
void this.#syncModelDefaultTick();
|
|
208
|
-
|
|
237
|
+
// AGENTS.md §五·五 "tick 永不 spawn": the catalog refresh runs a real
|
|
238
|
+
// `omp models --json` subprocess, so it may only fire while an OMP
|
|
239
|
+
// session is live. An idle daemon's tick pulls ZERO children; the
|
|
240
|
+
// on-disk model cache (models.ts) keeps serving the boot catalog.
|
|
241
|
+
if (liveOmpSessionExists(this.liveSessions))
|
|
242
|
+
void refreshOmpModelsCli();
|
|
209
243
|
void this.#attachScannedSessions(registry);
|
|
210
244
|
};
|
|
211
245
|
run();
|
|
@@ -292,16 +326,14 @@ export class OmpProvider extends Service {
|
|
|
292
326
|
if (meta.parentSession !== undefined) {
|
|
293
327
|
throw new Error(`cannot fork session "${meta.parentSession}" onto the OMP provider: OMP has no native session fork`);
|
|
294
328
|
}
|
|
295
|
-
// Approval policy is pinned at OMP launch
|
|
296
|
-
//
|
|
297
|
-
//
|
|
298
|
-
//
|
|
299
|
-
//
|
|
300
|
-
//
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const approvalMode = envMode ?? ompApprovalMode(preset);
|
|
304
|
-
trace(`create id=${id} preset=${preset ?? "none"} approval-mode=${approvalMode}${envMode === undefined ? "" : " (env override)"}`);
|
|
329
|
+
// Approval policy is pinned at OMP launch — but it is NOT frozen here.
|
|
330
|
+
// The Dash session is prepared (and `pinInitialPermission`-stamped) only
|
|
331
|
+
// inside setupAndPublish, AFTER this wrapper exists, and the new-chat
|
|
332
|
+
// `/permission` pick arrives as a later `permission/preset`; the child
|
|
333
|
+
// materializes on the first prompt. The launch flag is therefore resolved
|
|
334
|
+
// at spawn time from the session's own events
|
|
335
|
+
// (`resolveSpawnPermission`), with the composition default as the fallback
|
|
336
|
+
// and OMP_APPROVAL_MODE (headless runs) as the override.
|
|
305
337
|
// Backend takes ZERO part in the UI's browser-local new-session draft:
|
|
306
338
|
// no OMP child, no transcript, no index row, no session events — the
|
|
307
339
|
// session announces blank, exactly like the native agent-loop factory's
|
|
@@ -309,11 +341,13 @@ export class OmpProvider extends Service {
|
|
|
309
341
|
// (LazyOmpRpc); adoptSpawnedChild writes the index row and the
|
|
310
342
|
// supervisor's held baseline at that moment, so a draft abandoned before
|
|
311
343
|
// the first prompt costs and leaves NOTHING server-side.
|
|
312
|
-
const rpc = new LazyOmpRpc(["--approval-mode", approvalMode], cwd);
|
|
313
344
|
const preparation = SessionPreparation.create(loopCtx.sessions.prepare(id, {
|
|
314
345
|
...(options.seed === undefined ? {} : { seed: options.seed }),
|
|
315
346
|
...(meta === undefined ? {} : { meta }),
|
|
316
347
|
}));
|
|
348
|
+
const session = preparation.session;
|
|
349
|
+
const spawnPermission = () => resolveSpawnPermission(session.snapshotEvents(), defaultPermissionPreset(loopCtx), envApprovalMode());
|
|
350
|
+
const rpc = new LazyOmpRpc(() => ["--approval-mode", spawnPermission().approvalMode], cwd);
|
|
317
351
|
// The prepared header is the single source of truth for the row the
|
|
318
352
|
// first-spawn adoption writes: v2 folds header identity across
|
|
319
353
|
// live/listed/loaded observations, so created_at/cwd must be copied from
|
|
@@ -328,13 +362,13 @@ export class OmpProvider extends Service {
|
|
|
328
362
|
? undefined
|
|
329
363
|
: { handle: await persistence.create(header, { inheritedEventCount: preparation.session.inheritedEventCount }) };
|
|
330
364
|
rpc.onSpawned((client) => {
|
|
331
|
-
void adoptSpawnedChild(id, header, client, preset);
|
|
365
|
+
void adoptSpawnedChild(id, header, client, spawnPermission().preset);
|
|
332
366
|
});
|
|
333
367
|
// Nothing can fail from the child here (none exists yet); the publish
|
|
334
368
|
// transaction below unwinds its own half-published state on failure — its
|
|
335
369
|
// catch calls rpc.close(), a no-op until the first prompt spawns.
|
|
336
|
-
const handle = await setupAndPublish(loopCtx, ownerCtx, id, options.agentOptions ?? {}, options.setup,
|
|
337
|
-
return this.registerHeld(id, "", handle);
|
|
370
|
+
const handle = await setupAndPublish(loopCtx, ownerCtx, id, options.agentOptions ?? {}, options.setup, session, rpc, options.parentAgent, "startup", { enterSession: true, preparation, stored });
|
|
371
|
+
return this.registerHeld(id, "", handle, rpc);
|
|
338
372
|
}
|
|
339
373
|
async resume(ownerCtx, options) {
|
|
340
374
|
const loopCtx = this.runtime.ctx;
|
|
@@ -374,14 +408,12 @@ export class OmpProvider extends Service {
|
|
|
374
408
|
const freshOmp = record === undefined;
|
|
375
409
|
const ompFile = record?.ompSessionFile;
|
|
376
410
|
const spawnCwd = validatedCwd(record?.cwd) ?? validatedCwd(stored.header.cwd);
|
|
377
|
-
// Approval mode:
|
|
378
|
-
//
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
trace(`resume id=${id} preset=${preset ?? "none"} approval-mode=${approvalMode}${envMode === undefined ? "" : " (env override)"}${freshOmp ? " fresh-omp" : ""}`);
|
|
384
|
-
// OMP live runtime — ALWAYS LAZY (sidecar-decoupling ruling, both branches).
|
|
411
|
+
// Approval mode: the session's own last `permission/preset` (= what the
|
|
412
|
+
// `/permission` pick wrote, or the event synthesized at create), with the
|
|
413
|
+
// bridge-index row as the fallback for logs that predate the event. Resolved
|
|
414
|
+
// AT SPAWN so a pick made after this resume but before the first prompt
|
|
415
|
+
// still reaches the child; OMP_APPROVAL_MODE (headless) wins.
|
|
416
|
+
// OMP live runtime — ALWAYS LAZY (lazy-transport ruling, both branches).
|
|
385
417
|
// Opening/replaying a persisted session in the WebUI promotes it through
|
|
386
418
|
// history.follow → resume; a read-only visit must NOT materialize the OMP
|
|
387
419
|
// child. `LazyOmpRpc` spawns on the FIRST dispatch (prompt/followUp/steer/
|
|
@@ -391,15 +423,8 @@ export class OmpProvider extends Service {
|
|
|
391
423
|
// With content: the child re-attaches its OWN app-home file via --resume
|
|
392
424
|
// (never the dsh transcript); the mapping already exists in the bridge
|
|
393
425
|
// store, so no adoption hook is needed.
|
|
426
|
+
// Both branches are LAZY and both resolve their launch flag at spawn.
|
|
394
427
|
let rpc;
|
|
395
|
-
if (ompFile === undefined) {
|
|
396
|
-
const lazy = new LazyOmpRpc(["--approval-mode", approvalMode], spawnCwd);
|
|
397
|
-
lazy.onSpawned((client) => { void adoptSpawnedChild(id, stored.header, client, preset); });
|
|
398
|
-
rpc = lazy;
|
|
399
|
-
}
|
|
400
|
-
else {
|
|
401
|
-
rpc = new LazyOmpRpc(["--approval-mode", approvalMode, "--resume", ompFile], spawnCwd);
|
|
402
|
-
}
|
|
403
428
|
try {
|
|
404
429
|
// Fresh attach: seed the dsh session from the dsh log (native replay),
|
|
405
430
|
// header identity folded from the stored header verbatim.
|
|
@@ -409,32 +434,47 @@ export class OmpProvider extends Service {
|
|
|
409
434
|
inheritedEventCount: stored.inheritedEventCount,
|
|
410
435
|
eventState: coldRead.eventState,
|
|
411
436
|
}));
|
|
412
|
-
|
|
437
|
+
const rowPreset = () => getBridgeStore()?.byDshId(String(id))?.permission_preset ?? undefined;
|
|
438
|
+
trace(`resume id=${id} seeded from dsh session log (${coldRead.events.length} events), approval=resolve-at-spawn (row fallback ${rowPreset() ?? "none"})${freshOmp ? " fresh-omp" : ""}`);
|
|
439
|
+
const spawnPermission = () => resolveSpawnPermission(preparation.session.snapshotEvents(), rowPreset(), envApprovalMode());
|
|
440
|
+
if (ompFile === undefined) {
|
|
441
|
+
const lazy = new LazyOmpRpc(() => ["--approval-mode", spawnPermission().approvalMode], spawnCwd);
|
|
442
|
+
lazy.onSpawned((client) => { void adoptSpawnedChild(id, stored.header, client, spawnPermission().preset); });
|
|
443
|
+
rpc = lazy;
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
rpc = new LazyOmpRpc(() => ["--approval-mode", spawnPermission().approvalMode, "--resume", ompFile], spawnCwd);
|
|
447
|
+
}
|
|
413
448
|
const handle = await setupAndPublish(loopCtx, ownerCtx, id, options.agentOptions ?? {}, options.setup, preparation.session, rpc, options.parentAgent, "resume", { enterSession: true, preparation, stored: { handle: stored, written: coldRead.events.length } });
|
|
414
|
-
return this.registerHeld(id, ompFile ?? "", handle);
|
|
449
|
+
return this.registerHeld(id, ompFile ?? "", handle, rpc);
|
|
415
450
|
}
|
|
416
451
|
catch (error) {
|
|
417
452
|
trace(`resume ${id} THREW: ${String(error)} | stack=${error instanceof Error ? error.stack?.slice(0, 400) : "n/a"}`);
|
|
418
453
|
await stored.close().catch(() => { });
|
|
419
|
-
rpc
|
|
454
|
+
rpc?.close();
|
|
420
455
|
throw error;
|
|
421
456
|
}
|
|
422
457
|
}
|
|
423
458
|
/**
|
|
424
|
-
* Track a published RPC-backed agent
|
|
425
|
-
*
|
|
426
|
-
* a
|
|
427
|
-
*
|
|
459
|
+
* Track a published RPC-backed agent, register its transport wrapper with the
|
|
460
|
+
* tick's liveness gate, and hand the session to the supervisor as held. Arrow
|
|
461
|
+
* FIELD (not a `#` method): the provider is exposed through a Cordis tracing
|
|
462
|
+
* proxy, and hard-private methods fail their brand check on the proxy receiver
|
|
463
|
+
* (the same reason setupAndPublish is module-level).
|
|
428
464
|
*/
|
|
429
|
-
registerHeld = (id, file, handle) => {
|
|
465
|
+
registerHeld = (id, file, handle, rpc) => {
|
|
430
466
|
const wrapped = {
|
|
431
467
|
agent: handle.agent,
|
|
432
468
|
dispose: async () => {
|
|
433
469
|
this.heldAgents.delete(String(id));
|
|
470
|
+
this.liveSessions.delete(rpc);
|
|
434
471
|
await handle.dispose();
|
|
435
472
|
},
|
|
436
473
|
};
|
|
437
474
|
this.heldAgents.set(String(id), wrapped);
|
|
475
|
+
// Populate the tick's liveness gate: without this the gate is a constant
|
|
476
|
+
// false and the catalog refresh never runs (dead-input regression).
|
|
477
|
+
this.liveSessions.add(rpc);
|
|
438
478
|
return wrapped;
|
|
439
479
|
};
|
|
440
480
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { OmpRpcClient } from "./rpc-client.js";
|
|
2
2
|
/** Synthetic state for a not-yet-spawned child: quiescent by definition. */
|
|
3
3
|
const IDLE_STATE = { isStreaming: false };
|
|
4
4
|
export class LazyOmpRpc {
|
|
@@ -12,6 +12,12 @@ export class LazyOmpRpc {
|
|
|
12
12
|
#failureListeners = new Set();
|
|
13
13
|
#pendingModel;
|
|
14
14
|
#onSpawned;
|
|
15
|
+
/**
|
|
16
|
+
* @param spawnArgs - the child's argv after `--mode rpc`, or a thunk called
|
|
17
|
+
* AT SPAWN. Use the thunk whenever a launch fact can still change between
|
|
18
|
+
* construction and the first prompt — the new-chat permission pick does.
|
|
19
|
+
* @param cwd - the spawn cwd.
|
|
20
|
+
*/
|
|
15
21
|
constructor(spawnArgs, cwd) {
|
|
16
22
|
this.#spawnArgs = spawnArgs;
|
|
17
23
|
this.#cwd = cwd;
|
|
@@ -35,7 +41,8 @@ export class LazyOmpRpc {
|
|
|
35
41
|
if (this.#client !== undefined)
|
|
36
42
|
return Promise.resolve(this.#client);
|
|
37
43
|
if (this.#spawn === undefined) {
|
|
38
|
-
|
|
44
|
+
const args = typeof this.#spawnArgs === "function" ? this.#spawnArgs() : this.#spawnArgs;
|
|
45
|
+
this.#spawn = OmpRpcClient.spawn(args, this.#cwd).then((client) => {
|
|
39
46
|
// A close() that raced the in-flight spawn wins: dispose immediately.
|
|
40
47
|
if (this.#closed) {
|
|
41
48
|
client.close();
|
|
@@ -89,8 +96,8 @@ export class LazyOmpRpc {
|
|
|
89
96
|
}
|
|
90
97
|
async prompt(message) {
|
|
91
98
|
const client = await this.#ensure();
|
|
92
|
-
// Apply a pre-spawn model selection now, ahead of the prompt (the
|
|
93
|
-
//
|
|
99
|
+
// Apply a pre-spawn model selection now, ahead of the prompt (the child
|
|
100
|
+
// emits a config_update, like a pre-turn set_model).
|
|
94
101
|
const pending = this.#pendingModel;
|
|
95
102
|
if (pending !== undefined) {
|
|
96
103
|
this.#pendingModel = undefined;
|
|
@@ -1,147 +1,164 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* OMP
|
|
3
|
-
* (
|
|
4
|
-
*
|
|
2
|
+
* OMP CLI subprocess data plane — the model catalog and the `modelRoles` map
|
|
3
|
+
* are read from (and written through) the operator's own `omp` binary, the
|
|
4
|
+
* first-party entries the bridge uses instead of parsing OMP's files or
|
|
5
|
+
* hitting frame-limited RPC:
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* rejected; the entire modelRoles object is the unit).
|
|
7
|
+
* - `omp models --json` → the credential-resolved AVAILABLE model catalog
|
|
8
|
+
* (documented selectability rule: provider not in `disabledProviders` and
|
|
9
|
+
* keyless or with a resolvable credential — 7-level precedence, including
|
|
10
|
+
* OAuth/login keys stored in agent.db that no config file reveals).
|
|
11
|
+
* - `omp config get modelRoles --json` → the role→"provider/model[:effort]"
|
|
12
|
+
* map, including the persisted `default` selector (TUI semantics: the
|
|
13
|
+
* default model IS config state, surviving sessions).
|
|
14
|
+
* - `omp config set modelRoles --json <obj>` → whole-object roundtrip write
|
|
15
|
+
* (dotted keys are rejected; the entire modelRoles object is the unit).
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* All calls run with cwd = `OMP_HOME` (omp writes its own state db next to
|
|
18
|
+
* the agent dir) and inherit the daemon environment. The binary is resolved
|
|
19
|
+
* per call ({@link resolveOmpExecutable}), so `$OMP_EXECUTABLE` and PATH
|
|
20
|
+
* changes take effect without a reload.
|
|
20
21
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* fetch repopulates the cache. The async variants always fetch fresh.
|
|
25
|
-
*
|
|
26
|
-
* Fail-soft: any sidecar failure (not started / crashed) yields `undefined`
|
|
27
|
-
* (reads) or `false` (write), so the provider never fails to boot on a down
|
|
28
|
-
* sidecar.
|
|
22
|
+
* Fail-soft: a missing, broken or unresponsive `omp` yields `undefined`
|
|
23
|
+
* (reads) or `false` (write) — the provider never fails to boot because the
|
|
24
|
+
* CLI is down.
|
|
29
25
|
*
|
|
30
26
|
* @module agent-omp/omp-cli
|
|
31
27
|
*/
|
|
32
|
-
import {
|
|
28
|
+
import { execFile, execFileSync } from "node:child_process";
|
|
29
|
+
import { promisify } from "node:util";
|
|
30
|
+
import { OMP_HOME } from "./knobs.js";
|
|
31
|
+
import { resolveOmpExecutable } from "./rpc-client.js";
|
|
32
|
+
const execFileAsync = promisify(execFile);
|
|
33
|
+
const TIMEOUT_MS = 15_000;
|
|
34
|
+
const MAX_BUFFER = 64 * 1024 * 1024;
|
|
33
35
|
// ---------------------------------------------------------------------------
|
|
34
|
-
//
|
|
36
|
+
// Subprocess plumbing (fail-soft: any failure → undefined/false)
|
|
35
37
|
// ---------------------------------------------------------------------------
|
|
36
|
-
/** `
|
|
37
|
-
async function
|
|
38
|
-
// LIVE-GATED: resolves only when the shared sidecar already exists (a live
|
|
39
|
-
// session or an on-demand op). Cold → undefined → the disk fallback engages;
|
|
40
|
-
// the reconcile tick can never spawn a sidecar (sidecar-decoupling ruling).
|
|
38
|
+
/** One `omp <args>` invocation parsed as JSON; undefined on any failure. */
|
|
39
|
+
async function ompCliJson(args) {
|
|
41
40
|
try {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const out = [];
|
|
49
|
-
for (const model of models) {
|
|
50
|
-
if (model !== null &&
|
|
51
|
-
typeof model === "object" &&
|
|
52
|
-
typeof model.provider === "string" &&
|
|
53
|
-
typeof model.id === "string") {
|
|
54
|
-
out.push(model);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return out.length > 0 ? out : undefined;
|
|
41
|
+
const { stdout } = await execFileAsync(resolveOmpExecutable(), args, {
|
|
42
|
+
cwd: OMP_HOME,
|
|
43
|
+
timeout: TIMEOUT_MS,
|
|
44
|
+
maxBuffer: MAX_BUFFER,
|
|
45
|
+
});
|
|
46
|
+
return JSON.parse(stdout);
|
|
58
47
|
}
|
|
59
48
|
catch {
|
|
60
49
|
return undefined;
|
|
61
50
|
}
|
|
62
51
|
}
|
|
63
|
-
/**
|
|
64
|
-
|
|
52
|
+
/** Synchronous variant of {@link ompCliJson} — the boot-time path. */
|
|
53
|
+
function ompCliJsonSync(args) {
|
|
65
54
|
try {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
out[role] = selector;
|
|
74
|
-
}
|
|
75
|
-
return Object.keys(out).length > 0 ? out : undefined;
|
|
55
|
+
const stdout = execFileSync(resolveOmpExecutable(), args, {
|
|
56
|
+
cwd: OMP_HOME,
|
|
57
|
+
timeout: TIMEOUT_MS,
|
|
58
|
+
encoding: "utf8",
|
|
59
|
+
maxBuffer: MAX_BUFFER,
|
|
60
|
+
});
|
|
61
|
+
return JSON.parse(stdout);
|
|
76
62
|
}
|
|
77
63
|
catch {
|
|
78
64
|
return undefined;
|
|
79
65
|
}
|
|
80
66
|
}
|
|
81
|
-
/**
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
67
|
+
/** Validate an `omp models --json` payload; undefined when empty or invalid. */
|
|
68
|
+
function parseModels(json) {
|
|
69
|
+
if (json === null || typeof json !== "object")
|
|
70
|
+
return undefined;
|
|
71
|
+
const models = json.models;
|
|
72
|
+
if (!Array.isArray(models))
|
|
73
|
+
return undefined;
|
|
74
|
+
const out = [];
|
|
75
|
+
for (const model of models) {
|
|
76
|
+
if (model !== null && typeof model === "object" && typeof model.provider === "string") {
|
|
77
|
+
out.push(model);
|
|
78
|
+
}
|
|
86
79
|
}
|
|
87
|
-
|
|
88
|
-
|
|
80
|
+
return out.length > 0 ? out : undefined;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Validate a `modelRoles` payload; undefined when unset, empty or invalid.
|
|
84
|
+
*
|
|
85
|
+
* `omp config get <key> --json` prints `{key, value, type, description}`, but
|
|
86
|
+
* the bare value map appears in older builds, so both shapes are accepted. A
|
|
87
|
+
* wrapped payload for a different key is rejected rather than misread as a
|
|
88
|
+
* role map.
|
|
89
|
+
*/
|
|
90
|
+
function parseModelRoles(json) {
|
|
91
|
+
if (json === null || typeof json !== "object" || Array.isArray(json))
|
|
92
|
+
return undefined;
|
|
93
|
+
const record = json;
|
|
94
|
+
let value = record;
|
|
95
|
+
if ("key" in record || "value" in record) {
|
|
96
|
+
if (typeof record.key === "string" && record.key !== "modelRoles")
|
|
97
|
+
return undefined;
|
|
98
|
+
value = record.value;
|
|
99
|
+
}
|
|
100
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
101
|
+
return undefined;
|
|
102
|
+
const out = {};
|
|
103
|
+
for (const [role, selector] of Object.entries(value)) {
|
|
104
|
+
if (typeof selector === "string" && selector !== "")
|
|
105
|
+
out[role] = selector;
|
|
89
106
|
}
|
|
107
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
90
108
|
}
|
|
91
109
|
// ---------------------------------------------------------------------------
|
|
92
110
|
// Model catalog
|
|
93
111
|
// ---------------------------------------------------------------------------
|
|
94
|
-
let modelsCache;
|
|
95
112
|
/**
|
|
96
|
-
* Synchronous
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* `models.ts` falls back to the on-disk registry without spawning anything.
|
|
113
|
+
* Synchronous `omp models --json` — the boot-time variant (the adapter's
|
|
114
|
+
* provider route list must be known before `registerAdapter` returns).
|
|
115
|
+
* Blocks up to the timeout; prefer the async variant everywhere else.
|
|
100
116
|
*/
|
|
101
117
|
export function ompAvailableModelsSync() {
|
|
102
|
-
return
|
|
118
|
+
return parseModels(ompCliJsonSync(["models", "--json"]));
|
|
103
119
|
}
|
|
104
120
|
/**
|
|
105
|
-
* `models
|
|
106
|
-
* when the
|
|
107
|
-
*
|
|
121
|
+
* `omp models --json` — the AVAILABLE model catalog (credential-resolved,
|
|
122
|
+
* `disabledProviders`-aware). `undefined` when the CLI fails; callers fall
|
|
123
|
+
* back to the on-disk sources.
|
|
108
124
|
*/
|
|
109
125
|
export async function ompAvailableModels() {
|
|
110
|
-
|
|
111
|
-
if (models !== undefined)
|
|
112
|
-
modelsCache = models;
|
|
113
|
-
return models;
|
|
126
|
+
return parseModels(await ompCliJson(["models", "--json"]));
|
|
114
127
|
}
|
|
115
128
|
// ---------------------------------------------------------------------------
|
|
116
129
|
// modelRoles
|
|
117
130
|
// ---------------------------------------------------------------------------
|
|
118
|
-
let rolesCache;
|
|
119
131
|
/**
|
|
120
|
-
* Synchronous
|
|
121
|
-
* {@link
|
|
122
|
-
* `config.yml` on disk (omp-store); this cache only ever reflects what a live
|
|
123
|
-
* sidecar reported. The write path (`ompModelRoles`/`ompSetModelRoles`) may
|
|
124
|
-
* spawn on real need — a default-model change is a user action, not a tick.
|
|
132
|
+
* Synchronous `omp config get modelRoles --json` — boot-time variant of
|
|
133
|
+
* {@link ompModelRoles}.
|
|
125
134
|
*/
|
|
126
135
|
export function ompModelRolesSync() {
|
|
127
|
-
return
|
|
136
|
+
return parseModelRoles(ompCliJsonSync(["config", "get", "modelRoles", "--json"]));
|
|
128
137
|
}
|
|
129
138
|
/**
|
|
130
|
-
* `
|
|
131
|
-
*
|
|
132
|
-
*
|
|
139
|
+
* `omp config get modelRoles --json` — the current modelRoles object, read
|
|
140
|
+
* fresh on every call (the OMP TUI may have rewritten config.yml). `undefined`
|
|
141
|
+
* when unset or the CLI fails.
|
|
133
142
|
*/
|
|
134
143
|
export async function ompModelRoles() {
|
|
135
|
-
|
|
136
|
-
if (roles !== undefined)
|
|
137
|
-
rolesCache = roles;
|
|
138
|
-
return roles;
|
|
144
|
+
return parseModelRoles(await ompCliJson(["config", "get", "modelRoles", "--json"]));
|
|
139
145
|
}
|
|
140
146
|
/**
|
|
141
|
-
* `
|
|
142
|
-
*
|
|
143
|
-
*
|
|
147
|
+
* `omp config set modelRoles --json <obj>` — the TUI-grade default-model
|
|
148
|
+
* write: OMP persists `modelRoles.default` itself, so every new session
|
|
149
|
+
* (TUI or Web) inherits the selection. Whole-object roundtrip: dotted keys
|
|
150
|
+
* are rejected by the CLI. `false` on any failure (fail-soft).
|
|
144
151
|
*/
|
|
145
152
|
export async function ompSetModelRoles(roles) {
|
|
146
|
-
|
|
153
|
+
try {
|
|
154
|
+
await execFileAsync(resolveOmpExecutable(), ["config", "set", "modelRoles", "--json", JSON.stringify(roles)], {
|
|
155
|
+
cwd: OMP_HOME,
|
|
156
|
+
timeout: TIMEOUT_MS,
|
|
157
|
+
maxBuffer: MAX_BUFFER,
|
|
158
|
+
});
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
147
164
|
}
|
|
@@ -434,9 +434,10 @@ function isDescendantOf(pid, ancestor) {
|
|
|
434
434
|
* OMP has no session-level exclusivity: neither the TUI nor `--mode rpc`
|
|
435
435
|
* locks its transcript, so two live writers interleave appends silently
|
|
436
436
|
* (verified: a TUI `omp` and a bridge `omp --mode rpc --resume` coexisted on
|
|
437
|
-
* one file with plain `w` descriptors and no advisory locks). The
|
|
438
|
-
*
|
|
439
|
-
* the TUI case
|
|
437
|
+
* one file with plain `w` descriptors and no advisory locks). The intended
|
|
438
|
+
* enforcement is to refuse resuming a transcript another process already writes
|
|
439
|
+
* (the TUI case), closing the split-brain at the only layer that can see it —
|
|
440
|
+
* but see {@link foreignWriterPid}: that hold is NOT wired in this round.
|
|
440
441
|
* Own descendants (a just-torn-down bridge child whose fds linger) are
|
|
441
442
|
* exempt to keep teardown races from wedging re-resume.
|
|
442
443
|
*/
|
|
@@ -504,7 +505,14 @@ export function scanForeignWriters() {
|
|
|
504
505
|
/**
|
|
505
506
|
* Pid of a FOREIGN process holding `file` open for writing, if any.
|
|
506
507
|
* Convenience wrapper over {@link scanForeignWriters} for the single-file
|
|
507
|
-
* resume gate
|
|
508
|
+
* resume gate.
|
|
509
|
+
*
|
|
510
|
+
* UNWIRED (2026-10-03): this helper has no caller — the `resume` path never
|
|
511
|
+
* invokes it, so nothing currently refuses a transcript another process (the
|
|
512
|
+
* TUI) is writing. Spec §3.4 of `docs/specs/omp-rpc-transport/spec.md`
|
|
513
|
+
* registers that accepted exposure and the planned follow-up: a fail-loud
|
|
514
|
+
* `foreignWriterPid(<sessionFile>)` check wired into `resume`, to be decided
|
|
515
|
+
* by the operator. Helper kept exported for that follow-up.
|
|
508
516
|
*/
|
|
509
517
|
export function foreignWriterPid(file) {
|
|
510
518
|
let canonical;
|