@cursor/july 0.1.96 → 0.1.99
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 +1 -1
- package/README.md +3 -2
- package/dist/bin/agent-serve.js +13 -3
- package/dist/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +16 -0
- package/dist/channels/origin/api.d.ts +15 -4
- package/dist/channels/origin/api.d.ts.map +1 -1
- package/dist/channels/origin/api.js +19 -3
- package/dist/channels/origin/binding.d.ts +11 -0
- package/dist/channels/origin/binding.d.ts.map +1 -1
- package/dist/channels/origin/binding.js +41 -25
- package/dist/channels/origin/index.d.ts +1 -0
- package/dist/channels/origin/index.d.ts.map +1 -1
- package/dist/channels/origin/index.js +1 -0
- 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 +40 -4
- package/dist/channels/origin/reactions.d.ts +56 -0
- package/dist/channels/origin/reactions.d.ts.map +1 -0
- package/dist/channels/origin/reactions.js +67 -0
- package/dist/channels/origin/types.d.ts +11 -0
- package/dist/channels/origin/types.d.ts.map +1 -1
- package/dist/channels/slack/approvals.d.ts +16 -0
- package/dist/channels/slack/approvals.d.ts.map +1 -1
- package/dist/channels/slack/approvals.js +86 -0
- package/dist/channels/slack/cli-project.d.ts +19 -0
- package/dist/channels/slack/cli-project.d.ts.map +1 -0
- package/dist/channels/slack/cli-project.js +234 -0
- package/dist/channels/slack/defaults.d.ts.map +1 -1
- package/dist/channels/slack/defaults.js +142 -63
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +1 -1
- package/dist/channels/slack/init.d.ts +3 -2
- package/dist/channels/slack/init.d.ts.map +1 -1
- package/dist/channels/slack/init.js +5 -5
- package/dist/channels/slack/interactive.d.ts +2 -1
- package/dist/channels/slack/interactive.d.ts.map +1 -1
- package/dist/channels/slack/interactive.js +15 -4
- package/dist/channels/slack/live-delivery.d.ts +1 -1
- package/dist/channels/slack/live-delivery.d.ts.map +1 -1
- package/dist/channels/slack/live-delivery.js +4 -2
- package/dist/channels/slack/manifest.d.ts +1 -1
- package/dist/channels/slack/manifest.js +1 -1
- 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/setup.d.ts +1 -1
- package/dist/channels/slack/setup.d.ts.map +1 -1
- package/dist/channels/slack/setup.js +10 -6
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +1 -0
- package/dist/channels/slack/tool-approval-events.d.ts.map +1 -1
- package/dist/channels/slack/tool-approval-events.js +91 -1
- package/dist/channels/slack/types.d.ts +9 -1
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.jXleCmOc.js → app.BplEAZ3S.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DpuI4i7Z.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.8LhyglIT.js → VPLocalSearchBox.CHXyv4wP.js} +1 -1
- package/dist/docs/assets/chunks/{theme.9ged-pr2.js → theme.BTcsZmjQ.js} +2 -2
- package/dist/docs/assets/{deployment.md.D9msOFOW.js → deployment.md.MFPKqsqI.js} +2 -2
- package/dist/docs/assets/{guides_mcp-oauth.md.DPYmBCbV.js → guides_mcp-oauth.md.CN-6YmTJ.js} +4 -4
- package/dist/docs/assets/{guides_mcp-oauth.md.DPYmBCbV.lean.js → guides_mcp-oauth.md.CN-6YmTJ.lean.js} +1 -1
- package/dist/docs/assets/guides_slack.md.VDXQV3ja.js +22 -0
- package/dist/docs/assets/guides_slack.md.VDXQV3ja.lean.js +1 -0
- package/dist/docs/assets/index.md.CVeRUOeZ.js +5 -0
- package/dist/docs/assets/{reference_agent-config.md.Cp_x38Nl.js → reference_agent-config.md.BHeYMgme.js} +1 -1
- package/dist/docs/assets/{reference_cli.md.CCnG-nJx.js → reference_cli.md.DfoeyvL0.js} +9 -6
- package/dist/docs/assets/{reference_cli.md.CCnG-nJx.lean.js → reference_cli.md.DfoeyvL0.lean.js} +1 -1
- package/dist/docs/assets/{reference_connections.md.BH8Oc0D0.js → reference_connections.md.DxldvyIB.js} +4 -3
- package/dist/docs/assets/{reference_http-api.md.D89k1mdm.js → reference_http-api.md.BEJx9XVj.js} +2 -2
- package/dist/docs/assets/reference_sessions.md.NxFP_CAC.js +1 -0
- package/dist/docs/assets/{reference_tools.md.BHeXn2id.js → reference_tools.md.CvAHsdSp.js} +10 -2
- package/dist/docs/assets/{reference_tools.md.BHeXn2id.lean.js → reference_tools.md.CvAHsdSp.lean.js} +1 -1
- package/dist/docs/assets/templates_agents-md.md.CXSMZljE.js +2 -0
- package/dist/docs/assets/templates_agents-md.md.CXSMZljE.lean.js +1 -0
- package/dist/docs/assets/troubleshooting.md.DCiPBhYs.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/deployment.md +5 -4
- package/dist/docs/design/runtime-abstraction.md +1757 -0
- package/dist/docs/evals.html +4 -4
- package/dist/docs/guides/agent-to-agent.html +4 -4
- package/dist/docs/guides/cloud-runtime.html +4 -4
- package/dist/docs/guides/convert-automation.html +4 -4
- package/dist/docs/guides/github.html +4 -4
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/mcp-oauth.html +8 -8
- package/dist/docs/guides/mcp-oauth.md +26 -8
- package/dist/docs/guides/opentelemetry.html +4 -4
- package/dist/docs/guides/slack.html +17 -33
- package/dist/docs/guides/slack.md +92 -201
- package/dist/docs/guides/webhooks.html +4 -4
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +4 -4
- package/dist/docs/index.html +6 -6
- package/dist/docs/index.md +5 -3
- package/dist/docs/llms-full.txt +2065 -637
- package/dist/docs/llms.txt +3 -2
- package/dist/docs/quickstart.html +4 -4
- package/dist/docs/reference/agent-config.html +6 -6
- package/dist/docs/reference/agent-config.md +2 -2
- package/dist/docs/reference/artifacts.html +4 -4
- package/dist/docs/reference/channels.html +4 -4
- package/dist/docs/reference/cli.html +13 -10
- package/dist/docs/reference/cli.md +32 -16
- package/dist/docs/reference/connections.html +9 -8
- package/dist/docs/reference/connections.md +6 -3
- package/dist/docs/reference/hooks.html +4 -4
- package/dist/docs/reference/http-api.html +7 -7
- package/dist/docs/reference/http-api.md +3 -1
- 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 +5 -5
- package/dist/docs/reference/sessions.md +4 -2
- package/dist/docs/reference/skills.html +4 -4
- package/dist/docs/reference/subagents.html +4 -4
- package/dist/docs/reference/tools.html +15 -7
- package/dist/docs/reference/tools.md +34 -2
- package/dist/docs/scaffolding-agents.html +4 -4
- package/dist/docs/storage.html +4 -4
- package/dist/docs/templates/agentic-owners.html +4 -4
- package/dist/docs/templates/agents-md.html +28 -0
- package/dist/docs/templates/agents-md.md +105 -0
- package/dist/docs/templates/code-wiki.html +5 -5
- 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 +5 -5
- package/dist/docs/templates/triage.html +4 -4
- package/dist/docs/troubleshooting.html +5 -5
- package/dist/docs/troubleshooting.md +2 -2
- package/dist/evals/run-facts.d.ts.map +1 -1
- package/dist/evals/run-facts.js +27 -0
- package/dist/files-backends/cursor-hosted.d.ts +3 -2
- package/dist/files-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/files-backends/cursor-hosted.js +3 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/internal/advertise-tools.d.ts.map +1 -1
- package/dist/internal/advertise-tools.js +15 -9
- package/dist/internal/approval-gate.d.ts +12 -13
- package/dist/internal/approval-gate.d.ts.map +1 -1
- package/dist/internal/approval-gate.js +16 -21
- 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/chat-client.js +10 -0
- package/dist/internal/cli-deploy.d.ts +6 -0
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +149 -21
- package/dist/internal/cli-mcp-oauth.js +8 -18
- package/dist/internal/cli-slack-manual.d.ts +24 -2
- package/dist/internal/cli-slack-manual.d.ts.map +1 -1
- package/dist/internal/cli-slack-manual.js +251 -20
- package/dist/internal/cli-slack.d.ts +9 -2
- package/dist/internal/cli-slack.d.ts.map +1 -1
- package/dist/internal/cli-slack.js +29 -82
- package/dist/internal/cursor/account-mcp.d.ts +1 -0
- package/dist/internal/cursor/account-mcp.d.ts.map +1 -1
- package/dist/internal/cursor/account-mcp.js +17 -0
- package/dist/internal/cursor-agent-template.d.ts +1 -1
- package/dist/internal/cursor-agent-template.d.ts.map +1 -1
- package/dist/internal/cursor-agent-template.js +1 -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 +17 -0
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +31 -10
- package/dist/internal/env-file.d.ts +19 -0
- package/dist/internal/env-file.d.ts.map +1 -0
- package/dist/internal/env-file.js +90 -0
- package/dist/internal/framework-file-storage.d.ts +11 -17
- package/dist/internal/framework-file-storage.d.ts.map +1 -1
- package/dist/internal/framework-file-storage.js +17 -40
- 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 +37 -26
- 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 +13 -1
- package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
- package/dist/internal/hosted-delivery-protocol.js +8 -1
- package/dist/internal/hosted-delivery.d.ts +21 -1
- package/dist/internal/hosted-delivery.d.ts.map +1 -1
- package/dist/internal/hosted-delivery.js +101 -34
- package/dist/internal/hosted-execution-diag.d.ts +59 -0
- package/dist/internal/hosted-execution-diag.d.ts.map +1 -0
- package/dist/internal/hosted-execution-diag.js +187 -0
- package/dist/internal/http-channel.d.ts.map +1 -1
- package/dist/internal/http-channel.js +85 -2
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +2 -0
- package/dist/internal/logs-client.d.ts +5 -0
- package/dist/internal/logs-client.d.ts.map +1 -1
- package/dist/internal/logs-client.js +5 -0
- package/dist/internal/mcp-endpoint.js +2 -3
- package/dist/internal/mcp-host.d.ts +10 -0
- package/dist/internal/mcp-host.d.ts.map +1 -1
- package/dist/internal/mcp-host.js +57 -8
- package/dist/internal/mcp-oauth-on-demand.d.ts +68 -0
- package/dist/internal/mcp-oauth-on-demand.d.ts.map +1 -0
- package/dist/internal/mcp-oauth-on-demand.js +287 -0
- package/dist/internal/mcp-oauth-persist.d.ts +42 -0
- package/dist/internal/mcp-oauth-persist.d.ts.map +1 -0
- package/dist/internal/mcp-oauth-persist.js +92 -0
- package/dist/internal/mcp-oauth.d.ts +1 -0
- package/dist/internal/mcp-oauth.d.ts.map +1 -1
- package/dist/internal/mcp-oauth.js +10 -2
- package/dist/internal/null-kv-storage.d.ts +48 -0
- package/dist/internal/null-kv-storage.d.ts.map +1 -0
- package/dist/internal/null-kv-storage.js +58 -0
- package/dist/internal/otel/collector.d.ts.map +1 -1
- package/dist/internal/otel/collector.js +22 -9
- package/dist/internal/server.d.ts +6 -0
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +123 -33
- package/dist/internal/session-engine.d.ts +88 -3
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +538 -46
- 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 +359 -0
- package/dist/internal/sessions-client.d.ts +9 -7
- package/dist/internal/sessions-client.d.ts.map +1 -1
- package/dist/internal/sessions-client.js +3 -1
- package/dist/internal/storage-coordinator.d.ts +13 -0
- package/dist/internal/storage-coordinator.d.ts.map +1 -1
- package/dist/internal/storage-coordinator.js +37 -6
- package/dist/internal/storage-roles.d.ts +5 -0
- package/dist/internal/storage-roles.d.ts.map +1 -1
- package/dist/internal/stream-progress.d.ts.map +1 -1
- package/dist/internal/stream-progress.js +10 -0
- package/dist/internal/tool-policy.d.ts +29 -9
- package/dist/internal/tool-policy.d.ts.map +1 -1
- package/dist/internal/tool-policy.js +24 -0
- package/dist/internal/trajectory.d.ts.map +1 -1
- package/dist/internal/trajectory.js +10 -0
- package/dist/internal/turn-outbox.d.ts +24 -0
- package/dist/internal/turn-outbox.d.ts.map +1 -0
- package/dist/internal/turn-outbox.js +80 -0
- package/dist/playground/assets/index-D7rGZNjy.css +1 -0
- package/dist/playground/assets/index-jzNbJENa.js +69 -0
- package/dist/playground/index.html +2 -2
- package/dist/storage-backends/cursor-hosted.d.ts +1 -1
- package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/storage-backends/cursor-hosted.js +12 -0
- 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/storage.d.ts +8 -0
- package/dist/storage.d.ts.map +1 -1
- package/dist/tools.d.ts +12 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/types.d.ts +88 -7
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +5 -3
- package/docs/deployment.md +5 -4
- package/docs/design/runtime-abstraction.md +1757 -0
- package/docs/guides/mcp-oauth.md +26 -8
- package/docs/guides/slack.md +93 -202
- package/docs/reference/agent-config.md +2 -2
- package/docs/reference/cli.md +32 -16
- package/docs/reference/connections.md +6 -3
- package/docs/reference/http-api.md +3 -1
- package/docs/reference/sessions.md +4 -2
- package/docs/reference/tools.md +34 -2
- package/docs/templates/agents-md.md +110 -0
- package/docs/troubleshooting.md +2 -2
- package/package.json +1 -1
- package/skills/create-agent/SKILL.md +2 -1
- package/skills/mcp-auth/SKILL.md +6 -3
- package/skills/setup-slack/SKILL.md +23 -13
- package/src/bin/agent-serve.ts +12 -1
- package/src/channels/github/github-channel.ts +17 -0
- package/src/channels/origin/api.ts +24 -5
- package/src/channels/origin/binding.ts +55 -26
- package/src/channels/origin/index.ts +1 -0
- package/src/channels/origin/origin-channel.ts +78 -10
- package/src/channels/origin/reactions.ts +82 -0
- package/src/channels/origin/types.ts +11 -0
- package/src/channels/slack/approvals.ts +105 -0
- package/src/channels/slack/cli-project.ts +267 -0
- package/src/channels/slack/defaults.ts +165 -74
- package/src/channels/slack/dispatch.ts +2 -0
- package/src/channels/slack/init.ts +10 -5
- package/src/channels/slack/interactive.ts +35 -9
- package/src/channels/slack/live-delivery.ts +5 -3
- package/src/channels/slack/manifest.ts +1 -1
- package/src/channels/slack/progress-delivery.ts +133 -0
- package/src/channels/slack/setup.ts +10 -6
- package/src/channels/slack/slack-channel.ts +1 -0
- package/src/channels/slack/tool-approval-events.ts +93 -0
- package/src/channels/slack/types.ts +9 -1
- package/src/evals/run-facts.ts +32 -0
- package/src/files-backends/cursor-hosted.ts +3 -2
- package/src/index.ts +4 -0
- package/src/internal/advertise-tools.ts +31 -10
- package/src/internal/approval-gate.ts +20 -29
- package/src/internal/as-of.ts +107 -0
- package/src/internal/chat-client.ts +19 -0
- package/src/internal/cli-deploy.ts +203 -13
- package/src/internal/cli-mcp-oauth.ts +7 -21
- package/src/internal/cli-slack-manual.ts +350 -18
- package/src/internal/cli-slack.ts +40 -84
- package/src/internal/cursor/account-mcp.ts +21 -0
- package/src/internal/cursor-agent-template.ts +1 -0
- package/src/internal/cursor-event-relay.ts +5 -6
- package/src/internal/deploy-client.ts +43 -0
- package/src/internal/env-file.ts +84 -0
- package/src/internal/framework-file-storage.ts +18 -46
- package/src/internal/framework-storage-selection.ts +41 -25
- package/src/internal/hosted-admission-adapter.ts +42 -0
- package/src/internal/hosted-delivery-protocol.ts +14 -2
- package/src/internal/hosted-delivery.ts +169 -44
- package/src/internal/hosted-execution-diag.ts +252 -0
- package/src/internal/http-channel.ts +107 -0
- package/src/internal/init-project.ts +2 -0
- package/src/internal/logs-client.ts +5 -0
- package/src/internal/mcp-endpoint.ts +13 -4
- package/src/internal/mcp-host.ts +77 -11
- package/src/internal/mcp-oauth-on-demand.ts +352 -0
- package/src/internal/mcp-oauth-persist.ts +127 -0
- package/src/internal/mcp-oauth.ts +17 -1
- package/src/internal/null-kv-storage.ts +74 -0
- package/src/internal/otel/collector.ts +30 -0
- package/src/internal/server.ts +159 -11
- package/src/internal/session-engine.ts +691 -57
- package/src/internal/session-run-log.ts +584 -0
- package/src/internal/sessions-client.ts +17 -8
- package/src/internal/storage-coordinator.ts +49 -9
- package/src/internal/storage-roles.ts +7 -0
- package/src/internal/stream-progress.ts +19 -0
- package/src/internal/tool-policy.ts +49 -9
- package/src/internal/trajectory.ts +10 -0
- package/src/internal/turn-outbox.ts +85 -0
- package/src/storage-backends/cursor-hosted.ts +13 -1
- package/src/storage-protocol.ts +10 -0
- package/src/storage.ts +12 -0
- package/src/tools.ts +12 -2
- package/src/types.ts +93 -8
- package/templates/agents-md/README.md +39 -0
- package/templates/agents-md/agent/agent.ts +8 -0
- package/templates/agents-md/agent/channels/github.ts +10 -0
- package/templates/agents-md/agent/channels/queue.ts +28 -0
- package/templates/agents-md/agent/channels/slack.ts +9 -0
- package/templates/agents-md/agent/instructions.md +39 -0
- package/templates/agents-md/agent/lib/collect.ts +439 -0
- package/templates/agents-md/agent/lib/config.ts +8 -0
- package/templates/agents-md/agent/lib/paths.ts +51 -0
- package/templates/agents-md/agent/schedules/weekly.ts +17 -0
- package/templates/agents-md/agent/tools/apply_agents.ts +226 -0
- package/templates/agents-md/evals/agents.eval.ts +135 -0
- package/templates/agents-md/evals/evals.config.ts +5 -0
- package/templates/agents-md/init.json +38 -0
- package/templates/agents-md/package.json +18 -0
- package/templates/agents-md/tsconfig.json +12 -0
- package/dist/docs/assets/chunks/@localSearchIndexroot.DFoGuETX.js +0 -1
- package/dist/docs/assets/guides_slack.md.C32HsdKk.js +0 -38
- package/dist/docs/assets/guides_slack.md.C32HsdKk.lean.js +0 -1
- package/dist/docs/assets/index.md.CI93Ltlb.js +0 -5
- package/dist/docs/assets/reference_sessions.md.D0mIh4KK.js +0 -1
- package/dist/docs/assets/troubleshooting.md.Ctv3T8C2.js +0 -1
- package/dist/docs/design/agsh.md +0 -406
- package/dist/playground/assets/index-B1DDvBeC.css +0 -1
- package/dist/playground/assets/index-Cx9CclK_.js +0 -88
- package/docs/design/agsh.md +0 -406
- /package/dist/docs/assets/{deployment.md.D9msOFOW.lean.js → deployment.md.MFPKqsqI.lean.js} +0 -0
- /package/dist/docs/assets/{index.md.CI93Ltlb.lean.js → index.md.CVeRUOeZ.lean.js} +0 -0
- /package/dist/docs/assets/{reference_agent-config.md.Cp_x38Nl.lean.js → reference_agent-config.md.BHeYMgme.lean.js} +0 -0
- /package/dist/docs/assets/{reference_connections.md.BH8Oc0D0.lean.js → reference_connections.md.DxldvyIB.lean.js} +0 -0
- /package/dist/docs/assets/{reference_http-api.md.D89k1mdm.lean.js → reference_http-api.md.BEJx9XVj.lean.js} +0 -0
- /package/dist/docs/assets/{reference_sessions.md.D0mIh4KK.lean.js → reference_sessions.md.NxFP_CAC.lean.js} +0 -0
- /package/dist/docs/assets/{troubleshooting.md.Ctv3T8C2.lean.js → troubleshooting.md.DCiPBhYs.lean.js} +0 -0
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
|
-
import {
|
|
3
|
-
dispatchGitHubWebhook,
|
|
4
|
-
getGitHubChannelOptions,
|
|
5
|
-
getGitHubDispatchOutcome,
|
|
6
|
-
withPropagatingGitHubBackgroundErrors,
|
|
7
|
-
} from "../channels/github/github-channel.js";
|
|
8
2
|
import type {
|
|
9
3
|
AuthPolicy,
|
|
10
4
|
ChannelHandlerArgs,
|
|
@@ -14,6 +8,10 @@ import {
|
|
|
14
8
|
createDeferredChannelSession,
|
|
15
9
|
DeferredChannelSessionError,
|
|
16
10
|
} from "./deferred-channel-session.js";
|
|
11
|
+
import {
|
|
12
|
+
getHostedAdmissionAdapter,
|
|
13
|
+
type HostedAdmissionAdapter,
|
|
14
|
+
} from "./hosted-admission-adapter.js";
|
|
17
15
|
import {
|
|
18
16
|
HostedAdmissionLocalSendError,
|
|
19
17
|
runDuringHostedAdmission,
|
|
@@ -27,9 +25,22 @@ import {
|
|
|
27
25
|
parseHostedAdmission,
|
|
28
26
|
parseHostedExecution,
|
|
29
27
|
} from "./hosted-delivery-protocol.js";
|
|
28
|
+
import {
|
|
29
|
+
abortReasonFromTurnStatus,
|
|
30
|
+
HOSTED_EXECUTION_DIAG_RUNNING_STATUS,
|
|
31
|
+
hostedErrorStackSnippet,
|
|
32
|
+
hostedExecutionDiagId,
|
|
33
|
+
writeHostedExecutionDiag,
|
|
34
|
+
} from "./hosted-execution-diag.js";
|
|
30
35
|
import { recordHostedManagedAutomation } from "./hosted-managed-automation.js";
|
|
31
36
|
import { errorResponse, readJsonObject } from "./http-channel.js";
|
|
37
|
+
import type { LogRingEntry } from "./log-ring.js";
|
|
32
38
|
import type { Router } from "./router.js";
|
|
39
|
+
import {
|
|
40
|
+
type SessionRunExecutionWrite,
|
|
41
|
+
serializeSessionRunError,
|
|
42
|
+
snapshotSessionRunLogs,
|
|
43
|
+
} from "./session-run-log.js";
|
|
33
44
|
|
|
34
45
|
type DispatchInput = {
|
|
35
46
|
delivery: HostedAdmission;
|
|
@@ -109,17 +120,20 @@ function nonRetryableRejection(error: unknown): AdmissionDispatch | undefined {
|
|
|
109
120
|
}
|
|
110
121
|
|
|
111
122
|
/**
|
|
112
|
-
* Run authored admission without creating a framework session.
|
|
123
|
+
* Run authored admission without creating a framework session. Authored
|
|
113
124
|
* `task` callbacks execute as sessionless work; the first `send` is captured
|
|
114
125
|
* and executed only after the control plane allocates `ses_…`. Capturing
|
|
115
126
|
* does not terminate the caller — `send` returns an inert deferred handle so
|
|
116
127
|
* a task body runs to completion before the admission response is computed.
|
|
128
|
+
*
|
|
129
|
+
* Dispatch is the adapter the mounted channel attached. Hosted delivery
|
|
130
|
+
* does not switch on `channelKind`.
|
|
117
131
|
*/
|
|
118
132
|
export async function dispatchHostedAdmission(
|
|
119
133
|
input: DispatchInput
|
|
120
134
|
): Promise<AdmissionDispatch> {
|
|
121
|
-
const
|
|
122
|
-
if (
|
|
135
|
+
const adapter = getHostedAdmissionAdapter(input.definition);
|
|
136
|
+
if (adapter === undefined || adapter.kind !== input.delivery.channelKind) {
|
|
123
137
|
return {
|
|
124
138
|
response: Response.json(
|
|
125
139
|
{ ok: false, error: "channel_kind_mismatch" },
|
|
@@ -129,16 +143,16 @@ export async function dispatchHostedAdmission(
|
|
|
129
143
|
}
|
|
130
144
|
// The admission window marker follows the async graph into task bodies so
|
|
131
145
|
// the engine can refuse local sends that would bypass the capture stub.
|
|
132
|
-
return runDuringHostedAdmission(() => runAdmission(input,
|
|
146
|
+
return runDuringHostedAdmission(() => runAdmission(input, adapter));
|
|
133
147
|
}
|
|
134
148
|
|
|
135
149
|
async function runAdmission(
|
|
136
150
|
input: DispatchInput,
|
|
137
|
-
|
|
151
|
+
adapter: HostedAdmissionAdapter
|
|
138
152
|
): Promise<AdmissionDispatch> {
|
|
139
153
|
const background: Promise<unknown>[] = [];
|
|
140
154
|
let captured: { message: string; options: SendMessageOptions } | undefined;
|
|
141
|
-
const
|
|
155
|
+
const capturingArgs: ChannelHandlerArgs = {
|
|
142
156
|
...input.args,
|
|
143
157
|
send: async (message, options = {}) => {
|
|
144
158
|
if (captured !== undefined) {
|
|
@@ -150,12 +164,12 @@ async function runAdmission(
|
|
|
150
164
|
waitUntil: (promise) => {
|
|
151
165
|
background.push(promise);
|
|
152
166
|
},
|
|
153
|
-
}
|
|
167
|
+
};
|
|
168
|
+
const args = adapter.prepareArgs?.(capturingArgs) ?? capturingArgs;
|
|
154
169
|
let dispatched: Response;
|
|
155
170
|
try {
|
|
156
|
-
dispatched = await
|
|
157
|
-
|
|
158
|
-
eventName: input.delivery.eventType,
|
|
171
|
+
dispatched = await adapter.dispatch({
|
|
172
|
+
eventType: input.delivery.eventType,
|
|
159
173
|
payload: input.delivery.payload,
|
|
160
174
|
deliveryId: input.delivery.deliveryId,
|
|
161
175
|
args,
|
|
@@ -206,7 +220,7 @@ async function runAdmission(
|
|
|
206
220
|
},
|
|
207
221
|
};
|
|
208
222
|
}
|
|
209
|
-
const outcome =
|
|
223
|
+
const outcome = adapter.getOutcome(dispatched);
|
|
210
224
|
if (outcome === undefined) {
|
|
211
225
|
return {
|
|
212
226
|
response: Response.json(
|
|
@@ -252,6 +266,21 @@ export function registerHostedDeliveryRoute(input: {
|
|
|
252
266
|
* the conversation on the next wake.
|
|
253
267
|
*/
|
|
254
268
|
drainSessionDurability?(sessionId: string): Promise<void>;
|
|
269
|
+
/**
|
|
270
|
+
* Serve `--state-root` (not the per-slug SessionEngine dir). Hosted
|
|
271
|
+
* freeze is `/workspace/.agent-serve`; the Temporal host reads
|
|
272
|
+
* `hosted-execution.json` from that path after the POST returns.
|
|
273
|
+
*/
|
|
274
|
+
stateRoot?: string;
|
|
275
|
+
/**
|
|
276
|
+
* Upsert this execution into the v2 session-level framework-store log.
|
|
277
|
+
* Called for the pre-turn `running` claim and every terminal outcome so
|
|
278
|
+
* a dead Node process still leaves a leftover. Soft-fail inside the
|
|
279
|
+
* store; this path still awaits so a parked pod cannot shed the write.
|
|
280
|
+
*/
|
|
281
|
+
persistSessionRun?(write: SessionRunExecutionWrite): Promise<void>;
|
|
282
|
+
/** Snapshot of the process log ring at persist time. */
|
|
283
|
+
snapshotLogs?(): LogRingEntry[];
|
|
255
284
|
}): void {
|
|
256
285
|
const pending = new Map<
|
|
257
286
|
string,
|
|
@@ -383,39 +412,135 @@ export function registerHostedDeliveryRoute(input: {
|
|
|
383
412
|
}
|
|
384
413
|
admitted.sessionPublicId = execution.sessionPublicId;
|
|
385
414
|
admitted.execution ??= (async (): Promise<ExecutionReply> => {
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
415
|
+
const startedAt = performance.now();
|
|
416
|
+
const executionId = hostedExecutionDiagId(execution.admissionToken);
|
|
417
|
+
const writeDiag = async (fields: {
|
|
418
|
+
sessionId: string;
|
|
419
|
+
turnStatus: string;
|
|
420
|
+
message?: string;
|
|
421
|
+
abortReason?:
|
|
422
|
+
| ReturnType<typeof abortReasonFromTurnStatus>
|
|
423
|
+
| "execute_throw";
|
|
424
|
+
error?: unknown;
|
|
425
|
+
}): Promise<void> => {
|
|
426
|
+
const durationMs = Math.max(
|
|
427
|
+
0,
|
|
428
|
+
Math.round(performance.now() - startedAt)
|
|
429
|
+
);
|
|
430
|
+
const message =
|
|
431
|
+
fields.message === undefined
|
|
432
|
+
? undefined
|
|
433
|
+
: boundedMessage(fields.message);
|
|
434
|
+
const stack =
|
|
435
|
+
fields.error === undefined
|
|
436
|
+
? undefined
|
|
437
|
+
: hostedErrorStackSnippet(fields.error);
|
|
438
|
+
if (input.stateRoot !== undefined) {
|
|
439
|
+
await writeHostedExecutionDiag(input.stateRoot, {
|
|
440
|
+
v: 1,
|
|
441
|
+
executionId,
|
|
442
|
+
writtenAt: new Date().toISOString(),
|
|
443
|
+
sessionPublicId: execution.sessionPublicId,
|
|
444
|
+
sessionId: fields.sessionId,
|
|
445
|
+
turnStatus: fields.turnStatus,
|
|
446
|
+
durationMs,
|
|
447
|
+
...(message === undefined ? {} : { message }),
|
|
448
|
+
...(stack === undefined || stack.length === 0 ? {} : { stack }),
|
|
449
|
+
...(message !== undefined && /abort/i.test(message)
|
|
450
|
+
? { signalAborted: true }
|
|
451
|
+
: {}),
|
|
452
|
+
...(fields.abortReason === undefined
|
|
453
|
+
? {}
|
|
454
|
+
: { abortReason: fields.abortReason }),
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
if (input.persistSessionRun === undefined) {
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
const error =
|
|
461
|
+
fields.error === undefined
|
|
462
|
+
? undefined
|
|
463
|
+
: serializeSessionRunError(fields.error);
|
|
464
|
+
try {
|
|
465
|
+
await input.persistSessionRun({
|
|
466
|
+
executionId,
|
|
467
|
+
writtenAt: new Date().toISOString(),
|
|
468
|
+
sessionPublicId: execution.sessionPublicId,
|
|
469
|
+
sessionId: fields.sessionId,
|
|
470
|
+
turnStatus: fields.turnStatus,
|
|
471
|
+
durationMs,
|
|
472
|
+
...(fields.abortReason === undefined
|
|
473
|
+
? {}
|
|
474
|
+
: { abortReason: fields.abortReason }),
|
|
475
|
+
...(message === undefined ? {} : { message }),
|
|
476
|
+
...(error === undefined ? {} : { error }),
|
|
477
|
+
logs: snapshotSessionRunLogs(input.snapshotLogs?.() ?? []),
|
|
478
|
+
});
|
|
479
|
+
} catch {
|
|
480
|
+
// Soft-fail: a store outage must not change the HTTP reply.
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
// Claim the file before the turn runs. The host reads it after its
|
|
484
|
+
// own POST returns, which for a 9-minute timeout is while this turn
|
|
485
|
+
// is still going; without this marker that read would surface the
|
|
486
|
+
// PREVIOUS delivery's terminal record as if it were this one's.
|
|
487
|
+
await writeDiag({
|
|
488
|
+
sessionId: "",
|
|
489
|
+
turnStatus: HOSTED_EXECUTION_DIAG_RUNNING_STATUS,
|
|
395
490
|
});
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
491
|
+
try {
|
|
492
|
+
const args = input.buildHandlerArgs(
|
|
493
|
+
admitted.send.channelId,
|
|
494
|
+
execution.sessionPublicId
|
|
495
|
+
);
|
|
496
|
+
const session = await args.send(admitted.send.message, {
|
|
497
|
+
...admitted.send.options,
|
|
498
|
+
mailboxContinuation: {
|
|
499
|
+
key: admitted.send.mailboxContinuationKey,
|
|
500
|
+
},
|
|
501
|
+
});
|
|
502
|
+
const outcome = await session.waitForCompletion();
|
|
503
|
+
// Failed turns drain too: their record/continuation updates must
|
|
504
|
+
// survive the park just like a finished turn's.
|
|
505
|
+
await input.drainSessionDurability?.(session.id);
|
|
506
|
+
await writeDiag({
|
|
507
|
+
sessionId: session.id,
|
|
508
|
+
turnStatus: outcome.status,
|
|
509
|
+
message: outcome.errorMessage,
|
|
510
|
+
abortReason: abortReasonFromTurnStatus(
|
|
511
|
+
outcome.status,
|
|
512
|
+
outcome.errorMessage
|
|
513
|
+
),
|
|
514
|
+
});
|
|
515
|
+
if (outcome.status !== "finished") {
|
|
516
|
+
return {
|
|
517
|
+
status: 503,
|
|
518
|
+
body: {
|
|
519
|
+
ok: false,
|
|
520
|
+
error: "turn_failed",
|
|
521
|
+
turnStatus: outcome.status,
|
|
522
|
+
message: outcome.errorMessage,
|
|
523
|
+
},
|
|
524
|
+
};
|
|
525
|
+
}
|
|
401
526
|
return {
|
|
402
|
-
status:
|
|
527
|
+
status: 200,
|
|
403
528
|
body: {
|
|
404
|
-
ok:
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
message: outcome.errorMessage,
|
|
529
|
+
ok: true,
|
|
530
|
+
v: HOSTED_DELIVERY_PROTOCOL_VERSION,
|
|
531
|
+
outcome: "send",
|
|
408
532
|
},
|
|
409
533
|
};
|
|
534
|
+
} catch (error) {
|
|
535
|
+
await writeDiag({
|
|
536
|
+
sessionId: "",
|
|
537
|
+
turnStatus: "error",
|
|
538
|
+
message: error instanceof Error ? error.message : String(error),
|
|
539
|
+
abortReason: "execute_throw",
|
|
540
|
+
error,
|
|
541
|
+
});
|
|
542
|
+
throw error;
|
|
410
543
|
}
|
|
411
|
-
return {
|
|
412
|
-
status: 200,
|
|
413
|
-
body: {
|
|
414
|
-
ok: true,
|
|
415
|
-
v: HOSTED_DELIVERY_PROTOCOL_VERSION,
|
|
416
|
-
outcome: "send",
|
|
417
|
-
},
|
|
418
|
-
};
|
|
419
544
|
})();
|
|
420
545
|
const reply = await admitted.execution;
|
|
421
546
|
return finish(Response.json(reply.body, { status: reply.status }));
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
+
import { mkdir, rename, unlink, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
import { HOSTED_EXECUTION_DIAG_FILENAME } from "./hosted-delivery-protocol.js";
|
|
6
|
+
|
|
7
|
+
export const HOSTED_EXECUTION_DIAG_VERSION = 1;
|
|
8
|
+
/** Cap so a runaway message cannot fill the guest disk or the host log. */
|
|
9
|
+
export const HOSTED_EXECUTION_DIAG_MAX_BYTES = 4096;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Correlate the on-disk record with the execution that produced it. One pod
|
|
13
|
+
* serves many deliveries of a continuation session, and the file lives at a
|
|
14
|
+
* single fixed path, so without this the host can read a *previous*
|
|
15
|
+
* delivery's terminal record whenever July never reaches its own write —
|
|
16
|
+
* the 9-minute host POST timeout, a 409 on an expired admission token, or a
|
|
17
|
+
* replayed token that short-circuits on the memoized reply.
|
|
18
|
+
*
|
|
19
|
+
* Derived from the admission token (which both sides already hold) rather
|
|
20
|
+
* than carried on the wire. The digest is one-way, so the token itself never
|
|
21
|
+
* reaches the guest disk or the host logs.
|
|
22
|
+
*/
|
|
23
|
+
export function hostedExecutionDiagId(admissionToken: string): string {
|
|
24
|
+
return createHash("sha256").update(admissionToken).digest("hex").slice(0, 16);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type HostedExecutionDiagAbortReason =
|
|
28
|
+
| "signal_abort"
|
|
29
|
+
| "runner_cancelled"
|
|
30
|
+
| "runner_error"
|
|
31
|
+
| "execute_throw";
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Bounded guest-side record of one hosted-execution attempt. No prompt,
|
|
35
|
+
* tool content, or API key — the host copies known keys onto the Temporal
|
|
36
|
+
* activity span for Datadog.
|
|
37
|
+
*/
|
|
38
|
+
export type HostedExecutionDiag = {
|
|
39
|
+
v: typeof HOSTED_EXECUTION_DIAG_VERSION;
|
|
40
|
+
/** {@link hostedExecutionDiagId} of the execution that wrote this record. */
|
|
41
|
+
executionId: string;
|
|
42
|
+
writtenAt: string;
|
|
43
|
+
sessionPublicId: string;
|
|
44
|
+
sessionId: string;
|
|
45
|
+
/** Terminal {@link TurnOutcome} status, or `running` before the turn ends. */
|
|
46
|
+
turnStatus: string;
|
|
47
|
+
durationMs: number;
|
|
48
|
+
message?: string;
|
|
49
|
+
/** First frames of a thrown Error; omitted on soft turn failures. */
|
|
50
|
+
stack?: string;
|
|
51
|
+
signalAborted?: boolean;
|
|
52
|
+
requestId?: string;
|
|
53
|
+
abortReason?: HostedExecutionDiagAbortReason;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** Written before the turn starts so a stalled execution still has a record. */
|
|
57
|
+
export const HOSTED_EXECUTION_DIAG_RUNNING_STATUS = "running";
|
|
58
|
+
|
|
59
|
+
const DIAG_KEYS = new Set([
|
|
60
|
+
"v",
|
|
61
|
+
"executionId",
|
|
62
|
+
"writtenAt",
|
|
63
|
+
"sessionPublicId",
|
|
64
|
+
"sessionId",
|
|
65
|
+
"turnStatus",
|
|
66
|
+
"durationMs",
|
|
67
|
+
"message",
|
|
68
|
+
"stack",
|
|
69
|
+
"signalAborted",
|
|
70
|
+
"requestId",
|
|
71
|
+
"abortReason",
|
|
72
|
+
]);
|
|
73
|
+
|
|
74
|
+
const ABORT_REASONS = new Set<HostedExecutionDiagAbortReason>([
|
|
75
|
+
"signal_abort",
|
|
76
|
+
"runner_cancelled",
|
|
77
|
+
"runner_error",
|
|
78
|
+
"execute_throw",
|
|
79
|
+
]);
|
|
80
|
+
|
|
81
|
+
function boundedOptionalString(
|
|
82
|
+
value: unknown,
|
|
83
|
+
max: number
|
|
84
|
+
): string | undefined {
|
|
85
|
+
return typeof value === "string" && value.length > 0
|
|
86
|
+
? value.length > max
|
|
87
|
+
? `${value.slice(0, max)}…`
|
|
88
|
+
: value
|
|
89
|
+
: undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** First line + frames; factory `last_error` is 1024 chars with a TAS prefix. */
|
|
93
|
+
export const HOSTED_ERROR_STACK_LINE_COUNT = 5;
|
|
94
|
+
export const HOSTED_ERROR_SNIPPET_MAX_CHARS = 700;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Bounded throw text for the hosted 500 body and the on-disk diag `stack`.
|
|
98
|
+
* Prefers `Error.stack` (message line + `at` frames) so `last_error` is not
|
|
99
|
+
* the generic "Internal server error." when the throw has a stack.
|
|
100
|
+
*/
|
|
101
|
+
export function hostedErrorStackSnippet(error: unknown): string {
|
|
102
|
+
if (error instanceof Error) {
|
|
103
|
+
const stack = error.stack?.trim();
|
|
104
|
+
if (stack !== undefined && stack.length > 0) {
|
|
105
|
+
const lines = stack
|
|
106
|
+
.split("\n")
|
|
107
|
+
.map((line) => line.trimEnd())
|
|
108
|
+
.filter((line) => line.trim().length > 0)
|
|
109
|
+
.slice(0, HOSTED_ERROR_STACK_LINE_COUNT);
|
|
110
|
+
const text = lines.join("\n");
|
|
111
|
+
return text.length > HOSTED_ERROR_SNIPPET_MAX_CHARS
|
|
112
|
+
? `${text.slice(0, HOSTED_ERROR_SNIPPET_MAX_CHARS)}…`
|
|
113
|
+
: text;
|
|
114
|
+
}
|
|
115
|
+
return error.message.length > HOSTED_ERROR_SNIPPET_MAX_CHARS
|
|
116
|
+
? `${error.message.slice(0, HOSTED_ERROR_SNIPPET_MAX_CHARS)}…`
|
|
117
|
+
: error.message;
|
|
118
|
+
}
|
|
119
|
+
const text = String(error);
|
|
120
|
+
if (text.length === 0) {
|
|
121
|
+
return "";
|
|
122
|
+
}
|
|
123
|
+
return text.length > HOSTED_ERROR_SNIPPET_MAX_CHARS
|
|
124
|
+
? `${text.slice(0, HOSTED_ERROR_SNIPPET_MAX_CHARS)}…`
|
|
125
|
+
: text;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function hostedExecutionDiagPath(stateRoot: string): string {
|
|
129
|
+
return join(stateRoot, HOSTED_EXECUTION_DIAG_FILENAME);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function abortReasonFromTurnStatus(
|
|
133
|
+
status: string,
|
|
134
|
+
message: string | undefined
|
|
135
|
+
): HostedExecutionDiagAbortReason | undefined {
|
|
136
|
+
if (status === "finished") {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
if (status === "cancelled") {
|
|
140
|
+
return message !== undefined && /abort/i.test(message)
|
|
141
|
+
? "signal_abort"
|
|
142
|
+
: "runner_cancelled";
|
|
143
|
+
}
|
|
144
|
+
return "runner_error";
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function parseHostedExecutionDiag(
|
|
148
|
+
raw: string
|
|
149
|
+
): HostedExecutionDiag | undefined {
|
|
150
|
+
if (raw.length === 0 || raw.length > HOSTED_EXECUTION_DIAG_MAX_BYTES) {
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
let parsed: unknown;
|
|
154
|
+
try {
|
|
155
|
+
parsed = JSON.parse(raw);
|
|
156
|
+
} catch {
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
const body = parsed as Record<string, unknown>;
|
|
163
|
+
if (Object.keys(body).some((key) => !DIAG_KEYS.has(key))) {
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
if (
|
|
167
|
+
body.v !== HOSTED_EXECUTION_DIAG_VERSION ||
|
|
168
|
+
typeof body.executionId !== "string" ||
|
|
169
|
+
body.executionId.length === 0 ||
|
|
170
|
+
body.executionId.length > 64 ||
|
|
171
|
+
typeof body.writtenAt !== "string" ||
|
|
172
|
+
body.writtenAt.length === 0 ||
|
|
173
|
+
body.writtenAt.length > 64 ||
|
|
174
|
+
typeof body.sessionPublicId !== "string" ||
|
|
175
|
+
body.sessionPublicId.length === 0 ||
|
|
176
|
+
body.sessionPublicId.length > 128 ||
|
|
177
|
+
typeof body.sessionId !== "string" ||
|
|
178
|
+
body.sessionId.length > 128 ||
|
|
179
|
+
typeof body.turnStatus !== "string" ||
|
|
180
|
+
body.turnStatus.length === 0 ||
|
|
181
|
+
body.turnStatus.length > 64 ||
|
|
182
|
+
typeof body.durationMs !== "number" ||
|
|
183
|
+
!Number.isFinite(body.durationMs) ||
|
|
184
|
+
body.durationMs < 0
|
|
185
|
+
) {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
const message = boundedOptionalString(body.message, 1024);
|
|
189
|
+
const stack = boundedOptionalString(
|
|
190
|
+
body.stack,
|
|
191
|
+
HOSTED_ERROR_SNIPPET_MAX_CHARS
|
|
192
|
+
);
|
|
193
|
+
const requestId = boundedOptionalString(body.requestId, 128);
|
|
194
|
+
const abortReason =
|
|
195
|
+
body.abortReason === undefined
|
|
196
|
+
? undefined
|
|
197
|
+
: typeof body.abortReason === "string" &&
|
|
198
|
+
ABORT_REASONS.has(body.abortReason as HostedExecutionDiagAbortReason)
|
|
199
|
+
? (body.abortReason as HostedExecutionDiagAbortReason)
|
|
200
|
+
: undefined;
|
|
201
|
+
if (body.abortReason !== undefined && abortReason === undefined) {
|
|
202
|
+
return undefined;
|
|
203
|
+
}
|
|
204
|
+
if (
|
|
205
|
+
body.signalAborted !== undefined &&
|
|
206
|
+
typeof body.signalAborted !== "boolean"
|
|
207
|
+
) {
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
v: HOSTED_EXECUTION_DIAG_VERSION,
|
|
212
|
+
executionId: body.executionId,
|
|
213
|
+
writtenAt: body.writtenAt,
|
|
214
|
+
sessionPublicId: body.sessionPublicId,
|
|
215
|
+
sessionId: body.sessionId,
|
|
216
|
+
turnStatus: body.turnStatus,
|
|
217
|
+
durationMs: body.durationMs,
|
|
218
|
+
...(message === undefined ? {} : { message }),
|
|
219
|
+
...(stack === undefined ? {} : { stack }),
|
|
220
|
+
...(body.signalAborted === true ? { signalAborted: true } : {}),
|
|
221
|
+
...(requestId === undefined ? {} : { requestId }),
|
|
222
|
+
...(abortReason === undefined ? {} : { abortReason }),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Overwrite the per-execution diag file. Soft-fail: a write error must not
|
|
228
|
+
* change the hosted-execution HTTP reply.
|
|
229
|
+
*/
|
|
230
|
+
export async function writeHostedExecutionDiag(
|
|
231
|
+
stateRoot: string,
|
|
232
|
+
diag: HostedExecutionDiag
|
|
233
|
+
): Promise<void> {
|
|
234
|
+
const path = hostedExecutionDiagPath(stateRoot);
|
|
235
|
+
const tmp = `${path}.${randomBytes(8).toString("hex")}.tmp`;
|
|
236
|
+
try {
|
|
237
|
+
await mkdir(stateRoot, { recursive: true });
|
|
238
|
+
const serialized = JSON.stringify(diag);
|
|
239
|
+
const body =
|
|
240
|
+
serialized.length > HOSTED_EXECUTION_DIAG_MAX_BYTES
|
|
241
|
+
? JSON.stringify({
|
|
242
|
+
...diag,
|
|
243
|
+
stack: undefined,
|
|
244
|
+
message: boundedOptionalString(diag.message, 256),
|
|
245
|
+
})
|
|
246
|
+
: serialized;
|
|
247
|
+
await writeFile(tmp, body, "utf8");
|
|
248
|
+
await rename(tmp, path);
|
|
249
|
+
} catch {
|
|
250
|
+
await unlink(tmp).catch(() => undefined);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
@@ -25,6 +25,8 @@ import type { ServerRoute } from "./router.js";
|
|
|
25
25
|
import {
|
|
26
26
|
ApprovalInterruptedError,
|
|
27
27
|
ApprovalNotFoundError,
|
|
28
|
+
AuthInterruptedError,
|
|
29
|
+
AuthNotFoundError,
|
|
28
30
|
HTTP_CHANNEL_ID,
|
|
29
31
|
SessionAuthError,
|
|
30
32
|
type SessionEngine,
|
|
@@ -93,6 +95,13 @@ export function buildHttpChannelRoutes(args: {
|
|
|
93
95
|
"Expected a JSON body with a string `message`."
|
|
94
96
|
);
|
|
95
97
|
}
|
|
98
|
+
if (body.asOf !== undefined && typeof body.asOf !== "string") {
|
|
99
|
+
return errorResponse(
|
|
100
|
+
400,
|
|
101
|
+
"invalid_request",
|
|
102
|
+
"`asOf` must be an ISO-8601 instant with a timezone when provided."
|
|
103
|
+
);
|
|
104
|
+
}
|
|
96
105
|
const message = await transformMessage(body.message, ctx.auth);
|
|
97
106
|
const workspaceFiles = readWorkspaceFiles(body.workspaceFiles);
|
|
98
107
|
// Per-session cloud attach (create only). Eval harness uses this to pin
|
|
@@ -103,6 +112,7 @@ export function buildHttpChannelRoutes(args: {
|
|
|
103
112
|
...(typeof body.title === "string" ? { title: body.title } : {}),
|
|
104
113
|
...(body.purpose === "eval" ? { purpose: "eval" as const } : {}),
|
|
105
114
|
...(body.dryRun === true ? { dryRun: true } : {}),
|
|
115
|
+
asOf: body.asOf,
|
|
106
116
|
...(workspaceFiles === undefined ? {} : { workspaceFiles }),
|
|
107
117
|
...(cloud === undefined ? {} : { cloud }),
|
|
108
118
|
});
|
|
@@ -454,6 +464,101 @@ export function buildHttpChannelRoutes(args: {
|
|
|
454
464
|
},
|
|
455
465
|
};
|
|
456
466
|
|
|
467
|
+
const listAuths: ServerRoute = {
|
|
468
|
+
method: "GET",
|
|
469
|
+
pattern: `${base}/v1/session/:sessionId/auths`,
|
|
470
|
+
auth,
|
|
471
|
+
handler: async (_request, ctx) => {
|
|
472
|
+
const sessionId = ctx.params.sessionId ?? "";
|
|
473
|
+
const record = await engine.getOrRestoreSession(sessionId);
|
|
474
|
+
if (record === undefined) {
|
|
475
|
+
return errorResponse(
|
|
476
|
+
404,
|
|
477
|
+
"session_not_found",
|
|
478
|
+
`Unknown session ${sessionId}.`
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
const ownership = requireSessionOwner(record, ctx.auth, {
|
|
482
|
+
relax: relaxSessionReadAuth,
|
|
483
|
+
beyondLoopback: relaxSessionReadAuthBeyondLoopback,
|
|
484
|
+
requestIp: ctx.requestIp,
|
|
485
|
+
});
|
|
486
|
+
if (ownership !== undefined) {
|
|
487
|
+
return ownership;
|
|
488
|
+
}
|
|
489
|
+
const pending = await engine.listPendingAuths(sessionId);
|
|
490
|
+
return jsonResponse({ ok: true, sessionId, pending });
|
|
491
|
+
},
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
const resolveAuth: ServerRoute = {
|
|
495
|
+
method: "POST",
|
|
496
|
+
pattern: `${base}/v1/session/:sessionId/auths/:callId`,
|
|
497
|
+
auth,
|
|
498
|
+
handler: async (request, ctx) => {
|
|
499
|
+
const sessionId = ctx.params.sessionId ?? "";
|
|
500
|
+
const callId = ctx.params.callId ?? "";
|
|
501
|
+
const record = await engine.getOrRestoreSession(sessionId);
|
|
502
|
+
if (record === undefined) {
|
|
503
|
+
return errorResponse(
|
|
504
|
+
404,
|
|
505
|
+
"session_not_found",
|
|
506
|
+
`Unknown session ${sessionId}.`
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
const bridgeSlackAuth =
|
|
510
|
+
relaxSessionApprovalAuth === true &&
|
|
511
|
+
record.auth?.authenticator === "slack" &&
|
|
512
|
+
(relaxSessionReadAuthBeyondLoopback === true ||
|
|
513
|
+
(typeof ctx.requestIp === "string" &&
|
|
514
|
+
isLoopbackAddress(ctx.requestIp)));
|
|
515
|
+
const ownership = requireSessionOwner(record, ctx.auth, {
|
|
516
|
+
relax: bridgeSlackAuth,
|
|
517
|
+
beyondLoopback: relaxSessionReadAuthBeyondLoopback,
|
|
518
|
+
requestIp: ctx.requestIp,
|
|
519
|
+
});
|
|
520
|
+
if (ownership !== undefined) {
|
|
521
|
+
return ownership;
|
|
522
|
+
}
|
|
523
|
+
const body = await readJsonObject(request);
|
|
524
|
+
const decision = body?.decision;
|
|
525
|
+
if (decision !== "approve" && decision !== "deny") {
|
|
526
|
+
return errorResponse(
|
|
527
|
+
400,
|
|
528
|
+
"invalid_request",
|
|
529
|
+
'Expected a JSON body with `decision`: "approve" | "deny".'
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
try {
|
|
533
|
+
const pending = await engine.resolveAuth(
|
|
534
|
+
sessionId,
|
|
535
|
+
callId,
|
|
536
|
+
decision,
|
|
537
|
+
ctx.auth,
|
|
538
|
+
bridgeSlackAuth ? { asSessionOwner: true } : undefined
|
|
539
|
+
);
|
|
540
|
+
return jsonResponse({
|
|
541
|
+
ok: true,
|
|
542
|
+
sessionId,
|
|
543
|
+
callId,
|
|
544
|
+
decision,
|
|
545
|
+
connection: pending.connection,
|
|
546
|
+
});
|
|
547
|
+
} catch (error) {
|
|
548
|
+
if (error instanceof AuthNotFoundError) {
|
|
549
|
+
return errorResponse(404, "auth_not_found", error.message);
|
|
550
|
+
}
|
|
551
|
+
if (error instanceof AuthInterruptedError) {
|
|
552
|
+
return errorResponse(409, "auth_interrupted", error.message);
|
|
553
|
+
}
|
|
554
|
+
if (error instanceof SessionAuthError) {
|
|
555
|
+
return errorResponse(403, "forbidden", error.message);
|
|
556
|
+
}
|
|
557
|
+
throw error;
|
|
558
|
+
}
|
|
559
|
+
},
|
|
560
|
+
};
|
|
561
|
+
|
|
457
562
|
return [
|
|
458
563
|
createSession,
|
|
459
564
|
followUp,
|
|
@@ -463,6 +568,8 @@ export function buildHttpChannelRoutes(args: {
|
|
|
463
568
|
cost,
|
|
464
569
|
listApprovals,
|
|
465
570
|
resolveApproval,
|
|
571
|
+
listAuths,
|
|
572
|
+
resolveAuth,
|
|
466
573
|
];
|
|
467
574
|
}
|
|
468
575
|
|