@cursor/july 0.1.97 → 0.1.100
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 +5 -3
- package/dist/bin/agent-serve.js +6 -7
- package/dist/channels/github/defaults.d.ts.map +1 -1
- package/dist/channels/github/defaults.js +28 -3
- package/dist/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +16 -0
- package/dist/channels/github/progress.d.ts +7 -0
- package/dist/channels/github/progress.d.ts.map +1 -1
- package/dist/channels/origin/origin-channel.d.ts +2 -0
- package/dist/channels/origin/origin-channel.d.ts.map +1 -1
- package/dist/channels/origin/origin-channel.js +37 -4
- package/dist/channels/slack/api.d.ts +16 -7
- package/dist/channels/slack/api.d.ts.map +1 -1
- package/dist/channels/slack/api.js +29 -13
- package/dist/channels/slack/constants.d.ts +6 -0
- package/dist/channels/slack/constants.d.ts.map +1 -1
- package/dist/channels/slack/constants.js +6 -0
- package/dist/channels/slack/defaults.d.ts +9 -4
- package/dist/channels/slack/defaults.d.ts.map +1 -1
- package/dist/channels/slack/defaults.js +221 -122
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +3 -3
- package/dist/channels/slack/inbound.d.ts +9 -0
- package/dist/channels/slack/inbound.d.ts.map +1 -1
- package/dist/channels/slack/inbound.js +14 -0
- package/dist/channels/slack/index.d.ts +1 -0
- package/dist/channels/slack/index.d.ts.map +1 -1
- package/dist/channels/slack/index.js +1 -0
- package/dist/channels/slack/live-delivery.d.ts +6 -1
- package/dist/channels/slack/live-delivery.d.ts.map +1 -1
- package/dist/channels/slack/live-delivery.js +110 -20
- package/dist/channels/slack/progress-delivery.d.ts +28 -0
- package/dist/channels/slack/progress-delivery.d.ts.map +1 -0
- package/dist/channels/slack/progress-delivery.js +115 -0
- package/dist/channels/slack/reasoning-card.d.ts +38 -0
- package/dist/channels/slack/reasoning-card.d.ts.map +1 -0
- package/dist/channels/slack/reasoning-card.js +104 -0
- package/dist/channels/slack/redact.d.ts +2 -0
- package/dist/channels/slack/redact.d.ts.map +1 -0
- package/dist/channels/slack/redact.js +5 -0
- package/dist/channels/slack/reply-options.d.ts +40 -0
- package/dist/channels/slack/reply-options.d.ts.map +1 -0
- package/dist/channels/slack/reply-options.js +150 -0
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +8 -4
- package/dist/channels/slack/stream.d.ts +22 -10
- package/dist/channels/slack/stream.d.ts.map +1 -1
- package/dist/channels/slack/stream.js +9 -16
- package/dist/channels/slack/tool-cards.d.ts +22 -0
- package/dist/channels/slack/tool-cards.d.ts.map +1 -0
- package/dist/channels/slack/tool-cards.js +178 -0
- package/dist/channels/slack/types.d.ts +154 -7
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +6 -6
- package/dist/docs/ab.md +1 -1
- package/dist/docs/assets/{ab.md.DJo5r4R-.js → ab.md.mlVgqvSk.js} +1 -1
- package/dist/docs/assets/{app.B3rWNYE1.js → app.DZ1e0Ycq.js} +1 -1
- package/dist/docs/assets/{building-with-agents.md.DI4mEzlt.js → building-with-agents.md.CUSWxlP_.js} +2 -2
- package/dist/docs/assets/chunks/@localSearchIndexroot.j70vvPL4.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.C1lhqJJR.js → VPLocalSearchBox.l8omwc6D.js} +1 -1
- package/dist/docs/assets/chunks/{theme.B40_SXuv.js → theme.JVcD6gel.js} +2 -2
- package/dist/docs/assets/{evals.md.lfJoEVc8.js → evals.md.CPzDAwoH.js} +1 -1
- package/dist/docs/assets/{guides_github.md.Cnh2mL4a.js → guides_github.md.BtPr9GaP.js} +1 -1
- package/dist/docs/assets/{guides_mcp-oauth.md.CN-6YmTJ.js → guides_mcp-oauth.md.Dp6cDP7f.js} +1 -1
- package/dist/docs/assets/{guides_opentelemetry.md.bmPmkvJu.js → guides_opentelemetry.md.BVTXDCRg.js} +2 -2
- package/dist/docs/assets/{guides_slack.md.VDXQV3ja.js → guides_slack.md.9oHPye9o.js} +19 -3
- package/dist/docs/assets/{guides_slack.md.VDXQV3ja.lean.js → guides_slack.md.9oHPye9o.lean.js} +1 -1
- package/dist/docs/assets/hillclimbing.md.CpTGTCle.js +4 -0
- package/dist/docs/assets/{index.md.CVeRUOeZ.js → index.md.Bb4k8kUm.js} +1 -1
- package/dist/docs/assets/{quickstart.md.Nj_LjW_a.js → quickstart.md.DdQOF7Y8.js} +1 -1
- package/dist/docs/assets/{reference_cli.md.RyZf5OTE.js → reference_cli.md.DfoeyvL0.js} +4 -4
- package/dist/docs/assets/{reference_cli.md.RyZf5OTE.lean.js → reference_cli.md.DfoeyvL0.lean.js} +1 -1
- package/dist/docs/assets/{reference_connections.md.DxldvyIB.js → reference_connections.md.BiGoBAk2.js} +1 -1
- package/dist/docs/assets/{reference_http-api.md.5zOAbV86.js → reference_http-api.md.BEJx9XVj.js} +1 -1
- package/dist/docs/assets/{reference_tools.md.B84gw9Ii.js → reference_tools.md.CvAHsdSp.js} +10 -2
- package/dist/docs/assets/{reference_tools.md.B84gw9Ii.lean.js → reference_tools.md.CvAHsdSp.lean.js} +1 -1
- package/dist/docs/assets/scaffolding-agents.md.em43xlY1.js +1 -0
- package/dist/docs/assets/skills_ab.md.CsFNatVx.js +26 -0
- package/dist/docs/assets/skills_ab.md.CsFNatVx.lean.js +1 -0
- package/dist/docs/assets/skills_create-agent.md.BVoWPcan.js +8 -0
- package/dist/docs/assets/skills_create-agent.md.BVoWPcan.lean.js +1 -0
- package/dist/docs/assets/skills_debug.md.CDbPhHfg.js +1 -0
- package/dist/docs/assets/skills_debug.md.CDbPhHfg.lean.js +1 -0
- package/dist/docs/assets/skills_evals.md.723kpUmA.js +25 -0
- package/dist/docs/assets/skills_evals.md.723kpUmA.lean.js +1 -0
- package/dist/docs/assets/skills_framework-map.md.BTi817yv.js +1 -0
- package/dist/docs/assets/skills_framework-map.md.BTi817yv.lean.js +1 -0
- package/dist/docs/assets/skills_github.md.D0JahM8c.js +16 -0
- package/dist/docs/assets/skills_github.md.D0JahM8c.lean.js +1 -0
- package/dist/docs/assets/skills_hillclimb.md.B_zJerxA.js +7 -0
- package/dist/docs/assets/skills_hillclimb.md.B_zJerxA.lean.js +1 -0
- package/dist/docs/assets/skills_index.md.DKwIxzGg.js +1 -0
- package/dist/docs/assets/skills_index.md.DKwIxzGg.lean.js +1 -0
- package/dist/docs/assets/skills_mcp-auth.md.DGvFP3HE.js +18 -0
- package/dist/docs/assets/skills_mcp-auth.md.DGvFP3HE.lean.js +1 -0
- package/dist/docs/assets/skills_otel.md.CgiZryR3.js +8 -0
- package/dist/docs/assets/skills_otel.md.CgiZryR3.lean.js +1 -0
- package/dist/docs/assets/skills_setup-slack.md.BBgx8lUz.js +20 -0
- package/dist/docs/assets/skills_setup-slack.md.BBgx8lUz.lean.js +1 -0
- package/dist/docs/assets/{troubleshooting.md.DCiPBhYs.js → troubleshooting.md.Cus_YZga.js} +1 -1
- package/dist/docs/building-with-agents.html +6 -6
- package/dist/docs/building-with-agents.md +14 -11
- package/dist/docs/concepts.html +4 -4
- package/dist/docs/deployment.html +4 -4
- package/dist/docs/design/runtime-abstraction.md +1757 -0
- package/dist/docs/evals.html +6 -6
- package/dist/docs/evals.md +1 -1
- 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 +5 -5
- package/dist/docs/guides/github.md +1 -1
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/mcp-oauth.html +5 -5
- package/dist/docs/guides/mcp-oauth.md +1 -1
- package/dist/docs/guides/opentelemetry.html +7 -7
- package/dist/docs/guides/opentelemetry.md +2 -2
- package/dist/docs/guides/slack.html +22 -6
- package/dist/docs/guides/slack.md +80 -1
- package/dist/docs/guides/webhooks.html +5 -5
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +6 -6
- package/dist/docs/hillclimbing.md +5 -5
- package/dist/docs/index.html +6 -6
- package/dist/docs/index.md +1 -1
- package/dist/docs/llms-full.txt +2830 -37
- package/dist/docs/llms.txt +15 -0
- package/dist/docs/quickstart.html +5 -5
- package/dist/docs/quickstart.md +1 -1
- 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 -7
- package/dist/docs/reference/cli.md +1 -7
- package/dist/docs/reference/connections.html +6 -6
- package/dist/docs/reference/connections.md +1 -1
- package/dist/docs/reference/hooks.html +4 -4
- package/dist/docs/reference/http-api.html +6 -6
- package/dist/docs/reference/http-api.md +1 -0
- 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 +4 -4
- package/dist/docs/reference/subagents.html +4 -4
- package/dist/docs/reference/tools.html +14 -6
- package/dist/docs/reference/tools.md +25 -0
- package/dist/docs/scaffolding-agents.html +5 -5
- package/dist/docs/scaffolding-agents.md +3 -3
- package/dist/docs/skills/ab.html +52 -0
- package/dist/docs/skills/ab.md +50 -0
- package/dist/docs/skills/create-agent.html +34 -0
- package/dist/docs/skills/create-agent.md +160 -0
- package/dist/docs/skills/debug.html +27 -0
- package/dist/docs/skills/debug.md +36 -0
- package/dist/docs/skills/evals.html +51 -0
- package/dist/docs/skills/evals.md +99 -0
- package/dist/docs/skills/framework-map.html +27 -0
- package/dist/docs/skills/framework-map.md +95 -0
- package/dist/docs/skills/github.html +42 -0
- package/dist/docs/skills/github.md +93 -0
- package/dist/docs/skills/hillclimb.html +33 -0
- package/dist/docs/skills/hillclimb.md +55 -0
- package/dist/docs/skills/index.html +27 -0
- package/dist/docs/skills/index.md +21 -0
- package/dist/docs/skills/mcp-auth.html +44 -0
- package/dist/docs/skills/mcp-auth.md +76 -0
- package/dist/docs/skills/otel.html +34 -0
- package/dist/docs/skills/otel.md +48 -0
- package/dist/docs/skills/setup-slack.html +46 -0
- package/dist/docs/skills/setup-slack.md +141 -0
- package/dist/docs/storage.html +4 -4
- package/dist/docs/templates/agentic-owners.html +4 -4
- package/dist/docs/templates/agents-md.html +4 -4
- package/dist/docs/templates/code-wiki.html +4 -4
- package/dist/docs/templates/demo.html +4 -4
- package/dist/docs/templates/pr-autofixer.html +4 -4
- package/dist/docs/templates/security-help.html +4 -4
- package/dist/docs/templates/security-reviewer.html +4 -4
- package/dist/docs/templates/triage.html +4 -4
- package/dist/docs/troubleshooting.html +5 -5
- package/dist/docs/troubleshooting.md +1 -1
- package/dist/files-backends/cursor-hosted.d.ts +9 -2
- package/dist/files-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/files-backends/cursor-hosted.js +9 -11
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/internal/as-of.d.ts +19 -0
- package/dist/internal/as-of.d.ts.map +1 -0
- package/dist/internal/as-of.js +82 -0
- package/dist/internal/cli-deploy.d.ts +2 -1
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +102 -7
- package/dist/internal/conversation-mirror.d.ts.map +1 -1
- package/dist/internal/conversation-mirror.js +3 -4
- package/dist/internal/cursor/hosted-store-secrets.d.ts +15 -0
- package/dist/internal/cursor/hosted-store-secrets.d.ts.map +1 -0
- package/dist/internal/cursor/hosted-store-secrets.js +48 -0
- package/dist/internal/cursor-event-relay.d.ts.map +1 -1
- package/dist/internal/cursor-event-relay.js +5 -6
- package/dist/internal/deploy-client.d.ts +6 -6
- package/dist/internal/deploy-client.js +7 -7
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +11 -1
- package/dist/internal/framework-file-storage.d.ts +5 -4
- package/dist/internal/framework-file-storage.d.ts.map +1 -1
- package/dist/internal/framework-file-storage.js +6 -4
- package/dist/internal/framework-storage-selection.d.ts +13 -11
- package/dist/internal/framework-storage-selection.d.ts.map +1 -1
- package/dist/internal/framework-storage-selection.js +28 -23
- package/dist/internal/hosted-admission-adapter.d.ts +28 -0
- package/dist/internal/hosted-admission-adapter.d.ts.map +1 -0
- package/dist/internal/hosted-admission-adapter.js +7 -0
- package/dist/internal/hosted-delivery-protocol.d.ts +6 -1
- package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
- package/dist/internal/hosted-delivery-protocol.js +1 -1
- package/dist/internal/hosted-delivery.d.ts +15 -1
- package/dist/internal/hosted-delivery.d.ts.map +1 -1
- package/dist/internal/hosted-delivery.js +47 -21
- package/dist/internal/hosted-execution-diag.d.ts +11 -0
- package/dist/internal/hosted-execution-diag.d.ts.map +1 -1
- package/dist/internal/hosted-execution-diag.js +39 -2
- package/dist/internal/http-channel.d.ts.map +1 -1
- package/dist/internal/http-channel.js +4 -1
- package/dist/internal/server.d.ts +3 -0
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +82 -19
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +24 -4
- package/dist/internal/session-run-log.d.ts +120 -0
- package/dist/internal/session-run-log.d.ts.map +1 -0
- package/dist/internal/session-run-log.js +358 -0
- package/dist/internal/tool-policy.d.ts +20 -8
- package/dist/internal/tool-policy.d.ts.map +1 -1
- package/dist/internal/tool-policy.js +11 -0
- package/dist/playground/assets/index-BszoQDc6.css +1 -0
- package/dist/playground/assets/index-DVs98vPL.js +69 -0
- package/dist/playground/index.html +2 -2
- package/dist/storage-backends/cursor-hosted-v2.d.ts +83 -0
- package/dist/storage-backends/cursor-hosted-v2.d.ts.map +1 -0
- package/dist/storage-backends/cursor-hosted-v2.js +164 -0
- package/dist/storage-backends/cursor-hosted.d.ts +6 -0
- package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/storage-backends/cursor-hosted.js +6 -1
- package/dist/storage-protocol.d.ts +8 -0
- package/dist/storage-protocol.d.ts.map +1 -1
- package/dist/storage-protocol.js +8 -0
- package/dist/tools.d.ts +12 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/types.d.ts +30 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +1 -1
- package/docs/ab.md +1 -1
- package/docs/building-with-agents.md +14 -11
- package/docs/design/runtime-abstraction.md +1757 -0
- package/docs/evals.md +1 -1
- package/docs/guides/github.md +1 -1
- package/docs/guides/mcp-oauth.md +1 -1
- package/docs/guides/opentelemetry.md +2 -2
- package/docs/guides/slack.md +80 -1
- package/docs/hillclimbing.md +5 -5
- package/docs/quickstart.md +1 -1
- package/docs/reference/cli.md +1 -7
- package/docs/reference/connections.md +1 -1
- package/docs/reference/http-api.md +1 -0
- package/docs/reference/tools.md +25 -0
- package/docs/scaffolding-agents.md +3 -3
- package/docs/skills/index.md +26 -0
- package/docs/troubleshooting.md +1 -1
- package/package.json +1 -1
- package/src/bin/agent-serve.ts +6 -7
- package/src/channels/github/defaults.ts +36 -3
- package/src/channels/github/github-channel.ts +17 -0
- package/src/channels/github/progress.ts +7 -0
- package/src/channels/origin/origin-channel.ts +74 -10
- package/src/channels/slack/api.ts +35 -17
- package/src/channels/slack/constants.ts +6 -0
- package/src/channels/slack/defaults.ts +253 -144
- package/src/channels/slack/dispatch.ts +10 -1
- package/src/channels/slack/inbound.ts +17 -0
- package/src/channels/slack/index.ts +1 -0
- package/src/channels/slack/live-delivery.ts +135 -22
- package/src/channels/slack/progress-delivery.ts +133 -0
- package/src/channels/slack/reasoning-card.ts +140 -0
- package/src/channels/slack/redact.ts +6 -0
- package/src/channels/slack/reply-options.ts +229 -0
- package/src/channels/slack/slack-channel.ts +7 -2
- package/src/channels/slack/stream.ts +31 -24
- package/src/channels/slack/tool-cards.ts +221 -0
- package/src/channels/slack/types.ts +168 -7
- package/src/files-backends/cursor-hosted.ts +18 -7
- package/src/index.ts +4 -0
- package/src/internal/as-of.ts +107 -0
- package/src/internal/cli-deploy.ts +130 -10
- package/src/internal/conversation-mirror.ts +3 -8
- package/src/internal/cursor/hosted-store-secrets.ts +72 -0
- package/src/internal/cursor-event-relay.ts +5 -6
- package/src/internal/deploy-client.ts +10 -10
- package/src/internal/discovery.ts +9 -1
- package/src/internal/framework-file-storage.ts +6 -4
- package/src/internal/framework-storage-selection.ts +28 -25
- package/src/internal/hosted-admission-adapter.ts +42 -0
- package/src/internal/hosted-delivery-protocol.ts +7 -2
- package/src/internal/hosted-delivery.ts +88 -36
- package/src/internal/hosted-execution-diag.ts +45 -0
- package/src/internal/http-channel.ts +8 -0
- package/src/internal/server.ts +100 -2
- package/src/internal/session-engine.ts +32 -1
- package/src/internal/session-run-log.ts +579 -0
- package/src/internal/tool-policy.ts +31 -9
- package/src/storage-backends/cursor-hosted-v2.ts +219 -0
- package/src/storage-backends/cursor-hosted.ts +6 -1
- package/src/storage-protocol.ts +10 -0
- package/src/tools.ts +12 -2
- package/src/types.ts +30 -0
- package/dist/docs/assets/chunks/@localSearchIndexroot.BU9nHdnQ.js +0 -1
- package/dist/docs/assets/hillclimbing.md.DhESf3OO.js +0 -4
- package/dist/docs/assets/scaffolding-agents.md.D7UUkWw0.js +0 -1
- package/dist/playground/assets/index-DrkI6y5O.js +0 -88
- package/dist/playground/assets/index-DzNGwm7q.css +0 -1
- /package/dist/docs/assets/{ab.md.DJo5r4R-.lean.js → ab.md.mlVgqvSk.lean.js} +0 -0
- /package/dist/docs/assets/{building-with-agents.md.DI4mEzlt.lean.js → building-with-agents.md.CUSWxlP_.lean.js} +0 -0
- /package/dist/docs/assets/{evals.md.lfJoEVc8.lean.js → evals.md.CPzDAwoH.lean.js} +0 -0
- /package/dist/docs/assets/{guides_github.md.Cnh2mL4a.lean.js → guides_github.md.BtPr9GaP.lean.js} +0 -0
- /package/dist/docs/assets/{guides_mcp-oauth.md.CN-6YmTJ.lean.js → guides_mcp-oauth.md.Dp6cDP7f.lean.js} +0 -0
- /package/dist/docs/assets/{guides_opentelemetry.md.bmPmkvJu.lean.js → guides_opentelemetry.md.BVTXDCRg.lean.js} +0 -0
- /package/dist/docs/assets/{hillclimbing.md.DhESf3OO.lean.js → hillclimbing.md.CpTGTCle.lean.js} +0 -0
- /package/dist/docs/assets/{index.md.CVeRUOeZ.lean.js → index.md.Bb4k8kUm.lean.js} +0 -0
- /package/dist/docs/assets/{quickstart.md.Nj_LjW_a.lean.js → quickstart.md.DdQOF7Y8.lean.js} +0 -0
- /package/dist/docs/assets/{reference_connections.md.DxldvyIB.lean.js → reference_connections.md.BiGoBAk2.lean.js} +0 -0
- /package/dist/docs/assets/{reference_http-api.md.5zOAbV86.lean.js → reference_http-api.md.BEJx9XVj.lean.js} +0 -0
- /package/dist/docs/assets/{scaffolding-agents.md.D7UUkWw0.lean.js → scaffolding-agents.md.em43xlY1.lean.js} +0 -0
- /package/dist/docs/assets/{troubleshooting.md.DCiPBhYs.lean.js → troubleshooting.md.Cus_YZga.lean.js} +0 -0
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable hosted-execution run log on the framework file store. **v2 only.**
|
|
3
|
+
*
|
|
4
|
+
* One JSON document per factory session (`ses_…`), with per-execution
|
|
5
|
+
* entries inside. The later customer UI is session-scoped ("why did this
|
|
6
|
+
* session fail?") so a single GET is the read path; continuations are FIFO
|
|
7
|
+
* so last-write-wins merge is safe. Guest-local `hosted-execution.json` and
|
|
8
|
+
* factorydb `last_error` are lossy: the on-disk diag dies with the pod, and
|
|
9
|
+
* the session row only keeps the first 1024 characters of a generic HTTP
|
|
10
|
+
* 500.
|
|
11
|
+
*
|
|
12
|
+
* Process death: persist `running` at turn start so SIGKILL / OOM still
|
|
13
|
+
* leaves a durable leftover. `uncaughtException` / SIGTERM flush the same
|
|
14
|
+
* session key with `processDeath: true` and the real throw. Soft-fail on
|
|
15
|
+
* persist so a store outage never changes the hosted-execution HTTP reply.
|
|
16
|
+
*
|
|
17
|
+
* Keys: {@link storageKeys.sessionRun}.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type { FileSink } from "../files.js";
|
|
21
|
+
import {
|
|
22
|
+
cursorFrameworkStoreSourceId,
|
|
23
|
+
cursorHostedFiles,
|
|
24
|
+
} from "../files-backends/cursor-hosted.js";
|
|
25
|
+
import { storageKeys } from "../storage-protocol.js";
|
|
26
|
+
import type { AgentProject } from "../types.js";
|
|
27
|
+
import { resolveApiKeySync } from "./cursor/credentials.js";
|
|
28
|
+
import { AGENT_SERVE_GENERATION_ENV_VAR } from "./cursor-relay-core.js";
|
|
29
|
+
import { describeError } from "./describe-error.js";
|
|
30
|
+
import type { HostedExecutionDiagAbortReason } from "./hosted-execution-diag.js";
|
|
31
|
+
import type { LogRingEntry } from "./log-ring.js";
|
|
32
|
+
|
|
33
|
+
export const SESSION_RUN_LOG_VERSION = 1;
|
|
34
|
+
/** Last N log-ring lines kept on each execution. */
|
|
35
|
+
export const SESSION_RUN_LOG_MAX_ENTRIES = 200;
|
|
36
|
+
/** Per-line cap so a runaway logger cannot blow the document. */
|
|
37
|
+
export const SESSION_RUN_LOG_MAX_LINE_CHARS = 4096;
|
|
38
|
+
/** Continuations are FIFO; keep a bounded history for the session UI. */
|
|
39
|
+
export const SESSION_RUN_LOG_MAX_EXECUTIONS = 20;
|
|
40
|
+
const MAX_ERROR_MESSAGE_CHARS = 4096;
|
|
41
|
+
const MAX_ERROR_STACK_CHARS = 16_384;
|
|
42
|
+
const MAX_DOCUMENT_CHARS = 512 * 1024;
|
|
43
|
+
/** Crash-handler flush must not block `process.exit`. */
|
|
44
|
+
export const SESSION_RUN_FATAL_FLUSH_TIMEOUT_MS = 2_000;
|
|
45
|
+
|
|
46
|
+
export type SessionRunAbortReason =
|
|
47
|
+
| HostedExecutionDiagAbortReason
|
|
48
|
+
| "process_exit"
|
|
49
|
+
| "uncaught_exception"
|
|
50
|
+
| "unhandled_rejection";
|
|
51
|
+
|
|
52
|
+
export type SessionRunLogError = {
|
|
53
|
+
name: string;
|
|
54
|
+
message: string;
|
|
55
|
+
stack?: string;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export type SessionRunExecution = {
|
|
59
|
+
executionId: string;
|
|
60
|
+
writtenAt: string;
|
|
61
|
+
sessionId: string;
|
|
62
|
+
turnStatus: string;
|
|
63
|
+
durationMs: number;
|
|
64
|
+
abortReason?: SessionRunAbortReason;
|
|
65
|
+
message?: string;
|
|
66
|
+
error?: SessionRunLogError;
|
|
67
|
+
/** True when this entry was flushed from a process-death handler. */
|
|
68
|
+
processDeath?: boolean;
|
|
69
|
+
logs: LogRingEntry[];
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Latest non-success execution, including leftover `running` after the
|
|
74
|
+
* process died without a terminal persist (SIGKILL / OOM). Undefined when
|
|
75
|
+
* the latest execution `turnStatus` is `finished`.
|
|
76
|
+
*/
|
|
77
|
+
export type SessionRunLastFailure = {
|
|
78
|
+
executionId: string;
|
|
79
|
+
writtenAt: string;
|
|
80
|
+
turnStatus: string;
|
|
81
|
+
abortReason?: SessionRunAbortReason;
|
|
82
|
+
message?: string;
|
|
83
|
+
error?: SessionRunLogError;
|
|
84
|
+
processDeath?: boolean;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/** One document per factory session. The customer UI reads this object. */
|
|
88
|
+
export type SessionRunLogDocument = {
|
|
89
|
+
v: typeof SESSION_RUN_LOG_VERSION;
|
|
90
|
+
sessionPublicId: string;
|
|
91
|
+
updatedAt: string;
|
|
92
|
+
lastFailure?: SessionRunLastFailure;
|
|
93
|
+
executions: SessionRunExecution[];
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/** Persist input: one execution upserted into the session document. */
|
|
97
|
+
export type SessionRunExecutionWrite = SessionRunExecution & {
|
|
98
|
+
sessionPublicId: string;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export interface SessionRunLogStore {
|
|
102
|
+
persist(write: SessionRunExecutionWrite): Promise<void>;
|
|
103
|
+
read(sessionPublicId: string): Promise<SessionRunLogDocument | undefined>;
|
|
104
|
+
/**
|
|
105
|
+
* Upgrade the in-flight `running` claim to a terminal process-death
|
|
106
|
+
* entry. No-op when no execution is active (already terminal, or never
|
|
107
|
+
* started). Soft-fail.
|
|
108
|
+
*/
|
|
109
|
+
flushProcessDeath(
|
|
110
|
+
reason: Extract<
|
|
111
|
+
SessionRunAbortReason,
|
|
112
|
+
"process_exit" | "uncaught_exception" | "unhandled_rejection"
|
|
113
|
+
>,
|
|
114
|
+
error?: unknown
|
|
115
|
+
): Promise<void>;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
type ActiveExecution = {
|
|
119
|
+
sessionPublicId: string;
|
|
120
|
+
executionId: string;
|
|
121
|
+
sessionId: string;
|
|
122
|
+
startedAt: number;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
let fatalFlush: ((error: unknown) => Promise<void>) | undefined;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Install the crash-handler flush used by `bin/agent-serve` on
|
|
129
|
+
* `uncaughtException`. One process, one active hosted serve.
|
|
130
|
+
*/
|
|
131
|
+
export function registerSessionRunFatalFlush(
|
|
132
|
+
flush: (error: unknown) => Promise<void>
|
|
133
|
+
): void {
|
|
134
|
+
fatalFlush = flush;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function unregisterSessionRunFatalFlush(): void {
|
|
138
|
+
fatalFlush = undefined;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Await the registered crash flush, bounded so a hung store cannot prevent
|
|
143
|
+
* `process.exit`. Soft-fail.
|
|
144
|
+
*/
|
|
145
|
+
export async function flushSessionRunFatal(error: unknown): Promise<void> {
|
|
146
|
+
if (fatalFlush === undefined) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
await Promise.race([
|
|
150
|
+
fatalFlush(error).catch(() => undefined),
|
|
151
|
+
new Promise<void>((resolve) => {
|
|
152
|
+
setTimeout(resolve, SESSION_RUN_FATAL_FLUSH_TIMEOUT_MS);
|
|
153
|
+
}),
|
|
154
|
+
]);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function boundedText(value: string, max: number): string {
|
|
158
|
+
return value.length > max ? `${value.slice(0, max)}…` : value;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function boundedOptionalText(
|
|
162
|
+
value: string | undefined,
|
|
163
|
+
max: number
|
|
164
|
+
): string | undefined {
|
|
165
|
+
if (value === undefined || value.length === 0) {
|
|
166
|
+
return undefined;
|
|
167
|
+
}
|
|
168
|
+
return boundedText(value, max);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function serializeSessionRunError(error: unknown): SessionRunLogError {
|
|
172
|
+
if (error instanceof Error) {
|
|
173
|
+
const stack = boundedOptionalText(error.stack, MAX_ERROR_STACK_CHARS);
|
|
174
|
+
return {
|
|
175
|
+
name: boundedText(error.name.length > 0 ? error.name : "Error", 128),
|
|
176
|
+
message: boundedText(error.message, MAX_ERROR_MESSAGE_CHARS),
|
|
177
|
+
...(stack === undefined ? {} : { stack }),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
name: "Error",
|
|
182
|
+
message: boundedText(String(error), MAX_ERROR_MESSAGE_CHARS),
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function snapshotSessionRunLogs(
|
|
187
|
+
entries: readonly LogRingEntry[]
|
|
188
|
+
): LogRingEntry[] {
|
|
189
|
+
const tail = entries.slice(-SESSION_RUN_LOG_MAX_ENTRIES);
|
|
190
|
+
return tail.map((entry) => ({
|
|
191
|
+
index: entry.index,
|
|
192
|
+
at: entry.at,
|
|
193
|
+
line: boundedText(entry.line, SESSION_RUN_LOG_MAX_LINE_CHARS),
|
|
194
|
+
}));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
198
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function decodeSinkBody(body: Uint8Array | { body: Uint8Array }): Uint8Array {
|
|
202
|
+
return body instanceof Uint8Array ? body : body.body;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function parseError(value: unknown): SessionRunLogError | undefined {
|
|
206
|
+
if (!isRecord(value)) {
|
|
207
|
+
return undefined;
|
|
208
|
+
}
|
|
209
|
+
const name = typeof value.name === "string" ? value.name : "";
|
|
210
|
+
const message = typeof value.message === "string" ? value.message : "";
|
|
211
|
+
const stack = typeof value.stack === "string" ? value.stack : undefined;
|
|
212
|
+
return {
|
|
213
|
+
name,
|
|
214
|
+
message,
|
|
215
|
+
...(stack === undefined ? {} : { stack }),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function parseLogs(value: unknown): LogRingEntry[] {
|
|
220
|
+
if (!Array.isArray(value)) {
|
|
221
|
+
return [];
|
|
222
|
+
}
|
|
223
|
+
const logs: LogRingEntry[] = [];
|
|
224
|
+
for (const entry of value) {
|
|
225
|
+
if (!isRecord(entry)) {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
if (
|
|
229
|
+
typeof entry.index !== "number" ||
|
|
230
|
+
typeof entry.at !== "string" ||
|
|
231
|
+
typeof entry.line !== "string"
|
|
232
|
+
) {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
logs.push({ index: entry.index, at: entry.at, line: entry.line });
|
|
236
|
+
}
|
|
237
|
+
return logs;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function parseExecution(value: unknown): SessionRunExecution | undefined {
|
|
241
|
+
if (!isRecord(value)) {
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
const executionId =
|
|
245
|
+
typeof value.executionId === "string" ? value.executionId : undefined;
|
|
246
|
+
const writtenAt =
|
|
247
|
+
typeof value.writtenAt === "string" ? value.writtenAt : undefined;
|
|
248
|
+
const sessionId =
|
|
249
|
+
typeof value.sessionId === "string" ? value.sessionId : undefined;
|
|
250
|
+
const turnStatus =
|
|
251
|
+
typeof value.turnStatus === "string" ? value.turnStatus : undefined;
|
|
252
|
+
const durationMs =
|
|
253
|
+
typeof value.durationMs === "number" && Number.isFinite(value.durationMs)
|
|
254
|
+
? value.durationMs
|
|
255
|
+
: undefined;
|
|
256
|
+
if (
|
|
257
|
+
executionId === undefined ||
|
|
258
|
+
executionId.length === 0 ||
|
|
259
|
+
writtenAt === undefined ||
|
|
260
|
+
sessionId === undefined ||
|
|
261
|
+
turnStatus === undefined ||
|
|
262
|
+
turnStatus.length === 0 ||
|
|
263
|
+
durationMs === undefined ||
|
|
264
|
+
durationMs < 0
|
|
265
|
+
) {
|
|
266
|
+
return undefined;
|
|
267
|
+
}
|
|
268
|
+
const abortReason =
|
|
269
|
+
typeof value.abortReason === "string" ? value.abortReason : undefined;
|
|
270
|
+
const message = typeof value.message === "string" ? value.message : undefined;
|
|
271
|
+
const error = parseError(value.error);
|
|
272
|
+
return {
|
|
273
|
+
executionId,
|
|
274
|
+
writtenAt,
|
|
275
|
+
sessionId,
|
|
276
|
+
turnStatus,
|
|
277
|
+
durationMs,
|
|
278
|
+
...(abortReason === undefined
|
|
279
|
+
? {}
|
|
280
|
+
: { abortReason: abortReason as SessionRunAbortReason }),
|
|
281
|
+
...(message === undefined ? {} : { message }),
|
|
282
|
+
...(error === undefined ? {} : { error }),
|
|
283
|
+
...(value.processDeath === true ? { processDeath: true } : {}),
|
|
284
|
+
logs: parseLogs(value.logs),
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export function lastFailureFromExecutions(
|
|
289
|
+
executions: readonly SessionRunExecution[]
|
|
290
|
+
): SessionRunLastFailure | undefined {
|
|
291
|
+
const latest = executions.at(-1);
|
|
292
|
+
if (latest === undefined || latest.turnStatus === "finished") {
|
|
293
|
+
return undefined;
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
executionId: latest.executionId,
|
|
297
|
+
writtenAt: latest.writtenAt,
|
|
298
|
+
turnStatus: latest.turnStatus,
|
|
299
|
+
...(latest.abortReason === undefined
|
|
300
|
+
? {}
|
|
301
|
+
: { abortReason: latest.abortReason }),
|
|
302
|
+
...(latest.message === undefined ? {} : { message: latest.message }),
|
|
303
|
+
...(latest.error === undefined ? {} : { error: latest.error }),
|
|
304
|
+
...(latest.processDeath === true ? { processDeath: true } : {}),
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function boundExecution(write: SessionRunExecution): SessionRunExecution {
|
|
309
|
+
const error =
|
|
310
|
+
write.error === undefined
|
|
311
|
+
? undefined
|
|
312
|
+
: {
|
|
313
|
+
name: boundedText(write.error.name, 128),
|
|
314
|
+
message: boundedText(write.error.message, MAX_ERROR_MESSAGE_CHARS),
|
|
315
|
+
...(write.error.stack === undefined
|
|
316
|
+
? {}
|
|
317
|
+
: {
|
|
318
|
+
stack: boundedText(write.error.stack, MAX_ERROR_STACK_CHARS),
|
|
319
|
+
}),
|
|
320
|
+
};
|
|
321
|
+
return {
|
|
322
|
+
executionId: write.executionId,
|
|
323
|
+
writtenAt: write.writtenAt,
|
|
324
|
+
sessionId: write.sessionId,
|
|
325
|
+
turnStatus: write.turnStatus,
|
|
326
|
+
durationMs: write.durationMs,
|
|
327
|
+
...(write.abortReason === undefined
|
|
328
|
+
? {}
|
|
329
|
+
: { abortReason: write.abortReason }),
|
|
330
|
+
...(boundedOptionalText(write.message, MAX_ERROR_MESSAGE_CHARS) ===
|
|
331
|
+
undefined
|
|
332
|
+
? {}
|
|
333
|
+
: {
|
|
334
|
+
message: boundedOptionalText(write.message, MAX_ERROR_MESSAGE_CHARS),
|
|
335
|
+
}),
|
|
336
|
+
...(error === undefined ? {} : { error }),
|
|
337
|
+
...(write.processDeath === true ? { processDeath: true } : {}),
|
|
338
|
+
logs: snapshotSessionRunLogs(write.logs),
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function upsertExecution(
|
|
343
|
+
existing: readonly SessionRunExecution[],
|
|
344
|
+
next: SessionRunExecution
|
|
345
|
+
): SessionRunExecution[] {
|
|
346
|
+
const executions = existing.filter(
|
|
347
|
+
(entry) => entry.executionId !== next.executionId
|
|
348
|
+
);
|
|
349
|
+
executions.push(next);
|
|
350
|
+
executions.sort((a, b) => a.writtenAt.localeCompare(b.writtenAt));
|
|
351
|
+
return executions.slice(-SESSION_RUN_LOG_MAX_EXECUTIONS);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function shrinkSessionDocument(
|
|
355
|
+
document: SessionRunLogDocument
|
|
356
|
+
): SessionRunLogDocument {
|
|
357
|
+
let executions = document.executions;
|
|
358
|
+
let next: SessionRunLogDocument = {
|
|
359
|
+
...document,
|
|
360
|
+
executions,
|
|
361
|
+
lastFailure: lastFailureFromExecutions(executions),
|
|
362
|
+
};
|
|
363
|
+
let serialized = JSON.stringify(next);
|
|
364
|
+
while (serialized.length > MAX_DOCUMENT_CHARS) {
|
|
365
|
+
const oldestWithLogs = executions.findIndex(
|
|
366
|
+
(entry) => entry.logs.length > 0
|
|
367
|
+
);
|
|
368
|
+
if (oldestWithLogs >= 0) {
|
|
369
|
+
const entry = executions[oldestWithLogs]!;
|
|
370
|
+
executions = executions.map((item, index) =>
|
|
371
|
+
index === oldestWithLogs
|
|
372
|
+
? {
|
|
373
|
+
...entry,
|
|
374
|
+
logs: entry.logs.slice(Math.ceil(entry.logs.length / 2)),
|
|
375
|
+
}
|
|
376
|
+
: item
|
|
377
|
+
);
|
|
378
|
+
} else if (executions.length > 1) {
|
|
379
|
+
executions = executions.slice(1);
|
|
380
|
+
} else {
|
|
381
|
+
break;
|
|
382
|
+
}
|
|
383
|
+
next = {
|
|
384
|
+
...document,
|
|
385
|
+
executions,
|
|
386
|
+
lastFailure: lastFailureFromExecutions(executions),
|
|
387
|
+
};
|
|
388
|
+
serialized = JSON.stringify(next);
|
|
389
|
+
}
|
|
390
|
+
return next;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
export function parseSessionRunLogDocument(
|
|
394
|
+
value: unknown
|
|
395
|
+
): SessionRunLogDocument | undefined {
|
|
396
|
+
if (!isRecord(value) || value.v !== SESSION_RUN_LOG_VERSION) {
|
|
397
|
+
return undefined;
|
|
398
|
+
}
|
|
399
|
+
const sessionPublicId =
|
|
400
|
+
typeof value.sessionPublicId === "string"
|
|
401
|
+
? value.sessionPublicId
|
|
402
|
+
: undefined;
|
|
403
|
+
const updatedAt =
|
|
404
|
+
typeof value.updatedAt === "string" ? value.updatedAt : undefined;
|
|
405
|
+
if (
|
|
406
|
+
sessionPublicId === undefined ||
|
|
407
|
+
sessionPublicId.length === 0 ||
|
|
408
|
+
updatedAt === undefined ||
|
|
409
|
+
!Array.isArray(value.executions)
|
|
410
|
+
) {
|
|
411
|
+
return undefined;
|
|
412
|
+
}
|
|
413
|
+
const executions: SessionRunExecution[] = [];
|
|
414
|
+
for (const entry of value.executions) {
|
|
415
|
+
const parsed = parseExecution(entry);
|
|
416
|
+
if (parsed !== undefined) {
|
|
417
|
+
executions.push(parsed);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
executions.sort((a, b) => a.writtenAt.localeCompare(b.writtenAt));
|
|
421
|
+
const lastFailure = lastFailureFromExecutions(executions);
|
|
422
|
+
return {
|
|
423
|
+
v: SESSION_RUN_LOG_VERSION,
|
|
424
|
+
sessionPublicId,
|
|
425
|
+
updatedAt,
|
|
426
|
+
...(lastFailure === undefined ? {} : { lastFailure }),
|
|
427
|
+
executions,
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export function createSessionRunLogStore(input: {
|
|
432
|
+
sink: FileSink;
|
|
433
|
+
agentName: string;
|
|
434
|
+
logger?: (line: string) => void;
|
|
435
|
+
snapshotLogs?: () => readonly LogRingEntry[];
|
|
436
|
+
}): SessionRunLogStore {
|
|
437
|
+
const { sink, agentName } = input;
|
|
438
|
+
const logger = input.logger ?? (() => {});
|
|
439
|
+
let active: ActiveExecution | undefined;
|
|
440
|
+
|
|
441
|
+
const readDocument = async (
|
|
442
|
+
sessionPublicId: string
|
|
443
|
+
): Promise<SessionRunLogDocument | undefined> => {
|
|
444
|
+
const key = storageKeys.sessionRun(agentName, sessionPublicId);
|
|
445
|
+
const body = await sink.get(key);
|
|
446
|
+
if (body === undefined) {
|
|
447
|
+
return undefined;
|
|
448
|
+
}
|
|
449
|
+
try {
|
|
450
|
+
return parseSessionRunLogDocument(
|
|
451
|
+
JSON.parse(new TextDecoder().decode(decodeSinkBody(body)))
|
|
452
|
+
);
|
|
453
|
+
} catch (error) {
|
|
454
|
+
logger(
|
|
455
|
+
`[agent-sdk] session-run log: skip unreadable ${key} (${describeError(error)})`
|
|
456
|
+
);
|
|
457
|
+
return undefined;
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
const persistWrite = async (
|
|
462
|
+
write: SessionRunExecutionWrite
|
|
463
|
+
): Promise<void> => {
|
|
464
|
+
const fitted = boundExecution(write);
|
|
465
|
+
let existing: SessionRunLogDocument | undefined;
|
|
466
|
+
try {
|
|
467
|
+
existing = await readDocument(write.sessionPublicId);
|
|
468
|
+
} catch (error) {
|
|
469
|
+
logger(
|
|
470
|
+
`[agent-sdk] session-run log: persist failed for ${fitted.executionId} (${describeError(error)})`
|
|
471
|
+
);
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
const executions = upsertExecution(existing?.executions ?? [], fitted);
|
|
475
|
+
const document = shrinkSessionDocument({
|
|
476
|
+
v: SESSION_RUN_LOG_VERSION,
|
|
477
|
+
sessionPublicId: write.sessionPublicId,
|
|
478
|
+
updatedAt: fitted.writtenAt,
|
|
479
|
+
executions,
|
|
480
|
+
});
|
|
481
|
+
const key = storageKeys.sessionRun(agentName, document.sessionPublicId);
|
|
482
|
+
try {
|
|
483
|
+
await sink.put(key, new TextEncoder().encode(JSON.stringify(document)));
|
|
484
|
+
} catch (error) {
|
|
485
|
+
logger(
|
|
486
|
+
`[agent-sdk] session-run log: persist failed for ${fitted.executionId} (${describeError(error)})`
|
|
487
|
+
);
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
if (fitted.turnStatus === "running") {
|
|
491
|
+
active = {
|
|
492
|
+
sessionPublicId: write.sessionPublicId,
|
|
493
|
+
executionId: fitted.executionId,
|
|
494
|
+
sessionId: fitted.sessionId,
|
|
495
|
+
startedAt: Date.now(),
|
|
496
|
+
};
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
if (active?.executionId === fitted.executionId) {
|
|
500
|
+
active = undefined;
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
return {
|
|
505
|
+
persist: persistWrite,
|
|
506
|
+
|
|
507
|
+
async read(sessionPublicId) {
|
|
508
|
+
try {
|
|
509
|
+
return await readDocument(sessionPublicId);
|
|
510
|
+
} catch (error) {
|
|
511
|
+
logger(
|
|
512
|
+
`[agent-sdk] session-run log: read failed for ${sessionPublicId} (${describeError(error)})`
|
|
513
|
+
);
|
|
514
|
+
return undefined;
|
|
515
|
+
}
|
|
516
|
+
},
|
|
517
|
+
|
|
518
|
+
async flushProcessDeath(reason, error) {
|
|
519
|
+
if (active === undefined) {
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
const current = active;
|
|
523
|
+
const serialized =
|
|
524
|
+
error === undefined ? undefined : serializeSessionRunError(error);
|
|
525
|
+
const message =
|
|
526
|
+
error instanceof Error
|
|
527
|
+
? error.message
|
|
528
|
+
: error !== undefined
|
|
529
|
+
? String(error)
|
|
530
|
+
: "process died during hosted execution";
|
|
531
|
+
await persistWrite({
|
|
532
|
+
sessionPublicId: current.sessionPublicId,
|
|
533
|
+
executionId: current.executionId,
|
|
534
|
+
writtenAt: new Date().toISOString(),
|
|
535
|
+
sessionId: current.sessionId,
|
|
536
|
+
turnStatus: "error",
|
|
537
|
+
durationMs: Math.max(0, Date.now() - current.startedAt),
|
|
538
|
+
abortReason: reason,
|
|
539
|
+
processDeath: true,
|
|
540
|
+
message,
|
|
541
|
+
...(serialized === undefined ? {} : { error: serialized }),
|
|
542
|
+
logs: snapshotSessionRunLogs(input.snapshotLogs?.() ?? []),
|
|
543
|
+
});
|
|
544
|
+
},
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Same selection as the conversation mirror: architecture-v2 hosted
|
|
550
|
+
* engines (generation env set). Store ids resolve lazily from env or the
|
|
551
|
+
* task-bind files under `/run/cursor/secrets`. Self-hosted and v1 stay on
|
|
552
|
+
* local disk / today's behavior.
|
|
553
|
+
*/
|
|
554
|
+
export function selectSessionRunLogStore(input: {
|
|
555
|
+
project: AgentProject;
|
|
556
|
+
logger?: (line: string) => void;
|
|
557
|
+
env?: Record<string, string | undefined>;
|
|
558
|
+
snapshotLogs?: () => readonly LogRingEntry[];
|
|
559
|
+
}): SessionRunLogStore | undefined {
|
|
560
|
+
if (input.project.agent.architecture !== "v2") {
|
|
561
|
+
return undefined;
|
|
562
|
+
}
|
|
563
|
+
const env = input.env ?? process.env;
|
|
564
|
+
if (env[AGENT_SERVE_GENERATION_ENV_VAR] === undefined) {
|
|
565
|
+
return undefined;
|
|
566
|
+
}
|
|
567
|
+
const envSnapshot = env as NodeJS.ProcessEnv;
|
|
568
|
+
return createSessionRunLogStore({
|
|
569
|
+
sink: cursorHostedFiles({
|
|
570
|
+
resolveStoreSourceId: () => cursorFrameworkStoreSourceId(envSnapshot),
|
|
571
|
+
resolveApiKey: () => resolveApiKeySync()?.apiKey,
|
|
572
|
+
}),
|
|
573
|
+
agentName: input.project.name,
|
|
574
|
+
logger: input.logger,
|
|
575
|
+
...(input.snapshotLogs === undefined
|
|
576
|
+
? {}
|
|
577
|
+
: { snapshotLogs: input.snapshotLogs }),
|
|
578
|
+
});
|
|
579
|
+
}
|
|
@@ -16,6 +16,7 @@ import type {
|
|
|
16
16
|
ToolEffectDeclaration,
|
|
17
17
|
ToolExecuteResult,
|
|
18
18
|
} from "../types.js";
|
|
19
|
+
import { timeArgViolation } from "./as-of.js";
|
|
19
20
|
|
|
20
21
|
/** What the host does with one tool call. */
|
|
21
22
|
export type ToolDecision =
|
|
@@ -24,12 +25,17 @@ export type ToolDecision =
|
|
|
24
25
|
/** Do not run it; return this to the model instead. */
|
|
25
26
|
| { kind: "answer"; result: ToolExecuteResult }
|
|
26
27
|
/**
|
|
27
|
-
* Do not run it; fail the call with this message. Only
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
28
|
+
* Do not run it; fail the call with this message. Only produced in a dry
|
|
29
|
+
* run, for an MCP tool nothing classifies: stubbing it would let the run
|
|
30
|
+
* reach a confident conclusion from no data, and running it could leak a
|
|
31
|
+
* real write — refusing is the one failure that is loud.
|
|
31
32
|
*/
|
|
32
|
-
| { kind: "refuse"; message: string }
|
|
33
|
+
| { kind: "refuse"; message: string }
|
|
34
|
+
/**
|
|
35
|
+
* Do not run it; the `asOf` session was asked to read without absolute
|
|
36
|
+
* bounds, which would read the present.
|
|
37
|
+
*/
|
|
38
|
+
| { kind: "unbounded"; message: string };
|
|
33
39
|
|
|
34
40
|
/** What a policy decision needs to know about the call. */
|
|
35
41
|
export interface ToolCallFacts {
|
|
@@ -48,12 +54,16 @@ export interface ToolCallFacts {
|
|
|
48
54
|
/** What a stubbed write should answer with, if the tool supplied one. */
|
|
49
55
|
dryRunResult?: ToolExecuteResult | ((input: never) => ToolExecuteResult);
|
|
50
56
|
/**
|
|
51
|
-
* Validated input, as the tool body would receive it.
|
|
52
|
-
*
|
|
53
|
-
* with
|
|
54
|
-
* can omit it.
|
|
57
|
+
* Validated input, as the tool body would receive it. Read by a per-call
|
|
58
|
+
* `effect` predicate, a `dryRunResult` function, and the {@link timeArgs}
|
|
59
|
+
* bound check, so a caller with none of those — a bridged MCP tool, which
|
|
60
|
+
* carries no declaration at all — can omit it.
|
|
55
61
|
*/
|
|
56
62
|
input?: unknown;
|
|
63
|
+
/** The session's frozen instant, when it has one. */
|
|
64
|
+
asOf?: string;
|
|
65
|
+
/** The tool's declared time-bound argument names, if it made them. */
|
|
66
|
+
timeArgs?: readonly string[];
|
|
57
67
|
}
|
|
58
68
|
|
|
59
69
|
/**
|
|
@@ -147,6 +157,18 @@ function unclassifiedRefusal(toolName: string): string {
|
|
|
147
157
|
|
|
148
158
|
/** Decide what to do with one tool call. Pure; no I/O, no session state. */
|
|
149
159
|
export function decideToolCall(facts: ToolCallFacts): ToolDecision {
|
|
160
|
+
// Before the dry-run split: a frozen session bounds its reads whether or
|
|
161
|
+
// not its writes are stubbed.
|
|
162
|
+
if (
|
|
163
|
+
facts.asOf !== undefined &&
|
|
164
|
+
facts.timeArgs !== undefined &&
|
|
165
|
+
facts.timeArgs.length > 0
|
|
166
|
+
) {
|
|
167
|
+
const violation = timeArgViolation(facts.input, facts.timeArgs, facts.asOf);
|
|
168
|
+
if (violation !== undefined) {
|
|
169
|
+
return { kind: "unbounded", message: violation };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
150
172
|
if (!facts.dryRun) {
|
|
151
173
|
return { kind: "run" };
|
|
152
174
|
}
|