@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,109 @@
|
|
|
1
|
+
import { type CodexAppServerBackend, type CodexLaunchPermit } from "./codex-app-server.js";
|
|
2
|
+
import { type CodexEvent } from "./codex-events.js";
|
|
3
|
+
import type { CodexSession } from "./codex-session.js";
|
|
4
|
+
import type { RuntimeBackendReasonCode } from "./types.js";
|
|
5
|
+
export type ConversationState = "opening" | "ready" | "running" | "closing" | "closed" | "failed";
|
|
6
|
+
export type ConversationFailure = "unavailable" | "protocol" | "timeout" | "cancelled" | "cleanup" | "delivery";
|
|
7
|
+
export declare class CodexConversationError extends Error {
|
|
8
|
+
readonly code: ConversationFailure | "attachment" | "busy" | "input" | "closed";
|
|
9
|
+
readonly backendReason?: RuntimeBackendReasonCode<"codex-app-server"> | undefined;
|
|
10
|
+
constructor(code: ConversationFailure | "attachment" | "busy" | "input" | "closed", backendReason?: RuntimeBackendReasonCode<"codex-app-server"> | undefined);
|
|
11
|
+
}
|
|
12
|
+
type ConversationEvent = Exclude<CodexEvent, {
|
|
13
|
+
method: "command/exec/outputDelta" | "remoteControl/status/changed" | "thread/started";
|
|
14
|
+
}>;
|
|
15
|
+
export type ConversationFrame = {
|
|
16
|
+
readonly type: "event";
|
|
17
|
+
readonly sequence: number;
|
|
18
|
+
readonly event: ConversationEvent;
|
|
19
|
+
} | {
|
|
20
|
+
readonly type: "state";
|
|
21
|
+
readonly sequence: number;
|
|
22
|
+
readonly state: ConversationState;
|
|
23
|
+
readonly failure?: ConversationFailure;
|
|
24
|
+
readonly backendReason?: RuntimeBackendReasonCode<"codex-app-server">;
|
|
25
|
+
};
|
|
26
|
+
export interface ConversationReplay {
|
|
27
|
+
readonly type: "replay";
|
|
28
|
+
readonly afterSequence: number;
|
|
29
|
+
readonly throughSequence: number;
|
|
30
|
+
readonly truncated: boolean;
|
|
31
|
+
readonly retainedBytes: number;
|
|
32
|
+
readonly retentionLimitBytes: number;
|
|
33
|
+
readonly state: ConversationState;
|
|
34
|
+
readonly failure?: ConversationFailure;
|
|
35
|
+
readonly backendReason?: RuntimeBackendReasonCode<"codex-app-server">;
|
|
36
|
+
}
|
|
37
|
+
export interface CodexConversationAttachment {
|
|
38
|
+
submit(text: string): Promise<{
|
|
39
|
+
readonly turnId: string;
|
|
40
|
+
readonly status: "completed" | "interrupted" | "failed";
|
|
41
|
+
}>;
|
|
42
|
+
interrupt(): Promise<void>;
|
|
43
|
+
steer?(text: string, expectedTurnId: string): Promise<{
|
|
44
|
+
turnId: string;
|
|
45
|
+
}>;
|
|
46
|
+
detach(): void;
|
|
47
|
+
}
|
|
48
|
+
type Driver = Pick<CodexSession, "startThread" | "runTurn" | "interruptTurn" | "close" | "onFailure"> & Partial<Pick<CodexSession, "steerTurn">>;
|
|
49
|
+
type OpenInput = Omit<Parameters<CodexAppServerBackend["open"]>[1], "onEvent">;
|
|
50
|
+
interface Backend {
|
|
51
|
+
open(permit: CodexLaunchPermit, input: OpenInput & {
|
|
52
|
+
onEvent(event: CodexEvent): void;
|
|
53
|
+
}): Promise<Driver>;
|
|
54
|
+
}
|
|
55
|
+
interface Attachment {
|
|
56
|
+
readonly listener: (frame: ConversationFrame | ConversationReplay) => void;
|
|
57
|
+
readonly queue: string[];
|
|
58
|
+
queuedBytes: number;
|
|
59
|
+
draining: boolean;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Process-local Phase 5 bridge, not an application activation path.
|
|
63
|
+
* The host obtains the permit before durable writes and owns scope materialization.
|
|
64
|
+
* Browser code receives only attachment methods and projected frames, never a driver.
|
|
65
|
+
*/
|
|
66
|
+
export declare class CodexConversation {
|
|
67
|
+
private readonly onState?;
|
|
68
|
+
readonly capability: `${string}-${string}-${string}-${string}-${string}`;
|
|
69
|
+
readonly ready: Promise<void>;
|
|
70
|
+
private state;
|
|
71
|
+
private failed?;
|
|
72
|
+
private backendReason;
|
|
73
|
+
private sequence;
|
|
74
|
+
private retainedBytes;
|
|
75
|
+
private frames;
|
|
76
|
+
private owner;
|
|
77
|
+
private driver?;
|
|
78
|
+
private threadId?;
|
|
79
|
+
private readonly connection;
|
|
80
|
+
private closing?;
|
|
81
|
+
private detachFailure?;
|
|
82
|
+
private active;
|
|
83
|
+
private interrupting;
|
|
84
|
+
private turnStarted;
|
|
85
|
+
private rejectStopped;
|
|
86
|
+
private readonly stopped;
|
|
87
|
+
constructor(backend: Backend, permit: CodexLaunchPermit, input: OpenInput, onState?: ((state: ConversationState, failure?: ConversationFailure) => void) | undefined);
|
|
88
|
+
snapshot(): {
|
|
89
|
+
state: ConversationState;
|
|
90
|
+
failure?: ConversationFailure;
|
|
91
|
+
backendReason?: RuntimeBackendReasonCode<"codex-app-server">;
|
|
92
|
+
sequence: number;
|
|
93
|
+
};
|
|
94
|
+
private initialize;
|
|
95
|
+
/** Host-only identity for the official terminal adapter; never a resume grant. */
|
|
96
|
+
terminalThreadId(): string;
|
|
97
|
+
private receive;
|
|
98
|
+
private transition;
|
|
99
|
+
private record;
|
|
100
|
+
private deliveryFailed;
|
|
101
|
+
private drain;
|
|
102
|
+
attach(capability: string, listener: Attachment["listener"], afterSequence?: number): CodexConversationAttachment;
|
|
103
|
+
private submit;
|
|
104
|
+
private interrupt;
|
|
105
|
+
/** Detach retains the conversation; the Sessions lease owner decides when to close. */
|
|
106
|
+
close(): Promise<void>;
|
|
107
|
+
private finish;
|
|
108
|
+
}
|
|
109
|
+
export {};
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { randomUUID, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { CodexBackendError } from "./codex-app-server.js";
|
|
3
|
+
import { parseCodexEvent } from "./codex-events.js";
|
|
4
|
+
import { CodexRpcError } from "./codex-rpc.js";
|
|
5
|
+
export class CodexConversationError extends Error {
|
|
6
|
+
code;
|
|
7
|
+
backendReason;
|
|
8
|
+
constructor(code, backendReason) {
|
|
9
|
+
super("Codex conversation: " + code);
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.backendReason = backendReason;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const MAX_BYTES = 1_048_576;
|
|
15
|
+
const MAX_FRAMES = 256;
|
|
16
|
+
function failure(error) {
|
|
17
|
+
if (error instanceof CodexConversationError && ["protocol", "timeout", "cancelled", "cleanup", "delivery"].includes(error.code))
|
|
18
|
+
return error.code;
|
|
19
|
+
if (error instanceof CodexBackendError && error.code === "codex_app_server_cleanup_failed")
|
|
20
|
+
return "cleanup";
|
|
21
|
+
if (error instanceof CodexRpcError) {
|
|
22
|
+
if (error.reason === "cleanup" || error.reason === "timeout" || error.reason === "cancelled")
|
|
23
|
+
return error.reason;
|
|
24
|
+
return "protocol";
|
|
25
|
+
}
|
|
26
|
+
return "unavailable";
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Process-local Phase 5 bridge, not an application activation path.
|
|
30
|
+
* The host obtains the permit before durable writes and owns scope materialization.
|
|
31
|
+
* Browser code receives only attachment methods and projected frames, never a driver.
|
|
32
|
+
*/
|
|
33
|
+
export class CodexConversation {
|
|
34
|
+
onState;
|
|
35
|
+
capability = randomUUID();
|
|
36
|
+
ready;
|
|
37
|
+
state = "opening";
|
|
38
|
+
failed;
|
|
39
|
+
backendReason;
|
|
40
|
+
sequence = 0;
|
|
41
|
+
retainedBytes = 0;
|
|
42
|
+
frames = [];
|
|
43
|
+
owner;
|
|
44
|
+
driver;
|
|
45
|
+
threadId;
|
|
46
|
+
connection;
|
|
47
|
+
closing;
|
|
48
|
+
detachFailure;
|
|
49
|
+
active;
|
|
50
|
+
interrupting;
|
|
51
|
+
turnStarted = false;
|
|
52
|
+
rejectStopped;
|
|
53
|
+
stopped = new Promise((_, reject) => { this.rejectStopped = reject; });
|
|
54
|
+
constructor(backend, permit, input, onState) {
|
|
55
|
+
this.onState = onState;
|
|
56
|
+
void this.stopped.catch(() => { });
|
|
57
|
+
// Defer open until ownership of both promises exists; synchronous fake peers
|
|
58
|
+
// and early events must obey the same lifecycle as the production backend.
|
|
59
|
+
this.connection = Promise.resolve().then(() => {
|
|
60
|
+
if (this.state !== "opening")
|
|
61
|
+
throw new CodexConversationError("closed");
|
|
62
|
+
permit.assertActive();
|
|
63
|
+
return backend.open(permit, { ...input, onEvent: (event) => this.receive(event) });
|
|
64
|
+
}).then((driver) => {
|
|
65
|
+
this.driver = driver;
|
|
66
|
+
this.detachFailure = driver.onFailure((error) => {
|
|
67
|
+
if (this.state !== "closing" && this.state !== "closed" && this.state !== "failed") {
|
|
68
|
+
void this.finish(failure(error)).catch(() => { });
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
return driver;
|
|
72
|
+
}).catch((error) => {
|
|
73
|
+
if (error instanceof CodexBackendError)
|
|
74
|
+
this.backendReason = error.code;
|
|
75
|
+
permit.release();
|
|
76
|
+
throw error;
|
|
77
|
+
});
|
|
78
|
+
this.ready = this.initialize();
|
|
79
|
+
void this.ready.catch(() => { });
|
|
80
|
+
}
|
|
81
|
+
snapshot() {
|
|
82
|
+
return { state: this.state, ...(this.failed ? { failure: this.failed } : {}),
|
|
83
|
+
...(this.backendReason ? { backendReason: this.backendReason } : {}), sequence: this.sequence };
|
|
84
|
+
}
|
|
85
|
+
async initialize() {
|
|
86
|
+
try {
|
|
87
|
+
const driver = await this.connection;
|
|
88
|
+
if (this.state !== "opening")
|
|
89
|
+
throw new CodexConversationError("closed");
|
|
90
|
+
this.threadId = await driver.startThread();
|
|
91
|
+
if (this.state !== "opening")
|
|
92
|
+
throw new CodexConversationError("closed");
|
|
93
|
+
this.transition("ready");
|
|
94
|
+
if (this.snapshot().state !== "ready")
|
|
95
|
+
throw new CodexConversationError("closed");
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
await this.finish(failure(error));
|
|
99
|
+
throw new CodexConversationError(this.failed ?? (this.closing ? "closed" : failure(error)), this.backendReason);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/** Host-only identity for the official terminal adapter; never a resume grant. */
|
|
103
|
+
terminalThreadId() {
|
|
104
|
+
if (!this.threadId || !["ready", "running"].includes(this.state))
|
|
105
|
+
throw new CodexConversationError("closed");
|
|
106
|
+
return this.threadId;
|
|
107
|
+
}
|
|
108
|
+
receive(value) {
|
|
109
|
+
if (this.state === "closing" || this.state === "closed" || this.state === "failed")
|
|
110
|
+
return;
|
|
111
|
+
try {
|
|
112
|
+
const event = parseCodexEvent(value.method, value.params);
|
|
113
|
+
if (event.method === "command/exec/outputDelta" || event.method === "remoteControl/status/changed" || event.method === "thread/started") {
|
|
114
|
+
throw new CodexConversationError("protocol");
|
|
115
|
+
}
|
|
116
|
+
if (event.method === "turn/started")
|
|
117
|
+
this.turnStarted = true;
|
|
118
|
+
this.record({ type: "event", sequence: ++this.sequence, event });
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
void this.finish("protocol").catch(() => { });
|
|
122
|
+
throw new CodexConversationError("protocol");
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
transition(state, code) {
|
|
126
|
+
this.state = state;
|
|
127
|
+
if (code)
|
|
128
|
+
this.failed = code;
|
|
129
|
+
this.onState?.(state, this.failed);
|
|
130
|
+
this.record({ type: "state", sequence: ++this.sequence, state, ...(this.failed ? { failure: this.failed } : {}),
|
|
131
|
+
...(this.backendReason ? { backendReason: this.backendReason } : {}) });
|
|
132
|
+
}
|
|
133
|
+
record(frame) {
|
|
134
|
+
const json = JSON.stringify(frame);
|
|
135
|
+
const bytes = Buffer.byteLength(json);
|
|
136
|
+
if (bytes > MAX_BYTES)
|
|
137
|
+
throw new CodexConversationError("protocol");
|
|
138
|
+
this.frames.push({ sequence: frame.sequence, json, bytes });
|
|
139
|
+
this.retainedBytes += bytes;
|
|
140
|
+
while (this.frames.length > MAX_FRAMES || this.retainedBytes > MAX_BYTES) {
|
|
141
|
+
this.retainedBytes -= this.frames.shift().bytes;
|
|
142
|
+
}
|
|
143
|
+
if (this.owner) {
|
|
144
|
+
if (this.owner.queue.length >= MAX_FRAMES + 1 || this.owner.queuedBytes + bytes > MAX_BYTES + 4096) {
|
|
145
|
+
this.deliveryFailed();
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
this.owner.queue.push(json);
|
|
149
|
+
this.owner.queuedBytes += bytes;
|
|
150
|
+
this.drain(this.owner);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
deliveryFailed() {
|
|
154
|
+
this.owner = undefined;
|
|
155
|
+
void this.finish("delivery").catch(() => { });
|
|
156
|
+
}
|
|
157
|
+
drain(owner) {
|
|
158
|
+
if (owner.draining)
|
|
159
|
+
return;
|
|
160
|
+
owner.draining = true;
|
|
161
|
+
try {
|
|
162
|
+
// Reentrant live events append behind the captured replay, never inside it.
|
|
163
|
+
let delivered = 0;
|
|
164
|
+
while (this.owner === owner && owner.queue.length) {
|
|
165
|
+
if (++delivered > MAX_FRAMES * 2 + 1) {
|
|
166
|
+
this.deliveryFailed();
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
const json = owner.queue.shift();
|
|
170
|
+
owner.queuedBytes -= Buffer.byteLength(json);
|
|
171
|
+
owner.listener(JSON.parse(json));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
this.deliveryFailed();
|
|
176
|
+
}
|
|
177
|
+
finally {
|
|
178
|
+
owner.draining = false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
attach(capability, listener, afterSequence = 0) {
|
|
182
|
+
if (typeof capability !== "string" || capability.length !== this.capability.length)
|
|
183
|
+
throw new CodexConversationError("attachment");
|
|
184
|
+
const supplied = Buffer.from(capability);
|
|
185
|
+
const expected = Buffer.from(this.capability);
|
|
186
|
+
if (supplied.length !== expected.length || !timingSafeEqual(supplied, expected) || this.owner || typeof listener !== "function")
|
|
187
|
+
throw new CodexConversationError("attachment");
|
|
188
|
+
if (!Number.isSafeInteger(afterSequence) || afterSequence < 0 || afterSequence > this.sequence)
|
|
189
|
+
throw new CodexConversationError("input");
|
|
190
|
+
const replay = { type: "replay", afterSequence, throughSequence: this.sequence,
|
|
191
|
+
truncated: afterSequence < (this.frames[0]?.sequence ?? 1) - 1,
|
|
192
|
+
retainedBytes: this.retainedBytes, retentionLimitBytes: MAX_BYTES, state: this.state,
|
|
193
|
+
...(this.failed ? { failure: this.failed } : {}), ...(this.backendReason ? { backendReason: this.backendReason } : {}) };
|
|
194
|
+
const queue = [JSON.stringify(replay), ...this.frames.filter((frame) => frame.sequence > afterSequence).map((frame) => frame.json)];
|
|
195
|
+
const owner = { listener, queue, queuedBytes: queue.reduce((sum, json) => sum + Buffer.byteLength(json), 0), draining: false };
|
|
196
|
+
this.owner = owner;
|
|
197
|
+
const assertOwner = () => { if (this.owner !== owner)
|
|
198
|
+
throw new CodexConversationError("attachment"); };
|
|
199
|
+
const handle = Object.freeze({
|
|
200
|
+
submit: (text) => { assertOwner(); return this.submit(text); },
|
|
201
|
+
interrupt: () => { assertOwner(); return this.interrupt(); },
|
|
202
|
+
steer: (text, expectedTurnId) => {
|
|
203
|
+
assertOwner();
|
|
204
|
+
if (this.state !== "running" || !this.active || !this.turnStarted || !this.driver?.steerTurn)
|
|
205
|
+
throw new CodexConversationError("closed");
|
|
206
|
+
return this.driver.steerTurn(text, expectedTurnId);
|
|
207
|
+
},
|
|
208
|
+
detach: () => { if (this.owner === owner)
|
|
209
|
+
this.owner = undefined; },
|
|
210
|
+
});
|
|
211
|
+
this.drain(owner);
|
|
212
|
+
return handle;
|
|
213
|
+
}
|
|
214
|
+
submit(text) {
|
|
215
|
+
if (this.active)
|
|
216
|
+
throw new CodexConversationError("busy");
|
|
217
|
+
if (this.state !== "ready" || !this.driver)
|
|
218
|
+
throw new CodexConversationError("closed");
|
|
219
|
+
if (typeof text !== "string" || !text.trim() || Buffer.byteLength(text) > 262_144)
|
|
220
|
+
throw new CodexConversationError("input");
|
|
221
|
+
const driver = this.driver;
|
|
222
|
+
this.turnStarted = false;
|
|
223
|
+
const active = Promise.resolve().then(async () => {
|
|
224
|
+
if (this.state !== "running")
|
|
225
|
+
throw new CodexConversationError("closed");
|
|
226
|
+
const result = await Promise.race([driver.runTurn(text, { timeoutMs: 300_000 }), this.stopped]);
|
|
227
|
+
if (result.status === "inProgress")
|
|
228
|
+
throw new CodexConversationError("protocol");
|
|
229
|
+
return { turnId: result.id, status: result.status };
|
|
230
|
+
}).catch(async (error) => {
|
|
231
|
+
await this.finish(failure(error));
|
|
232
|
+
throw new CodexConversationError(this.failed ?? (error instanceof CodexConversationError ? error.code : failure(error)));
|
|
233
|
+
}).finally(() => {
|
|
234
|
+
this.active = undefined;
|
|
235
|
+
this.interrupting = undefined;
|
|
236
|
+
if (this.state === "running")
|
|
237
|
+
this.transition("ready");
|
|
238
|
+
});
|
|
239
|
+
this.active = active;
|
|
240
|
+
void active.catch(() => { });
|
|
241
|
+
this.transition("running");
|
|
242
|
+
return active;
|
|
243
|
+
}
|
|
244
|
+
interrupt() {
|
|
245
|
+
if (!this.active || this.state !== "running" || !this.driver)
|
|
246
|
+
throw new CodexConversationError("closed");
|
|
247
|
+
// Before turn/start is acknowledged there is no interruptible vendor ID.
|
|
248
|
+
// Closing the owned connection cancels that early window without guessing it.
|
|
249
|
+
if (!this.turnStarted)
|
|
250
|
+
return this.finish("cancelled");
|
|
251
|
+
const active = this.active;
|
|
252
|
+
this.interrupting ??= this.driver.interruptTurn().then(async () => { await active; }).catch(async (error) => {
|
|
253
|
+
await this.finish(failure(error));
|
|
254
|
+
throw new CodexConversationError(this.failed ?? failure(error));
|
|
255
|
+
});
|
|
256
|
+
void this.interrupting.catch(() => { });
|
|
257
|
+
return this.interrupting;
|
|
258
|
+
}
|
|
259
|
+
/** Detach retains the conversation; the Sessions lease owner decides when to close. */
|
|
260
|
+
close() { return this.finish(); }
|
|
261
|
+
finish(code) {
|
|
262
|
+
if (this.closing)
|
|
263
|
+
return this.closing;
|
|
264
|
+
if (code)
|
|
265
|
+
this.failed = code;
|
|
266
|
+
this.state = "closing";
|
|
267
|
+
this.rejectStopped(new CodexConversationError(code ?? "closed"));
|
|
268
|
+
this.closing = Promise.resolve().then(async () => {
|
|
269
|
+
try {
|
|
270
|
+
let driver;
|
|
271
|
+
try {
|
|
272
|
+
driver = await this.connection;
|
|
273
|
+
}
|
|
274
|
+
catch (error) {
|
|
275
|
+
if (failure(error) === "cleanup")
|
|
276
|
+
throw error;
|
|
277
|
+
}
|
|
278
|
+
await driver?.close();
|
|
279
|
+
this.detachFailure?.();
|
|
280
|
+
this.transition(this.failed ? "failed" : "closed");
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
this.backendReason = "codex_app_server_cleanup_failed";
|
|
284
|
+
this.detachFailure?.();
|
|
285
|
+
this.transition("failed", "cleanup");
|
|
286
|
+
throw new CodexConversationError("cleanup", this.backendReason);
|
|
287
|
+
}
|
|
288
|
+
finally {
|
|
289
|
+
this.owner = undefined;
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
void this.closing.catch(() => { });
|
|
293
|
+
return this.closing;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type NativeComponentBindings } from "../native-component-preparation.js";
|
|
2
|
+
/** Host-only connection authority. Neither its identity nor credentials enter tool arguments. */
|
|
3
|
+
export interface CodexSessionTools {
|
|
4
|
+
readonly accountEmail: string;
|
|
5
|
+
readonly definitions: readonly {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly description: string;
|
|
8
|
+
readonly inputSchema: unknown;
|
|
9
|
+
}[];
|
|
10
|
+
assertCurrent(): void;
|
|
11
|
+
call(name: string, input: unknown, callId: string, signal: AbortSignal): Promise<unknown>;
|
|
12
|
+
close(): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
/** Materializes only a verified host plan. No vendor config, .codex directory, or MCP endpoint. */
|
|
15
|
+
export declare function prepareCodexDirectTools(options: {
|
|
16
|
+
readonly irDocument: string;
|
|
17
|
+
readonly scope: Readonly<Record<string, unknown>>;
|
|
18
|
+
readonly bindings: NativeComponentBindings;
|
|
19
|
+
readonly producerBinary: string;
|
|
20
|
+
readonly accountEmail: string;
|
|
21
|
+
readonly expiresAt: number;
|
|
22
|
+
readonly signal: AbortSignal;
|
|
23
|
+
}): Promise<CodexSessionTools>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { promisify } from "node:util";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { planDigest, readExecutionPlan } from "../../harness/components/plan.js";
|
|
9
|
+
import { prepareDirectCodexComponentHost } from "../native-component-preparation.js";
|
|
10
|
+
import { NativeComponentAdmission } from "../native-components.js";
|
|
11
|
+
/** Materializes only a verified host plan. No vendor config, .codex directory, or MCP endpoint. */
|
|
12
|
+
export async function prepareCodexDirectTools(options) {
|
|
13
|
+
const { irDocument, accountEmail, expiresAt, signal } = options;
|
|
14
|
+
if (!accountEmail.trim() || !Number.isSafeInteger(expiresAt) || expiresAt <= Date.now())
|
|
15
|
+
throw new Error("Direct session authority expired");
|
|
16
|
+
const prepared = prepareDirectCodexComponentHost(irDocument, options.scope, options.bindings);
|
|
17
|
+
const check = () => { signal.throwIfAborted(); if (Date.now() >= expiresAt)
|
|
18
|
+
throw new Error("Direct session authority expired"); prepared.assertCurrent(); };
|
|
19
|
+
check();
|
|
20
|
+
const root = Object.keys(prepared.hostRoots)[0];
|
|
21
|
+
const hostDocument = JSON.stringify(prepared.hostRoots[root]);
|
|
22
|
+
const ir = z.object({ context_binding: z.record(z.string(), z.unknown()), components: z.array(z.object({ id: z.string() }).passthrough()) }).passthrough().parse(JSON.parse(irDocument));
|
|
23
|
+
const directory = await mkdtemp(path.join(os.tmpdir(), "genbi-direct-plan-"));
|
|
24
|
+
try {
|
|
25
|
+
await writeFile(path.join(directory, "ir.json"), irDocument, { mode: 0o600 });
|
|
26
|
+
await writeFile(path.join(directory, "host.json"), hostDocument, { mode: 0o600 });
|
|
27
|
+
check();
|
|
28
|
+
await promisify(execFile)(options.producerBinary, ["produce-session", path.join(directory, "ir.json"), "--component", root,
|
|
29
|
+
"--host-contract", path.join(directory, "host.json"), "--out", path.join(directory, "plan.json")], { cwd: directory, env: { PATH: [path.dirname(process.execPath), "/usr/bin", "/bin"].join(path.delimiter) }, timeout: 30_000, maxBuffer: 1_048_576, signal });
|
|
30
|
+
check();
|
|
31
|
+
const document = await readFile(path.join(directory, "plan.json"), "utf8");
|
|
32
|
+
const digest = z.object({ plan_sha256: z.string().regex(/^sha256:[a-f0-9]{64}$/) }).parse(JSON.parse(document)).plan_sha256;
|
|
33
|
+
const plan = readExecutionPlan(document, { digest, inputIrDigest: `sha256:${createHash("sha256").update(irDocument).digest("hex")}`,
|
|
34
|
+
contextBinding: ir.context_binding, declarations: Object.fromEntries(ir.components.map((node) => [node.id, node])), directHostDocument: hostDocument });
|
|
35
|
+
if (plan.entries.length !== 1 || plan.entries[0] !== root)
|
|
36
|
+
throw new Error("Direct session entry mismatch");
|
|
37
|
+
const name = `warble_run_${planDigest({ identity: prepared.identity, plan: plan.identity }).slice(7, 39)}`;
|
|
38
|
+
const admission = new NativeComponentAdmission({ identity: prepared.identity, digest: plan.identity, roots: { [root]: plan }, tools: { [root]: name } }, () => { check(); return prepared.identity; }, prepared.host);
|
|
39
|
+
const lifetime = new AbortController();
|
|
40
|
+
const timeout = setTimeout(() => lifetime.abort(), Math.min(expiresAt - Date.now(), 2_147_483_647));
|
|
41
|
+
timeout.unref();
|
|
42
|
+
const combined = AbortSignal.any([signal, lifetime.signal]);
|
|
43
|
+
let closing;
|
|
44
|
+
const close = () => { lifetime.abort(); clearTimeout(timeout); return closing ??= admission.close(); };
|
|
45
|
+
combined.addEventListener("abort", () => { void close().catch(() => { }); }, { once: true });
|
|
46
|
+
const current = () => { combined.throwIfAborted(); check(); };
|
|
47
|
+
const definitions = Object.freeze(admission.list().map((tool) => Object.freeze({ ...tool, inputSchema: structuredClone(tool.inputSchema) })));
|
|
48
|
+
return Object.freeze({ accountEmail, definitions, assertCurrent: current,
|
|
49
|
+
async call(tool, input, callId, parent) {
|
|
50
|
+
current();
|
|
51
|
+
const result = await admission.call(tool, input, callId, AbortSignal.any([combined, parent]));
|
|
52
|
+
current();
|
|
53
|
+
return result;
|
|
54
|
+
}, close });
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
await rm(directory, { recursive: true, force: true });
|
|
58
|
+
}
|
|
59
|
+
}
|