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
|
@@ -3,53 +3,89 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The browser model selector never touches the Codex agent directly: it is an
|
|
5
5
|
* RPC round-trip through the apiproxy, which reads `ctx.llm.listProviders /
|
|
6
|
-
* listModels /
|
|
7
|
-
* queries from the
|
|
8
|
-
*
|
|
9
|
-
* native deepseek-official / pi-ai routes, which the profile disables.
|
|
6
|
+
* listModels / resolveModelInfo` to build the catalog. This adapter answers
|
|
7
|
+
* those queries from the ADAPTER's canonical provider store
|
|
8
|
+
* (`codex-providers.ts`), registering ONE ROUTE PER CONFIGURED PROVIDER.
|
|
10
9
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* That one-route-per-provider shape is the 2026-10-04 rework, and it is what
|
|
11
|
+
* makes the whole feature DSH-native: the harness's own model selector already
|
|
12
|
+
* renders a provider-grouped catalog, already keeps the choice per session
|
|
13
|
+
* (`session/selectModel` → the durable `model/selection` event + the
|
|
14
|
+
* `modelSelection` projection), and already reports which route a session last
|
|
15
|
+
* used. Registering the Codex providers as real routes means Super D gets all
|
|
16
|
+
* of that by construction instead of re-implementing it — there is no "active
|
|
17
|
+
* provider" left for the UI to hold.
|
|
18
|
+
*
|
|
19
|
+
* `stream` is never dispatched: Codex owns generation through its app-server
|
|
20
|
+
* thread (`CodexAgent`), so the adapter only serves catalog/metadata queries.
|
|
21
|
+
* It throws rather than fabricating a wire route the harness would misuse.
|
|
22
|
+
*
|
|
23
|
+
* @module agent-codex/adapter
|
|
14
24
|
*/
|
|
15
25
|
import { LlmAdapter, LlmError, } from "@deepseek-ai/dsh-llm";
|
|
16
|
-
import {
|
|
17
|
-
/**
|
|
26
|
+
import { readProviderStore } from "./codex-providers.js";
|
|
27
|
+
/**
|
|
28
|
+
* The single route every Codex model was served under before the multi-provider
|
|
29
|
+
* rework. No longer registered (a session pinned to it falls back to whatever
|
|
30
|
+
* the native `config.toml` runs), but kept as the one named constant so the
|
|
31
|
+
* legacy value is greppable in session logs.
|
|
32
|
+
*/
|
|
18
33
|
export const CODEX_PROVIDER_ID = "codex";
|
|
34
|
+
/** Whether a provider may be registered as a model route. */
|
|
35
|
+
export function routableProvider(entry) {
|
|
36
|
+
return entry.models.length > 0;
|
|
37
|
+
}
|
|
38
|
+
/** The route ids a store currently contributes to `ctx.llm`. */
|
|
39
|
+
export function routeIds(store) {
|
|
40
|
+
return store.providers.filter(routableProvider).map((entry) => entry.id);
|
|
41
|
+
}
|
|
42
|
+
/** The provider entry a route id names, or `undefined`. */
|
|
43
|
+
export function providerFor(worldHome, providerId) {
|
|
44
|
+
return readProviderStore(worldHome).providers.find((entry) => entry.id === providerId);
|
|
45
|
+
}
|
|
19
46
|
export class CodexLlmAdapter extends LlmAdapter {
|
|
20
|
-
|
|
47
|
+
worldHome;
|
|
21
48
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
49
|
+
* @param worldHome - the world's DSH home, holding the canonical provider
|
|
50
|
+
* store in `<worldHome>/codex-providers.json`. Read on every call, so a
|
|
51
|
+
* panel edit is visible to the next catalog query without re-registration.
|
|
25
52
|
*/
|
|
26
|
-
constructor(
|
|
53
|
+
constructor(worldHome) {
|
|
27
54
|
super();
|
|
28
|
-
this.
|
|
55
|
+
this.worldHome = worldHome;
|
|
29
56
|
}
|
|
30
|
-
providerInfo(
|
|
31
|
-
|
|
57
|
+
providerInfo(provider) {
|
|
58
|
+
// The display name is the entry's label — for a provider folded in from the
|
|
59
|
+
// user's own config that is its endpoint URL, which is the only name a
|
|
60
|
+
// hand-written codex config supplies.
|
|
61
|
+
return { id: provider, name: providerFor(this.worldHome, provider)?.label ?? provider };
|
|
32
62
|
}
|
|
33
|
-
async listModels(
|
|
34
|
-
|
|
35
|
-
|
|
63
|
+
async listModels(provider) {
|
|
64
|
+
const entry = providerFor(this.worldHome, provider);
|
|
65
|
+
if (entry === undefined)
|
|
66
|
+
return [];
|
|
67
|
+
return entry.models.map((model) => ({
|
|
68
|
+
provider,
|
|
36
69
|
id: model.id,
|
|
37
|
-
name: model.
|
|
70
|
+
name: model.displayName ?? model.id,
|
|
38
71
|
}));
|
|
39
72
|
}
|
|
40
|
-
async resolveModel(
|
|
41
|
-
const
|
|
73
|
+
async resolveModel(provider, model, _signal) {
|
|
74
|
+
const entry = providerFor(this.worldHome, provider);
|
|
75
|
+
const found = entry?.models.find((candidate) => candidate.id === model);
|
|
42
76
|
if (found === undefined) {
|
|
43
|
-
throw new LlmError(`Codex provider "${
|
|
77
|
+
throw new LlmError(`Codex provider "${provider}" does not serve model "${model}"`, "MODEL_NOT_FOUND");
|
|
44
78
|
}
|
|
45
79
|
return {
|
|
46
|
-
provider
|
|
80
|
+
provider,
|
|
47
81
|
id: found.id,
|
|
48
|
-
name: found.
|
|
49
|
-
...(found.contextWindow !== undefined && found.contextWindow > 0
|
|
82
|
+
name: found.displayName ?? found.id,
|
|
83
|
+
...(found.contextWindow !== undefined && found.contextWindow > 0
|
|
84
|
+
? { context: { contextWindow: found.contextWindow } }
|
|
85
|
+
: {}),
|
|
50
86
|
};
|
|
51
87
|
}
|
|
52
88
|
stream(_options) {
|
|
53
|
-
throw new LlmError("the Codex adapter does not stream: Codex owns generation through its
|
|
89
|
+
throw new LlmError("the Codex adapter does not stream: Codex owns generation through its app-server thread", "UNSUPPORTED_STREAM");
|
|
54
90
|
}
|
|
55
91
|
}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { agentEvents } from "@deepseek-ai/dsh-agent";
|
|
2
2
|
import { Inbox } from "./inbox.js";
|
|
3
|
+
import { canonicalHeader, headerEquals } from "@deepseek-ai/dsh-session";
|
|
3
4
|
import { LlmAttemptId, ToolCallId, QUOTA_EXCEEDED_CODE, createAssistantMessage, createSystemMessage, createToolResultMessage, createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
4
5
|
import { createScope } from "@deepseek-ai/dsh-scope";
|
|
6
|
+
import { makeApprovalHandler, parseApprovalTimeout } from "./app-server-approvals.js";
|
|
7
|
+
import { presetFromEvents, turnPolicyFromEvents } from "./permission.js";
|
|
5
8
|
import { withSystemHead } from "../../superd-hub/dist/index.js";
|
|
9
|
+
import { NATIVE_LAUNCH } from "./codex-launch.js";
|
|
10
|
+
import { resolveRoute } from "./codex-route.js";
|
|
6
11
|
/**
|
|
7
12
|
* v2 live assistant-stream publication for the Codex bridge — the same frame
|
|
8
13
|
* protocol the reference loop's AssistantStreamAttempt speaks (start marker,
|
|
@@ -193,11 +198,15 @@ function userMessageText(message) {
|
|
|
193
198
|
/**
|
|
194
199
|
* Dash-host plugin narration from the approval service: the policy-change
|
|
195
200
|
* notice `agent.inject` carries when `/permission` switches a live session's
|
|
196
|
-
* approval policy.
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
+
* approval policy.
|
|
202
|
+
*
|
|
203
|
+
* App-server line (2026-10-03): the switch itself is now REAL — the fold in
|
|
204
|
+
* `#syncTurnPolicy` reads the accompanying `approval/policy` + `sandbox/mode`
|
|
205
|
+
* events and overrides both fields on the next `turn/start`. The narration TEXT
|
|
206
|
+
* is still dropped on purpose: it is DSH's own sandbox-policy sentence, written
|
|
207
|
+
* for the native tool loop, and Codex enforces its own policy inside the rust
|
|
208
|
+
* runtime. Forwarding it would steer it into the in-flight turn (interrupting
|
|
209
|
+
* tool work) and echo into history as a fake user prompt.
|
|
201
210
|
*/
|
|
202
211
|
function isApprovalNarration(message) {
|
|
203
212
|
// 0.1.7: the shared `plugin` source kind is gone (MessageSourceMap is
|
|
@@ -235,6 +244,12 @@ function parseIdleExit(raw) {
|
|
|
235
244
|
const value = Number(raw);
|
|
236
245
|
return Number.isFinite(value) && value >= 0 ? value : 600_000;
|
|
237
246
|
}
|
|
247
|
+
/**
|
|
248
|
+
* Approval answer timeout: fail closed (decline) after this long so a headless
|
|
249
|
+
* or unattended run can never hang Codex on a card nobody answers. `0`
|
|
250
|
+
* disables the timeout. Default: 5 minutes.
|
|
251
|
+
*/
|
|
252
|
+
const CODEX_APPROVAL_TIMEOUT_MS = parseApprovalTimeout(process.env.CODEX_APPROVAL_TIMEOUT_MS);
|
|
238
253
|
export class CodexAgent {
|
|
239
254
|
id;
|
|
240
255
|
options;
|
|
@@ -281,8 +296,26 @@ export class CodexAgent {
|
|
|
281
296
|
* failure/abort paths so the turn's visible text is never dropped.
|
|
282
297
|
*/
|
|
283
298
|
#deferredAssistant = null;
|
|
284
|
-
/**
|
|
285
|
-
|
|
299
|
+
/**
|
|
300
|
+
* The route the OPEN turn runs, captured when the turn opened.
|
|
301
|
+
*
|
|
302
|
+
* Codex's wire messages carry no provider attribution, so the assistant
|
|
303
|
+
* message's `source` has to be stamped from here. `options` is the wrong
|
|
304
|
+
* source for it: options are frozen at create/resume, so after a mid-session
|
|
305
|
+
* switch they name the provider the session STARTED on. Measured on the 4999
|
|
306
|
+
* line: turn 2 ran on `deepseek/deepseek-flash` while its assistant message was
|
|
307
|
+
* attributed to `zhipu-cn/glm-5.3-flash` — a user-visible lie, since the UI and
|
|
308
|
+
* the model-switch notice both read that attribution.
|
|
309
|
+
*/
|
|
310
|
+
#turnRoute = undefined;
|
|
311
|
+
/**
|
|
312
|
+
* The route the LIVE child was last told to run, as `provider/model`. Purely
|
|
313
|
+
* a work-saver: the client is idempotent about `setModel` and `reattach`, and
|
|
314
|
+
* this only keeps a steady session from re-issuing either every turn.
|
|
315
|
+
*/
|
|
316
|
+
#routeKey = null;
|
|
317
|
+
/** Whether THIS agent instance has already logged a `request/header` snapshot. */
|
|
318
|
+
#requestHeaderLogged = false;
|
|
286
319
|
/** Fires when the agent has been idle past CODEX_IDLE_EXIT_MS (tears down the client). */
|
|
287
320
|
#onIdleExit;
|
|
288
321
|
#idleExitTimer = undefined;
|
|
@@ -310,37 +343,43 @@ export class CodexAgent {
|
|
|
310
343
|
this.#lastTurn = session.snapshotEvents().findLast((event) => event.type === "turn/start")?.data.turn ?? 0;
|
|
311
344
|
client.on((event) => this.#handleEvent(event));
|
|
312
345
|
client.onFailure((error) => this.#fail(error));
|
|
346
|
+
// App-server line (2026-10-03): Codex now ASKS before running a command or
|
|
347
|
+
// applying a patch (`item/commandExecution|fileChange/requestApproval` plus
|
|
348
|
+
// the legacy execCommand/applyPatch pair) and blocks the tool until the
|
|
349
|
+
// client answers. Bridge those requests onto the Dash approval seam, whose
|
|
350
|
+
// Web UI answerer renders the ApprovalPanel. Every failure path answers
|
|
351
|
+
// DECLINE (fail-closed) so a request is never left hanging; the service is
|
|
352
|
+
// resolved lazily because the approval row lives in a sibling fiber.
|
|
353
|
+
client.setServerRequestHandler(makeApprovalHandler({
|
|
354
|
+
getApproval: () => this.ctx.get("approval"),
|
|
355
|
+
agent: this,
|
|
356
|
+
timeoutMs: CODEX_APPROVAL_TIMEOUT_MS,
|
|
357
|
+
asCallId: (raw) => ToolCallId(raw),
|
|
358
|
+
trace: (message) => trace(message),
|
|
359
|
+
}));
|
|
313
360
|
// Session-identity events (the agent-preset stamp) are committed on the
|
|
314
361
|
// FIRST turn by #bootstrapSessionIdentity — only once the client actually
|
|
315
362
|
// exists — so a freshly created session announces completely blank,
|
|
316
363
|
// exactly like the native agent-loop factory's: the backend takes zero
|
|
317
364
|
// part in the UI's browser-local new-session draft.
|
|
318
|
-
//
|
|
319
|
-
//
|
|
320
|
-
//
|
|
321
|
-
//
|
|
322
|
-
//
|
|
323
|
-
//
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
kind: "error",
|
|
330
|
-
text: "Approval policy is fixed at Codex launch; runtime switching is not supported for Codex sessions.",
|
|
331
|
-
}),
|
|
332
|
-
}));
|
|
333
|
-
// `/compact` parity: the SDK line exposes no compaction surface (V1 gap
|
|
334
|
-
// table: no messages surgery/compact — fail-soft default). Shadowing the
|
|
335
|
-
// loop-owned global name keeps the (disabled) native pipeline from
|
|
336
|
-
// receiving it; the command node renders the clean refusal natively.
|
|
365
|
+
//
|
|
366
|
+
// `/permission` is NOT shadowed on the app-server line: the app-server
|
|
367
|
+
// accepts `approvalPolicy` + `sandboxPolicy` on every `turn/start`, so the
|
|
368
|
+
// global command's `permission/preset` + `sandbox/mode` + `approval/policy`
|
|
369
|
+
// triple is folded per turn by #syncTurnPolicy and takes effect from the
|
|
370
|
+
// NEXT turn on. (The SDK line had to shadow it with a refusal.)
|
|
371
|
+
// `/compact` parity: the app-server DOES expose `thread/compact/start` +
|
|
372
|
+
// `thread/compacted`, but the DSH compaction event vocabulary is not wired
|
|
373
|
+
// to it yet (registered backlog item). Shadowing the loop-owned global name
|
|
374
|
+
// keeps the (disabled) native pipeline from receiving it; the command node
|
|
375
|
+
// renders the clean refusal natively.
|
|
337
376
|
this.ctx.inject(["commands"], (cmdCtx) => cmdCtx.commands.register({
|
|
338
377
|
name: "compact",
|
|
339
|
-
description: "Unavailable for Codex sessions (
|
|
378
|
+
description: "Unavailable for Codex sessions (thread compaction is not wired yet)",
|
|
340
379
|
input: { hint: "[instructions]" },
|
|
341
380
|
handler: () => ({
|
|
342
381
|
kind: "error",
|
|
343
|
-
text: "Compaction is not available for Codex sessions: the
|
|
382
|
+
text: "Compaction is not available for Codex sessions yet: the app-server's thread/compact/start is not wired to the Dash compaction events.",
|
|
344
383
|
}),
|
|
345
384
|
}));
|
|
346
385
|
}
|
|
@@ -360,18 +399,16 @@ export class CodexAgent {
|
|
|
360
399
|
steer(message) {
|
|
361
400
|
// Mirrors dsh-agent-loop's `steer = send(input, "next-step", true)`: an
|
|
362
401
|
// idle driver starts a turn; a running driver consumes it at the next
|
|
363
|
-
// step (
|
|
402
|
+
// step (app-server line: `turn/steer` into the live turn).
|
|
364
403
|
this.#deliver(message, "next-step", true);
|
|
365
404
|
}
|
|
366
405
|
inject(message) {
|
|
367
|
-
//
|
|
368
|
-
//
|
|
369
|
-
//
|
|
370
|
-
//
|
|
371
|
-
// accompanying `approval/policy` session event stays recorded so the UI's
|
|
372
|
-
// permission state remains truthful.
|
|
406
|
+
// Drop the approval-policy narration text (see isApprovalNarration). The
|
|
407
|
+
// real switch rides the accompanying `approval/policy` + `sandbox/mode`
|
|
408
|
+
// session events, which #syncTurnPolicy folds into the next turn/start;
|
|
409
|
+
// recording them keeps the UI's permission state truthful.
|
|
373
410
|
if (isApprovalNarration(message)) {
|
|
374
|
-
trace("inject: dropped approval-policy narration (
|
|
411
|
+
trace("inject: dropped approval-policy narration (the switch rides the session events, not the text)");
|
|
375
412
|
return;
|
|
376
413
|
}
|
|
377
414
|
// Mirrors dsh-agent-loop's `inject = send(input, "next-step", false)`:
|
|
@@ -430,7 +467,7 @@ export class CodexAgent {
|
|
|
430
467
|
*
|
|
431
468
|
* Busy is the LOCAL turn state (`#turnOpen`), not the `#streaming` flag:
|
|
432
469
|
* the client needs a moment to emit `agent_start` after a prompt, and in
|
|
433
|
-
* that gap a bare `prompt` is still safe on the
|
|
470
|
+
* that gap a bare `prompt` is still safe on the app-server line, but keeping the
|
|
434
471
|
* omp queuing semantics makes the echo mapping identical.
|
|
435
472
|
*/
|
|
436
473
|
#deliver(message, target, wakeup) {
|
|
@@ -471,10 +508,15 @@ export class CodexAgent {
|
|
|
471
508
|
* reservation.
|
|
472
509
|
*/
|
|
473
510
|
async #startTurn(message) {
|
|
511
|
+
let route;
|
|
474
512
|
try {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
513
|
+
// The route is resolved and put in force BEFORE the thread is touched: a
|
|
514
|
+
// provider switch is a different child process, and `thread/resume` cannot
|
|
515
|
+
// move a thread a live app-server already holds (`#applyRoute`).
|
|
516
|
+
route = this.#resolveRoute();
|
|
517
|
+
await this.#applyRoute(route);
|
|
518
|
+
this.#turnRoute = route;
|
|
519
|
+
this.#syncTurnPolicy();
|
|
478
520
|
}
|
|
479
521
|
catch (error) {
|
|
480
522
|
trace(`#startTurn cold start failed: ${String(error)}`);
|
|
@@ -489,7 +531,9 @@ export class CodexAgent {
|
|
|
489
531
|
if (this.#disposed)
|
|
490
532
|
return;
|
|
491
533
|
this.session.append("turn/start", { turn: this.#dashTurn });
|
|
492
|
-
|
|
534
|
+
if (route !== undefined)
|
|
535
|
+
this.#commitRequestHeader(route);
|
|
536
|
+
this.#emitRequestContext(route);
|
|
493
537
|
this.session.append("user/message", message, { surfaceOp: "append" });
|
|
494
538
|
void this.#client.prompt(userMessageText(message)).catch((error) => this.#fail(error));
|
|
495
539
|
}
|
|
@@ -529,28 +573,34 @@ export class CodexAgent {
|
|
|
529
573
|
* Turn-enclosed (no step required) and deduped by route key, so it lands at
|
|
530
574
|
* the start of a turn and again only when the effective route changes.
|
|
531
575
|
*/
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
576
|
+
/**
|
|
577
|
+
* Stamp route metadata for the next request (`request/context`): the effective
|
|
578
|
+
* provider/model plus the provider's own context window when it advertises one.
|
|
579
|
+
* Turn-enclosed (no step required) and deduped by route key, so it lands at
|
|
580
|
+
* the start of a turn and again only when the effective route changes.
|
|
581
|
+
*
|
|
582
|
+
* @param route - the route this turn runs, already resolved by `#resolveRoute`.
|
|
583
|
+
*/
|
|
584
|
+
#emitRequestContext(route) {
|
|
585
|
+
if (route === undefined || !this.#turnOpen)
|
|
537
586
|
return;
|
|
538
587
|
const key = `${route.provider}/${route.model}`;
|
|
539
588
|
if (key === this.#lastRouteKey)
|
|
540
589
|
return;
|
|
541
590
|
this.#lastRouteKey = key;
|
|
591
|
+
const contextWindow = this.#runtimeInfo?.contextWindowFor?.(route.provider, route.model);
|
|
542
592
|
this.session.append("request/context", {
|
|
543
593
|
provider: route.provider,
|
|
544
594
|
model: route.model,
|
|
545
|
-
...(
|
|
595
|
+
...(contextWindow === undefined ? {} : { contextWindow }),
|
|
546
596
|
});
|
|
547
|
-
trace(`request/context ${key}${
|
|
597
|
+
trace(`request/context ${key}${contextWindow === undefined ? "" : ` ctx=${contextWindow}`}`);
|
|
548
598
|
}
|
|
549
599
|
/**
|
|
550
600
|
* One-shot first-turn identity commit: the agent-preset stamp (idempotent).
|
|
551
|
-
* The
|
|
552
|
-
*
|
|
553
|
-
*
|
|
601
|
+
* The system prompt is not sent BY us: Codex owns its base instructions and
|
|
602
|
+
* the adapter recovers them from the rollout HEAD (see #emitSystemMessage).
|
|
603
|
+
* The guard —
|
|
554
604
|
* skip when the seed already carries a `system/message` — stays for shape
|
|
555
605
|
* parity with resumed logs that might carry one.
|
|
556
606
|
*/
|
|
@@ -580,8 +630,26 @@ export class CodexAgent {
|
|
|
580
630
|
if (this.#streaming || transport === "followUp")
|
|
581
631
|
this.#flushRemote();
|
|
582
632
|
}
|
|
633
|
+
/**
|
|
634
|
+
* Retarget the model the NEXT turn requests, without disturbing a live one.
|
|
635
|
+
*
|
|
636
|
+
* A delivery can arrive while a turn is running (steering, or a queued
|
|
637
|
+
* follow-up the client will auto-generate), and the app-server fixes a turn's
|
|
638
|
+
* route at `turn/start`. So the model — which the app-server does accept per
|
|
639
|
+
* turn — is re-pinned here, while a PROVIDER move is deliberately left to the
|
|
640
|
+
* next turn's `#startTurn`, which is the only place a child can be replaced.
|
|
641
|
+
*/
|
|
642
|
+
async #pinNextModel() {
|
|
643
|
+
const route = this.#resolveRoute();
|
|
644
|
+
if (route === undefined)
|
|
645
|
+
return;
|
|
646
|
+
await this.#client.setModel(route.provider, route.model).catch((error) => {
|
|
647
|
+
trace(`set_model ${route.provider}/${route.model} failed: ${String(error)}`);
|
|
648
|
+
});
|
|
649
|
+
}
|
|
583
650
|
#flushRemote() {
|
|
584
|
-
|
|
651
|
+
this.#syncTurnPolicy();
|
|
652
|
+
void this.#pinNextModel().finally(() => {
|
|
585
653
|
for (const entry of this.#remoteQueue) {
|
|
586
654
|
if (entry.sent)
|
|
587
655
|
continue;
|
|
@@ -594,33 +662,130 @@ export class CodexAgent {
|
|
|
594
662
|
});
|
|
595
663
|
}
|
|
596
664
|
/**
|
|
597
|
-
*
|
|
598
|
-
*
|
|
599
|
-
*
|
|
600
|
-
*
|
|
601
|
-
*
|
|
602
|
-
*
|
|
603
|
-
*
|
|
604
|
-
*
|
|
665
|
+
* The route this session must run, resolved the way the harness itself
|
|
666
|
+
* resolves it.
|
|
667
|
+
*
|
|
668
|
+
* `sessionController.selectionFor(agent).current` is `pending ?? logged ??
|
|
669
|
+
* default`, and `pending` is the durable `modelSelection` projection's view of
|
|
670
|
+
* the latest `model/selection` event that no later `request/header` has
|
|
671
|
+
* consumed. Reading the PROJECTION (rather than re-scanning the log) is what
|
|
672
|
+
* makes a mid-turn switch work for free: the harness's own `session.selectModel`
|
|
673
|
+
* appends that event and retires it once this agent logs the header that used
|
|
674
|
+
* it, so "switch now, run next turn" is the projection's semantics, not ours.
|
|
675
|
+
*
|
|
676
|
+
* @returns the route, or `undefined` when nothing in the log or the services
|
|
677
|
+
* names a complete provider/model pair.
|
|
678
|
+
* @returns the route, or `undefined` when nothing in the log or the services
|
|
679
|
+
* names a complete provider/model pair (see `codex-route.ts` for the order).
|
|
605
680
|
*/
|
|
606
|
-
|
|
607
|
-
const
|
|
681
|
+
#resolveRoute() {
|
|
682
|
+
const projections = this.#loopCtx.get("sessionProjections");
|
|
608
683
|
const service = this.ctx.get("agentDefaultModel");
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
:
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
684
|
+
return resolveRoute({
|
|
685
|
+
pending: projections?.stateOf?.(this.session, "modelSelection")?.pending,
|
|
686
|
+
logged: this.session.requestHeader()?.config,
|
|
687
|
+
preferred: service?.currentSelection(),
|
|
688
|
+
fallback: { provider: this.options.provider, model: this.options.model },
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Put the resolved route in force on the native side, switching the CHILD if
|
|
693
|
+
* the provider moved.
|
|
694
|
+
*
|
|
695
|
+
* Three cases, in increasing cost:
|
|
696
|
+
*
|
|
697
|
+
* 1. nothing attached yet — pin the launch and let the first prompt spawn
|
|
698
|
+
* (this is the lazy-spawn gate: choosing a model must not cost a process);
|
|
699
|
+
* 2. same provider, different model — `setModel` rides the next `turn/start`,
|
|
700
|
+
* which the app-server documents as "this turn and subsequent turns";
|
|
701
|
+
* 3. different provider — the child has to be replaced, because a provider is
|
|
702
|
+
* an argv-level property of the process and `thread/resume` cannot move a
|
|
703
|
+
* thread a live app-server already holds (see `app-server-client.ts`
|
|
704
|
+
* `reattach`). This is THE turn boundary the feature is built around.
|
|
705
|
+
*
|
|
706
|
+
* @param route - the resolved route, or `undefined` when none is known (the
|
|
707
|
+
* native config then decides, which is also a legacy `codex` selection's fate).
|
|
708
|
+
*/
|
|
709
|
+
async #applyRoute(route) {
|
|
710
|
+
const launch = route === undefined ? undefined : this.#runtimeInfo?.launchFor(route.provider);
|
|
711
|
+
// An unknown provider id (a session pinned before the multi-provider rework)
|
|
712
|
+
// maps to "no injection": the user's own config.toml is the honest answer.
|
|
713
|
+
const launchSpec = launch ?? NATIVE_LAUNCH;
|
|
714
|
+
const providerId = launch === undefined ? null : route?.provider ?? null;
|
|
715
|
+
const client = this.#client;
|
|
716
|
+
const key = `${providerId ?? "native"}/${route?.model ?? ""}`;
|
|
717
|
+
if (!client.spawned && client.threadId === null) {
|
|
718
|
+
client.applyLaunch(launchSpec, providerId);
|
|
719
|
+
await client.ensureStarted();
|
|
720
|
+
}
|
|
721
|
+
else if (client.threadId === null) {
|
|
722
|
+
// A live child with no thread yet: still just a launch swap.
|
|
723
|
+
client.applyLaunch(launchSpec, providerId);
|
|
724
|
+
await client.ensureStarted();
|
|
725
|
+
}
|
|
726
|
+
else if (client.appliedProvider !== providerId) {
|
|
727
|
+
await client.reattach(launchSpec, providerId, route?.model);
|
|
728
|
+
}
|
|
729
|
+
else if (route !== undefined) {
|
|
730
|
+
await client.setModel(route.provider, route.model).catch((error) => {
|
|
731
|
+
trace(`set_model ${key} failed: ${String(error)}`);
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
this.#routeKey = key;
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* Log the request header the NEXT request runs under, exactly as the reference
|
|
738
|
+
* loop does.
|
|
739
|
+
*
|
|
740
|
+
* This single append is what wires Super D into the harness's model-selection
|
|
741
|
+
* state machine rather than beside it: the `modelSelection` projection takes
|
|
742
|
+
* `lastUsed` from it, `sessionController.consumeSelection` retires a pending
|
|
743
|
+
* switch when it matches, and `installModelSelection` compares it to decide
|
|
744
|
+
* whether a model-change notice is owed. Without it a resumed session can
|
|
745
|
+
* never say which provider produced its transcript.
|
|
746
|
+
*
|
|
747
|
+
* @param route - the route this turn runs.
|
|
748
|
+
*/
|
|
749
|
+
#commitRequestHeader(route) {
|
|
750
|
+
const header = canonicalHeader({
|
|
751
|
+
config: {
|
|
752
|
+
provider: route.provider,
|
|
753
|
+
model: route.model,
|
|
754
|
+
...(route.reasoningEffort === undefined
|
|
755
|
+
? {}
|
|
756
|
+
: { reasoningEffort: route.reasoningEffort }),
|
|
757
|
+
},
|
|
622
758
|
});
|
|
623
|
-
|
|
759
|
+
const baseline = this.session.requestHeader();
|
|
760
|
+
if (!this.#requestHeaderLogged) {
|
|
761
|
+
this.session.append("request/header", { header, reason: baseline === undefined ? "initial" : "resume" });
|
|
762
|
+
this.#requestHeaderLogged = true;
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
if (baseline === undefined || !headerEquals(baseline, header)) {
|
|
766
|
+
this.session.append("request/header", { header, reason: "change" });
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* Fold the session's LIVE permission state into the codex per-turn overrides
|
|
771
|
+
* and hand them to the client. The app-server accepts `approvalPolicy` and
|
|
772
|
+
* `sandboxPolicy` on every `turn/start` ("for this turn and subsequent
|
|
773
|
+
* turns"), so a `/permission` switch is honored from the NEXT turn on — the
|
|
774
|
+
* capability whose absence forced the SDK line to shadow that command with a
|
|
775
|
+
* refusal. Never throws: any fold failure leaves the previous override in
|
|
776
|
+
* place (the thread's launch posture), which is the safe direction.
|
|
777
|
+
*/
|
|
778
|
+
#syncTurnPolicy() {
|
|
779
|
+
try {
|
|
780
|
+
const events = this.session.snapshotEvents();
|
|
781
|
+
const cwd = this.#runtimeInfo?.cwd?.() ?? "";
|
|
782
|
+
const policy = turnPolicyFromEvents(events, presetFromEvents(events), cwd);
|
|
783
|
+
this.#client.setTurnPolicy(policy);
|
|
784
|
+
trace(`turn policy: approval=${policy.approvalPolicy} sandbox=${policy.sandbox.type}`);
|
|
785
|
+
}
|
|
786
|
+
catch (error) {
|
|
787
|
+
trace(`turn policy fold failed: ${String(error)}`);
|
|
788
|
+
}
|
|
624
789
|
}
|
|
625
790
|
/**
|
|
626
791
|
* Reserve a turn WITHOUT appending anything: the prompt path reserves
|
|
@@ -643,7 +808,15 @@ export class CodexAgent {
|
|
|
643
808
|
this.#reserveTurn();
|
|
644
809
|
this.#localUserPending = localUser;
|
|
645
810
|
this.session.append("turn/start", { turn: this.#dashTurn });
|
|
646
|
-
|
|
811
|
+
// This path opens a turn for a message the CLIENT queued and is now
|
|
812
|
+
// generating, so the child is already running: resolve the route for the
|
|
813
|
+
// log's benefit only. It cannot have moved since the turn it belongs to
|
|
814
|
+
// started, because a provider switch needs a turn boundary.
|
|
815
|
+
const route = this.#resolveRoute();
|
|
816
|
+
this.#turnRoute = route;
|
|
817
|
+
if (route !== undefined)
|
|
818
|
+
this.#commitRequestHeader(route);
|
|
819
|
+
this.#emitRequestContext(route);
|
|
647
820
|
this.session.append("user/message", message, { surfaceOp: "append" });
|
|
648
821
|
}
|
|
649
822
|
#closeTurn(reason) {
|
|
@@ -994,10 +1167,11 @@ export class CodexAgent {
|
|
|
994
1167
|
const assistant = createAssistantMessage({
|
|
995
1168
|
content,
|
|
996
1169
|
source: {
|
|
997
|
-
//
|
|
998
|
-
//
|
|
999
|
-
|
|
1000
|
-
|
|
1170
|
+
// Codex's wire messages carry no attribution at all, so the fallback is
|
|
1171
|
+
// the route the OPEN turn runs — never `options`, which is frozen at
|
|
1172
|
+
// create/resume and would name the provider the session started on.
|
|
1173
|
+
provider: String(message.provider ?? this.#turnRoute?.provider ?? this.options.provider ?? ""),
|
|
1174
|
+
model: String(message.model ?? this.#turnRoute?.model ?? this.options.model ?? ""),
|
|
1001
1175
|
},
|
|
1002
1176
|
});
|
|
1003
1177
|
const bridge = this.#streamBridge;
|