@hraness/xcb 0.9.1

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.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/MANAGED-CODEX.md +213 -0
  3. package/README.md +555 -0
  4. package/dist/accounts.d.ts +46 -0
  5. package/dist/broker-descriptors.d.ts +5 -0
  6. package/dist/broker.d.ts +62 -0
  7. package/dist/browser-session.d.ts +161 -0
  8. package/dist/canonical-json.d.ts +2 -0
  9. package/dist/capabilities.d.ts +72 -0
  10. package/dist/claude-api-models.d.ts +24 -0
  11. package/dist/claude-api-transport.d.ts +5 -0
  12. package/dist/claude-api.d.ts +23 -0
  13. package/dist/claude-credentials.d.ts +13 -0
  14. package/dist/claude-options.d.ts +10 -0
  15. package/dist/claude-sdk.d.ts +48 -0
  16. package/dist/claude-task-adapter.d.ts +65 -0
  17. package/dist/cli.js +4190 -0
  18. package/dist/codex-account-process.d.ts +149 -0
  19. package/dist/codex-account-transport.d.ts +39 -0
  20. package/dist/codex-account.d.ts +126 -0
  21. package/dist/codex-config.d.ts +53 -0
  22. package/dist/codex-host.d.ts +40 -0
  23. package/dist/codex-managed-baseline.d.ts +5 -0
  24. package/dist/codex-managed-catalog.d.ts +32 -0
  25. package/dist/codex-managed-config.d.ts +93 -0
  26. package/dist/codex-managed-ledger.d.ts +37 -0
  27. package/dist/codex-managed-session.d.ts +62 -0
  28. package/dist/codex-managed-task-adapter.d.ts +21 -0
  29. package/dist/codex-process.d.ts +67 -0
  30. package/dist/codex-protocol-manifest.d.ts +27 -0
  31. package/dist/codex-relay.d.ts +80 -0
  32. package/dist/codex-scratch.d.ts +36 -0
  33. package/dist/codex-session.d.ts +44 -0
  34. package/dist/codex-task-adapter.d.ts +24 -0
  35. package/dist/codex-task-process.d.ts +21 -0
  36. package/dist/devin-acp.d.ts +105 -0
  37. package/dist/devin-adapter.d.ts +44 -0
  38. package/dist/devin-client.d.ts +36 -0
  39. package/dist/devin-mcp.d.ts +28 -0
  40. package/dist/egress-bridge.d.ts +35 -0
  41. package/dist/egress-client.d.ts +66 -0
  42. package/dist/index-kg2gx694.js +7217 -0
  43. package/dist/index.d.ts +58 -0
  44. package/dist/index.js +3455 -0
  45. package/dist/judge.d.ts +121 -0
  46. package/dist/loopback-server.d.ts +17 -0
  47. package/dist/managed-account.d.ts +176 -0
  48. package/dist/models.d.ts +23 -0
  49. package/dist/os-sandbox.d.ts +168 -0
  50. package/dist/private-file.d.ts +215 -0
  51. package/dist/process-port.d.ts +44 -0
  52. package/dist/process-write.d.ts +13 -0
  53. package/dist/provider-process.d.ts +26 -0
  54. package/dist/public-web.d.ts +21 -0
  55. package/dist/router.d.ts +43 -0
  56. package/dist/runtime.d.ts +90 -0
  57. package/dist/sqlite-port.d.ts +29 -0
  58. package/dist/task-runtime.d.ts +150 -0
  59. package/dist/validation.d.ts +6 -0
  60. package/package.json +70 -0
  61. package/sandbox/loopback-forwarder.cjs +172 -0
