@cursor/bdk 0.2.13 → 0.2.14
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/docs/404.html +2 -2
- package/dist/docs/assets/{app.CaLG1pPt.js → app.BN3sgOOo.js} +4 -4
- package/dist/docs/assets/chunks/@localSearchIndexroot.CzrtdJ8s.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.YsP_2wqD.js → VPLocalSearchBox.CbA00dML.js} +1 -1
- package/dist/docs/assets/chunks/{arc.C2j9hR7c.js → arc.Byug8CZM.js} +1 -1
- package/dist/docs/assets/chunks/{architectureDiagram-Q4EWVU46.C5qkGp7y.js → architectureDiagram-Q4EWVU46.D1_qmnQy.js} +1 -1
- package/dist/docs/assets/chunks/{baseUniq.U6GgTHOD.js → baseUniq.Dj-Nurf-.js} +1 -1
- package/dist/docs/assets/chunks/{blockDiagram-DXYQGD6D.D51mJop2.js → blockDiagram-DXYQGD6D.BZwq3EIg.js} +1 -1
- package/dist/docs/assets/chunks/{c4Diagram-AHTNJAMY.w1d6QxV7.js → c4Diagram-AHTNJAMY.dx75urO7.js} +1 -1
- package/dist/docs/assets/chunks/channel.6ebHhoF9.js +1 -0
- package/dist/docs/assets/chunks/{chunk-4BX2VUAB.B_A815un.js → chunk-4BX2VUAB.CIoddsDQ.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-4TB4RGXK.DMrm-ZCb.js → chunk-4TB4RGXK.O3VsVBES.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-55IACEB6.8qw6Xdal.js → chunk-55IACEB6.WNNWh0Pe.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-EDXVE4YY.Cq_nHODn.js → chunk-EDXVE4YY.BKVQwEME.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-FMBD7UC4.B67ohCYn.js → chunk-FMBD7UC4.Cv5iBz7A.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-OYMX7WX6.Bo8rQI8F.js → chunk-OYMX7WX6.wCDCToee.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-QZHKN3VN.BERUMfE9.js → chunk-QZHKN3VN.D3IGXhXA.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-YZCP3GAM.DWG2tXIX.js → chunk-YZCP3GAM.CAPPh-E7.js} +1 -1
- package/dist/docs/assets/chunks/classDiagram-6PBFFD2Q.BJ-CwKwu.js +1 -0
- package/dist/docs/assets/chunks/classDiagram-v2-HSJHXN6E.BJ-CwKwu.js +1 -0
- package/dist/docs/assets/chunks/clone.DfV2fkWH.js +1 -0
- package/dist/docs/assets/chunks/{cose-bilkent-S5V4N54A.tezXBs9b.js → cose-bilkent-S5V4N54A.BQCFH3M5.js} +1 -1
- package/dist/docs/assets/chunks/{dagre-KV5264BT.aSbEUwT7.js → dagre-KV5264BT.lHmdrh7L.js} +1 -1
- package/dist/docs/assets/chunks/{diagram-5BDNPKRD.vnT6aJ57.js → diagram-5BDNPKRD.EtSPnHgD.js} +1 -1
- package/dist/docs/assets/chunks/{diagram-G4DWMVQ6.hGYdrDa2.js → diagram-G4DWMVQ6.DCyUnQT8.js} +1 -1
- package/dist/docs/assets/chunks/{diagram-MMDJMWI5.CmdSUP6L.js → diagram-MMDJMWI5._E0Qqblb.js} +1 -1
- package/dist/docs/assets/chunks/{diagram-TYMM5635.BbAnLEBB.js → diagram-TYMM5635.BDj4oyy_.js} +1 -1
- package/dist/docs/assets/chunks/{erDiagram-SMLLAGMA.H40W4HWE.js → erDiagram-SMLLAGMA.CrvnO6a3.js} +1 -1
- package/dist/docs/assets/chunks/{flowDiagram-DWJPFMVM.BFMtUSv-.js → flowDiagram-DWJPFMVM.DIhYc-2F.js} +1 -1
- package/dist/docs/assets/chunks/{ganttDiagram-T4ZO3ILL.CbYiu7Dt.js → ganttDiagram-T4ZO3ILL.hFqm_vio.js} +1 -1
- package/dist/docs/assets/chunks/{gitGraphDiagram-UUTBAWPF.Cyc5mzZD.js → gitGraphDiagram-UUTBAWPF.N3GDaVDD.js} +1 -1
- package/dist/docs/assets/chunks/{graph.BRptDG1H.js → graph.DK28rMBx.js} +1 -1
- package/dist/docs/assets/chunks/{infoDiagram-42DDH7IO.Bz6KCA3a.js → infoDiagram-42DDH7IO.DHmE6W9M.js} +1 -1
- package/dist/docs/assets/chunks/{ishikawaDiagram-UXIWVN3A.DDJ_7_Fq.js → ishikawaDiagram-UXIWVN3A.ByUJuOYm.js} +1 -1
- package/dist/docs/assets/chunks/{journeyDiagram-VCZTEJTY.D0t_ltlb.js → journeyDiagram-VCZTEJTY.BIUChFMh.js} +1 -1
- package/dist/docs/assets/chunks/{kanban-definition-6JOO6SKY.DyyNsMfi.js → kanban-definition-6JOO6SKY.CNgFXwo-.js} +1 -1
- package/dist/docs/assets/chunks/{layout.Cbxq-Rhp.js → layout.BDnwE4ET.js} +1 -1
- package/dist/docs/assets/chunks/{linear.DxGYL0MP.js → linear.DmrYLXFs.js} +1 -1
- package/dist/docs/assets/chunks/{min.DA5KL8wu.js → min.BbYcWakN.js} +1 -1
- package/dist/docs/assets/chunks/{mindmap-definition-QFDTVHPH.Bc72Utls.js → mindmap-definition-QFDTVHPH.C9x-klkv.js} +1 -1
- package/dist/docs/assets/chunks/{pieDiagram-DEJITSTG.BtpHnIB_.js → pieDiagram-DEJITSTG.iufpEbi0.js} +1 -1
- package/dist/docs/assets/chunks/{quadrantDiagram-34T5L4WZ.Cx4oCSRR.js → quadrantDiagram-34T5L4WZ.DdoXbWL9.js} +1 -1
- package/dist/docs/assets/chunks/{requirementDiagram-MS252O5E.BC0hXWlW.js → requirementDiagram-MS252O5E.XL1-_pNk.js} +1 -1
- package/dist/docs/assets/chunks/{sankeyDiagram-XADWPNL6.Dg8T1SEp.js → sankeyDiagram-XADWPNL6.DySXofNs.js} +1 -1
- package/dist/docs/assets/chunks/{sequenceDiagram-FGHM5R23.CWSdHYAF.js → sequenceDiagram-FGHM5R23.Cp0GZoOU.js} +1 -1
- package/dist/docs/assets/chunks/{stateDiagram-FHFEXIEX.0MVPRI28.js → stateDiagram-FHFEXIEX.DvK-sXUX.js} +1 -1
- package/dist/docs/assets/chunks/stateDiagram-v2-QKLJ7IA2.DNdUCJyd.js +1 -0
- package/dist/docs/assets/chunks/{theme.DO1avLBn.js → theme.Clup4iIo.js} +2 -2
- package/dist/docs/assets/chunks/{timeline-definition-GMOUNBTQ.BuRinAVi.js → timeline-definition-GMOUNBTQ.gHI9ZLm6.js} +1 -1
- package/dist/docs/assets/chunks/{vennDiagram-DHZGUBPP.DXf0PCoJ.js → vennDiagram-DHZGUBPP.G2N2zXYe.js} +1 -1
- package/dist/docs/assets/chunks/wardley-RL74JXVD.C46G0P5a.js +162 -0
- package/dist/docs/assets/chunks/{wardleyDiagram-NUSXRM2D.T4ThngYV.js → wardleyDiagram-NUSXRM2D.BgUKEagd.js} +1 -1
- package/dist/docs/assets/chunks/{xychartDiagram-5P7HB3ND.b4pZF3D5.js → xychartDiagram-5P7HB3ND.CwH7oege.js} +1 -1
- package/dist/docs/assets/{reference_skills.md.C0guGcH0.js → reference_skills.md.ClkMoQsV.js} +3 -3
- package/dist/docs/building-with-agents.html +35 -35
- package/dist/docs/deployment.html +35 -35
- package/dist/docs/evals.html +35 -35
- package/dist/docs/guides/agent-to-agent.html +35 -35
- package/dist/docs/guides/bitbucket.html +35 -35
- package/dist/docs/guides/cloud-agents.html +35 -35
- package/dist/docs/guides/convert-automation.html +35 -35
- package/dist/docs/guides/github.html +35 -35
- package/dist/docs/guides/gitlab.html +35 -35
- package/dist/docs/guides/grokbot-agents.html +35 -35
- package/dist/docs/guides/hooks.html +35 -35
- package/dist/docs/guides/improve.html +35 -35
- package/dist/docs/guides/jev.html +35 -35
- package/dist/docs/guides/mcp-oauth.html +35 -35
- package/dist/docs/guides/migrate.html +35 -35
- package/dist/docs/guides/opentelemetry.html +35 -35
- package/dist/docs/guides/slack.html +35 -35
- package/dist/docs/guides/webhooks.html +35 -35
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +35 -35
- package/dist/docs/index.html +35 -35
- package/dist/docs/quickstart.html +35 -35
- package/dist/docs/reference/agent-config.html +35 -35
- package/dist/docs/reference/artifacts.html +35 -35
- package/dist/docs/reference/channels.html +35 -35
- package/dist/docs/reference/cli.html +35 -35
- package/dist/docs/reference/connections.html +35 -35
- package/dist/docs/reference/evals.html +35 -35
- package/dist/docs/reference/extensions.html +35 -35
- package/dist/docs/reference/hooks.html +35 -35
- package/dist/docs/reference/http-api.html +35 -35
- package/dist/docs/reference/instructions.html +35 -35
- package/dist/docs/reference/playground.html +35 -35
- package/dist/docs/reference/project-layout.html +35 -35
- package/dist/docs/reference/prompt.html +35 -35
- package/dist/docs/reference/schedules.html +35 -35
- package/dist/docs/reference/sessions.html +35 -35
- package/dist/docs/reference/skills.html +38 -38
- package/dist/docs/reference/subagents.html +35 -35
- package/dist/docs/reference/tools.html +35 -35
- package/dist/docs/templates/agentic-owners.html +35 -35
- package/dist/docs/templates/grokbot-agents.html +35 -35
- package/dist/docs/templates/security-reviewer.html +35 -35
- package/dist/docs/templates/thermo-quality-review.html +35 -35
- package/dist/docs/templates/thermo-review.html +35 -35
- package/dist/docs/templates/triage.html +35 -35
- package/dist/docs/troubleshooting.html +35 -35
- package/dist/executors.d.ts +11 -0
- package/dist/executors.d.ts.map +1 -0
- package/dist/executors.js +21 -0
- package/dist/extensions/cursor-cloud-agents/extension.d.ts +1 -1
- package/dist/extensions/cursor-cloud-agents/extension.d.ts.map +1 -1
- package/dist/extensions/cursor-cloud-agents/extension.js +1 -1
- package/dist/extensions/cursor-cloud-agents/lib/launch-agent.d.ts +21 -4
- package/dist/extensions/cursor-cloud-agents/lib/launch-agent.d.ts.map +1 -1
- package/dist/extensions/cursor-cloud-agents/lib/launch-agent.js +26 -5
- package/dist/files.d.ts +1 -0
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +3 -1
- package/dist/filesystem.d.ts +10 -0
- package/dist/filesystem.d.ts.map +1 -1
- package/dist/filesystem.js +128 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +3 -0
- package/dist/internal/conversation-mirror.d.ts +11 -0
- package/dist/internal/conversation-mirror.d.ts.map +1 -1
- package/dist/internal/conversation-mirror.js +198 -125
- package/dist/internal/conversation-pack.d.ts +29 -0
- package/dist/internal/conversation-pack.d.ts.map +1 -0
- package/dist/internal/conversation-pack.js +96 -0
- package/dist/internal/deploy-client.d.ts +2 -0
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +4 -0
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +8 -0
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +1 -0
- package/dist/internal/turn-cost.d.ts +2 -2
- package/dist/internal/turn-cost.d.ts.map +1 -1
- package/dist/internal/turn-cost.js +17 -3
- package/dist/playground/assets/index-BKSsuEMw.css +1 -0
- package/dist/playground/assets/{index-DIiKbZ25.js → index-ClxRl2Cw.js} +44 -44
- package/dist/playground/index.html +2 -2
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +12 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DvlKuIO-.js +0 -1
- package/dist/docs/assets/chunks/channel.BRSFxCcD.js +0 -1
- package/dist/docs/assets/chunks/classDiagram-6PBFFD2Q.Cmtm6WkN.js +0 -1
- package/dist/docs/assets/chunks/classDiagram-v2-HSJHXN6E.Cmtm6WkN.js +0 -1
- package/dist/docs/assets/chunks/clone.JMC96Eo6.js +0 -1
- package/dist/docs/assets/chunks/stateDiagram-v2-QKLJ7IA2.N4Am6sXd.js +0 -1
- package/dist/docs/assets/chunks/wardley-RL74JXVD._eATmGtu.js +0 -162
- package/dist/playground/assets/index-CzttDc-d.css +0 -1
- /package/dist/docs/assets/{reference_skills.md.C0guGcH0.lean.js → reference_skills.md.ClkMoQsV.lean.js} +0 -0
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
* persists it after each local turn. Conversation data uses framework-owned
|
|
6
6
|
* storage, isolated from author-facing files and KV.
|
|
7
7
|
*
|
|
8
|
+
* Checkpoint blobs are written in content-addressed packs
|
|
9
|
+
* (`conversation-pack.ts`): a turn's new blobs are one store write, not one
|
|
10
|
+
* per blob. The manifest (v2) lists the packs and is written last. Manifests
|
|
11
|
+
* from engines that wrote one object per blob (v1) still restore; the first
|
|
12
|
+
* persist after such a restore re-packs the whole conversation from the
|
|
13
|
+
* local store.
|
|
14
|
+
*
|
|
8
15
|
* Restore failures propagate so a temporary storage outage cannot fork an
|
|
9
16
|
* existing thread into a fresh conversation. Run bookkeeping remains local;
|
|
10
17
|
* the durable session event stream already records user-visible history.
|
|
@@ -12,6 +19,7 @@
|
|
|
12
19
|
import { agentSdkAgentStoreKeys } from "../files.js";
|
|
13
20
|
import { cursorHostedFiles } from "../files-backends/cursor-hosted.js";
|
|
14
21
|
import { mapBounded } from "./bounded.js";
|
|
22
|
+
import { conversationPackEntrySize, decodeConversationPack, encodeConversationPack, } from "./conversation-pack.js";
|
|
15
23
|
import { resolveApiKeySync } from "./cursor/credentials.js";
|
|
16
24
|
import { describeError } from "./describe-error.js";
|
|
17
25
|
import { resolveAgentV2Hosting } from "./v2-hosting.js";
|
|
@@ -19,41 +27,27 @@ import { resolveAgentV2Hosting } from "./v2-hosting.js";
|
|
|
19
27
|
export function createConversationMirror(options) {
|
|
20
28
|
const { sink, agentName } = options;
|
|
21
29
|
const logger = options.logger ?? (() => { });
|
|
30
|
+
const packTargetBytes = options.packTargetBytes ?? PACK_TARGET_BYTES;
|
|
31
|
+
const maxPacks = options.maxPacks ?? MAX_PACKS;
|
|
22
32
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
33
|
+
* Packs each conversation's last successful manifest listed. Bounded
|
|
34
|
+
* oldest-out like the files backend's etag cache: a resident engine
|
|
35
|
+
* touches an unbounded number of conversations over its lifetime, and a
|
|
36
|
+
* miss costs one manifest read, never correctness.
|
|
27
37
|
*/
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
const oldest = mirrored.keys().next();
|
|
32
|
-
if (!oldest.done) {
|
|
33
|
-
mirrored.delete(oldest.value);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
mirrored.set(sdkAgentId, blobIds);
|
|
38
|
+
const packStates = new Map();
|
|
39
|
+
const rememberPacks = (sdkAgentId, packs) => {
|
|
40
|
+
rememberBounded(packStates, sdkAgentId, packs);
|
|
37
41
|
};
|
|
38
42
|
/**
|
|
39
43
|
* Content of the last manifest each conversation **successfully** wrote
|
|
40
44
|
* (minus `persistedAtMs`, which changes on every call by construction).
|
|
41
|
-
* A persist whose agent document and
|
|
45
|
+
* A persist whose agent document and pack list are unchanged skips the
|
|
42
46
|
* manifest rewrite — `persist` runs on every turn boundary, but most
|
|
43
47
|
* boundaries between checkpoints change nothing durable. Same bound and
|
|
44
|
-
* eviction as `
|
|
48
|
+
* eviction as `packStates`; a miss only costs a redundant rewrite.
|
|
45
49
|
*/
|
|
46
50
|
const lastManifestContent = new Map();
|
|
47
|
-
const rememberManifest = (sdkAgentId, content) => {
|
|
48
|
-
if (!lastManifestContent.has(sdkAgentId) &&
|
|
49
|
-
lastManifestContent.size >= MAX_TRACKED_CONVERSATIONS) {
|
|
50
|
-
const oldest = lastManifestContent.keys().next();
|
|
51
|
-
if (!oldest.done) {
|
|
52
|
-
lastManifestContent.delete(oldest.value);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
lastManifestContent.set(sdkAgentId, content);
|
|
56
|
-
};
|
|
57
51
|
/**
|
|
58
52
|
* Per-conversation serialization so a persist cannot interleave with a
|
|
59
53
|
* hydrate (or another persist) for the same conversation. Entries are
|
|
@@ -74,67 +68,61 @@ export function createConversationMirror(options) {
|
|
|
74
68
|
chains.set(sdkAgentId, stored);
|
|
75
69
|
return next;
|
|
76
70
|
}
|
|
77
|
-
async function
|
|
78
|
-
const cached =
|
|
71
|
+
async function persistedPacks(sdkAgentId) {
|
|
72
|
+
const cached = packStates.get(sdkAgentId);
|
|
79
73
|
if (cached !== undefined) {
|
|
80
74
|
return cached;
|
|
81
75
|
}
|
|
82
|
-
// Seed from the previous manifest so a new process
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
|
|
76
|
+
// Seed from the previous manifest so a new process appends to it instead
|
|
77
|
+
// of re-packing the whole history. Anything but a usable v2 manifest
|
|
78
|
+
// seeds empty — re-packing from the local store is wasteful, never wrong.
|
|
79
|
+
let seeded = [];
|
|
86
80
|
try {
|
|
87
81
|
const read = await readManifest(sink, agentName, sdkAgentId);
|
|
88
|
-
if (read.outcome === "found") {
|
|
89
|
-
|
|
90
|
-
seeded.add(blobId);
|
|
91
|
-
}
|
|
82
|
+
if (read.outcome === "found" && read.manifest.v === 2) {
|
|
83
|
+
seeded = read.manifest.packs;
|
|
92
84
|
}
|
|
93
85
|
}
|
|
94
86
|
catch (error) {
|
|
95
|
-
logger(`[bdk] conversation mirror: could not read the prior manifest for ${sdkAgentId} (${describeError(error)}); re-
|
|
87
|
+
logger(`[bdk] conversation mirror: could not read the prior manifest for ${sdkAgentId} (${describeError(error)}); re-packing the conversation`);
|
|
96
88
|
}
|
|
97
|
-
|
|
89
|
+
rememberPacks(sdkAgentId, seeded);
|
|
98
90
|
return seeded;
|
|
99
91
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (entries.length === 0) {
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
if (sink.putMany === undefined) {
|
|
116
|
-
for (const entry of entries) {
|
|
117
|
-
await sink.put(entry.key, entry.body);
|
|
118
|
-
uploaded.add(entry.blobId);
|
|
92
|
+
/**
|
|
93
|
+
* Read `blobIds` from the local store and upload them as packs of at most
|
|
94
|
+
* `packTargetBytes` (a larger blob gets a pack of its own). Packs upload
|
|
95
|
+
* one at a time so a re-pack never holds more than one pack in memory.
|
|
96
|
+
*/
|
|
97
|
+
async function writePacks(store, sdkAgentId, blobIds) {
|
|
98
|
+
const written = [];
|
|
99
|
+
let entries = [];
|
|
100
|
+
let bytes = 0;
|
|
101
|
+
const flush = async () => {
|
|
102
|
+
if (entries.length === 0) {
|
|
103
|
+
return;
|
|
119
104
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
105
|
+
const { packId, body } = encodeConversationPack(entries);
|
|
106
|
+
await sink.put(agentSdkAgentStoreKeys.conversationPack(agentName, sdkAgentId, packId), body);
|
|
107
|
+
written.push({ id: packId, blobIds: entries.map((entry) => entry.blobId) });
|
|
108
|
+
entries = [];
|
|
109
|
+
bytes = 0;
|
|
110
|
+
};
|
|
111
|
+
for (const blobId of blobIds) {
|
|
112
|
+
const data = await store.checkpoints.get({ agentId: sdkAgentId, blobId });
|
|
113
|
+
if (data === null) {
|
|
114
|
+
continue;
|
|
128
115
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
116
|
+
const entry = { blobId, data };
|
|
117
|
+
const size = conversationPackEntrySize(entry);
|
|
118
|
+
if (entries.length > 0 && bytes + size > packTargetBytes) {
|
|
119
|
+
await flush();
|
|
133
120
|
}
|
|
121
|
+
entries.push(entry);
|
|
122
|
+
bytes += size;
|
|
134
123
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
124
|
+
await flush();
|
|
125
|
+
return written;
|
|
138
126
|
}
|
|
139
127
|
return {
|
|
140
128
|
persist(store, sdkAgentId) {
|
|
@@ -144,34 +132,53 @@ export function createConversationMirror(options) {
|
|
|
144
132
|
// Nothing restorable yet (no conversation, or no checkpoint).
|
|
145
133
|
return;
|
|
146
134
|
}
|
|
147
|
-
const
|
|
148
|
-
const
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
135
|
+
const previous = await persistedPacks(sdkAgentId);
|
|
136
|
+
const packed = new Set(previous.flatMap((pack) => pack.blobIds));
|
|
137
|
+
const local = await listCheckpointBlobIds(store, sdkAgentId);
|
|
138
|
+
const pending = local.filter((blobId) => !packed.has(blobId));
|
|
139
|
+
let packs = previous;
|
|
140
|
+
if (pending.length > 0) {
|
|
141
|
+
const localSet = new Set(local);
|
|
142
|
+
const canRepack = [...packed].every((blobId) => localSet.has(blobId));
|
|
143
|
+
if (previous.length + 1 > maxPacks && canRepack) {
|
|
144
|
+
// The packs this replaces stay in the store: another process
|
|
145
|
+
// holding an outdated pack list may still write a manifest that
|
|
146
|
+
// references them, and that manifest must remain restorable.
|
|
147
|
+
packs = await writePacks(store, sdkAgentId, local);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
packs = [...previous, ...(await writePacks(store, sdkAgentId, pending))];
|
|
151
|
+
}
|
|
152
152
|
}
|
|
153
153
|
const manifest = {
|
|
154
|
-
v:
|
|
154
|
+
v: 2,
|
|
155
155
|
// The referenced run is process-local bookkeeping and is not mirrored;
|
|
156
156
|
// a hydrated store must not point at a run row it does not hold.
|
|
157
157
|
agent: { ...document, activeRunId: null },
|
|
158
|
-
|
|
158
|
+
packs,
|
|
159
159
|
persistedAtMs: Date.now(),
|
|
160
160
|
};
|
|
161
161
|
// Skip the rewrite when nothing durable changed. `persistedAtMs`
|
|
162
162
|
// is excluded from the comparison — it differs on every call by
|
|
163
163
|
// construction and carries no restore semantics.
|
|
164
|
-
const content = JSON.stringify({
|
|
165
|
-
agent: manifest.agent,
|
|
166
|
-
blobIds: manifest.blobIds,
|
|
167
|
-
});
|
|
164
|
+
const content = JSON.stringify({ agent: manifest.agent, packs: manifest.packs });
|
|
168
165
|
if (lastManifestContent.get(sdkAgentId) === content) {
|
|
169
166
|
return;
|
|
170
167
|
}
|
|
171
168
|
lastManifestContent.delete(sdkAgentId);
|
|
172
|
-
// Manifest last: it only ever references
|
|
173
|
-
|
|
174
|
-
|
|
169
|
+
// Manifest last: it only ever references packs already uploaded.
|
|
170
|
+
try {
|
|
171
|
+
await sink.put(agentSdkAgentStoreKeys.conversationManifest(agentName, sdkAgentId), new TextEncoder().encode(JSON.stringify(manifest)));
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
// A conflict means another process wrote the manifest. Re-read it
|
|
175
|
+
// on the next persist and append to its packs rather than
|
|
176
|
+
// overwriting them from this process's outdated list.
|
|
177
|
+
packStates.delete(sdkAgentId);
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
rememberPacks(sdkAgentId, packs);
|
|
181
|
+
rememberBounded(lastManifestContent, sdkAgentId, content);
|
|
175
182
|
});
|
|
176
183
|
},
|
|
177
184
|
hydrate(store, sdkAgentId) {
|
|
@@ -193,62 +200,114 @@ export function createConversationMirror(options) {
|
|
|
193
200
|
return false;
|
|
194
201
|
}
|
|
195
202
|
const manifest = read.manifest;
|
|
203
|
+
const listed = new Set(manifest.v === 2 ? manifest.packs.flatMap((pack) => pack.blobIds) : manifest.blobIds);
|
|
196
204
|
const rootBlobId = manifest.agent.latestCheckpoint?.rootBlobId;
|
|
197
|
-
if (rootBlobId === undefined || !
|
|
205
|
+
if (rootBlobId === undefined || !listed.has(rootBlobId)) {
|
|
198
206
|
logger(`[bdk] conversation mirror: the manifest for ${sdkAgentId} does not list its root checkpoint blob; treating the conversation as unrestorable`);
|
|
199
207
|
return false;
|
|
200
208
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
209
|
+
const blobs = manifest.v === 2
|
|
210
|
+
? await readPackedBlobs(sdkAgentId, manifest.packs)
|
|
211
|
+
: await readLooseBlobs(sdkAgentId, manifest.blobIds);
|
|
212
|
+
if (blobs === undefined) {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
// The manifest lists exactly what a previous persist uploaded, so
|
|
216
|
+
// any definitive absence is store loss — and the checkpoint graph
|
|
217
|
+
// may reach any listed blob, not just the root. Restoring a partial
|
|
218
|
+
// graph would create an agent row whose resume succeeds and whose
|
|
219
|
+
// checkpoint walk then fails unrecoverably.
|
|
220
|
+
const missing = [...listed].filter((blobId) => !blobs.has(blobId));
|
|
212
221
|
if (missing.length > 0) {
|
|
213
|
-
// The manifest lists exactly what a previous persist uploaded, so
|
|
214
|
-
// any definitive absence is store loss — and the checkpoint graph
|
|
215
|
-
// may reach any listed blob, not just the root. Restoring a
|
|
216
|
-
// partial graph would create an agent row whose resume succeeds
|
|
217
|
-
// and whose checkpoint walk then fails unrecoverably.
|
|
218
222
|
logger(`[bdk] conversation mirror: ${missing.length} checkpoint blob(s) for ${sdkAgentId} are missing from the sink; treating the conversation as unrestorable`);
|
|
219
223
|
return false;
|
|
220
224
|
}
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const bytes = body instanceof Uint8Array ? body : body.body;
|
|
225
|
-
if ((await store.checkpoints.get({
|
|
226
|
-
agentId: sdkAgentId,
|
|
227
|
-
blobId: entry.blobId,
|
|
228
|
-
})) === null) {
|
|
229
|
-
await store.checkpoints.create({
|
|
230
|
-
agentId: sdkAgentId,
|
|
231
|
-
blobId: entry.blobId,
|
|
232
|
-
data: bytes,
|
|
233
|
-
});
|
|
225
|
+
for (const [blobId, data] of blobs) {
|
|
226
|
+
if ((await store.checkpoints.get({ agentId: sdkAgentId, blobId })) === null) {
|
|
227
|
+
await store.checkpoints.create({ agentId: sdkAgentId, blobId, data });
|
|
234
228
|
}
|
|
235
|
-
restored.add(entry.blobId);
|
|
236
229
|
}
|
|
237
230
|
await store.agents.create({
|
|
238
231
|
agent: { ...manifest.agent, activeRunId: null },
|
|
239
232
|
});
|
|
240
|
-
|
|
241
|
-
|
|
233
|
+
if (manifest.v === 2) {
|
|
234
|
+
rememberPacks(sdkAgentId, manifest.packs);
|
|
235
|
+
}
|
|
236
|
+
logger(`[bdk] conversation mirror: restored the conversation for ${sdkAgentId} (${blobs.size} checkpoint blob(s))`);
|
|
242
237
|
return true;
|
|
243
238
|
});
|
|
244
239
|
},
|
|
245
240
|
};
|
|
241
|
+
/** This sits on the resume critical path: one batched read when the sink has one. */
|
|
242
|
+
async function readMany(keys) {
|
|
243
|
+
return sink.getMany !== undefined
|
|
244
|
+
? await sink.getMany(keys)
|
|
245
|
+
: await mapBounded(keys, HYDRATE_DOWNLOAD_CONCURRENCY, (key) => sink.get(key));
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Every blob in `packs`, or `undefined` when a pack is absent or damaged
|
|
249
|
+
* (definitive store loss: the conversation is unrestorable).
|
|
250
|
+
*/
|
|
251
|
+
async function readPackedBlobs(sdkAgentId, packs) {
|
|
252
|
+
const bodies = await readMany(packs.map((pack) => agentSdkAgentStoreKeys.conversationPack(agentName, sdkAgentId, pack.id)));
|
|
253
|
+
const blobs = new Map();
|
|
254
|
+
for (const [index, pack] of packs.entries()) {
|
|
255
|
+
const body = bodies[index];
|
|
256
|
+
if (body === undefined) {
|
|
257
|
+
logger(`[bdk] conversation mirror: checkpoint pack ${pack.id} for ${sdkAgentId} is missing from the sink; treating the conversation as unrestorable`);
|
|
258
|
+
return undefined;
|
|
259
|
+
}
|
|
260
|
+
try {
|
|
261
|
+
for (const entry of decodeConversationPack(pack.id, bytesOf(body))) {
|
|
262
|
+
blobs.set(entry.blobId, entry.data);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
catch (error) {
|
|
266
|
+
logger(`[bdk] conversation mirror: checkpoint pack ${pack.id} for ${sdkAgentId} is unreadable (${describeError(error)}); treating the conversation as unrestorable`);
|
|
267
|
+
return undefined;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return blobs;
|
|
271
|
+
}
|
|
272
|
+
async function readLooseBlobs(sdkAgentId, blobIds) {
|
|
273
|
+
const bodies = await readMany(blobIds.map((blobId) => agentSdkAgentStoreKeys.conversationBlob(agentName, sdkAgentId, blobId)));
|
|
274
|
+
const blobs = new Map();
|
|
275
|
+
for (const [index, blobId] of blobIds.entries()) {
|
|
276
|
+
const body = bodies[index];
|
|
277
|
+
if (body !== undefined) {
|
|
278
|
+
blobs.set(blobId, bytesOf(body));
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return blobs;
|
|
282
|
+
}
|
|
246
283
|
}
|
|
247
|
-
/** Oldest-out cap on the per-conversation
|
|
284
|
+
/** Oldest-out cap on the per-conversation caches. */
|
|
248
285
|
const MAX_TRACKED_CONVERSATIONS = 1_000;
|
|
249
|
-
|
|
286
|
+
function rememberBounded(map, key, value) {
|
|
287
|
+
if (!map.has(key) && map.size >= MAX_TRACKED_CONVERSATIONS) {
|
|
288
|
+
const oldest = map.keys().next();
|
|
289
|
+
if (!oldest.done) {
|
|
290
|
+
map.delete(oldest.value);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
map.set(key, value);
|
|
294
|
+
}
|
|
295
|
+
/** Parallel downloads during hydrate when the sink has no batched read. */
|
|
250
296
|
const HYDRATE_DOWNLOAD_CONCURRENCY = 8;
|
|
251
|
-
|
|
297
|
+
/**
|
|
298
|
+
* Pack split size. Well under the store's single-part upload limit, and small
|
|
299
|
+
* enough that a re-pack holds little in memory at a time.
|
|
300
|
+
*/
|
|
301
|
+
const PACK_TARGET_BYTES = 8 * 1024 * 1024;
|
|
302
|
+
/**
|
|
303
|
+
* Packs a manifest may list before the next persist re-packs the whole
|
|
304
|
+
* conversation. Bounds the reads a resume costs; a re-pack rewrites the
|
|
305
|
+
* conversation's bytes once per this many persists that add blobs.
|
|
306
|
+
*/
|
|
307
|
+
const MAX_PACKS = 32;
|
|
308
|
+
function bytesOf(body) {
|
|
309
|
+
return body instanceof Uint8Array ? body : body.body;
|
|
310
|
+
}
|
|
252
311
|
/**
|
|
253
312
|
* Read the persisted manifest. Sink transport failures propagate (an
|
|
254
313
|
* outage must fail the follow-up, not fork the thread); everything the
|
|
@@ -259,26 +318,40 @@ async function readManifest(sink, agentName, sdkAgentId) {
|
|
|
259
318
|
if (body === undefined) {
|
|
260
319
|
return { outcome: "miss" };
|
|
261
320
|
}
|
|
262
|
-
const bytes = body instanceof Uint8Array ? body : body.body;
|
|
263
321
|
let parsed;
|
|
264
322
|
try {
|
|
265
|
-
parsed = JSON.parse(new TextDecoder().decode(
|
|
323
|
+
parsed = JSON.parse(new TextDecoder().decode(bytesOf(body)));
|
|
266
324
|
}
|
|
267
325
|
catch {
|
|
268
326
|
return { outcome: "unusable", reason: "not JSON" };
|
|
269
327
|
}
|
|
270
|
-
if (parsed === null || typeof parsed !== "object"
|
|
328
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
329
|
+
return { outcome: "unusable", reason: "malformed manifest" };
|
|
330
|
+
}
|
|
331
|
+
const version = parsed.v;
|
|
332
|
+
if (version !== 1 && version !== 2) {
|
|
271
333
|
return { outcome: "unusable", reason: "unsupported manifest version" };
|
|
272
334
|
}
|
|
273
335
|
const manifest = parsed;
|
|
274
336
|
if (manifest.agent === null ||
|
|
275
337
|
typeof manifest.agent !== "object" ||
|
|
276
338
|
manifest.agent.agentId !== sdkAgentId ||
|
|
277
|
-
!
|
|
339
|
+
!(manifest.v === 1 ? isStringArray(manifest.blobIds) : isPackRefArray(manifest.packs))) {
|
|
278
340
|
return { outcome: "unusable", reason: "malformed manifest" };
|
|
279
341
|
}
|
|
280
342
|
return { outcome: "found", manifest };
|
|
281
343
|
}
|
|
344
|
+
function isStringArray(value) {
|
|
345
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
346
|
+
}
|
|
347
|
+
function isPackRefArray(value) {
|
|
348
|
+
return (Array.isArray(value) &&
|
|
349
|
+
value.every((item) => item !== null &&
|
|
350
|
+
typeof item === "object" &&
|
|
351
|
+
typeof item.id === "string" &&
|
|
352
|
+
/^[0-9a-f]{64}$/.test(item.id) &&
|
|
353
|
+
isStringArray(item.blobIds)));
|
|
354
|
+
}
|
|
282
355
|
async function listCheckpointBlobIds(store, sdkAgentId) {
|
|
283
356
|
const blobIds = [];
|
|
284
357
|
let cursor;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire format for a conversation checkpoint pack: many content-addressed
|
|
3
|
+
* checkpoint blobs in one durable object, so a turn's new blobs cost one
|
|
4
|
+
* store write instead of one per blob.
|
|
5
|
+
*
|
|
6
|
+
* Layout (big-endian): the 8-byte magic, a u32 entry count, then per entry a
|
|
7
|
+
* u16 blob-id length, the UTF-8 blob id, a u32 data length and the data. The
|
|
8
|
+
* pack id is the SHA-256 of the whole encoding, so a pack is immutable and
|
|
9
|
+
* a re-upload after a failed persist lands on the same key.
|
|
10
|
+
*/
|
|
11
|
+
export interface ConversationPackEntry {
|
|
12
|
+
blobId: string;
|
|
13
|
+
data: Uint8Array;
|
|
14
|
+
}
|
|
15
|
+
export interface EncodedConversationPack {
|
|
16
|
+
packId: string;
|
|
17
|
+
body: Uint8Array;
|
|
18
|
+
}
|
|
19
|
+
/** Bytes an entry adds to a pack; used to split packs before encoding. */
|
|
20
|
+
export declare function conversationPackEntrySize(entry: ConversationPackEntry): number;
|
|
21
|
+
export declare function conversationPackId(body: Uint8Array): string;
|
|
22
|
+
export declare function encodeConversationPack(entries: readonly ConversationPackEntry[]): EncodedConversationPack;
|
|
23
|
+
/**
|
|
24
|
+
* Decode a pack read back from the store. Throws on any structural damage or
|
|
25
|
+
* when the bytes do not hash to `packId`; the caller treats that exactly like
|
|
26
|
+
* a missing pack.
|
|
27
|
+
*/
|
|
28
|
+
export declare function decodeConversationPack(packId: string, body: Uint8Array): ConversationPackEntry[];
|
|
29
|
+
//# sourceMappingURL=conversation-pack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation-pack.d.ts","sourceRoot":"","sources":["../../src/internal/conversation-pack.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,UAAU,CAAC;CAClB;AAMD,0EAA0E;AAC1E,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,qBAAqB,GAAG,MAAM,CAE9E;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAE3D;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,SAAS,qBAAqB,EAAE,GACxC,uBAAuB,CAgCzB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,qBAAqB,EAAE,CAoChG"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire format for a conversation checkpoint pack: many content-addressed
|
|
3
|
+
* checkpoint blobs in one durable object, so a turn's new blobs cost one
|
|
4
|
+
* store write instead of one per blob.
|
|
5
|
+
*
|
|
6
|
+
* Layout (big-endian): the 8-byte magic, a u32 entry count, then per entry a
|
|
7
|
+
* u16 blob-id length, the UTF-8 blob id, a u32 data length and the data. The
|
|
8
|
+
* pack id is the SHA-256 of the whole encoding, so a pack is immutable and
|
|
9
|
+
* a re-upload after a failed persist lands on the same key.
|
|
10
|
+
*/
|
|
11
|
+
import { createHash } from "node:crypto";
|
|
12
|
+
const MAGIC = new TextEncoder().encode("BDKCPK1\n");
|
|
13
|
+
const MAX_BLOB_ID_BYTES = 0xffff;
|
|
14
|
+
const MAX_ENTRY_BYTES = 0xffffffff;
|
|
15
|
+
/** Bytes an entry adds to a pack; used to split packs before encoding. */
|
|
16
|
+
export function conversationPackEntrySize(entry) {
|
|
17
|
+
return 2 + new TextEncoder().encode(entry.blobId).length + 4 + entry.data.length;
|
|
18
|
+
}
|
|
19
|
+
export function conversationPackId(body) {
|
|
20
|
+
return createHash("sha256").update(body).digest("hex");
|
|
21
|
+
}
|
|
22
|
+
export function encodeConversationPack(entries) {
|
|
23
|
+
const encoder = new TextEncoder();
|
|
24
|
+
const ids = entries.map((entry) => encoder.encode(entry.blobId));
|
|
25
|
+
let size = MAGIC.length + 4;
|
|
26
|
+
for (const [index, entry] of entries.entries()) {
|
|
27
|
+
const id = ids[index];
|
|
28
|
+
if (id.length === 0 || id.length > MAX_BLOB_ID_BYTES) {
|
|
29
|
+
throw new Error(`conversation pack: blob id "${entry.blobId}" has an unencodable length`);
|
|
30
|
+
}
|
|
31
|
+
if (entry.data.length > MAX_ENTRY_BYTES) {
|
|
32
|
+
throw new Error(`conversation pack: blob "${entry.blobId}" is too large to pack`);
|
|
33
|
+
}
|
|
34
|
+
size += 2 + id.length + 4 + entry.data.length;
|
|
35
|
+
}
|
|
36
|
+
const body = new Uint8Array(size);
|
|
37
|
+
const view = new DataView(body.buffer);
|
|
38
|
+
body.set(MAGIC, 0);
|
|
39
|
+
let offset = MAGIC.length;
|
|
40
|
+
view.setUint32(offset, entries.length);
|
|
41
|
+
offset += 4;
|
|
42
|
+
for (const [index, entry] of entries.entries()) {
|
|
43
|
+
const id = ids[index];
|
|
44
|
+
view.setUint16(offset, id.length);
|
|
45
|
+
offset += 2;
|
|
46
|
+
body.set(id, offset);
|
|
47
|
+
offset += id.length;
|
|
48
|
+
view.setUint32(offset, entry.data.length);
|
|
49
|
+
offset += 4;
|
|
50
|
+
body.set(entry.data, offset);
|
|
51
|
+
offset += entry.data.length;
|
|
52
|
+
}
|
|
53
|
+
return { packId: conversationPackId(body), body };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Decode a pack read back from the store. Throws on any structural damage or
|
|
57
|
+
* when the bytes do not hash to `packId`; the caller treats that exactly like
|
|
58
|
+
* a missing pack.
|
|
59
|
+
*/
|
|
60
|
+
export function decodeConversationPack(packId, body) {
|
|
61
|
+
if (conversationPackId(body) !== packId) {
|
|
62
|
+
throw new Error("conversation pack: content does not match its id");
|
|
63
|
+
}
|
|
64
|
+
if (body.length < MAGIC.length + 4 || MAGIC.some((byte, index) => body[index] !== byte)) {
|
|
65
|
+
throw new Error("conversation pack: unrecognized format");
|
|
66
|
+
}
|
|
67
|
+
const view = new DataView(body.buffer, body.byteOffset, body.byteLength);
|
|
68
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
69
|
+
let offset = MAGIC.length;
|
|
70
|
+
const count = view.getUint32(offset);
|
|
71
|
+
offset += 4;
|
|
72
|
+
const entries = [];
|
|
73
|
+
const need = (bytes) => {
|
|
74
|
+
if (offset + bytes > body.length) {
|
|
75
|
+
throw new Error("conversation pack: truncated");
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
for (let index = 0; index < count; index++) {
|
|
79
|
+
need(2);
|
|
80
|
+
const idLength = view.getUint16(offset);
|
|
81
|
+
offset += 2;
|
|
82
|
+
need(idLength);
|
|
83
|
+
const blobId = decoder.decode(body.subarray(offset, offset + idLength));
|
|
84
|
+
offset += idLength;
|
|
85
|
+
need(4);
|
|
86
|
+
const dataLength = view.getUint32(offset);
|
|
87
|
+
offset += 4;
|
|
88
|
+
need(dataLength);
|
|
89
|
+
entries.push({ blobId, data: body.slice(offset, offset + dataLength) });
|
|
90
|
+
offset += dataLength;
|
|
91
|
+
}
|
|
92
|
+
if (offset !== body.length) {
|
|
93
|
+
throw new Error("conversation pack: trailing bytes");
|
|
94
|
+
}
|
|
95
|
+
return entries;
|
|
96
|
+
}
|
|
@@ -355,6 +355,8 @@ export declare class AgentServeDeployClient {
|
|
|
355
355
|
persistent?: boolean;
|
|
356
356
|
/** Single-agent `hosting.image`; components carry their own. */
|
|
357
357
|
image?: string;
|
|
358
|
+
/** `init --template` scaffold name, reported for deploy analytics. */
|
|
359
|
+
template?: string;
|
|
358
360
|
}): Promise<AgentkitDeployAccepted>;
|
|
359
361
|
getAgentkitDeploy(args: {
|
|
360
362
|
teamId: number;
|