@cursor/july 0.1.94 → 0.1.95
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/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +14 -2
- package/dist/channels/github/types.d.ts +18 -3
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/channels/origin/origin-channel.d.ts.map +1 -1
- package/dist/channels/origin/origin-channel.js +11 -3
- package/dist/channels/origin/origin-webhook.d.ts +11 -1
- package/dist/channels/origin/origin-webhook.d.ts.map +1 -1
- package/dist/channels/origin/origin-webhook.js +23 -3
- package/dist/channels/origin/types.d.ts +8 -0
- package/dist/channels/origin/types.d.ts.map +1 -1
- package/dist/channels/slack/pr-ref.d.ts +7 -1
- package/dist/channels/slack/pr-ref.d.ts.map +1 -1
- package/dist/channels/slack/pr-ref.js +42 -23
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.CFDEas4I.js → app.BBj0klBO.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.oqPawjiD.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.B1IIYpYS.js → VPLocalSearchBox.CUEXpL78.js} +1 -1
- package/dist/docs/assets/chunks/{theme.Ct4NSiLm.js → theme.DabmQnia.js} +2 -2
- package/dist/docs/assets/index.md.BoHaMdeZ.js +5 -0
- package/dist/docs/assets/{index.md.DRakGHFe.lean.js → index.md.BoHaMdeZ.lean.js} +1 -1
- package/dist/docs/assets/{reference_cli.md.Cw6_ICYG.js → reference_cli.md.BsMOxDvh.js} +3 -2
- package/dist/docs/assets/{reference_cli.md.Cw6_ICYG.lean.js → reference_cli.md.BsMOxDvh.lean.js} +1 -1
- package/dist/docs/assets/{reference_skills.md.8son6Hjm.js → reference_skills.md.BFW9retM.js} +3 -3
- package/dist/docs/assets/{templates_agentic-owners.md.DqtPdm6f.js → templates_agentic-owners.md.BZSH4N9z.js} +1 -1
- package/dist/docs/assets/{templates_demo.md.DhFcWN6j.js → templates_demo.md.BeQX9V3H.js} +1 -1
- package/dist/docs/assets/{templates_pr-autofixer.md.DU7dQpor.js → templates_pr-autofixer.md.x5zl6-GT.js} +2 -2
- package/dist/docs/assets/{templates_pr-autofixer.md.DU7dQpor.lean.js → templates_pr-autofixer.md.x5zl6-GT.lean.js} +1 -1
- package/dist/docs/assets/templates_security-help.md.C3Ny_Qr2.js +4 -0
- package/dist/docs/assets/templates_security-help.md.C3Ny_Qr2.lean.js +1 -0
- package/dist/docs/assets/{templates_security-reviewer.md.CTa7u_l1.js → templates_security-reviewer.md.lshxbCLK.js} +2 -2
- package/dist/docs/assets/{templates_security-reviewer.md.CTa7u_l1.lean.js → templates_security-reviewer.md.lshxbCLK.lean.js} +1 -1
- package/dist/docs/assets/{templates_triage.md.CVlpctKS.js → templates_triage.md.Co4UNzkZ.js} +3 -3
- package/dist/docs/assets/{templates_triage.md.CVlpctKS.lean.js → templates_triage.md.Co4UNzkZ.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +4 -4
- package/dist/docs/concepts.html +4 -4
- package/dist/docs/deployment.html +4 -4
- package/dist/docs/evals.html +4 -4
- package/dist/docs/guides/agent-to-agent.html +4 -4
- package/dist/docs/guides/cloud-runtime.html +4 -4
- package/dist/docs/guides/convert-automation.html +4 -4
- package/dist/docs/guides/github.html +4 -4
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/mcp-oauth.html +4 -4
- package/dist/docs/guides/opentelemetry.html +4 -4
- package/dist/docs/guides/slack.html +4 -4
- package/dist/docs/guides/webhooks.html +4 -4
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +4 -4
- package/dist/docs/index.html +6 -6
- package/dist/docs/index.md +3 -1
- package/dist/docs/llms-full.txt +90 -26
- package/dist/docs/llms.txt +1 -0
- package/dist/docs/quickstart.html +4 -4
- package/dist/docs/reference/agent-config.html +4 -4
- package/dist/docs/reference/artifacts.html +4 -4
- package/dist/docs/reference/channels.html +4 -4
- package/dist/docs/reference/cli.html +7 -6
- package/dist/docs/reference/cli.md +3 -1
- package/dist/docs/reference/connections.html +4 -4
- package/dist/docs/reference/hooks.html +4 -4
- package/dist/docs/reference/http-api.html +4 -4
- package/dist/docs/reference/instructions.html +4 -4
- package/dist/docs/reference/playground.html +4 -4
- package/dist/docs/reference/project-layout.html +4 -4
- package/dist/docs/reference/prompt.html +4 -4
- package/dist/docs/reference/schedules.html +4 -4
- package/dist/docs/reference/sessions.html +4 -4
- package/dist/docs/reference/skills.html +7 -7
- package/dist/docs/reference/subagents.html +4 -4
- package/dist/docs/reference/tools.html +4 -4
- package/dist/docs/scaffolding-agents.html +4 -4
- package/dist/docs/storage.html +4 -4
- package/dist/docs/templates/agentic-owners.html +7 -7
- package/dist/docs/templates/agentic-owners.md +1 -1
- package/dist/docs/templates/demo.html +6 -6
- package/dist/docs/templates/demo.md +3 -2
- package/dist/docs/templates/pr-autofixer.html +6 -6
- package/dist/docs/templates/pr-autofixer.md +7 -13
- package/dist/docs/templates/security-help.html +30 -0
- package/dist/docs/templates/security-help.md +65 -0
- package/dist/docs/templates/security-reviewer.html +6 -6
- package/dist/docs/templates/security-reviewer.md +1 -2
- package/dist/docs/templates/triage.html +7 -7
- package/dist/docs/templates/triage.md +2 -6
- package/dist/docs/troubleshooting.html +4 -4
- package/dist/files-backends/cursor-hosted.d.ts +6 -2
- package/dist/files-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/files-backends/cursor-hosted.js +2 -2
- package/dist/files.d.ts +2 -0
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +5 -0
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +135 -6
- package/dist/internal/conversation-mirror.d.ts +82 -0
- package/dist/internal/conversation-mirror.d.ts.map +1 -0
- package/dist/internal/conversation-mirror.js +251 -0
- package/dist/internal/deferred-channel-session.d.ts +20 -0
- package/dist/internal/deferred-channel-session.d.ts.map +1 -0
- package/dist/internal/deferred-channel-session.js +62 -0
- package/dist/internal/deploy-client.d.ts +13 -1
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +11 -1
- package/dist/internal/deploy-manifest.d.ts.map +1 -1
- package/dist/internal/deploy-manifest.js +11 -5
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +22 -2
- package/dist/internal/framework-storage-selection.d.ts +32 -16
- package/dist/internal/framework-storage-selection.d.ts.map +1 -1
- package/dist/internal/framework-storage-selection.js +51 -17
- package/dist/internal/hosted-admission-context.d.ts +20 -0
- package/dist/internal/hosted-admission-context.d.ts.map +1 -0
- package/dist/internal/hosted-admission-context.js +31 -0
- package/dist/internal/hosted-delivery-protocol.d.ts +5 -0
- package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
- package/dist/internal/hosted-delivery-protocol.js +33 -1
- package/dist/internal/hosted-delivery.d.ts +4 -2
- package/dist/internal/hosted-delivery.d.ts.map +1 -1
- package/dist/internal/hosted-delivery.js +75 -35
- package/dist/internal/hosted-managed-automation.d.ts +30 -0
- package/dist/internal/hosted-managed-automation.d.ts.map +1 -0
- package/dist/internal/hosted-managed-automation.js +58 -0
- package/dist/internal/review-comments.d.ts.map +1 -1
- package/dist/internal/review-comments.js +10 -98
- package/dist/internal/scm/pr-url.d.ts +14 -0
- package/dist/internal/scm/pr-url.d.ts.map +1 -0
- package/dist/internal/scm/pr-url.js +65 -0
- package/dist/internal/sdk-runner.d.ts +14 -0
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +41 -2
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +10 -2
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +9 -0
- package/dist/playground/assets/index-CF7hUDnQ.css +1 -0
- package/dist/playground/assets/{index-B3JCyigB.js → index-CZA1uEWA.js} +48 -48
- package/dist/playground/index.html +2 -2
- package/dist/storage-backends/cursor-hosted.d.ts +7 -2
- package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/storage-backends/cursor-hosted.js +2 -2
- package/docs/README.md +3 -1
- package/docs/reference/cli.md +3 -1
- package/docs/templates/agentic-owners.md +1 -1
- package/docs/templates/demo.md +3 -2
- package/docs/templates/pr-autofixer.md +7 -13
- package/docs/templates/security-help.md +70 -0
- package/docs/templates/security-reviewer.md +1 -2
- package/docs/templates/triage.md +2 -6
- package/package.json +1 -1
- package/src/channels/github/github-channel.ts +23 -8
- package/src/channels/github/types.ts +19 -2
- package/src/channels/origin/origin-channel.ts +13 -1
- package/src/channels/origin/origin-webhook.ts +27 -3
- package/src/channels/origin/types.ts +8 -0
- package/src/channels/slack/pr-ref.ts +56 -25
- package/src/files-backends/cursor-hosted.ts +9 -3
- package/src/files.ts +11 -0
- package/src/internal/cli-deploy.ts +171 -7
- package/src/internal/conversation-mirror.ts +330 -0
- package/src/internal/deferred-channel-session.ts +61 -0
- package/src/internal/deploy-client.ts +24 -1
- package/src/internal/deploy-manifest.ts +10 -5
- package/src/internal/discovery.ts +25 -2
- package/src/internal/framework-storage-selection.ts +61 -19
- package/src/internal/hosted-admission-context.ts +37 -0
- package/src/internal/hosted-delivery-protocol.ts +44 -1
- package/src/internal/hosted-delivery.ts +95 -40
- package/src/internal/hosted-managed-automation.ts +72 -0
- package/src/internal/review-comments.ts +10 -113
- package/src/internal/scm/pr-url.ts +95 -0
- package/src/internal/sdk-runner.ts +57 -2
- package/src/internal/server.ts +10 -0
- package/src/internal/session-engine.ts +12 -0
- package/src/storage-backends/cursor-hosted.ts +10 -3
- package/templates/agentic-owners/README.md +1 -1
- package/templates/agentic-owners/agent/agent.ts +0 -10
- package/templates/agentic-owners/agent/channels/github.ts +5 -14
- package/templates/agentic-owners/agent/lib/config.ts +0 -8
- package/templates/agentic-owners/agent/lib/review.ts +2 -15
- package/templates/agentic-owners/agent/tools/record_review.ts +2 -4
- package/templates/demo/agent/agent.ts +0 -10
- package/templates/pr-autofixer/README.md +0 -2
- package/templates/pr-autofixer/agent/agent.ts +0 -11
- package/templates/pr-autofixer/agent/channels/slack.ts +0 -7
- package/templates/pr-autofixer/agent/lib/pr-state.ts +5 -17
- package/templates/pr-autofixer/agent/lib/repos.ts +0 -1
- package/templates/security-help/README.md +2 -2
- package/templates/security-help/agent/agent.ts +1 -2
- package/templates/security-help/agent/channels/slack.ts +0 -3
- package/templates/security-help/agent/instructions.md +9 -10
- package/templates/security-help/agent/skills/access-request.md +1 -1
- package/templates/security-help/agent/skills/faq.md +31 -0
- package/templates/security-help/agent/skills/security-playbooks.md +1 -1
- package/templates/security-help/package.json +1 -2
- package/templates/security-reviewer/agent/agent.ts +0 -10
- package/templates/triage/agent/agent.ts +0 -10
- package/templates/triage/agent/channels/queue.ts +1 -1
- package/templates/triage/agent/channels/webhook.ts +1 -3
- package/dist/docs/assets/chunks/@localSearchIndexroot.DU3U2Ij2.js +0 -1
- package/dist/docs/assets/index.md.DRakGHFe.js +0 -5
- package/dist/playground/assets/index-D9N7-q97.css +0 -1
- package/templates/security-help/agent/knowledge/faq/approvals.md +0 -5
- package/templates/security-help/agent/knowledge/faq/channels.md +0 -6
- package/templates/security-help/agent/knowledge/faq/phishing.md +0 -10
- package/templates/security-help/agent/skills/security-first-pass.md +0 -15
- /package/dist/docs/assets/{reference_skills.md.8son6Hjm.lean.js → reference_skills.md.BFW9retM.lean.js} +0 -0
- /package/dist/docs/assets/{templates_agentic-owners.md.DqtPdm6f.lean.js → templates_agentic-owners.md.BZSH4N9z.lean.js} +0 -0
- /package/dist/docs/assets/{templates_demo.md.DhFcWN6j.lean.js → templates_demo.md.BeQX9V3H.lean.js} +0 -0
|
@@ -27,6 +27,7 @@ import { authorizeCursorAccountConnectors } from "./cursor-account-mcp-auth.js";
|
|
|
27
27
|
import { readDeployTemplate } from "./cursor-agent-template.js";
|
|
28
28
|
import {
|
|
29
29
|
type AgentkitDeployAccepted,
|
|
30
|
+
type AgentkitDeployComponent,
|
|
30
31
|
type AgentkitDeployStatus,
|
|
31
32
|
type AgentkitStopAccepted,
|
|
32
33
|
AgentServeDeployClient,
|
|
@@ -294,10 +295,35 @@ export async function cmdDeploy(options: DeployCliOptions): Promise<number> {
|
|
|
294
295
|
return targets;
|
|
295
296
|
}
|
|
296
297
|
|
|
298
|
+
if (targets.length === 1) {
|
|
299
|
+
return deployOne(ctx, options, targets[0]!);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Multi-agent parent: when every child declares `architecture: "v2"`,
|
|
303
|
+
// the v2 control plane models sibling agents as release components of
|
|
304
|
+
// ONE application, so deploy them together (one app, one component per
|
|
305
|
+
// child) instead of minting a separate application per child. Mixed and
|
|
306
|
+
// v1 parents keep the per-child sequential deploys.
|
|
307
|
+
const manifests: DeployManifest[] = [];
|
|
308
|
+
for (const target of targets) {
|
|
309
|
+
manifests.push(await readDeployManifest(target.dir));
|
|
310
|
+
}
|
|
311
|
+
if (manifests.every((manifest) => manifest.architecture === "v2")) {
|
|
312
|
+
return deployAgentsdkApplication(ctx, options, targets);
|
|
313
|
+
}
|
|
314
|
+
if (
|
|
315
|
+
!ctx.json &&
|
|
316
|
+
manifests.some((manifest) => manifest.architecture === "v2")
|
|
317
|
+
) {
|
|
318
|
+
ctx.err(
|
|
319
|
+
`warning: agents under this parent mix architectures; deploying each separately (v2 children become standalone single-component applications).\n`
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
|
|
297
323
|
// --path is per-agent (relative to the repo root). When deploying several
|
|
298
324
|
// agents, ignore a shared --path so each target can infer its own path.
|
|
299
325
|
let deployOptions = options;
|
|
300
|
-
if (
|
|
326
|
+
if (options.path !== undefined) {
|
|
301
327
|
if (!ctx.json) {
|
|
302
328
|
ctx.err(
|
|
303
329
|
`warning: ignoring --path ${JSON.stringify(options.path)} for a multi-agent deploy; each agent infers its own path from git.\n`
|
|
@@ -306,10 +332,6 @@ export async function cmdDeploy(options: DeployCliOptions): Promise<number> {
|
|
|
306
332
|
deployOptions = { ...options, path: undefined };
|
|
307
333
|
}
|
|
308
334
|
|
|
309
|
-
if (targets.length === 1) {
|
|
310
|
-
return deployOne(ctx, deployOptions, targets[0]!);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
335
|
// Multi-agent batch: deploy sequentially; --json emits an array.
|
|
314
336
|
const jsonResults: unknown[] = [];
|
|
315
337
|
let failures = 0;
|
|
@@ -477,6 +499,19 @@ async function resolveDeployTargets(
|
|
|
477
499
|
return [match];
|
|
478
500
|
}
|
|
479
501
|
|
|
502
|
+
// An all-v2 parent deploys as ONE application (siblings are components
|
|
503
|
+
// of a single app on the v2 control plane), so "deploy the parent" is
|
|
504
|
+
// unambiguous and needs no picker or --all. A child that is v1 — or
|
|
505
|
+
// fails to load — keeps the interactive picker below.
|
|
506
|
+
if (await allChildrenAreV2(children)) {
|
|
507
|
+
if (!ctx.json) {
|
|
508
|
+
ctx.err(
|
|
509
|
+
`Deploying all ${children.length} v2 agents under ${basename(dir)} as one application (pass --slug <name> to deploy a single agent standalone).\n`
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
return [...children];
|
|
513
|
+
}
|
|
514
|
+
|
|
480
515
|
const chooser =
|
|
481
516
|
options.chooseTargets ??
|
|
482
517
|
((agents) =>
|
|
@@ -508,6 +543,27 @@ function formatAgentList(agents: readonly DeployTarget[]): string {
|
|
|
508
543
|
return agents.map((agent, i) => ` ${i + 1}) ${agent.slug}\n`).join("");
|
|
509
544
|
}
|
|
510
545
|
|
|
546
|
+
/**
|
|
547
|
+
* True when every child agent project loads and declares
|
|
548
|
+
* `architecture: "v2"`. Broken projects count as not-v2 so target
|
|
549
|
+
* resolution falls back to the picker instead of failing the whole deploy
|
|
550
|
+
* on a child the user might not even select.
|
|
551
|
+
*/
|
|
552
|
+
async function allChildrenAreV2(
|
|
553
|
+
children: readonly DeployTarget[]
|
|
554
|
+
): Promise<boolean> {
|
|
555
|
+
for (const child of children) {
|
|
556
|
+
try {
|
|
557
|
+
if ((await readDeployManifest(child.dir)).architecture !== "v2") {
|
|
558
|
+
return false;
|
|
559
|
+
}
|
|
560
|
+
} catch {
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
return true;
|
|
565
|
+
}
|
|
566
|
+
|
|
511
567
|
/**
|
|
512
568
|
* TTY picker for multi-agent parents. Non-interactive callers get `[]`
|
|
513
569
|
* so {@link resolveDeployTargets} can print the --slug/--all hint.
|
|
@@ -777,6 +833,100 @@ async function deployOne(
|
|
|
777
833
|
return 0;
|
|
778
834
|
}
|
|
779
835
|
|
|
836
|
+
/**
|
|
837
|
+
* Multi-agent parent whose children all declare `architecture: "v2"`:
|
|
838
|
+
* deploy ONE application whose release carries a component per child. The
|
|
839
|
+
* application slug derives from the parent directory name; each component
|
|
840
|
+
* key is the child's slug and its path is the child directory relative to
|
|
841
|
+
* the repo root (a shared --path names the parent's directory in the repo).
|
|
842
|
+
*/
|
|
843
|
+
async function deployAgentsdkApplication(
|
|
844
|
+
ctx: DeployCliContext,
|
|
845
|
+
options: DeployCliOptions,
|
|
846
|
+
targets: readonly DeployTarget[]
|
|
847
|
+
): Promise<number> {
|
|
848
|
+
const parentDir = resolve(options.dir ?? ".");
|
|
849
|
+
const slug = slugFromDirectoryName(basename(parentDir));
|
|
850
|
+
if (slug === undefined) {
|
|
851
|
+
ctx.err(
|
|
852
|
+
`Could not derive an application slug from the parent directory name ${JSON.stringify(basename(parentDir))} — rename it (deploy slugs use lowercase letters, digits, '-' or '_').\n`
|
|
853
|
+
);
|
|
854
|
+
return 1;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
const needsGitInference =
|
|
858
|
+
options.repo === undefined ||
|
|
859
|
+
options.ref === undefined ||
|
|
860
|
+
options.path === undefined;
|
|
861
|
+
const inferred = needsGitInference
|
|
862
|
+
? await inferDeployGitSource(parentDir, {
|
|
863
|
+
...(options.git === undefined ? {} : { git: options.git }),
|
|
864
|
+
})
|
|
865
|
+
: {};
|
|
866
|
+
const gitRepoUrl = options.repo ?? inferred.gitRepoUrl;
|
|
867
|
+
const gitRef = options.ref ?? inferred.gitRef;
|
|
868
|
+
const parentPath = options.path ?? inferred.agentPath;
|
|
869
|
+
|
|
870
|
+
if (gitRepoUrl !== undefined && !gitRepoUrl.startsWith("https://")) {
|
|
871
|
+
ctx.err(
|
|
872
|
+
`Invalid --repo: ${gitRepoUrl} (expected an https:// git repository URL)\n`
|
|
873
|
+
);
|
|
874
|
+
return 1;
|
|
875
|
+
}
|
|
876
|
+
if (gitRepoUrl === undefined) {
|
|
877
|
+
ctx.err(
|
|
878
|
+
`Could not resolve an https:// git repository URL for this deploy.\n` +
|
|
879
|
+
` Pass --repo https://github.com/<owner>/<repo> (inferred from origin when possible).\n`
|
|
880
|
+
);
|
|
881
|
+
return 1;
|
|
882
|
+
}
|
|
883
|
+
for (const repo of options.cursorEventsRepos ?? []) {
|
|
884
|
+
if (!isValidCursorEventRepo(repo)) {
|
|
885
|
+
ctx.err(`Invalid --cursor-events-repo: ${repo} (expected owner/name)\n`);
|
|
886
|
+
return 1;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
const components: AgentkitDeployComponent[] = targets.map((target) => ({
|
|
891
|
+
componentKey: target.slug,
|
|
892
|
+
agentPath: [parentPath, basename(target.dir)]
|
|
893
|
+
.filter((part): part is string => part !== undefined && part !== "")
|
|
894
|
+
.join("/"),
|
|
895
|
+
}));
|
|
896
|
+
|
|
897
|
+
if (!ctx.json) {
|
|
898
|
+
ctx.err(
|
|
899
|
+
`Deploying "${slug}" as one v2 application with ${targets.length} components (slug from the parent directory name).\n` +
|
|
900
|
+
` architecture: v2\n` +
|
|
901
|
+
` source: ${gitRepoUrl}${gitRef === undefined ? "" : ` @ ${gitRef}`}\n` +
|
|
902
|
+
components
|
|
903
|
+
.map(
|
|
904
|
+
(component) =>
|
|
905
|
+
` component: ${component.componentKey} (${component.agentPath ?? "."})\n`
|
|
906
|
+
)
|
|
907
|
+
.join("")
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// Components bake sequentially on the control plane, so the default wait
|
|
912
|
+
// budget is per component; otherwise a 2+ component deploy routinely hits
|
|
913
|
+
// the client-side timeout while the server-side deploy is still fine. An
|
|
914
|
+
// explicitly passed timeout always wins.
|
|
915
|
+
const waitCtx =
|
|
916
|
+
options.timeoutMs === undefined
|
|
917
|
+
? { ...ctx, timeoutMs: ctx.timeoutMs * targets.length }
|
|
918
|
+
: ctx;
|
|
919
|
+
|
|
920
|
+
return deployOneAgentsdk(waitCtx, undefined, {
|
|
921
|
+
slug,
|
|
922
|
+
gitRepoUrl,
|
|
923
|
+
gitRef,
|
|
924
|
+
agentPath: undefined,
|
|
925
|
+
components,
|
|
926
|
+
repositories: options.cursorEventsRepos,
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
|
|
780
930
|
/**
|
|
781
931
|
* `architecture: "v2"` deploy: POST `/internal/agentkit/deployments` and
|
|
782
932
|
* poll the matching GET. The v2 accept path starts freeze bake; it does
|
|
@@ -790,10 +940,17 @@ async function deployOneAgentsdk(
|
|
|
790
940
|
gitRepoUrl: string;
|
|
791
941
|
gitRef: string | undefined;
|
|
792
942
|
agentPath: string | undefined;
|
|
943
|
+
components?: AgentkitDeployComponent[];
|
|
793
944
|
repositories: string[] | undefined;
|
|
794
945
|
}
|
|
795
946
|
): Promise<number> {
|
|
796
947
|
const { slug } = args;
|
|
948
|
+
const componentsLine =
|
|
949
|
+
args.components === undefined
|
|
950
|
+
? ""
|
|
951
|
+
: ` components: ${args.components
|
|
952
|
+
.map((component) => component.componentKey)
|
|
953
|
+
.join(", ")}\n`;
|
|
797
954
|
let accepted: AgentkitDeployAccepted;
|
|
798
955
|
try {
|
|
799
956
|
accepted = await ctx.client.deployAgentkit({
|
|
@@ -802,6 +959,7 @@ async function deployOneAgentsdk(
|
|
|
802
959
|
gitRepoUrl: args.gitRepoUrl,
|
|
803
960
|
gitRef: args.gitRef,
|
|
804
961
|
agentPath: args.agentPath,
|
|
962
|
+
components: args.components,
|
|
805
963
|
repositories: args.repositories,
|
|
806
964
|
});
|
|
807
965
|
} catch (error) {
|
|
@@ -817,7 +975,8 @@ async function deployOneAgentsdk(
|
|
|
817
975
|
ctx.out(
|
|
818
976
|
`Deploy accepted: ${slug} (team ${ctx.teamId}, v2)\n` +
|
|
819
977
|
` application: ${accepted.applicationId}\n` +
|
|
820
|
-
` release: ${accepted.releaseId}\n`
|
|
978
|
+
` release: ${accepted.releaseId}\n` +
|
|
979
|
+
componentsLine
|
|
821
980
|
);
|
|
822
981
|
}
|
|
823
982
|
return 0;
|
|
@@ -851,7 +1010,12 @@ async function deployOneAgentsdk(
|
|
|
851
1010
|
` status: running\n` +
|
|
852
1011
|
` architecture: v2\n` +
|
|
853
1012
|
` application: ${detail.applicationId}\n` +
|
|
854
|
-
` release: ${detail.releaseId}\n`
|
|
1013
|
+
` release: ${detail.releaseId}\n` +
|
|
1014
|
+
(args.components === undefined
|
|
1015
|
+
? ""
|
|
1016
|
+
: ` components: ${args.components
|
|
1017
|
+
.map((component) => component.componentKey)
|
|
1018
|
+
.join(", ")}\n`)
|
|
855
1019
|
);
|
|
856
1020
|
return 0;
|
|
857
1021
|
}
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable mirror for the SDK runner's conversation store.
|
|
3
|
+
*
|
|
4
|
+
* The Cursor SDK keeps each local conversation in SQLite under the engine's
|
|
5
|
+
* state root (`runner/index.db` + `runner/agents/agent-<hash>/store.db`).
|
|
6
|
+
* That disk dies with the pod: on architecture v2, session **records** and
|
|
7
|
+
* event chunks survive replacement through the control-plane storage mirror
|
|
8
|
+
* (framework-storage-selection.ts), but the conversation itself was never
|
|
9
|
+
* hydrated anywhere — a replacement pod resumed every thread as a fresh
|
|
10
|
+
* conversation.
|
|
11
|
+
*
|
|
12
|
+
* This module mirrors the conversation through the SDK's own store APIs —
|
|
13
|
+
* the agent document (which carries `sdkMetadata.blobEncryptionKey`, without
|
|
14
|
+
* which the checkpoint blobs cannot be decrypted) plus its content-addressed
|
|
15
|
+
* checkpoint blobs — into the deployment's shared file storage. On
|
|
16
|
+
* Cursor-managed hosting that is the Agent Store reached over
|
|
17
|
+
* `/v0/agent-serve-files` presigned URLs: the store is **not mounted** as a
|
|
18
|
+
* filesystem in the engine VM (the FUSE mount at
|
|
19
|
+
* {@link DEPLOYMENT_STORE_MOUNT_PATH} exists only on cloud-runtime VMs), so
|
|
20
|
+
* the mirror moves bytes through the {@link FileSink} contract instead.
|
|
21
|
+
*
|
|
22
|
+
* Going through the store APIs rather than copying `store.db` files sidesteps
|
|
23
|
+
* WAL consistency and the shared `index.db` (per-session raw copies would
|
|
24
|
+
* clobber other sessions' rows); checkpoint blobs are content-addressed, so
|
|
25
|
+
* repeat persists upload only what is new.
|
|
26
|
+
*
|
|
27
|
+
* Timing: the runner hydrates before `Agent.resume` (so a follow-up landing
|
|
28
|
+
* on a fresh pod — including a v2 hosted-execution delivery — finds the
|
|
29
|
+
* conversation before the turn runs) and persists after each local turn
|
|
30
|
+
* settles, **awaited**, because a hosted pod can be parked immediately after
|
|
31
|
+
* the execution reply.
|
|
32
|
+
*
|
|
33
|
+
* Run rows and run events are deliberately not mirrored: they are per-pod
|
|
34
|
+
* bookkeeping (turn numbers, stream offsets), and the durable session event
|
|
35
|
+
* stream already lives in the control-plane storage mirror.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
import type { LocalAgentDocument, LocalAgentStore } from "@cursor/sdk";
|
|
39
|
+
import { agentStoreKeys, type FileSink } from "../files.js";
|
|
40
|
+
import {
|
|
41
|
+
cursorHostedFiles,
|
|
42
|
+
isCursorHostedFilesAvailable,
|
|
43
|
+
} from "../files-backends/cursor-hosted.js";
|
|
44
|
+
import type { AgentProject } from "../types.js";
|
|
45
|
+
import { resolveApiKeySync } from "./cursor/credentials.js";
|
|
46
|
+
import { AGENT_SERVE_GENERATION_ENV_VAR } from "./cursor-relay-core.js";
|
|
47
|
+
import { describeError } from "./describe-error.js";
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Persist/restore one SDK conversation between a {@link LocalAgentStore} and
|
|
51
|
+
* durable file storage. Implemented by {@link createConversationMirror};
|
|
52
|
+
* consumed by the SDK runner.
|
|
53
|
+
*/
|
|
54
|
+
export interface ConversationMirror {
|
|
55
|
+
/**
|
|
56
|
+
* Restore `sdkAgentId`'s conversation into `store` when the store does not
|
|
57
|
+
* already hold it. Resolves `true` when a conversation was restored,
|
|
58
|
+
* `false` on a definitive miss (never persisted, or already local — the
|
|
59
|
+
* caller falls through to its fresh-conversation path). A sink **failure**
|
|
60
|
+
* propagates: an outage must fail the turn (retryable) rather than
|
|
61
|
+
* silently fork the thread onto a fresh conversation.
|
|
62
|
+
*/
|
|
63
|
+
hydrate(store: LocalAgentStore, sdkAgentId: string): Promise<boolean>;
|
|
64
|
+
/**
|
|
65
|
+
* Mirror `sdkAgentId`'s conversation from `store` to the sink
|
|
66
|
+
* (last write wins). No-op until the conversation has a checkpoint.
|
|
67
|
+
*/
|
|
68
|
+
persist(store: LocalAgentStore, sdkAgentId: string): Promise<void>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Wire shape at {@link agentStoreKeys.conversationManifest}. */
|
|
72
|
+
interface ConversationManifestV1 {
|
|
73
|
+
v: 1;
|
|
74
|
+
/** The SDK agent document, verbatim apart from `activeRunId` (nulled). */
|
|
75
|
+
agent: LocalAgentDocument;
|
|
76
|
+
/** Checkpoint blob ids present under the conversation's blob prefix. */
|
|
77
|
+
blobIds: string[];
|
|
78
|
+
persistedAtMs: number;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface CreateConversationMirrorOptions {
|
|
82
|
+
sink: FileSink;
|
|
83
|
+
/** Agent name baked into every minted key. */
|
|
84
|
+
agentName: string;
|
|
85
|
+
logger?: (line: string) => void;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Build a {@link ConversationMirror} over a {@link FileSink}. */
|
|
89
|
+
export function createConversationMirror(
|
|
90
|
+
options: CreateConversationMirrorOptions
|
|
91
|
+
): ConversationMirror {
|
|
92
|
+
const { sink, agentName } = options;
|
|
93
|
+
const logger = options.logger ?? (() => {});
|
|
94
|
+
/** Blob ids known to be in the sink, per SDK agent id (upload dedupe). */
|
|
95
|
+
const mirrored = new Map<string, Set<string>>();
|
|
96
|
+
/**
|
|
97
|
+
* Per-conversation serialization so a persist cannot interleave with a
|
|
98
|
+
* hydrate (or another persist) for the same conversation.
|
|
99
|
+
*/
|
|
100
|
+
const chains = new Map<string, Promise<unknown>>();
|
|
101
|
+
|
|
102
|
+
function serialize<T>(sdkAgentId: string, run: () => Promise<T>): Promise<T> {
|
|
103
|
+
const previous = chains.get(sdkAgentId) ?? Promise.resolve();
|
|
104
|
+
const next = previous.then(run, run);
|
|
105
|
+
chains.set(
|
|
106
|
+
sdkAgentId,
|
|
107
|
+
next.catch(() => {})
|
|
108
|
+
);
|
|
109
|
+
return next;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function mirroredBlobIds(sdkAgentId: string): Promise<Set<string>> {
|
|
113
|
+
const cached = mirrored.get(sdkAgentId);
|
|
114
|
+
if (cached !== undefined) {
|
|
115
|
+
return cached;
|
|
116
|
+
}
|
|
117
|
+
// Seed from the previous manifest so a fresh pod does not re-upload the
|
|
118
|
+
// whole history on its first persist. A miss (or parse failure) seeds
|
|
119
|
+
// empty — re-uploading is wasteful, never wrong.
|
|
120
|
+
const seeded = new Set<string>();
|
|
121
|
+
try {
|
|
122
|
+
const manifest = await readManifest(sink, agentName, sdkAgentId);
|
|
123
|
+
for (const blobId of manifest?.blobIds ?? []) {
|
|
124
|
+
seeded.add(blobId);
|
|
125
|
+
}
|
|
126
|
+
} catch (error) {
|
|
127
|
+
logger(
|
|
128
|
+
`[agent-sdk] conversation mirror: could not read the prior manifest for ${sdkAgentId} (${describeError(error)}); re-uploading blobs`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
mirrored.set(sdkAgentId, seeded);
|
|
132
|
+
return seeded;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
persist(store, sdkAgentId): Promise<void> {
|
|
137
|
+
return serialize(sdkAgentId, async () => {
|
|
138
|
+
const document = await store.agents.get({ agentId: sdkAgentId });
|
|
139
|
+
if (document === null || document.latestCheckpoint == null) {
|
|
140
|
+
// Nothing restorable yet (no conversation, or no checkpoint).
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const uploaded = await mirroredBlobIds(sdkAgentId);
|
|
144
|
+
const blobIds = await listCheckpointBlobIds(store, sdkAgentId);
|
|
145
|
+
for (const blobId of blobIds) {
|
|
146
|
+
if (uploaded.has(blobId)) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const bytes = await store.checkpoints.get({
|
|
150
|
+
agentId: sdkAgentId,
|
|
151
|
+
blobId,
|
|
152
|
+
});
|
|
153
|
+
if (bytes === null) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
await sink.put(
|
|
157
|
+
agentStoreKeys.conversationBlob(agentName, sdkAgentId, blobId),
|
|
158
|
+
bytes
|
|
159
|
+
);
|
|
160
|
+
// Content-addressed: once in the sink, never re-uploaded.
|
|
161
|
+
uploaded.add(blobId);
|
|
162
|
+
}
|
|
163
|
+
const manifest: ConversationManifestV1 = {
|
|
164
|
+
v: 1,
|
|
165
|
+
// The referenced run is pod-local bookkeeping and is not mirrored;
|
|
166
|
+
// a hydrated store must not point at a run row it does not hold.
|
|
167
|
+
agent: { ...document, activeRunId: null },
|
|
168
|
+
blobIds: blobIds.filter((blobId) => uploaded.has(blobId)),
|
|
169
|
+
persistedAtMs: Date.now(),
|
|
170
|
+
};
|
|
171
|
+
// Manifest last: it only ever references blobs already uploaded.
|
|
172
|
+
await sink.put(
|
|
173
|
+
agentStoreKeys.conversationManifest(agentName, sdkAgentId),
|
|
174
|
+
new TextEncoder().encode(JSON.stringify(manifest))
|
|
175
|
+
);
|
|
176
|
+
});
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
hydrate(store, sdkAgentId): Promise<boolean> {
|
|
180
|
+
return serialize(sdkAgentId, async () => {
|
|
181
|
+
const existing = await store.agents.get({ agentId: sdkAgentId });
|
|
182
|
+
if (existing !== null) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
const manifest = await readManifest(sink, agentName, sdkAgentId);
|
|
186
|
+
if (manifest === undefined) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
const rootBlobId = manifest.agent.latestCheckpoint?.rootBlobId;
|
|
190
|
+
const restored = new Set<string>();
|
|
191
|
+
for (const blobId of manifest.blobIds) {
|
|
192
|
+
const body = await sink.get(
|
|
193
|
+
agentStoreKeys.conversationBlob(agentName, sdkAgentId, blobId)
|
|
194
|
+
);
|
|
195
|
+
const bytes =
|
|
196
|
+
body === undefined
|
|
197
|
+
? undefined
|
|
198
|
+
: body instanceof Uint8Array
|
|
199
|
+
? body
|
|
200
|
+
: body.body;
|
|
201
|
+
if (bytes === undefined) {
|
|
202
|
+
// Definitive absence (never uploaded, or deleted). A missing
|
|
203
|
+
// root blob makes the conversation unrestorable — fall through
|
|
204
|
+
// to the caller's fresh-conversation path rather than creating
|
|
205
|
+
// an agent row whose checkpoint cannot load.
|
|
206
|
+
if (blobId === rootBlobId) {
|
|
207
|
+
logger(
|
|
208
|
+
`[agent-sdk] conversation mirror: the root checkpoint blob for ${sdkAgentId} is missing from the sink; treating the conversation as unrestorable`
|
|
209
|
+
);
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (
|
|
215
|
+
(await store.checkpoints.get({ agentId: sdkAgentId, blobId })) ===
|
|
216
|
+
null
|
|
217
|
+
) {
|
|
218
|
+
await store.checkpoints.create({
|
|
219
|
+
agentId: sdkAgentId,
|
|
220
|
+
blobId,
|
|
221
|
+
data: bytes,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
restored.add(blobId);
|
|
225
|
+
}
|
|
226
|
+
if (rootBlobId !== undefined && !restored.has(rootBlobId)) {
|
|
227
|
+
logger(
|
|
228
|
+
`[agent-sdk] conversation mirror: the manifest for ${sdkAgentId} does not list its root checkpoint blob; treating the conversation as unrestorable`
|
|
229
|
+
);
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
await store.agents.create({
|
|
233
|
+
agent: { ...manifest.agent, activeRunId: null },
|
|
234
|
+
});
|
|
235
|
+
mirrored.set(sdkAgentId, restored);
|
|
236
|
+
logger(
|
|
237
|
+
`[agent-sdk] conversation mirror: restored the conversation for ${sdkAgentId} (${restored.size} checkpoint blob(s))`
|
|
238
|
+
);
|
|
239
|
+
return true;
|
|
240
|
+
});
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async function readManifest(
|
|
246
|
+
sink: FileSink,
|
|
247
|
+
agentName: string,
|
|
248
|
+
sdkAgentId: string
|
|
249
|
+
): Promise<ConversationManifestV1 | undefined> {
|
|
250
|
+
const body = await sink.get(
|
|
251
|
+
agentStoreKeys.conversationManifest(agentName, sdkAgentId)
|
|
252
|
+
);
|
|
253
|
+
if (body === undefined) {
|
|
254
|
+
return undefined;
|
|
255
|
+
}
|
|
256
|
+
const bytes = body instanceof Uint8Array ? body : body.body;
|
|
257
|
+
const parsed: unknown = JSON.parse(new TextDecoder().decode(bytes));
|
|
258
|
+
if (
|
|
259
|
+
parsed === null ||
|
|
260
|
+
typeof parsed !== "object" ||
|
|
261
|
+
(parsed as { v?: unknown }).v !== 1
|
|
262
|
+
) {
|
|
263
|
+
throw new Error(
|
|
264
|
+
`conversation mirror: unsupported manifest for ${sdkAgentId}`
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
const manifest = parsed as ConversationManifestV1;
|
|
268
|
+
if (
|
|
269
|
+
manifest.agent === null ||
|
|
270
|
+
typeof manifest.agent !== "object" ||
|
|
271
|
+
manifest.agent.agentId !== sdkAgentId ||
|
|
272
|
+
!Array.isArray(manifest.blobIds)
|
|
273
|
+
) {
|
|
274
|
+
throw new Error(
|
|
275
|
+
`conversation mirror: malformed manifest for ${sdkAgentId}`
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
return manifest;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
async function listCheckpointBlobIds(
|
|
282
|
+
store: LocalAgentStore,
|
|
283
|
+
sdkAgentId: string
|
|
284
|
+
): Promise<string[]> {
|
|
285
|
+
const blobIds: string[] = [];
|
|
286
|
+
let cursor: string | undefined;
|
|
287
|
+
do {
|
|
288
|
+
const page = await store.checkpoints.list({
|
|
289
|
+
filter: { agentIds: [sdkAgentId], cursor },
|
|
290
|
+
});
|
|
291
|
+
blobIds.push(...page.items);
|
|
292
|
+
cursor = page.nextCursor;
|
|
293
|
+
} while (cursor !== undefined);
|
|
294
|
+
return blobIds;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Whether (and over which sink) this serve process mirrors conversations.
|
|
299
|
+
*
|
|
300
|
+
* Selected for architecture-v2 agents on Cursor-managed hosting — the same
|
|
301
|
+
* generation signal as framework-storage-selection.ts, plus a provisioned
|
|
302
|
+
* deployment file store. Self-hosted v2 keeps everything on the state root
|
|
303
|
+
* (the operator owns the disk), and v1 keeps today's behavior while it runs.
|
|
304
|
+
*/
|
|
305
|
+
export function selectConversationMirror(input: {
|
|
306
|
+
project: AgentProject;
|
|
307
|
+
logger?: (line: string) => void;
|
|
308
|
+
env?: Record<string, string | undefined>;
|
|
309
|
+
}): ConversationMirror | undefined {
|
|
310
|
+
if (input.project.agent.architecture !== "v2") {
|
|
311
|
+
return undefined;
|
|
312
|
+
}
|
|
313
|
+
const env = input.env ?? process.env;
|
|
314
|
+
if (
|
|
315
|
+
env[AGENT_SERVE_GENERATION_ENV_VAR] === undefined ||
|
|
316
|
+
!isCursorHostedFilesAvailable(env as NodeJS.ProcessEnv)
|
|
317
|
+
) {
|
|
318
|
+
return undefined;
|
|
319
|
+
}
|
|
320
|
+
return createConversationMirror({
|
|
321
|
+
sink: cursorHostedFiles({
|
|
322
|
+
// Full chain (env → key file → stored login): frozen engines listen
|
|
323
|
+
// unsigned and bind CURSOR_API_KEY_FILE at task time, after this sink
|
|
324
|
+
// is constructed.
|
|
325
|
+
apiKey: () => resolveApiKeySync()?.apiKey,
|
|
326
|
+
}),
|
|
327
|
+
agentName: input.project.name,
|
|
328
|
+
logger: input.logger,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { ChannelSession } from "../types.js";
|
|
2
|
+
|
|
3
|
+
const deferredSessions = new WeakSet<ChannelSession>();
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Authored code touched a member of the deferred handle that needs the live
|
|
7
|
+
* session. This is deterministic (the session never exists during admission),
|
|
8
|
+
* so the admission route surfaces it as a non-retryable 422 rather than a
|
|
9
|
+
* retryable failure.
|
|
10
|
+
*/
|
|
11
|
+
export class DeferredChannelSessionError extends Error {
|
|
12
|
+
constructor(member: string) {
|
|
13
|
+
super(
|
|
14
|
+
`ChannelSession.${member} is not available on a send() captured ` +
|
|
15
|
+
"during hosted admission: the control plane allocates the session " +
|
|
16
|
+
"(and runs the turn) only after this delivery is acknowledged, so " +
|
|
17
|
+
"the handle cannot observe it yet. Move work that needs the live " +
|
|
18
|
+
"session into the session turn instead."
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function unavailable(member: string): never {
|
|
24
|
+
throw new DeferredChannelSessionError(member);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Inert `ChannelSession` returned by the hosted-admission `send` stub: every
|
|
29
|
+
* member fails loudly via {@link DeferredChannelSessionError}. Caveat: new
|
|
30
|
+
* OPTIONAL members added to `ChannelSession` slip through this factory
|
|
31
|
+
* silently (reads return `undefined`) until a throwing getter is added here.
|
|
32
|
+
*/
|
|
33
|
+
export function createDeferredChannelSession(): ChannelSession {
|
|
34
|
+
const session: ChannelSession = {
|
|
35
|
+
get id(): string {
|
|
36
|
+
return unavailable("id");
|
|
37
|
+
},
|
|
38
|
+
get continuationToken(): string | null {
|
|
39
|
+
return unavailable("continuationToken");
|
|
40
|
+
},
|
|
41
|
+
get isNew(): boolean {
|
|
42
|
+
return unavailable("isNew");
|
|
43
|
+
},
|
|
44
|
+
get coalesced(): boolean | undefined {
|
|
45
|
+
return unavailable("coalesced");
|
|
46
|
+
},
|
|
47
|
+
get info(): ChannelSession["info"] {
|
|
48
|
+
return unavailable("info");
|
|
49
|
+
},
|
|
50
|
+
getEventStream: async () => unavailable("getEventStream()"),
|
|
51
|
+
waitForCompletion: async () => unavailable("waitForCompletion()"),
|
|
52
|
+
setContinuationToken: async () => unavailable("setContinuationToken()"),
|
|
53
|
+
};
|
|
54
|
+
deferredSessions.add(session);
|
|
55
|
+
return session;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** True when `send()` returned a hosted-admission capture handle. */
|
|
59
|
+
export function isDeferredChannelSession(session: ChannelSession): boolean {
|
|
60
|
+
return deferredSessions.has(session);
|
|
61
|
+
}
|
|
@@ -224,6 +224,16 @@ export interface AgentkitDeployAccepted {
|
|
|
224
224
|
status: string;
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
+
/**
|
|
228
|
+
* One release component of a v2 multi-component deploy: a stable key (the
|
|
229
|
+
* child agent's slug) plus that agent's directory relative to the repo
|
|
230
|
+
* root.
|
|
231
|
+
*/
|
|
232
|
+
export interface AgentkitDeployComponent {
|
|
233
|
+
componentKey: string;
|
|
234
|
+
agentPath?: string;
|
|
235
|
+
}
|
|
236
|
+
|
|
227
237
|
/** Customer-visible v2 deploy status (`GET /internal/agentkit/deployments/:slug`). */
|
|
228
238
|
export type AgentkitCustomerStatus =
|
|
229
239
|
| "pending"
|
|
@@ -576,7 +586,9 @@ export class AgentServeDeployClient {
|
|
|
576
586
|
/**
|
|
577
587
|
* Create or redeploy onto the v2 control plane
|
|
578
588
|
* (`POST /internal/agentkit/deployments`). 202 means accepted; poll
|
|
579
|
-
* {@link getAgentkitDeploy} until a terminal status.
|
|
589
|
+
* {@link getAgentkitDeploy} until a terminal status. `components` deploys
|
|
590
|
+
* one application with several release components (one per agent
|
|
591
|
+
* directory); it is mutually exclusive with `agentPath`.
|
|
580
592
|
*/
|
|
581
593
|
async deployAgentkit(args: {
|
|
582
594
|
teamId: number;
|
|
@@ -584,12 +596,22 @@ export class AgentServeDeployClient {
|
|
|
584
596
|
gitRepoUrl: string;
|
|
585
597
|
gitRef?: string;
|
|
586
598
|
agentPath?: string;
|
|
599
|
+
components?: AgentkitDeployComponent[];
|
|
587
600
|
repositories?: string[];
|
|
588
601
|
}): Promise<AgentkitDeployAccepted> {
|
|
589
602
|
const slugError = validateDeploymentSlug(args.slug);
|
|
590
603
|
if (slugError !== undefined) {
|
|
591
604
|
throw new DeployApiError(slugError, 400);
|
|
592
605
|
}
|
|
606
|
+
for (const component of args.components ?? []) {
|
|
607
|
+
const keyError = validateDeploymentSlug(component.componentKey);
|
|
608
|
+
if (keyError !== undefined) {
|
|
609
|
+
throw new DeployApiError(
|
|
610
|
+
`Invalid component key ${JSON.stringify(component.componentKey)}: use lowercase letters, digits, '-' or '_' (max 64 chars)`,
|
|
611
|
+
400
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
593
615
|
const raw = await this.request({
|
|
594
616
|
method: "POST",
|
|
595
617
|
path: "/internal/agentkit/deployments",
|
|
@@ -599,6 +621,7 @@ export class AgentServeDeployClient {
|
|
|
599
621
|
gitRepoUrl: args.gitRepoUrl,
|
|
600
622
|
gitRef: args.gitRef,
|
|
601
623
|
agentPath: args.agentPath,
|
|
624
|
+
components: args.components,
|
|
602
625
|
repositories:
|
|
603
626
|
args.repositories !== undefined && args.repositories.length > 0
|
|
604
627
|
? args.repositories
|