@agent-compose/sdk 0.8.3 → 0.8.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/dist/agent/agent-context.d.ts +9 -1
- package/dist/agent/agent-loop.d.ts +14 -1
- package/dist/client.d.ts +171 -33
- package/dist/directives.d.ts +14 -0
- package/dist/generated/verb-synopsis.d.ts +34 -0
- package/dist/index.d.ts +7 -5
- package/dist/index.js +1043 -41
- package/dist/runtimes/_cli-agent.d.ts +118 -0
- package/dist/runtimes/claude-code.d.ts +31 -1
- package/dist/runtimes/openai-desktop.d.ts +50 -0
- package/dist/runtimes/openai-desktop.js +1065 -59
- package/dist/runtimes/openai-desktop.test.d.ts +20 -0
- package/dist/runtimes/tool-pulse.test.d.ts +17 -0
- package/dist/sandbox/devbox.d.ts +5 -5
- package/dist/sandbox/registry.d.ts +12 -0
- package/dist/sandbox/sizes.d.ts +11 -5
- package/dist/sandbox.d.ts +2 -2
- package/dist/step-invocation/types.d.ts +1 -1
- package/dist/types/api-conversations.d.ts +85 -12
- package/dist/types/api-factory.d.ts +111 -1
- package/dist/types/conversation-stream.d.ts +22 -1
- package/dist/types/protocol.d.ts +130 -1
- package/dist/types/runtime.d.ts +71 -0
- package/package.json +1 -1
- package/src/agent/agent-context.ts +43 -9
- package/src/agent/agent-loop.ts +14 -3
- package/src/agent/desktop-open.ts +13 -1
- package/src/client.ts +256 -38
- package/src/directives.ts +21 -1
- package/src/generated/verb-synopsis.ts +544 -0
- package/src/index.ts +20 -5
- package/src/runtimes/_cli-agent.ts +333 -22
- package/src/runtimes/claude-code.ts +260 -14
- package/src/runtimes/openai-desktop.ts +82 -19
- package/src/sandbox/devbox.ts +5 -5
- package/src/sandbox/providers/e2b.ts +89 -17
- package/src/sandbox/registry.ts +19 -1
- package/src/sandbox/sizes.ts +11 -5
- package/src/sandbox.ts +2 -1
- package/src/types/api-conversations.ts +65 -13
- package/src/types/api-factory.ts +121 -1
- package/src/types/conversation-stream.ts +24 -1
- package/src/types/protocol.ts +127 -1
- package/src/types/runtime.ts +63 -0
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* registry entry.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { Sandbox } from "e2b";
|
|
8
|
+
import { Sandbox, Template } from "e2b";
|
|
9
9
|
import type { CommandHandle } from "e2b";
|
|
10
10
|
import type { Sandbox as Desktop } from "@e2b/desktop";
|
|
11
11
|
import pRetry from "p-retry";
|
|
@@ -13,7 +13,7 @@ import type {
|
|
|
13
13
|
SandboxProvider, SandboxCommandRunOptions, SandboxBackgroundProcess, SandboxPtyHandle,
|
|
14
14
|
} from "../../types/sandbox.js";
|
|
15
15
|
import { toE2bNetwork } from "../network-policy.js";
|
|
16
|
-
import { DEFAULT_SANDBOX_SIZE,
|
|
16
|
+
import { DEFAULT_SANDBOX_SIZE, e2bAgentEnvTemplate, isE2bSupportedSize } from "../sizes.js";
|
|
17
17
|
import { AGENT_COMPOSE_TAG } from "../provider-def.js";
|
|
18
18
|
import type { OwnedSandbox, SandboxProviderDef } from "../provider-def.js";
|
|
19
19
|
|
|
@@ -104,6 +104,9 @@ export function e2bMaxSandboxMs(): number {
|
|
|
104
104
|
return Number(process.env.E2B_MAX_SANDBOX_MS) || 60 * 60 * 1000;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
/** Once-per-process latch for the extendLifetime clamp warning below. */
|
|
108
|
+
let e2bExtendClampWarned = false;
|
|
109
|
+
|
|
107
110
|
/** Wrap an E2B `CommandHandle` as a provider-agnostic background process.
|
|
108
111
|
* `wait()` is normalised NOT to throw on a non-zero exit (mirroring the
|
|
109
112
|
* `commands.run` contract in `makeSandboxProvider`) so callers branch on
|
|
@@ -245,7 +248,20 @@ function makeE2bSandboxProvider(sb: Sandbox): SandboxProvider {
|
|
|
245
248
|
// the create timeout — an over-cap push would 400 and leave the OLD
|
|
246
249
|
// (possibly short) deadline standing.
|
|
247
250
|
async extendLifetime(ms) {
|
|
248
|
-
|
|
251
|
+
const maxMs = e2bMaxSandboxMs();
|
|
252
|
+
if (ms > maxMs && !e2bExtendClampWarned) {
|
|
253
|
+
// Once per process, not per extension: every far-horizon push clamps
|
|
254
|
+
// identically, and the fact worth surfacing is the CONFIGURATION —
|
|
255
|
+
// the server believes it bought `ms` of kill-clock slack but the
|
|
256
|
+
// plan cap silently shortens it, so the "orphan backstop" can reap
|
|
257
|
+
// a machine the platform still considers covered.
|
|
258
|
+
e2bExtendClampWarned = true;
|
|
259
|
+
console.warn(
|
|
260
|
+
`[sandbox] e2b lifetime extension clamped: requested ${ms}ms exceeds the plan cap ${maxMs}ms — ` +
|
|
261
|
+
`the kill-clock backstop fires at the cap, not the requested horizon. Raise E2B_MAX_SANDBOX_MS on plans that allow more.`,
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
await sb.setTimeout(Math.min(ms, maxMs));
|
|
249
265
|
},
|
|
250
266
|
// Push a freshly-resolved egress policy onto the live sandbox via E2B's
|
|
251
267
|
// native `updateNetwork` — the E2B analogue of Vercel's `update({
|
|
@@ -281,18 +297,24 @@ export const e2bProviderDef: SandboxProviderDef = {
|
|
|
281
297
|
// E2B has no create-time resource knob (specs are baked into the
|
|
282
298
|
// template/snapshot), so honouring `size` on E2B = picking a PRE-SIZED
|
|
283
299
|
// template, not passing the field through. We resolve the boot template
|
|
284
|
-
// from `size` below (`
|
|
300
|
+
// from `size` below (`e2bAgentEnvTemplate(size)`) when the caller gave no
|
|
285
301
|
// explicit template/bootFrom; the field itself is never forwarded to E2B.
|
|
286
302
|
create: async ({ template, timeoutMs, networkPolicy, size, ...rest }) => {
|
|
287
303
|
// `template` is an E2B template id/alias, a snapshot id (a valid create
|
|
288
304
|
// source that persists beyond its origin sandbox — bootFrom parity), or
|
|
289
|
-
// absent. When absent we pick the SIZE-MATCHED platform
|
|
290
|
-
// (`agent-
|
|
291
|
-
//
|
|
292
|
-
//
|
|
293
|
-
//
|
|
294
|
-
//
|
|
295
|
-
//
|
|
305
|
+
// absent. When absent we pick the SIZE-MATCHED platform agent-env alias
|
|
306
|
+
// (`agent-env-<size>`) — the SAME image every cloud session boots
|
|
307
|
+
// (server/src/sandbox/persistent.ts resolves the identical
|
|
308
|
+
// `e2bAgentEnvTemplate(size)`), so a template-less workflow run gets the
|
|
309
|
+
// full session toolchain (claude runtime, dev toolbelt, agentc, fsgw
|
|
310
|
+
// client) instead of a thinner image. One seam, both lanes: the
|
|
311
|
+
// session/run divergence that caused fleet-wide `exit 127`s (missing
|
|
312
|
+
// harness CLIs, fixed by on-demand install in v0.10.73) is structurally
|
|
313
|
+
// gone — there is no separate run-lane template to drift. The per-size
|
|
314
|
+
// aliasing also keeps `resources.size` giving the same machine spec on
|
|
315
|
+
// E2B as on Vercel (cross-provider parity). Only when no size is
|
|
316
|
+
// resolvable at all do we fall back to E2B_DEFAULT_TEMPLATE if set (the
|
|
317
|
+
// per-deployment analogue of Vercel's node24); else E2B's stock base.
|
|
296
318
|
// Self-provisioning runtimes (claude/codex/amp via bootFrom:"reuse") install
|
|
297
319
|
// their CLI on the base and cache it in the captured snapshot, so a
|
|
298
320
|
// template-less first run boots, installs, snapshots. Clamp the lifetime to
|
|
@@ -317,28 +339,41 @@ export const e2bProviderDef: SandboxProviderDef = {
|
|
|
317
339
|
const sandboxOpts = {
|
|
318
340
|
...rest,
|
|
319
341
|
timeoutMs: Math.min(timeoutMs, maxMs),
|
|
342
|
+
// Lifetime-deadline expiry PAUSES instead of killing, and the next
|
|
343
|
+
// incoming connection auto-resumes (recycle forensics 2026-08-23:
|
|
344
|
+
// uncontrolled kill-clock loss was the dominant machine-end cause
|
|
345
|
+
// ~60:1 — under this policy a missed extension becomes a resumable
|
|
346
|
+
// park our redial paths revive). CREATE-time policy, persistent for
|
|
347
|
+
// the sandbox's life — connect() takes no lifecycle. NOTE: this is
|
|
348
|
+
// e2b's `lifecycle` contract, NOT the deprecated `autoPause` flag
|
|
349
|
+
// (which the create body would silently drop).
|
|
350
|
+
lifecycle: { onTimeout: "pause" as const, autoResume: true },
|
|
320
351
|
...(networkPolicy ? { network: toE2bNetwork(networkPolicy) } : {}),
|
|
321
352
|
};
|
|
322
353
|
// Boot template resolution, in priority order:
|
|
323
354
|
// 1. explicit `template`/bootFrom (a pinned snapshot or alias),
|
|
324
|
-
// 2. else the SIZE-MATCHED
|
|
325
|
-
// (`size` resolved to DEFAULT_SANDBOX_SIZE
|
|
326
|
-
//
|
|
355
|
+
// 2. else the SIZE-MATCHED agent-env alias `agent-env-<size>` — the
|
|
356
|
+
// session-identical image (`size` resolved to DEFAULT_SANDBOX_SIZE
|
|
357
|
+
// when unset). NEVER the legacy `agent-compose-base-<size>` — that
|
|
358
|
+
// family remains a valid EXPLICIT bootFrom target only,
|
|
327
359
|
// 3. else E2B_DEFAULT_TEMPLATE as an ultimate per-deployment fallback,
|
|
328
360
|
// 4. else E2B's stock base.
|
|
329
|
-
// `32vcpu-64gb` has no
|
|
361
|
+
// `32vcpu-64gb` has no per-size template (Pro caps ~8 vCPU); the
|
|
330
362
|
// register + invoke guards reject it before a run reaches here, so we
|
|
331
|
-
// never synthesize a non-existent `agent-
|
|
363
|
+
// never synthesize a non-existent `agent-env-32vcpu-64gb` alias.
|
|
332
364
|
const resolvedSize = size ?? DEFAULT_SANDBOX_SIZE;
|
|
333
365
|
const tmpl =
|
|
334
366
|
template ??
|
|
335
|
-
(isE2bSupportedSize(resolvedSize) ?
|
|
367
|
+
(isE2bSupportedSize(resolvedSize) ? e2bAgentEnvTemplate(resolvedSize) : undefined) ??
|
|
336
368
|
process.env.E2B_DEFAULT_TEMPLATE;
|
|
337
369
|
return makeE2bSandboxProvider(
|
|
338
370
|
await (tmpl ? Sandbox.create(tmpl, sandboxOpts) : Sandbox.create(sandboxOpts)),
|
|
339
371
|
);
|
|
340
372
|
},
|
|
341
373
|
reconnect: async (sandboxId) => makeE2bSandboxProvider(
|
|
374
|
+
// The pause-on-timeout policy is create-time and persistent — connect
|
|
375
|
+
// takes no lifecycle options (the old per-connect autoPause flag is
|
|
376
|
+
// deprecated API-side).
|
|
342
377
|
await Sandbox.connect(sandboxId, { apiKey: process.env.E2B_API_KEY ?? "", timeoutMs: 60 * 60 * 1000 }),
|
|
343
378
|
),
|
|
344
379
|
killAll: async () => {
|
|
@@ -355,4 +390,41 @@ export const e2bProviderDef: SandboxProviderDef = {
|
|
|
355
390
|
// E2B snapshots are team-scoped; delete by id. Best-effort like Vercel's.
|
|
356
391
|
await Sandbox.deleteSnapshot(snapshotId, { apiKey: env.E2B_API_KEY });
|
|
357
392
|
},
|
|
393
|
+
snapshotExists: async (snapshotId, env) => {
|
|
394
|
+
// Existence probe for a CREATE SOURCE (the `snapshotResolves` contract:
|
|
395
|
+
// `true` = resolves, `false` = the provider definitively says it does not
|
|
396
|
+
// exist, anything indeterminate PROPAGATES — never reported as missing).
|
|
397
|
+
//
|
|
398
|
+
// e2b 2.30.5 has no GET-snapshot-by-id, so this composes the two
|
|
399
|
+
// documented lookups, cheapest first:
|
|
400
|
+
// 1. `Template.exists` — ONE round-trip to the template-existence
|
|
401
|
+
// endpoint (`GET /templates/aliases/{alias}`), with DOCUMENTED
|
|
402
|
+
// not-found semantics: 404 → false, 403 → exists but owned by
|
|
403
|
+
// another team → true (the SDK's own mapping). Snapshots are
|
|
404
|
+
// templates provider-side, and this probe also resolves the
|
|
405
|
+
// `agent-env-*` template ALIASES that E2B-pinned default templates
|
|
406
|
+
// register as their bootFrom — boot-time platform validation
|
|
407
|
+
// (validatePlatformSnapshots) probes those through this same seam,
|
|
408
|
+
// so a snapshots-only lookup would falsely alert "unresolvable" on
|
|
409
|
+
// every alias.
|
|
410
|
+
// 2. A paged scan of the team's snapshot list (`Sandbox.listSnapshots`)
|
|
411
|
+
// — authoritative for `createSnapshot` artifacts whatever their id
|
|
412
|
+
// shape, reached only when the template probe answered not-found.
|
|
413
|
+
// Bounded by the team's snapshot count (session rings are GC'd to a
|
|
414
|
+
// fixed retain depth), and this is rare-path code: machine-loss
|
|
415
|
+
// recovery and boot validation, never a hot loop.
|
|
416
|
+
// `false` therefore means BOTH documented lookups answered not-found.
|
|
417
|
+
// (`Sandbox.listSnapshots({ sandboxId })` — source-filtered — was
|
|
418
|
+
// rejected as the primary: callers hold only the snapshot id, and a
|
|
419
|
+
// filtered miss would still need the full scan before "absent" is
|
|
420
|
+
// honest.) Transport/auth faults from either call throw — indeterminate.
|
|
421
|
+
const opts = { apiKey: env.E2B_API_KEY };
|
|
422
|
+
if (await Template.exists(snapshotId, opts)) return true;
|
|
423
|
+
const paginator = Sandbox.listSnapshots(opts);
|
|
424
|
+
while (paginator.hasNext) {
|
|
425
|
+
const items = await pRetry(() => paginator.nextItems(), { retries: 3, minTimeout: 500, factor: 2 });
|
|
426
|
+
if (items.some((s) => s.snapshotId === snapshotId || s.names.includes(snapshotId))) return true;
|
|
427
|
+
}
|
|
428
|
+
return false;
|
|
429
|
+
},
|
|
358
430
|
};
|
package/src/sandbox/registry.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* at creation time.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { SandboxNotFoundError, RateLimitError } from "e2b";
|
|
9
|
+
import { SandboxNotFoundError, RateLimitError, NotFoundError } from "e2b";
|
|
10
10
|
import pRetry from "p-retry";
|
|
11
11
|
import type { FailedAttemptError } from "p-retry";
|
|
12
12
|
import type { SandboxProvider } from "../types/sandbox.js";
|
|
@@ -45,6 +45,24 @@ function isTransientSandboxError(error: unknown): boolean {
|
|
|
45
45
|
.test(message);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
/** Did a CREATE fail because its create SOURCE (template alias / snapshot id)
|
|
49
|
+
* does not resolve on the provider — vs any other create fault? The server's
|
|
50
|
+
* recovery-boot fall-through keys on this: a ring snapshot licensed on an
|
|
51
|
+
* INDETERMINATE existence probe that turns out to be gone must fall through
|
|
52
|
+
* the ring toward the base image, while every other create fault (quota,
|
|
53
|
+
* auth, 5xx, network) must propagate — falling through on those would trade
|
|
54
|
+
* a still-recoverable machine state for a base boot. E2B maps the create-
|
|
55
|
+
* time 404 ("template not found") to its typed `NotFoundError`; the message
|
|
56
|
+
* match is the fallback for untyped transports (Vercel). Lives in the SDK
|
|
57
|
+
* because the server holds its OWN `e2b` module instance — an `instanceof`
|
|
58
|
+
* against the class from the wrong copy is always false. */
|
|
59
|
+
export function isSandboxSourceNotFoundError(error: unknown): boolean {
|
|
60
|
+
if (error instanceof NotFoundError) return true;
|
|
61
|
+
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
62
|
+
return /(?:template|snapshot)[^\n]{0,80}not[\s_-]?found|not[\s_-]?found[^\n]{0,80}(?:template|snapshot)/i
|
|
63
|
+
.test(message);
|
|
64
|
+
}
|
|
65
|
+
|
|
48
66
|
/** The ONE place the sandbox retry policy lives. Provisioning, reconnecting, and
|
|
49
67
|
* snapshotting all race with the sandbox being paused/reclaimed; this runs the call
|
|
50
68
|
* through p-retry's generic backoff, retrying ONLY the transient race (and failing
|
package/src/sandbox/sizes.ts
CHANGED
|
@@ -148,15 +148,21 @@ export function sandboxSizeLabel(size: SandboxSize): string {
|
|
|
148
148
|
/** Stable E2B template ALIAS for the platform base at a given size
|
|
149
149
|
* (`agent-compose-base-<size>`). Aliases — not snapshot ids — so the refs are
|
|
150
150
|
* multi-account-clean: the same string resolves in any E2B account that built
|
|
151
|
-
* the templates. Built by `infra/e2b-template/build.ts
|
|
152
|
-
*
|
|
151
|
+
* the templates. Built by `infra/e2b-template/build.ts`. An EXPLICIT
|
|
152
|
+
* `bootFrom`/template target only — nothing boots it by default: template-less
|
|
153
|
+
* E2B creates resolve `e2bAgentEnvTemplate` (the session-identical image)
|
|
154
|
+
* instead, so the run and session lanes can never diverge on baked tooling. */
|
|
153
155
|
export function e2bBaseTemplate(size: SandboxSize): string {
|
|
154
156
|
return `agent-compose-base-${size}`;
|
|
155
157
|
}
|
|
156
158
|
|
|
157
|
-
/** Stable E2B template ALIAS for the agent runtime (base + claude binary
|
|
158
|
-
* given size (`agent-env-<size>`).
|
|
159
|
-
*
|
|
159
|
+
/** Stable E2B template ALIAS for the agent runtime (base + claude binary +
|
|
160
|
+
* dev toolbelt + session desktop) at a given size (`agent-env-<size>`).
|
|
161
|
+
* THE default E2B boot image, both lanes: every cloud SESSION boots it
|
|
162
|
+
* (server/src/sandbox/persistent.ts), and every template-less E2B workflow
|
|
163
|
+
* RUN boots it too (the e2b provider's size-matched default). The agent
|
|
164
|
+
* default templates boot from this via
|
|
165
|
+
* `bootFrom: { snapshotId: e2bAgentEnvTemplate(size) }`. Multi-account-clean
|
|
160
166
|
* for the same reason as `e2bBaseTemplate`. */
|
|
161
167
|
export function e2bAgentEnvTemplate(size: SandboxSize): string {
|
|
162
168
|
return `agent-env-${size}`;
|
package/src/sandbox.ts
CHANGED
|
@@ -54,7 +54,7 @@ export type { SandboxCreateOpts, OwnedSandbox } from "./sandbox/provider-def.js"
|
|
|
54
54
|
export { parseSseExecStream } from "./sandbox/exec-stream.js";
|
|
55
55
|
export type { ParseSseExecStreamOptions } from "./sandbox/exec-stream.js";
|
|
56
56
|
|
|
57
|
-
export { makeSandboxProvider } from "./sandbox/providers/e2b.js";
|
|
57
|
+
export { makeSandboxProvider, e2bMaxSandboxMs } from "./sandbox/providers/e2b.js";
|
|
58
58
|
export { makeDesktopSandboxProvider } from "./sandbox/providers/desktop.js";
|
|
59
59
|
export { VERCEL_MAX_TAGS, buildVercelTags } from "./sandbox/providers/vercel.js";
|
|
60
60
|
export { makeLocalSandboxProvider } from "./sandbox/providers/local.js";
|
|
@@ -65,6 +65,7 @@ export {
|
|
|
65
65
|
reconnectSandbox,
|
|
66
66
|
deleteSandboxSnapshot,
|
|
67
67
|
snapshotResolves,
|
|
68
|
+
isSandboxSourceNotFoundError,
|
|
68
69
|
getSandboxQuotas,
|
|
69
70
|
listOwnedSandboxes,
|
|
70
71
|
killSandboxById,
|
|
@@ -430,6 +430,49 @@ export interface BackgroundWorkHeld {
|
|
|
430
430
|
leaseUntil: string;
|
|
431
431
|
}
|
|
432
432
|
|
|
433
|
+
/** One background CHILD declared beside the busy lease (task #63): a
|
|
434
|
+
* detached process, by pid, with its durable journal/log file — what lets
|
|
435
|
+
* the platform VERIFY the work (`/proc/<pid>` + journal mtime) and
|
|
436
|
+
* reattach it after a park instead of losing it. */
|
|
437
|
+
export interface BackgroundWorkChildDecl {
|
|
438
|
+
pid: number;
|
|
439
|
+
/** Absolute guest-side path to the child's own durable output/journal
|
|
440
|
+
* file — its mtime is the progress evidence. */
|
|
441
|
+
journalPath?: string;
|
|
442
|
+
label?: string;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/** Outcome of requesting one machine size up
|
|
446
|
+
* (`POST /conversations/:id/machine/request-upsize` — task #110, the
|
|
447
|
+
* auto-resize policy's agent door). The platform arbitrates: within the
|
|
448
|
+
* team's daily cap the upsize is auto-granted (and lands immediately when
|
|
449
|
+
* no turn/background work holds the machine); past it a human approval
|
|
450
|
+
* card is posted. Wire shape mirrors
|
|
451
|
+
* server/src/sandbox/session-auto-resize.ts `UpsizeRequestOutcome`. */
|
|
452
|
+
export interface MachineUpsizeOutcome {
|
|
453
|
+
/** "executed" (landed now), "granted" (lands when the work settles),
|
|
454
|
+
* "pending_approval" (a card awaits the owner). Refusals arrive as HTTP
|
|
455
|
+
* errors carrying `code`. */
|
|
456
|
+
outcome: "executed" | "granted" | "pending_approval";
|
|
457
|
+
/** The size the grant/card names (e.g. "4vcpu-8gb"). */
|
|
458
|
+
size: string;
|
|
459
|
+
approvalId: string;
|
|
460
|
+
/** One human-readable line the CLI can print verbatim. */
|
|
461
|
+
message: string;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/** The session's background-work status
|
|
465
|
+
* (`GET /conversations/:id/background-work`). */
|
|
466
|
+
export interface BackgroundWorkStatus {
|
|
467
|
+
/** ISO deadline of the live lease, or null (no lease held). */
|
|
468
|
+
leaseUntil: string | null;
|
|
469
|
+
/** The declared children (each with its server-stamped `declaredAt`). */
|
|
470
|
+
children: Array<BackgroundWorkChildDecl & { declaredAt: string }>;
|
|
471
|
+
/** Non-null when a mid-work park froze declared children and the session
|
|
472
|
+
* still owes its conversation a status report (ISO park instant). */
|
|
473
|
+
frozenAt: string | null;
|
|
474
|
+
}
|
|
475
|
+
|
|
433
476
|
// ── Session branch proposals (ADR-0053) ─────────────────────────────────────
|
|
434
477
|
// Every cloud session works on its own factory-drive branch; the whole branch
|
|
435
478
|
// is the unit of review, like a PR. Wire shapes mirror
|
|
@@ -511,11 +554,9 @@ export interface SessionChangeSet {
|
|
|
511
554
|
* (hard cap 2; a manual re-review resets it). */
|
|
512
555
|
reviewAutoFollowup?: boolean;
|
|
513
556
|
reviewAutoRounds?: number;
|
|
514
|
-
/** The
|
|
515
|
-
* older servers; default
|
|
516
|
-
|
|
517
|
-
* from main headlessly. Toggled via `setSessionAutoRebase`. */
|
|
518
|
-
autoRebaseFromMain?: boolean;
|
|
557
|
+
/** The session's selectable cap on automatic follow-up rounds (additive
|
|
558
|
+
* — absent on older servers; default 2, range 1..5). */
|
|
559
|
+
reviewAutoRoundsMax?: number;
|
|
519
560
|
/** STRUCTURED review suggestions beside the notes (additive — absent on
|
|
520
561
|
* older servers): individually actionable {id, path, title, rationale,
|
|
521
562
|
* patch} entries the review session wrote back, status-stamped
|
|
@@ -793,14 +834,6 @@ export interface SessionRebaseReport {
|
|
|
793
834
|
foldSkipped?: boolean;
|
|
794
835
|
}
|
|
795
836
|
|
|
796
|
-
/** Result of `POST /conversations/:id/changes/autorebase` — the opt-in
|
|
797
|
-
* main-advance auto-rebase reflex's new state. */
|
|
798
|
-
export interface SessionAutoRebaseState {
|
|
799
|
-
object: "session_autorebase";
|
|
800
|
-
conversationId: string;
|
|
801
|
-
autoRebaseFromMain: boolean;
|
|
802
|
-
}
|
|
803
|
-
|
|
804
837
|
/** The sender's page stamp (HUD bar sends) — persisted server-side, never
|
|
805
838
|
* echoed back on the wire. Mirrors the server's `PageContext` schema. */
|
|
806
839
|
export interface ConversationPageContext {
|
|
@@ -1004,3 +1037,22 @@ export interface StreamConversationOptions {
|
|
|
1004
1037
|
lastEventId?: number;
|
|
1005
1038
|
signal?: AbortSignal;
|
|
1006
1039
|
}
|
|
1040
|
+
|
|
1041
|
+
/** Shared chat creation includes its own Ivy unless explicitly disabled. */
|
|
1042
|
+
export interface CreateChatInput {
|
|
1043
|
+
title?: string;
|
|
1044
|
+
visibility: "shared";
|
|
1045
|
+
access?: "public" | "private";
|
|
1046
|
+
memberIds?: string[];
|
|
1047
|
+
includeIvy?: boolean;
|
|
1048
|
+
}
|
|
1049
|
+
export interface ChannelIvyState {
|
|
1050
|
+
projects: Array<{ id: string; name: string }>;
|
|
1051
|
+
agent: AgentListRow | null;
|
|
1052
|
+
enabled: boolean; agentId: string | null; canManage: boolean; nextReviewAt: string | null;
|
|
1053
|
+
work: Array<{ id: string; title: string; status: string; evidence: string; nextAction: string; waitingOn: string | null; reviewAt: string | null }>;
|
|
1054
|
+
}
|
|
1055
|
+
export interface ProjectIvyConnection {
|
|
1056
|
+
id: string; teamId: string; projectId: string; provider: "github"; installationId: string;
|
|
1057
|
+
accountLogin: string; repositories: string[]; allowWrites: boolean; configuredBy: string | null; updatedAt: string;
|
|
1058
|
+
}
|
package/src/types/api-factory.ts
CHANGED
|
@@ -107,9 +107,15 @@ export interface RegisterWorkflowInput {
|
|
|
107
107
|
export interface TemplateRow {
|
|
108
108
|
name: string;
|
|
109
109
|
version: string;
|
|
110
|
-
|
|
110
|
+
/** Owning factory's slug. `null` on platform-published rows — a published
|
|
111
|
+
* default belongs to no single factory and is invokable via any slug. */
|
|
112
|
+
factorySlug: string | null;
|
|
111
113
|
/** Share scope (ADR-0045) — `null` = unscoped/grandfathered team tier. */
|
|
112
114
|
scope: ArtifactScope | null;
|
|
115
|
+
/** `factory` = registered in a factory of the caller's team; `published` =
|
|
116
|
+
* platform default (team-wide read+invoke in EVERY factory, never write;
|
|
117
|
+
* fork it to edit under a new name). Optional: older servers omit it. */
|
|
118
|
+
origin?: "factory" | "published";
|
|
113
119
|
}
|
|
114
120
|
|
|
115
121
|
/** Template detail (`GET /factories/:slug/templates/:name`) — the typed
|
|
@@ -132,6 +138,17 @@ export interface ListTemplatesOptions {
|
|
|
132
138
|
factorySlug?: string;
|
|
133
139
|
}
|
|
134
140
|
|
|
141
|
+
/** One team connector grant, as `GET /api/v1/connectors` returns it (wire
|
|
142
|
+
* shape verbatim — snake_case). The typed subset the SDK pins; additional
|
|
143
|
+
* fields flow through untyped. */
|
|
144
|
+
export interface ConnectorGrantSummary {
|
|
145
|
+
id: string;
|
|
146
|
+
provider: string;
|
|
147
|
+
external_account_label: string | null;
|
|
148
|
+
status: "active" | "revoked" | "reauth_required";
|
|
149
|
+
connected_by_user_id: string;
|
|
150
|
+
}
|
|
151
|
+
|
|
135
152
|
// ── Factory-file search (wire shape mirrors routes/factory-files.ts) ────────
|
|
136
153
|
|
|
137
154
|
/** Public "anyone with the link" state — the additive `public` field on file
|
|
@@ -381,6 +398,27 @@ export interface SessionSecretRequestCreated {
|
|
|
381
398
|
requestId: string;
|
|
382
399
|
url: string;
|
|
383
400
|
expiresAt: string;
|
|
401
|
+
/** True = a standing auto-approve set already filled the request at the
|
|
402
|
+
* mint — nothing is pending; source the session env now. */
|
|
403
|
+
autoApproved?: boolean;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/** What KIND of credential a vault request asks for (advisory): the vault
|
|
407
|
+
* page leads with the user's MATCHING standing entries. */
|
|
408
|
+
export type VaultRequestKind =
|
|
409
|
+
| "login" | "password" | "api_key" | "payment_card" | "env_file" | "note" | "other";
|
|
410
|
+
|
|
411
|
+
/** One STANDING vault entry usable for a session (the catalog read,
|
|
412
|
+
* 2026-08-31) — labels, kinds, and field NAMES only; values are write-only
|
|
413
|
+
* and never on this wire. */
|
|
414
|
+
export interface VaultCatalogEntry {
|
|
415
|
+
label: string;
|
|
416
|
+
kind: string;
|
|
417
|
+
fieldKeys: string[];
|
|
418
|
+
via: "owner" | "member" | "project";
|
|
419
|
+
ownerName: string | null;
|
|
420
|
+
projectName: string | null;
|
|
421
|
+
lastUsedAt: string | null;
|
|
384
422
|
}
|
|
385
423
|
|
|
386
424
|
/** A vault link's polled status. */
|
|
@@ -389,6 +427,26 @@ export interface SessionSecretRequestStatus {
|
|
|
389
427
|
status: "pending" | "fulfilled" | "cancelled" | "expired";
|
|
390
428
|
keys: string[];
|
|
391
429
|
expiresAt: string;
|
|
430
|
+
/** Cancellation attribution (task #111) — present on cancelled rows:
|
|
431
|
+
* who ended the ask ('human' = denied; 'agent' = withdrawn) and why. */
|
|
432
|
+
cancelledVia?: "human" | "agent";
|
|
433
|
+
cancelReason?: string | null;
|
|
434
|
+
/** The denying human's display name (best-effort; 'human' via only). */
|
|
435
|
+
deniedByName?: string | null;
|
|
436
|
+
/** Newest USER message in the session SINCE the mint (pending rows only)
|
|
437
|
+
* — the chat-interrupt probe: the human may be answering in chat while
|
|
438
|
+
* the agent blocks on --wait. */
|
|
439
|
+
userMessageAt?: string;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/** One OPEN vault request, as the list endpoint returns it — receipts
|
|
443
|
+
* only (keys + reason + clocks), never values. */
|
|
444
|
+
export interface SessionSecretRequestSummary {
|
|
445
|
+
requestId: string;
|
|
446
|
+
keys: string[];
|
|
447
|
+
reason: string | null;
|
|
448
|
+
expiresAt: string;
|
|
449
|
+
createdAt: string;
|
|
392
450
|
}
|
|
393
451
|
|
|
394
452
|
export interface CreateApiKeyInput {
|
|
@@ -515,6 +573,23 @@ export interface DriveRepoLink {
|
|
|
515
573
|
* auto-unlink; capped at 100 branches). Explicit `trackedBranches`:
|
|
516
574
|
* index 0 is the PRIMARY and keeps the plain placement; every additional
|
|
517
575
|
* branch lands at the sibling `dirPrefix@<sanitized-branch>` placement. */
|
|
576
|
+
/** POST /factories/:slug/repo-links/native — a repo whose origin IS the
|
|
577
|
+
* platform git plane (no GitHub side). `name` is ONE segment of GitHub's
|
|
578
|
+
* repo alphabet; `dirPrefix` defaults to `repos/<name>`. */
|
|
579
|
+
export interface CreateNativeRepoInput {
|
|
580
|
+
name: string;
|
|
581
|
+
dirPrefix?: string;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
export interface CreateNativeRepoResult {
|
|
585
|
+
link: DriveRepoLink;
|
|
586
|
+
/** Stock-git clone/push URL on the platform origin (credentials are
|
|
587
|
+
* minted per operation by `agentc repos git-credential --link`). */
|
|
588
|
+
repoUrl: string;
|
|
589
|
+
/** The CLI one-liner: `agentc repos clone <id> --via origin`. */
|
|
590
|
+
cloneCommand: string;
|
|
591
|
+
}
|
|
592
|
+
|
|
518
593
|
export interface CreateDriveRepoLinkInput {
|
|
519
594
|
dirPrefix: string;
|
|
520
595
|
repoFullName: string;
|
|
@@ -524,3 +599,48 @@ export interface CreateDriveRepoLinkInput {
|
|
|
524
599
|
/** Two-way sync ("push-out") — defaults to TRUE for new links. */
|
|
525
600
|
pushOut?: boolean;
|
|
526
601
|
}
|
|
602
|
+
|
|
603
|
+
/** A retained merge conflict. Resolution edits a live session branch;
|
|
604
|
+
* publication still goes through its normal merge and approval gates. */
|
|
605
|
+
/** A project-scoped secret's METADATA (scoped-secrets spec §4.1) — names
|
|
606
|
+
* only; values are write-only. Reaches SESSIONS linked into the project whose
|
|
607
|
+
* owner is a live member; workflow runs never receive project secrets. */
|
|
608
|
+
export interface ProjectSecretMeta {
|
|
609
|
+
id: string;
|
|
610
|
+
projectId: string;
|
|
611
|
+
secretKey: string;
|
|
612
|
+
createdBy: string | null;
|
|
613
|
+
delivery: "injected" | "brokered";
|
|
614
|
+
brokerHost: string | null;
|
|
615
|
+
brokerHeaderName: string | null;
|
|
616
|
+
brokerHeaderScheme: string | null;
|
|
617
|
+
createdAt: string | null;
|
|
618
|
+
updatedAt: string | null;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
export interface ProjectSecretList {
|
|
622
|
+
secrets: ProjectSecretMeta[];
|
|
623
|
+
tier: "project";
|
|
624
|
+
audience: "sessions";
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
export interface FactoryFileConflict {
|
|
628
|
+
id: string;
|
|
629
|
+
path: string;
|
|
630
|
+
conversationId: string | null;
|
|
631
|
+
runId: string | null;
|
|
632
|
+
runLabel: string | null;
|
|
633
|
+
reason: "same_region" | "binary" | "delete_vs_edit";
|
|
634
|
+
oursHash: string | null;
|
|
635
|
+
theirsHash: string | null;
|
|
636
|
+
baseHash: string | null;
|
|
637
|
+
status: string;
|
|
638
|
+
createdAt: string;
|
|
639
|
+
resolvesTo: "branch" | "main";
|
|
640
|
+
}
|
|
641
|
+
export interface FactoryFileConflictList {
|
|
642
|
+
object: "list";
|
|
643
|
+
data: FactoryFileConflict[];
|
|
644
|
+
has_more: boolean;
|
|
645
|
+
next_cursor: string | null;
|
|
646
|
+
}
|
|
@@ -130,6 +130,15 @@ export interface ConversationTurnStateEvent {
|
|
|
130
130
|
pendingCount: number;
|
|
131
131
|
at: number;
|
|
132
132
|
partial: true;
|
|
133
|
+
/** When the open turn started (ms) — carried by the connect-time
|
|
134
|
+
* snapshot frame only; absent on live transition frames. */
|
|
135
|
+
startedAt?: number | null;
|
|
136
|
+
/** The session holds a live background-work lease (snapshot frames
|
|
137
|
+
* only). */
|
|
138
|
+
leaseHeld?: boolean;
|
|
139
|
+
/** The open turn is blocked on a HUMAN ask — an open vault/secret
|
|
140
|
+
* request or approval (snapshot frames only). */
|
|
141
|
+
blockedOnAsk?: "secrets" | "approval" | null;
|
|
133
142
|
}
|
|
134
143
|
|
|
135
144
|
/** One attached surface (a dashboard tab, an `agentc session` TUI process)
|
|
@@ -185,6 +194,19 @@ export interface ConversationSessionStatusEvent {
|
|
|
185
194
|
at: number;
|
|
186
195
|
}
|
|
187
196
|
|
|
197
|
+
/** Live-only tool-run pulse (2026-08-29): the running turn's guest session
|
|
198
|
+
* PROVED progress (CPU/output counters advanced between heartbeat pulse
|
|
199
|
+
* samples) while the stream was otherwise silent — a long foreground tool
|
|
200
|
+
* (`bun install`) is working. Emitted at the executor's evidence-tick
|
|
201
|
+
* cadence, only on proof; carries no output content. Same contract as
|
|
202
|
+
* `turn_state`: never persisted, never advances `Last-Event-ID`. */
|
|
203
|
+
export interface ConversationToolPulseEvent {
|
|
204
|
+
event: "tool_pulse";
|
|
205
|
+
conversationId: string;
|
|
206
|
+
advancing: true;
|
|
207
|
+
at: number;
|
|
208
|
+
}
|
|
209
|
+
|
|
188
210
|
export type ConversationStreamEvent =
|
|
189
211
|
| ConversationPartEvent
|
|
190
212
|
| ConversationPartPartialEvent
|
|
@@ -195,7 +217,8 @@ export type ConversationStreamEvent =
|
|
|
195
217
|
| ConversationReplayContinueEvent
|
|
196
218
|
| ConversationTurnStateEvent
|
|
197
219
|
| ConversationPresenceEvent
|
|
198
|
-
| ConversationSessionStatusEvent
|
|
220
|
+
| ConversationSessionStatusEvent
|
|
221
|
+
| ConversationToolPulseEvent;
|
|
199
222
|
|
|
200
223
|
/**
|
|
201
224
|
* Fold one parsed SSE frame into a `ConversationStreamEvent`.
|