@cursor/july 0.1.1 → 0.1.3
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/AGENTS.md +40 -7
- package/README.md +33 -24
- package/dist/bin/agent-serve.d.ts +3 -1
- package/dist/bin/agent-serve.d.ts.map +1 -1
- package/dist/bin/agent-serve.js +466 -140
- package/dist/channels/github/api.d.ts.map +1 -1
- package/dist/channels/github/api.js +31 -14
- package/dist/channels/github/cursor-account.d.ts +43 -0
- package/dist/channels/github/cursor-account.d.ts.map +1 -0
- package/dist/channels/github/cursor-account.js +95 -0
- package/dist/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +46 -9
- package/dist/channels/github/index.d.ts +2 -2
- package/dist/channels/github/index.js +2 -2
- package/dist/channels/github/types.d.ts +17 -0
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.d.ts +8 -2
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +8 -0
- package/dist/channels/slack/types.d.ts +24 -3
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/channels/slack/types.js +15 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +8 -8
- package/dist/docs/assets/{ab.md.COdXkces.js → ab.md.BMCZ6Hd7.js} +3 -3
- package/dist/docs/assets/{ab.md.COdXkces.lean.js → ab.md.BMCZ6Hd7.lean.js} +1 -1
- package/dist/docs/assets/{app.DqfFEmJd.js → app.BR0RbIdx.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DtIOQzGj.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.BaLEdS15.js → VPLocalSearchBox.qjsVTneI.js} +1 -1
- package/dist/docs/assets/chunks/{theme.CZRvu_0q.js → theme.2Kg8jIp_.js} +2 -2
- package/dist/docs/assets/{deployment.md.Dx1TYNk5.js → deployment.md.DTKwE15Z.js} +3 -3
- package/dist/docs/assets/{deployment.md.Dx1TYNk5.lean.js → deployment.md.DTKwE15Z.lean.js} +1 -1
- package/dist/docs/assets/{evals.md.DPZ_MAnI.js → evals.md.DAgEc_hL.js} +3 -3
- package/dist/docs/assets/{guides_agent-to-agent.md.CrtrsySy.js → guides_agent-to-agent.md.Bpzgq2Pq.js} +1 -1
- package/dist/docs/assets/{guides_cloud-runtime.md.CYlNMTNp.js → guides_cloud-runtime.md.gVzabdQL.js} +1 -1
- package/dist/docs/assets/{guides_github.md.DwbKhCeS.js → guides_github.md.DOOCpqsW.js} +11 -4
- package/dist/docs/assets/{guides_github.md.DwbKhCeS.lean.js → guides_github.md.DOOCpqsW.lean.js} +1 -1
- package/dist/docs/assets/{guides_human-in-the-loop.md.Dvuctx7s.js → guides_human-in-the-loop.md.DlUqsp1S.js} +2 -2
- package/dist/docs/assets/{guides_slack.md.bv41fHfW.js → guides_slack.md.CCwqHvSV.js} +4 -4
- package/dist/docs/assets/{guides_slack.md.bv41fHfW.lean.js → guides_slack.md.CCwqHvSV.lean.js} +1 -1
- package/dist/docs/assets/{guides_webhooks.md.hFTik3lf.js → guides_webhooks.md.B1EswtUu.js} +2 -2
- package/dist/docs/assets/index.md.m81y7TY7.js +20 -0
- package/dist/docs/assets/{index.md.BPKcj5AI.lean.js → index.md.m81y7TY7.lean.js} +1 -1
- package/dist/docs/assets/quickstart.md.BU6Iwi_9.js +204 -0
- package/dist/docs/assets/quickstart.md.BU6Iwi_9.lean.js +1 -0
- package/dist/docs/assets/{reference_agent-config.md.Bpd7HQwf.js → reference_agent-config.md.DrW2JUM8.js} +4 -4
- package/dist/docs/assets/{reference_agent-config.md.Bpd7HQwf.lean.js → reference_agent-config.md.DrW2JUM8.lean.js} +1 -1
- package/dist/docs/assets/{reference_channels.md.D7JTR03W.js → reference_channels.md.DdmiKgqf.js} +4 -4
- package/dist/docs/assets/{reference_channels.md.D7JTR03W.lean.js → reference_channels.md.DdmiKgqf.lean.js} +1 -1
- package/dist/docs/assets/{reference_cli.md.DA730zCu.js → reference_cli.md.Bv6pOxcF.js} +11 -6
- package/dist/docs/assets/{reference_cli.md.DA730zCu.lean.js → reference_cli.md.Bv6pOxcF.lean.js} +1 -1
- package/dist/docs/assets/{reference_connections.md.C3vNH_DE.js → reference_connections.md.zaEYCLHT.js} +1 -1
- package/dist/docs/assets/{reference_hooks.md.BCEc3MyM.js → reference_hooks.md.DyLVfE1O.js} +1 -1
- package/dist/docs/assets/{reference_hooks.md.BCEc3MyM.lean.js → reference_hooks.md.DyLVfE1O.lean.js} +1 -1
- package/dist/docs/assets/{reference_http-api.md.DBAahtdz.js → reference_http-api.md.Dx_nmDG6.js} +1 -1
- package/dist/docs/assets/{reference_instructions.md.BC05LEQ8.js → reference_instructions.md.CgoV-YEb.js} +9 -7
- package/dist/docs/assets/{reference_instructions.md.BC05LEQ8.lean.js → reference_instructions.md.CgoV-YEb.lean.js} +1 -1
- package/dist/docs/assets/{reference_schedules.md.D7qijxLk.js → reference_schedules.md.w_F2mXB6.js} +2 -2
- package/dist/docs/assets/{reference_skills.md.VQnlBT3Q.js → reference_skills.md.B_jHN7JL.js} +3 -3
- package/dist/docs/assets/{reference_skills.md.VQnlBT3Q.lean.js → reference_skills.md.B_jHN7JL.lean.js} +1 -1
- package/dist/docs/assets/{reference_subagents.md.CIRAVcPK.js → reference_subagents.md.zWAMNfi1.js} +1 -1
- package/dist/docs/assets/{reference_tools.md.DF5kwlt0.js → reference_tools.md.CqgJroI0.js} +2 -2
- package/dist/docs/assets/scaffolding-agents.md.C3pTrmoE.js +1 -0
- package/dist/docs/assets/scaffolding-agents.md.C3pTrmoE.lean.js +1 -0
- package/dist/docs/assets/storage.md.CVnInNiN.js +17 -0
- package/dist/docs/assets/storage.md.CVnInNiN.lean.js +1 -0
- package/dist/docs/building-with-agents.html +4 -4
- package/dist/docs/concepts.html +4 -4
- package/dist/docs/deployment.html +7 -7
- package/dist/docs/evals.html +7 -7
- package/dist/docs/guides/agent-to-agent.html +6 -6
- package/dist/docs/guides/cloud-runtime.html +5 -5
- package/dist/docs/guides/github.html +14 -7
- package/dist/docs/guides/human-in-the-loop.html +6 -6
- package/dist/docs/guides/slack.html +8 -8
- package/dist/docs/guides/webhooks.html +6 -6
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +5 -5
- package/dist/docs/index.html +7 -7
- package/dist/docs/quickstart.html +195 -26
- package/dist/docs/reference/agent-config.html +7 -7
- package/dist/docs/reference/channels.html +8 -8
- package/dist/docs/reference/cli.html +14 -9
- package/dist/docs/reference/connections.html +5 -5
- package/dist/docs/reference/hooks.html +5 -5
- package/dist/docs/reference/http-api.html +6 -6
- package/dist/docs/reference/instructions.html +13 -11
- package/dist/docs/reference/playground.html +4 -4
- package/dist/docs/reference/project-layout.html +4 -4
- package/dist/docs/reference/schedules.html +7 -7
- package/dist/docs/reference/sessions.html +4 -4
- package/dist/docs/reference/skills.html +6 -6
- package/dist/docs/reference/subagents.html +6 -6
- package/dist/docs/reference/tools.html +7 -7
- package/dist/docs/scaffolding-agents.html +5 -5
- package/dist/docs/storage.html +41 -0
- package/dist/docs/troubleshooting.html +4 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/internal/chat-client.d.ts +29 -3
- package/dist/internal/chat-client.d.ts.map +1 -1
- package/dist/internal/chat-client.js +180 -27
- package/dist/internal/cli-ax.d.ts +75 -2
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +600 -52
- package/dist/internal/cli-cursor.d.ts.map +1 -1
- package/dist/internal/cli-cursor.js +8 -36
- package/dist/internal/cli-deploy.d.ts +108 -0
- package/dist/internal/cli-deploy.d.ts.map +1 -0
- package/dist/internal/cli-deploy.js +1009 -0
- package/dist/internal/cursor/backend-client.d.ts +10 -1
- package/dist/internal/cursor/backend-client.d.ts.map +1 -1
- package/dist/internal/cursor/backend-client.js +79 -1
- package/dist/internal/cursor/credentials.d.ts +9 -0
- package/dist/internal/cursor/credentials.d.ts.map +1 -1
- package/dist/internal/cursor/credentials.js +12 -0
- package/dist/internal/cursor/github-credentials.d.ts +44 -0
- package/dist/internal/cursor/github-credentials.d.ts.map +1 -0
- package/dist/internal/cursor/github-credentials.js +195 -0
- package/dist/internal/deploy-client.d.ts +184 -0
- package/dist/internal/deploy-client.d.ts.map +1 -0
- package/dist/internal/deploy-client.js +395 -0
- package/dist/internal/deploy-source.d.ts +35 -0
- package/dist/internal/deploy-source.d.ts.map +1 -0
- package/dist/internal/deploy-source.js +118 -0
- package/dist/internal/discovery.d.ts +11 -0
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +116 -19
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +1 -0
- package/dist/internal/eval-run-store.d.ts +22 -3
- package/dist/internal/eval-run-store.d.ts.map +1 -1
- package/dist/internal/eval-run-store.js +39 -20
- package/dist/internal/eval-runner.d.ts +2 -0
- package/dist/internal/eval-runner.d.ts.map +1 -1
- package/dist/internal/eval-runner.js +2 -0
- package/dist/internal/event-mapper.d.ts +54 -1
- package/dist/internal/event-mapper.d.ts.map +1 -1
- package/dist/internal/event-mapper.js +151 -41
- package/dist/internal/handleAgentServeTrigger.d.ts.map +1 -1
- package/dist/internal/handleAgentServeTrigger.js +10 -12
- package/dist/internal/host-platforms.d.ts +7 -2
- package/dist/internal/host-platforms.d.ts.map +1 -1
- package/dist/internal/host-platforms.js +15 -10
- package/dist/internal/hosting.d.ts +37 -0
- package/dist/internal/hosting.d.ts.map +1 -0
- package/dist/internal/hosting.js +67 -0
- package/dist/internal/init-project.d.ts +88 -1
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +221 -31
- package/dist/internal/install-cursor-skills.d.ts +64 -0
- package/dist/internal/install-cursor-skills.d.ts.map +1 -0
- package/dist/internal/install-cursor-skills.js +274 -0
- package/dist/internal/logs-client.d.ts +60 -0
- package/dist/internal/logs-client.d.ts.map +1 -0
- package/dist/internal/logs-client.js +311 -0
- package/dist/internal/open-browser.d.ts +5 -0
- package/dist/internal/open-browser.d.ts.map +1 -0
- package/dist/internal/open-browser.js +34 -0
- package/dist/internal/playground/toolchain.d.ts.map +1 -1
- package/dist/internal/playground/toolchain.js +16 -11
- package/dist/internal/playground-proxy.d.ts +69 -0
- package/dist/internal/playground-proxy.d.ts.map +1 -0
- package/dist/internal/playground-proxy.js +468 -0
- package/dist/internal/prompt-context.d.ts +35 -0
- package/dist/internal/prompt-context.d.ts.map +1 -0
- package/dist/internal/prompt-context.js +71 -0
- package/dist/internal/reminder-runner.d.ts +7 -0
- package/dist/internal/reminder-runner.d.ts.map +1 -1
- package/dist/internal/reminder-runner.js +50 -6
- package/dist/internal/reminder-store.d.ts +2 -0
- package/dist/internal/reminder-store.d.ts.map +1 -1
- package/dist/internal/reminder-store.js +18 -0
- package/dist/internal/request-headers.d.ts +11 -0
- package/dist/internal/request-headers.d.ts.map +1 -0
- package/dist/internal/request-headers.js +14 -0
- package/dist/internal/resolve-prod-target.d.ts +42 -0
- package/dist/internal/resolve-prod-target.d.ts.map +1 -0
- package/dist/internal/resolve-prod-target.js +111 -0
- package/dist/internal/run-client.d.ts +2 -2
- package/dist/internal/run-client.d.ts.map +1 -1
- package/dist/internal/run-client.js +38 -23
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +182 -45
- package/dist/internal/session-engine.d.ts +50 -1
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +267 -23
- package/dist/internal/sessions-client.d.ts +21 -0
- package/dist/internal/sessions-client.d.ts.map +1 -0
- package/dist/internal/sessions-client.js +168 -0
- package/dist/internal/storage-coordinator.d.ts +139 -0
- package/dist/internal/storage-coordinator.d.ts.map +1 -0
- package/dist/internal/storage-coordinator.js +499 -0
- package/dist/internal/stream-progress.d.ts.map +1 -1
- package/dist/internal/stream-progress.js +31 -3
- package/dist/internal/terminal-style.d.ts +22 -0
- package/dist/internal/terminal-style.d.ts.map +1 -0
- package/dist/internal/terminal-style.js +49 -0
- package/dist/internal/trajectory.d.ts +10 -5
- package/dist/internal/trajectory.d.ts.map +1 -1
- package/dist/internal/trajectory.js +82 -10
- package/dist/internal/workspace.d.ts +11 -0
- package/dist/internal/workspace.d.ts.map +1 -1
- package/dist/internal/workspace.js +38 -4
- package/dist/playground/assets/cursor-icons-outline-BxTT_FVJ.woff2 +0 -0
- package/dist/playground/assets/index-D-vo2lV_.css +1 -0
- package/dist/playground/assets/index-x60b9q2j.js +312 -0
- package/dist/playground/index.html +2 -2
- package/dist/storage.d.ts +204 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +153 -0
- package/dist/types.d.ts +70 -4
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +3 -2
- package/docs/guides/github.md +43 -8
- package/docs/guides/slack.md +1 -1
- package/docs/quickstart.md +350 -57
- package/docs/reference/cli.md +51 -3
- package/docs/reference/instructions.md +8 -6
- package/docs/scaffolding-agents.md +1 -1
- package/docs/storage.md +98 -0
- package/package.json +10 -1
- package/skills/ab/SKILL.md +8 -8
- package/skills/create-agent/SKILL.md +28 -22
- package/skills/debug/SKILL.md +11 -11
- package/skills/evals/SKILL.md +16 -16
- package/skills/framework-map/SKILL.md +6 -6
- package/skills/github/SKILL.md +22 -14
- package/skills/hillclimb/SKILL.md +10 -10
- package/skills/setup-slack/SKILL.md +13 -13
- package/src/bin/agent-serve.ts +552 -179
- package/src/channels/github/api.ts +42 -23
- package/src/channels/github/cursor-account.ts +165 -0
- package/src/channels/github/github-channel.ts +66 -6
- package/src/channels/github/index.ts +2 -2
- package/src/channels/github/types.ts +19 -0
- package/src/channels/slack/slack-channel.ts +17 -3
- package/src/channels/slack/types.ts +44 -3
- package/src/index.ts +13 -0
- package/src/internal/chat-client.ts +252 -37
- package/src/internal/cli-ax.ts +722 -72
- package/src/internal/cli-cursor.ts +14 -42
- package/src/internal/cli-deploy.ts +1319 -0
- package/src/internal/cursor/backend-client.ts +103 -1
- package/src/internal/cursor/credentials.ts +14 -0
- package/src/internal/cursor/github-credentials.ts +248 -0
- package/src/internal/deploy-client.ts +632 -0
- package/src/internal/deploy-source.ts +133 -0
- package/src/internal/discovery.ts +141 -17
- package/src/internal/distribution.ts +1 -0
- package/src/internal/eval-run-store.ts +50 -20
- package/src/internal/eval-runner.ts +5 -0
- package/src/internal/event-mapper.ts +222 -42
- package/src/internal/handleAgentServeTrigger.ts +10 -12
- package/src/internal/host-platforms.ts +28 -11
- package/src/internal/hosting.ts +77 -0
- package/src/internal/init-project.ts +333 -51
- package/src/internal/install-cursor-skills.ts +327 -0
- package/src/internal/logs-client.ts +442 -0
- package/src/internal/open-browser.ts +41 -0
- package/src/internal/playground/toolchain.ts +15 -13
- package/src/internal/playground-proxy.ts +576 -0
- package/src/internal/prompt-context.ts +95 -0
- package/src/internal/reminder-runner.ts +50 -6
- package/src/internal/reminder-store.ts +21 -0
- package/src/internal/request-headers.ts +23 -0
- package/src/internal/resolve-prod-target.ts +150 -0
- package/src/internal/run-client.ts +39 -36
- package/src/internal/server.ts +223 -28
- package/src/internal/session-engine.ts +307 -10
- package/src/internal/sessions-client.ts +182 -0
- package/src/internal/storage-coordinator.ts +615 -0
- package/src/internal/stream-progress.ts +36 -2
- package/src/internal/terminal-style.ts +74 -0
- package/src/internal/trajectory.ts +91 -13
- package/src/internal/workspace.ts +42 -4
- package/src/storage.ts +325 -0
- package/src/types.ts +82 -8
- package/dist/docs/assets/chunks/@localSearchIndexroot.CcVk1uKq.js +0 -1
- package/dist/docs/assets/index.md.BPKcj5AI.js +0 -20
- package/dist/docs/assets/quickstart.md.tVPiGK_L.js +0 -35
- package/dist/docs/assets/quickstart.md.tVPiGK_L.lean.js +0 -1
- package/dist/docs/assets/scaffolding-agents.md.CyYfWGdc.js +0 -1
- package/dist/docs/assets/scaffolding-agents.md.CyYfWGdc.lean.js +0 -1
- package/dist/playground/assets/cursor-icons-outline-oY2V_mvK.woff2 +0 -0
- package/dist/playground/assets/index-1K-hG-7p.css +0 -1
- package/dist/playground/assets/index-FlWjhg3x.js +0 -79
- /package/dist/docs/assets/{evals.md.DPZ_MAnI.lean.js → evals.md.DAgEc_hL.lean.js} +0 -0
- /package/dist/docs/assets/{guides_agent-to-agent.md.CrtrsySy.lean.js → guides_agent-to-agent.md.Bpzgq2Pq.lean.js} +0 -0
- /package/dist/docs/assets/{guides_cloud-runtime.md.CYlNMTNp.lean.js → guides_cloud-runtime.md.gVzabdQL.lean.js} +0 -0
- /package/dist/docs/assets/{guides_human-in-the-loop.md.Dvuctx7s.lean.js → guides_human-in-the-loop.md.DlUqsp1S.lean.js} +0 -0
- /package/dist/docs/assets/{guides_webhooks.md.hFTik3lf.lean.js → guides_webhooks.md.B1EswtUu.lean.js} +0 -0
- /package/dist/docs/assets/{reference_connections.md.C3vNH_DE.lean.js → reference_connections.md.zaEYCLHT.lean.js} +0 -0
- /package/dist/docs/assets/{reference_http-api.md.DBAahtdz.lean.js → reference_http-api.md.Dx_nmDG6.lean.js} +0 -0
- /package/dist/docs/assets/{reference_schedules.md.D7qijxLk.lean.js → reference_schedules.md.w_F2mXB6.lean.js} +0 -0
- /package/dist/docs/assets/{reference_subagents.md.CIRAVcPK.lean.js → reference_subagents.md.zWAMNfi1.lean.js} +0 -0
- /package/dist/docs/assets/{reference_tools.md.DF5kwlt0.lean.js → reference_tools.md.CqgJroI0.lean.js} +0 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tiny ANSI helpers for CLI progress output. Respects `NO_COLOR` and
|
|
3
|
+
* only enables when the target stream is a TTY.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { clearScreenDown, cursorTo } from "node:readline";
|
|
7
|
+
|
|
8
|
+
export interface TerminalPalette {
|
|
9
|
+
dim: (s: string) => string;
|
|
10
|
+
bold: (s: string) => string;
|
|
11
|
+
cyan: (s: string) => string;
|
|
12
|
+
green: (s: string) => string;
|
|
13
|
+
red: (s: string) => string;
|
|
14
|
+
magenta: (s: string) => string;
|
|
15
|
+
yellow: (s: string) => string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const IDENTITY = (s: string): string => s;
|
|
19
|
+
|
|
20
|
+
const PLAIN: TerminalPalette = {
|
|
21
|
+
dim: IDENTITY,
|
|
22
|
+
bold: IDENTITY,
|
|
23
|
+
cyan: IDENTITY,
|
|
24
|
+
green: IDENTITY,
|
|
25
|
+
red: IDENTITY,
|
|
26
|
+
magenta: IDENTITY,
|
|
27
|
+
yellow: IDENTITY,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export function colorEnabledFor(
|
|
31
|
+
stream: NodeJS.WriteStream = process.stdout
|
|
32
|
+
): boolean {
|
|
33
|
+
return stream.isTTY === true && process.env.NO_COLOR === undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function makePalette(enabled: boolean): TerminalPalette {
|
|
37
|
+
if (!enabled) {
|
|
38
|
+
return PLAIN;
|
|
39
|
+
}
|
|
40
|
+
const wrap =
|
|
41
|
+
(code: string) =>
|
|
42
|
+
(s: string): string =>
|
|
43
|
+
`\u001b[${code}m${s}\u001b[0m`;
|
|
44
|
+
return {
|
|
45
|
+
dim: wrap("2"),
|
|
46
|
+
bold: wrap("1"),
|
|
47
|
+
cyan: wrap("36"),
|
|
48
|
+
green: wrap("32"),
|
|
49
|
+
red: wrap("31"),
|
|
50
|
+
magenta: wrap("35"),
|
|
51
|
+
yellow: wrap("33"),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Palette for human-facing stdout (init summaries, next steps). */
|
|
56
|
+
export function stdoutPalette(): TerminalPalette {
|
|
57
|
+
return makePalette(colorEnabledFor(process.stdout));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Palette for human-facing stderr (login progress, warnings). */
|
|
61
|
+
export function stderrPalette(): TerminalPalette {
|
|
62
|
+
return makePalette(colorEnabledFor(process.stderr));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Wipe the terminal before a `dev` Enter-to-restart cycle. */
|
|
66
|
+
export function clearTerminal(
|
|
67
|
+
stream: NodeJS.WriteStream = process.stdout
|
|
68
|
+
): void {
|
|
69
|
+
if (stream.isTTY !== true) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
cursorTo(stream, 0, 0);
|
|
73
|
+
clearScreenDown(stream);
|
|
74
|
+
}
|
|
@@ -17,6 +17,16 @@ export interface TrajectoryToolCall {
|
|
|
17
17
|
isError: boolean;
|
|
18
18
|
/** Present when the tool required human approval before execute. */
|
|
19
19
|
approvalDecision?: "approve" | "deny";
|
|
20
|
+
/** Parent task/subagent call id when this tool ran inside a subagent. */
|
|
21
|
+
parentCallId?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface TrajectorySubagent {
|
|
25
|
+
callId: string;
|
|
26
|
+
name?: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
/** Nested tool calls streamed while this subagent was running. */
|
|
29
|
+
toolCalls: TrajectoryToolCall[];
|
|
20
30
|
}
|
|
21
31
|
|
|
22
32
|
export interface TrajectoryTurn {
|
|
@@ -25,7 +35,7 @@ export interface TrajectoryTurn {
|
|
|
25
35
|
assistantText: string;
|
|
26
36
|
reasoningText?: string;
|
|
27
37
|
toolCalls: TrajectoryToolCall[];
|
|
28
|
-
subagents:
|
|
38
|
+
subagents: TrajectorySubagent[];
|
|
29
39
|
usage?: TurnUsage;
|
|
30
40
|
result?: string;
|
|
31
41
|
failed: boolean;
|
|
@@ -95,12 +105,20 @@ export function buildTrajectory(events: SessionEvent[]): Trajectory {
|
|
|
95
105
|
break;
|
|
96
106
|
}
|
|
97
107
|
case "message.completed": {
|
|
108
|
+
// Nested subagent text must not overwrite the parent turn reply.
|
|
109
|
+
if (event.data.parentCallId !== undefined) {
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
98
112
|
if (event.turnId !== undefined && event.data.text !== "") {
|
|
99
113
|
ensureTurn(event.turnId).assistantText = event.data.text;
|
|
100
114
|
}
|
|
101
115
|
break;
|
|
102
116
|
}
|
|
103
117
|
case "reasoning.appended": {
|
|
118
|
+
// Nested subagent thinking stays off the parent turn transcript.
|
|
119
|
+
if (event.data.parentCallId !== undefined) {
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
104
122
|
if (event.turnId !== undefined) {
|
|
105
123
|
ensureTurn(event.turnId).reasoningText = event.data.text;
|
|
106
124
|
}
|
|
@@ -112,14 +130,28 @@ export function buildTrajectory(events: SessionEvent[]): Trajectory {
|
|
|
112
130
|
}
|
|
113
131
|
const turn = ensureTurn(event.turnId);
|
|
114
132
|
for (const call of event.data.calls) {
|
|
133
|
+
const parentCallId = call.parentCallId ?? event.data.parentCallId;
|
|
134
|
+
// Keep bare top-level `task` in toolCalls so evals like
|
|
135
|
+
// `calledTool("task")` / `notCalledTool("task")` keep working.
|
|
136
|
+
// Nested progress also appears under `subagents`.
|
|
115
137
|
const entry: TrajectoryToolCall = {
|
|
116
138
|
callId: call.callId,
|
|
117
139
|
toolName: call.toolName,
|
|
118
140
|
isError: false,
|
|
119
|
-
|
|
141
|
+
args: call.args,
|
|
142
|
+
parentCallId,
|
|
120
143
|
};
|
|
121
144
|
pendingCalls.set(call.callId, entry);
|
|
122
|
-
|
|
145
|
+
if (parentCallId !== undefined) {
|
|
146
|
+
let sub = turn.subagents.find((s) => s.callId === parentCallId);
|
|
147
|
+
if (sub === undefined) {
|
|
148
|
+
sub = { callId: parentCallId, toolCalls: [] };
|
|
149
|
+
turn.subagents.push(sub);
|
|
150
|
+
}
|
|
151
|
+
sub.toolCalls.push(entry);
|
|
152
|
+
} else {
|
|
153
|
+
turn.toolCalls.push(entry);
|
|
154
|
+
}
|
|
123
155
|
}
|
|
124
156
|
break;
|
|
125
157
|
}
|
|
@@ -141,24 +173,49 @@ export function buildTrajectory(events: SessionEvent[]): Trajectory {
|
|
|
141
173
|
pending.isError = event.data.isError;
|
|
142
174
|
pending.toolName = event.data.toolName;
|
|
143
175
|
} else if (event.turnId !== undefined) {
|
|
144
|
-
ensureTurn(event.turnId)
|
|
176
|
+
const turn = ensureTurn(event.turnId);
|
|
177
|
+
const entry: TrajectoryToolCall = {
|
|
145
178
|
callId: event.data.callId,
|
|
146
179
|
toolName: event.data.toolName,
|
|
147
180
|
output: event.data.output,
|
|
148
181
|
isError: event.data.isError,
|
|
149
|
-
|
|
182
|
+
parentCallId: event.data.parentCallId,
|
|
183
|
+
};
|
|
184
|
+
if (event.data.parentCallId !== undefined) {
|
|
185
|
+
let sub = turn.subagents.find(
|
|
186
|
+
(s) => s.callId === event.data.parentCallId
|
|
187
|
+
);
|
|
188
|
+
if (sub === undefined) {
|
|
189
|
+
sub = {
|
|
190
|
+
callId: event.data.parentCallId,
|
|
191
|
+
toolCalls: [],
|
|
192
|
+
};
|
|
193
|
+
turn.subagents.push(sub);
|
|
194
|
+
}
|
|
195
|
+
sub.toolCalls.push(entry);
|
|
196
|
+
} else {
|
|
197
|
+
turn.toolCalls.push(entry);
|
|
198
|
+
}
|
|
150
199
|
}
|
|
151
200
|
break;
|
|
152
201
|
}
|
|
153
202
|
case "subagent.called": {
|
|
154
203
|
if (event.turnId !== undefined) {
|
|
155
|
-
ensureTurn(event.turnId)
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
204
|
+
const turn = ensureTurn(event.turnId);
|
|
205
|
+
const existing = turn.subagents.find(
|
|
206
|
+
(s) => s.callId === event.data.callId
|
|
207
|
+
);
|
|
208
|
+
if (existing !== undefined) {
|
|
209
|
+
existing.name = event.data.name;
|
|
210
|
+
existing.description = event.data.description;
|
|
211
|
+
} else {
|
|
212
|
+
turn.subagents.push({
|
|
213
|
+
callId: event.data.callId,
|
|
214
|
+
name: event.data.name,
|
|
215
|
+
description: event.data.description,
|
|
216
|
+
toolCalls: [],
|
|
217
|
+
});
|
|
218
|
+
}
|
|
162
219
|
}
|
|
163
220
|
break;
|
|
164
221
|
}
|
|
@@ -202,7 +259,13 @@ export function buildTrajectory(events: SessionEvent[]): Trajectory {
|
|
|
202
259
|
|
|
203
260
|
const turns = turnOrder.map((id) => turnsById.get(id)!);
|
|
204
261
|
const turnsFailed = turns.filter((t) => t.failed).length;
|
|
205
|
-
const toolCallCount = turns.reduce(
|
|
262
|
+
const toolCallCount = turns.reduce(
|
|
263
|
+
(n, t) =>
|
|
264
|
+
n +
|
|
265
|
+
t.toolCalls.length +
|
|
266
|
+
t.subagents.reduce((m, s) => m + s.toolCalls.length, 0),
|
|
267
|
+
0
|
|
268
|
+
);
|
|
206
269
|
const lastGood = [...turns].reverse().find((t) => t.assistantText !== "");
|
|
207
270
|
return {
|
|
208
271
|
sessionId,
|
|
@@ -270,6 +333,21 @@ export function formatTrajectoryText(trajectory: Trajectory): string {
|
|
|
270
333
|
lines.push(` ${truncate(stringify(call.output), 160)}`);
|
|
271
334
|
}
|
|
272
335
|
}
|
|
336
|
+
for (const sub of turn.subagents) {
|
|
337
|
+
const label = sub.name ?? "subagent";
|
|
338
|
+
const detail =
|
|
339
|
+
sub.description !== undefined
|
|
340
|
+
? ` — ${truncate(sub.description, 80)}`
|
|
341
|
+
: "";
|
|
342
|
+
lines.push(` task ${label}${detail}`);
|
|
343
|
+
for (const call of sub.toolCalls) {
|
|
344
|
+
const mark = call.isError ? "✗" : "→";
|
|
345
|
+
lines.push(` ${mark} ${call.toolName}${formatArgs(call.args)}`);
|
|
346
|
+
if (call.output !== undefined) {
|
|
347
|
+
lines.push(` ${truncate(stringify(call.output), 160)}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
273
351
|
if (turn.assistantText.length > 0) {
|
|
274
352
|
lines.push(` agent: ${truncate(turn.assistantText, 400)}`);
|
|
275
353
|
}
|
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
ResolvedAgent,
|
|
16
16
|
} from "../types.js";
|
|
17
17
|
import { parseFrontmatter, serializeFrontmatter } from "./frontmatter.js";
|
|
18
|
+
import { buildIdentitySection } from "./prompt-context.js";
|
|
18
19
|
|
|
19
20
|
export interface MaterializeWorkspaceOptions {
|
|
20
21
|
agent: ResolvedAgent;
|
|
@@ -31,6 +32,41 @@ export function agentToolScriptPath(toolName: string): string {
|
|
|
31
32
|
return `.agent-serve/tools/${toolName}.sh`;
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Always-on identity line prepended when composing instructions. Sourced from
|
|
37
|
+
* {@link ResolvedAgent.name} (`defineAgent({ name })`, else package/dir name)
|
|
38
|
+
* so agents know who they are without restating it in authored prose.
|
|
39
|
+
*/
|
|
40
|
+
export function buildIdentityPreamble(name: string): string {
|
|
41
|
+
const trimmed = name.trim();
|
|
42
|
+
if (trimmed === "") {
|
|
43
|
+
return "";
|
|
44
|
+
}
|
|
45
|
+
return `You are ${trimmed}. That is your name; use it when people ask who you are.`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Prepend {@link buildIdentityPreamble} to authored instructions. Used when
|
|
50
|
+
* loading `instructions.md` / `.ts` / dir into {@link ResolvedAgent.instructions}.
|
|
51
|
+
*/
|
|
52
|
+
export function withIdentityPreamble(
|
|
53
|
+
name: string,
|
|
54
|
+
instructions: string | undefined
|
|
55
|
+
): string | undefined {
|
|
56
|
+
const preamble = buildIdentityPreamble(name);
|
|
57
|
+
const body = instructions?.trim() ?? "";
|
|
58
|
+
if (preamble === "" && body === "") {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
if (preamble === "") {
|
|
62
|
+
return body === "" ? undefined : body;
|
|
63
|
+
}
|
|
64
|
+
if (body === "") {
|
|
65
|
+
return preamble;
|
|
66
|
+
}
|
|
67
|
+
return `${preamble}\n\n${body}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
34
70
|
/**
|
|
35
71
|
* Markdown catalog of agent-side tools for AGENTS.md / cloud first prompt.
|
|
36
72
|
*
|
|
@@ -96,12 +132,14 @@ export async function materializeWorkspace(
|
|
|
96
132
|
const agentToolsSection = buildAgentToolsCatalog(agent.tools, {
|
|
97
133
|
includeScripts: false,
|
|
98
134
|
});
|
|
99
|
-
const agentsMd = [
|
|
135
|
+
const agentsMd = [
|
|
136
|
+
agent.instructions?.trim() ?? "",
|
|
137
|
+
buildIdentitySection(agent),
|
|
138
|
+
agentToolsSection,
|
|
139
|
+
]
|
|
100
140
|
.filter((part) => part !== "")
|
|
101
141
|
.join("\n\n");
|
|
102
|
-
|
|
103
|
-
await writeFile(join(workspaceDir, "AGENTS.md"), agentsMd, "utf8");
|
|
104
|
-
}
|
|
142
|
+
await writeFile(join(workspaceDir, "AGENTS.md"), agentsMd, "utf8");
|
|
105
143
|
|
|
106
144
|
for (const tool of agent.tools) {
|
|
107
145
|
if (tool.execution !== "agent" || tool.script === undefined) {
|
package/src/storage.ts
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable storage plug-in for agent-serve.
|
|
3
|
+
*
|
|
4
|
+
* Author `agent/storage.ts` with {@link defineStorage} to mirror the
|
|
5
|
+
* framework's durable state into storage you own (a database, S3, a data
|
|
6
|
+
* pipeline, …). Without it, state lives under `--state-root` on local disk
|
|
7
|
+
* only (and eval/A/B history follows the narrower `persistRuns` /
|
|
8
|
+
* `persistSamples` / `persistSnapshots` hooks).
|
|
9
|
+
*
|
|
10
|
+
* The sink is a plain key-value store — four functions, no schema:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { defineStorage } from "@anysphere/agent-serve/storage";
|
|
14
|
+
*
|
|
15
|
+
* export default defineStorage({
|
|
16
|
+
* put: (key, value) => db.upsert(key, value),
|
|
17
|
+
* get: (key) => db.get(key),
|
|
18
|
+
* delete: (key) => db.delete(key),
|
|
19
|
+
* list: (prefix) => db.listByPrefix(prefix), // [{ key, value }] in key order
|
|
20
|
+
* });
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* The **framework mints every key** from a stable, versioned scheme (see
|
|
24
|
+
* {@link storageKeys}) and decides **when** to call the sink: session
|
|
25
|
+
* records and event chunks flush when a turn's handlers have settled,
|
|
26
|
+
* reads happen at serve start (bulk restore), on continuation-token misses
|
|
27
|
+
* (lazy restore), and at playground hydration. Authors do not schedule
|
|
28
|
+
* reads or writes — the only timing knobs are {@link StoragePolicy}'s
|
|
29
|
+
* `debounceMs` (event write batching) and `restore` (startup hydration).
|
|
30
|
+
*
|
|
31
|
+
* Because keys are opaque strings to the sink, new kinds of durable state
|
|
32
|
+
* (channel cursors, thread affinity, …) are new key prefixes — existing
|
|
33
|
+
* sinks store them with no code changes.
|
|
34
|
+
*
|
|
35
|
+
* Delivery semantics: writes are **serialized** (one sink call in flight
|
|
36
|
+
* per agent, in order), **bounded** (a sink that falls behind sheds writes
|
|
37
|
+
* rather than growing memory), and **at-most-once** — a throwing `put` is
|
|
38
|
+
* logged and dropped, never retried, and never fails a turn. The local
|
|
39
|
+
* event log under `--state-root` remains the live source of truth; this
|
|
40
|
+
* interface is the durable mirror.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
import { createHash } from "node:crypto";
|
|
44
|
+
import { brandDefinition } from "./internal/brand.js";
|
|
45
|
+
import type { JsonValue } from "./types.js";
|
|
46
|
+
|
|
47
|
+
// ============================================================================
|
|
48
|
+
// Sink interface
|
|
49
|
+
// ============================================================================
|
|
50
|
+
|
|
51
|
+
/** One `{ key, value }` pair returned by {@link StorageConfig.list}. */
|
|
52
|
+
export interface StorageEntry {
|
|
53
|
+
key: string;
|
|
54
|
+
value: JsonValue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Context passed to every sink call. */
|
|
58
|
+
export interface StorageContext {
|
|
59
|
+
/** Agent name (also baked into every key; see {@link storageKeys}). */
|
|
60
|
+
agentName: string;
|
|
61
|
+
/** Absolute agent project root (directory that contains `agent/`). */
|
|
62
|
+
projectRoot: string;
|
|
63
|
+
/**
|
|
64
|
+
* Why the framework is calling:
|
|
65
|
+
* - `"policy"` — a flush trigger fired (turn end, debounce, change)
|
|
66
|
+
* - `"shutdown"` — the serve process is draining; last chance to write
|
|
67
|
+
* - `"restore"` — serve start or a lazy restore; reads rebuilding state
|
|
68
|
+
*/
|
|
69
|
+
reason: "policy" | "shutdown" | "restore";
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface StoragePolicy {
|
|
73
|
+
/**
|
|
74
|
+
* Batch event-chunk writes on a quiet-period timer instead of flushing
|
|
75
|
+
* once per turn. The debounce **spans turn boundaries** — a rapid
|
|
76
|
+
* multi-turn exchange becomes one write when the session goes quiet —
|
|
77
|
+
* so it is the right choice for chatty sessions where per-turn writes
|
|
78
|
+
* are too many. Unset (default): one event chunk per turn.
|
|
79
|
+
*/
|
|
80
|
+
debounceMs?: number;
|
|
81
|
+
/**
|
|
82
|
+
* Guardrails for the **startup bulk restore**. Whatever `list` returns
|
|
83
|
+
* is filtered to these caps before anything is written to local disk, so
|
|
84
|
+
* a large store cannot blow up `--state-root` or stall serve start.
|
|
85
|
+
* Newest sessions (by `updatedAt`) win within each cap.
|
|
86
|
+
*
|
|
87
|
+
* `"off"` disables the startup restore entirely — sessions then restore
|
|
88
|
+
* one at a time as follow-ups actually arrive (lazy-only; recommended
|
|
89
|
+
* for high-traffic deployments).
|
|
90
|
+
*/
|
|
91
|
+
restore?: StorageRestorePolicy | "off";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Caps applied to the startup bulk restore. See {@link StoragePolicy.restore}. */
|
|
95
|
+
export interface StorageRestorePolicy {
|
|
96
|
+
/** Max sessions materialized (default {@link STORAGE_DEFAULT_RESTORE_MAX_SESSIONS}). */
|
|
97
|
+
maxSessions?: number;
|
|
98
|
+
/**
|
|
99
|
+
* Skip sessions whose `updatedAt` is older than this (default
|
|
100
|
+
* {@link STORAGE_DEFAULT_RESTORE_MAX_AGE_MS}). Older sessions remain
|
|
101
|
+
* reachable lazily on their next follow-up.
|
|
102
|
+
*/
|
|
103
|
+
maxAgeMs?: number;
|
|
104
|
+
/**
|
|
105
|
+
* Stop restoring once this many bytes of records + events have been
|
|
106
|
+
* written (default {@link STORAGE_DEFAULT_RESTORE_MAX_TOTAL_BYTES}).
|
|
107
|
+
* Checked before each session is written, so one oversized stream
|
|
108
|
+
* cannot blow past the budget.
|
|
109
|
+
*/
|
|
110
|
+
maxTotalBytes?: number;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface StorageConfig {
|
|
114
|
+
/** Optional label surfaced on `GET /v1/info` diagnostics. */
|
|
115
|
+
name?: string;
|
|
116
|
+
/** Timing knobs; see {@link StoragePolicy}. */
|
|
117
|
+
policy?: StoragePolicy;
|
|
118
|
+
/**
|
|
119
|
+
* Store one value under a key (upsert, last-write-wins). Called on the
|
|
120
|
+
* framework's schedule — never concurrently, always in order. Keep it
|
|
121
|
+
* fast or buffer internally: the delivery queue is bounded, so a sink
|
|
122
|
+
* that falls behind sustained traffic sheds writes (logged) instead of
|
|
123
|
+
* growing memory; it never stalls the agent loop.
|
|
124
|
+
*/
|
|
125
|
+
put(key: string, value: JsonValue, ctx: StorageContext): void | Promise<void>;
|
|
126
|
+
/** Remove a key. Optional — without it, deletions are skipped. */
|
|
127
|
+
delete?(key: string, ctx: StorageContext): void | Promise<void>;
|
|
128
|
+
/**
|
|
129
|
+
* Point lookup. Optional — required for **lazy restore** (resolving a
|
|
130
|
+
* continuation token on a replacement host) and the A/B backfill.
|
|
131
|
+
* Return `undefined`/`null` only for a **definitive** miss: on the lazy
|
|
132
|
+
* restore path a throw propagates and fails the follow-up (retryable) —
|
|
133
|
+
* a store outage must not read as "unknown token", which would fork the
|
|
134
|
+
* conversation onto a new session.
|
|
135
|
+
*/
|
|
136
|
+
get?(
|
|
137
|
+
key: string,
|
|
138
|
+
ctx: StorageContext
|
|
139
|
+
): JsonValue | undefined | null | Promise<JsonValue | undefined | null>;
|
|
140
|
+
/**
|
|
141
|
+
* All entries under a key prefix, in ascending key order. Optional —
|
|
142
|
+
* required for the **startup bulk restore** (sessions + event streams)
|
|
143
|
+
* and playground eval history.
|
|
144
|
+
*/
|
|
145
|
+
list?(
|
|
146
|
+
prefix: string,
|
|
147
|
+
ctx: StorageContext
|
|
148
|
+
): StorageEntry[] | Promise<StorageEntry[]>;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export type StorageDefinition = StorageConfig & {
|
|
152
|
+
readonly __agentServe: "storage";
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Author the project storage sink (`agent/storage.ts`, default
|
|
157
|
+
* export). See the module doc for semantics and an example.
|
|
158
|
+
*/
|
|
159
|
+
export function defineStorage(config: StorageConfig): StorageDefinition {
|
|
160
|
+
if (typeof config.put !== "function") {
|
|
161
|
+
throw new Error("defineStorage: config.put must be a function");
|
|
162
|
+
}
|
|
163
|
+
for (const hook of ["get", "list", "delete"] as const) {
|
|
164
|
+
const value = config[hook];
|
|
165
|
+
if (value !== undefined && typeof value !== "function") {
|
|
166
|
+
throw new Error(
|
|
167
|
+
`defineStorage: config.${hook} must be a function when set`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// Validate eagerly so a bad policy fails at discovery, not first flush.
|
|
172
|
+
resolveStoragePolicy(config.policy);
|
|
173
|
+
return brandDefinition("storage", config);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ============================================================================
|
|
177
|
+
// Key scheme
|
|
178
|
+
// ============================================================================
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Framework-owned key root. All {@link storageKeys} values live under this
|
|
182
|
+
* prefix so a shared store can route or namespace agentkit data.
|
|
183
|
+
*/
|
|
184
|
+
export const STORAGE_KEY_ROOT = "agentkit/v1" as const;
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* The framework-owned key scheme. Keys are a **stable, versioned contract**
|
|
188
|
+
* under {@link STORAGE_KEY_ROOT}: sinks may treat them as opaque strings, or
|
|
189
|
+
* route on prefixes (e.g. event chunks to object storage, everything else
|
|
190
|
+
* to a database). Channel ids and continuation tokens are the only segments
|
|
191
|
+
* that may contain caller-controlled characters; they are URI-encoded, and
|
|
192
|
+
* a segment whose encoding exceeds {@link MAX_KEY_SEGMENT_BYTES} is replaced
|
|
193
|
+
* by a `sha256:…` digest — so every minted key has a bounded length that any
|
|
194
|
+
* backend (VARCHAR columns, btree index tuples, S3 key limits) can store,
|
|
195
|
+
* no matter what a caller stuffs into a token. The substitution is
|
|
196
|
+
* deterministic: writes and continuation lookups build the same key.
|
|
197
|
+
*
|
|
198
|
+
* | Key | Value |
|
|
199
|
+
* | --- | --- |
|
|
200
|
+
* | `agentkit/v1/{agent}/session/{sessionId}` | `SessionRecord` |
|
|
201
|
+
* | `agentkit/v1/{agent}/session-events/{sessionId}/{index}` | `SessionEvent[]` chunk (index = first event's index, zero-padded) |
|
|
202
|
+
* | `agentkit/v1/{agent}/continuation/{channelId}/{token}` | `{ sessionId }` |
|
|
203
|
+
* | `agentkit/v1/{agent}/reminder/{reminderId}` | `ReminderRecord` |
|
|
204
|
+
* | `agentkit/v1/{agent}/eval-run/{runId}` | `EvalRunSnapshot` |
|
|
205
|
+
* | `agentkit/v1/{agent}/ab-sample/{sessionId}/{at}` | `ABMetricSample` |
|
|
206
|
+
* | `agentkit/v1/{agent}/ab-snapshot` | latest aggregate `ABSnapshot` |
|
|
207
|
+
*/
|
|
208
|
+
export const storageKeys = {
|
|
209
|
+
session: (agent: string, sessionId: string): string =>
|
|
210
|
+
`${STORAGE_KEY_ROOT}/${agent}/session/${sessionId}`,
|
|
211
|
+
sessionPrefix: (agent: string): string =>
|
|
212
|
+
`${STORAGE_KEY_ROOT}/${agent}/session/`,
|
|
213
|
+
sessionEvents: (
|
|
214
|
+
agent: string,
|
|
215
|
+
sessionId: string,
|
|
216
|
+
firstIndex: number
|
|
217
|
+
): string =>
|
|
218
|
+
`${STORAGE_KEY_ROOT}/${agent}/session-events/${sessionId}/${String(firstIndex).padStart(8, "0")}`,
|
|
219
|
+
sessionEventsPrefix: (agent: string, sessionId: string): string =>
|
|
220
|
+
`${STORAGE_KEY_ROOT}/${agent}/session-events/${sessionId}/`,
|
|
221
|
+
continuation: (
|
|
222
|
+
agent: string,
|
|
223
|
+
channelId: string,
|
|
224
|
+
continuationKey: string
|
|
225
|
+
): string =>
|
|
226
|
+
`${STORAGE_KEY_ROOT}/${agent}/continuation/${keySegment(channelId)}/${keySegment(continuationKey)}`,
|
|
227
|
+
reminder: (agent: string, reminderId: string): string =>
|
|
228
|
+
`${STORAGE_KEY_ROOT}/${agent}/reminder/${reminderId}`,
|
|
229
|
+
reminderPrefix: (agent: string): string =>
|
|
230
|
+
`${STORAGE_KEY_ROOT}/${agent}/reminder/`,
|
|
231
|
+
evalRun: (agent: string, runId: string): string =>
|
|
232
|
+
`${STORAGE_KEY_ROOT}/${agent}/eval-run/${runId}`,
|
|
233
|
+
evalRunPrefix: (agent: string): string =>
|
|
234
|
+
`${STORAGE_KEY_ROOT}/${agent}/eval-run/`,
|
|
235
|
+
abSample: (agent: string, sessionId: string, at: string): string =>
|
|
236
|
+
`${STORAGE_KEY_ROOT}/${agent}/ab-sample/${sessionId}/${at}`,
|
|
237
|
+
abSnapshot: (agent: string): string =>
|
|
238
|
+
`${STORAGE_KEY_ROOT}/${agent}/ab-snapshot`,
|
|
239
|
+
} as const;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Max bytes a caller-controlled key segment may occupy after URI-encoding.
|
|
243
|
+
* Chosen so full keys stay well under common backend limits (Postgres btree
|
|
244
|
+
* index tuples cap at ~2704 bytes; S3 keys at 1024). Longer segments are
|
|
245
|
+
* replaced by their SHA-256 digest, keeping every minted key bounded.
|
|
246
|
+
*/
|
|
247
|
+
export const MAX_KEY_SEGMENT_BYTES = 256;
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* URI-encode one caller-controlled key segment, substituting a `sha256:…`
|
|
251
|
+
* digest of the raw value when the encoding exceeds
|
|
252
|
+
* {@link MAX_KEY_SEGMENT_BYTES}. Deterministic, so key construction on the
|
|
253
|
+
* write path and the continuation-lookup path always agree.
|
|
254
|
+
*/
|
|
255
|
+
function keySegment(raw: string): string {
|
|
256
|
+
const encoded = encodeURIComponent(raw);
|
|
257
|
+
if (Buffer.byteLength(encoded, "utf8") <= MAX_KEY_SEGMENT_BYTES) {
|
|
258
|
+
return encoded;
|
|
259
|
+
}
|
|
260
|
+
return `sha256:${createHash("sha256").update(raw, "utf8").digest("hex")}`;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ============================================================================
|
|
264
|
+
// Policy resolution
|
|
265
|
+
// ============================================================================
|
|
266
|
+
|
|
267
|
+
export const STORAGE_DEFAULT_RESTORE_MAX_SESSIONS = 1_000;
|
|
268
|
+
export const STORAGE_DEFAULT_RESTORE_MAX_AGE_MS: number = 30 * 24 * 60 * 60_000;
|
|
269
|
+
export const STORAGE_DEFAULT_RESTORE_MAX_TOTAL_BYTES = 1_073_741_824; // 1 GiB
|
|
270
|
+
|
|
271
|
+
/** {@link StoragePolicy} with defaults applied. */
|
|
272
|
+
export interface ResolvedStoragePolicy {
|
|
273
|
+
/** Event-chunk flush trigger: per turn, or debounced across turns. */
|
|
274
|
+
events: "turnEnd" | { debounceMs: number };
|
|
275
|
+
restore:
|
|
276
|
+
| "off"
|
|
277
|
+
| { maxSessions: number; maxAgeMs: number; maxTotalBytes: number };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** Apply {@link StoragePolicy} defaults (exposed for tooling/tests). */
|
|
281
|
+
export function resolveStoragePolicy(
|
|
282
|
+
policy: StoragePolicy | undefined
|
|
283
|
+
): ResolvedStoragePolicy {
|
|
284
|
+
return {
|
|
285
|
+
events:
|
|
286
|
+
policy?.debounceMs === undefined
|
|
287
|
+
? "turnEnd"
|
|
288
|
+
: { debounceMs: clampInt(policy.debounceMs, 2_000, 0, 60_000) },
|
|
289
|
+
restore:
|
|
290
|
+
policy?.restore === "off"
|
|
291
|
+
? "off"
|
|
292
|
+
: {
|
|
293
|
+
maxSessions: clampInt(
|
|
294
|
+
policy?.restore?.maxSessions,
|
|
295
|
+
STORAGE_DEFAULT_RESTORE_MAX_SESSIONS,
|
|
296
|
+
0,
|
|
297
|
+
1_000_000
|
|
298
|
+
),
|
|
299
|
+
maxAgeMs: clampInt(
|
|
300
|
+
policy?.restore?.maxAgeMs,
|
|
301
|
+
STORAGE_DEFAULT_RESTORE_MAX_AGE_MS,
|
|
302
|
+
0,
|
|
303
|
+
10 * 365 * 24 * 60 * 60_000
|
|
304
|
+
),
|
|
305
|
+
maxTotalBytes: clampInt(
|
|
306
|
+
policy?.restore?.maxTotalBytes,
|
|
307
|
+
STORAGE_DEFAULT_RESTORE_MAX_TOTAL_BYTES,
|
|
308
|
+
0,
|
|
309
|
+
1_099_511_627_776 // 1 TiB
|
|
310
|
+
),
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function clampInt(
|
|
316
|
+
value: number | undefined,
|
|
317
|
+
fallback: number,
|
|
318
|
+
min: number,
|
|
319
|
+
max: number
|
|
320
|
+
): number {
|
|
321
|
+
if (value === undefined || !Number.isFinite(value)) {
|
|
322
|
+
return fallback;
|
|
323
|
+
}
|
|
324
|
+
return Math.min(max, Math.max(min, Math.floor(value)));
|
|
325
|
+
}
|