@agent-compose/sdk 0.8.1 → 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/client.d.ts +29 -11
- package/dist/index.d.ts +2 -2
- package/dist/index.js +267 -85
- package/dist/runtimes/_cli-agent.d.ts +25 -0
- package/dist/runtimes/codex.buildcommand.test.d.ts +9 -0
- package/dist/runtimes/openai-desktop.js +259 -85
- package/dist/sandbox/sizes.d.ts +120 -30
- package/dist/sandbox.d.ts +1 -1
- package/dist/types/api-conversations.d.ts +167 -0
- package/dist/types/api-factory.d.ts +59 -7
- package/dist/types/api-runs.d.ts +2 -1
- package/dist/types/workflow-metadata.d.ts +6 -5
- package/package.json +1 -1
- package/src/agent/agent-context.ts +28 -17
- package/src/client.ts +56 -20
- package/src/index.ts +11 -6
- package/src/runtimes/_cli-agent.ts +293 -56
- package/src/runtimes/codex.ts +8 -2
- 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 +155 -0
- package/src/types/api-factory.ts +57 -7
- package/src/types/api-runs.ts +2 -1
- package/src/types/workflow-metadata.ts +6 -5
|
@@ -101,6 +101,9 @@ export interface ConversationMessageRow {
|
|
|
101
101
|
reactions: Record<string, string[]>;
|
|
102
102
|
replyCount: number;
|
|
103
103
|
lastReplyAt: string | null;
|
|
104
|
+
/** When the author last edited the content; null/absent = never edited
|
|
105
|
+
* (absent on older servers). */
|
|
106
|
+
editedAt?: string | null;
|
|
104
107
|
createdAt: string;
|
|
105
108
|
/** Thread-root facepile (≤3) — present only on roots with replies. */
|
|
106
109
|
replyAuthors?: Array<{
|
|
@@ -172,6 +175,16 @@ export interface ConversationDetail {
|
|
|
172
175
|
* affordances only; the server remains the authority on every action.
|
|
173
176
|
* Public channels report implicit `write` for non-member teammates. */
|
|
174
177
|
viewerRole: ConversationMemberRole;
|
|
178
|
+
/** Provenance when `viewerRole` is DERIVED rather than held: the project
|
|
179
|
+
* (ADR-0045 read floor) or attached channel (ADR-0057) the viewer
|
|
180
|
+
* follows this conversation through. Null/absent for real members —
|
|
181
|
+
* clients use it to explain read-only honestly ("You follow this
|
|
182
|
+
* session through <project>…"). Absent on older servers. */
|
|
183
|
+
viewerRoleVia?: {
|
|
184
|
+
kind: "project" | "channel";
|
|
185
|
+
id: string;
|
|
186
|
+
name: string | null;
|
|
187
|
+
} | null;
|
|
175
188
|
/** SSE replay watermark: the conversation's highest durable stream-event
|
|
176
189
|
* id at hydrate time — everything at or below it is already folded into
|
|
177
190
|
* `messages`. Seed the stream's first `Last-Event-ID` from it instead of
|
|
@@ -257,6 +270,15 @@ export interface CreateCloudSessionInput {
|
|
|
257
270
|
contentB64: string;
|
|
258
271
|
mode: "write" | "append";
|
|
259
272
|
}>;
|
|
273
|
+
/** DIFF REVIEW spawn: the conversation whose proposed changes this
|
|
274
|
+
* session is born to review. Server-resolved: on a graph-plane drive
|
|
275
|
+
* the new session's branch is minted as a FORK of the reviewed branch
|
|
276
|
+
* (its working dir IS the proposal, `.review/` comparison materials
|
|
277
|
+
* included); on the legacy plane the spawn degrades to an ordinary
|
|
278
|
+
* session. Requires a human caller and a chat session on the reviewed
|
|
279
|
+
* session's factory; 409 `review_of_review` when the target is itself
|
|
280
|
+
* a review session. */
|
|
281
|
+
reviewOfConversationId?: string;
|
|
260
282
|
}
|
|
261
283
|
export interface CloudSessionCreated {
|
|
262
284
|
conversation: ConversationRow;
|
|
@@ -326,6 +348,14 @@ export interface SessionForked {
|
|
|
326
348
|
/** The new child conversation to switch to. */
|
|
327
349
|
conversationId: string;
|
|
328
350
|
}
|
|
351
|
+
/** Result of holding a session's background-work busy lease
|
|
352
|
+
* (`POST /conversations/:id/background-work`). While the lease is live the
|
|
353
|
+
* between-turns park/suspend leaves the session's VM running; it lapses on
|
|
354
|
+
* its own — re-hold to extend. */
|
|
355
|
+
export interface BackgroundWorkHeld {
|
|
356
|
+
/** ISO timestamp the lease now runs to. */
|
|
357
|
+
leaseUntil: string;
|
|
358
|
+
}
|
|
329
359
|
/** One changed file on a session's drive branch relative to `main`. */
|
|
330
360
|
export interface SessionFileChange {
|
|
331
361
|
/** Factory-relative path. */
|
|
@@ -357,6 +387,131 @@ export interface SessionChangeSet {
|
|
|
357
387
|
truncated: boolean;
|
|
358
388
|
/** Open `factory_file_conflicts` rows from this session's prior merges. */
|
|
359
389
|
openConflicts: number;
|
|
390
|
+
/** MERGE GATE (merge-gate spec, additive — absent on older servers):
|
|
391
|
+
* null = ungated. Present: whether THIS caller's merge lands directly
|
|
392
|
+
* (`canMerge` — an owner or named approver) or stages a kind='merge'
|
|
393
|
+
* approval instead (`mergeSessionChanges` answers 202
|
|
394
|
+
* `SessionMergeGated`), plus the resolved approver set. */
|
|
395
|
+
mergeGate?: {
|
|
396
|
+
enabled: true;
|
|
397
|
+
canMerge: boolean;
|
|
398
|
+
approvers: Array<{
|
|
399
|
+
userId: string;
|
|
400
|
+
label: string | null;
|
|
401
|
+
}>;
|
|
402
|
+
} | null;
|
|
403
|
+
/** The OPEN kind='merge' approval already waiting on this session's
|
|
404
|
+
* branch, if any (additive — absent on older servers). */
|
|
405
|
+
pendingApprovalId?: string | null;
|
|
406
|
+
/** DIFF REVIEW (additive — absent on older servers): notes written back
|
|
407
|
+
* by a spawned review session, whether they predate the served diff
|
|
408
|
+
* (`reviewStale` — the changed-file signature no longer matches), the
|
|
409
|
+
* bound review session's conversation + whether it is still live, and
|
|
410
|
+
* whether the deployment offers reviews at all (`reviewEnabled`; false
|
|
411
|
+
* hides the affordance). Reviews are USER-TRIGGERED only — binding
|
|
412
|
+
* rides `POST /conversations/:id/changes/review`, and notes arrive
|
|
413
|
+
* through the review session's own gated write-back. */
|
|
414
|
+
reviewNotes?: string | null;
|
|
415
|
+
reviewStale?: boolean;
|
|
416
|
+
reviewEnabled?: boolean;
|
|
417
|
+
reviewSessionConversationId?: string | null;
|
|
418
|
+
reviewSessionActive?: boolean;
|
|
419
|
+
/** STRUCTURED review suggestions beside the notes (additive — absent on
|
|
420
|
+
* older servers): individually actionable {id, path, title, rationale,
|
|
421
|
+
* patch} entries the review session wrote back, status-stamped
|
|
422
|
+
* `"proposed"` by the server (`"accepted"`/`"rejected"` are reserved for
|
|
423
|
+
* the human decision pass). */
|
|
424
|
+
reviewSuggestions?: ReviewSuggestion[];
|
|
425
|
+
/** THE REVIEWER MARKER (additive — absent on older servers): non-null ⇒
|
|
426
|
+
* THIS session was born to review that conversation's diff. Its own
|
|
427
|
+
* branch is a fork of the reviewed branch and can never merge
|
|
428
|
+
* (`mergeable` false, merge 409s `fork_branch_unmergeable`), and it is
|
|
429
|
+
* refused as a review target (409 `review_of_review`). */
|
|
430
|
+
reviewOfConversationId?: string | null;
|
|
431
|
+
}
|
|
432
|
+
/** One STRUCTURED, individually actionable suggestion a review session
|
|
433
|
+
* wrote back beside its notes (`SessionChangeSet.reviewSuggestions`). */
|
|
434
|
+
export interface ReviewSuggestion {
|
|
435
|
+
/** Reviewer-minted stable id — survives full-replace re-posts. */
|
|
436
|
+
id: string;
|
|
437
|
+
/** The changed file the suggestion targets (always in the change set —
|
|
438
|
+
* the writeback refuses paths outside it). */
|
|
439
|
+
path: string;
|
|
440
|
+
title: string;
|
|
441
|
+
rationale: string;
|
|
442
|
+
/** Unified-diff hunk targeting the PROPOSED side of `path`. */
|
|
443
|
+
patch: string;
|
|
444
|
+
/** Server-stamped `"proposed"` at writeback; the HUMAN decision routes
|
|
445
|
+
* (`POST …/changes/suggestions/:suggestionId/decide` / `…/decide-all`)
|
|
446
|
+
* are the only writers of the rest — `"accepted"` (the patch landed on
|
|
447
|
+
* the session branch), `"rejected"`, or `"stale"` (an accept found the
|
|
448
|
+
* file drifted since review; nothing was written). */
|
|
449
|
+
status: "proposed" | "accepted" | "rejected" | "stale";
|
|
450
|
+
}
|
|
451
|
+
/** `POST /conversations/:id/changes/suggestions/:suggestionId/decide` —
|
|
452
|
+
* the post-call truth for that suggestion (an accept that found drift
|
|
453
|
+
* answers `"stale"`; repeats on a settled row are no-ops). */
|
|
454
|
+
export interface ReviewSuggestionDecision {
|
|
455
|
+
object: "review_suggestion_decision";
|
|
456
|
+
conversationId: string;
|
|
457
|
+
id: string;
|
|
458
|
+
status: ReviewSuggestion["status"];
|
|
459
|
+
}
|
|
460
|
+
/** `POST /conversations/:id/changes/suggestions/decide-all` — every stored
|
|
461
|
+
* suggestion's post-call status (only `"proposed"` rows flip). */
|
|
462
|
+
export interface ReviewSuggestionDecisions {
|
|
463
|
+
object: "review_suggestion_decisions";
|
|
464
|
+
conversationId: string;
|
|
465
|
+
results: Array<{
|
|
466
|
+
id: string;
|
|
467
|
+
status: ReviewSuggestion["status"];
|
|
468
|
+
}>;
|
|
469
|
+
}
|
|
470
|
+
/** `GET /conversations/:id/changes/stats` — the changes chip's aggregate
|
|
471
|
+
* +added/−removed line counts, cached server-side per (base, theirsHead).
|
|
472
|
+
* `unavailable` = no cheap head anchors (legacy plane, unbranched) — the
|
|
473
|
+
* chip degrades to its file count, never fake zeros. */
|
|
474
|
+
export type SessionChangeStats = {
|
|
475
|
+
object: "session_change_stats";
|
|
476
|
+
conversationId: string;
|
|
477
|
+
state: "unavailable";
|
|
478
|
+
} | {
|
|
479
|
+
object: "session_change_stats";
|
|
480
|
+
conversationId: string;
|
|
481
|
+
state: "ok";
|
|
482
|
+
additions: number;
|
|
483
|
+
deletions: number;
|
|
484
|
+
files: number;
|
|
485
|
+
/** True when the count is partial (list truncated / file cap). */
|
|
486
|
+
truncated: boolean;
|
|
487
|
+
base: string;
|
|
488
|
+
theirsHead: string;
|
|
489
|
+
};
|
|
490
|
+
/** `POST /conversations/:id/changes/review` — binds a just-spawned review
|
|
491
|
+
* session to the reviewed session and stamps the diff signature the
|
|
492
|
+
* review covers. */
|
|
493
|
+
export interface SessionDiffReviewBound {
|
|
494
|
+
object: "session_diff_review";
|
|
495
|
+
conversationId: string;
|
|
496
|
+
reviewSessionConversationId: string;
|
|
497
|
+
reviewDiffSignature: string;
|
|
498
|
+
}
|
|
499
|
+
/** 202 from `POST /conversations/:id/changes/merge` on a merge-GATED
|
|
500
|
+
* session when the caller is not an approver: nothing merged — the ask
|
|
501
|
+
* froze into (`approval_required`) or converged on (`approval_pending`) a
|
|
502
|
+
* kind='merge' approval routed to the approvers. */
|
|
503
|
+
export interface SessionMergeGated {
|
|
504
|
+
object: "session_merge_gated";
|
|
505
|
+
conversationId: string;
|
|
506
|
+
code: "approval_required" | "approval_pending";
|
|
507
|
+
approvalId: string;
|
|
508
|
+
branch?: string;
|
|
509
|
+
changeCount?: number;
|
|
510
|
+
openConflicts?: number;
|
|
511
|
+
approvers: Array<{
|
|
512
|
+
userId: string;
|
|
513
|
+
label: string | null;
|
|
514
|
+
}>;
|
|
360
515
|
}
|
|
361
516
|
/** Per-file accounting of one session branch merge — the merge core's
|
|
362
517
|
* honest report, returned verbatim. */
|
|
@@ -425,6 +580,14 @@ export interface SendConversationMessageInput {
|
|
|
425
580
|
* streaming; `queued` = a turn is already in flight — the message is owed
|
|
426
581
|
* work and a coalesced follow-up turn will answer it. NOT an error. */
|
|
427
582
|
export type ConversationTurnState = "none" | "started" | "queued";
|
|
583
|
+
/** One @-mentioned user the server DROPPED as a non-member — the mention
|
|
584
|
+
* ping went to nobody, and the response says so instead of staying
|
|
585
|
+
* silent. `name` is the label the sender's own text carried for the
|
|
586
|
+
* mention (picked in the typeahead — no new information). */
|
|
587
|
+
export interface UnnotifiedMention {
|
|
588
|
+
userId: string;
|
|
589
|
+
name: string;
|
|
590
|
+
}
|
|
428
591
|
export interface SendConversationMessageResult {
|
|
429
592
|
/** The persisted user message's id. */
|
|
430
593
|
messageId: string;
|
|
@@ -437,6 +600,10 @@ export interface SendConversationMessageResult {
|
|
|
437
600
|
* each runs its own turn in its own conversation. Absent on older
|
|
438
601
|
* servers and non-channel sends. */
|
|
439
602
|
relayedSessionIds?: string[];
|
|
603
|
+
/** Mentioned users whose ping was dropped by the member filter (a
|
|
604
|
+
* private channel pings members only; a DM pings only the pair) —
|
|
605
|
+
* present only when non-empty. Absent on older servers. */
|
|
606
|
+
unnotifiedMentions?: UnnotifiedMention[];
|
|
440
607
|
}
|
|
441
608
|
/** Response of the presence heartbeat (ADR-0037 §6): a fresh agent-liveness
|
|
442
609
|
* snapshot + the roster TTL. The attach roster itself is NOT returned —
|
|
@@ -284,6 +284,25 @@ export interface ApiKey {
|
|
|
284
284
|
export interface ApiKeyCreated extends ApiKey {
|
|
285
285
|
key: string;
|
|
286
286
|
}
|
|
287
|
+
/** Options for `GET /api-keys` — the list is bounded and keyset-paginated
|
|
288
|
+
* (every run step / session boot mints a key row, so "all keys ever" is
|
|
289
|
+
* unbounded by construction). */
|
|
290
|
+
export interface ListApiKeysOptions {
|
|
291
|
+
/** Page size, 1–200 (server default 100). */
|
|
292
|
+
limit?: number;
|
|
293
|
+
/** `active` = unrevoked + unexpired only; `all` (default) includes
|
|
294
|
+
* revoked/expired history. */
|
|
295
|
+
status?: "active" | "all";
|
|
296
|
+
/** Opaque cursor from a previous page's `nextCursor`. */
|
|
297
|
+
cursor?: string;
|
|
298
|
+
}
|
|
299
|
+
/** One page of `GET /api-keys`. */
|
|
300
|
+
export interface ApiKeyPage {
|
|
301
|
+
data: ApiKey[];
|
|
302
|
+
hasMore: boolean;
|
|
303
|
+
/** Pass as `cursor` to fetch the next page; null on the last page. */
|
|
304
|
+
nextCursor: string | null;
|
|
305
|
+
}
|
|
287
306
|
/** Single rollup row from `GET /api/v1/usage`. */
|
|
288
307
|
export interface UsageRollupRow {
|
|
289
308
|
eventType: string;
|
|
@@ -300,7 +319,10 @@ export interface UsageResponse {
|
|
|
300
319
|
to: string | null;
|
|
301
320
|
}
|
|
302
321
|
/** One drive-directory ⇄ GitHub-repo link (`/factories/:slug/repo-links`).
|
|
303
|
-
*
|
|
322
|
+
* Multi-branch model: ONE link row per (repo, branch); sibling branches
|
|
323
|
+
* of a repo occupy sibling `base@<branch>` placements. The webhook secret
|
|
324
|
+
* ref is operator plumbing and never on the wire — `webhookConfigured` /
|
|
325
|
+
* `webhookVerifiedAt` carry the honest connect status instead. */
|
|
304
326
|
export interface DriveRepoLink {
|
|
305
327
|
id: string;
|
|
306
328
|
factoryId: string;
|
|
@@ -310,25 +332,55 @@ export interface DriveRepoLink {
|
|
|
310
332
|
provider: string;
|
|
311
333
|
/** "org/repo". */
|
|
312
334
|
repoFullName: string;
|
|
313
|
-
/** The GitHub branch
|
|
335
|
+
/** The GitHub branch this link's placement syncs with. */
|
|
314
336
|
trackedBranch: string;
|
|
315
337
|
connectorGrantId: string | null;
|
|
316
338
|
/** v1 links are always 'write' (the round trip is the feature). */
|
|
317
339
|
access: "read" | "write";
|
|
340
|
+
/** A webhook secret exists for this link (registration done). NOT proof
|
|
341
|
+
* of delivery — that is `webhookVerifiedAt`. */
|
|
342
|
+
webhookConfigured: boolean;
|
|
343
|
+
/** When a signed GitHub delivery last PROVED the webhook delivers; null
|
|
344
|
+
* = unproven (the link syncs by poll). */
|
|
345
|
+
webhookVerifiedAt: string | null;
|
|
346
|
+
/** Two-way sync: drive edits under the prefix push back to the tracked
|
|
347
|
+
* branch. ON by default for new links. */
|
|
348
|
+
pushOutEnabled: boolean;
|
|
349
|
+
/** Follow-all mode (stamped identically on every sibling link of one
|
|
350
|
+
* repo): pushes to NEW branches auto-link them; GitHub branch deletes
|
|
351
|
+
* auto-unlink. False = explicit selection. */
|
|
352
|
+
followAllBranches: boolean;
|
|
353
|
+
/** Branches follow-all could NOT link (with the reason) — stamped on
|
|
354
|
+
* the repo's PRIMARY link row only; empty everywhere else. */
|
|
355
|
+
autoLinkSkips: Array<{
|
|
356
|
+
branch: string;
|
|
357
|
+
reason: string;
|
|
358
|
+
at: string;
|
|
359
|
+
}>;
|
|
318
360
|
lastSyncedGitSha: string | null;
|
|
319
361
|
lastSyncedAcgCommit: string | null;
|
|
320
|
-
syncState: "idle" | "syncing" | "diverged" | "reauth_required";
|
|
321
|
-
|
|
362
|
+
syncState: "idle" | "syncing" | "pushing" | "error" | "conflict" | "diverged" | "reauth_required";
|
|
363
|
+
/** Human-readable detail when syncState is 'error' or 'conflict'. */
|
|
364
|
+
syncError: string | null;
|
|
322
365
|
createdBy: string | null;
|
|
323
366
|
lastSyncedAt: string | null;
|
|
324
367
|
createdAt: string;
|
|
325
368
|
}
|
|
326
369
|
/** Input for `POST /factories/:slug/repo-links`. Requires the drive to be
|
|
327
|
-
* graph-authoritative (ADR-0058 Phase 4 promoted) — 409 otherwise.
|
|
370
|
+
* graph-authoritative (ADR-0058 Phase 4 promoted) — 409 otherwise.
|
|
371
|
+
* Exactly ONE of `followAllBranches` / `trackedBranches`:
|
|
372
|
+
* `followAllBranches: true` (the default connect mode) links EVERY
|
|
373
|
+
* branch — default branch primary at the plain `dirPrefix` — and keeps
|
|
374
|
+
* following live (new branches auto-link on push, deleted branches
|
|
375
|
+
* auto-unlink; capped at 100 branches). Explicit `trackedBranches`:
|
|
376
|
+
* index 0 is the PRIMARY and keeps the plain placement; every additional
|
|
377
|
+
* branch lands at the sibling `dirPrefix@<sanitized-branch>` placement. */
|
|
328
378
|
export interface CreateDriveRepoLinkInput {
|
|
329
379
|
dirPrefix: string;
|
|
330
380
|
repoFullName: string;
|
|
331
|
-
|
|
381
|
+
trackedBranches?: string[];
|
|
382
|
+
followAllBranches?: boolean;
|
|
332
383
|
connectorGrantId: string;
|
|
333
|
-
|
|
384
|
+
/** Two-way sync ("push-out") — defaults to TRUE for new links. */
|
|
385
|
+
pushOut?: boolean;
|
|
334
386
|
}
|
package/dist/types/api-runs.d.ts
CHANGED
|
@@ -283,7 +283,8 @@ export interface ListRunsOptions {
|
|
|
283
283
|
factorySlug?: string;
|
|
284
284
|
/** Substring match on the registered workflow name (`metadata._workflow`). */
|
|
285
285
|
workflow?: string;
|
|
286
|
-
/** Substring match across title / task title / branch
|
|
286
|
+
/** Substring match across title / task title / branch — or an exact run
|
|
287
|
+
* id (full UUID). */
|
|
287
288
|
search?: string;
|
|
288
289
|
outcome?: string;
|
|
289
290
|
sort?: "newest" | "oldest" | "fastest" | "slowest";
|
|
@@ -146,11 +146,12 @@ export interface InvokePolicy {
|
|
|
146
146
|
* today; kept as its own object so finer controls (disk, gpu, …) can be
|
|
147
147
|
* added later without reshaping `WorkflowMetadata`. */
|
|
148
148
|
export interface SandboxResources {
|
|
149
|
-
/** Machine hardware SKU
|
|
150
|
-
*
|
|
151
|
-
* provider specs at create time
|
|
152
|
-
*
|
|
153
|
-
*
|
|
149
|
+
/** Machine hardware SKU. The vocabulary is `SANDBOX_SIZES` in
|
|
150
|
+
* `sandbox/sizes.ts` — the single source; do not restate it here or
|
|
151
|
+
* anywhere else. Maps to provider specs at create time: Vercel takes the
|
|
152
|
+
* vCPU count and allocates RAM at 2048 MB/vCPU; E2B has no create-time
|
|
153
|
+
* cpu/mem knob at all, so the size selects a PRE-BUILT per-size template
|
|
154
|
+
* (`E2B_TEMPLATE_SIZES`). Omit → `DEFAULT_SANDBOX_SIZE`. */
|
|
154
155
|
size?: SandboxSize;
|
|
155
156
|
/** Sandbox provider this workflow's runs execute on — `"vercel"` or
|
|
156
157
|
* `"e2b"`. Optional and additive: omit and the run resolves to the
|
package/package.json
CHANGED
|
@@ -173,38 +173,49 @@ there is nothing to type, paste, or screenshot a credential from.
|
|
|
173
173
|
|
|
174
174
|
## Recording a demo — the desktop, captured to a video the human can play
|
|
175
175
|
|
|
176
|
-
"Record a demo of you using X" is a normal ask, and this machine does it
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
to any teammate watching; a recording is the durable, replayable artifact.
|
|
181
|
-
Both modes exist; say so when it matters.)
|
|
176
|
+
"Record a demo of you using X" is a normal ask, and this machine does it.
|
|
177
|
+
(For a LIVE view no recording is needed — the session header's **Desktop**
|
|
178
|
+
button already streams this display to any teammate watching; a recording is
|
|
179
|
+
the durable, replayable artifact. Both modes exist; say so when it matters.)
|
|
182
180
|
|
|
183
|
-
**
|
|
181
|
+
**Use \`ac-record\` — the platform recorder is already on PATH** (cloud
|
|
182
|
+
sessions; \`command -v ac-record\` to confirm on older machines):
|
|
184
183
|
|
|
185
|
-
|
|
184
|
+
ac-record start # begins capturing the desktop (display :0)
|
|
185
|
+
# ... drive the app with xdotool, screenshotting as you go ...
|
|
186
|
+
ac-record stop # finishes + saves to recordings/ in your workspace
|
|
187
|
+
ac-record status # one JSON line: {"recording":true,...}
|
|
188
|
+
|
|
189
|
+
It records the whole display (with desktop audio when the machine has a
|
|
190
|
+
PulseAudio monitor), enforces sane caps (5 min / 200 MB — start a fresh
|
|
191
|
+
recording per scene rather than one long take), keeps the file playable even
|
|
192
|
+
if the machine dies mid-take, and \`stop\` prints the saved path — the file
|
|
193
|
+
lands ON THE DRIVE in \`recordings/\`, visible in Files and playable in the
|
|
194
|
+
dashboard. A human watching the Desktop pane sees the recording indicator
|
|
195
|
+
while you record.
|
|
186
196
|
|
|
187
|
-
|
|
197
|
+
If \`ac-record\` is missing (older machine), record by hand.
|
|
198
|
+
**ffmpeg IS pre-installed** on platform images (\`command -v ffmpeg\`; only
|
|
199
|
+
if absent: \`sudo apt-get update -q && sudo apt-get install -y -q ffmpeg\`):
|
|
188
200
|
|
|
189
201
|
DISPLAY=:0 ffmpeg -f x11grab \\
|
|
190
202
|
-video_size "$(DISPLAY=:0 xdotool getdisplaygeometry | tr ' ' x)" \\
|
|
191
203
|
-framerate 10 -i :0 -c:v libvpx -b:v 1M -deadline realtime -cpu-used 8 \\
|
|
192
204
|
demo.webm &
|
|
193
205
|
FFMPEG_PID=$!
|
|
194
|
-
# ... drive the app with xdotool
|
|
206
|
+
# ... drive the app with xdotool ...
|
|
195
207
|
kill -INT "$FFMPEG_PID" && wait "$FFMPEG_PID"
|
|
196
208
|
|
|
197
|
-
The gotchas, each one earned:
|
|
209
|
+
The hand-rolled gotchas, each one earned:
|
|
198
210
|
- **Stop with SIGINT (\`kill -INT\`), never SIGKILL** — ffmpeg finalizes the
|
|
199
211
|
file on SIGINT; a hard kill truncates the encode mid-write.
|
|
200
|
-
- **Record WebM (matroska-family), not MP4** — mp4 writes its moov atom
|
|
201
|
-
END, so a killed or crashed encode leaves an UNPLAYABLE file; webm
|
|
202
|
-
playable up to the last written frame and plays natively in the
|
|
203
|
-
|
|
204
|
-
afterwards if you truly need it, never record straight to it.
|
|
212
|
+
- **Record WebM (matroska-family), not plain MP4** — mp4 writes its moov atom
|
|
213
|
+
at the END, so a killed or crashed encode leaves an UNPLAYABLE file; webm
|
|
214
|
+
stays playable up to the last written frame and plays natively in the
|
|
215
|
+
browser. (\`ac-record\` sidesteps this with fragmented mp4.)
|
|
205
216
|
- **\`-video_size\` must match the real screen** — x11grab does not default to
|
|
206
217
|
it; read the geometry from \`xdotool getdisplaygeometry\` as above.
|
|
207
|
-
- **10–
|
|
218
|
+
- **10–15 fps is right for a screen demo** — small files, legible UI motion;
|
|
208
219
|
this is not video production.
|
|
209
220
|
- **Write to the drive, not /tmp** — the recording must land in your working
|
|
210
221
|
directory to persist and show up in Files; a file in /tmp dies with the
|
package/src/client.ts
CHANGED
|
@@ -31,8 +31,8 @@ import type {
|
|
|
31
31
|
TeamMember, Mention, CreateMentionsInput,
|
|
32
32
|
ConversationsPage, SessionsPage, ConversationDetail, ConversationThread,
|
|
33
33
|
CreateCloudSessionInput, CloudSessionCreated,
|
|
34
|
-
SessionPreview, PreviewOpened, OpenPreviewInput, SessionForked,
|
|
35
|
-
SessionChangeSet, SessionMergeReport, SessionDiscardReport,
|
|
34
|
+
SessionPreview, PreviewOpened, OpenPreviewInput, SessionForked, BackgroundWorkHeld,
|
|
35
|
+
SessionChangeSet, SessionMergeGated, SessionMergeReport, SessionDiscardReport,
|
|
36
36
|
SendConversationMessageInput, SendConversationMessageResult,
|
|
37
37
|
ChannelSessionRow, ChannelSessionsResponse, SessionChannelMessagePosted,
|
|
38
38
|
ConversationPresenceSnapshot, StreamConversationOptions, AgentListRow,
|
|
@@ -52,7 +52,7 @@ import type {
|
|
|
52
52
|
FactoryRow, CreateFactoryInput, UpdateFactoryInput,
|
|
53
53
|
ScheduleRow, CreateScheduleInput,
|
|
54
54
|
SecretOptions, SetSecretResult, SecretListEntry,
|
|
55
|
-
CreateApiKeyInput, ApiKey, ApiKeyCreated, UsageResponse,
|
|
55
|
+
CreateApiKeyInput, ApiKey, ApiKeyCreated, ListApiKeysOptions, ApiKeyPage, UsageResponse,
|
|
56
56
|
DriveRepoLink, CreateDriveRepoLinkInput,
|
|
57
57
|
DriveMountSession, CreateDriveMountSessionInput,
|
|
58
58
|
} from "./types/api-factory.js";
|
|
@@ -82,11 +82,12 @@ export type {
|
|
|
82
82
|
ConversationMessagePart, ConversationRow, ConversationMessageRow,
|
|
83
83
|
ConversationsPage, SessionsPage, ConversationDetail,
|
|
84
84
|
CreateCloudSessionInput, CloudSessionCreated, ConversationThread,
|
|
85
|
-
SessionPreview, PreviewOpened, OpenPreviewInput, SessionForked,
|
|
86
|
-
SessionFileChange, SessionChangeSet,
|
|
87
|
-
|
|
85
|
+
SessionPreview, PreviewOpened, OpenPreviewInput, SessionForked, BackgroundWorkHeld,
|
|
86
|
+
SessionFileChange, SessionChangeSet, SessionChangeStats, SessionDiffReviewBound, ReviewSuggestion,
|
|
87
|
+
ReviewSuggestionDecision, ReviewSuggestionDecisions,
|
|
88
|
+
SessionMergeReportDetail, SessionMergeReport, SessionMergeGated, SessionDiscardReport,
|
|
88
89
|
ConversationPageContext, SendConversationMessageInput, ConversationTurnState,
|
|
89
|
-
SendConversationMessageResult, ConversationPresenceSnapshot, AgentListRow, StreamConversationOptions,
|
|
90
|
+
SendConversationMessageResult, UnnotifiedMention, ConversationPresenceSnapshot, AgentListRow, StreamConversationOptions,
|
|
90
91
|
ChannelSessionStatus, ChannelSessionRow, ChannelSessionsResponse, SessionChannelMessagePosted,
|
|
91
92
|
} from "./types/api-conversations.js";
|
|
92
93
|
export type {
|
|
@@ -108,7 +109,7 @@ export type {
|
|
|
108
109
|
FactoryRow, CreateFactoryInput, UpdateFactoryInput,
|
|
109
110
|
ScheduleRow, CreateScheduleInput,
|
|
110
111
|
SecretOptions, SetSecretResult, SecretListEntry,
|
|
111
|
-
CreateApiKeyInput, ApiKey, ApiKeyCreated,
|
|
112
|
+
CreateApiKeyInput, ApiKey, ApiKeyCreated, ListApiKeysOptions, ApiKeyPage,
|
|
112
113
|
UsageRollupRow, UsageResponse,
|
|
113
114
|
DriveRepoLink, CreateDriveRepoLinkInput,
|
|
114
115
|
DriveMountSession, CreateDriveMountSessionInput,
|
|
@@ -984,6 +985,28 @@ export class AgentComposeClient {
|
|
|
984
985
|
return body.closed;
|
|
985
986
|
}
|
|
986
987
|
|
|
988
|
+
/** Hold (or extend — the stamp is monotone) a cloud session's
|
|
989
|
+
* background-work busy lease: while it is live, the between-turns
|
|
990
|
+
* park/suspend leaves the session's VM running so work the turn left
|
|
991
|
+
* behind keeps executing. The lease lapses on its own — re-hold to
|
|
992
|
+
* extend past `minutes` (server-capped). Write-tier. */
|
|
993
|
+
holdBackgroundWork(conversationId: string, input?: { minutes?: number }): Promise<BackgroundWorkHeld> {
|
|
994
|
+
return this.fetch<BackgroundWorkHeld>(
|
|
995
|
+
`/api/v1/conversations/${encodeURIComponent(conversationId)}/background-work`,
|
|
996
|
+
{ method: "POST", body: { ...(input?.minutes !== undefined ? { minutes: input.minutes } : {}) } },
|
|
997
|
+
);
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/** Release the session's background-work lease (the work finished).
|
|
1001
|
+
* Idempotent — `released` is false when no lease was held. */
|
|
1002
|
+
async releaseBackgroundWork(conversationId: string): Promise<boolean> {
|
|
1003
|
+
const body = await this.fetch<{ released: boolean }>(
|
|
1004
|
+
`/api/v1/conversations/${encodeURIComponent(conversationId)}/background-work`,
|
|
1005
|
+
{ method: "DELETE" },
|
|
1006
|
+
);
|
|
1007
|
+
return body.released;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
987
1010
|
/** Fork a cloud session from HEAD: snapshot the VM + branch the drive + seed
|
|
988
1011
|
* a new conversation from the transcript so far, booting from both. Returns
|
|
989
1012
|
* the child conversation id to switch to. Write-tier; "Branch from here." */
|
|
@@ -1017,6 +1040,11 @@ export class AgentComposeClient {
|
|
|
1017
1040
|
* `main`. Write-tier + human caller. Pass `opts.branch` (the branch you
|
|
1018
1041
|
* reviewed) to fail 409 `branch_changed` if it moved since.
|
|
1019
1042
|
*
|
|
1043
|
+
* On a merge-GATED session (merge-gate spec) a non-approver's call does
|
|
1044
|
+
* NOT merge: it answers 202 `SessionMergeGated` — the ask froze into (or
|
|
1045
|
+
* converged on) a kind='merge' approval routed to the session's
|
|
1046
|
+
* approvers. Discriminate on `object`.
|
|
1047
|
+
*
|
|
1020
1048
|
* Failures: 403 `role_read_only` (read-only member) or a plain 403 for
|
|
1021
1049
|
* session toolbelt keys (agents cannot self-approve); 409 `turn_active`
|
|
1022
1050
|
* (a turn is running — retry when idle) | `branch_changed`; 400
|
|
@@ -1026,8 +1054,8 @@ export class AgentComposeClient {
|
|
|
1026
1054
|
mergeSessionChanges(
|
|
1027
1055
|
conversationId: string,
|
|
1028
1056
|
opts: { branch?: string } = {},
|
|
1029
|
-
): Promise<SessionMergeReport> {
|
|
1030
|
-
return this.fetch<SessionMergeReport>(
|
|
1057
|
+
): Promise<SessionMergeReport | SessionMergeGated> {
|
|
1058
|
+
return this.fetch<SessionMergeReport | SessionMergeGated>(
|
|
1031
1059
|
`/api/v1/conversations/${encodeURIComponent(conversationId)}/changes/merge`,
|
|
1032
1060
|
{ method: "POST", body: opts.branch ? { branch: opts.branch } : {} },
|
|
1033
1061
|
);
|
|
@@ -1608,17 +1636,18 @@ export class AgentComposeClient {
|
|
|
1608
1636
|
return body.links;
|
|
1609
1637
|
}
|
|
1610
1638
|
|
|
1611
|
-
/** Link a drive directory to a GitHub repo
|
|
1612
|
-
* scope)
|
|
1613
|
-
*
|
|
1639
|
+
/** Link a drive directory to a GitHub repo's tracked BRANCHES (`manage`
|
|
1640
|
+
* scope) — one link row per branch, created in one action. Requires the
|
|
1641
|
+
* drive to be graph-authoritative — 409 names the promotion
|
|
1642
|
+
* prerequisite otherwise. */
|
|
1614
1643
|
async createRepoLink(
|
|
1615
1644
|
input: CreateDriveRepoLinkInput, opts?: { factorySlug?: string },
|
|
1616
|
-
): Promise<DriveRepoLink> {
|
|
1645
|
+
): Promise<DriveRepoLink[]> {
|
|
1617
1646
|
const slug = opts?.factorySlug ?? DEFAULT_FACTORY;
|
|
1618
|
-
const body = await this.fetch<{
|
|
1647
|
+
const body = await this.fetch<{ links: DriveRepoLink[] }>(
|
|
1619
1648
|
`/api/v1/factories/${encodeURIComponent(slug)}/repo-links`,
|
|
1620
1649
|
{ method: "POST", body: input });
|
|
1621
|
-
return body.
|
|
1650
|
+
return body.links;
|
|
1622
1651
|
}
|
|
1623
1652
|
|
|
1624
1653
|
/** Unlink (§6.3: the prefix's files and history stay on the drive). */
|
|
@@ -1668,10 +1697,17 @@ export class AgentComposeClient {
|
|
|
1668
1697
|
}
|
|
1669
1698
|
|
|
1670
1699
|
/** List API keys on the caller's team (metadata only — plaintext keys are
|
|
1671
|
-
* never returned).
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1700
|
+
* never returned). Bounded + keyset-paginated: pass `cursor` from the
|
|
1701
|
+
* previous page's `nextCursor` to walk forward. */
|
|
1702
|
+
async listApiKeys(opts?: ListApiKeysOptions): Promise<ApiKeyPage> {
|
|
1703
|
+
const qs = new URLSearchParams();
|
|
1704
|
+
if (opts?.limit !== undefined) qs.set("limit", String(opts.limit));
|
|
1705
|
+
if (opts?.status !== undefined) qs.set("status", opts.status);
|
|
1706
|
+
if (opts?.cursor !== undefined) qs.set("cursor", opts.cursor);
|
|
1707
|
+
const suffix = qs.toString() ? `?${qs.toString()}` : "";
|
|
1708
|
+
const body = await this.fetch<{ object: "list"; data: ApiKey[]; has_more: boolean; next_cursor: string | null }>(
|
|
1709
|
+
`/api-keys${suffix}`);
|
|
1710
|
+
return { data: body.data, hasMore: body.has_more, nextCursor: body.next_cursor ?? null };
|
|
1675
1711
|
}
|
|
1676
1712
|
|
|
1677
1713
|
// ── Usage ──────────────────────────────────────────────────────────────────
|
package/src/index.ts
CHANGED
|
@@ -125,7 +125,7 @@ export type {
|
|
|
125
125
|
RunListEntry, ListRunsOptions, RunDetail,
|
|
126
126
|
FundingLane, RunFundingStamp, RunFundingUsageRow, RunFundingResponse,
|
|
127
127
|
RegisteredRuntime, RunState, RunStatus, FactoryRow, SnapshotListEntry, SnapshotListResponse,
|
|
128
|
-
ApiKey, ApiKeyCreated,
|
|
128
|
+
ApiKey, ApiKeyCreated, ListApiKeysOptions, ApiKeyPage,
|
|
129
129
|
UsageRollupRow, UsageResponse,
|
|
130
130
|
CancelRunResponse,
|
|
131
131
|
RequestAgentPauseOptions, RequestAgentPauseResponse,
|
|
@@ -135,13 +135,15 @@ export type {
|
|
|
135
135
|
ConversationMessagePart, ConversationRow, ConversationMessageRow,
|
|
136
136
|
ConversationsPage, ConversationDetail, ConversationThread,
|
|
137
137
|
ConversationPageContext, SendConversationMessageInput, SendConversationMessageResult,
|
|
138
|
+
UnnotifiedMention,
|
|
138
139
|
ConversationTurnState, ConversationPresenceSnapshot, StreamConversationOptions, AgentListRow,
|
|
139
140
|
CreateCloudSessionInput, CloudSessionCreated,
|
|
140
141
|
// Cloud-session developer surface (ADR-0052)
|
|
141
|
-
SessionPreview, PreviewOpened, OpenPreviewInput, SessionForked,
|
|
142
|
+
SessionPreview, PreviewOpened, OpenPreviewInput, SessionForked, BackgroundWorkHeld,
|
|
142
143
|
// Session branch proposals (ADR-0053)
|
|
143
|
-
SessionFileChange, SessionChangeSet,
|
|
144
|
-
|
|
144
|
+
SessionFileChange, SessionChangeSet, SessionChangeStats, SessionDiffReviewBound, ReviewSuggestion,
|
|
145
|
+
ReviewSuggestionDecision, ReviewSuggestionDecisions,
|
|
146
|
+
SessionMergeReportDetail, SessionMergeReport, SessionMergeGated, SessionDiscardReport,
|
|
145
147
|
// User drive mounts (`agentc files mount` on a human's own machine)
|
|
146
148
|
DriveMountSession, CreateDriveMountSessionInput,
|
|
147
149
|
// Channel-attached sessions (ADR-0057)
|
|
@@ -278,8 +280,11 @@ export { createSandbox, reconnectSandbox, killAllSandboxes, killSandboxById,
|
|
|
278
280
|
getSandboxQuotas, listOwnedSandboxes, deleteSandboxSnapshot, snapshotResolves,
|
|
279
281
|
makeSandboxProvider, makeDesktopSandboxProvider,
|
|
280
282
|
parseSseExecStream, AGENT_COMPOSE_TAG,
|
|
281
|
-
SANDBOX_VCPUS, DEFAULT_SANDBOX_SIZE,
|
|
282
|
-
|
|
283
|
+
SANDBOX_SIZES, SANDBOX_MACHINES, SANDBOX_VCPUS, DEFAULT_SANDBOX_SIZE, SESSION_DEFAULT_SANDBOX_SIZE,
|
|
284
|
+
E2B_TEMPLATE_SIZES, E2B_MAX_VCPUS, E2B_MAX_MEMORY_MB,
|
|
285
|
+
VERCEL_MEMORY_MB_PER_VCPU,
|
|
286
|
+
isE2bSupportedSize, isVercelSupportedSize, sandboxSizeLabel,
|
|
287
|
+
e2bMachineSpec, e2bBaseTemplate, e2bAgentEnvTemplate,
|
|
283
288
|
isPlatformE2bTemplateAlias,
|
|
284
289
|
// ADR-0038 "Computer" — the per-member persistent desktop machine template.
|
|
285
290
|
E2B_DEVBOX_TEMPLATE, E2B_DEVBOX_SPEC, E2B_DEVBOX_RECIPE_VERSION,
|