@runuai/host 0.9.150 → 0.9.151
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/db/migrations/0027_cloud_credential_foundation.sql +51 -0
- package/db/migrations/0028_leased_engine_runtimes.sql +11 -0
- package/db/migrations/meta/_journal.json +14 -0
- package/db/schema.ts +188 -0
- package/lib/agent-cli.ts +11 -13
- package/lib/agent.ts +177 -7
- package/lib/agents/durable-proc.ts +56 -44
- package/lib/agents/registry.ts +19 -3
- package/lib/agents/session-state.ts +23 -10
- package/lib/agents/transport.ts +101 -69
- package/lib/apple-one-off.ts +277 -0
- package/lib/apple-runtime-recycle.ts +4 -3
- package/lib/attachments.ts +19 -17
- package/lib/capture-flow.ts +319 -0
- package/lib/cloud-credential-cleanup-adapter.ts +123 -0
- package/lib/cloud-credential-host.ts +512 -0
- package/lib/cloud-credential-leases.ts +2544 -0
- package/lib/cloud-credential-runtime.ts +1035 -0
- package/lib/codex-auth.ts +21 -1
- package/lib/container-runtime.ts +76 -0
- package/lib/credential-cleanup-inventory.ts +405 -0
- package/lib/credential-consumers/engine-anchor.ts +1876 -0
- package/lib/credential-consumers/engines.ts +773 -0
- package/lib/credential-consumers/github.ts +594 -0
- package/lib/credential-consumers/mcp.ts +384 -0
- package/lib/credential-consumers/task-files.ts +127 -0
- package/lib/credential-store-pickup.ts +15 -2
- package/lib/credential-store.ts +361 -33
- package/lib/credentials-activation-authority.ts +546 -0
- package/lib/credentials-purge.ts +1006 -0
- package/lib/db.ts +11 -0
- package/lib/docker-exec.ts +3 -1
- package/lib/engine-accounts.ts +42 -32
- package/lib/engine-connect.ts +47 -2
- package/lib/engine-credential-env.ts +50 -0
- package/lib/engine-login.ts +83 -1
- package/lib/env-file.ts +62 -8
- package/lib/env.ts +5 -0
- package/lib/fs-aliases.ts +49 -0
- package/lib/git-clone-credentials.ts +334 -0
- package/lib/git-diff.ts +219 -177
- package/lib/git-identity.ts +59 -14
- package/lib/github-origin.ts +28 -0
- package/lib/github-tokens.ts +56 -21
- package/lib/host-env.ts +17 -4
- package/lib/launch-mode.ts +25 -0
- package/lib/legacy-runtime-retirement.ts +388 -0
- package/lib/machine-exec.ts +14 -3
- package/lib/machine-provider-aws.ts +50 -8
- package/lib/machine-provider-local.ts +4 -0
- package/lib/machine-provider.ts +4 -0
- package/lib/managed-operation-drain.ts +8 -0
- package/lib/managed-runtime.ts +371 -7
- package/lib/mcp-capture.ts +860 -0
- package/lib/mcp-connections.ts +8 -1
- package/lib/mcp-gateway.ts +168 -10
- package/lib/obs.ts +2 -6
- package/lib/orchestrator.ts +1830 -224
- package/lib/person-capture.ts +327 -0
- package/lib/project-env-legacy-tags.ts +130 -0
- package/lib/project-env-task-replica.ts +1752 -0
- package/lib/runner-boot.ts +10 -0
- package/lib/runtime-activation-tail.ts +50 -3
- package/lib/runtime-archive.ts +68 -1
- package/lib/runtime-reconnect.ts +86 -0
- package/lib/runtime-state.ts +429 -13
- package/lib/safe-http.ts +364 -0
- package/lib/secrets-proxy/task-trust.ts +34 -3
- package/lib/sidecar-resume.ts +18 -5
- package/lib/skills.ts +47 -8
- package/lib/standard-image.ts +157 -103
- package/lib/stopped-task-status-queue.ts +68 -10
- package/lib/task-credential-cleanup.ts +525 -0
- package/lib/task-diff-sandbox.ts +464 -0
- package/lib/task-diff.ts +66 -163
- package/lib/task-env-source.ts +68 -14
- package/lib/task-environment/anchor.ts +31 -0
- package/lib/task-environment/apple-container.ts +69 -28
- package/lib/task-environment/docker.ts +101 -20
- package/lib/task-environment/machine-environment.ts +61 -0
- package/lib/task-environment/machine-task-up.ts +459 -96
- package/lib/task-environment/machine.ts +101 -11
- package/lib/task-environment/self.ts +108 -50
- package/lib/task-environment/types.ts +89 -5
- package/lib/task-environment/workspace-files.ts +623 -25
- package/lib/task-environment/workspace-runtime.fixture.ts +50 -0
- package/lib/task-gateway-recovery.ts +4 -1
- package/lib/task-gateway-state.ts +15 -0
- package/lib/task-machine-swap-safety.ts +729 -0
- package/lib/token-broker-adopt.ts +344 -0
- package/lib/token-broker-lambda.ts +56 -2
- package/lib/token-broker-local.ts +28 -4
- package/lib/token-broker-revision.ts +310 -0
- package/lib/token-broker-ssm.ts +368 -28
- package/lib/token-lanes/openai-oauth.ts +7 -3
- package/lib/token-lanes/xai-oauth.ts +7 -3
- package/lib/transcript.ts +9 -5
- package/package.json +6 -2
- package/scripts/agent/task-up.sh +242 -100
- package/src/cloud-credentials.ts +2399 -0
- package/src/event-outbox.ts +8 -6
- package/src/index.ts +1386 -875
- package/src/main.ts +1061 -139
- package/src/outbound-frame.ts +31 -0
- package/src/project-env-policy.ts +125 -10
- package/src/protocol.ts +699 -23
- package/src/ui/server.ts +48 -10
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the runner may send the cloud. The cloud's bridge closes a runner's
|
|
3
|
+
* connection on any frame past its ceiling (100 MiB, lib/host-bridge.ts), so
|
|
4
|
+
* a frame that would serialize past it is never sent: a command's result is
|
|
5
|
+
* replaced by its failure, and anything else is dropped. Either way the
|
|
6
|
+
* connection, and every other task's traffic on it, stays open.
|
|
7
|
+
*/
|
|
8
|
+
import { HostErrorCode, type HostToCloud } from "./protocol";
|
|
9
|
+
|
|
10
|
+
/** Under the cloud's ceiling, with room to spare. */
|
|
11
|
+
export const MAX_OUTBOUND_FRAME_BYTES = 96 * 1024 * 1024;
|
|
12
|
+
|
|
13
|
+
/** The serialized frame to send for `frame`: itself, a command result's
|
|
14
|
+
* failure in its place, or null when nothing of it can be sent. */
|
|
15
|
+
export function outboundFrameJson(frame: HostToCloud): string | null {
|
|
16
|
+
const json = JSON.stringify(frame);
|
|
17
|
+
if (Buffer.byteLength(json, "utf8") <= MAX_OUTBOUND_FRAME_BYTES) return json;
|
|
18
|
+
if (frame.kind === "result") {
|
|
19
|
+
return JSON.stringify({
|
|
20
|
+
kind: "result",
|
|
21
|
+
commandId: frame.commandId,
|
|
22
|
+
result: {
|
|
23
|
+
ok: false,
|
|
24
|
+
code: HostErrorCode.Internal,
|
|
25
|
+
message: "the result is too large to send",
|
|
26
|
+
},
|
|
27
|
+
} satisfies HostToCloud);
|
|
28
|
+
}
|
|
29
|
+
console.warn(`[host] dropped an outbound ${frame.kind} frame past the bridge's size limit`);
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
@@ -32,9 +32,11 @@
|
|
|
32
32
|
|
|
33
33
|
import {
|
|
34
34
|
MAX_PROJECT_ENV_PROXY_DOMAINS,
|
|
35
|
+
isDeclaredProjectEnvBinding,
|
|
35
36
|
isProjectEnvBinding,
|
|
36
37
|
isProjectEnvSchemaDeclaration,
|
|
37
38
|
type ProjectEnvBinding,
|
|
39
|
+
type ProjectEnvPlaceholderShape,
|
|
38
40
|
type ProjectEnvSchemaDeclaration,
|
|
39
41
|
} from "./protocol";
|
|
40
42
|
|
|
@@ -49,11 +51,17 @@ import {
|
|
|
49
51
|
* pin each retained epoch's results on a fixed set of inputs: a tripwire,
|
|
50
52
|
* not a proof.
|
|
51
53
|
*/
|
|
52
|
-
export const PROJECT_ENV_POLICY_EPOCH =
|
|
54
|
+
export const PROJECT_ENV_POLICY_EPOCH = 2;
|
|
53
55
|
|
|
54
56
|
/** Every policy epoch this build derives under, oldest first. A runner
|
|
55
|
-
* advertises this set; the cloud derives under the newest it shares.
|
|
56
|
-
|
|
57
|
+
* advertises this set; the cloud derives under the newest it shares.
|
|
58
|
+
*
|
|
59
|
+
* - 1: ADR-153.
|
|
60
|
+
* - 2: ADR-154 §9, a key the owner declared proxied in Settings (a `proxy`
|
|
61
|
+
* binding with `declared`): the repository can only narrow it. Every other
|
|
62
|
+
* binding derives exactly as under 1, so it is stored under 1
|
|
63
|
+
* (`projectEnvBindingEpoch`) and needs no upgrade. */
|
|
64
|
+
export const PROJECT_ENV_POLICY_EPOCHS: readonly number[] = [1, 2];
|
|
57
65
|
|
|
58
66
|
/** What one task gets for a key. */
|
|
59
67
|
export type ProjectEnvDecision =
|
|
@@ -141,13 +149,15 @@ const deliversValue = (binding: ProjectEnvBinding | null): boolean =>
|
|
|
141
149
|
binding === null || binding.kind === "passthrough";
|
|
142
150
|
|
|
143
151
|
/**
|
|
144
|
-
* What a task gets for one key
|
|
145
|
-
* binding, and why a wider ask cannot be
|
|
146
|
-
*
|
|
147
|
-
*
|
|
152
|
+
* What a task gets for one key under epoch 1 (ADR-153), frozen: what its
|
|
153
|
+
* declaration proposes beyond the binding, and why a wider ask cannot be
|
|
154
|
+
* proposed if so. A binding declared in Settings did not exist under it, and
|
|
155
|
+
* is one it cannot accept.
|
|
148
156
|
*/
|
|
149
|
-
|
|
150
|
-
if (bindingInput !== null && !isProjectEnvBinding(bindingInput)
|
|
157
|
+
function projectEnvKeyPolicyV1(schemaInput: unknown, bindingInput: unknown): ProjectEnvKeyPolicy {
|
|
158
|
+
if (bindingInput !== null && (!isProjectEnvBinding(bindingInput) || isDeclaredProjectEnvBinding(bindingInput))) {
|
|
159
|
+
return policy(withhold("binding_invalid"));
|
|
160
|
+
}
|
|
151
161
|
const binding = bindingInput as ProjectEnvBinding | null;
|
|
152
162
|
const schema = evidenceOf(schemaInput);
|
|
153
163
|
|
|
@@ -198,6 +208,66 @@ export function projectEnvKeyPolicy(schemaInput: unknown, bindingInput: unknown)
|
|
|
198
208
|
}
|
|
199
209
|
}
|
|
200
210
|
|
|
211
|
+
/**
|
|
212
|
+
* ADR-154 §9: a key the owner declared proxied in Settings. Declaring was the
|
|
213
|
+
* owner's approval of exactly these domains, which the repository's schema
|
|
214
|
+
* can only narrow:
|
|
215
|
+
* - unreadable, undeclared, plain or `passthrough`: proxied to the declared
|
|
216
|
+
* domains, and nothing proposed (the repository never asks to deliver it);
|
|
217
|
+
* - `@proxy=omit`, or an unsupported directive: withheld;
|
|
218
|
+
* - `@proxy` naming some of the declared domains: proxied to those; any it
|
|
219
|
+
* names beyond them are a proposal (still declared), as in ADR-153, or
|
|
220
|
+
* blocked past the domain limit.
|
|
221
|
+
*/
|
|
222
|
+
function declaredKeyPolicy(
|
|
223
|
+
schema: ProjectEnvSchemaEvidence,
|
|
224
|
+
binding: Extract<ProjectEnvBinding, { kind: "proxy" }> & { declared: { placeholder: ProjectEnvPlaceholderShape | null } },
|
|
225
|
+
): ProjectEnvKeyPolicy {
|
|
226
|
+
const settings = sorted(binding.domains);
|
|
227
|
+
const asDeclared: ProjectEnvDecision = { kind: "proxy", domains: settings };
|
|
228
|
+
if (schema.status === "unavailable") return policy(asDeclared);
|
|
229
|
+
const directive = schema.declaration?.directive ?? { kind: "none" as const };
|
|
230
|
+
switch (directive.kind) {
|
|
231
|
+
case "none":
|
|
232
|
+
case "passthrough":
|
|
233
|
+
return policy(asDeclared);
|
|
234
|
+
case "omit":
|
|
235
|
+
return policy(withhold("repository_omits"));
|
|
236
|
+
case "unsupported":
|
|
237
|
+
return policy(withhold(directive.reason));
|
|
238
|
+
case "proxy": {
|
|
239
|
+
const declared = sorted(directive.domains);
|
|
240
|
+
const allowed = declared.filter((domain) => settings.includes(domain));
|
|
241
|
+
const decision: ProjectEnvDecision =
|
|
242
|
+
allowed.length > 0 ? { kind: "proxy", domains: allowed } : withhold("awaiting_approval");
|
|
243
|
+
if (allowed.length === declared.length) return policy(decision);
|
|
244
|
+
const union = sorted([...settings, ...declared]);
|
|
245
|
+
if (union.length > MAX_PROJECT_ENV_PROXY_DOMAINS) {
|
|
246
|
+
return policy(allowed.length > 0 ? decision : withhold("too_many_domains"), null, "too_many_domains");
|
|
247
|
+
}
|
|
248
|
+
return policy(decision, { kind: "proxy", domains: union, declared: { placeholder: binding.declared.placeholder } });
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* What a task gets for one key under the newest epoch (2), what its
|
|
255
|
+
* declaration proposes beyond the binding, and why a wider ask cannot be
|
|
256
|
+
* proposed if so. `binding` is null for a row the owner never approved. Both
|
|
257
|
+
* arguments are checked: see the file's header. A binding declared in
|
|
258
|
+
* Settings follows `declaredKeyPolicy`; every other derives as under epoch 1.
|
|
259
|
+
*/
|
|
260
|
+
export function projectEnvKeyPolicy(schemaInput: unknown, bindingInput: unknown): ProjectEnvKeyPolicy {
|
|
261
|
+
if (bindingInput !== null && !isProjectEnvBinding(bindingInput)) return policy(withhold("binding_invalid"));
|
|
262
|
+
if (isDeclaredProjectEnvBinding(bindingInput as ProjectEnvBinding | null)) {
|
|
263
|
+
return declaredKeyPolicy(
|
|
264
|
+
evidenceOf(schemaInput),
|
|
265
|
+
bindingInput as Parameters<typeof declaredKeyPolicy>[1],
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
return projectEnvKeyPolicyV1(schemaInput, bindingInput);
|
|
269
|
+
}
|
|
270
|
+
|
|
201
271
|
/**
|
|
202
272
|
* The binding a value is stored with when its row has none yet (ADR-153 §4:
|
|
203
273
|
* "a value set in settings is stored atomically with the policy the owner
|
|
@@ -239,6 +309,8 @@ export function initialProjectEnvBinding(schemaInput: unknown): ProjectEnvBindin
|
|
|
239
309
|
export function projectEnvKeyPolicyAt(epoch: unknown, schemaInput: unknown, bindingInput: unknown): ProjectEnvKeyPolicy {
|
|
240
310
|
switch (epoch) {
|
|
241
311
|
case 1:
|
|
312
|
+
return projectEnvKeyPolicyV1(schemaInput, bindingInput);
|
|
313
|
+
case 2:
|
|
242
314
|
return projectEnvKeyPolicy(schemaInput, bindingInput);
|
|
243
315
|
default:
|
|
244
316
|
return policy(withhold("policy_epoch_unknown"));
|
|
@@ -246,16 +318,59 @@ export function projectEnvKeyPolicyAt(epoch: unknown, schemaInput: unknown, bind
|
|
|
246
318
|
}
|
|
247
319
|
|
|
248
320
|
/** The binding a first save stores under a given epoch; withheld under an
|
|
249
|
-
* epoch this build does not know.
|
|
321
|
+
* epoch this build does not know. Epoch 2 stores what 1 does: a Settings
|
|
322
|
+
* declaration is the owner's own act, never a first save's. */
|
|
250
323
|
export function initialProjectEnvBindingAt(epoch: unknown, schemaInput: unknown): ProjectEnvBinding | null {
|
|
251
324
|
switch (epoch) {
|
|
252
325
|
case 1:
|
|
326
|
+
case 2:
|
|
253
327
|
return initialProjectEnvBinding(schemaInput);
|
|
254
328
|
default:
|
|
255
329
|
return { kind: "withhold", reason: "policy_epoch_unknown" };
|
|
256
330
|
}
|
|
257
331
|
}
|
|
258
332
|
|
|
333
|
+
/**
|
|
334
|
+
* The placeholder shape a proxied key's task gets, under its binding's
|
|
335
|
+
* epoch: the repository's `@placeholder` when its schema declares the key
|
|
336
|
+
* proxied with one; under epoch 2 a Settings declaration's shape next; null
|
|
337
|
+
* otherwise (the default shape). Only meaningful for a key the policy
|
|
338
|
+
* proxies; checked like the policy's own inputs.
|
|
339
|
+
*/
|
|
340
|
+
export function projectEnvPlaceholderAt(
|
|
341
|
+
epoch: unknown,
|
|
342
|
+
schemaInput: unknown,
|
|
343
|
+
bindingInput: unknown,
|
|
344
|
+
): ProjectEnvPlaceholderShape | null {
|
|
345
|
+
if (epoch !== 1 && epoch !== 2) return null;
|
|
346
|
+
const schema = evidenceOf(schemaInput);
|
|
347
|
+
const fromSchema =
|
|
348
|
+
schema.status === "resolved" && schema.declaration?.directive.kind === "proxy"
|
|
349
|
+
? (schema.declaration.placeholder ?? null)
|
|
350
|
+
: null;
|
|
351
|
+
if (fromSchema !== null || epoch === 1) return fromSchema;
|
|
352
|
+
const binding = bindingInput !== null && isProjectEnvBinding(bindingInput) ? bindingInput : null;
|
|
353
|
+
return isDeclaredProjectEnvBinding(binding) ? binding.declared.placeholder : null;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* The epoch to store a binding under: the lowest this build knows whose
|
|
358
|
+
* derivation for it is the newest's, never below `floor` (a record's epoch
|
|
359
|
+
* only moves up). A declared binding needs 2; every other means under 1 what
|
|
360
|
+
* it means under 2, so it stays readable by runners that know only 1.
|
|
361
|
+
*/
|
|
362
|
+
export function projectEnvBindingEpoch(binding: ProjectEnvBinding | null, floor = 1): number {
|
|
363
|
+
return Math.max(isDeclaredProjectEnvBinding(binding) ? 2 : 1, floor);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/** Whether moving a binding from one epoch to a newer one changes what any
|
|
367
|
+
* task would get: only then is an upgrade offered for the owner's Approve. */
|
|
368
|
+
export function projectEnvEpochUpgradeChanges(fromEpoch: number, toEpoch: number, binding: unknown): boolean {
|
|
369
|
+
if (fromEpoch >= toEpoch) return false;
|
|
370
|
+
// Epochs 1 and 2 derive the same for every binding valid under 1.
|
|
371
|
+
return !(fromEpoch === 1 && toEpoch === 2 && !(isProjectEnvBinding(binding) && isDeclaredProjectEnvBinding(binding)));
|
|
372
|
+
}
|
|
373
|
+
|
|
259
374
|
/** The most policy epochs a runner may advertise. */
|
|
260
375
|
export const MAX_ADVERTISED_PROJECT_ENV_POLICY_EPOCHS = 64;
|
|
261
376
|
|