@@ -0,0 +1,67 @@
1
+ import type { ProviderProcessWriteResult } from "./process-port.ts";
2
+ import { type Readable, type Writable } from "node:stream";
3
+ import { type CodexParentRuntimeBinding } from "./codex-host.ts";
4
+ export declare const CODEX_NATIVE_VERSION = "0.153.4";
5
+ export declare const CODEX_NATIVE_SHA256 = "b973d440acac501fd2594a43e7ca9ce41e0a65b9dfb28d0d7a7837c99e1261e3";
6
+ export type CodexProcessReceipt = Readonly<{
7
+ nativeVersion: string;
8
+ executableSha256: string;
9
+ runtimeSnapshotSha256: string;
10
+ configSha256: string;
11
+ profileSha256: string;
12
+ custodyPath: string;
13
+ parentRuntimeSha256: string;
14
+ scratchContentSha256: string;
15
+ scratchIdentitySha256: string;
16
+ pid: number | null;
17
+ pgid: number | null;
18
+ rootExited: boolean;
19
+ groupAbsent: boolean;
20
+ stdioJoined: boolean;
21
+ scratchRetained: boolean;
22
+ nativeExitCode: number | null;
23
+ nativeExitSignal: string | null;
24
+ runtimeErrors: readonly string[];
25
+ cleanupErrors: readonly string[];
26
+ }>;
27
+ export interface CodexProcessHandle {
28
+ readonly cwd: string;
29
+ readonly stdin?: Writable;
30
+ readonly stdout: Readable;
31
+ /** Full acceptance is distinct from the RPC result; never replay uncertain bytes. */
32
+ write(bytes: Uint8Array): Promise<ProviderProcessWriteResult>;
33
+ readonly exited: Promise<void>;
34
+ readonly ready: Promise<void>;
35
+ receipt(): CodexProcessReceipt;
36
+ stopAndJoin(): Promise<CodexProcessReceipt>;
37
+ }
38
+ export interface CodexProcessLauncher {
39
+ /** No contact path, credential, arbitrary executable, or caller-selected argv. */
40
+ launch(input: {
41
+ runId: string;
42
+ accountId: string;
43
+ workspaceId: string;
44
+ configuration: string;
45
+ relayPort: number;
46
+ signal: AbortSignal;
47
+ }): Promise<CodexProcessHandle>;
48
+ }
49
+ /** Journal decoding supplies recovery evidence, never process-stop authority. */
50
+ export declare function parseCodexCustodyJournal(text: string): {
51
+ snapshot: Readonly<Record<string, unknown>>;
52
+ incompleteTail: boolean;
53
+ };
54
+ /** Inspect through one no-follow descriptor before copying any executable bytes. */
55
+ export declare function inspectCodexExecutable(path: string): Promise<Buffer>;
56
+ /** Experimental runtime policy; the launcher never issues a qualification. */
57
+ export declare function codexMacSandbox(input: {
58
+ executable: string;
59
+ scratch: string;
60
+ relayPort: number;
61
+ }): string;
62
+ /** Internal host launcher. Account/API integration and production admission are separate. */
63
+ export declare function createCodexProcessLauncher(options: {
64
+ executablePath: string;
65
+ stateRoot: string;
66
+ parentRuntime: CodexParentRuntimeBinding;
67
+ }): CodexProcessLauncher;
@@ -0,0 +1,27 @@
1
+ /** Immutable identity emitted by a trusted host after it has inspected the
2
+ * exact Codex executable and generated protocol schema. A digest supplied by
3
+ * a caller is only an assertion until this manifest is host-produced. */
4
+ export type CodexProtocolManifest = Readonly<{
5
+ protocol: "codex-app-server-experimental";
6
+ protocolVersion: string;
7
+ sourceVersion: string;
8
+ executableSha256: string;
9
+ schemaSha256: string;
10
+ manifestSha256: string;
11
+ generatedAtUnixMs: number;
12
+ }>;
13
+ export declare function buildCodexProtocolManifest(input: Readonly<{
14
+ protocolVersion: string;
15
+ sourceVersion: string;
16
+ executableSha256: string;
17
+ schemaSha256: string;
18
+ generatedAtUnixMs: number;
19
+ }>): CodexProtocolManifest;
20
+ /** Bind a trusted host manifest to the runtime selected for a task. This does
21
+ * not inspect files or execute Codex; those effects belong to the host that
22
+ * created the manifest. */
23
+ export declare function assertCodexProtocolManifest(value: unknown, runtime: Readonly<{
24
+ version: string;
25
+ sha256: string;
26
+ schemaSha256: string;
27
+ }>): CodexProtocolManifest;
@@ -0,0 +1,80 @@
1
+ import type { BrokerToolName } from "./broker.ts";
2
+ import { type CodexTool, type CodexCapabilityMapping, type CodexTaskSettings } from "./codex-config.ts";
3
+ /** A trusted host port, deliberately without credential discovery or a live HTTP implementation.
4
+ * A paid implementation must separately reserve/account usage and impose its own output-token cap.
5
+ */
6
+ export interface CodexResponsesUpstream {
7
+ request(body: Readonly<Record<string, unknown>>, signal: AbortSignal): Promise<Response>;
8
+ }
9
+ export type CodexLimits = Readonly<{
10
+ deadlineMs: number;
11
+ ioMs: number;
12
+ cleanupMs: number;
13
+ maxRequests: number;
14
+ maxRequestBytes: number;
15
+ maxResponseBytes: number;
16
+ maxFrameBytes: number;
17
+ maxFrames: number;
18
+ }>;
19
+ export declare function codexLimits(input?: Partial<CodexLimits>): CodexLimits;
20
+ /** Application tasks have an explicit run/cleanup allocation. Keep the legacy
21
+ * contact limits and every IO/request/byte limit unchanged, and retain the
22
+ * task runtime's one-hour combined ceiling. */
23
+ export declare function codexTaskLimits(input?: Partial<CodexLimits>): CodexLimits;
24
+ export declare function codexAssert(value: unknown, code: string): asserts value;
25
+ export declare function codexRecord(value: unknown): Record<string, unknown>;
26
+ export declare function codexBounded<T>(promise: Promise<T>, ms: number, code: string): Promise<T>;
27
+ export type CodexRelayReceipt = Readonly<{
28
+ requests: number;
29
+ calls: number;
30
+ started: number;
31
+ completed: number;
32
+ outputsObserved: number;
33
+ finalObserved: boolean;
34
+ joined: boolean;
35
+ failure: string | null;
36
+ }>;
37
+ export type CodexTaskRelayOptions = Readonly<{
38
+ mapping: CodexCapabilityMapping;
39
+ settings: CodexTaskSettings;
40
+ executionDeadlineUnixMs: number;
41
+ maxOutputBytes: number;
42
+ now?: () => number;
43
+ }>;
44
+ export interface CodexRelay {
45
+ readonly baseUrl: string;
46
+ readonly port: number;
47
+ bindTurn(threadId: string, turnId: string): void;
48
+ claimCall(params: Record<string, unknown>): {
49
+ id: string;
50
+ name: BrokerToolName;
51
+ input: Record<string, unknown>;
52
+ };
53
+ claimTaskCall(params: Record<string, unknown>): {
54
+ id: string;
55
+ name: string;
56
+ input: Record<string, unknown>;
57
+ };
58
+ completeCall(id: string, text: string, success: boolean, write: () => Promise<void>): Promise<void>;
59
+ observeTool(params: Record<string, unknown>, phase: "started" | "completed"): void;
60
+ observeFinal(text: unknown): void;
61
+ result(): unknown;
62
+ resultText(): string | null;
63
+ usage(): Readonly<{
64
+ inputTokens: number | null;
65
+ outputTokens: number | null;
66
+ totalTokens: number | null;
67
+ }>;
68
+ receipt(): CodexRelayReceipt;
69
+ close(): Promise<CodexRelayReceipt>;
70
+ }
71
+ export declare function startCodexRelay(options: {
72
+ model: string;
73
+ prompt: string;
74
+ tools: readonly CodexTool[];
75
+ upstream: CodexResponsesUpstream;
76
+ signal: AbortSignal;
77
+ limits: CodexLimits;
78
+ fail(code: string): void;
79
+ task?: CodexTaskRelayOptions;
80
+ }): Promise<CodexRelay>;
@@ -0,0 +1,36 @@
1
+ declare const directories: readonly ["", "home", "state", "tmp", "work"];
2
+ type Directory = typeof directories[number];
3
+ type EntryPath = Directory | "state/config.toml";
4
+ export type CodexScratchEntry = Readonly<{
5
+ path: EntryPath;
6
+ kind: "directory" | "file";
7
+ device: string;
8
+ inode: string;
9
+ uid: number;
10
+ mode: number;
11
+ links: number;
12
+ size: string;
13
+ modifiedNs: string;
14
+ changedNs: string;
15
+ }>;
16
+ export type CodexScratchInspection = Readonly<{
17
+ schema: "xcb.codex-scratch.v1";
18
+ configurationSha256: string;
19
+ configurationBytes: number;
20
+ /** Content/layout identity, independent of the newly allocated directory inodes. */
21
+ contentSha256: string;
22
+ /** Exact inspected physical root, relative entries, and stable filesystem metadata. */
23
+ identitySha256: string;
24
+ entries: readonly CodexScratchEntry[];
25
+ }>;
26
+ /** Inspect only a newly created host scratch tree, immediately before launch.
27
+ * Model/tool-written paths must never be supplied to this host-only function.
28
+ * No contact data, aliases, credentials, plugins, or extra files belong here.
29
+ * This is a checked snapshot, not a filesystem lease: a hostile same-UID host
30
+ * or root racing preparation is excluded. The launcher must retain that trust
31
+ * boundary after inspection; the result alone does not qualify a provider. */
32
+ export declare function inspectCodexScratch(input: {
33
+ scratch: string;
34
+ configuration: string | Uint8Array;
35
+ }): Promise<CodexScratchInspection>;
36
+ export {};
@@ -0,0 +1,44 @@
1
+ import type { ToolBroker } from "./broker.ts";
2
+ import type { CapabilityBroker } from "./capabilities.ts";
3
+ import type { CodexProcessLauncher, CodexProcessReceipt } from "./codex-process.ts";
4
+ import { type CodexLimits, type CodexRelayReceipt, type CodexResponsesUpstream, type CodexTaskRelayOptions } from "./codex-relay.ts";
5
+ import type { AgentRunRequest } from "./runtime.ts";
6
+ export type CodexSessionReceipt = Readonly<{
7
+ status: "completed" | "failed";
8
+ productionQualified: false;
9
+ initialized: boolean;
10
+ turnCompleted: boolean;
11
+ usage: Readonly<{
12
+ inputTokens: number | null;
13
+ outputTokens: number | null;
14
+ totalTokens: number | null;
15
+ }>;
16
+ process: CodexProcessReceipt | null;
17
+ relay: CodexRelayReceipt | null;
18
+ handlersJoined: boolean;
19
+ processStopped: boolean;
20
+ failures: readonly string[];
21
+ frames: number;
22
+ stdoutBytes: number;
23
+ unexpectedNotification: string | null;
24
+ failureStage: "prepare" | "initialize" | "thread/start" | "turn/start" | "turn" | "cleanup" | null;
25
+ deniedNativeRequest: string | null;
26
+ }>;
27
+ export declare class CodexSessionError extends Error {
28
+ readonly receipt: CodexSessionReceipt;
29
+ constructor(receipt: CodexSessionReceipt);
30
+ }
31
+ /** Low-level unqualified driver. Native ownership and upstream access are explicit trusted ports.
32
+ * No module import, account discovery, adapter registration or credential-bearing network operation.
33
+ */
34
+ export declare function runCodexSession(options: {
35
+ request: AgentRunRequest;
36
+ broker: ToolBroker | CapabilityBroker;
37
+ upstream: CodexResponsesUpstream;
38
+ launcher: CodexProcessLauncher;
39
+ limits?: Partial<CodexLimits>;
40
+ task?: CodexTaskRelayOptions;
41
+ }): Promise<{
42
+ output: unknown;
43
+ receipt: CodexSessionReceipt;
44
+ }>;
@@ -0,0 +1,24 @@
1
+ import type { CodexProcessLauncher } from "./codex-process.ts";
2
+ import { type CodexResponsesUpstream } from "./codex-relay.ts";
3
+ import type { AgentTaskAdapter, AgentTaskRoute, TaskRuntimeQualification } from "./task-runtime.ts";
4
+ export type CodexTaskAdapterOptions = Readonly<{
5
+ route: AgentTaskRoute;
6
+ runtime: Readonly<{
7
+ version: string;
8
+ digest: string;
9
+ }>;
10
+ qualification: TaskRuntimeQualification;
11
+ instructions: Readonly<{
12
+ base: string;
13
+ developer: string;
14
+ }>;
15
+ upstream: CodexResponsesUpstream;
16
+ launcher: CodexProcessLauncher;
17
+ now(): number;
18
+ }>;
19
+ /**
20
+ * Native Codex task adapter. It receives a capability broker from xcb,
21
+ * maps that exact profile into the relay, and retains the native stop receipt
22
+ * until the task runtime has joined the process and broker cleanup.
23
+ */
24
+ export declare function createCodexTaskAdapter(options: CodexTaskAdapterOptions): AgentTaskAdapter;
@@ -0,0 +1,21 @@
1
+ import { bindCodexAccountProcess } from "./codex-account-process.ts";
2
+ import type { CodexAccountProcessCloseReceipt } from "./codex-account-transport.ts";
3
+ import type { CodexProcessHandle, CodexProcessReceipt } from "./codex-process.ts";
4
+ export type CodexTaskProcessOptions = Parameters<typeof bindCodexAccountProcess>[0] & Readonly<{
5
+ cwd: string;
6
+ joinTimeoutMs: number;
7
+ /** Application-owned policy, scratch and journal observations. */
8
+ receipt(): CodexProcessReceipt;
9
+ /** Called only after matching physical proof and settled local work. The host
10
+ * persists closure and handles its own scratch; native join supplies neither
11
+ * configuration qualification nor successful product finalization. */
12
+ finalize(observation: Readonly<{
13
+ physical: CodexAccountProcessCloseReceipt;
14
+ operationCompleted: boolean;
15
+ }>): Promise<CodexProcessReceipt>;
16
+ }>;
17
+ /** Bridge for a trusted CodexProcessLauncher or CodexManagedProcessLauncher.
18
+ * The application still prepares and binds its exact task/account/profile,
19
+ * admits the shared artifact, owns launch intent and returns an owned port even
20
+ * when readiness fails. No launcher or qualification is inferred here. */
21
+ export declare function bindCodexTaskProcess(options: CodexTaskProcessOptions): CodexProcessHandle;
@@ -0,0 +1,105 @@
1
+ import { Transform } from "node:stream";
2
+ /** Bounded ACP v1 codec. Every inbound frame is withheld until its byte bound,
3
+ * UTF-8, JSON object and JSON-RPC envelope pass; malformed recognized frames
4
+ * close the client rather than being parsed hopefully. */
5
+ export declare const DEVIN_ACP_MAX_FRAME_BYTES: number;
6
+ export declare const DEVIN_ACP_MAX_PROMPT_BYTES: number;
7
+ export declare const DEVIN_ACP_PROTOCOL_VERSION = 1;
8
+ export type DevinAcpError = Readonly<{
9
+ code: number;
10
+ message: string;
11
+ }>;
12
+ export type DevinAcpInbound = Readonly<{
13
+ kind: "request";
14
+ id: string | number;
15
+ method: string;
16
+ params: unknown;
17
+ }> | Readonly<{
18
+ kind: "notification";
19
+ method: string;
20
+ params: unknown;
21
+ }> | Readonly<{
22
+ kind: "response";
23
+ id: string | number;
24
+ result: unknown;
25
+ }> | Readonly<{
26
+ kind: "errorResponse";
27
+ id: string | number;
28
+ error: DevinAcpError;
29
+ }>;
30
+ export type DevinStopReason = "end_turn" | "max_tokens" | "max_turn_requests" | "refusal" | "cancelled";
31
+ export type DevinPermissionOutcome = Readonly<{
32
+ outcome: "cancelled";
33
+ }> | Readonly<{
34
+ outcome: "selected";
35
+ optionId: string;
36
+ }>;
37
+ export type DevinPermissionOption = Readonly<{
38
+ optionId: string;
39
+ name: string;
40
+ kind: "allow_once" | "allow_always" | "reject_once" | "reject_always";
41
+ }>;
42
+ export type DevinPermissionRequest = Readonly<{
43
+ requestId: string;
44
+ sessionId: string;
45
+ toolCall: Readonly<{
46
+ toolCallId: string;
47
+ title: string | null;
48
+ kind: string | null;
49
+ status: string | null;
50
+ }>;
51
+ options: readonly DevinPermissionOption[];
52
+ }>;
53
+ export type DevinFact = Readonly<{
54
+ type: "assistantDelta";
55
+ sessionId: string;
56
+ text: string;
57
+ }> | Readonly<{
58
+ type: "usageUpdated";
59
+ sessionId: string;
60
+ used: number;
61
+ size: number;
62
+ inputTokens: number | null;
63
+ outputTokens: number | null;
64
+ }> | Readonly<{
65
+ type: "protocolNotice";
66
+ sessionId: string | null;
67
+ method: string;
68
+ }>;
69
+ export type DevinSessionConfigOption = Readonly<{
70
+ id: string;
71
+ currentValue: string;
72
+ }>;
73
+ export type DevinNewSession = Readonly<{
74
+ sessionId: string;
75
+ currentMode: string | null;
76
+ configOptions: readonly DevinSessionConfigOption[];
77
+ }>;
78
+ export type DevinPromptUsage = Readonly<{
79
+ totalTokens: number | null;
80
+ inputTokens: number | null;
81
+ outputTokens: number | null;
82
+ }>;
83
+ export type DevinPromptResult = Readonly<{
84
+ stopReason: DevinStopReason;
85
+ usage: DevinPromptUsage | null;
86
+ }>;
87
+ /** Splits a provider byte stream into validated single-line ACP frames. The
88
+ * last partial line is still validated; a missing trailing LF is not evidence. */
89
+ export declare function devinAcpFraming(maximumFrameBytes?: number): Transform;
90
+ export declare function parseAcpInbound(value: unknown): DevinAcpInbound;
91
+ export declare function parseInitializeResult(value: unknown): Readonly<{
92
+ protocolVersion: number;
93
+ loadSession: boolean;
94
+ }>;
95
+ export declare function parseNewSessionResult(value: unknown): DevinNewSession;
96
+ export declare function parsePromptResult(value: unknown): DevinPromptResult;
97
+ export declare function parseSessionUpdate(value: unknown): readonly DevinFact[];
98
+ export declare function parsePermissionRequest(requestId: string | number, value: unknown): DevinPermissionRequest;
99
+ export declare function validatePermissionOutcome(value: unknown, options: readonly DevinPermissionOption[]): DevinPermissionOutcome;
100
+ /** Chooses the first reject option; absent one, cancels the request. The host's
101
+ * allow decision must come from its own policy callback, never from the frame. */
102
+ export declare function denyPermissionOutcome(options: readonly DevinPermissionOption[]): DevinPermissionOutcome;
103
+ export declare function boundedDevinPrompt(value: unknown): string;
104
+ export declare function boundedDevinSessionId(value: unknown): string;
105
+ export declare function devinModeId(value: unknown): string;
@@ -0,0 +1,44 @@
1
+ import type { BoundedProviderProcessFactory } from "./provider-process.ts";
2
+ import type { DevinPermissionOutcome, DevinPermissionRequest } from "./devin-acp.ts";
3
+ import type { AgentTaskAdapter, AgentTaskRoute, TaskRuntimeQualification } from "./task-runtime.ts";
4
+ /**
5
+ * Devin task adapter over ACP v1 (`devin acp`). One bounded child per run:
6
+ * initialize -> session/new (host cwd, stdio MCP bridge when the profile has
7
+ * tools) -> optional mode/model selection -> session/prompt -> join. The
8
+ * profile's tools are exposed only through the host-owned loopback relay; fs
9
+ * and terminal client capabilities stay unimplemented. This adapter is
10
+ * admission-only: the supplied qualification decides whether tasks may run.
11
+ */
12
+ export type DevinAcpAdapterOptions = Readonly<{
13
+ route: AgentTaskRoute;
14
+ runtime: Readonly<{
15
+ version: string;
16
+ digest: string;
17
+ }>;
18
+ qualification: TaskRuntimeQualification;
19
+ /** Host-admitted Devin CLI executable; an absolute pinned artifact path. */
20
+ executable: string;
21
+ /** Exact child environment; the host owns credential and HOME isolation. */
22
+ env: Readonly<Record<string, string>>;
23
+ factory: BoundedProviderProcessFactory;
24
+ /** Runtime executable that runs the stdio MCP bridge (`-e` source), e.g. a
25
+ * pinned bun/node path. Required when a task profile declares tools. */
26
+ bridgeExecutable?: string;
27
+ /** Where the tool relay listens: default is an ephemeral host TCP port.
28
+ * On Linux the provider runs inside a private net namespace, so the relay
29
+ * binds `socketPath` (mounted into the namespace) and the in-namespace
30
+ * forwarder re-publishes it at `127.0.0.1:port` — the advertised bridge
31
+ * URL uses that port either way. */
32
+ relayListen?: Readonly<{
33
+ socketPath: string;
34
+ port: number;
35
+ }>;
36
+ /** Canonical absolute cwd for the Devin session's workspace. */
37
+ workspaceCwd: (workspaceId: string) => string;
38
+ /** Session mode pinned for every task, e.g. "plan" or "ask". */
39
+ mode?: string;
40
+ /** Host permission policy for provider tool gates; default denies all. */
41
+ permission?: (request: DevinPermissionRequest) => DevinPermissionOutcome | Promise<DevinPermissionOutcome>;
42
+ now(): number;
43
+ }>;
44
+ export declare function createDevinAcpAdapter(options: DevinAcpAdapterOptions): AgentTaskAdapter;
@@ -0,0 +1,36 @@
1
+ import type { BoundedProviderProcess } from "./provider-process.ts";
2
+ import { type DevinFact, type DevinNewSession, type DevinPermissionOutcome, type DevinPermissionRequest, type DevinPromptResult } from "./devin-acp.ts";
3
+ /** One owned `devin acp` child behind the bounded-process custody contract. The
4
+ * client serializes writes, correlates requests, routes inbound fs/permission
5
+ * requests to host handlers and refuses concurrent prompts. */
6
+ export type DevinAcpClientOptions = Readonly<{
7
+ process: BoundedProviderProcess;
8
+ onFact?: (fact: DevinFact) => void;
9
+ onPermission?: (request: DevinPermissionRequest) => DevinPermissionOutcome | Promise<DevinPermissionOutcome>;
10
+ maximumPendingRequests?: number;
11
+ shutdownGraceMs?: number;
12
+ shutdownForceJoinMs?: number;
13
+ }>;
14
+ export declare class DevinAcpClient {
15
+ #private;
16
+ constructor(options: DevinAcpClientOptions);
17
+ get closed(): Promise<void>;
18
+ initialize(signal?: AbortSignal): Promise<Readonly<{
19
+ protocolVersion: number;
20
+ loadSession: boolean;
21
+ }>>;
22
+ /** mcpServers entries are stdio transports only — Devin advertises no
23
+ * http/sse MCP capability; the caller must not pass other transports. */
24
+ newSession(input: {
25
+ cwd: string;
26
+ mcpServers?: readonly Record<string, unknown>[];
27
+ signal?: AbortSignal;
28
+ }): Promise<DevinNewSession>;
29
+ setMode(sessionIdInput: string, mode: string, signal?: AbortSignal): Promise<void>;
30
+ setConfigOption(sessionIdInput: string, configId: string, value: string, signal?: AbortSignal): Promise<void>;
31
+ prompt(sessionIdInput: string, text: string, signal?: AbortSignal): Promise<DevinPromptResult>;
32
+ cancel(sessionIdInput: string): Promise<void>;
33
+ resolvePermission(requestId: string, outcome: DevinPermissionOutcome): void;
34
+ close(): Promise<void>;
35
+ }
36
+ export type { DevinFact, DevinPermissionRequest, DevinPermissionOutcome, DevinPromptResult, DevinNewSession };
@@ -0,0 +1,28 @@
1
+ import type { CapabilityBroker } from "./capabilities.ts";
2
+ export type DevinToolRelay = Readonly<{
3
+ /** Closed loopback origin carrying the relay token; only the bridge sees it. */
4
+ bridgeEnv(): Readonly<Record<string, string>>;
5
+ mcpServerEntry(): Readonly<Record<string, unknown>>;
6
+ stop(): Promise<void>;
7
+ }>;
8
+ export type DevinToolRelayOptions = Readonly<{
9
+ broker: CapabilityBroker;
10
+ /** Runtime executable that runs DEVIN_MCP_BRIDGE_SOURCE, e.g. an exact
11
+ * host-pinned `bun`/`node` path. Required when the profile has tools. */
12
+ bridgeExecutable: string;
13
+ /** Default is an ephemeral TCP listener on host loopback. On Linux the
14
+ * provider runs inside a private network namespace, so the relay instead
15
+ * listens on `socketPath` (bound into the namespace) and the in-namespace
16
+ * forwarder re-publishes it at 127.0.0.1:`port` — the bridge env URL uses
17
+ * that advertised port in both modes. */
18
+ listen?: Readonly<{
19
+ socketPath: string;
20
+ port: number;
21
+ }>;
22
+ signal?: AbortSignal;
23
+ }>;
24
+ export declare function startDevinToolRelay(options: DevinToolRelayOptions): Promise<DevinToolRelay>;
25
+ /** Self-contained MCP-over-stdio server that proxies `tools/list`/`tools/call`
26
+ * to the host relay. Spawned as `executable -e <this source>` by the Devin ACP
27
+ * agent; uses only the runtime's own stdio/fetch surface. */
28
+ export declare const DEVIN_MCP_BRIDGE_SOURCE: string;
@@ -0,0 +1,35 @@
1
+ import type { Duplex } from "node:stream";
2
+ /** The only network authority the bridge uses; supplied by the trusted host. */
3
+ export interface EgressBridgeDialer {
4
+ connect(host: string, port: number): Promise<Duplex>;
5
+ }
6
+ export type EgressBridgeOptions = Readonly<{
7
+ /** Canonical absolute socket path inside a private 0700 directory. */
8
+ socketPath: string;
9
+ /** Exact hostnames admitted for CONNECT; absent admits any host on :443,
10
+ * matching the seatbelt `remote tcp "*:443"` candidate semantics. */
11
+ allowlist?: readonly string[];
12
+ maxConnections?: number;
13
+ idleTimeoutMs?: number;
14
+ dialer: EgressBridgeDialer;
15
+ }>;
16
+ export type EgressBridgeReceipt = Readonly<{
17
+ socketPath: string;
18
+ productionQualified: false;
19
+ connectionsAccepted: number;
20
+ connectionsRefused: number;
21
+ bytesIn: number;
22
+ bytesOut: number;
23
+ listenerClosed: boolean;
24
+ socketsJoined: boolean;
25
+ socketRemoved: boolean;
26
+ }>;
27
+ export interface EgressBridge {
28
+ readonly socketPath: string;
29
+ readonly connections: number;
30
+ close(): Promise<EgressBridgeReceipt>;
31
+ }
32
+ /** Default dialer for the real host seam: a bounded plain TCP connect whose
33
+ * resolver runs host-side. The in-sandbox process never performs DNS. */
34
+ export declare const egressBridgeDialer: EgressBridgeDialer;
35
+ export declare function createEgressBridge(options: EgressBridgeOptions): Promise<EgressBridge>;
@@ -0,0 +1,66 @@
1
+ import { type Socket } from "node:net";
2
+ import { type TLSSocket } from "node:tls";
3
+ import { Agent as HttpsAgent } from "node:https";
4
+ import type { Duplex } from "node:stream";
5
+ /** In-sandbox consumer for the egress-bridge contract — the reverse seam of
6
+ * `egress-bridge.ts`. A confined process reads `XCB_EGRESS_SOCKET`,
7
+ * speaks `CONNECT host:443` over that unix socket, and receives a raw tunnel
8
+ * to layer TLS or HTTP onto. The bridge owns DNS and dialing; this module
9
+ * never resolves names and never opens a direct socket. Works under Node ≥ 20
10
+ * and Bun; no dependencies beyond node: builtins.
11
+ *
12
+ * Nothing here widens the sandbox: if the bridge refuses (allowlist miss,
13
+ * port, malformed head) the failure surfaces as a typed error and the process
14
+ * keeps whatever isolation it already had. */
15
+ export declare const EGRESS_SOCKET_ENV = "XCB_EGRESS_SOCKET";
16
+ /** Read the bridge socket path from an environment map (defaults to
17
+ * `process.env`). Absent → null; present but malformed → typed failure. */
18
+ export declare function egressSocketFromEnv(env?: Readonly<Record<string, string | undefined>>): string | null;
19
+ export type EgressConnectOptions = Readonly<{
20
+ /** Bridge socket path; defaults to `XCB_EGRESS_SOCKET`. */
21
+ socketPath?: string;
22
+ timeoutMs?: number;
23
+ }>;
24
+ /** One `CONNECT host:443` over the bridge socket. The returned socket is
25
+ * positioned past the `200` head — bytes after it are tunnel traffic. Any
26
+ * non-200 head, timeout, or socket failure destroys the socket and throws. */
27
+ export declare function connectEgress(host: string, options?: EgressConnectOptions): Promise<Socket>;
28
+ /** CONNECT plus a TLS handshake over the tunnel. `servername` defaults to the
29
+ * CONNECT host so SNI and verification match the requested origin. Extra
30
+ * `tls.connect` options (ALPN, ca, checkServerIdentity) pass through. */
31
+ export declare function connectEgressTls(host: string, options?: EgressConnectOptions & Readonly<{
32
+ alpnProtocols?: readonly string[];
33
+ rejectUnauthorized?: boolean;
34
+ ca?: string | readonly string[];
35
+ servername?: string;
36
+ }>): Promise<TLSSocket>;
37
+ /** `https.Agent` whose connections arrive over the egress bridge. Plug into
38
+ * `https.request`/`https.get` via `{ agent }`; keepAlive stays off because the
39
+ * bridge meters each CONNECT. Only :443 origins are reachable — the bridge
40
+ * refuses everything else. */
41
+ export declare function createEgressHttpsAgent(options?: EgressConnectOptions): HttpsAgent;
42
+ export type EgressFetchInput = Readonly<{
43
+ method?: string;
44
+ headers?: Readonly<Record<string, string>>;
45
+ body?: string | Uint8Array;
46
+ maxBodyBytes?: number;
47
+ signal?: AbortSignal;
48
+ /** Injectable transport for tests; default is CONNECT + TLS to the host. */
49
+ transport?: (host: string, options: {
50
+ socketPath: string;
51
+ timeoutMs: number;
52
+ }) => Promise<Duplex>;
53
+ }>;
54
+ export type EgressFetchResponse = Readonly<{
55
+ status: number;
56
+ statusText: string;
57
+ headers: Readonly<Record<string, readonly string[]>>;
58
+ body: Uint8Array;
59
+ text(): string;
60
+ json(): unknown;
61
+ }>;
62
+ /** Minimal `fetch`-shaped HTTPS client over the egress bridge: https URLs on
63
+ * :443 only, bounded redirects and body. This is a request primitive, not a
64
+ * full fetch implementation — no cookies, cache, streaming request bodies or
65
+ * HTTP/2. */
66
+ export declare function fetchViaEgress(rawUrl: string, input?: EgressFetchInput & EgressConnectOptions): Promise<EgressFetchResponse>;