@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
package/src/client.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ConversationRow, CreateChatInput, ChannelIvyState, ProjectIvyConnection } from "./types/api-conversations.js";
|
|
1
2
|
/**
|
|
2
3
|
* AgentComposeClient — HTTP client for the agent-compose server API.
|
|
3
4
|
*
|
|
@@ -32,8 +33,9 @@ import type {
|
|
|
32
33
|
ConversationsPage, SessionsPage, ConversationDetail, ConversationThread,
|
|
33
34
|
CreateCloudSessionInput, CloudSessionCreated,
|
|
34
35
|
SessionPreview, PreviewOpened, OpenPreviewInput, SessionForked, BackgroundWorkHeld,
|
|
36
|
+
BackgroundWorkChildDecl, BackgroundWorkStatus, MachineUpsizeOutcome,
|
|
35
37
|
SessionChangeSet, SessionMergeGated, SessionMergeReport, SessionDiscardReport,
|
|
36
|
-
SessionChangePreflight, SessionRebaseReport,
|
|
38
|
+
SessionChangePreflight, SessionRebaseReport,
|
|
37
39
|
ReviewSuggestionInput, ReviewNotesPublished, ReviewGitCredential,
|
|
38
40
|
SendConversationMessageInput, SendConversationMessageResult,
|
|
39
41
|
ChannelSessionRow, ChannelSessionsResponse, SessionChannelMessagePosted,
|
|
@@ -53,13 +55,14 @@ import type {
|
|
|
53
55
|
import type {
|
|
54
56
|
RegisterResult, RegisterWorkflowInput, TemplateRow, TemplateDetail, ListTemplatesOptions,
|
|
55
57
|
SearchFactoryFilesOptions, FactoryFileSearchResult, FactoryFileWriteResult,
|
|
56
|
-
FactoryFileListRow, ListFactoryFilesOptions, FactoryFileListPage,
|
|
58
|
+
FactoryFileListRow, ListFactoryFilesOptions, FactoryFileListPage, FactoryFileConflict, FactoryFileConflictList, ProjectSecretMeta, ProjectSecretList,
|
|
57
59
|
FactoryRow, CreateFactoryInput, UpdateFactoryInput, FactoryPerfSummary,
|
|
58
60
|
ScheduleRow, CreateScheduleInput,
|
|
59
|
-
SecretOptions, SetSecretResult, SecretListEntry, SessionSecretEntry, SessionSecretInput, SessionSecretRequestCreated, SessionSecretRequestStatus,
|
|
61
|
+
SecretOptions, SetSecretResult, SecretListEntry, SessionSecretEntry, SessionSecretInput, SessionSecretRequestCreated, SessionSecretRequestStatus, SessionSecretRequestSummary, VaultRequestKind, VaultCatalogEntry,
|
|
60
62
|
CreateApiKeyInput, ApiKey, ApiKeyCreated, ListApiKeysOptions, ApiKeyPage, UsageResponse,
|
|
61
|
-
DriveRepoLink, CreateDriveRepoLinkInput,
|
|
63
|
+
DriveRepoLink, CreateDriveRepoLinkInput, CreateNativeRepoInput, CreateNativeRepoResult,
|
|
62
64
|
DriveMountSession, CreateDriveMountSessionInput,
|
|
65
|
+
ConnectorGrantSummary,
|
|
63
66
|
} from "./types/api-factory.js";
|
|
64
67
|
import type {
|
|
65
68
|
ComplianceSession, RequestComplianceSessionInput, ListComplianceSessionsOptions,
|
|
@@ -85,15 +88,17 @@ export type {
|
|
|
85
88
|
} from "./types/api-runs.js";
|
|
86
89
|
export type {
|
|
87
90
|
TeamMember, Mention, CreateMentionsInput,
|
|
91
|
+
CreateChatInput, ChannelIvyState, ProjectIvyConnection,
|
|
88
92
|
ConversationMessagePart, ConversationRow, ConversationMessageRow,
|
|
89
93
|
ConversationsPage, SessionsPage, ConversationDetail,
|
|
90
94
|
CreateCloudSessionInput, CloudSessionCreated, ConversationThread,
|
|
91
95
|
SessionPreview, PreviewOpened, OpenPreviewInput, SessionForked, BackgroundWorkHeld,
|
|
96
|
+
BackgroundWorkChildDecl, BackgroundWorkStatus, MachineUpsizeOutcome,
|
|
92
97
|
SessionFileChange, SessionChangeSet, SessionChangeStats, SessionDiffReviewBound, ReviewSuggestion,
|
|
93
98
|
ReviewSuggestionInput, ReviewNotesPublished, ReviewGitCredential, SessionReviewDocument,
|
|
94
99
|
ReviewSuggestionDecision, ReviewSuggestionDecisions,
|
|
95
100
|
SessionMergeReportDetail, SessionMergeReport, SessionMergeGated, SessionDiscardReport,
|
|
96
|
-
SessionPreflightFileRow, SessionChangePreflight, SessionRebaseReport,
|
|
101
|
+
SessionPreflightFileRow, SessionChangePreflight, SessionRebaseReport,
|
|
97
102
|
ConversationPageContext, SendConversationMessageInput, ConversationTurnState,
|
|
98
103
|
SendConversationMessageResult, UnnotifiedMention, ConversationPresenceSnapshot, AgentListRow, StreamConversationOptions,
|
|
99
104
|
ChannelSessionStatus, ChannelSessionRow, ChannelSessionsResponse, SessionChannelMessagePosted,
|
|
@@ -115,15 +120,16 @@ export type {
|
|
|
115
120
|
RegisterResult, RegisteredRuntime, RuntimeSourceInput, TemplateSourceRef, RegisterWorkflowInput,
|
|
116
121
|
TemplateRow, TemplateDetail, ListTemplatesOptions,
|
|
117
122
|
FactoryFileSearchRow, FactoryFolderSearchRow, SearchFactoryFilesOptions, FactoryFileSearchResult, FactoryFileWriteResult,
|
|
118
|
-
FactoryFileListRow, ListFactoryFilesOptions, FactoryFileListPage,
|
|
123
|
+
FactoryFileListRow, ListFactoryFilesOptions, FactoryFileListPage, FactoryFileConflict, FactoryFileConflictList, ProjectSecretMeta, ProjectSecretList,
|
|
119
124
|
PublicFileLinkState,
|
|
120
125
|
FactoryRow, CreateFactoryInput, UpdateFactoryInput, FactoryPerfSummary,
|
|
121
126
|
ScheduleRow, CreateScheduleInput,
|
|
122
|
-
SecretOptions, SetSecretResult, SecretListEntry, SessionSecretEntry, SessionSecretInput, SessionSecretRequestCreated, SessionSecretRequestStatus,
|
|
127
|
+
SecretOptions, SetSecretResult, SecretListEntry, SessionSecretEntry, SessionSecretInput, SessionSecretRequestCreated, SessionSecretRequestStatus, SessionSecretRequestSummary, VaultRequestKind, VaultCatalogEntry,
|
|
123
128
|
CreateApiKeyInput, ApiKey, ApiKeyCreated, ListApiKeysOptions, ApiKeyPage,
|
|
124
129
|
UsageRollupRow, UsageResponse,
|
|
125
|
-
DriveRepoLink, CreateDriveRepoLinkInput,
|
|
130
|
+
DriveRepoLink, CreateDriveRepoLinkInput, CreateNativeRepoInput, CreateNativeRepoResult,
|
|
126
131
|
DriveMountSession, CreateDriveMountSessionInput,
|
|
132
|
+
ConnectorGrantSummary,
|
|
127
133
|
} from "./types/api-factory.js";
|
|
128
134
|
export type {
|
|
129
135
|
ComplianceScopeKind, ComplianceStatus, ComplianceSession, ComplianceAccess,
|
|
@@ -770,6 +776,55 @@ export class AgentComposeClient {
|
|
|
770
776
|
};
|
|
771
777
|
}
|
|
772
778
|
|
|
779
|
+
/** Bounded, cursor-paginated conflicts. A session filter is applied in the database. */
|
|
780
|
+
/** Project secrets (the group tier) — names + metadata, project members only. */
|
|
781
|
+
listProjectSecrets(projectId: string): Promise<ProjectSecretList> {
|
|
782
|
+
return this.fetch(`/api/v1/projects/${encodeURIComponent(projectId)}/secrets`);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/** Upsert a project secret. Human credential required (a session toolbelt
|
|
786
|
+
* key is refused). Values are write-only — never echoed. */
|
|
787
|
+
upsertProjectSecret(projectId: string, secret: {
|
|
788
|
+
key: string; value: string;
|
|
789
|
+
delivery?: "injected" | "brokered"; brokerHost?: string; brokerHeaderName?: string; brokerHeaderScheme?: string;
|
|
790
|
+
}): Promise<{ key: string; delivery: "injected" | "brokered" }> {
|
|
791
|
+
return this.fetch(`/api/v1/projects/${encodeURIComponent(projectId)}/secrets`, {
|
|
792
|
+
method: "POST", body: JSON.stringify(secret),
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
deleteProjectSecret(projectId: string, key: string): Promise<void> {
|
|
797
|
+
return this.fetch(`/api/v1/projects/${encodeURIComponent(projectId)}/secrets/${encodeURIComponent(key)}`, { method: "DELETE" });
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
listFactoryFileConflicts(factorySlug: string, opts: { limit?: number; cursor?: string; conversationId?: string } = {}): Promise<FactoryFileConflictList> {
|
|
801
|
+
const q = new URLSearchParams();
|
|
802
|
+
if (opts.limit !== undefined) q.set("limit", String(opts.limit));
|
|
803
|
+
if (opts.cursor) q.set("cursor", opts.cursor);
|
|
804
|
+
if (opts.conversationId) q.set("conversationId", opts.conversationId);
|
|
805
|
+
return this.fetch(`/api/v1/factories/${encodeURIComponent(factorySlug)}/files/conflicts?${q}`);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
async getFactoryFileConflictContent(factorySlug: string, id: string): Promise<Uint8Array> {
|
|
809
|
+
const bytes = await this.fetch<ArrayBuffer, "arrayBuffer">(
|
|
810
|
+
`/api/v1/factories/${encodeURIComponent(factorySlug)}/files/conflicts/${encodeURIComponent(id)}/content`,
|
|
811
|
+
{ responseType: "arrayBuffer" },
|
|
812
|
+
);
|
|
813
|
+
return new Uint8Array(bytes);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/** Records a decision about the exact inspected versions. Stale arms refuse. */
|
|
817
|
+
resolveFactoryFileConflict(factorySlug: string, id: string, decision: {
|
|
818
|
+
status: "resolved" | "dismissed";
|
|
819
|
+
settle: "branch" | "main";
|
|
820
|
+
expectedOursHash: string | null;
|
|
821
|
+
expectedTheirsHash: string | null;
|
|
822
|
+
}): Promise<FactoryFileConflict & { settled: "branch" | "main" | "row" }> {
|
|
823
|
+
return this.fetch(`/api/v1/factories/${encodeURIComponent(factorySlug)}/files/conflicts/${encodeURIComponent(id)}/resolve`, {
|
|
824
|
+
method: "POST", body: JSON.stringify(decision),
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
|
|
773
828
|
/** Read one file's current content (or a specific revision) as RAW BYTES —
|
|
774
829
|
* byte-exact for any content type. This is the wire truth; `getFactoryFile`
|
|
775
830
|
* is a UTF-8 decode over it. Binaries (images, archives) MUST come through
|
|
@@ -860,6 +915,17 @@ export class AgentComposeClient {
|
|
|
860
915
|
// access. Key callers see what their sender scope allows; the server is the
|
|
861
916
|
// sole authority — these are thin typed wrappers.
|
|
862
917
|
|
|
918
|
+
/** Create a shared chat, including its own Ivy by default. */
|
|
919
|
+
createChat(input: CreateChatInput): Promise<ConversationRow> {
|
|
920
|
+
return this.fetch("/api/v1/conversations", { method: "POST", body: JSON.stringify(input), headers: { "content-type": "application/json" } });
|
|
921
|
+
}
|
|
922
|
+
getChannelIvy(id: string): Promise<ChannelIvyState> {
|
|
923
|
+
return this.fetch(`/api/v1/conversations/${encodeURIComponent(id)}/ivy`);
|
|
924
|
+
}
|
|
925
|
+
listProjectIvyConnections(id: string): Promise<{ connections: ProjectIvyConnection[]; canManage: boolean }> {
|
|
926
|
+
return this.fetch(`/api/v1/projects/${encodeURIComponent(id)}/ivy-connections`);
|
|
927
|
+
}
|
|
928
|
+
|
|
863
929
|
/** List conversations the caller can access, newest-activity first.
|
|
864
930
|
* Cursor-paginated: pass the previous page's `next_cursor`. */
|
|
865
931
|
listConversations(opts?: { cursor?: string }): Promise<ConversationsPage> {
|
|
@@ -1095,19 +1161,62 @@ export class AgentComposeClient {
|
|
|
1095
1161
|
* background-work busy lease: while it is live, the between-turns
|
|
1096
1162
|
* park/suspend leaves the session's VM running so work the turn left
|
|
1097
1163
|
* behind keeps executing. The lease lapses on its own — re-hold to
|
|
1098
|
-
* extend past `minutes` (server-capped).
|
|
1099
|
-
|
|
1164
|
+
* extend past `minutes` (server-capped). `children` descriptors
|
|
1165
|
+
* ({pid, journalPath, label}) register the DETACHED processes the hold
|
|
1166
|
+
* vouches for, so the platform can verify and reattach them after a
|
|
1167
|
+
* park (task #63). Write-tier. */
|
|
1168
|
+
holdBackgroundWork(
|
|
1169
|
+
conversationId: string,
|
|
1170
|
+
input?: { minutes?: number; children?: BackgroundWorkChildDecl[] },
|
|
1171
|
+
): Promise<BackgroundWorkHeld> {
|
|
1100
1172
|
return this.fetch<BackgroundWorkHeld>(
|
|
1101
1173
|
`/api/v1/conversations/${encodeURIComponent(conversationId)}/background-work`,
|
|
1102
|
-
{ method: "POST", body: {
|
|
1174
|
+
{ method: "POST", body: {
|
|
1175
|
+
...(input?.minutes !== undefined ? { minutes: input.minutes } : {}),
|
|
1176
|
+
...(input?.children !== undefined ? { children: input.children } : {}),
|
|
1177
|
+
} },
|
|
1178
|
+
);
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
/** Request ONE machine size up for a cloud session (task #110 — the
|
|
1182
|
+
* auto-resize policy's agent door). The platform arbitrates: within the
|
|
1183
|
+
* team's daily cap the upsize is auto-granted (and executes the moment
|
|
1184
|
+
* no live turn / background work holds the machine — home essentials
|
|
1185
|
+
* carry over); past the cap it becomes an owner approval card.
|
|
1186
|
+
* Deliberately not `resize`: the in-sandbox toolbelt key may call THIS,
|
|
1187
|
+
* because the kill only ever happens under platform policy or human
|
|
1188
|
+
* approval. Refusals (memory ceiling, template-pinned, ended) arrive as
|
|
1189
|
+
* HTTP 409s carrying `code`. Write-tier. */
|
|
1190
|
+
requestMachineUpsize(
|
|
1191
|
+
conversationId: string, input?: { reason?: string },
|
|
1192
|
+
): Promise<MachineUpsizeOutcome> {
|
|
1193
|
+
return this.fetch<MachineUpsizeOutcome>(
|
|
1194
|
+
`/api/v1/conversations/${encodeURIComponent(conversationId)}/machine/request-upsize`,
|
|
1195
|
+
{ method: "POST", body: input?.reason ? { reason: input.reason } : {} },
|
|
1196
|
+
);
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
/** The session's background-work status: the lease deadline, the declared
|
|
1200
|
+
* children, and the owed-report marker a mid-work park leaves behind.
|
|
1201
|
+
* Read-tier. */
|
|
1202
|
+
getBackgroundWork(conversationId: string): Promise<BackgroundWorkStatus> {
|
|
1203
|
+
return this.fetch<BackgroundWorkStatus>(
|
|
1204
|
+
`/api/v1/conversations/${encodeURIComponent(conversationId)}/background-work`,
|
|
1103
1205
|
);
|
|
1104
1206
|
}
|
|
1105
1207
|
|
|
1106
1208
|
/** Release the session's background-work lease (the work finished).
|
|
1107
|
-
* Idempotent — `released` is false when no lease was held.
|
|
1108
|
-
|
|
1209
|
+
* Idempotent — `released` is false when no lease was held. `pid` scopes
|
|
1210
|
+
* the release to ONE declared child (what a `--while-pid` holder passes
|
|
1211
|
+
* when its watched process dies): the child is pruned from the declared
|
|
1212
|
+
* registry, and the lease clears only when it was the last — a sibling's
|
|
1213
|
+
* death never erases the other holders' declarations. */
|
|
1214
|
+
async releaseBackgroundWork(
|
|
1215
|
+
conversationId: string, opts?: { pid?: number },
|
|
1216
|
+
): Promise<boolean> {
|
|
1217
|
+
const pidQuery = opts?.pid !== undefined ? `?pid=${encodeURIComponent(opts.pid)}` : "";
|
|
1109
1218
|
const body = await this.fetch<{ released: boolean }>(
|
|
1110
|
-
`/api/v1/conversations/${encodeURIComponent(conversationId)}/background-work`,
|
|
1219
|
+
`/api/v1/conversations/${encodeURIComponent(conversationId)}/background-work${pidQuery}`,
|
|
1111
1220
|
{ method: "DELETE" },
|
|
1112
1221
|
);
|
|
1113
1222
|
return body.released;
|
|
@@ -1214,6 +1323,59 @@ export class AgentComposeClient {
|
|
|
1214
1323
|
);
|
|
1215
1324
|
}
|
|
1216
1325
|
|
|
1326
|
+
/** Mint ONE fresh short-lived WIP-push credential for the CALLING
|
|
1327
|
+
* session's git namespace (`POST /sessions/self/wip-git-credential`,
|
|
1328
|
+
* git-door slice 2) — the server half of `agentc repos clone` and its
|
|
1329
|
+
* git credential helper. The token's Push grant is exactly
|
|
1330
|
+
* `refs/sessions/<sessionId>/`; the door refuses everything else.
|
|
1331
|
+
* Callable only with a session's own toolbelt credential.
|
|
1332
|
+
*
|
|
1333
|
+
* Failures: 403 `session_credential_required` | `not_a_session`;
|
|
1334
|
+
* 409 `git_remote_unavailable` (unbranched / legacy plane / fsgw off). */
|
|
1335
|
+
getSessionWipGitCredential(): Promise<ReviewGitCredential & { pushNamespace: string }> {
|
|
1336
|
+
return this.fetch<ReviewGitCredential & { pushNamespace: string }>(
|
|
1337
|
+
"/api/v1/sessions/self/wip-git-credential",
|
|
1338
|
+
{ method: "POST" },
|
|
1339
|
+
);
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
/** Mint a short-lived native-git credential for a linked repo's MIRROR
|
|
1343
|
+
* namespace (`POST /:slug/repo-links/:id/git-credential`, git-door
|
|
1344
|
+
* slice 4). mode "fetch" (default) reads the mirror — gated on link
|
|
1345
|
+
* visibility; mode "mirror" pushes real GitHub history through the
|
|
1346
|
+
* closure-verified door — gated on the repo_link WRITE capability.
|
|
1347
|
+
* 409 `git_remote_unavailable` when no git plane serves the factory. */
|
|
1348
|
+
getRepoLinkGitCredential(linkId: string, opts?: { mode?: "fetch" | "mirror" | "github" | "native"; factorySlug?: string }):
|
|
1349
|
+
Promise<{
|
|
1350
|
+
url: string; username: string; password: string; repoUrl: string;
|
|
1351
|
+
/** Absent in `github` mode (the credential targets github.com, not the platform git plane). */
|
|
1352
|
+
namespace?: string; expiresAt: string; mode: string;
|
|
1353
|
+
/** `github` mode only: the host the credential is for. */
|
|
1354
|
+
host?: string;
|
|
1355
|
+
/** `github` mode only: the token's ceiling — "write" (contents:write,
|
|
1356
|
+
* push-capable) for viewers with write capability on the link,
|
|
1357
|
+
* "read" (clone/fetch only) otherwise. */
|
|
1358
|
+
access?: "read" | "write";
|
|
1359
|
+
/** `github` mode only: the GRANTED permission map GitHub echoed at
|
|
1360
|
+
* mint (e.g. `{contents:"write", pulls:"write"}`). A capability
|
|
1361
|
+
* missing here (pulls, issues) means the App installation does not
|
|
1362
|
+
* carry it — the OWNER must widen the installation; retrying can
|
|
1363
|
+
* never help. */
|
|
1364
|
+
permissions?: Record<string, string>;
|
|
1365
|
+
/** `github` mode only: the INSTALLATION's recorded grant ledger
|
|
1366
|
+
* (`key:level` strings, probe-synced). A capability present here
|
|
1367
|
+
* but missing from `permissions` is a platform downscope; missing
|
|
1368
|
+
* from both layers, an App-installation gap only the owner can
|
|
1369
|
+
* widen. Empty = not yet recorded (reconnect or next probe). */
|
|
1370
|
+
installationScopes?: string[];
|
|
1371
|
+
}> {
|
|
1372
|
+
const slug = opts?.factorySlug ?? DEFAULT_FACTORY;
|
|
1373
|
+
return this.fetch(
|
|
1374
|
+
`/api/v1/factories/${encodeURIComponent(slug)}/repo-links/${encodeURIComponent(linkId)}/git-credential`,
|
|
1375
|
+
{ method: "POST", body: JSON.stringify({ mode: opts?.mode ?? "fetch" }), headers: { "content-type": "application/json" } },
|
|
1376
|
+
);
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1217
1379
|
/** Approve the session's proposed changes: 3-way merge its branch into
|
|
1218
1380
|
* `main`, then continue the session on a fresh branch off post-merge
|
|
1219
1381
|
* `main`. Write-tier + human caller. Pass `opts.branch` (the branch you
|
|
@@ -1224,9 +1386,14 @@ export class AgentComposeClient {
|
|
|
1224
1386
|
* converged on) a kind='merge' approval routed to the session's
|
|
1225
1387
|
* approvers. Discriminate on `object`.
|
|
1226
1388
|
*
|
|
1227
|
-
* Failures: 403 `role_read_only` (read-only member)
|
|
1228
|
-
* session toolbelt
|
|
1229
|
-
*
|
|
1389
|
+
* Failures: 403 `role_read_only` (read-only member); 403 `not_own_branch`
|
|
1390
|
+
* (a session toolbelt key merges ONLY its own bound session's branch —
|
|
1391
|
+
* the agent self-merge door, owner ruling 2026-08-31); 403 `merge_gated`
|
|
1392
|
+
* (a gated session's self-merge — a designated approver merges from the
|
|
1393
|
+
* Changes panel instead, and no card is minted); 409 `turn_active` (a
|
|
1394
|
+
* turn is running — retry when idle; a session's OWN self-merge is
|
|
1395
|
+
* exempt) | `branch_changed` | `branch_claimed` (a teammate session
|
|
1396
|
+
* holds the branch claim — merge after it releases); 400
|
|
1230
1397
|
* `no_branch` (session predates branching); 502 `quiesce_failed`
|
|
1231
1398
|
* (nothing changed — safe to retry) | `merge_failed` (branch retained,
|
|
1232
1399
|
* named in the body) | `rebranch_failed` (merge landed; retry safe). */
|
|
@@ -1290,24 +1457,6 @@ export class AgentComposeClient {
|
|
|
1290
1457
|
);
|
|
1291
1458
|
}
|
|
1292
1459
|
|
|
1293
|
-
/** Arm/disarm the OPT-IN main-advance auto-rebase reflex for a session
|
|
1294
|
-
* (default OFF): when armed and the drive's main moves, the platform
|
|
1295
|
-
* preflights and — only on a provably clean fold (zero conflicts) —
|
|
1296
|
-
* rebases the session's branch from main headlessly, posting a quiet
|
|
1297
|
-
* receipt. Trade-off (why default OFF): the agent's view of files it
|
|
1298
|
-
* did not touch can change mid-task. Human, write-tier act.
|
|
1299
|
-
*
|
|
1300
|
-
* Failures: 409 `review_session_scratch` | `plane_unsupported`; 403
|
|
1301
|
-
* (needs a user-bound human caller with write access). */
|
|
1302
|
-
setSessionAutoRebase(
|
|
1303
|
-
conversationId: string, enabled: boolean,
|
|
1304
|
-
): Promise<SessionAutoRebaseState> {
|
|
1305
|
-
return this.fetch<SessionAutoRebaseState>(
|
|
1306
|
-
`/api/v1/conversations/${encodeURIComponent(conversationId)}/changes/autorebase`,
|
|
1307
|
-
{ method: "POST", body: { enabled } },
|
|
1308
|
-
);
|
|
1309
|
-
}
|
|
1310
|
-
|
|
1311
1460
|
// ── Conversation membership (ADR-0045) ────────────────────────────────────
|
|
1312
1461
|
// Channel rosters carry roles (`owner > write > read`). Inviting is a
|
|
1313
1462
|
// `write` action; removing and re-roling are `owner` (or team admin)
|
|
@@ -1497,6 +1646,13 @@ export class AgentComposeClient {
|
|
|
1497
1646
|
};
|
|
1498
1647
|
}
|
|
1499
1648
|
|
|
1649
|
+
/** The credential's user role on the active team; null for a creator-less
|
|
1650
|
+
* key or a user with no membership. */
|
|
1651
|
+
async getTeamRole(): Promise<string | null> {
|
|
1652
|
+
const body = await this.fetch<{ role: string | null }>("/api/v1/team/role");
|
|
1653
|
+
return body.role;
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1500
1656
|
/** List the human members of your team — the people you (or an agent) can
|
|
1501
1657
|
* @-flag with `createMentions`. Each row's `userId` is what
|
|
1502
1658
|
* `mentionedUserIds` expects. */
|
|
@@ -1914,6 +2070,27 @@ export class AgentComposeClient {
|
|
|
1914
2070
|
return body.links;
|
|
1915
2071
|
}
|
|
1916
2072
|
|
|
2073
|
+
/** Create a repo whose origin IS Agent Compose (`manage` scope) — no
|
|
2074
|
+
* GitHub behind it; clone/fetch/push ride the platform git door. 409
|
|
2075
|
+
* names the missing prerequisite (graph drive, git plane) or an
|
|
2076
|
+
* overlapping placement. */
|
|
2077
|
+
async createNativeRepo(
|
|
2078
|
+
input: CreateNativeRepoInput, opts?: { factorySlug?: string },
|
|
2079
|
+
): Promise<CreateNativeRepoResult> {
|
|
2080
|
+
const slug = opts?.factorySlug ?? DEFAULT_FACTORY;
|
|
2081
|
+
return this.fetch<CreateNativeRepoResult>(
|
|
2082
|
+
`/api/v1/factories/${encodeURIComponent(slug)}/repo-links/native`,
|
|
2083
|
+
{ method: "POST", body: input });
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
/** The team's connector grants (metadata only — never tokens). Used by
|
|
2087
|
+
* `agentc repos link` to default `--grant` when the team holds exactly
|
|
2088
|
+
* one active GitHub grant. */
|
|
2089
|
+
async listConnectorGrants(): Promise<ConnectorGrantSummary[]> {
|
|
2090
|
+
const body = await this.fetch<{ data: ConnectorGrantSummary[] }>("/api/v1/connectors");
|
|
2091
|
+
return body.data;
|
|
2092
|
+
}
|
|
2093
|
+
|
|
1917
2094
|
/** Unlink (§6.3: the prefix's files and history stay on the drive). */
|
|
1918
2095
|
deleteRepoLink(linkId: string, opts?: { factorySlug?: string }): Promise<void> {
|
|
1919
2096
|
const slug = opts?.factorySlug ?? DEFAULT_FACTORY;
|
|
@@ -1986,20 +2163,61 @@ export class AgentComposeClient {
|
|
|
1986
2163
|
* toolbelt credential (the in-sandbox agent's door) or by a session
|
|
1987
2164
|
* writer. */
|
|
1988
2165
|
requestSessionSecrets(
|
|
1989
|
-
conversationId: string, keys: string[],
|
|
2166
|
+
conversationId: string, keys: string[],
|
|
2167
|
+
opts?: { reason?: string; setLabel?: string; kind?: VaultRequestKind },
|
|
1990
2168
|
): Promise<SessionSecretRequestCreated> {
|
|
1991
2169
|
return this.fetch(
|
|
1992
2170
|
`/api/v1/conversations/${encodeURIComponent(conversationId)}/secret-requests`,
|
|
1993
|
-
{ method: "POST", body: {
|
|
2171
|
+
{ method: "POST", body: {
|
|
2172
|
+
keys,
|
|
2173
|
+
...(opts?.reason ? { reason: opts.reason } : {}),
|
|
2174
|
+
// Suggested batch name — prefills the vault form's save-as-set label.
|
|
2175
|
+
...(opts?.setLabel ? { setLabel: opts.setLabel } : {}),
|
|
2176
|
+
// WHAT KIND of credential — the vault page leads with the user's
|
|
2177
|
+
// matching STANDING entries (personal-vault reuse, 2026-08-31).
|
|
2178
|
+
...(opts?.kind ? { kind: opts.kind } : {}),
|
|
2179
|
+
} });
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2182
|
+
/** The session's STANDING-vault catalog (2026-08-31): entries the vault
|
|
2183
|
+
* could grant to this session — labels, kinds, field NAMES, last-used.
|
|
2184
|
+
* Values are write-only and never on this wire. Query it BEFORE asking
|
|
2185
|
+
* the human for a credential: a matching entry means the ask is a GRANT
|
|
2186
|
+
* ask naming it, never a blank re-entry form. */
|
|
2187
|
+
getSessionVaultCatalog(conversationId: string): Promise<{ entries: VaultCatalogEntry[]; note?: string }> {
|
|
2188
|
+
return this.fetch(
|
|
2189
|
+
`/api/v1/conversations/${encodeURIComponent(conversationId)}/vault-catalog`);
|
|
1994
2190
|
}
|
|
1995
2191
|
|
|
1996
2192
|
/** Poll a vault link's status (`pending` → `fulfilled`; `expired` when the
|
|
1997
|
-
* clock ran out
|
|
2193
|
+
* clock ran out; `cancelled` when the human denied it or the requester
|
|
2194
|
+
* withdrew it — attribution rides the answer). The in-sandbox
|
|
2195
|
+
* `agentc secrets request --wait` loop. */
|
|
1998
2196
|
getSessionSecretRequest(conversationId: string, requestId: string): Promise<SessionSecretRequestStatus> {
|
|
1999
2197
|
return this.fetch(
|
|
2000
2198
|
`/api/v1/conversations/${encodeURIComponent(conversationId)}/secret-requests/${encodeURIComponent(requestId)}`);
|
|
2001
2199
|
}
|
|
2002
2200
|
|
|
2201
|
+
/** List a session's OPEN vault requests (pending + unexpired), newest
|
|
2202
|
+
* first — receipts only (keys, reason, clocks). The cancel verb's
|
|
2203
|
+
* `--all` enumeration. */
|
|
2204
|
+
async listSessionSecretRequests(conversationId: string): Promise<SessionSecretRequestSummary[]> {
|
|
2205
|
+
const body = await this.fetch<{ requests: SessionSecretRequestSummary[] }>(
|
|
2206
|
+
`/api/v1/conversations/${encodeURIComponent(conversationId)}/secret-requests`);
|
|
2207
|
+
return body.requests;
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
/** Cancel one vault request (task #111). The requesting agent withdraws
|
|
2211
|
+
* its own ask (session toolbelt key), or a session writer denies it.
|
|
2212
|
+
* 409 `request_not_pending` when it already settled. */
|
|
2213
|
+
cancelSessionSecretRequest(
|
|
2214
|
+
conversationId: string, requestId: string, opts?: { reason?: string },
|
|
2215
|
+
): Promise<{ requestId: string; status: "cancelled" }> {
|
|
2216
|
+
return this.fetch(
|
|
2217
|
+
`/api/v1/conversations/${encodeURIComponent(conversationId)}/secret-requests/${encodeURIComponent(requestId)}/cancel`,
|
|
2218
|
+
{ method: "POST", body: { ...(opts?.reason ? { reason: opts.reason } : {}) } });
|
|
2219
|
+
}
|
|
2220
|
+
|
|
2003
2221
|
// ── API keys ───────────────────────────────────────────────────────────────
|
|
2004
2222
|
// Both endpoints require an admin-scoped key as the bearer token.
|
|
2005
2223
|
|
package/src/directives.ts
CHANGED
|
@@ -61,7 +61,15 @@ export type CloudDirective =
|
|
|
61
61
|
/** Interactive question card gated on a named approver — resolution and
|
|
62
62
|
* the mentions ping are server-authoritative (`display_ask` shape). The
|
|
63
63
|
* one write-bearing directive (see the module header). */
|
|
64
|
-
| { kind: "ask"; prompt: string; approver: string; options?: Array<{ id: string; label: string }> }
|
|
64
|
+
| { kind: "ask"; prompt: string; approver: string; options?: Array<{ id: string; label: string }> }
|
|
65
|
+
/** Offer the human the desktop takeover door (`request_desktop_takeover`
|
|
66
|
+
* shape): the server captures the current screen and posts a card with a
|
|
67
|
+
* signed takeover link — "You can take over from here." Grants nothing
|
|
68
|
+
* by itself: the link's viewer must still be a signed-in member with
|
|
69
|
+
* write access to THIS session (the server re-checks on every resolve).
|
|
70
|
+
* ONLY for structurally-human walls (a CAPTCHA, human-only verification)
|
|
71
|
+
* or when the user asked to do it themselves. */
|
|
72
|
+
| { kind: "takeover"; note?: string };
|
|
65
73
|
|
|
66
74
|
/** `runId` values the run directives accept: a UUID, or the literal
|
|
67
75
|
* `latest` (resolved server-side against the session's factory, optionally
|
|
@@ -85,6 +93,10 @@ export const ASK_DIRECTIVE_OPTION_LABEL_MAX = 60;
|
|
|
85
93
|
export const ASK_APPROVER_MIN = 3;
|
|
86
94
|
export const ASK_APPROVER_MAX = 320;
|
|
87
95
|
|
|
96
|
+
/** Takeover-note bound — one line naming what the human must do on the
|
|
97
|
+
* screen (mirrors the display-desktop caption cap). */
|
|
98
|
+
export const TAKEOVER_NOTE_MAX_CHARS = 200;
|
|
99
|
+
|
|
88
100
|
function validDirectivePath(v: unknown): string | null {
|
|
89
101
|
return typeof v === "string" && v.length >= 1 && v.length <= DIRECTIVE_PATH_MAX ? v : null;
|
|
90
102
|
}
|
|
@@ -152,6 +164,14 @@ function validDirective(value: unknown): CloudDirective | null {
|
|
|
152
164
|
}
|
|
153
165
|
return { kind: "ask", prompt, approver, ...(options !== undefined ? { options } : {}) };
|
|
154
166
|
}
|
|
167
|
+
if (d.kind === "takeover") {
|
|
168
|
+
const note = d.note;
|
|
169
|
+
if (note !== undefined
|
|
170
|
+
&& (typeof note !== "string" || note.length < 1 || note.length > TAKEOVER_NOTE_MAX_CHARS)) {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
return { kind: "takeover", ...(note !== undefined ? { note } : {}) };
|
|
174
|
+
}
|
|
155
175
|
return null;
|
|
156
176
|
}
|
|
157
177
|
|