@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/client.ts
CHANGED
|
@@ -18,7 +18,8 @@ import type { RunEvent } from "./types/events.js";
|
|
|
18
18
|
import type { ConversationStreamEvent } from "./types/conversation-stream.js";
|
|
19
19
|
import { normalizeConversationStreamEvent } from "./types/conversation-stream.js";
|
|
20
20
|
import type {
|
|
21
|
-
InvokeWorkflowOptions, InvokeAndWaitOptions, InvokeResult, StreamRunLogsOptions,
|
|
21
|
+
InvokeWorkflowOptions, InvokeAndWaitOptions, InvokeResult, FundingChoice, StreamRunLogsOptions,
|
|
22
|
+
InlineWorkflowPayload, InvokeInlineOptions, InvokeInlineAndWaitOptions,
|
|
22
23
|
RunStatus, ResumePauseOptions, ResumePauseResponse, AnswerSteerOptions,
|
|
23
24
|
RequestAgentPauseOptions, RequestAgentPauseResponse,
|
|
24
25
|
SendAgentMessageOptions, SendAgentMessageResponse,
|
|
@@ -30,8 +31,8 @@ import type {
|
|
|
30
31
|
TeamMember, Mention, CreateMentionsInput,
|
|
31
32
|
ConversationsPage, SessionsPage, ConversationDetail, ConversationThread,
|
|
32
33
|
CreateCloudSessionInput, CloudSessionCreated,
|
|
33
|
-
SessionPreview, PreviewOpened, OpenPreviewInput, SessionForked,
|
|
34
|
-
SessionChangeSet, SessionMergeReport, SessionDiscardReport,
|
|
34
|
+
SessionPreview, PreviewOpened, OpenPreviewInput, SessionForked, BackgroundWorkHeld,
|
|
35
|
+
SessionChangeSet, SessionMergeGated, SessionMergeReport, SessionDiscardReport,
|
|
35
36
|
SendConversationMessageInput, SendConversationMessageResult,
|
|
36
37
|
ChannelSessionRow, ChannelSessionsResponse, SessionChannelMessagePosted,
|
|
37
38
|
ConversationPresenceSnapshot, StreamConversationOptions, AgentListRow,
|
|
@@ -51,8 +52,9 @@ import type {
|
|
|
51
52
|
FactoryRow, CreateFactoryInput, UpdateFactoryInput,
|
|
52
53
|
ScheduleRow, CreateScheduleInput,
|
|
53
54
|
SecretOptions, SetSecretResult, SecretListEntry,
|
|
54
|
-
CreateApiKeyInput, ApiKey, ApiKeyCreated, UsageResponse,
|
|
55
|
+
CreateApiKeyInput, ApiKey, ApiKeyCreated, ListApiKeysOptions, ApiKeyPage, UsageResponse,
|
|
55
56
|
DriveRepoLink, CreateDriveRepoLinkInput,
|
|
57
|
+
DriveMountSession, CreateDriveMountSessionInput,
|
|
56
58
|
} from "./types/api-factory.js";
|
|
57
59
|
import type {
|
|
58
60
|
ComplianceSession, RequestComplianceSessionInput, ListComplianceSessionsOptions,
|
|
@@ -64,7 +66,8 @@ import type {
|
|
|
64
66
|
// here so `client.js` stays the single import surface for the client and its
|
|
65
67
|
// shapes (index.ts re-exports from here).
|
|
66
68
|
export type {
|
|
67
|
-
RunState, InvokeWorkflowOptions, InvokeAndWaitOptions, InvokeResult, StreamRunLogsOptions,
|
|
69
|
+
RunState, InvokeWorkflowOptions, InvokeAndWaitOptions, InvokeResult, FundingChoice, StreamRunLogsOptions,
|
|
70
|
+
InlineWorkflowPayload, InvokeInlineOptions, InvokeInlineAndWaitOptions,
|
|
68
71
|
RunStatus, ResumePauseActor, ResumePauseSuccess, ResumePausePending, ResumePauseResponse,
|
|
69
72
|
ResumePauseOptions, RequestAgentPauseOptions, AnswerSteerOptions, RequestAgentPauseResponse,
|
|
70
73
|
SendAgentMessageOptions, SendAgentMessageResponse,
|
|
@@ -79,11 +82,12 @@ export type {
|
|
|
79
82
|
ConversationMessagePart, ConversationRow, ConversationMessageRow,
|
|
80
83
|
ConversationsPage, SessionsPage, ConversationDetail,
|
|
81
84
|
CreateCloudSessionInput, CloudSessionCreated, ConversationThread,
|
|
82
|
-
SessionPreview, PreviewOpened, OpenPreviewInput, SessionForked,
|
|
83
|
-
SessionFileChange, SessionChangeSet,
|
|
84
|
-
|
|
85
|
+
SessionPreview, PreviewOpened, OpenPreviewInput, SessionForked, BackgroundWorkHeld,
|
|
86
|
+
SessionFileChange, SessionChangeSet, SessionChangeStats, SessionDiffReviewBound, ReviewSuggestion,
|
|
87
|
+
ReviewSuggestionDecision, ReviewSuggestionDecisions,
|
|
88
|
+
SessionMergeReportDetail, SessionMergeReport, SessionMergeGated, SessionDiscardReport,
|
|
85
89
|
ConversationPageContext, SendConversationMessageInput, ConversationTurnState,
|
|
86
|
-
SendConversationMessageResult, ConversationPresenceSnapshot, AgentListRow, StreamConversationOptions,
|
|
90
|
+
SendConversationMessageResult, UnnotifiedMention, ConversationPresenceSnapshot, AgentListRow, StreamConversationOptions,
|
|
87
91
|
ChannelSessionStatus, ChannelSessionRow, ChannelSessionsResponse, SessionChannelMessagePosted,
|
|
88
92
|
} from "./types/api-conversations.js";
|
|
89
93
|
export type {
|
|
@@ -105,9 +109,10 @@ export type {
|
|
|
105
109
|
FactoryRow, CreateFactoryInput, UpdateFactoryInput,
|
|
106
110
|
ScheduleRow, CreateScheduleInput,
|
|
107
111
|
SecretOptions, SetSecretResult, SecretListEntry,
|
|
108
|
-
CreateApiKeyInput, ApiKey, ApiKeyCreated,
|
|
112
|
+
CreateApiKeyInput, ApiKey, ApiKeyCreated, ListApiKeysOptions, ApiKeyPage,
|
|
109
113
|
UsageRollupRow, UsageResponse,
|
|
110
114
|
DriveRepoLink, CreateDriveRepoLinkInput,
|
|
115
|
+
DriveMountSession, CreateDriveMountSessionInput,
|
|
111
116
|
} from "./types/api-factory.js";
|
|
112
117
|
export type {
|
|
113
118
|
ComplianceScopeKind, ComplianceStatus, ComplianceSession, ComplianceAccess,
|
|
@@ -296,6 +301,45 @@ export class AgentComposeClient {
|
|
|
296
301
|
...(opts?.size !== undefined ? { size: opts.size } : {}),
|
|
297
302
|
...(parentRunId ? { parentRunId } : {}),
|
|
298
303
|
...(opts?.agentId ? { agentId: opts.agentId } : {}),
|
|
304
|
+
...(opts?.funding !== undefined ? { funding: opts.funding } : {}),
|
|
305
|
+
...(opts?.fundingSecret !== undefined ? { fundingSecret: opts.fundingSecret } : {}),
|
|
306
|
+
},
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/** Invoke an INLINE workflow — the exact payload `bundleWorkflow` produced
|
|
311
|
+
* plus a `name` — WITHOUT registering it. The server validates it through
|
|
312
|
+
* the same core as registration (manifest required + bound to the source
|
|
313
|
+
* bytes) but writes no registry row: the run snapshots the source it
|
|
314
|
+
* executes, and registration stays the door for named/versioned/scheduled
|
|
315
|
+
* workflows. Same parent-child auto-detection and `Idempotency-Key`
|
|
316
|
+
* semantics as `invoke()`. Requires the `invoke` scope. */
|
|
317
|
+
invokeInline(
|
|
318
|
+
workflow: InlineWorkflowPayload,
|
|
319
|
+
input?: Record<string, unknown>,
|
|
320
|
+
opts?: InvokeInlineOptions,
|
|
321
|
+
): Promise<InvokeResult> {
|
|
322
|
+
const parentRunId = opts?.parentRunId === undefined
|
|
323
|
+
? detectAmbientParentRunId()
|
|
324
|
+
: opts.parentRunId;
|
|
325
|
+
const factorySlug = opts?.factorySlug ?? DEFAULT_FACTORY;
|
|
326
|
+
const headers: Record<string, string> = {};
|
|
327
|
+
if (opts?.idempotencyKey) headers["Idempotency-Key"] = opts.idempotencyKey;
|
|
328
|
+
return this.fetch(`/api/v1/factories/${encodeURIComponent(factorySlug)}/invoke`, {
|
|
329
|
+
method: "POST",
|
|
330
|
+
...(opts?.idempotencyKey ? { headers } : {}),
|
|
331
|
+
body: {
|
|
332
|
+
workflow,
|
|
333
|
+
input,
|
|
334
|
+
...(opts?.title !== undefined ? { title: opts.title } : {}),
|
|
335
|
+
...(opts?.snapshots !== undefined ? { snapshots: opts.snapshots } : {}),
|
|
336
|
+
...(opts?.networkPolicy !== undefined ? { networkPolicy: opts.networkPolicy } : {}),
|
|
337
|
+
...(opts?.placeholders !== undefined ? { placeholders: opts.placeholders } : {}),
|
|
338
|
+
...(opts?.size !== undefined ? { size: opts.size } : {}),
|
|
339
|
+
...(parentRunId ? { parentRunId } : {}),
|
|
340
|
+
...(opts?.agentId ? { agentId: opts.agentId } : {}),
|
|
341
|
+
...(opts?.funding !== undefined ? { funding: opts.funding } : {}),
|
|
342
|
+
...(opts?.fundingSecret !== undefined ? { fundingSecret: opts.fundingSecret } : {}),
|
|
299
343
|
},
|
|
300
344
|
});
|
|
301
345
|
}
|
|
@@ -312,12 +356,33 @@ export class AgentComposeClient {
|
|
|
312
356
|
input?: Record<string, unknown>,
|
|
313
357
|
opts?: InvokeAndWaitOptions,
|
|
314
358
|
): Promise<RunStatus<TOutput>> {
|
|
315
|
-
const timeoutMs = opts?.timeoutMs ?? 30 * 60 * 1000;
|
|
316
|
-
const pollMs = opts?.pollIntervalMs ?? 1000;
|
|
317
359
|
// InvokeAndWaitOptions extends InvokeWorkflowOptions, so we can forward
|
|
318
360
|
// `opts` directly — invoke() picks only the fields it sends, so the
|
|
319
361
|
// extra `timeoutMs` / `pollIntervalMs` never leak into the request body.
|
|
320
362
|
const { id: runId } = await this.invoke(name, input, opts);
|
|
363
|
+
return this.waitForRun<TOutput>(runId, opts);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/** `invokeInline` + block until the run settles — the "call blocks →
|
|
367
|
+
* result returns on the same turn" contract for inline sub-workflows. */
|
|
368
|
+
async invokeInlineAndWait<TOutput = unknown>(
|
|
369
|
+
workflow: InlineWorkflowPayload,
|
|
370
|
+
input?: Record<string, unknown>,
|
|
371
|
+
opts?: InvokeInlineAndWaitOptions,
|
|
372
|
+
): Promise<RunStatus<TOutput>> {
|
|
373
|
+
const { id: runId } = await this.invokeInline(workflow, input, opts);
|
|
374
|
+
return this.waitForRun<TOutput>(runId, opts);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/** Poll one run until it settles (success / failed / abandoned) and return
|
|
378
|
+
* its final status. Shared tail of `invokeAndWait` / `invokeInlineAndWait`;
|
|
379
|
+
* also useful to re-attach to a run you dispatched fire-and-forget. */
|
|
380
|
+
async waitForRun<TOutput = unknown>(
|
|
381
|
+
runId: string,
|
|
382
|
+
opts?: { timeoutMs?: number; pollIntervalMs?: number },
|
|
383
|
+
): Promise<RunStatus<TOutput>> {
|
|
384
|
+
const timeoutMs = opts?.timeoutMs ?? 30 * 60 * 1000;
|
|
385
|
+
const pollMs = opts?.pollIntervalMs ?? 1000;
|
|
321
386
|
const deadline = Date.now() + timeoutMs;
|
|
322
387
|
while (Date.now() < deadline) {
|
|
323
388
|
const status = await this.getStatus<TOutput>(runId);
|
|
@@ -329,7 +394,7 @@ export class AgentComposeClient {
|
|
|
329
394
|
// Use AgentComposeError (not plain Error) so catch-blocks handling SDK
|
|
330
395
|
// transport failures also handle timeouts uniformly. HTTP 504 is the
|
|
331
396
|
// closest idiomatic status for "upstream didn't answer in time."
|
|
332
|
-
throw new AgentComposeError(504, `
|
|
397
|
+
throw new AgentComposeError(504, `waitForRun: run ${runId} did not settle within ${timeoutMs}ms`);
|
|
333
398
|
}
|
|
334
399
|
|
|
335
400
|
/** List captured snapshots in a factory. */
|
|
@@ -622,6 +687,21 @@ export class AgentComposeClient {
|
|
|
622
687
|
}));
|
|
623
688
|
}
|
|
624
689
|
|
|
690
|
+
/** One run artifact's bytes, resolved server-side through the DRIVE INDEX
|
|
691
|
+
* (never the run's gone sandbox or branch) — a listed artifact with an
|
|
692
|
+
* indexed path is always readable here, including after the run's branch
|
|
693
|
+
* is merged/retired. The path is the listing's `path`, sent as a single
|
|
694
|
+
* query parameter so slashes / spaces / unicode in agent-derived
|
|
695
|
+
* filenames survive verbatim. */
|
|
696
|
+
async getRunArtifactBytes(runId: string, path: string): Promise<Uint8Array> {
|
|
697
|
+
const q = new URLSearchParams({ path });
|
|
698
|
+
const buf = await this.fetch<ArrayBuffer, "arrayBuffer">(
|
|
699
|
+
`/api/v1/workflows/${encodeURIComponent(runId)}/artifacts/content?${q}`,
|
|
700
|
+
{ responseType: "arrayBuffer" },
|
|
701
|
+
);
|
|
702
|
+
return new Uint8Array(buf);
|
|
703
|
+
}
|
|
704
|
+
|
|
625
705
|
// ── Factory files ──────────────────────────────────────────────────────────
|
|
626
706
|
// The factory drive: documents surfaced in the dashboard's Files tab.
|
|
627
707
|
// Writes from inside a sandbox automatically carry the run-callback token
|
|
@@ -665,13 +745,17 @@ export class AgentComposeClient {
|
|
|
665
745
|
* U+FFFD and the original bytes are unrecoverable). */
|
|
666
746
|
async getFactoryFileBytes(
|
|
667
747
|
path: string,
|
|
668
|
-
opts?: { factorySlug?: string; revision?: number },
|
|
748
|
+
opts?: { factorySlug?: string; revision?: number; branch?: string },
|
|
669
749
|
): Promise<Uint8Array> {
|
|
670
750
|
const factorySlug = opts?.factorySlug
|
|
671
751
|
?? (typeof process !== "undefined" ? process.env?.AGENT_COMPOSE_FACTORY : undefined)
|
|
672
752
|
?? DEFAULT_FACTORY;
|
|
673
753
|
const q = new URLSearchParams({ path });
|
|
674
754
|
if (opts?.revision !== undefined) q.set("revision", String(opts.revision));
|
|
755
|
+
// Branch view (read-only): the file AS OF a drive branch — e.g. a cloud
|
|
756
|
+
// session's own `session-<uuid>` branch, where its unmerged work lives.
|
|
757
|
+
// Mutually exclusive with `revision` (the server rejects the combination).
|
|
758
|
+
if (opts?.branch !== undefined) q.set("branch", opts.branch);
|
|
675
759
|
const buf = await this.fetch<ArrayBuffer, "arrayBuffer">(
|
|
676
760
|
`/api/v1/factories/${encodeURIComponent(factorySlug)}/files/content?${q}`,
|
|
677
761
|
{ responseType: "arrayBuffer" },
|
|
@@ -684,11 +768,57 @@ export class AgentComposeClient {
|
|
|
684
768
|
* text corrupts the bytes irreversibly. */
|
|
685
769
|
async getFactoryFile(
|
|
686
770
|
path: string,
|
|
687
|
-
opts?: { factorySlug?: string; revision?: number },
|
|
771
|
+
opts?: { factorySlug?: string; revision?: number; branch?: string },
|
|
688
772
|
): Promise<string> {
|
|
689
773
|
return new TextDecoder().decode(await this.getFactoryFileBytes(path, opts));
|
|
690
774
|
}
|
|
691
775
|
|
|
776
|
+
// ── User drive mounts ──────────────────────────────────────────────────────
|
|
777
|
+
// `agentc files mount` on a human's own machine: the server forks a user
|
|
778
|
+
// branch, backs it with a mount session (the review surface), and mints a
|
|
779
|
+
// user-principal gateway token. Human-held credentials only (sign-in
|
|
780
|
+
// cookie or a device-flow bridge key) — sandbox session keys are refused.
|
|
781
|
+
|
|
782
|
+
/** Create a local drive mount (or re-mint an existing one's token by
|
|
783
|
+
* passing its `conversationId`). */
|
|
784
|
+
async createDriveMountSession(
|
|
785
|
+
opts?: CreateDriveMountSessionInput & { factorySlug?: string },
|
|
786
|
+
): Promise<DriveMountSession> {
|
|
787
|
+
const factorySlug = opts?.factorySlug
|
|
788
|
+
?? (typeof process !== "undefined" ? process.env?.AGENT_COMPOSE_FACTORY : undefined)
|
|
789
|
+
?? DEFAULT_FACTORY;
|
|
790
|
+
return this.fetch<DriveMountSession>(
|
|
791
|
+
`/api/v1/factories/${encodeURIComponent(factorySlug)}/files/mount-sessions`,
|
|
792
|
+
{
|
|
793
|
+
method: "POST",
|
|
794
|
+
body: {
|
|
795
|
+
...(opts?.conversationId ? { conversationId: opts.conversationId } : {}),
|
|
796
|
+
...(opts?.host ? { host: opts.host } : {}),
|
|
797
|
+
},
|
|
798
|
+
},
|
|
799
|
+
);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/** Release a local mount's gateway branch mount (clean unmount). The
|
|
803
|
+
* branch and its review card survive — this only drops the gateway's
|
|
804
|
+
* in-memory mount so the exclusive branch is not pinned. */
|
|
805
|
+
async releaseDriveMountSession(
|
|
806
|
+
conversationId: string,
|
|
807
|
+
opts?: { factorySlug?: string },
|
|
808
|
+
): Promise<{ released: boolean }> {
|
|
809
|
+
const factorySlug = opts?.factorySlug
|
|
810
|
+
?? (typeof process !== "undefined" ? process.env?.AGENT_COMPOSE_FACTORY : undefined)
|
|
811
|
+
?? DEFAULT_FACTORY;
|
|
812
|
+
return this.fetch<{ released: boolean }>(
|
|
813
|
+
`/api/v1/factories/${encodeURIComponent(factorySlug)}/files/mount-sessions/${encodeURIComponent(conversationId)}/release`,
|
|
814
|
+
{ method: "POST", body: {} },
|
|
815
|
+
);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
// A mount's change set + merge/discard ride the EXISTING session-branch
|
|
819
|
+
// proposal surface below (`getSessionChanges` / `mergeSessionChanges` /
|
|
820
|
+
// `discardSessionChanges`) — the mount's `conversationId` is a session.
|
|
821
|
+
|
|
692
822
|
// ── Conversations ──────────────────────────────────────────────────────────
|
|
693
823
|
// The chat surfaces (channels / solo chats / sessions) the caller can
|
|
694
824
|
// access. Key callers see what their sender scope allows; the server is the
|
|
@@ -711,9 +841,12 @@ export class AgentComposeClient {
|
|
|
711
841
|
return this.fetch<SessionsPage>(`/api/v1/sessions?${q}`);
|
|
712
842
|
}
|
|
713
843
|
|
|
714
|
-
/** One conversation with its latest page of messages.
|
|
715
|
-
|
|
716
|
-
|
|
844
|
+
/** One conversation with its latest page of messages. `limit` bounds the
|
|
845
|
+
* page (newest N) — metadata-only consumers (e.g. resolving the
|
|
846
|
+
* session's drive branch) pass 1 instead of pulling the full hydrate. */
|
|
847
|
+
getConversation(id: string, opts?: { limit?: number }): Promise<ConversationDetail> {
|
|
848
|
+
const q = opts?.limit !== undefined ? `?limit=${opts.limit}` : "";
|
|
849
|
+
return this.fetch<ConversationDetail>(`/api/v1/conversations/${encodeURIComponent(id)}${q}`);
|
|
717
850
|
}
|
|
718
851
|
|
|
719
852
|
/** Provision a CLOUD-native session (ADR-0037 Phase 3b / ADR-0055 §9): a
|
|
@@ -742,6 +875,9 @@ export class AgentComposeClient {
|
|
|
742
875
|
...(input.sandboxSize ? { sandboxSize: input.sandboxSize } : {}),
|
|
743
876
|
...(input.networkPolicy !== undefined ? { networkPolicy: input.networkPolicy } : {}),
|
|
744
877
|
...(input.connectorProfileId !== undefined ? { connectorProfileId: input.connectorProfileId } : {}),
|
|
878
|
+
...(input.setupCommand !== undefined ? { setupCommand: input.setupCommand } : {}),
|
|
879
|
+
...(input.handoffOrigin !== undefined ? { handoffOrigin: input.handoffOrigin } : {}),
|
|
880
|
+
...(input.seedFiles !== undefined ? { seedFiles: input.seedFiles } : {}),
|
|
745
881
|
},
|
|
746
882
|
});
|
|
747
883
|
}
|
|
@@ -849,6 +985,28 @@ export class AgentComposeClient {
|
|
|
849
985
|
return body.closed;
|
|
850
986
|
}
|
|
851
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
|
+
|
|
852
1010
|
/** Fork a cloud session from HEAD: snapshot the VM + branch the drive + seed
|
|
853
1011
|
* a new conversation from the transcript so far, booting from both. Returns
|
|
854
1012
|
* the child conversation id to switch to. Write-tier; "Branch from here." */
|
|
@@ -882,6 +1040,11 @@ export class AgentComposeClient {
|
|
|
882
1040
|
* `main`. Write-tier + human caller. Pass `opts.branch` (the branch you
|
|
883
1041
|
* reviewed) to fail 409 `branch_changed` if it moved since.
|
|
884
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
|
+
*
|
|
885
1048
|
* Failures: 403 `role_read_only` (read-only member) or a plain 403 for
|
|
886
1049
|
* session toolbelt keys (agents cannot self-approve); 409 `turn_active`
|
|
887
1050
|
* (a turn is running — retry when idle) | `branch_changed`; 400
|
|
@@ -891,8 +1054,8 @@ export class AgentComposeClient {
|
|
|
891
1054
|
mergeSessionChanges(
|
|
892
1055
|
conversationId: string,
|
|
893
1056
|
opts: { branch?: string } = {},
|
|
894
|
-
): Promise<SessionMergeReport> {
|
|
895
|
-
return this.fetch<SessionMergeReport>(
|
|
1057
|
+
): Promise<SessionMergeReport | SessionMergeGated> {
|
|
1058
|
+
return this.fetch<SessionMergeReport | SessionMergeGated>(
|
|
896
1059
|
`/api/v1/conversations/${encodeURIComponent(conversationId)}/changes/merge`,
|
|
897
1060
|
{ method: "POST", body: opts.branch ? { branch: opts.branch } : {} },
|
|
898
1061
|
);
|
|
@@ -1473,17 +1636,18 @@ export class AgentComposeClient {
|
|
|
1473
1636
|
return body.links;
|
|
1474
1637
|
}
|
|
1475
1638
|
|
|
1476
|
-
/** Link a drive directory to a GitHub repo
|
|
1477
|
-
* scope)
|
|
1478
|
-
*
|
|
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. */
|
|
1479
1643
|
async createRepoLink(
|
|
1480
1644
|
input: CreateDriveRepoLinkInput, opts?: { factorySlug?: string },
|
|
1481
|
-
): Promise<DriveRepoLink> {
|
|
1645
|
+
): Promise<DriveRepoLink[]> {
|
|
1482
1646
|
const slug = opts?.factorySlug ?? DEFAULT_FACTORY;
|
|
1483
|
-
const body = await this.fetch<{
|
|
1647
|
+
const body = await this.fetch<{ links: DriveRepoLink[] }>(
|
|
1484
1648
|
`/api/v1/factories/${encodeURIComponent(slug)}/repo-links`,
|
|
1485
1649
|
{ method: "POST", body: input });
|
|
1486
|
-
return body.
|
|
1650
|
+
return body.links;
|
|
1487
1651
|
}
|
|
1488
1652
|
|
|
1489
1653
|
/** Unlink (§6.3: the prefix's files and history stay on the drive). */
|
|
@@ -1533,10 +1697,17 @@ export class AgentComposeClient {
|
|
|
1533
1697
|
}
|
|
1534
1698
|
|
|
1535
1699
|
/** List API keys on the caller's team (metadata only — plaintext keys are
|
|
1536
|
-
* never returned).
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
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 };
|
|
1540
1711
|
}
|
|
1541
1712
|
|
|
1542
1713
|
// ── Usage ──────────────────────────────────────────────────────────────────
|
package/src/display.ts
CHANGED
|
@@ -101,8 +101,32 @@ export const TABLE_MAX_COLUMNS = 12;
|
|
|
101
101
|
export const TABLE_MAX_ROWS = 100;
|
|
102
102
|
export const TABLE_CELL_MAX_CHARS = 200;
|
|
103
103
|
|
|
104
|
-
/** Drive-path bound for
|
|
105
|
-
const
|
|
104
|
+
/** Drive-path bound for promoted/marker paths (mirrors the directive cap). */
|
|
105
|
+
export const DRIVE_PATH_MAX_CHARS = 512;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Whether a string is a PLAUSIBLE factory-drive path — the gate every
|
|
109
|
+
* promoted document/image/preview/diff path and document/image marker
|
|
110
|
+
* passes before it can become a card. A malformed agent command can hand
|
|
111
|
+
* the promoter shell fragments instead of a path (live failure: a partial
|
|
112
|
+
* `agentc display document` call promoted the redirect word `2>&1` — and a
|
|
113
|
+
* bare `/` — into "Showed document" cards whose viewer link was dead), so
|
|
114
|
+
* anything that reads as a shell operator, flag, or empty reference is not
|
|
115
|
+
* a path: reject whitespace-only, over-long, flag-shaped (leading `-`),
|
|
116
|
+
* shell operators / substitution (`|`, `<`, `>`, `;`, backticks, `$(`,
|
|
117
|
+
* which covers `2>&1`), control characters, backslashes, bare/duplicate
|
|
118
|
+
* slashes, and `.`/`..` segments (the server rejects those anyway).
|
|
119
|
+
*/
|
|
120
|
+
export function isPlausibleDrivePath(raw: string): boolean {
|
|
121
|
+
if (raw.trim().length === 0 || raw.length > DRIVE_PATH_MAX_CHARS) return false;
|
|
122
|
+
if (raw.startsWith("-")) return false;
|
|
123
|
+
// eslint-disable-next-line no-control-regex
|
|
124
|
+
if (/[\u0000-\u001f\u007f\\]/.test(raw)) return false;
|
|
125
|
+
if (/[|<>;`]|\$\(/.test(raw)) return false;
|
|
126
|
+
const trimmed = raw.replace(/^\/+|\/+$/g, "");
|
|
127
|
+
if (trimmed.length === 0) return false; // bare "/" (or only slashes)
|
|
128
|
+
return trimmed.split("/").every((seg) => seg !== "" && seg !== "." && seg !== "..");
|
|
129
|
+
}
|
|
106
130
|
|
|
107
131
|
function clampCellString(s: string): string {
|
|
108
132
|
return s.length > TABLE_CELL_MAX_CHARS ? `${s.slice(0, TABLE_CELL_MAX_CHARS - 1)}…` : s;
|
|
@@ -288,7 +312,7 @@ export function parseDisplayMarker(line: string): DisplayMarker | null {
|
|
|
288
312
|
}
|
|
289
313
|
case "document": {
|
|
290
314
|
const path = str("path");
|
|
291
|
-
if (!path) return null;
|
|
315
|
+
if (!path || !isPlausibleDrivePath(path)) return null;
|
|
292
316
|
return {
|
|
293
317
|
kind: "document", path,
|
|
294
318
|
...(str("factorySlug") ? { factorySlug: str("factorySlug") } : {}),
|
|
@@ -301,7 +325,7 @@ export function parseDisplayMarker(line: string): DisplayMarker | null {
|
|
|
301
325
|
}
|
|
302
326
|
case "image": {
|
|
303
327
|
const path = str("path");
|
|
304
|
-
if (!path || path
|
|
328
|
+
if (!path || !isPlausibleDrivePath(path)) return null;
|
|
305
329
|
return {
|
|
306
330
|
kind: "image", path,
|
|
307
331
|
...(str("factorySlug") ? { factorySlug: str("factorySlug") } : {}),
|
|
@@ -446,6 +470,11 @@ export function detectAgentcInvocation(command: string): AgentcInvocation | null
|
|
|
446
470
|
for (let i = start + 1; i < words.length; i += 1) {
|
|
447
471
|
const w = words[i];
|
|
448
472
|
if (SHELL_STOPPERS.has(w)) break;
|
|
473
|
+
// A merged-stream redirect (`2>&1`) is tolerated by the control-word
|
|
474
|
+
// gate above, but it is shell syntax, never an argument — collecting
|
|
475
|
+
// it turned a truncated `agentc display document 2>&1` into a "Showed
|
|
476
|
+
// document" card whose path was the redirect itself (live failure).
|
|
477
|
+
if (/^\d*>&\d+$/.test(w)) continue;
|
|
449
478
|
if (w.startsWith("--")) {
|
|
450
479
|
if (VALUE_FLAGS.has(w) && i + 1 < words.length && !SHELL_STOPPERS.has(words[i + 1])) {
|
|
451
480
|
flags.set(w, words[i + 1]);
|
|
@@ -458,6 +487,12 @@ export function detectAgentcInvocation(command: string): AgentcInvocation | null
|
|
|
458
487
|
const factorySlug = flags.get("--factory");
|
|
459
488
|
const withSlug = factorySlug ? { factorySlug } : {};
|
|
460
489
|
const uuid = (v: string | undefined): string | null => (v && UUID_RE.test(v) ? v : null);
|
|
490
|
+
// Path-taking verbs promote only PLAUSIBLE drive paths — a shell
|
|
491
|
+
// fragment or bare `/` from a malformed command is unparseable-argument
|
|
492
|
+
// territory (the pair persists raw, the CLI's own error is the agent's
|
|
493
|
+
// feedback), never a document card.
|
|
494
|
+
const drivePath = (v: string | undefined): string | null =>
|
|
495
|
+
(v && isPlausibleDrivePath(v) ? v : null);
|
|
461
496
|
|
|
462
497
|
if (args[0] === "display") {
|
|
463
498
|
switch (args[1]) {
|
|
@@ -469,19 +504,28 @@ export function detectAgentcInvocation(command: string): AgentcInvocation | null
|
|
|
469
504
|
const runId = uuid(args[2]);
|
|
470
505
|
return runId ? { verb: "display-changes", runId, ...withSlug } : null;
|
|
471
506
|
}
|
|
472
|
-
case "document":
|
|
473
|
-
|
|
507
|
+
case "document": {
|
|
508
|
+
const path = drivePath(args[2]);
|
|
509
|
+
return path ? { verb: "display-document", path, ...withSlug } : null;
|
|
510
|
+
}
|
|
474
511
|
case "plan":
|
|
475
512
|
return { verb: "display-plan" };
|
|
476
|
-
case "image":
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
513
|
+
case "image": {
|
|
514
|
+
const path = drivePath(args[2]);
|
|
515
|
+
return path ? { verb: "display-image", path, ...withSlug } : null;
|
|
516
|
+
}
|
|
517
|
+
case "preview": {
|
|
518
|
+
const path = drivePath(args[2]);
|
|
519
|
+
return path ? { verb: "display-preview", path } : null;
|
|
520
|
+
}
|
|
480
521
|
case "table": {
|
|
481
522
|
const file = flags.get("--file");
|
|
482
523
|
const hasData = flags.has("--data");
|
|
483
524
|
// Exactly one source; both or neither is an unpromotable combination.
|
|
484
|
-
if (file && !hasData)
|
|
525
|
+
if (file && !hasData) {
|
|
526
|
+
const path = drivePath(file);
|
|
527
|
+
return path ? { verb: "display-table", source: "file", path } : null;
|
|
528
|
+
}
|
|
485
529
|
if (hasData && !file) return { verb: "display-table", source: "inline" };
|
|
486
530
|
return null;
|
|
487
531
|
}
|
|
@@ -494,9 +538,10 @@ export function detectAgentcInvocation(command: string): AgentcInvocation | null
|
|
|
494
538
|
case "diff": {
|
|
495
539
|
const from = flags.get("--from");
|
|
496
540
|
const to = flags.get("--to");
|
|
497
|
-
|
|
541
|
+
const path = drivePath(args[2]);
|
|
542
|
+
if (!path || !from || !to) return null;
|
|
498
543
|
if (!REVISION_SELECTOR_RE.test(from) || !REVISION_SELECTOR_RE.test(to)) return null;
|
|
499
|
-
return { verb: "display-diff", path
|
|
544
|
+
return { verb: "display-diff", path, from, to };
|
|
500
545
|
}
|
|
501
546
|
case "ask": {
|
|
502
547
|
const prompt = flags.get("--prompt");
|
|
@@ -524,8 +569,9 @@ export function detectAgentcInvocation(command: string): AgentcInvocation | null
|
|
|
524
569
|
const runId = uuid(args[2]);
|
|
525
570
|
return runId ? { verb: "run-get", runId } : null;
|
|
526
571
|
}
|
|
527
|
-
if (args[0] === "files" && args[1] === "read"
|
|
528
|
-
|
|
572
|
+
if (args[0] === "files" && args[1] === "read") {
|
|
573
|
+
const path = drivePath(args[2]);
|
|
574
|
+
return path ? { verb: "files-read", path, ...withSlug } : null;
|
|
529
575
|
}
|
|
530
576
|
return null;
|
|
531
577
|
}
|
package/src/index.ts
CHANGED
|
@@ -113,7 +113,8 @@ export type {
|
|
|
113
113
|
export { AgentComposeClient } from "./client.js";
|
|
114
114
|
export type {
|
|
115
115
|
RegisterResult, RegisterWorkflowInput, RuntimeSourceInput, TemplateSourceRef,
|
|
116
|
-
InvokeWorkflowOptions, InvokeAndWaitOptions, InvokeResult,
|
|
116
|
+
InvokeWorkflowOptions, InvokeAndWaitOptions, InvokeResult, FundingChoice,
|
|
117
|
+
InlineWorkflowPayload, InvokeInlineOptions, InvokeInlineAndWaitOptions,
|
|
117
118
|
ListSnapshotsOptions, TemplateRow, ListTemplatesOptions,
|
|
118
119
|
CreateFactoryInput, UpdateFactoryInput,
|
|
119
120
|
SecretOptions, SetSecretResult, SecretListEntry,
|
|
@@ -124,7 +125,7 @@ export type {
|
|
|
124
125
|
RunListEntry, ListRunsOptions, RunDetail,
|
|
125
126
|
FundingLane, RunFundingStamp, RunFundingUsageRow, RunFundingResponse,
|
|
126
127
|
RegisteredRuntime, RunState, RunStatus, FactoryRow, SnapshotListEntry, SnapshotListResponse,
|
|
127
|
-
ApiKey, ApiKeyCreated,
|
|
128
|
+
ApiKey, ApiKeyCreated, ListApiKeysOptions, ApiKeyPage,
|
|
128
129
|
UsageRollupRow, UsageResponse,
|
|
129
130
|
CancelRunResponse,
|
|
130
131
|
RequestAgentPauseOptions, RequestAgentPauseResponse,
|
|
@@ -134,13 +135,17 @@ export type {
|
|
|
134
135
|
ConversationMessagePart, ConversationRow, ConversationMessageRow,
|
|
135
136
|
ConversationsPage, ConversationDetail, ConversationThread,
|
|
136
137
|
ConversationPageContext, SendConversationMessageInput, SendConversationMessageResult,
|
|
138
|
+
UnnotifiedMention,
|
|
137
139
|
ConversationTurnState, ConversationPresenceSnapshot, StreamConversationOptions, AgentListRow,
|
|
138
140
|
CreateCloudSessionInput, CloudSessionCreated,
|
|
139
141
|
// Cloud-session developer surface (ADR-0052)
|
|
140
|
-
SessionPreview, PreviewOpened, OpenPreviewInput, SessionForked,
|
|
142
|
+
SessionPreview, PreviewOpened, OpenPreviewInput, SessionForked, BackgroundWorkHeld,
|
|
141
143
|
// Session branch proposals (ADR-0053)
|
|
142
|
-
SessionFileChange, SessionChangeSet,
|
|
143
|
-
|
|
144
|
+
SessionFileChange, SessionChangeSet, SessionChangeStats, SessionDiffReviewBound, ReviewSuggestion,
|
|
145
|
+
ReviewSuggestionDecision, ReviewSuggestionDecisions,
|
|
146
|
+
SessionMergeReportDetail, SessionMergeReport, SessionMergeGated, SessionDiscardReport,
|
|
147
|
+
// User drive mounts (`agentc files mount` on a human's own machine)
|
|
148
|
+
DriveMountSession, CreateDriveMountSessionInput,
|
|
144
149
|
// Channel-attached sessions (ADR-0057)
|
|
145
150
|
ChannelSessionStatus, ChannelSessionRow, ChannelSessionsResponse, SessionChannelMessagePosted,
|
|
146
151
|
FactoryFileSearchRow, FactoryFolderSearchRow, SearchFactoryFilesOptions, FactoryFileSearchResult, PublicFileLinkState,
|
|
@@ -206,6 +211,10 @@ export {
|
|
|
206
211
|
BUNDLER_VERSION,
|
|
207
212
|
WorkflowSourceValidationError,
|
|
208
213
|
assertDefaultExportIsDefineWorkflow,
|
|
214
|
+
SDK_PACKAGE,
|
|
215
|
+
SDK_SPECIFIER_ALIASES,
|
|
216
|
+
resolveSdkAlias,
|
|
217
|
+
explainBundleFailure,
|
|
209
218
|
} from "./utils/bundler.js";
|
|
210
219
|
export type { BundledWorkflow, WorkflowManifest } from "./utils/bundler.js";
|
|
211
220
|
|
|
@@ -232,7 +241,7 @@ export type { GatewayModelId } from "ai";
|
|
|
232
241
|
// sandbox and stream-parse its JSONL. No heavy npm deps (the CLI lives in the
|
|
233
242
|
// sandbox image), so these are root-exported like claudeRuntime.
|
|
234
243
|
export { createCodexRuntime, codexSpec } from "./runtimes/codex.js";
|
|
235
|
-
export type { CodexRuntimeConfig } from "./runtimes/codex.js";
|
|
244
|
+
export type { CodexRuntimeConfig, CodexReasoningEffort } from "./runtimes/codex.js";
|
|
236
245
|
export { default as codexRuntime } from "./runtimes/codex.js";
|
|
237
246
|
// Reasoning-effort level a CLI runtime turn may carry (claude-code / codex).
|
|
238
247
|
export type { CliReasoningEffort } from "./runtimes/_cli-agent.js";
|
|
@@ -255,7 +264,7 @@ export { default as droidRuntime } from "./runtimes/droid.js";
|
|
|
255
264
|
// adapter) — the cloud-hostable counterpart of `claudeRuntime` (claude.ts,
|
|
256
265
|
// which drives the Agent SDK in the calling process and so can never run a
|
|
257
266
|
// server-driven cloud-session turn).
|
|
258
|
-
export { createClaudeCodeRuntime, claudeCodeSpec, CLAUDE_CODE_ACP_ADAPTER,
|
|
267
|
+
export { createClaudeCodeRuntime, claudeCodeSpec, CLAUDE_CODE_ACP_ADAPTER, CLAUDE_CODE_EFFORT_LEVELS } from "./runtimes/claude-code.js";
|
|
259
268
|
export type { ClaudeCodeRuntimeConfig } from "./runtimes/claude-code.js";
|
|
260
269
|
export { default as claudeCodeRuntime } from "./runtimes/claude-code.js";
|
|
261
270
|
|
|
@@ -271,8 +280,11 @@ export { createSandbox, reconnectSandbox, killAllSandboxes, killSandboxById,
|
|
|
271
280
|
getSandboxQuotas, listOwnedSandboxes, deleteSandboxSnapshot, snapshotResolves,
|
|
272
281
|
makeSandboxProvider, makeDesktopSandboxProvider,
|
|
273
282
|
parseSseExecStream, AGENT_COMPOSE_TAG,
|
|
274
|
-
SANDBOX_VCPUS, DEFAULT_SANDBOX_SIZE,
|
|
275
|
-
|
|
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,
|
|
276
288
|
isPlatformE2bTemplateAlias,
|
|
277
289
|
// ADR-0038 "Computer" — the per-member persistent desktop machine template.
|
|
278
290
|
E2B_DEVBOX_TEMPLATE, E2B_DEVBOX_SPEC, E2B_DEVBOX_RECIPE_VERSION,
|
|
@@ -386,6 +398,7 @@ export {
|
|
|
386
398
|
TABLE_MAX_COLUMNS, TABLE_MAX_ROWS, TABLE_CELL_MAX_CHARS,
|
|
387
399
|
CHART_MAX_SERIES, CHART_MAX_POINTS_PER_SERIES, CHART_LABEL_MAX_CHARS,
|
|
388
400
|
ASK_PROMPT_MAX_CHARS, ASK_MAX_OPTIONS, ASK_OPTION_ID_MAX_CHARS, ASK_OPTION_LABEL_MAX_CHARS,
|
|
401
|
+
DRIVE_PATH_MAX_CHARS, isPlausibleDrivePath,
|
|
389
402
|
serializeDisplayMarker, parseDisplayMarker, findDisplayMarker, clampPlanEntries,
|
|
390
403
|
clampTableData, clampChartSeries, clampChartAxisLabel, clampAskOptions,
|
|
391
404
|
detectAgentcInvocation, shellWords, createDisplayPromoter,
|