@agent-compose/sdk 0.8.0 → 0.8.2
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 +1 -1
- package/dist/agent/agent-loop.d.ts +8 -0
- package/dist/agent/run-agent.d.ts +4 -0
- package/dist/client.d.ts +77 -15
- package/dist/display.d.ts +16 -0
- package/dist/index.d.ts +6 -6
- package/dist/index.js +522 -123
- package/dist/runtimes/_cli-agent.d.ts +34 -7
- package/dist/runtimes/claude-code.d.ts +10 -8
- package/dist/runtimes/codex.buildcommand.test.d.ts +9 -0
- package/dist/runtimes/codex.d.ts +4 -1
- package/dist/runtimes/openai-desktop.js +507 -122
- package/dist/sandbox/sizes.d.ts +120 -30
- package/dist/sandbox.d.ts +1 -1
- package/dist/types/api-conversations.d.ts +198 -0
- package/dist/types/api-factory.d.ts +84 -7
- package/dist/types/api-runs.d.ts +48 -2
- package/dist/types/protocol.d.ts +8 -0
- package/dist/types/workflow-metadata.d.ts +14 -5
- package/dist/utils/bundler.d.ts +56 -0
- package/dist/workflow-steps/workflow.d.ts +7 -0
- package/dist/workflows/invoke-child.d.ts +18 -0
- package/dist/workflows/invoke-child.test.d.ts +9 -0
- package/package.json +2 -2
- package/src/agent/agent-context.ts +28 -17
- package/src/agent/agent-loop.ts +9 -0
- package/src/agent/run-agent.ts +5 -0
- package/src/client.ts +201 -30
- package/src/display.ts +61 -15
- package/src/index.ts +22 -9
- package/src/runtimes/_cli-agent.ts +302 -63
- package/src/runtimes/claude-code.ts +25 -15
- package/src/runtimes/codex.ts +19 -5
- package/src/sandbox/providers/e2b.ts +8 -4
- package/src/sandbox/sizes.ts +127 -44
- package/src/sandbox.ts +8 -0
- package/src/types/api-conversations.ts +180 -0
- package/src/types/api-factory.ts +89 -7
- package/src/types/api-runs.ts +50 -2
- package/src/types/protocol.ts +8 -0
- package/src/types/workflow-metadata.ts +15 -5
- package/src/utils/bundler.ts +213 -3
- package/src/workflow-steps/workflow.ts +7 -0
- package/src/workflows/invoke-child.ts +47 -11
package/src/sandbox/sizes.ts
CHANGED
|
@@ -1,27 +1,68 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Sandbox machine sizes
|
|
2
|
+
* Sandbox machine sizes — THE single source of the size vocabulary.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Everything that names a size (the server's `SANDBOX_DEFAULT_SIZE` env enum,
|
|
5
|
+
* the register/invoke zod schemas, the run + session row types, the CLI's
|
|
6
|
+
* `--size` flag, the dashboard pickers via `GET /v1/sandbox-sizes`) derives
|
|
7
|
+
* from `SANDBOX_SIZES` / `SandboxSize` here. Adding a size is a ONE-LINE edit
|
|
8
|
+
* to `SANDBOX_MACHINES`: the type, the enums, the E2B build matrix and the
|
|
9
|
+
* pickers all follow. Do NOT re-declare the union inline anywhere.
|
|
10
|
+
*
|
|
11
|
+
* A size is a coarse hardware knob that maps to provider machine specs:
|
|
12
|
+
* Vercel honours it natively via `resources.vcpus`; E2B sizing is BAKED INTO
|
|
13
|
+
* THE TEMPLATE (e2b 2.30.5 has no create-time cpu/mem knob — `NewSandbox`
|
|
14
|
+
* carries only `templateID`), so on E2B a size resolves to a pre-built
|
|
15
|
+
* per-size template.
|
|
7
16
|
*/
|
|
8
17
|
|
|
9
|
-
/**
|
|
10
|
-
* than abstract t-shirt sizes
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
/** Every sandbox hardware SKU, with its real machine spec. Named for the
|
|
19
|
+
* machine (vCPU + RAM) rather than abstract t-shirt sizes, so a size can
|
|
20
|
+
* never quietly mean something different than it says.
|
|
21
|
+
*
|
|
22
|
+
* RAM is 2048 MB/vCPU everywhere EXCEPT `8vcpu-8gb`, which exists because
|
|
23
|
+
* E2B caps a sandbox at 8 vCPU / 8192 MB (e2b.dev/docs/billing: Hobby and
|
|
24
|
+
* Pro both "8 vCPU / 8 GB", raised only by arrangement): 8 vCPU at the 2 GB
|
|
25
|
+
* rule would need 16 GiB and cannot be built. `8vcpu-8gb` is the CPU ceiling
|
|
26
|
+
* at the memory ceiling — the only way to get 8 cores on E2B today, and a
|
|
27
|
+
* spec already proven bakeable by the devbox (`E2B_DEVBOX_SPEC`).
|
|
28
|
+
*
|
|
29
|
+
* Adding an entry here automatically: widens `SandboxSize`, widens every
|
|
30
|
+
* derived enum, and — if it fits under the E2B caps — adds it to
|
|
31
|
+
* `E2B_TEMPLATE_SIZES`, which is what `infra/e2b-template/build.ts` and the
|
|
32
|
+
* `sandbox-images` CI job loop over. Two templates get baked per size, so
|
|
33
|
+
* the matrix is not free; see that workflow's header. */
|
|
34
|
+
export const SANDBOX_MACHINES = {
|
|
35
|
+
/** 1 vCPU / 2 GiB — the cheap floor. Plenty for a terminal session or a
|
|
36
|
+
* shell-shaped agent; tight for a big `bun install` or a browser. */
|
|
37
|
+
"1vcpu-2gb": { vcpus: 1, memoryMB: 2048 },
|
|
38
|
+
/** 2 vCPU / 4 GiB — the default (Vercel's own default machine too). */
|
|
39
|
+
"2vcpu-4gb": { vcpus: 2, memoryMB: 4096 },
|
|
40
|
+
/** 4 vCPU / 8 GiB — comfortable for builds and multi-tool agent turns. */
|
|
41
|
+
"4vcpu-8gb": { vcpus: 4, memoryMB: 8192 },
|
|
42
|
+
/** 8 vCPU / 8 GiB — E2B's per-sandbox CEILING (cores maxed at the memory
|
|
43
|
+
* cap). NOT expressible on Vercel, whose RAM follows vCPUs at 2 GB each. */
|
|
44
|
+
"8vcpu-8gb": { vcpus: 8, memoryMB: 8192 },
|
|
45
|
+
/** 8 vCPU / 16 GiB — Vercel only; exceeds E2B's 8 GiB memory cap. */
|
|
46
|
+
"8vcpu-16gb": { vcpus: 8, memoryMB: 16384 },
|
|
47
|
+
/** 32 vCPU / 64 GiB — Vercel Enterprise only; far past every E2B cap. */
|
|
48
|
+
"32vcpu-64gb": { vcpus: 32, memoryMB: 65536 },
|
|
49
|
+
} as const satisfies Record<string, { vcpus: number; memoryMB: number }>;
|
|
50
|
+
|
|
51
|
+
/** Sandbox hardware SKU. Derived from `SANDBOX_MACHINES` — never re-spelled
|
|
52
|
+
* as an inline union. */
|
|
53
|
+
export type SandboxSize = keyof typeof SANDBOX_MACHINES;
|
|
54
|
+
|
|
55
|
+
/** The vocabulary as an ordered, smallest-first array — the shape zod
|
|
56
|
+
* (`z.enum`), the CLI's `--size` validation, and the wire catalogue want.
|
|
57
|
+
* Ordering is the pickers' display order, so keep it ascending. */
|
|
58
|
+
export const SANDBOX_SIZES = Object.keys(SANDBOX_MACHINES) as readonly SandboxSize[] as
|
|
59
|
+
readonly [SandboxSize, ...SandboxSize[]];
|
|
60
|
+
|
|
61
|
+
/** SKU → Vercel vCPU count (Vercel's RAM follows automatically at 2048
|
|
62
|
+
* MB/vCPU — which is why `isVercelSupportedSize` exists). */
|
|
63
|
+
export const SANDBOX_VCPUS: Record<SandboxSize, number> = Object.fromEntries(
|
|
64
|
+
SANDBOX_SIZES.map((s) => [s, SANDBOX_MACHINES[s].vcpus]),
|
|
65
|
+
) as Record<SandboxSize, number>;
|
|
25
66
|
|
|
26
67
|
/** SDK fallback size when neither the caller nor the deployment specifies one.
|
|
27
68
|
* Deliberately conservative — the OPERATIONAL default is the server's
|
|
@@ -29,37 +70,79 @@ export const SANDBOX_VCPUS: Record<SandboxSize, number> = {
|
|
|
29
70
|
* small matters because Vercel rate-limits creation by vCPUs-per-window
|
|
30
71
|
* (`api-sandboxes-vcpus-creation`); a large default 429s bursty/simultaneous
|
|
31
72
|
* creates. Workloads that need more RAM/CPU declare `resources.size` on the
|
|
32
|
-
* workflow rather than inflating the default for everyone.
|
|
73
|
+
* workflow rather than inflating the default for everyone.
|
|
74
|
+
*
|
|
75
|
+
* NOT `1vcpu-2gb`: the floor is an opt-IN for cheap sessions, not a quiet
|
|
76
|
+
* downgrade of every existing run's machine. */
|
|
33
77
|
export const DEFAULT_SANDBOX_SIZE: SandboxSize = "2vcpu-4gb";
|
|
34
78
|
|
|
35
|
-
/**
|
|
36
|
-
* (
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
79
|
+
/** SESSION default — deliberately one size up from the run default
|
|
80
|
+
* (2026-08-13): a session's sandbox carries the full desktop toolbelt
|
|
81
|
+
* (VS Code + Chromium + dockerd) plus the KasmVNC encoder at the 60fps
|
|
82
|
+
* cap, and that stack swap-thrashes on 4 GiB while the encoder starves on
|
|
83
|
+
* 2 shared vCPUs. Workflow runs keep DEFAULT_SANDBOX_SIZE — no desktop,
|
|
84
|
+
* no toolbelt weight. Sessions bill active time only (parked = storage),
|
|
85
|
+
* so the delta applies to active hours, not the fleet. */
|
|
86
|
+
export const SESSION_DEFAULT_SANDBOX_SIZE: SandboxSize = "4vcpu-8gb";
|
|
87
|
+
|
|
88
|
+
/** E2B's per-sandbox ceiling on the plans we run (e2b.dev/docs/billing —
|
|
89
|
+
* Hobby: "8 vCPU / 8 GB"; Pro: the same, "8+" only by arrangement with
|
|
90
|
+
* support). Recorded live too: `Template.build` 400s with "Memory can't be
|
|
91
|
+
* higher than 8192 MiB" past the memory cap.
|
|
92
|
+
*
|
|
93
|
+
* These two numbers are the ONLY knob for which sizes get an E2B template —
|
|
94
|
+
* raise them after E2B raises the account limit and the build matrix (and
|
|
95
|
+
* therefore the session picker) widens on its own. */
|
|
96
|
+
export const E2B_MAX_VCPUS = 8;
|
|
97
|
+
export const E2B_MAX_MEMORY_MB = 8192;
|
|
98
|
+
|
|
99
|
+
/** The E2B sizes we pre-build a template for — DERIVED from the caps, not
|
|
100
|
+
* hand-listed, so a new `SANDBOX_MACHINES` entry can never be offered
|
|
101
|
+
* without a template or omitted despite fitting. E2B sizing is
|
|
102
|
+
* template-baked (no per-create cpu/mem knob), so honouring `resources.size`
|
|
103
|
+
* on E2B means ONE pre-built template per size; `infra/e2b-template/build.ts`
|
|
104
|
+
* loops exactly this list. The register / invoke / session-spawn / resize
|
|
105
|
+
* guards all reject an unsupported E2B size before it can reach a create. */
|
|
106
|
+
export const E2B_TEMPLATE_SIZES: readonly SandboxSize[] = SANDBOX_SIZES.filter(
|
|
107
|
+
(s) => SANDBOX_MACHINES[s].vcpus <= E2B_MAX_VCPUS
|
|
108
|
+
&& SANDBOX_MACHINES[s].memoryMB <= E2B_MAX_MEMORY_MB,
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
/** Is `size` one E2B can be built/booted at? False for the sizes past E2B's
|
|
112
|
+
* 8 vCPU / 8 GiB ceiling (`8vcpu-16gb`, `32vcpu-64gb`) — those run on Vercel.
|
|
113
|
+
* The "no E2B equivalent" decision lives in exactly one place: the caps. */
|
|
52
114
|
export function isE2bSupportedSize(size: SandboxSize): boolean {
|
|
53
115
|
return E2B_TEMPLATE_SIZES.includes(size);
|
|
54
116
|
}
|
|
55
117
|
|
|
56
|
-
/**
|
|
57
|
-
*
|
|
58
|
-
|
|
59
|
-
|
|
118
|
+
/** Vercel's fixed memory-per-vCPU ratio. Vercel takes `resources.vcpus` and
|
|
119
|
+
* allocates RAM itself at this rate — there is no independent memory knob. */
|
|
120
|
+
export const VERCEL_MEMORY_MB_PER_VCPU = 2048;
|
|
121
|
+
|
|
122
|
+
/** Is `size` expressible on Vercel? Only when its RAM matches what Vercel
|
|
123
|
+
* would allocate for that vCPU count — otherwise asking for it would hand
|
|
124
|
+
* the caller a machine that does not match the name (`8vcpu-8gb` would come
|
|
125
|
+
* back with 16 GiB). Vercel's own ceiling (32 vCPU, Enterprise) is a plan
|
|
126
|
+
* matter, not a shape matter, so it is not encoded here. */
|
|
127
|
+
export function isVercelSupportedSize(size: SandboxSize): boolean {
|
|
128
|
+
const m = SANDBOX_MACHINES[size];
|
|
129
|
+
return m.memoryMB === m.vcpus * VERCEL_MEMORY_MB_PER_VCPU;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Machine spec for a SandboxSize, in the shape `Template.build` wants. Used
|
|
133
|
+
* by `infra/e2b-template/build.ts` to stamp the per-size base + agent-env
|
|
134
|
+
* templates. Reads the explicit table rather than deriving RAM from vCPUs —
|
|
135
|
+
* `8vcpu-8gb` is deliberately off the 2048 MB/vCPU line. */
|
|
60
136
|
export function e2bMachineSpec(size: SandboxSize): { cpuCount: number; memoryMB: number } {
|
|
61
|
-
const
|
|
62
|
-
return { cpuCount, memoryMB:
|
|
137
|
+
const m = SANDBOX_MACHINES[size];
|
|
138
|
+
return { cpuCount: m.vcpus, memoryMB: m.memoryMB };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Human label for a size — "2 vCPU · 4 GB". The wire catalogue carries it so
|
|
142
|
+
* the dashboard never has to parse the id back into numbers. */
|
|
143
|
+
export function sandboxSizeLabel(size: SandboxSize): string {
|
|
144
|
+
const m = SANDBOX_MACHINES[size];
|
|
145
|
+
return `${m.vcpus} vCPU · ${Math.round(m.memoryMB / 1024)} GB`;
|
|
63
146
|
}
|
|
64
147
|
|
|
65
148
|
/** Stable E2B template ALIAS for the platform base at a given size
|
package/src/sandbox.ts
CHANGED
|
@@ -23,10 +23,18 @@ export { DOT_SEGMENT_PATH_RE2, toVercelNetworkPolicy, toE2bNetwork } from "./san
|
|
|
23
23
|
|
|
24
24
|
export type { SandboxSize } from "./sandbox/sizes.js";
|
|
25
25
|
export {
|
|
26
|
+
SANDBOX_SIZES,
|
|
27
|
+
SANDBOX_MACHINES,
|
|
26
28
|
SANDBOX_VCPUS,
|
|
27
29
|
DEFAULT_SANDBOX_SIZE,
|
|
30
|
+
SESSION_DEFAULT_SANDBOX_SIZE,
|
|
28
31
|
E2B_TEMPLATE_SIZES,
|
|
32
|
+
E2B_MAX_VCPUS,
|
|
33
|
+
E2B_MAX_MEMORY_MB,
|
|
34
|
+
VERCEL_MEMORY_MB_PER_VCPU,
|
|
29
35
|
isE2bSupportedSize,
|
|
36
|
+
isVercelSupportedSize,
|
|
37
|
+
sandboxSizeLabel,
|
|
30
38
|
e2bMachineSpec,
|
|
31
39
|
e2bBaseTemplate,
|
|
32
40
|
e2bAgentEnvTemplate,
|
|
@@ -104,6 +104,9 @@ export interface ConversationMessageRow {
|
|
|
104
104
|
reactions: Record<string, string[]>;
|
|
105
105
|
replyCount: number;
|
|
106
106
|
lastReplyAt: string | null;
|
|
107
|
+
/** When the author last edited the content; null/absent = never edited
|
|
108
|
+
* (absent on older servers). */
|
|
109
|
+
editedAt?: string | null;
|
|
107
110
|
createdAt: string;
|
|
108
111
|
/** Thread-root facepile (≤3) — present only on roots with replies. */
|
|
109
112
|
replyAuthors?: Array<{ kind: string; id: string | null }>;
|
|
@@ -155,11 +158,24 @@ export interface ConversationDetail {
|
|
|
155
158
|
* in drivers. Null/absent when never reported (platform agents, older
|
|
156
159
|
* daemons/servers). */
|
|
157
160
|
sessionCommands?: Array<{ name: string; description: string }> | null;
|
|
161
|
+
/** Where this conversation's UNMERGED work lives: a cloud session writes
|
|
162
|
+
* its own private drive branch, and every drive read a client makes for
|
|
163
|
+
* THAT factory must carry `?branch=` or it resolves against `main`,
|
|
164
|
+
* where session-born files do not exist. Non-null only for a session
|
|
165
|
+
* with a drive branch and a resolvable factory; null/absent = main
|
|
166
|
+
* only (read loosely — older servers omit it). */
|
|
167
|
+
sessionDrive?: { factorySlug: string; branch: string } | null;
|
|
158
168
|
viewerLastReadAt: string | null;
|
|
159
169
|
/** The caller's role in this conversation (ADR-0045) — drives client
|
|
160
170
|
* affordances only; the server remains the authority on every action.
|
|
161
171
|
* Public channels report implicit `write` for non-member teammates. */
|
|
162
172
|
viewerRole: ConversationMemberRole;
|
|
173
|
+
/** Provenance when `viewerRole` is DERIVED rather than held: the project
|
|
174
|
+
* (ADR-0045 read floor) or attached channel (ADR-0057) the viewer
|
|
175
|
+
* follows this conversation through. Null/absent for real members —
|
|
176
|
+
* clients use it to explain read-only honestly ("You follow this
|
|
177
|
+
* session through <project>…"). Absent on older servers. */
|
|
178
|
+
viewerRoleVia?: { kind: "project" | "channel"; id: string; name: string | null } | null;
|
|
163
179
|
/** SSE replay watermark: the conversation's highest durable stream-event
|
|
164
180
|
* id at hydrate time — everything at or below it is already folded into
|
|
165
181
|
* `messages`. Seed the stream's first `Last-Event-ID` from it instead of
|
|
@@ -226,6 +242,33 @@ export interface CreateCloudSessionInput {
|
|
|
226
242
|
* profile ids, or "none" for a bare session. Omitted = the owner's
|
|
227
243
|
* default profile. */
|
|
228
244
|
connectorProfileId?: string;
|
|
245
|
+
// ── Session import (session-import spec) ────────────────────────────────
|
|
246
|
+
/** Idempotent dependency-setup command run by every fresh sandbox acquire
|
|
247
|
+
* after the drive mounts (`bun install`, `npm ci`, …). Strict charset —
|
|
248
|
+
* no shell metacharacters (400). Mutually exclusive with `templateId`
|
|
249
|
+
* (a snapshot carries its own installed state). */
|
|
250
|
+
setupCommand?: string;
|
|
251
|
+
/** Provenance of a session born by `agentc session import`: where the
|
|
252
|
+
* imported local session lived and which harness thread seeded it. */
|
|
253
|
+
handoffOrigin?: {
|
|
254
|
+
cwd: string;
|
|
255
|
+
sourceAcpSessionId: string | null;
|
|
256
|
+
agentKind: string;
|
|
257
|
+
mirrorConversationId: string | null;
|
|
258
|
+
};
|
|
259
|
+
/** Imported claude memories (session-import spec §memories): guest-home
|
|
260
|
+
* seeds re-applied on every fresh acquire. Paths under .claude/ only;
|
|
261
|
+
* strict base64; 256KB decoded total (server-capped). */
|
|
262
|
+
seedFiles?: Array<{ path: string; contentB64: string; mode: "write" | "append" }>;
|
|
263
|
+
/** DIFF REVIEW spawn: the conversation whose proposed changes this
|
|
264
|
+
* session is born to review. Server-resolved: on a graph-plane drive
|
|
265
|
+
* the new session's branch is minted as a FORK of the reviewed branch
|
|
266
|
+
* (its working dir IS the proposal, `.review/` comparison materials
|
|
267
|
+
* included); on the legacy plane the spawn degrades to an ordinary
|
|
268
|
+
* session. Requires a human caller and a chat session on the reviewed
|
|
269
|
+
* session's factory; 409 `review_of_review` when the target is itself
|
|
270
|
+
* a review session. */
|
|
271
|
+
reviewOfConversationId?: string;
|
|
229
272
|
}
|
|
230
273
|
|
|
231
274
|
export interface CloudSessionCreated {
|
|
@@ -302,6 +345,15 @@ export interface SessionForked {
|
|
|
302
345
|
conversationId: string;
|
|
303
346
|
}
|
|
304
347
|
|
|
348
|
+
/** Result of holding a session's background-work busy lease
|
|
349
|
+
* (`POST /conversations/:id/background-work`). While the lease is live the
|
|
350
|
+
* between-turns park/suspend leaves the session's VM running; it lapses on
|
|
351
|
+
* its own — re-hold to extend. */
|
|
352
|
+
export interface BackgroundWorkHeld {
|
|
353
|
+
/** ISO timestamp the lease now runs to. */
|
|
354
|
+
leaseUntil: string;
|
|
355
|
+
}
|
|
356
|
+
|
|
305
357
|
// ── Session branch proposals (ADR-0053) ─────────────────────────────────────
|
|
306
358
|
// Every cloud session works on its own factory-drive branch; the whole branch
|
|
307
359
|
// is the unit of review, like a PR. Wire shapes mirror
|
|
@@ -339,6 +391,121 @@ export interface SessionChangeSet {
|
|
|
339
391
|
truncated: boolean;
|
|
340
392
|
/** Open `factory_file_conflicts` rows from this session's prior merges. */
|
|
341
393
|
openConflicts: number;
|
|
394
|
+
/** MERGE GATE (merge-gate spec, additive — absent on older servers):
|
|
395
|
+
* null = ungated. Present: whether THIS caller's merge lands directly
|
|
396
|
+
* (`canMerge` — an owner or named approver) or stages a kind='merge'
|
|
397
|
+
* approval instead (`mergeSessionChanges` answers 202
|
|
398
|
+
* `SessionMergeGated`), plus the resolved approver set. */
|
|
399
|
+
mergeGate?: {
|
|
400
|
+
enabled: true;
|
|
401
|
+
canMerge: boolean;
|
|
402
|
+
approvers: Array<{ userId: string; label: string | null }>;
|
|
403
|
+
} | null;
|
|
404
|
+
/** The OPEN kind='merge' approval already waiting on this session's
|
|
405
|
+
* branch, if any (additive — absent on older servers). */
|
|
406
|
+
pendingApprovalId?: string | null;
|
|
407
|
+
/** DIFF REVIEW (additive — absent on older servers): notes written back
|
|
408
|
+
* by a spawned review session, whether they predate the served diff
|
|
409
|
+
* (`reviewStale` — the changed-file signature no longer matches), the
|
|
410
|
+
* bound review session's conversation + whether it is still live, and
|
|
411
|
+
* whether the deployment offers reviews at all (`reviewEnabled`; false
|
|
412
|
+
* hides the affordance). Reviews are USER-TRIGGERED only — binding
|
|
413
|
+
* rides `POST /conversations/:id/changes/review`, and notes arrive
|
|
414
|
+
* through the review session's own gated write-back. */
|
|
415
|
+
reviewNotes?: string | null;
|
|
416
|
+
reviewStale?: boolean;
|
|
417
|
+
reviewEnabled?: boolean;
|
|
418
|
+
reviewSessionConversationId?: string | null;
|
|
419
|
+
reviewSessionActive?: boolean;
|
|
420
|
+
/** STRUCTURED review suggestions beside the notes (additive — absent on
|
|
421
|
+
* older servers): individually actionable {id, path, title, rationale,
|
|
422
|
+
* patch} entries the review session wrote back, status-stamped
|
|
423
|
+
* `"proposed"` by the server (`"accepted"`/`"rejected"` are reserved for
|
|
424
|
+
* the human decision pass). */
|
|
425
|
+
reviewSuggestions?: ReviewSuggestion[];
|
|
426
|
+
/** THE REVIEWER MARKER (additive — absent on older servers): non-null ⇒
|
|
427
|
+
* THIS session was born to review that conversation's diff. Its own
|
|
428
|
+
* branch is a fork of the reviewed branch and can never merge
|
|
429
|
+
* (`mergeable` false, merge 409s `fork_branch_unmergeable`), and it is
|
|
430
|
+
* refused as a review target (409 `review_of_review`). */
|
|
431
|
+
reviewOfConversationId?: string | null;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/** One STRUCTURED, individually actionable suggestion a review session
|
|
435
|
+
* wrote back beside its notes (`SessionChangeSet.reviewSuggestions`). */
|
|
436
|
+
export interface ReviewSuggestion {
|
|
437
|
+
/** Reviewer-minted stable id — survives full-replace re-posts. */
|
|
438
|
+
id: string;
|
|
439
|
+
/** The changed file the suggestion targets (always in the change set —
|
|
440
|
+
* the writeback refuses paths outside it). */
|
|
441
|
+
path: string;
|
|
442
|
+
title: string;
|
|
443
|
+
rationale: string;
|
|
444
|
+
/** Unified-diff hunk targeting the PROPOSED side of `path`. */
|
|
445
|
+
patch: string;
|
|
446
|
+
/** Server-stamped `"proposed"` at writeback; the HUMAN decision routes
|
|
447
|
+
* (`POST …/changes/suggestions/:suggestionId/decide` / `…/decide-all`)
|
|
448
|
+
* are the only writers of the rest — `"accepted"` (the patch landed on
|
|
449
|
+
* the session branch), `"rejected"`, or `"stale"` (an accept found the
|
|
450
|
+
* file drifted since review; nothing was written). */
|
|
451
|
+
status: "proposed" | "accepted" | "rejected" | "stale";
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/** `POST /conversations/:id/changes/suggestions/:suggestionId/decide` —
|
|
455
|
+
* the post-call truth for that suggestion (an accept that found drift
|
|
456
|
+
* answers `"stale"`; repeats on a settled row are no-ops). */
|
|
457
|
+
export interface ReviewSuggestionDecision {
|
|
458
|
+
object: "review_suggestion_decision";
|
|
459
|
+
conversationId: string;
|
|
460
|
+
id: string;
|
|
461
|
+
status: ReviewSuggestion["status"];
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/** `POST /conversations/:id/changes/suggestions/decide-all` — every stored
|
|
465
|
+
* suggestion's post-call status (only `"proposed"` rows flip). */
|
|
466
|
+
export interface ReviewSuggestionDecisions {
|
|
467
|
+
object: "review_suggestion_decisions";
|
|
468
|
+
conversationId: string;
|
|
469
|
+
results: Array<{ id: string; status: ReviewSuggestion["status"] }>;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/** `GET /conversations/:id/changes/stats` — the changes chip's aggregate
|
|
473
|
+
* +added/−removed line counts, cached server-side per (base, theirsHead).
|
|
474
|
+
* `unavailable` = no cheap head anchors (legacy plane, unbranched) — the
|
|
475
|
+
* chip degrades to its file count, never fake zeros. */
|
|
476
|
+
export type SessionChangeStats =
|
|
477
|
+
| { object: "session_change_stats"; conversationId: string; state: "unavailable" }
|
|
478
|
+
| {
|
|
479
|
+
object: "session_change_stats"; conversationId: string; state: "ok";
|
|
480
|
+
additions: number; deletions: number; files: number;
|
|
481
|
+
/** True when the count is partial (list truncated / file cap). */
|
|
482
|
+
truncated: boolean;
|
|
483
|
+
base: string; theirsHead: string;
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
/** `POST /conversations/:id/changes/review` — binds a just-spawned review
|
|
487
|
+
* session to the reviewed session and stamps the diff signature the
|
|
488
|
+
* review covers. */
|
|
489
|
+
export interface SessionDiffReviewBound {
|
|
490
|
+
object: "session_diff_review";
|
|
491
|
+
conversationId: string;
|
|
492
|
+
reviewSessionConversationId: string;
|
|
493
|
+
reviewDiffSignature: string;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/** 202 from `POST /conversations/:id/changes/merge` on a merge-GATED
|
|
497
|
+
* session when the caller is not an approver: nothing merged — the ask
|
|
498
|
+
* froze into (`approval_required`) or converged on (`approval_pending`) a
|
|
499
|
+
* kind='merge' approval routed to the approvers. */
|
|
500
|
+
export interface SessionMergeGated {
|
|
501
|
+
object: "session_merge_gated";
|
|
502
|
+
conversationId: string;
|
|
503
|
+
code: "approval_required" | "approval_pending";
|
|
504
|
+
approvalId: string;
|
|
505
|
+
branch?: string;
|
|
506
|
+
changeCount?: number;
|
|
507
|
+
openConflicts?: number;
|
|
508
|
+
approvers: Array<{ userId: string; label: string | null }>;
|
|
342
509
|
}
|
|
343
510
|
|
|
344
511
|
/** Per-file accounting of one session branch merge — the merge core's
|
|
@@ -410,6 +577,15 @@ export interface SendConversationMessageInput {
|
|
|
410
577
|
* work and a coalesced follow-up turn will answer it. NOT an error. */
|
|
411
578
|
export type ConversationTurnState = "none" | "started" | "queued";
|
|
412
579
|
|
|
580
|
+
/** One @-mentioned user the server DROPPED as a non-member — the mention
|
|
581
|
+
* ping went to nobody, and the response says so instead of staying
|
|
582
|
+
* silent. `name` is the label the sender's own text carried for the
|
|
583
|
+
* mention (picked in the typeahead — no new information). */
|
|
584
|
+
export interface UnnotifiedMention {
|
|
585
|
+
userId: string;
|
|
586
|
+
name: string;
|
|
587
|
+
}
|
|
588
|
+
|
|
413
589
|
export interface SendConversationMessageResult {
|
|
414
590
|
/** The persisted user message's id. */
|
|
415
591
|
messageId: string;
|
|
@@ -422,6 +598,10 @@ export interface SendConversationMessageResult {
|
|
|
422
598
|
* each runs its own turn in its own conversation. Absent on older
|
|
423
599
|
* servers and non-channel sends. */
|
|
424
600
|
relayedSessionIds?: string[];
|
|
601
|
+
/** Mentioned users whose ping was dropped by the member filter (a
|
|
602
|
+
* private channel pings members only; a DM pings only the pair) —
|
|
603
|
+
* present only when non-empty. Absent on older servers. */
|
|
604
|
+
unnotifiedMentions?: UnnotifiedMention[];
|
|
425
605
|
}
|
|
426
606
|
|
|
427
607
|
/** Response of the presence heartbeat (ADR-0037 §6): a fresh agent-liveness
|
package/src/types/api-factory.ts
CHANGED
|
@@ -95,6 +95,11 @@ export interface RegisterWorkflowInput {
|
|
|
95
95
|
* server skips the /factory mount for its runs (#13). See
|
|
96
96
|
* `WorkflowMetadata.environmentBuild`. */
|
|
97
97
|
environmentBuild?: boolean;
|
|
98
|
+
/** Drive requirement declared via `defineWorkflow({ factoryDrive })`.
|
|
99
|
+
* Omitted ⇒ `"required"` on the server (a failed /factory mount fails
|
|
100
|
+
* the run); `"none"` = explicit no-drive opt-out. See
|
|
101
|
+
* `WorkflowMetadata.factoryDrive`. */
|
|
102
|
+
factoryDrive?: "required" | "none";
|
|
98
103
|
/** Factory slug. Defaults to `"default"`. */
|
|
99
104
|
factorySlug?: string;
|
|
100
105
|
}
|
|
@@ -194,6 +199,33 @@ export interface FactoryFileWriteResult {
|
|
|
194
199
|
created: boolean;
|
|
195
200
|
}
|
|
196
201
|
|
|
202
|
+
// ── User drive mounts (`agentc files mount` on a human's own machine) ────────
|
|
203
|
+
|
|
204
|
+
/** A minted local-mount grant: the user branch, the signed gateway token, and
|
|
205
|
+
* where to dial. The backing `conversationId` is the mount's review surface
|
|
206
|
+
* (its branch changes list + merge ride the session-changes routes). */
|
|
207
|
+
export interface DriveMountSession {
|
|
208
|
+
conversationId: string;
|
|
209
|
+
branch: string;
|
|
210
|
+
/** Signed gateway mount token (user principal, exclusive mode). Treat as a
|
|
211
|
+
* secret: write it to a 0600 token file, never argv/env of children. */
|
|
212
|
+
token: string;
|
|
213
|
+
gatewayWsUrl: string;
|
|
214
|
+
/** Token expiry, unix seconds — re-mint (same `conversationId`) before it. */
|
|
215
|
+
expiresAtS: number;
|
|
216
|
+
diskId: string;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export interface CreateDriveMountSessionInput {
|
|
220
|
+
/** Re-mint for an existing mount session (remount / token refresh). */
|
|
221
|
+
conversationId?: string;
|
|
222
|
+
/** The mounting machine's hostname — carried in the mount's title. */
|
|
223
|
+
host?: string;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// A mount's change set + merge ride the existing session-branch proposal
|
|
227
|
+
// types in `api-conversations.ts` (`SessionChangeSet`, `SessionMergeReport`).
|
|
228
|
+
|
|
197
229
|
/** A factory: a project-level grouping of workflows inside a team. */
|
|
198
230
|
export interface FactoryRow {
|
|
199
231
|
id: string;
|
|
@@ -281,6 +313,27 @@ export interface ApiKey {
|
|
|
281
313
|
* shown once at creation, never retrievable again. */
|
|
282
314
|
export interface ApiKeyCreated extends ApiKey { key: string }
|
|
283
315
|
|
|
316
|
+
/** Options for `GET /api-keys` — the list is bounded and keyset-paginated
|
|
317
|
+
* (every run step / session boot mints a key row, so "all keys ever" is
|
|
318
|
+
* unbounded by construction). */
|
|
319
|
+
export interface ListApiKeysOptions {
|
|
320
|
+
/** Page size, 1–200 (server default 100). */
|
|
321
|
+
limit?: number;
|
|
322
|
+
/** `active` = unrevoked + unexpired only; `all` (default) includes
|
|
323
|
+
* revoked/expired history. */
|
|
324
|
+
status?: "active" | "all";
|
|
325
|
+
/** Opaque cursor from a previous page's `nextCursor`. */
|
|
326
|
+
cursor?: string;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** One page of `GET /api-keys`. */
|
|
330
|
+
export interface ApiKeyPage {
|
|
331
|
+
data: ApiKey[];
|
|
332
|
+
hasMore: boolean;
|
|
333
|
+
/** Pass as `cursor` to fetch the next page; null on the last page. */
|
|
334
|
+
nextCursor: string | null;
|
|
335
|
+
}
|
|
336
|
+
|
|
284
337
|
/** Single rollup row from `GET /api/v1/usage`. */
|
|
285
338
|
export interface UsageRollupRow {
|
|
286
339
|
eventType: string;
|
|
@@ -301,7 +354,10 @@ export interface UsageResponse {
|
|
|
301
354
|
// ── GitHub-linked drive directories (ADR-0030 P1) ───────────────────────────
|
|
302
355
|
|
|
303
356
|
/** One drive-directory ⇄ GitHub-repo link (`/factories/:slug/repo-links`).
|
|
304
|
-
*
|
|
357
|
+
* Multi-branch model: ONE link row per (repo, branch); sibling branches
|
|
358
|
+
* of a repo occupy sibling `base@<branch>` placements. The webhook secret
|
|
359
|
+
* ref is operator plumbing and never on the wire — `webhookConfigured` /
|
|
360
|
+
* `webhookVerifiedAt` carry the honest connect status instead. */
|
|
305
361
|
export interface DriveRepoLink {
|
|
306
362
|
id: string;
|
|
307
363
|
factoryId: string;
|
|
@@ -311,26 +367,52 @@ export interface DriveRepoLink {
|
|
|
311
367
|
provider: string;
|
|
312
368
|
/** "org/repo". */
|
|
313
369
|
repoFullName: string;
|
|
314
|
-
/** The GitHub branch
|
|
370
|
+
/** The GitHub branch this link's placement syncs with. */
|
|
315
371
|
trackedBranch: string;
|
|
316
372
|
connectorGrantId: string | null;
|
|
317
373
|
/** v1 links are always 'write' (the round trip is the feature). */
|
|
318
374
|
access: "read" | "write";
|
|
375
|
+
/** A webhook secret exists for this link (registration done). NOT proof
|
|
376
|
+
* of delivery — that is `webhookVerifiedAt`. */
|
|
377
|
+
webhookConfigured: boolean;
|
|
378
|
+
/** When a signed GitHub delivery last PROVED the webhook delivers; null
|
|
379
|
+
* = unproven (the link syncs by poll). */
|
|
380
|
+
webhookVerifiedAt: string | null;
|
|
381
|
+
/** Two-way sync: drive edits under the prefix push back to the tracked
|
|
382
|
+
* branch. ON by default for new links. */
|
|
383
|
+
pushOutEnabled: boolean;
|
|
384
|
+
/** Follow-all mode (stamped identically on every sibling link of one
|
|
385
|
+
* repo): pushes to NEW branches auto-link them; GitHub branch deletes
|
|
386
|
+
* auto-unlink. False = explicit selection. */
|
|
387
|
+
followAllBranches: boolean;
|
|
388
|
+
/** Branches follow-all could NOT link (with the reason) — stamped on
|
|
389
|
+
* the repo's PRIMARY link row only; empty everywhere else. */
|
|
390
|
+
autoLinkSkips: Array<{ branch: string; reason: string; at: string }>;
|
|
319
391
|
lastSyncedGitSha: string | null;
|
|
320
392
|
lastSyncedAcgCommit: string | null;
|
|
321
|
-
syncState: "idle" | "syncing" | "diverged" | "reauth_required";
|
|
322
|
-
|
|
393
|
+
syncState: "idle" | "syncing" | "pushing" | "error" | "conflict" | "diverged" | "reauth_required";
|
|
394
|
+
/** Human-readable detail when syncState is 'error' or 'conflict'. */
|
|
395
|
+
syncError: string | null;
|
|
323
396
|
createdBy: string | null;
|
|
324
397
|
lastSyncedAt: string | null;
|
|
325
398
|
createdAt: string;
|
|
326
399
|
}
|
|
327
400
|
|
|
328
401
|
/** Input for `POST /factories/:slug/repo-links`. Requires the drive to be
|
|
329
|
-
* graph-authoritative (ADR-0058 Phase 4 promoted) — 409 otherwise.
|
|
402
|
+
* graph-authoritative (ADR-0058 Phase 4 promoted) — 409 otherwise.
|
|
403
|
+
* Exactly ONE of `followAllBranches` / `trackedBranches`:
|
|
404
|
+
* `followAllBranches: true` (the default connect mode) links EVERY
|
|
405
|
+
* branch — default branch primary at the plain `dirPrefix` — and keeps
|
|
406
|
+
* following live (new branches auto-link on push, deleted branches
|
|
407
|
+
* auto-unlink; capped at 100 branches). Explicit `trackedBranches`:
|
|
408
|
+
* index 0 is the PRIMARY and keeps the plain placement; every additional
|
|
409
|
+
* branch lands at the sibling `dirPrefix@<sanitized-branch>` placement. */
|
|
330
410
|
export interface CreateDriveRepoLinkInput {
|
|
331
411
|
dirPrefix: string;
|
|
332
412
|
repoFullName: string;
|
|
333
|
-
|
|
413
|
+
trackedBranches?: string[];
|
|
414
|
+
followAllBranches?: boolean;
|
|
334
415
|
connectorGrantId: string;
|
|
335
|
-
|
|
416
|
+
/** Two-way sync ("push-out") — defaults to TRUE for new links. */
|
|
417
|
+
pushOut?: boolean;
|
|
336
418
|
}
|