@wrenai/genbi 0.0.4 → 0.0.5
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 +64 -2
- package/dist/assets/{echarts-LTFC-ysd.js → echarts-DYM4hjvk.js} +2 -2
- package/dist/assets/index-A_Wrq7Ey.css +1 -0
- package/dist/assets/{index-Bo--QMUN.js → index-DsH129tV.js} +18 -17
- package/dist/index.html +2 -2
- package/dist-server/harness/bundle/schema.d.ts +38 -0
- package/dist-server/harness/bundle/schema.js +18 -10
- package/dist-server/harness/bundle/version.js +1 -1
- package/dist-server/harness/cli-args.d.ts +10 -2
- package/dist-server/harness/cli-args.js +24 -8
- package/dist-server/harness/cli.js +75 -9
- package/dist-server/harness/compile/cache.js +4 -4
- package/dist-server/harness/compile/compose-profile.d.ts +51 -14
- package/dist-server/harness/compile/compose-profile.js +85 -19
- package/dist-server/harness/compile/context-loader.d.ts +56 -0
- package/dist-server/harness/compile/context-loader.js +237 -0
- package/dist-server/harness/compile/errors.d.ts +19 -0
- package/dist-server/harness/compile/errors.js +35 -0
- package/dist-server/harness/compile/index.d.ts +4 -3
- package/dist-server/harness/compile/index.js +4 -3
- package/dist-server/harness/compile/pipeline.d.ts +15 -7
- package/dist-server/harness/compile/pipeline.js +62 -14
- package/dist-server/harness/compile/resolve-binary.d.ts +2 -2
- package/dist-server/harness/compile/resolve-binary.js +3 -3
- package/dist-server/harness/compile/types.d.ts +29 -0
- package/dist-server/harness/compile/warble-identity.d.ts +11 -0
- package/dist-server/harness/compile/warble-identity.js +13 -0
- package/dist-server/harness/components/ai-step.d.ts +4 -0
- package/dist-server/harness/components/ai-step.js +40 -0
- package/dist-server/harness/components/broker.d.ts +60 -0
- package/dist-server/harness/components/broker.js +117 -0
- package/dist-server/harness/components/capability-card.d.ts +98 -0
- package/dist-server/harness/components/capability-card.js +169 -0
- package/dist-server/harness/components/context.d.ts +2 -0
- package/dist-server/harness/components/context.js +30 -0
- package/dist-server/harness/components/display.d.ts +6 -0
- package/dist-server/harness/components/display.js +29 -0
- package/dist-server/harness/components/egress-judge.d.ts +12 -0
- package/dist-server/harness/components/egress-judge.js +27 -0
- package/dist-server/harness/components/egress.d.ts +142 -0
- package/dist-server/harness/components/egress.js +469 -0
- package/dist-server/harness/components/in-process.d.ts +7 -0
- package/dist-server/harness/components/in-process.js +245 -0
- package/dist-server/harness/components/normalize.d.ts +35 -0
- package/dist-server/harness/components/normalize.js +245 -0
- package/dist-server/harness/components/plan.d.ts +31 -0
- package/dist-server/harness/components/plan.js +184 -0
- package/dist-server/harness/components/report.d.ts +207 -0
- package/dist-server/harness/components/report.js +309 -0
- package/dist-server/harness/components/runner.d.ts +193 -0
- package/dist-server/harness/components/runner.js +492 -0
- package/dist-server/harness/components/semantic-guards.d.ts +20 -0
- package/dist-server/harness/components/semantic-guards.js +42 -0
- package/dist-server/harness/components/terminal-json.d.ts +18 -0
- package/dist-server/harness/components/terminal-json.js +82 -0
- package/dist-server/harness/components/usage.d.ts +14 -0
- package/dist-server/harness/components/usage.js +19 -0
- package/dist-server/harness/components/wren-access.d.ts +29 -0
- package/dist-server/harness/components/wren-access.js +233 -0
- package/dist-server/harness/components/zone-dry-run.d.ts +43 -0
- package/dist-server/harness/components/zone-dry-run.js +107 -0
- package/dist-server/harness/components/zone-gate.d.ts +76 -0
- package/dist-server/harness/components/zone-gate.js +144 -0
- package/dist-server/harness/events/index.d.ts +1 -1
- package/dist-server/harness/events/types.d.ts +52 -0
- package/dist-server/harness/index.d.ts +9 -2
- package/dist-server/harness/index.js +5 -1
- package/dist-server/harness/loop/executor.d.ts +1 -0
- package/dist-server/harness/loop/executor.js +37 -15
- package/dist-server/harness/npm-package-resolve.d.ts +2 -0
- package/dist-server/harness/npm-package-resolve.js +16 -4
- package/dist-server/harness/providers/adapters/openai-compatible.d.ts +13 -0
- package/dist-server/harness/providers/adapters/openai-compatible.js +17 -0
- package/dist-server/harness/providers/adapters/openai.d.ts +7 -0
- package/dist-server/harness/providers/adapters/openai.js +6 -0
- package/dist-server/harness/providers/adapters/pi-ai.d.ts +48 -0
- package/dist-server/harness/providers/adapters/pi-ai.js +518 -0
- package/dist-server/harness/providers/binding.d.ts +45 -4
- package/dist-server/harness/providers/binding.js +44 -8
- package/dist-server/harness/providers/index.d.ts +9 -3
- package/dist-server/harness/providers/index.js +10 -3
- package/dist-server/harness/providers/zone.d.ts +96 -0
- package/dist-server/harness/providers/zone.js +112 -0
- package/dist-server/harness/render/answering-query.d.ts +29 -0
- package/dist-server/harness/render/answering-query.js +62 -0
- package/dist-server/harness/render/envelope.d.ts +6 -0
- package/dist-server/harness/render/envelope.js +10 -3
- package/dist-server/harness/route/adapter-spec.d.ts +8 -9
- package/dist-server/harness/route/adapter-spec.js +13 -14
- package/dist-server/harness/route/codex-ask-events.js +2 -1
- package/dist-server/harness/route/codex-ask.d.ts +4 -0
- package/dist-server/harness/route/codex-ask.js +69 -30
- package/dist-server/harness/route/codex-host-query.d.ts +52 -0
- package/dist-server/harness/route/codex-host-query.js +191 -0
- package/dist-server/harness/route/codex-local-manifest.d.ts +9 -1
- package/dist-server/harness/route/codex-local-manifest.js +96 -15
- package/dist-server/harness/route/codex-wren-proxy.d.ts +33 -0
- package/dist-server/harness/route/codex-wren-proxy.js +227 -0
- package/dist-server/harness/route/describe.js +45 -6
- package/dist-server/harness/route/in-process.d.ts +22 -1
- package/dist-server/harness/route/in-process.js +54 -4
- package/dist-server/harness/route/route.js +5 -2
- package/dist-server/harness/route/types.d.ts +30 -1
- package/dist-server/harness/session/run.js +30 -10
- package/dist-server/harness/setup/runner.d.ts +2 -0
- package/dist-server/harness/setup/runner.js +38 -17
- package/dist-server/harness/setup/yaml-scalar.d.ts +23 -0
- package/dist-server/harness/setup/yaml-scalar.js +62 -0
- package/dist-server/server/adopt.js +1 -17
- package/dist-server/server/app.d.ts +1 -0
- package/dist-server/server/app.js +163 -14
- package/dist-server/server/auth-choice.d.ts +13 -1
- package/dist-server/server/auth-choice.js +66 -1
- package/dist-server/server/bin.js +177 -25
- package/dist-server/server/codex-local-grounding.d.ts +18 -0
- package/dist-server/server/codex-local-grounding.js +92 -0
- package/dist-server/server/codex-native-session.d.ts +25 -0
- package/dist-server/server/codex-native-session.js +1 -0
- package/dist-server/server/codex-runtime-composition.d.ts +34 -0
- package/dist-server/server/codex-runtime-composition.js +250 -0
- package/dist-server/server/codex-structured-ask.d.ts +23 -0
- package/dist-server/server/codex-structured-ask.js +102 -0
- package/dist-server/server/conn-config.js +4 -16
- package/dist-server/server/conversation-socket.d.ts +16 -0
- package/dist-server/server/conversation-socket.js +65 -0
- package/dist-server/server/db.d.ts +46 -0
- package/dist-server/server/db.js +88 -9
- package/dist-server/server/env-detect.d.ts +6 -0
- package/dist-server/server/env-detect.js +7 -0
- package/dist-server/server/fold.d.ts +22 -4
- package/dist-server/server/fold.js +37 -5
- package/dist-server/server/harness.d.ts +2 -2
- package/dist-server/server/harness.js +33 -7
- package/dist-server/server/interactive-terminal.d.ts +10 -6
- package/dist-server/server/interactive-terminal.js +33 -8
- package/dist-server/server/managed-wren-runtime.d.ts +116 -0
- package/dist-server/server/managed-wren-runtime.js +670 -0
- package/dist-server/server/native-artifacts.d.ts +23 -7
- package/dist-server/server/native-artifacts.js +43 -12
- package/dist-server/server/native-component-context.d.ts +19 -0
- package/dist-server/server/native-component-context.js +76 -0
- package/dist-server/server/native-component-preparation.d.ts +20 -0
- package/dist-server/server/native-component-preparation.js +129 -0
- package/dist-server/server/native-component-runtime.d.ts +36 -0
- package/dist-server/server/native-component-runtime.js +92 -0
- package/dist-server/server/native-components.d.ts +84 -0
- package/dist-server/server/native-components.js +160 -0
- package/dist-server/server/native-dispatch-process.d.ts +8 -0
- package/dist-server/server/native-dispatch-process.js +65 -0
- package/dist-server/server/native-runtime-spec.d.ts +89 -0
- package/dist-server/server/native-runtime-spec.js +169 -0
- package/dist-server/server/native-sessions.d.ts +172 -19
- package/dist-server/server/native-sessions.js +839 -244
- package/dist-server/server/native-shutdown.d.ts +6 -0
- package/dist-server/server/native-shutdown.js +28 -0
- package/dist-server/server/native-wren-home.d.ts +18 -0
- package/dist-server/server/native-wren-home.js +262 -0
- package/dist-server/server/node-pty-host.d.ts +2 -1
- package/dist-server/server/node-pty-host.js +3 -2
- package/dist-server/server/profile-registry.d.ts +139 -0
- package/dist-server/server/profile-registry.js +620 -0
- package/dist-server/server/runtime-binding.d.ts +4 -1
- package/dist-server/server/runtime-binding.js +50 -2
- package/dist-server/server/runtime-host/claude-component-step.d.ts +25 -0
- package/dist-server/server/runtime-host/claude-component-step.js +186 -0
- package/dist-server/server/runtime-host/codex-app-server.d.ts +75 -0
- package/dist-server/server/runtime-host/codex-app-server.js +239 -0
- package/dist-server/server/runtime-host/codex-compatibility.d.ts +110 -0
- package/dist-server/server/runtime-host/codex-compatibility.js +162 -0
- package/dist-server/server/runtime-host/codex-component-step.d.ts +19 -0
- package/dist-server/server/runtime-host/codex-component-step.js +308 -0
- package/dist-server/server/runtime-host/codex-conversation.d.ts +109 -0
- package/dist-server/server/runtime-host/codex-conversation.js +295 -0
- package/dist-server/server/runtime-host/codex-direct-tools.d.ts +23 -0
- package/dist-server/server/runtime-host/codex-direct-tools.js +59 -0
- package/dist-server/server/runtime-host/codex-events.d.ts +647 -0
- package/dist-server/server/runtime-host/codex-events.js +66 -0
- package/dist-server/server/runtime-host/codex-policy.d.ts +16 -0
- package/dist-server/server/runtime-host/codex-policy.js +151 -0
- package/dist-server/server/runtime-host/codex-process.d.ts +8 -0
- package/dist-server/server/runtime-host/codex-process.js +96 -0
- package/dist-server/server/runtime-host/codex-rpc.d.ts +47 -0
- package/dist-server/server/runtime-host/codex-rpc.js +182 -0
- package/dist-server/server/runtime-host/codex-session.d.ts +74 -0
- package/dist-server/server/runtime-host/codex-session.js +492 -0
- package/dist-server/server/runtime-host/codex-terminal-protocol.d.ts +35 -0
- package/dist-server/server/runtime-host/codex-terminal-protocol.js +243 -0
- package/dist-server/server/runtime-host/codex-terminal.d.ts +16 -0
- package/dist-server/server/runtime-host/codex-terminal.js +120 -0
- package/dist-server/server/runtime-host/index.d.ts +5 -0
- package/dist-server/server/runtime-host/index.js +3 -0
- package/dist-server/server/runtime-host/local.d.ts +29 -0
- package/dist-server/server/runtime-host/local.js +90 -0
- package/dist-server/server/runtime-host/policy.d.ts +8 -0
- package/dist-server/server/runtime-host/policy.js +78 -0
- package/dist-server/server/runtime-host/types.d.ts +52 -0
- package/dist-server/server/runtime-host/types.js +62 -0
- package/dist-server/server/structured-query-evidence.d.ts +7 -0
- package/dist-server/server/structured-query-evidence.js +47 -0
- package/dist-server/server/structured-runtime.d.ts +52 -0
- package/dist-server/server/structured-runtime.js +130 -0
- package/dist-server/server/structured-scope.d.ts +4 -0
- package/dist-server/server/structured-scope.js +12 -0
- package/dist-server/server/turn.d.ts +6 -1
- package/dist-server/server/turn.js +82 -25
- package/dist-server/server/wire-types.d.ts +67 -3
- package/dist-server/server/wren-profiles.js +1 -1
- package/managed-wren/README.md +60 -0
- package/managed-wren/dependency-wheels.json +282 -0
- package/managed-wren/manifest.json +373 -0
- package/managed-wren/release-inputs.json +45 -0
- package/managed-wren/runtime-tree.cjs +115 -0
- package/managed-wren/source-inputs.json +40 -0
- package/package.json +15 -6
- package/profiles/genbi-default/context/binding.yml +17 -7
- package/profiles/genbi-default/context/context.json +146 -0
- package/profiles/genbi-default/ir.golden.json +39 -43
- package/profiles/genbi-default/profile.yml +31 -0
- package/profiles/genbi-enrich-context/context/binding.yml +14 -3
- package/profiles/genbi-enrich-context/context/context.json +146 -0
- package/profiles/genbi-enrich-context/ir.golden.json +4 -5
- package/profiles/genbi-monitor/context/binding.yml +13 -5
- package/profiles/genbi-monitor/context/context.json +146 -0
- package/profiles/genbi-monitor/ir.golden.json +13 -7
- package/profiles/genbi-report/context/binding.yml +19 -0
- package/profiles/genbi-report/context/context.json +223 -0
- package/profiles/genbi-report/ir.golden.json +328 -0
- package/profiles/genbi-report/profile.yml +38 -0
- package/profiles/genbi-setup/ir.golden.json +3 -1
- package/dist/assets/index-Xs4q7pNo.css +0 -1
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { CODEX_BASELINE_VERSION } from "./codex-compatibility.js";
|
|
3
|
+
const request = z.object({ id: z.union([z.string().max(256), z.number().int()]).optional(), method: z.string().max(128), params: z.record(z.string(), z.unknown()).optional() }).strict();
|
|
4
|
+
const input = z.array(z.object({ type: z.literal("text"), text: z.string().min(1).max(262_144), text_elements: z.array(z.unknown()).max(0).optional() }).strict()).min(1).max(16);
|
|
5
|
+
const record = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
6
|
+
/**
|
|
7
|
+
* Protocol presented to the official remote TUI, not an app-server proxy.
|
|
8
|
+
* Only text submission and interruption reach the governed conversation.
|
|
9
|
+
* Config, filesystem, shell, auth, plugin and cross-thread mutations never do.
|
|
10
|
+
* Catalogs describe this host's restricted surface, not the operator's account.
|
|
11
|
+
*/
|
|
12
|
+
export class CodexTerminalProtocol {
|
|
13
|
+
conversation;
|
|
14
|
+
scope;
|
|
15
|
+
send;
|
|
16
|
+
assertActive;
|
|
17
|
+
onReady;
|
|
18
|
+
initialized = false;
|
|
19
|
+
acknowledged = false;
|
|
20
|
+
started = false;
|
|
21
|
+
submitted = false;
|
|
22
|
+
attachment;
|
|
23
|
+
pendingTurn;
|
|
24
|
+
activeTurn;
|
|
25
|
+
closed = false;
|
|
26
|
+
createdAt = Math.floor(Date.now() / 1000);
|
|
27
|
+
ids = new Set();
|
|
28
|
+
constructor(conversation, scope, send, assertActive, onReady = () => { }) {
|
|
29
|
+
this.conversation = conversation;
|
|
30
|
+
this.scope = scope;
|
|
31
|
+
this.send = send;
|
|
32
|
+
this.assertActive = assertActive;
|
|
33
|
+
this.onReady = onReady;
|
|
34
|
+
}
|
|
35
|
+
close() { this.closed = true; this.attachment?.detach(); this.attachment = undefined; }
|
|
36
|
+
thread() {
|
|
37
|
+
const id = this.conversation.terminalThreadId();
|
|
38
|
+
return { id, sessionId: id, forkedFromId: null, parentThreadId: null, preview: "", ephemeral: true,
|
|
39
|
+
section: null, sectionEnteredAt: null, projectId: null, historyMode: "legacy", modelProvider: "openai", model: this.scope.model,
|
|
40
|
+
reasoningEffort: null, createdAt: this.createdAt, updatedAt: this.createdAt, recencyAt: this.createdAt,
|
|
41
|
+
status: { type: this.activeTurn ? "active" : "idle", ...(this.activeTurn ? { activeFlags: [] } : {}) }, path: null,
|
|
42
|
+
cwd: this.scope.cwd, cliVersion: CODEX_BASELINE_VERSION, originator: "genbi", source: "appServer", threadSource: "user",
|
|
43
|
+
agentNickname: null, agentRole: null, gitInfo: null, name: null, turns: [] };
|
|
44
|
+
}
|
|
45
|
+
result(id, result) { if (!this.closed)
|
|
46
|
+
this.send({ id, result }); }
|
|
47
|
+
error(id, message = "This operation is unavailable in the pinned GenBI session.") {
|
|
48
|
+
if (!this.closed)
|
|
49
|
+
this.send({ id, error: { code: -32600, message } });
|
|
50
|
+
}
|
|
51
|
+
event(frame) {
|
|
52
|
+
if (this.closed || frame.type !== "event")
|
|
53
|
+
return;
|
|
54
|
+
const event = structuredClone(frame.event);
|
|
55
|
+
if (event.method === "turn/started" && record(event.params.turn)) {
|
|
56
|
+
this.activeTurn = String(event.params.turn.id);
|
|
57
|
+
if (this.pendingTurn !== undefined) {
|
|
58
|
+
this.result(this.pendingTurn, { turn: terminalTurn(event.params.turn) });
|
|
59
|
+
this.pendingTurn = undefined;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (record(event.params.turn))
|
|
63
|
+
event.params.turn = terminalTurn(event.params.turn);
|
|
64
|
+
if (record(event.params.item))
|
|
65
|
+
event.params.item = terminalItem(event.params.item);
|
|
66
|
+
this.send(event);
|
|
67
|
+
if (event.method === "turn/completed")
|
|
68
|
+
this.activeTurn = undefined;
|
|
69
|
+
}
|
|
70
|
+
async receive(value) {
|
|
71
|
+
const parsed = request.safeParse(value);
|
|
72
|
+
if (!parsed.success || this.closed)
|
|
73
|
+
throw new Error("Invalid terminal protocol message");
|
|
74
|
+
const { id, method, params = {} } = parsed.data;
|
|
75
|
+
if (method === "initialized" && id === undefined && this.initialized && !this.acknowledged) {
|
|
76
|
+
this.acknowledged = true;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (id === undefined)
|
|
80
|
+
throw new Error("Invalid terminal protocol notification");
|
|
81
|
+
if (this.ids.has(id) || this.ids.size >= 4096)
|
|
82
|
+
throw new Error("Duplicate or excessive terminal request");
|
|
83
|
+
this.ids.add(id);
|
|
84
|
+
try {
|
|
85
|
+
this.assertActive();
|
|
86
|
+
if (method === "initialize") {
|
|
87
|
+
if (this.initialized || !record(params.clientInfo) || params.clientInfo.name !== "codex-tui" || params.clientInfo.version !== CODEX_BASELINE_VERSION)
|
|
88
|
+
throw new Error();
|
|
89
|
+
this.initialized = true;
|
|
90
|
+
this.result(id, { userAgent: `genbi/${CODEX_BASELINE_VERSION}`, codexHome: this.scope.clientHome, platformFamily: "unix", platformOs: "macos" });
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (!this.acknowledged)
|
|
94
|
+
throw new Error();
|
|
95
|
+
// Every scoped request is confined to this process-owned thread/workspace.
|
|
96
|
+
if (params.threadId != null && params.threadId !== this.conversation.terminalThreadId())
|
|
97
|
+
throw new Error();
|
|
98
|
+
if (params.cwd != null && params.cwd !== this.scope.cwd)
|
|
99
|
+
throw new Error();
|
|
100
|
+
if (params.model != null && params.model !== this.scope.model)
|
|
101
|
+
throw new Error();
|
|
102
|
+
switch (method) {
|
|
103
|
+
case "account/read":
|
|
104
|
+
this.result(id, { account: null, requiresOpenaiAuth: false });
|
|
105
|
+
return; // login is owned by the host
|
|
106
|
+
case "config/read":
|
|
107
|
+
this.result(id, { config: { model: this.scope.model, model_provider: "openai", approval_policy: "never", sandbox_mode: "read-only",
|
|
108
|
+
web_search: "disabled", check_for_update_on_startup: false, project_doc_max_bytes: 0, project_root_markers: [],
|
|
109
|
+
projects: { [this.scope.cwd]: { trust_level: "trusted" } }, features: { remote_control: false, hooks: false, plugins: false, apps: false, multi_agent: false },
|
|
110
|
+
history: { persistence: "none" } }, origins: {}, layers: [] });
|
|
111
|
+
return;
|
|
112
|
+
case "configRequirements/read":
|
|
113
|
+
this.result(id, { requirements: { allowedApprovalPolicies: ["never"], allowedSandboxModes: ["read-only"], allowedWebSearchModes: ["disabled"] } });
|
|
114
|
+
return;
|
|
115
|
+
case "model/list":
|
|
116
|
+
this.result(id, { data: [{ id: this.scope.model, model: this.scope.model, displayName: this.scope.model,
|
|
117
|
+
description: "GenBI session model", hidden: false, isDefault: true, upgrade: null, upgradeInfo: null,
|
|
118
|
+
supportedReasoningEfforts: [], defaultReasoningEffort: "medium", inputModalities: ["text"], supportsPersonality: false }], nextCursor: null });
|
|
119
|
+
return;
|
|
120
|
+
case "hooks/list":
|
|
121
|
+
this.result(id, { data: [{ cwd: this.scope.cwd, hooks: [], warnings: [], errors: [] }] });
|
|
122
|
+
return;
|
|
123
|
+
case "skills/list":
|
|
124
|
+
this.result(id, { data: [{ cwd: this.scope.cwd, skills: [], errors: [] }] });
|
|
125
|
+
return;
|
|
126
|
+
case "plugin/list":
|
|
127
|
+
this.result(id, { marketplaces: [], marketplaceLoadErrors: [], featuredPluginIds: [] });
|
|
128
|
+
return;
|
|
129
|
+
case "app/installed":
|
|
130
|
+
this.result(id, { apps: [] });
|
|
131
|
+
return;
|
|
132
|
+
case "mcpServerStatus/list":
|
|
133
|
+
this.result(id, { data: [], nextCursor: null });
|
|
134
|
+
return;
|
|
135
|
+
case "collaborationMode/list":
|
|
136
|
+
this.result(id, { data: [] });
|
|
137
|
+
return;
|
|
138
|
+
case "thread/loaded/list":
|
|
139
|
+
this.result(id, { data: this.started ? [this.conversation.terminalThreadId()] : [], nextCursor: null });
|
|
140
|
+
return;
|
|
141
|
+
case "thread/list":
|
|
142
|
+
this.result(id, { data: [], nextCursor: null, backwardsCursor: null });
|
|
143
|
+
return;
|
|
144
|
+
case "thread/read":
|
|
145
|
+
if (!this.started || params.includeTurns === true)
|
|
146
|
+
throw new Error();
|
|
147
|
+
this.result(id, { thread: this.thread() });
|
|
148
|
+
return;
|
|
149
|
+
case "thread/turns/list":
|
|
150
|
+
if (!this.started || this.submitted)
|
|
151
|
+
throw new Error();
|
|
152
|
+
this.result(id, { data: [], nextCursor: null, backwardsCursor: null });
|
|
153
|
+
return;
|
|
154
|
+
case "thread/start": {
|
|
155
|
+
if (this.started)
|
|
156
|
+
throw new Error();
|
|
157
|
+
// Client-supplied policy, tools and instructions are never forwarded.
|
|
158
|
+
// The already-created host thread owns all execution authority.
|
|
159
|
+
this.started = true;
|
|
160
|
+
this.attachment = this.conversation.attach(this.conversation.capability, (f) => this.event(f), this.conversation.snapshot().sequence);
|
|
161
|
+
const thread = this.thread();
|
|
162
|
+
this.result(id, { thread, model: this.scope.model, modelProvider: "openai", serviceTier: null, disabledPluginIds: [], cwd: this.scope.cwd,
|
|
163
|
+
runtimeWorkspaceRoots: [], instructionSources: [], approvalPolicy: "never", approvalsReviewer: "user",
|
|
164
|
+
sandbox: { type: "readOnly", networkAccess: false }, activePermissionProfile: null, reasoningEffort: null });
|
|
165
|
+
this.send({ method: "thread/started", params: { thread } });
|
|
166
|
+
this.onReady();
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
case "turn/start": {
|
|
170
|
+
if (!this.started || !this.attachment || this.pendingTurn !== undefined || this.activeTurn)
|
|
171
|
+
throw new Error();
|
|
172
|
+
const text = input.parse(params.input).map((part) => part.text).join("\n");
|
|
173
|
+
if (Buffer.byteLength(text) > 262_144)
|
|
174
|
+
throw new Error();
|
|
175
|
+
// Only plain text enters the driver. Policy/model/config overrides,
|
|
176
|
+
// injected tool outputs and collaboration instructions are rejected.
|
|
177
|
+
for (const [key, val] of Object.entries(params)) {
|
|
178
|
+
if (["threadId", "input", "cwd", "model", "clientUserMessageId"].includes(key) || val == null)
|
|
179
|
+
continue;
|
|
180
|
+
if (key === "approvalPolicy" && val === "never")
|
|
181
|
+
continue;
|
|
182
|
+
if (key === "approvalsReviewer" && val === "user")
|
|
183
|
+
continue;
|
|
184
|
+
if (key === "turnTrigger" && val === "user")
|
|
185
|
+
continue;
|
|
186
|
+
if (key === "serviceTier" && val === "default")
|
|
187
|
+
continue;
|
|
188
|
+
if (key === "runtimeWorkspaceRoots" && Array.isArray(val) && val.length === 0)
|
|
189
|
+
continue;
|
|
190
|
+
if (key === "sandboxPolicy" && record(val) && val.type === "readOnly" && val.networkAccess !== true)
|
|
191
|
+
continue;
|
|
192
|
+
throw new Error();
|
|
193
|
+
}
|
|
194
|
+
this.pendingTurn = id;
|
|
195
|
+
this.submitted = true;
|
|
196
|
+
void Promise.resolve().then(() => this.attachment.submit(text)).catch(() => { if (this.pendingTurn !== undefined)
|
|
197
|
+
this.error(this.pendingTurn, "GenBI could not complete the turn."); this.pendingTurn = undefined; });
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
case "turn/steer": {
|
|
201
|
+
if (!this.activeTurn || params.expectedTurnId !== this.activeTurn || !this.attachment?.steer)
|
|
202
|
+
throw new Error();
|
|
203
|
+
const text = input.parse(params.input).map((part) => part.text).join("\n");
|
|
204
|
+
if (Buffer.byteLength(text) > 262_144)
|
|
205
|
+
throw new Error();
|
|
206
|
+
for (const [key, value] of Object.entries(params)) {
|
|
207
|
+
if (["threadId", "expectedTurnId", "input", "clientUserMessageId"].includes(key) || value == null)
|
|
208
|
+
continue;
|
|
209
|
+
throw new Error();
|
|
210
|
+
}
|
|
211
|
+
this.result(id, await this.attachment.steer(text, this.activeTurn));
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
case "turn/interrupt":
|
|
215
|
+
if (!this.activeTurn || params.turnId !== this.activeTurn || !this.attachment)
|
|
216
|
+
throw new Error();
|
|
217
|
+
await this.attachment.interrupt();
|
|
218
|
+
this.result(id, {});
|
|
219
|
+
return;
|
|
220
|
+
default:
|
|
221
|
+
this.error(id);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
this.error(id);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
function terminalTurn(turn) {
|
|
231
|
+
return { ...turn, items: Array.isArray(turn.items) ? turn.items.map((i) => record(i) ? terminalItem(i) : i) : [], itemsView: turn.itemsView ?? "full",
|
|
232
|
+
error: turn.status === "failed" ? { message: "GenBI execution failed.", codexErrorInfo: null, additionalDetails: null } : null,
|
|
233
|
+
startedAt: null, completedAt: null, durationMs: null };
|
|
234
|
+
}
|
|
235
|
+
function terminalItem(item) {
|
|
236
|
+
if (item.type === "userMessage" && Array.isArray(item.content))
|
|
237
|
+
return { ...item, content: item.content.map((p) => record(p) ? { ...p, text_elements: [] } : p) };
|
|
238
|
+
if (item.type === "agentMessage")
|
|
239
|
+
return { ...item, phase: null };
|
|
240
|
+
if (item.type === "dynamicToolCall")
|
|
241
|
+
return { ...item, arguments: {}, contentItems: null, durationMs: null };
|
|
242
|
+
return item;
|
|
243
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type InteractiveTerminalManager, type InteractiveTerminalSession } from "../interactive-terminal.js";
|
|
2
|
+
import { type NativeExecutableIdentity } from "../native-runtime-spec.js";
|
|
3
|
+
import type { CodexConversation } from "./codex-conversation.js";
|
|
4
|
+
/** Official CLI renderer with a credential-free home and a private Unix socket. */
|
|
5
|
+
export declare function startCodexTerminal(input: {
|
|
6
|
+
id: string;
|
|
7
|
+
conversation: CodexConversation;
|
|
8
|
+
manager: InteractiveTerminalManager;
|
|
9
|
+
vendor: NativeExecutableIdentity;
|
|
10
|
+
cwd: string;
|
|
11
|
+
model: string;
|
|
12
|
+
assertActive(): void;
|
|
13
|
+
}): Promise<{
|
|
14
|
+
terminal: InteractiveTerminalSession;
|
|
15
|
+
close(): Promise<void>;
|
|
16
|
+
}>;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { chmodSync, mkdirSync, mkdtempSync, realpathSync, rmSync } from "node:fs";
|
|
2
|
+
import { createServer } from "node:http";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { WebSocketServer } from "ws";
|
|
5
|
+
import { NO_INTERACTIVE_TERMINAL_LEASES } from "../interactive-terminal.js";
|
|
6
|
+
import { assertNativeExecutableIdentity } from "../native-runtime-spec.js";
|
|
7
|
+
import { CodexTerminalProtocol } from "./codex-terminal-protocol.js";
|
|
8
|
+
/** Official CLI renderer with a credential-free home and a private Unix socket. */
|
|
9
|
+
export async function startCodexTerminal(input) {
|
|
10
|
+
input.assertActive();
|
|
11
|
+
assertNativeExecutableIdentity(input.vendor);
|
|
12
|
+
// Short path for macOS sockaddr_un; mkdtemp creates an owner-only directory.
|
|
13
|
+
const root = realpathSync(mkdtempSync("/private/tmp/genbi-terminal-"));
|
|
14
|
+
const home = path.join(root, "home");
|
|
15
|
+
mkdirSync(home, { mode: 0o700 });
|
|
16
|
+
const socket = path.join(root, "rpc.sock");
|
|
17
|
+
const server = createServer((_req, res) => { res.writeHead(404); res.end(); });
|
|
18
|
+
const wss = new WebSocketServer({ noServer: true, maxPayload: 1_048_576, perMessageDeflate: false });
|
|
19
|
+
let claimed = false;
|
|
20
|
+
let protocol;
|
|
21
|
+
let terminal;
|
|
22
|
+
let closing;
|
|
23
|
+
let exited = false;
|
|
24
|
+
let resolveExit;
|
|
25
|
+
const exit = new Promise((resolve) => { resolveExit = resolve; });
|
|
26
|
+
let ready;
|
|
27
|
+
let failed;
|
|
28
|
+
const handshake = new Promise((resolve, reject) => { ready = resolve; failed = reject; });
|
|
29
|
+
void handshake.catch(() => { });
|
|
30
|
+
server.on("upgrade", (req, connection, head) => {
|
|
31
|
+
if (claimed || closing || req.headers.origin || req.url !== "/rpc") {
|
|
32
|
+
connection.destroy();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
claimed = true;
|
|
36
|
+
wss.handleUpgrade(req, connection, head, (ws) => {
|
|
37
|
+
protocol = new CodexTerminalProtocol(input.conversation, { cwd: input.cwd, clientHome: home, model: input.model }, (message) => {
|
|
38
|
+
if (ws.readyState !== ws.OPEN || ws.bufferedAmount > 1_048_576)
|
|
39
|
+
throw new Error("Terminal connection unavailable");
|
|
40
|
+
ws.send(JSON.stringify(message));
|
|
41
|
+
}, input.assertActive, ready);
|
|
42
|
+
ws.on("message", (bytes, binary) => {
|
|
43
|
+
if (binary) {
|
|
44
|
+
ws.terminate();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
void protocol.receive(JSON.parse(bytes.toString())).catch(() => ws.terminate());
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
ws.terminate();
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
ws.on("error", () => ws.terminate());
|
|
55
|
+
ws.on("close", () => { protocol?.close(); failed(new Error("Terminal connection closed")); terminal?.close(); });
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
const close = () => closing ??= (async () => {
|
|
59
|
+
protocol?.close();
|
|
60
|
+
terminal?.close();
|
|
61
|
+
for (const ws of wss.clients)
|
|
62
|
+
ws.terminate();
|
|
63
|
+
await new Promise((resolve) => wss.close(() => resolve()));
|
|
64
|
+
if (server.listening)
|
|
65
|
+
await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
|
|
66
|
+
if (terminal && !exited) {
|
|
67
|
+
let timer;
|
|
68
|
+
try {
|
|
69
|
+
await Promise.race([exit, new Promise((_, reject) => { timer = setTimeout(() => reject(new Error("Terminal cleanup failed")), 2_000); })]);
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
clearTimeout(timer);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
rmSync(root, { recursive: true });
|
|
76
|
+
})();
|
|
77
|
+
try {
|
|
78
|
+
await new Promise((resolve, reject) => { server.once("error", reject); server.listen(socket, () => { server.removeListener("error", reject); resolve(); }); });
|
|
79
|
+
chmodSync(socket, 0o600);
|
|
80
|
+
input.assertActive();
|
|
81
|
+
assertNativeExecutableIdentity(input.vendor);
|
|
82
|
+
const filesystem = { ":minimal": "read", "/private/tmp": "deny", "/private/var/tmp": "deny",
|
|
83
|
+
[path.dirname(input.vendor.executable)]: "read", [input.cwd]: "read", [root]: "write" };
|
|
84
|
+
const filesystemToml = `{${Object.entries(filesystem).map(([key, value]) => `${JSON.stringify(key)}=${JSON.stringify(value)}`).join(",")}}`;
|
|
85
|
+
terminal = input.manager.start({ version: "5", target: "codex:interactive", executable: "codex", hostExecutable: input.vendor.executable,
|
|
86
|
+
argv: ["sandbox", "-C", root, "-P", "genbi-terminal", "-c", `permissions.genbi-terminal.filesystem=${filesystemToml}`,
|
|
87
|
+
"-c", "permissions.genbi-terminal.network.enabled=false", "--allow-unix-socket", socket, "--", input.vendor.executable,
|
|
88
|
+
"--remote", `unix://${socket}`, "--no-alt-screen", "-C", input.cwd, "-c", "check_for_update_on_startup=false"],
|
|
89
|
+
cwd: root, artifact_root: root, handoff_path: "" }, { id: input.id, capability: input.conversation.capability }, { HOME: home, CODEX_HOME: home, PATH: "/usr/bin:/bin" }, NO_INTERACTIVE_TERMINAL_LEASES);
|
|
90
|
+
// This credential-free renderer starts before the browser attaches. Answer
|
|
91
|
+
// its initial terminal capability probes; xterm owns replies after startup.
|
|
92
|
+
let probeTail = "";
|
|
93
|
+
const stopProbe = terminal.onData((data) => {
|
|
94
|
+
probeTail = (probeTail + data).slice(-1024);
|
|
95
|
+
if (probeTail.includes("\x1b[6n")) {
|
|
96
|
+
terminal.write("\x1b[1;1R");
|
|
97
|
+
probeTail = probeTail.replaceAll("\x1b[6n", "");
|
|
98
|
+
}
|
|
99
|
+
if (probeTail.includes("\x1b]11;?")) {
|
|
100
|
+
terminal.write("\x1b]11;rgb:0000/0000/0000\x1b\\");
|
|
101
|
+
probeTail = probeTail.replaceAll("\x1b]11;?", "");
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
terminal.onExit(() => { exited = true; resolveExit(); failed(new Error("Terminal exited before startup")); });
|
|
105
|
+
let timeout;
|
|
106
|
+
try {
|
|
107
|
+
await Promise.race([handshake, new Promise((_, reject) => { timeout = setTimeout(() => reject(new Error("Terminal startup timed out")), 15_000); })]);
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
clearTimeout(timeout);
|
|
111
|
+
stopProbe();
|
|
112
|
+
}
|
|
113
|
+
input.assertActive();
|
|
114
|
+
return { terminal, close };
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
await close();
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { RuntimeHost } from "./local.js";
|
|
2
|
+
export { runtimeNotReady, runtimeReady, sanitizeRuntimeDiagnostic, sanitizeRuntimeReadiness } from "./policy.js";
|
|
3
|
+
export { RUNTIME_BACKEND_IDS, RUNTIME_BACKEND_REASON_CODES, RUNTIME_CAPABILITIES } from "./types.js";
|
|
4
|
+
export type { RuntimeDeployment, RuntimeHostOptions, VendorRuntimeProbes } from "./local.js";
|
|
5
|
+
export type { RuntimeBackendDiagnostic, RuntimeBackendId, RuntimeBackendNotReady, RuntimeBackendProbeResult, RuntimeBackendReadiness, RuntimeBackendReasonCode, RuntimeBackendReady, RuntimeCapability, RuntimeDiagnostics, RuntimeReadiness, } from "./types.js";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { RuntimeBackendId, RuntimeBackendProbeResult, RuntimeDiagnostics, RuntimeReadiness } from "./types.js";
|
|
2
|
+
export type RuntimeDeployment = "development" | "production";
|
|
3
|
+
export interface VendorRuntimeProbes {
|
|
4
|
+
readonly "codex-app-server"?: () => Promise<RuntimeBackendProbeResult<"codex-app-server">>;
|
|
5
|
+
readonly "claude-sandbox-runtime"?: () => Promise<RuntimeBackendProbeResult<"claude-sandbox-runtime">>;
|
|
6
|
+
}
|
|
7
|
+
export interface RuntimeHostOptions {
|
|
8
|
+
/** Server-owned selection. Browser input is deliberately absent from this type. */
|
|
9
|
+
readonly selected: RuntimeBackendId;
|
|
10
|
+
readonly deployment: RuntimeDeployment;
|
|
11
|
+
readonly localAvailable: () => boolean | Promise<boolean>;
|
|
12
|
+
/** Internal test/adaptor seam. Future vendor adapters must supply this server-side. */
|
|
13
|
+
readonly vendorProbes?: VendorRuntimeProbes;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Phase 1 read-only runtime boundary. It does not launch vendor processes;
|
|
17
|
+
* production adapters will replace only their own probe seam in later phases.
|
|
18
|
+
*/
|
|
19
|
+
export declare class RuntimeHost {
|
|
20
|
+
private readonly options;
|
|
21
|
+
private diagnostics?;
|
|
22
|
+
constructor(options: RuntimeHostOptions);
|
|
23
|
+
private probeLocal;
|
|
24
|
+
private probeCodex;
|
|
25
|
+
private probeClaude;
|
|
26
|
+
probe(): Promise<RuntimeReadiness>;
|
|
27
|
+
/** Host-only support evidence; never include this object in an API response. */
|
|
28
|
+
serverDiagnostics(): RuntimeDiagnostics | undefined;
|
|
29
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { runtimeNotReady, runtimeReady, sanitizeRuntimeDiagnostic, sanitizeRuntimeReadiness, } from "./policy.js";
|
|
2
|
+
const fixedCodexProbe = () => ({
|
|
3
|
+
// Phase 2A has no managed-runtime record/provisioner yet. This is the first
|
|
4
|
+
// blocking link for every Codex launch, even while the development-local
|
|
5
|
+
// PTY host itself is healthy.
|
|
6
|
+
readiness: runtimeNotReady("codex-app-server", "unprovisioned", "codex_wren_runtime_unprovisioned"),
|
|
7
|
+
diagnostic: { phase: "provisioning" },
|
|
8
|
+
});
|
|
9
|
+
const fixedClaudeProbe = () => ({
|
|
10
|
+
readiness: runtimeNotReady("claude-sandbox-runtime", "unprovisioned", "claude_sandbox_runtime_unprovisioned"),
|
|
11
|
+
diagnostic: { phase: "provisioning" },
|
|
12
|
+
});
|
|
13
|
+
/**
|
|
14
|
+
* Phase 1 read-only runtime boundary. It does not launch vendor processes;
|
|
15
|
+
* production adapters will replace only their own probe seam in later phases.
|
|
16
|
+
*/
|
|
17
|
+
export class RuntimeHost {
|
|
18
|
+
options;
|
|
19
|
+
diagnostics;
|
|
20
|
+
constructor(options) {
|
|
21
|
+
this.options = options;
|
|
22
|
+
}
|
|
23
|
+
async probeLocal() {
|
|
24
|
+
if (this.options.deployment === "production") {
|
|
25
|
+
return {
|
|
26
|
+
readiness: runtimeNotReady("local", "unavailable", "local_runtime_production_forbidden"),
|
|
27
|
+
diagnostic: { phase: "policy" },
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
if (await this.options.localAvailable()) {
|
|
32
|
+
return {
|
|
33
|
+
readiness: runtimeReady("development-local", ["pty", "terminal_replay", "terminal_resize", "terminal_terminate"]),
|
|
34
|
+
diagnostic: { phase: "capability", requiredCapability: "pty" },
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// The browser gets the same fixed result as a false host probe.
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
readiness: runtimeNotReady("local", "unavailable", "local_runtime_host_unavailable"),
|
|
43
|
+
diagnostic: { phase: "capability", requiredCapability: "pty" },
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
async probeCodex() {
|
|
47
|
+
try {
|
|
48
|
+
return await (this.options.vendorProbes?.["codex-app-server"]?.() ?? Promise.resolve(fixedCodexProbe()));
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return {
|
|
52
|
+
readiness: runtimeNotReady("codex-app-server", "unavailable", "runtime_probe_failed"),
|
|
53
|
+
diagnostic: { phase: "probe" },
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async probeClaude() {
|
|
58
|
+
try {
|
|
59
|
+
return await (this.options.vendorProbes?.["claude-sandbox-runtime"]?.() ?? Promise.resolve(fixedClaudeProbe()));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return {
|
|
63
|
+
readiness: runtimeNotReady("claude-sandbox-runtime", "unavailable", "runtime_probe_failed"),
|
|
64
|
+
diagnostic: { phase: "probe" },
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async probe() {
|
|
69
|
+
const [local, codex, claude] = await Promise.all([
|
|
70
|
+
this.probeLocal(),
|
|
71
|
+
this.probeCodex(),
|
|
72
|
+
this.probeClaude(),
|
|
73
|
+
]);
|
|
74
|
+
const backends = {
|
|
75
|
+
local: sanitizeRuntimeReadiness("local", local.readiness),
|
|
76
|
+
"codex-app-server": sanitizeRuntimeReadiness("codex-app-server", codex.readiness),
|
|
77
|
+
"claude-sandbox-runtime": sanitizeRuntimeReadiness("claude-sandbox-runtime", claude.readiness),
|
|
78
|
+
};
|
|
79
|
+
this.diagnostics = {
|
|
80
|
+
local: sanitizeRuntimeDiagnostic(local.diagnostic),
|
|
81
|
+
"codex-app-server": sanitizeRuntimeDiagnostic(codex.diagnostic),
|
|
82
|
+
"claude-sandbox-runtime": sanitizeRuntimeDiagnostic(claude.diagnostic),
|
|
83
|
+
};
|
|
84
|
+
return { selected: this.options.selected, selectedReadiness: backends[this.options.selected], backends };
|
|
85
|
+
}
|
|
86
|
+
/** Host-only support evidence; never include this object in an API response. */
|
|
87
|
+
serverDiagnostics() {
|
|
88
|
+
return this.diagnostics;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type RuntimeBackendDiagnostic, type RuntimeBackendId, type RuntimeBackendNotReady, type RuntimeBackendReadiness, type RuntimeBackendReasonCode, type RuntimeBackendReady, type RuntimeCapability, type RuntimeReadinessState } from "./types.js";
|
|
2
|
+
export declare function isRuntimeReasonCode<B extends RuntimeBackendId>(backend: B, code: string): code is RuntimeBackendReasonCode<B>;
|
|
3
|
+
export declare function runtimeNotReady<B extends RuntimeBackendId>(backend: B, state: Exclude<RuntimeReadinessState, "ready">, code: RuntimeBackendReasonCode<B>): RuntimeBackendNotReady<B>;
|
|
4
|
+
export declare function runtimeReady(version: string, capabilities: readonly RuntimeCapability[]): RuntimeBackendReady;
|
|
5
|
+
/** Rebuild a probe result so server-only probe seams cannot leak custom text. */
|
|
6
|
+
export declare function sanitizeRuntimeReadiness<B extends RuntimeBackendId>(backend: B, readiness: RuntimeBackendReadiness<B>): RuntimeBackendReadiness<B>;
|
|
7
|
+
/** Keep only parsed support evidence. This return value is never an API DTO. */
|
|
8
|
+
export declare function sanitizeRuntimeDiagnostic(diagnostic: RuntimeBackendDiagnostic): RuntimeBackendDiagnostic;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { RUNTIME_BACKEND_REASON_CODES, RUNTIME_CAPABILITIES, } from "./types.js";
|
|
2
|
+
const MESSAGES = {
|
|
3
|
+
local_runtime_disabled: "The local development runtime is disabled by server policy.",
|
|
4
|
+
local_runtime_host_unavailable: "The local development runtime cannot start a terminal host on this machine.",
|
|
5
|
+
local_runtime_production_forbidden: "The local development runtime is not available in production.",
|
|
6
|
+
runtime_probe_failed: "The runtime readiness probe could not complete.",
|
|
7
|
+
codex_app_server_unprovisioned: "The Codex app-server runtime has not been provisioned by this GenBI release.",
|
|
8
|
+
codex_cli_missing: "The required Codex CLI is not available.",
|
|
9
|
+
codex_cli_version_malformed: "The installed Codex CLI version is invalid.",
|
|
10
|
+
codex_cli_version_unsupported: "The installed Codex CLI version is not supported by this GenBI release.",
|
|
11
|
+
codex_identity_uncertified: "This Codex version has test evidence, but this installed identity is not certified.",
|
|
12
|
+
codex_dependencies_drifted: "Codex native is certified for other Warble, IR or context-loader versions than this install uses; re-certify Codex on the installed versions to enable it.",
|
|
13
|
+
codex_app_server_unreachable: "The Codex app-server runtime is unavailable.",
|
|
14
|
+
codex_app_server_cleanup_failed: "Codex runtime cleanup could not be confirmed. The runtime generation has been retained.",
|
|
15
|
+
codex_app_server_protocol_incompatible: "The Codex app-server protocol is incompatible with this GenBI release.",
|
|
16
|
+
codex_sandbox_policy_unavailable: "The required Codex sandbox policy is unavailable.",
|
|
17
|
+
codex_permission_profile_unavailable: "The required Codex permission profile is unavailable.",
|
|
18
|
+
codex_wren_manifest_missing: "The managed Wren runtime manifest is unavailable.",
|
|
19
|
+
codex_wren_manifest_invalid: "The managed Wren runtime manifest is invalid.",
|
|
20
|
+
codex_wren_platform_unsupported: "The managed Wren runtime does not support this platform.",
|
|
21
|
+
codex_wren_runtime_unprovisioned: "The managed Wren runtime has not been provisioned.",
|
|
22
|
+
codex_wren_provision_failed: "The managed Wren runtime could not be provisioned.",
|
|
23
|
+
codex_wren_interpreter_mismatch: "The managed Wren interpreter identity does not match this GenBI release.",
|
|
24
|
+
codex_wren_launcher_mismatch: "The managed Wren launcher identity does not match this GenBI release.",
|
|
25
|
+
codex_wren_package_mismatch: "The managed Wren package identity does not match this GenBI release.",
|
|
26
|
+
codex_wren_closure_mismatch: "The managed Wren runtime closure does not match this GenBI release.",
|
|
27
|
+
codex_wren_profile_unavailable: "The bound Wren profile is unavailable to this session.",
|
|
28
|
+
codex_wren_secret_unavailable: "A secret required by the bound Wren profile is unavailable.",
|
|
29
|
+
codex_wren_session_home_unavailable: "The private Wren session home is unavailable.",
|
|
30
|
+
codex_wren_child_env_invalid: "The managed Wren child environment is invalid.",
|
|
31
|
+
claude_sandbox_runtime_unprovisioned: "The Claude sandbox runtime has not been provisioned by this GenBI release.",
|
|
32
|
+
claude_cli_missing: "The required Claude CLI is not available.",
|
|
33
|
+
claude_cli_version_malformed: "The installed Claude CLI version is invalid.",
|
|
34
|
+
claude_cli_version_unsupported: "The installed Claude CLI version is not supported by this GenBI release.",
|
|
35
|
+
claude_identity_uncertified: "This Claude runtime version has test evidence, but this installed identity is not certified.",
|
|
36
|
+
claude_sandbox_runtime_missing: "The required Claude sandbox runtime is unavailable.",
|
|
37
|
+
claude_sandbox_runtime_version_malformed: "The installed Claude sandbox runtime version is invalid.",
|
|
38
|
+
claude_sandbox_runtime_version_unsupported: "The installed Claude sandbox runtime version is not supported by this GenBI release.",
|
|
39
|
+
claude_sandbox_capability_missing: "The required Claude sandbox capability is unavailable.",
|
|
40
|
+
runtime_platform_unsupported: "This runtime backend is not supported on this platform.",
|
|
41
|
+
runtime_policy_unavailable: "The runtime policy required by this backend is unavailable.",
|
|
42
|
+
};
|
|
43
|
+
const SAFE_VERSION = /^(?:[0-9]+(?:\.[0-9]+){1,3}(?:[-+._][A-Za-z0-9]+)?|development-local)$/;
|
|
44
|
+
const SAFE_IDENTITY = /^sha256:[a-f0-9]{64}$/;
|
|
45
|
+
export function isRuntimeReasonCode(backend, code) {
|
|
46
|
+
return RUNTIME_BACKEND_REASON_CODES[backend].includes(code);
|
|
47
|
+
}
|
|
48
|
+
export function runtimeNotReady(backend, state, code) {
|
|
49
|
+
return { state, code, message: MESSAGES[code] };
|
|
50
|
+
}
|
|
51
|
+
export function runtimeReady(version, capabilities) {
|
|
52
|
+
return {
|
|
53
|
+
state: "ready",
|
|
54
|
+
version: SAFE_VERSION.test(version) ? version : "development-local",
|
|
55
|
+
capabilities: capabilities.filter((capability) => RUNTIME_CAPABILITIES.includes(capability)),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** Rebuild a probe result so server-only probe seams cannot leak custom text. */
|
|
59
|
+
export function sanitizeRuntimeReadiness(backend, readiness) {
|
|
60
|
+
if (readiness.state === "ready")
|
|
61
|
+
return runtimeReady(readiness.version, readiness.capabilities);
|
|
62
|
+
return isRuntimeReasonCode(backend, readiness.code)
|
|
63
|
+
? runtimeNotReady(backend, readiness.state, readiness.code)
|
|
64
|
+
: runtimeNotReady(backend, "unavailable", "runtime_probe_failed");
|
|
65
|
+
}
|
|
66
|
+
/** Keep only parsed support evidence. This return value is never an API DTO. */
|
|
67
|
+
export function sanitizeRuntimeDiagnostic(diagnostic) {
|
|
68
|
+
return {
|
|
69
|
+
phase: diagnostic.phase,
|
|
70
|
+
...(diagnostic.observedVersion && SAFE_VERSION.test(diagnostic.observedVersion) ? { observedVersion: diagnostic.observedVersion } : {}),
|
|
71
|
+
...(diagnostic.requiredCapability && RUNTIME_CAPABILITIES.includes(diagnostic.requiredCapability)
|
|
72
|
+
? { requiredCapability: diagnostic.requiredCapability }
|
|
73
|
+
: {}),
|
|
74
|
+
...(diagnostic.attestedIdentity && SAFE_IDENTITY.test(diagnostic.attestedIdentity)
|
|
75
|
+
? { attestedIdentity: diagnostic.attestedIdentity }
|
|
76
|
+
: {}),
|
|
77
|
+
};
|
|
78
|
+
}
|