@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.
- package/LICENSE +21 -0
- package/MANAGED-CODEX.md +213 -0
- package/README.md +555 -0
- package/dist/accounts.d.ts +46 -0
- package/dist/broker-descriptors.d.ts +5 -0
- package/dist/broker.d.ts +62 -0
- package/dist/browser-session.d.ts +161 -0
- package/dist/canonical-json.d.ts +2 -0
- package/dist/capabilities.d.ts +72 -0
- package/dist/claude-api-models.d.ts +24 -0
- package/dist/claude-api-transport.d.ts +5 -0
- package/dist/claude-api.d.ts +23 -0
- package/dist/claude-credentials.d.ts +13 -0
- package/dist/claude-options.d.ts +10 -0
- package/dist/claude-sdk.d.ts +48 -0
- package/dist/claude-task-adapter.d.ts +65 -0
- package/dist/cli.js +4190 -0
- package/dist/codex-account-process.d.ts +149 -0
- package/dist/codex-account-transport.d.ts +39 -0
- package/dist/codex-account.d.ts +126 -0
- package/dist/codex-config.d.ts +53 -0
- package/dist/codex-host.d.ts +40 -0
- package/dist/codex-managed-baseline.d.ts +5 -0
- package/dist/codex-managed-catalog.d.ts +32 -0
- package/dist/codex-managed-config.d.ts +93 -0
- package/dist/codex-managed-ledger.d.ts +37 -0
- package/dist/codex-managed-session.d.ts +62 -0
- package/dist/codex-managed-task-adapter.d.ts +21 -0
- package/dist/codex-process.d.ts +67 -0
- package/dist/codex-protocol-manifest.d.ts +27 -0
- package/dist/codex-relay.d.ts +80 -0
- package/dist/codex-scratch.d.ts +36 -0
- package/dist/codex-session.d.ts +44 -0
- package/dist/codex-task-adapter.d.ts +24 -0
- package/dist/codex-task-process.d.ts +21 -0
- package/dist/devin-acp.d.ts +105 -0
- package/dist/devin-adapter.d.ts +44 -0
- package/dist/devin-client.d.ts +36 -0
- package/dist/devin-mcp.d.ts +28 -0
- package/dist/egress-bridge.d.ts +35 -0
- package/dist/egress-client.d.ts +66 -0
- package/dist/index-kg2gx694.js +7217 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.js +3455 -0
- package/dist/judge.d.ts +121 -0
- package/dist/loopback-server.d.ts +17 -0
- package/dist/managed-account.d.ts +176 -0
- package/dist/models.d.ts +23 -0
- package/dist/os-sandbox.d.ts +168 -0
- package/dist/private-file.d.ts +215 -0
- package/dist/process-port.d.ts +44 -0
- package/dist/process-write.d.ts +13 -0
- package/dist/provider-process.d.ts +26 -0
- package/dist/public-web.d.ts +21 -0
- package/dist/router.d.ts +43 -0
- package/dist/runtime.d.ts +90 -0
- package/dist/sqlite-port.d.ts +29 -0
- package/dist/task-runtime.d.ts +150 -0
- package/dist/validation.d.ts +6 -0
- package/package.json +70 -0
- package/sandbox/loopback-forwarder.cjs +172 -0
package/dist/judge.d.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider-neutral judgment port ("jev-style"): one fast request answers a
|
|
3
|
+
* batch of typed questions against one state object. A `Judge` is a routing,
|
|
4
|
+
* classification, and continuation helper — never an execution or custody
|
|
5
|
+
* boundary. Every consumer keeps a deterministic path when no judge is
|
|
6
|
+
* configured or a call fails, and bounds what it sends.
|
|
7
|
+
*
|
|
8
|
+
* Question kinds mirror the System One wire shape so additional backends can
|
|
9
|
+
* implement the same port: `noul` (yes/no probability), `choice` (pick one of
|
|
10
|
+
* labelled options with confidence), and `score` (score against criteria).
|
|
11
|
+
*/
|
|
12
|
+
export type JudgeState = string | object;
|
|
13
|
+
export interface NoulQuestion {
|
|
14
|
+
type: "noul";
|
|
15
|
+
instructions: string;
|
|
16
|
+
criteria?: {
|
|
17
|
+
true?: string;
|
|
18
|
+
false?: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export interface ChoiceQuestion {
|
|
22
|
+
type: "choice";
|
|
23
|
+
instructions: string;
|
|
24
|
+
criteria: Record<string, string | null>;
|
|
25
|
+
}
|
|
26
|
+
export interface ScoreQuestion {
|
|
27
|
+
type: "score";
|
|
28
|
+
instructions: string;
|
|
29
|
+
criteria: string[];
|
|
30
|
+
}
|
|
31
|
+
export type JudgeQuestion = NoulQuestion | ChoiceQuestion | ScoreQuestion;
|
|
32
|
+
export type JudgeQuestions = Record<string, JudgeQuestion>;
|
|
33
|
+
export interface NoulAnswer {
|
|
34
|
+
type: "noul";
|
|
35
|
+
noul: number;
|
|
36
|
+
}
|
|
37
|
+
export interface ChoiceAnswer {
|
|
38
|
+
type: "choice";
|
|
39
|
+
choice: string;
|
|
40
|
+
confidence: number;
|
|
41
|
+
probabilities: Record<string, number>;
|
|
42
|
+
}
|
|
43
|
+
export interface ScoreAnswer {
|
|
44
|
+
type: "score";
|
|
45
|
+
score: number;
|
|
46
|
+
confidence: number;
|
|
47
|
+
probabilities: Record<string, number>;
|
|
48
|
+
}
|
|
49
|
+
export type JudgeAnswer = NoulAnswer | ChoiceAnswer | ScoreAnswer;
|
|
50
|
+
export interface JudgeAnswers {
|
|
51
|
+
answers: Record<string, JudgeAnswer>;
|
|
52
|
+
model?: string;
|
|
53
|
+
usage?: {
|
|
54
|
+
input_tokens?: number;
|
|
55
|
+
output_tokens?: number;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** Anything that can answer a batch of judgment questions. */
|
|
59
|
+
export interface Judge {
|
|
60
|
+
ask(state: JudgeState, questions: JudgeQuestions): Promise<JudgeAnswers>;
|
|
61
|
+
}
|
|
62
|
+
export declare const SYSTEM_ONE_URL = "https://api.typesafe.ai/v1/systemone";
|
|
63
|
+
export declare const DEFAULT_JUDGE_MODEL = "jev-latest";
|
|
64
|
+
export declare const JUDGE_TOKEN_FILE = "jev-api-token";
|
|
65
|
+
export declare const JUDGE_KEY_ENV = "XCB_JEV_API_KEY";
|
|
66
|
+
export declare const JUDGE_KEY_VENDOR_ENV = "TYPESAFE_API_KEY";
|
|
67
|
+
export declare const JUDGE_URL_ENV = "XCB_JEV_URL";
|
|
68
|
+
export declare const JUDGE_MODEL_ENV = "XCB_JEV_MODEL";
|
|
69
|
+
export declare const MAX_JUDGE_STATE_BYTES: number;
|
|
70
|
+
export declare const MAX_JUDGE_QUESTIONS = 64;
|
|
71
|
+
export declare const MAX_JUDGE_INSTRUCTION_BYTES: number;
|
|
72
|
+
/** Validates a question batch before it reaches any backend. */
|
|
73
|
+
export declare function checkJudgeQuestions(questions: JudgeQuestions): JudgeQuestions;
|
|
74
|
+
/** Validates the serialized state before it reaches any backend. */
|
|
75
|
+
export declare function checkJudgeState(state: JudgeState): JudgeState;
|
|
76
|
+
/** Validates a System One response body; throws on anything malformed. */
|
|
77
|
+
export declare function parseJudgeResponse(status: number, body: string): JudgeAnswers;
|
|
78
|
+
/** Validates a complete response against the exact questions that were sent. */
|
|
79
|
+
export declare function checkJudgeAnswers(questions: JudgeQuestions, response: JudgeAnswers): JudgeAnswers;
|
|
80
|
+
/** Where a resolved judge key came from; `status` reports it, secrets never print. */
|
|
81
|
+
export type JudgeKeySource = "env" | "vault";
|
|
82
|
+
/** One HTTPS endpoint the judge may call; only `https` with a clean authority. */
|
|
83
|
+
export declare function parseJudgeEndpoint(url: string): URL;
|
|
84
|
+
export interface SystemOneOptions {
|
|
85
|
+
/** The API key. Never logged or persisted by the client. */
|
|
86
|
+
token: string;
|
|
87
|
+
/** Defaults to `jev-latest`. */
|
|
88
|
+
model?: string;
|
|
89
|
+
/** Defaults to the System One endpoint. */
|
|
90
|
+
endpoint?: string;
|
|
91
|
+
/** Injectable transport for tests; defaults to global `fetch`. */
|
|
92
|
+
fetch?: typeof fetch;
|
|
93
|
+
/** Request timeout in ms; defaults to 15000. */
|
|
94
|
+
timeoutMs?: number;
|
|
95
|
+
}
|
|
96
|
+
/** The TypeSafe System One backend: a `Judge` over one bounded HTTPS POST. */
|
|
97
|
+
export declare function createSystemOneJudge(options: SystemOneOptions): Judge;
|
|
98
|
+
/** Stores a judge key in the private state root; refuses to clobber. */
|
|
99
|
+
export declare function storeJudgeKey(stateRoot: string, key: string | Uint8Array): Promise<void>;
|
|
100
|
+
/** Removes the vaulted judge key; returns whether a file was removed. */
|
|
101
|
+
export declare function removeJudgeKey(stateRoot: string): Promise<boolean>;
|
|
102
|
+
export declare function hasJudgeKey(stateRoot: string): Promise<boolean>;
|
|
103
|
+
/** Resolves the effective judge key: environment first, then the vault file. */
|
|
104
|
+
export declare function resolveJudgeKey(stateRoot: string, env?: (name: string) => string | undefined): Promise<{
|
|
105
|
+
token: string;
|
|
106
|
+
source: JudgeKeySource;
|
|
107
|
+
} | null>;
|
|
108
|
+
export interface ResolveJudgeOptions {
|
|
109
|
+
/** Private state root holding the vault file. */
|
|
110
|
+
stateRoot: string;
|
|
111
|
+
/** The judge extension gate; resolution returns null when false. */
|
|
112
|
+
enabled: boolean;
|
|
113
|
+
model?: string;
|
|
114
|
+
endpoint?: string;
|
|
115
|
+
env?: (name: string) => string | undefined;
|
|
116
|
+
fetch?: typeof fetch;
|
|
117
|
+
}
|
|
118
|
+
/** Prevents the vaulted System One key from being redirected to another origin. */
|
|
119
|
+
export declare function checkJudgeKeyTarget(source: JudgeKeySource, endpoint?: string): void;
|
|
120
|
+
/** Resolves a ready judge when enabled and keyed; null for either absence. */
|
|
121
|
+
export declare function resolveJudge(options: ResolveJudgeOptions): Promise<Judge | null>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface LoopbackServer {
|
|
2
|
+
readonly port: number;
|
|
3
|
+
readonly pendingRequests: number;
|
|
4
|
+
stop(immediate?: boolean): Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
export type LoopbackServerOptions = Readonly<{
|
|
7
|
+
hostname: string;
|
|
8
|
+
/** When set, listen on this unix socket path instead of a TCP port —
|
|
9
|
+
* the host-side half of an in-namespace service forward. `port` reports
|
|
10
|
+
* 0; the caller owns the advertised reachability story. */
|
|
11
|
+
unixSocket?: string;
|
|
12
|
+
idleTimeoutMs?: number;
|
|
13
|
+
maxRequestBodyBytes?: number;
|
|
14
|
+
fetch(request: Request): Response | Promise<Response>;
|
|
15
|
+
error(error: unknown): Response;
|
|
16
|
+
}>;
|
|
17
|
+
export declare function createLoopbackServer(options: LoopbackServerOptions): Promise<LoopbackServer>;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import type { AccountLeaseStore } from "./accounts.ts";
|
|
2
|
+
import type { BrowserSessionBinding } from "./browser-session.ts";
|
|
3
|
+
import { type AgentProvider } from "./validation.ts";
|
|
4
|
+
/**
|
|
5
|
+
* Provider-neutral managed-account custody. This module owns the custody
|
|
6
|
+
* discipline every provider controller shares — the account lease, the exact
|
|
7
|
+
* binding (`provider`, `accountId`, `owner`, lease and process generations),
|
|
8
|
+
* the serialized state machine, unresolved-login recovery and the joined-close
|
|
9
|
+
* barrier. It is not an execution or authentication qualification: admitting a
|
|
10
|
+
* provider's runtime, its login transport and its usage surface remain
|
|
11
|
+
* separate host evidence.
|
|
12
|
+
*
|
|
13
|
+
* Provider-owned semantics stay behind `ManagedAccountSemantics`: the neutral
|
|
14
|
+
* controller never parses a provider account payload, chooses plan labels or
|
|
15
|
+
* guesses challenge shapes. The transport is a closed port — account reads,
|
|
16
|
+
* login lifecycle, logout and close only; no raw RPC, token export or turn
|
|
17
|
+
* method exists here.
|
|
18
|
+
*/
|
|
19
|
+
export type ManagedAccountState = "unchecked" | "signed-out" | "signing-in" | "signed-in" | "unavailable" | "recovery-required" | "closed";
|
|
20
|
+
export type ManagedAccountBinding = Readonly<{
|
|
21
|
+
provider: AgentProvider;
|
|
22
|
+
accountId: string;
|
|
23
|
+
owner: string;
|
|
24
|
+
leaseGeneration: number;
|
|
25
|
+
processGeneration: number;
|
|
26
|
+
}>;
|
|
27
|
+
/** How a login is driven. `provider-native` selects a provider-owned variant
|
|
28
|
+
* (Codex `chatgpt`/`chatgptDeviceCode`, Devin web login); `browser-session`
|
|
29
|
+
* drives sign-in inside the caller's per-account browser custody session —
|
|
30
|
+
* the session binding must equal the account's provider, accountId and owner
|
|
31
|
+
* so one account's cookie jar can never authenticate another. */
|
|
32
|
+
export type ManagedAccountLoginMethod = Readonly<{
|
|
33
|
+
type: "provider-native";
|
|
34
|
+
method: string;
|
|
35
|
+
}> | Readonly<{
|
|
36
|
+
type: "browser-session";
|
|
37
|
+
session: BrowserSessionBinding;
|
|
38
|
+
}>;
|
|
39
|
+
export type ManagedAccountLoginChallenge = Readonly<{
|
|
40
|
+
type: "provider-native";
|
|
41
|
+
method: string;
|
|
42
|
+
loginId: string;
|
|
43
|
+
fields: Readonly<Record<string, string>>;
|
|
44
|
+
}> | Readonly<{
|
|
45
|
+
type: "browser-session";
|
|
46
|
+
loginId: string;
|
|
47
|
+
session: BrowserSessionBinding;
|
|
48
|
+
navigationUrl: string;
|
|
49
|
+
}>;
|
|
50
|
+
/** Neutral projection the provider semantics maps its account payload onto.
|
|
51
|
+
* `state` is the only claim the controller interprets; `planLabel`/`detail`
|
|
52
|
+
* are bounded display text and never authority. */
|
|
53
|
+
export type ManagedAccountProjection = Readonly<{
|
|
54
|
+
state: "signed-out" | "signing-in" | "signed-in" | "unavailable";
|
|
55
|
+
planLabel?: string;
|
|
56
|
+
detail?: string;
|
|
57
|
+
}>;
|
|
58
|
+
export type ManagedAccountSnapshot = Readonly<{
|
|
59
|
+
provider: AgentProvider;
|
|
60
|
+
accountId: string;
|
|
61
|
+
state: ManagedAccountState;
|
|
62
|
+
accountGeneration: number;
|
|
63
|
+
processGeneration: number;
|
|
64
|
+
pendingLoginId: string | null;
|
|
65
|
+
planLabel?: string;
|
|
66
|
+
detail?: string;
|
|
67
|
+
}>;
|
|
68
|
+
export type ManagedAccountRequest = Readonly<{
|
|
69
|
+
binding: ManagedAccountBinding;
|
|
70
|
+
accountGeneration: number;
|
|
71
|
+
signal: AbortSignal;
|
|
72
|
+
deadlineMs: number;
|
|
73
|
+
}>;
|
|
74
|
+
export type ManagedAccountResponse = Readonly<{
|
|
75
|
+
binding: ManagedAccountBinding;
|
|
76
|
+
accountGeneration: number;
|
|
77
|
+
value: unknown;
|
|
78
|
+
}>;
|
|
79
|
+
export type ManagedAccountEvent = Readonly<{
|
|
80
|
+
binding: ManagedAccountBinding;
|
|
81
|
+
type: "account-updated" | "login-completed" | "disconnected";
|
|
82
|
+
loginId?: string | null;
|
|
83
|
+
success?: boolean;
|
|
84
|
+
}>;
|
|
85
|
+
export type ManagedAccountCloseReceipt = Readonly<{
|
|
86
|
+
binding: ManagedAccountBinding;
|
|
87
|
+
processExited: boolean;
|
|
88
|
+
processGroupStopped: boolean;
|
|
89
|
+
stdoutEnded: boolean;
|
|
90
|
+
stderrEnded: boolean;
|
|
91
|
+
writesSettled: boolean;
|
|
92
|
+
requestsSettled: boolean;
|
|
93
|
+
notificationsSettled: boolean;
|
|
94
|
+
}>;
|
|
95
|
+
/** Trusted implementation owns initialize, runtime/schema admission,
|
|
96
|
+
* credential custody and process custody. There is deliberately no raw RPC,
|
|
97
|
+
* token export or turn method. Return the handle before asynchronous launch,
|
|
98
|
+
* so close can join failed launches. A transport may implement extra
|
|
99
|
+
* provider-owned reads internally; they still run inside this fence. */
|
|
100
|
+
export interface ManagedAccountTransport {
|
|
101
|
+
accountRead(request: ManagedAccountRequest): Promise<ManagedAccountResponse>;
|
|
102
|
+
startLogin(request: ManagedAccountRequest & {
|
|
103
|
+
method: ManagedAccountLoginMethod;
|
|
104
|
+
}): Promise<ManagedAccountResponse>;
|
|
105
|
+
cancelLogin(request: ManagedAccountRequest & {
|
|
106
|
+
loginId: string;
|
|
107
|
+
}): Promise<ManagedAccountResponse>;
|
|
108
|
+
logout(request: ManagedAccountRequest): Promise<ManagedAccountResponse>;
|
|
109
|
+
close(request: {
|
|
110
|
+
binding: ManagedAccountBinding;
|
|
111
|
+
deadlineMs: number;
|
|
112
|
+
}): Promise<ManagedAccountCloseReceipt>;
|
|
113
|
+
}
|
|
114
|
+
/** Provider-owned interpretation. Runs inside the controller's serialized,
|
|
115
|
+
* generation-fenced operation; must throw on any payload it does not fully
|
|
116
|
+
* admit rather than coerce. The controller itself owns the closed challenge
|
|
117
|
+
* envelope and cancel-status enum — the only provider-specific
|
|
118
|
+
* interpretation is the account payload's projection onto the neutral
|
|
119
|
+
* readiness state. */
|
|
120
|
+
export interface ManagedAccountSemantics {
|
|
121
|
+
/** Map the transport's accountRead payload to a neutral projection. May
|
|
122
|
+
* issue further provider reads through the same fenced transport (paged
|
|
123
|
+
* model catalogs, quota detail) before returning. */
|
|
124
|
+
projectAccount(value: unknown, request: ManagedAccountRequest, transport: ManagedAccountTransport): Promise<ManagedAccountProjection>;
|
|
125
|
+
}
|
|
126
|
+
/** Bounded, read-only usage/quota observation. Windows are provider-named
|
|
127
|
+
* labels with optional utilization; `rawSha256` binds the provider's exact
|
|
128
|
+
* response as evidence without carrying it — quota bytes never enter custody
|
|
129
|
+
* journals, receipts or logs. A missing reader means the provider has no
|
|
130
|
+
* admitted usage surface; it is not a zero-usage claim. */
|
|
131
|
+
export type ManagedAccountUsageWindow = Readonly<{
|
|
132
|
+
label: string;
|
|
133
|
+
usedPercent: number | null;
|
|
134
|
+
resetsAt: number | null;
|
|
135
|
+
}>;
|
|
136
|
+
export type ManagedAccountUsage = Readonly<{
|
|
137
|
+
binding: ManagedAccountBinding;
|
|
138
|
+
observedAt: number;
|
|
139
|
+
planLabel: string | null;
|
|
140
|
+
windows: readonly ManagedAccountUsageWindow[];
|
|
141
|
+
rawSha256: string;
|
|
142
|
+
}>;
|
|
143
|
+
export interface ManagedAccountUsageReader {
|
|
144
|
+
readUsage(request: ManagedAccountRequest): Promise<ManagedAccountUsage>;
|
|
145
|
+
}
|
|
146
|
+
export type ManagedAccountOptions = Readonly<{
|
|
147
|
+
provider: AgentProvider;
|
|
148
|
+
accountId: string;
|
|
149
|
+
owner: string;
|
|
150
|
+
processGeneration: number;
|
|
151
|
+
leases: AccountLeaseStore;
|
|
152
|
+
transportFactory: (binding: ManagedAccountBinding, onEvent: (event: ManagedAccountEvent) => void) => ManagedAccountTransport;
|
|
153
|
+
semantics: ManagedAccountSemantics;
|
|
154
|
+
usageReader?: ManagedAccountUsageReader;
|
|
155
|
+
now?: () => number;
|
|
156
|
+
operationTimeoutMs?: number;
|
|
157
|
+
closeTimeoutMs?: number;
|
|
158
|
+
}>;
|
|
159
|
+
export interface ManagedAccountController {
|
|
160
|
+
readonly binding: () => ManagedAccountBinding | undefined;
|
|
161
|
+
snapshot(): ManagedAccountSnapshot;
|
|
162
|
+
check(signal?: AbortSignal): Promise<ManagedAccountSnapshot>;
|
|
163
|
+
startLogin(method: ManagedAccountLoginMethod, signal?: AbortSignal): Promise<ManagedAccountLoginChallenge>;
|
|
164
|
+
cancelLogin(loginId: string, signal?: AbortSignal): Promise<Readonly<{
|
|
165
|
+
status: "canceled" | "notFound";
|
|
166
|
+
}>>;
|
|
167
|
+
logout(signal?: AbortSignal): Promise<ManagedAccountSnapshot>;
|
|
168
|
+
/** Bounded quota observation through the admitted reader; `null` when the
|
|
169
|
+
* provider has none. Never extends custody or refreshes credentials. */
|
|
170
|
+
readUsage(signal?: AbortSignal): Promise<ManagedAccountUsage | null>;
|
|
171
|
+
close(): Promise<Readonly<{
|
|
172
|
+
released: boolean;
|
|
173
|
+
state: "closed" | "recovery-required";
|
|
174
|
+
}>>;
|
|
175
|
+
}
|
|
176
|
+
export declare function createManagedAccountController(options: ManagedAccountOptions): ManagedAccountController;
|
package/dist/models.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type AgentProvider } from "./validation.ts";
|
|
2
|
+
export type ModelEntry = Readonly<{
|
|
3
|
+
id: string;
|
|
4
|
+
inputUsdPerMillion: number;
|
|
5
|
+
outputUsdPerMillion: number;
|
|
6
|
+
supportsStructuredOutput: boolean;
|
|
7
|
+
available: boolean;
|
|
8
|
+
classifierEligible: boolean;
|
|
9
|
+
}>;
|
|
10
|
+
export type ModelCatalog = Readonly<{
|
|
11
|
+
provider: AgentProvider;
|
|
12
|
+
observedAt: number;
|
|
13
|
+
models: readonly ModelEntry[];
|
|
14
|
+
}>;
|
|
15
|
+
/** Host-supplied catalog; no invented model identifiers or unobserved availability. */
|
|
16
|
+
export declare function selectClassifierModel(value: unknown, now: number, maxAgeMs?: number): ModelEntry;
|
|
17
|
+
export type Classification = Readonly<{
|
|
18
|
+
respond: boolean;
|
|
19
|
+
confidence: number;
|
|
20
|
+
reason: "requested" | "helpful" | "human_active" | "not_needed" | "uncertain";
|
|
21
|
+
}>;
|
|
22
|
+
/** Strict JSON only: prose, fenced JSON, unknown fields and contradictory decisions fail closed. */
|
|
23
|
+
export declare function parseClassification(value: unknown): Classification;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { type BoundedProviderProcessFactory } from "./provider-process.ts";
|
|
2
|
+
/**
|
|
3
|
+
* OS-confinement port for provider processes. A backend turns a closed launch
|
|
4
|
+
* spec into a plan: a canonical policy artifact, its digest, the wrapper
|
|
5
|
+
* executable the child actually spawns, and a pure `wrap()` that rewrites one
|
|
6
|
+
* invocation onto the admitted policy. Planning is asynchronous so backends
|
|
7
|
+
* can re-verify their own admitted artifacts; wrapping is synchronous so it
|
|
8
|
+
* can run inside provider SDKs that spawn from a sync callback.
|
|
9
|
+
*
|
|
10
|
+
* Custody stays with the caller: bounded stdio, detached process groups,
|
|
11
|
+
* SIGTERM/SIGKILL escalation and join evidence are unchanged underneath every
|
|
12
|
+
* backend. A plan proves policy construction and artifact admission only — it
|
|
13
|
+
* is not proof that the kernel enforced the policy. Kernel-boundary evidence
|
|
14
|
+
* belongs to `qualification/` probes, and every receipt keeps
|
|
15
|
+
* `productionQualified: false` until a host supplies it.
|
|
16
|
+
*
|
|
17
|
+
* - `seatbelt` (darwin): the caller supplies reviewed SBPL policy text through
|
|
18
|
+
* `generateProfile`; the plan wraps argv as `/usr/bin/sandbox-exec -f
|
|
19
|
+
* <policyPath> <executable> <args...>` byte-identically to the launchers this
|
|
20
|
+
* port replaces. Environment passes through — callers already close it.
|
|
21
|
+
* - `bwrap` (linux): bubblewrap builds a private rootfs from per-file
|
|
22
|
+
* `--ro-bind` entries, `--bind` for the writable scratch/account roots,
|
|
23
|
+
* `--unshare-all`, `--new-session`, `--die-with-parent`, `--clearenv` +
|
|
24
|
+
* `--setenv`. Only `network: "denied"` is plannable: bwrap cannot express
|
|
25
|
+
* per-destination egress, and a unix-socket proxy bridge is a separate
|
|
26
|
+
* qualification. The wrapper binary is itself an admitted artifact whose
|
|
27
|
+
* SHA-256 is re-verified from a checked descriptor at plan time.
|
|
28
|
+
* `--setenv` values are visible in the wrapper's own command line, so a
|
|
29
|
+
* forwarder plan may instead declare `envFile`: `wrap()` then persists
|
|
30
|
+
* the closed env into the writable scratch (mode 0600) and emits no
|
|
31
|
+
* `--setenv` at all — the forwarder reads, deletes, and injects the
|
|
32
|
+
* pairs only into the supervised child's environment.
|
|
33
|
+
*/
|
|
34
|
+
export type OsSandboxNetworkPolicy = "denied" | "loopback" | "provider-tcp443-dns";
|
|
35
|
+
export type OsSandboxBackendName = "seatbelt" | "bwrap";
|
|
36
|
+
/** The platform the *admitted runtime* targets — host admission evidence, not
|
|
37
|
+
* `process.platform`. A backend refuses a spec whose platform it cannot
|
|
38
|
+
* enforce; synthetic custody tests exercise the real launch path on any host. */
|
|
39
|
+
export type OsSandboxPlatform = "darwin" | "linux";
|
|
40
|
+
export type OsSandboxSpec = Readonly<{
|
|
41
|
+
platform: OsSandboxPlatform;
|
|
42
|
+
/** Absolute canonical path of the in-sandbox executable (the admitted run
|
|
43
|
+
* snapshot), bound read-only and executable. */
|
|
44
|
+
executable: string;
|
|
45
|
+
/** Per-run private writable root (home/tmp/work). */
|
|
46
|
+
scratch: string;
|
|
47
|
+
/** Persistent private writable root holding account credentials. */
|
|
48
|
+
accountHome?: string;
|
|
49
|
+
/** Extra absolute file literals admitted read-only inside the sandbox
|
|
50
|
+
* (admitted library closure, fixed config). Never directories unless the
|
|
51
|
+
* backend documents subpath semantics. */
|
|
52
|
+
readOnlyPaths?: readonly string[];
|
|
53
|
+
/** Extra absolute paths admitted read-write inside the sandbox beyond
|
|
54
|
+
* scratch/accountHome — e.g. the consumer workspace for providers whose own
|
|
55
|
+
* filesystem tools write directly rather than through a host broker. Paths
|
|
56
|
+
* must not contain or enclose the executable (unless a `protectedPaths`
|
|
57
|
+
* entry re-covers it), each other, scratch, accountHome, policyPath, the
|
|
58
|
+
* egress socket, or forwarder artifacts. */
|
|
59
|
+
readWritePaths?: readonly string[];
|
|
60
|
+
/** Read-only re-covers mounted *after* the writable binds, so a path nested
|
|
61
|
+
* inside a writable root stays immutable (e.g. a provider's install tree
|
|
62
|
+
* inside its writable state root). On seatbelt these emit deny-write
|
|
63
|
+
* rules; on bwrap they are later ro binds that shadow the rw parent. Every
|
|
64
|
+
* entry must sit inside a writable root. */
|
|
65
|
+
protectedPaths?: readonly string[];
|
|
66
|
+
/** Masked subtrees nested inside a writable root: contents stay reachable
|
|
67
|
+
* on the host but are invisible inside the sandbox (e.g. credential stores
|
|
68
|
+
* that happen to sit under a workspace bound read-write). On seatbelt
|
|
69
|
+
* these emit deny read+write rules; on bwrap they are tmpfs mounts that
|
|
70
|
+
* shadow the bound subtree. Every entry must sit inside a writable root
|
|
71
|
+
* and must never cover the executable. */
|
|
72
|
+
hiddenPaths?: readonly string[];
|
|
73
|
+
network: OsSandboxNetworkPolicy;
|
|
74
|
+
/** Canonical absolute path of a host-side egress-bridge unix socket,
|
|
75
|
+
* bind-mounted read-write. Required iff `network` is
|
|
76
|
+
* `"provider-tcp443-dns"` on a bwrap plan; refused by seatbelt, whose
|
|
77
|
+
* profile carries egress internally. */
|
|
78
|
+
egressSocket?: string;
|
|
79
|
+
/** Optional in-namespace CONNECT forwarder for stock binaries that do not
|
|
80
|
+
* consume the bridge socket natively: an admitted JS runtime plus the
|
|
81
|
+
* admitted forwarder script are bound read-only and become the namespace
|
|
82
|
+
* entry point — `runtime script <socket> <port> <lo|- > <env|- > <svc|- > -- <executable> <args>`.
|
|
83
|
+
* The forwarder binds `127.0.0.1:<port>` and launches the child with
|
|
84
|
+
* standard proxy variables, so the plan env needs no proxy keys. Requires
|
|
85
|
+
* `egressSocket`; refused by seatbelt.
|
|
86
|
+
* `envFile` is an optional absolute path inside the writable scratch or
|
|
87
|
+
* account root: when set, `wrap()` writes the invocation env there
|
|
88
|
+
* (mode 0600) and the forwarder injects it into the child, keeping every
|
|
89
|
+
* value — secret or not — out of the wrapper's command line.
|
|
90
|
+
* `service` is an optional second listener: the forwarder binds
|
|
91
|
+
* `127.0.0.1:<service.port>` inside the namespace and pipes each accepted
|
|
92
|
+
* connection to `service.socket`, a host-side unix socket bound into the
|
|
93
|
+
* namespace — the in-namespace consumption path for host services (the
|
|
94
|
+
* tool relay) that are not CONNECT egress. */
|
|
95
|
+
egressForward?: Readonly<{
|
|
96
|
+
runtime: string;
|
|
97
|
+
script: string;
|
|
98
|
+
port: number;
|
|
99
|
+
envFile?: string;
|
|
100
|
+
service?: Readonly<{
|
|
101
|
+
socket: string;
|
|
102
|
+
port: number;
|
|
103
|
+
}>;
|
|
104
|
+
}>;
|
|
105
|
+
/** Absolute path of the durable policy artifact the caller persists
|
|
106
|
+
* (`sandbox.sb`, `sandbox.json`). Identity flows into custody journals. */
|
|
107
|
+
policyPath: string;
|
|
108
|
+
}>;
|
|
109
|
+
export type OsSandboxWrapInput = Readonly<{
|
|
110
|
+
args: readonly string[];
|
|
111
|
+
env: Readonly<Record<string, string>>;
|
|
112
|
+
cwd: string;
|
|
113
|
+
}>;
|
|
114
|
+
export type OsSandboxPlan = Readonly<{
|
|
115
|
+
backend: OsSandboxBackendName;
|
|
116
|
+
/** Canonical policy bytes: SBPL text on seatbelt, canonical plan JSON on
|
|
117
|
+
* bwrap. The caller persists this verbatim at `spec.policyPath`. */
|
|
118
|
+
policy: string;
|
|
119
|
+
policySha256: string;
|
|
120
|
+
/** The wrapper executable the child process spawns. */
|
|
121
|
+
executable: string;
|
|
122
|
+
wrap(input: OsSandboxWrapInput): {
|
|
123
|
+
args: readonly string[];
|
|
124
|
+
env: Readonly<Record<string, string>>;
|
|
125
|
+
};
|
|
126
|
+
}>;
|
|
127
|
+
export interface OsSandboxBackend {
|
|
128
|
+
readonly name: OsSandboxBackendName;
|
|
129
|
+
plan(spec: OsSandboxSpec): Promise<OsSandboxPlan>;
|
|
130
|
+
}
|
|
131
|
+
/** Re-verifies an admitted executable from a checked descriptor: owner,
|
|
132
|
+
* file identity, size bound, no-follow canonical path, and exact SHA-256.
|
|
133
|
+
* The owner may be the current user or root — a root-owned system tool like
|
|
134
|
+
* a distribution `bwrap` is at least as tamper-evident as a user-owned file.
|
|
135
|
+
* Any mutation or relabel is `OS_SANDBOX_EXECUTABLE_CHANGED`; a missing or
|
|
136
|
+
* non-file path is `OS_SANDBOX_EXECUTABLE_INVALID`. */
|
|
137
|
+
export declare function verifyOsSandboxExecutable(executablePath: string, sha256: string, sizeLimit: bigint): Promise<void>;
|
|
138
|
+
/** Pure seatbelt planning: the wrapper argv and artifact identity for an
|
|
139
|
+
* already-reviewed SBPL profile. Exported so tests can pin the exact spawn
|
|
140
|
+
* contract on any platform; `createSeatbeltOsSandbox` adds the darwin
|
|
141
|
+
* platform gate and generator ownership. */
|
|
142
|
+
export declare function planSeatbeltPolicy(input: OsSandboxSpec, policy: string): OsSandboxPlan;
|
|
143
|
+
/** Pure bwrap planning: canonical policy JSON plus wrapper argv for an
|
|
144
|
+
* already-validated spec. Exported so tests can pin the exact contract on any
|
|
145
|
+
* platform; `createBwrapOsSandbox` adds the linux platform gate, the
|
|
146
|
+
* `network: "denied"` bound, and wrapper-artifact re-verification. */
|
|
147
|
+
export declare function planBwrapPolicy(input: OsSandboxSpec, wrapperExecutable: string): OsSandboxPlan;
|
|
148
|
+
/** macOS seatbelt backend. The SBPL policy text is host-owned admission input
|
|
149
|
+
* — the port owns wrapping, canonical identity and the durable-artifact
|
|
150
|
+
* contract. `/usr/bin/sandbox-exec` is a fixed literal: PATH cannot inject. */
|
|
151
|
+
export declare function createSeatbeltOsSandbox(options: Readonly<{
|
|
152
|
+
/** Reviewed policy generator. Receives the validated spec; returns exact
|
|
153
|
+
* SBPL text. The generator, not this port, owns rule semantics. */
|
|
154
|
+
generateProfile(spec: OsSandboxSpec): string;
|
|
155
|
+
}>): OsSandboxBackend;
|
|
156
|
+
/** Linux bubblewrap backend, offline policy only. The wrapper binary is a
|
|
157
|
+
* second admitted artifact, re-verified at plan time. */
|
|
158
|
+
export declare function createBwrapOsSandbox(options: Readonly<{
|
|
159
|
+
/** Absolute canonical path of the admitted `bwrap` binary. */
|
|
160
|
+
executable: string;
|
|
161
|
+
/** SHA-256 of the admitted `bwrap` bytes. */
|
|
162
|
+
sha256: string;
|
|
163
|
+
}>): OsSandboxBackend;
|
|
164
|
+
/** Composes an admitted plan onto the unchanged bounded-provider custody:
|
|
165
|
+
* argv/env are rewritten, stdout/stderr bounds, detached group custody and
|
|
166
|
+
* join semantics are untouched. The returned factory stays synchronous so it
|
|
167
|
+
* can run inside provider SDKs that spawn from a sync callback. */
|
|
168
|
+
export declare function createSandboxedProviderProcessFactory(plan: OsSandboxPlan): BoundedProviderProcessFactory;
|