@cursor/bdk 0.2.1
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 +341 -0
- package/LICENSE.md +3 -0
- package/README.md +1190 -0
- package/dist/artifacts.d.ts +34 -0
- package/dist/artifacts.d.ts.map +1 -0
- package/dist/artifacts.js +51 -0
- package/dist/bin/agent-sdk.js +2 -0
- package/dist/bin/agent-serve.d.ts +19 -0
- package/dist/bin/agent-serve.d.ts.map +1 -0
- package/dist/bin/agent-serve.js +1578 -0
- package/dist/channels/bitbucket/api.d.ts +217 -0
- package/dist/channels/bitbucket/api.d.ts.map +1 -0
- package/dist/channels/bitbucket/api.js +664 -0
- package/dist/channels/bitbucket/binding.d.ts +75 -0
- package/dist/channels/bitbucket/binding.d.ts.map +1 -0
- package/dist/channels/bitbucket/binding.js +277 -0
- package/dist/channels/bitbucket/bitbucket-channel.d.ts +33 -0
- package/dist/channels/bitbucket/bitbucket-channel.d.ts.map +1 -0
- package/dist/channels/bitbucket/bitbucket-channel.js +120 -0
- package/dist/channels/bitbucket/bitbucket-webhook.d.ts +61 -0
- package/dist/channels/bitbucket/bitbucket-webhook.d.ts.map +1 -0
- package/dist/channels/bitbucket/bitbucket-webhook.js +384 -0
- package/dist/channels/bitbucket/checks.d.ts +67 -0
- package/dist/channels/bitbucket/checks.d.ts.map +1 -0
- package/dist/channels/bitbucket/checks.js +102 -0
- package/dist/channels/bitbucket/cursor-account.d.ts +37 -0
- package/dist/channels/bitbucket/cursor-account.d.ts.map +1 -0
- package/dist/channels/bitbucket/cursor-account.js +65 -0
- package/dist/channels/bitbucket/defaults.d.ts +26 -0
- package/dist/channels/bitbucket/defaults.d.ts.map +1 -0
- package/dist/channels/bitbucket/defaults.js +67 -0
- package/dist/channels/bitbucket/events.d.ts +22 -0
- package/dist/channels/bitbucket/events.d.ts.map +1 -0
- package/dist/channels/bitbucket/events.js +60 -0
- package/dist/channels/bitbucket/index.d.ts +34 -0
- package/dist/channels/bitbucket/index.d.ts.map +1 -0
- package/dist/channels/bitbucket/index.js +33 -0
- package/dist/channels/bitbucket/replay.d.ts +63 -0
- package/dist/channels/bitbucket/replay.d.ts.map +1 -0
- package/dist/channels/bitbucket/replay.js +302 -0
- package/dist/channels/bitbucket/state.d.ts +35 -0
- package/dist/channels/bitbucket/state.d.ts.map +1 -0
- package/dist/channels/bitbucket/state.js +21 -0
- package/dist/channels/bitbucket/types.d.ts +227 -0
- package/dist/channels/bitbucket/types.d.ts.map +1 -0
- package/dist/channels/bitbucket/types.js +1 -0
- package/dist/channels/change-monitors/change-monitors-channel.d.ts +5 -0
- package/dist/channels/change-monitors/change-monitors-channel.d.ts.map +1 -0
- package/dist/channels/change-monitors/change-monitors-channel.js +45 -0
- package/dist/channels/change-monitors/index.d.ts +4 -0
- package/dist/channels/change-monitors/index.d.ts.map +1 -0
- package/dist/channels/change-monitors/index.js +2 -0
- package/dist/channels/change-monitors/types.d.ts +42 -0
- package/dist/channels/change-monitors/types.d.ts.map +1 -0
- package/dist/channels/change-monitors/types.js +1 -0
- package/dist/channels/checks.d.ts +121 -0
- package/dist/channels/checks.d.ts.map +1 -0
- package/dist/channels/checks.js +86 -0
- package/dist/channels/deployments/deployments-channel.d.ts +95 -0
- package/dist/channels/deployments/deployments-channel.d.ts.map +1 -0
- package/dist/channels/deployments/deployments-channel.js +196 -0
- package/dist/channels/deployments/index.d.ts +25 -0
- package/dist/channels/deployments/index.d.ts.map +1 -0
- package/dist/channels/deployments/index.js +24 -0
- package/dist/channels/deployments/types.d.ts +83 -0
- package/dist/channels/deployments/types.d.ts.map +1 -0
- package/dist/channels/deployments/types.js +7 -0
- package/dist/channels/github/api.d.ts +133 -0
- package/dist/channels/github/api.d.ts.map +1 -0
- package/dist/channels/github/api.js +320 -0
- package/dist/channels/github/auth.d.ts +67 -0
- package/dist/channels/github/auth.d.ts.map +1 -0
- package/dist/channels/github/auth.js +233 -0
- package/dist/channels/github/binding.d.ts +86 -0
- package/dist/channels/github/binding.d.ts.map +1 -0
- package/dist/channels/github/binding.js +114 -0
- package/dist/channels/github/checks.d.ts +64 -0
- package/dist/channels/github/checks.d.ts.map +1 -0
- package/dist/channels/github/checks.js +98 -0
- package/dist/channels/github/cursor-account.d.ts +102 -0
- package/dist/channels/github/cursor-account.d.ts.map +1 -0
- package/dist/channels/github/cursor-account.js +194 -0
- package/dist/channels/github/defaults.d.ts +23 -0
- package/dist/channels/github/defaults.d.ts.map +1 -0
- package/dist/channels/github/defaults.js +157 -0
- package/dist/channels/github/events.d.ts +18 -0
- package/dist/channels/github/events.d.ts.map +1 -0
- package/dist/channels/github/events.js +56 -0
- package/dist/channels/github/forward.d.ts +139 -0
- package/dist/channels/github/forward.d.ts.map +1 -0
- package/dist/channels/github/forward.js +267 -0
- package/dist/channels/github/github-channel.d.ts +146 -0
- package/dist/channels/github/github-channel.d.ts.map +1 -0
- package/dist/channels/github/github-channel.js +1804 -0
- package/dist/channels/github/index.d.ts +54 -0
- package/dist/channels/github/index.d.ts.map +1 -0
- package/dist/channels/github/index.js +53 -0
- package/dist/channels/github/progress.d.ts +84 -0
- package/dist/channels/github/progress.d.ts.map +1 -0
- package/dist/channels/github/progress.js +236 -0
- package/dist/channels/github/replay.d.ts +115 -0
- package/dist/channels/github/replay.d.ts.map +1 -0
- package/dist/channels/github/replay.js +278 -0
- package/dist/channels/github/state.d.ts +47 -0
- package/dist/channels/github/state.d.ts.map +1 -0
- package/dist/channels/github/state.js +53 -0
- package/dist/channels/github/types.d.ts +491 -0
- package/dist/channels/github/types.d.ts.map +1 -0
- package/dist/channels/github/types.js +1 -0
- package/dist/channels/gitlab/api.d.ts +118 -0
- package/dist/channels/gitlab/api.d.ts.map +1 -0
- package/dist/channels/gitlab/api.js +234 -0
- package/dist/channels/gitlab/binding.d.ts +63 -0
- package/dist/channels/gitlab/binding.d.ts.map +1 -0
- package/dist/channels/gitlab/binding.js +147 -0
- package/dist/channels/gitlab/checks.d.ts +110 -0
- package/dist/channels/gitlab/checks.d.ts.map +1 -0
- package/dist/channels/gitlab/checks.js +122 -0
- package/dist/channels/gitlab/cursor-account.d.ts +36 -0
- package/dist/channels/gitlab/cursor-account.d.ts.map +1 -0
- package/dist/channels/gitlab/cursor-account.js +64 -0
- package/dist/channels/gitlab/defaults.d.ts +25 -0
- package/dist/channels/gitlab/defaults.d.ts.map +1 -0
- package/dist/channels/gitlab/defaults.js +55 -0
- package/dist/channels/gitlab/events.d.ts +12 -0
- package/dist/channels/gitlab/events.d.ts.map +1 -0
- package/dist/channels/gitlab/events.js +26 -0
- package/dist/channels/gitlab/gitlab-channel.d.ts +33 -0
- package/dist/channels/gitlab/gitlab-channel.d.ts.map +1 -0
- package/dist/channels/gitlab/gitlab-channel.js +120 -0
- package/dist/channels/gitlab/gitlab-webhook.d.ts +45 -0
- package/dist/channels/gitlab/gitlab-webhook.d.ts.map +1 -0
- package/dist/channels/gitlab/gitlab-webhook.js +388 -0
- package/dist/channels/gitlab/index.d.ts +31 -0
- package/dist/channels/gitlab/index.d.ts.map +1 -0
- package/dist/channels/gitlab/index.js +30 -0
- package/dist/channels/gitlab/replay.d.ts +105 -0
- package/dist/channels/gitlab/replay.d.ts.map +1 -0
- package/dist/channels/gitlab/replay.js +199 -0
- package/dist/channels/gitlab/state.d.ts +33 -0
- package/dist/channels/gitlab/state.d.ts.map +1 -0
- package/dist/channels/gitlab/state.js +23 -0
- package/dist/channels/gitlab/types.d.ts +211 -0
- package/dist/channels/gitlab/types.d.ts.map +1 -0
- package/dist/channels/gitlab/types.js +1 -0
- package/dist/channels/issues/index.d.ts +4 -0
- package/dist/channels/issues/index.d.ts.map +1 -0
- package/dist/channels/issues/index.js +2 -0
- package/dist/channels/issues/issues-channel.d.ts +5 -0
- package/dist/channels/issues/issues-channel.d.ts.map +1 -0
- package/dist/channels/issues/issues-channel.js +35 -0
- package/dist/channels/issues/types.d.ts +33 -0
- package/dist/channels/issues/types.d.ts.map +1 -0
- package/dist/channels/issues/types.js +1 -0
- package/dist/channels/origin/api.d.ts +157 -0
- package/dist/channels/origin/api.d.ts.map +1 -0
- package/dist/channels/origin/api.js +379 -0
- package/dist/channels/origin/binding.d.ts +57 -0
- package/dist/channels/origin/binding.d.ts.map +1 -0
- package/dist/channels/origin/binding.js +57 -0
- package/dist/channels/origin/checks.d.ts +119 -0
- package/dist/channels/origin/checks.d.ts.map +1 -0
- package/dist/channels/origin/checks.js +145 -0
- package/dist/channels/origin/cursor-account.d.ts +24 -0
- package/dist/channels/origin/cursor-account.d.ts.map +1 -0
- package/dist/channels/origin/cursor-account.js +35 -0
- package/dist/channels/origin/defaults.d.ts +43 -0
- package/dist/channels/origin/defaults.d.ts.map +1 -0
- package/dist/channels/origin/defaults.js +109 -0
- package/dist/channels/origin/events.d.ts +10 -0
- package/dist/channels/origin/events.d.ts.map +1 -0
- package/dist/channels/origin/events.js +25 -0
- package/dist/channels/origin/index.d.ts +35 -0
- package/dist/channels/origin/index.d.ts.map +1 -0
- package/dist/channels/origin/index.js +34 -0
- package/dist/channels/origin/origin-channel.d.ts +72 -0
- package/dist/channels/origin/origin-channel.d.ts.map +1 -0
- package/dist/channels/origin/origin-channel.js +634 -0
- package/dist/channels/origin/origin-webhook.d.ts +69 -0
- package/dist/channels/origin/origin-webhook.d.ts.map +1 -0
- package/dist/channels/origin/origin-webhook.js +231 -0
- 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 +58 -0
- package/dist/channels/origin/state.d.ts +32 -0
- package/dist/channels/origin/state.d.ts.map +1 -0
- package/dist/channels/origin/state.js +23 -0
- package/dist/channels/origin/types.d.ts +257 -0
- package/dist/channels/origin/types.d.ts.map +1 -0
- package/dist/channels/origin/types.js +1 -0
- package/dist/channels/slack/agentic-delivery.d.ts +23 -0
- package/dist/channels/slack/agentic-delivery.d.ts.map +1 -0
- package/dist/channels/slack/agentic-delivery.js +566 -0
- package/dist/channels/slack/agentic-posted.d.ts +30 -0
- package/dist/channels/slack/agentic-posted.d.ts.map +1 -0
- package/dist/channels/slack/agentic-posted.js +102 -0
- package/dist/channels/slack/agentic-signals.d.ts +48 -0
- package/dist/channels/slack/agentic-signals.d.ts.map +1 -0
- package/dist/channels/slack/agentic-signals.js +199 -0
- package/dist/channels/slack/api.d.ts +171 -0
- package/dist/channels/slack/api.d.ts.map +1 -0
- package/dist/channels/slack/api.js +613 -0
- package/dist/channels/slack/approvals.d.ts +48 -0
- package/dist/channels/slack/approvals.d.ts.map +1 -0
- package/dist/channels/slack/approvals.js +233 -0
- package/dist/channels/slack/assistant.d.ts +23 -0
- package/dist/channels/slack/assistant.d.ts.map +1 -0
- package/dist/channels/slack/assistant.js +67 -0
- package/dist/channels/slack/attachments.d.ts +82 -0
- package/dist/channels/slack/attachments.d.ts.map +1 -0
- package/dist/channels/slack/attachments.js +503 -0
- package/dist/channels/slack/auth.d.ts +46 -0
- package/dist/channels/slack/auth.d.ts.map +1 -0
- package/dist/channels/slack/auth.js +95 -0
- package/dist/channels/slack/blocks.d.ts +61 -0
- package/dist/channels/slack/blocks.d.ts.map +1 -0
- package/dist/channels/slack/blocks.js +90 -0
- package/dist/channels/slack/bot-mentions.d.ts +108 -0
- package/dist/channels/slack/bot-mentions.d.ts.map +1 -0
- package/dist/channels/slack/bot-mentions.js +257 -0
- package/dist/channels/slack/channel-watch.d.ts +76 -0
- package/dist/channels/slack/channel-watch.d.ts.map +1 -0
- package/dist/channels/slack/channel-watch.js +264 -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 +228 -0
- package/dist/channels/slack/code-fences.d.ts +17 -0
- package/dist/channels/slack/code-fences.d.ts.map +1 -0
- package/dist/channels/slack/code-fences.js +166 -0
- package/dist/channels/slack/constants.d.ts +25 -0
- package/dist/channels/slack/constants.d.ts.map +1 -0
- package/dist/channels/slack/constants.js +35 -0
- package/dist/channels/slack/credentials.d.ts +35 -0
- package/dist/channels/slack/credentials.d.ts.map +1 -0
- package/dist/channels/slack/credentials.js +88 -0
- package/dist/channels/slack/defaults.d.ts +50 -0
- package/dist/channels/slack/defaults.d.ts.map +1 -0
- package/dist/channels/slack/defaults.js +630 -0
- package/dist/channels/slack/dispatch.d.ts +141 -0
- package/dist/channels/slack/dispatch.d.ts.map +1 -0
- package/dist/channels/slack/dispatch.js +730 -0
- package/dist/channels/slack/doctor.d.ts +40 -0
- package/dist/channels/slack/doctor.d.ts.map +1 -0
- package/dist/channels/slack/doctor.js +291 -0
- package/dist/channels/slack/eval-directive.d.ts +59 -0
- package/dist/channels/slack/eval-directive.d.ts.map +1 -0
- package/dist/channels/slack/eval-directive.js +269 -0
- package/dist/channels/slack/event-id-dedupe.d.ts +10 -0
- package/dist/channels/slack/event-id-dedupe.d.ts.map +1 -0
- package/dist/channels/slack/event-id-dedupe.js +23 -0
- package/dist/channels/slack/external-policy.d.ts +28 -0
- package/dist/channels/slack/external-policy.d.ts.map +1 -0
- package/dist/channels/slack/external-policy.js +64 -0
- package/dist/channels/slack/footer.d.ts +33 -0
- package/dist/channels/slack/footer.d.ts.map +1 -0
- package/dist/channels/slack/footer.js +85 -0
- package/dist/channels/slack/inbound.d.ts +98 -0
- package/dist/channels/slack/inbound.d.ts.map +1 -0
- package/dist/channels/slack/inbound.js +314 -0
- package/dist/channels/slack/index.d.ts +53 -0
- package/dist/channels/slack/index.d.ts.map +1 -0
- package/dist/channels/slack/index.js +52 -0
- package/dist/channels/slack/init.d.ts +45 -0
- package/dist/channels/slack/init.d.ts.map +1 -0
- package/dist/channels/slack/init.js +126 -0
- package/dist/channels/slack/interactive.d.ts +65 -0
- package/dist/channels/slack/interactive.d.ts.map +1 -0
- package/dist/channels/slack/interactive.js +310 -0
- package/dist/channels/slack/links.d.ts +10 -0
- package/dist/channels/slack/links.d.ts.map +1 -0
- package/dist/channels/slack/links.js +19 -0
- package/dist/channels/slack/live-delivery.d.ts +77 -0
- package/dist/channels/slack/live-delivery.d.ts.map +1 -0
- package/dist/channels/slack/live-delivery.js +399 -0
- package/dist/channels/slack/log.d.ts +25 -0
- package/dist/channels/slack/log.d.ts.map +1 -0
- package/dist/channels/slack/log.js +44 -0
- package/dist/channels/slack/manifest.d.ts +80 -0
- package/dist/channels/slack/manifest.d.ts.map +1 -0
- package/dist/channels/slack/manifest.js +159 -0
- package/dist/channels/slack/message-body.d.ts +13 -0
- package/dist/channels/slack/message-body.d.ts.map +1 -0
- package/dist/channels/slack/message-body.js +74 -0
- package/dist/channels/slack/nudge-carrier.d.ts +21 -0
- package/dist/channels/slack/nudge-carrier.d.ts.map +1 -0
- package/dist/channels/slack/nudge-carrier.js +213 -0
- package/dist/channels/slack/nudge-store.d.ts +77 -0
- package/dist/channels/slack/nudge-store.d.ts.map +1 -0
- package/dist/channels/slack/nudge-store.js +315 -0
- package/dist/channels/slack/paint-start.d.ts +22 -0
- package/dist/channels/slack/paint-start.d.ts.map +1 -0
- package/dist/channels/slack/paint-start.js +91 -0
- package/dist/channels/slack/placeholder.d.ts +62 -0
- package/dist/channels/slack/placeholder.d.ts.map +1 -0
- package/dist/channels/slack/placeholder.js +266 -0
- package/dist/channels/slack/pr-ref.d.ts +24 -0
- package/dist/channels/slack/pr-ref.d.ts.map +1 -0
- package/dist/channels/slack/pr-ref.js +55 -0
- 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 +122 -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 +103 -0
- package/dist/channels/slack/reasoning-chip.d.ts +8 -0
- package/dist/channels/slack/reasoning-chip.d.ts.map +1 -0
- package/dist/channels/slack/reasoning-chip.js +30 -0
- package/dist/channels/slack/recording-sink.d.ts +29 -0
- package/dist/channels/slack/recording-sink.d.ts.map +1 -0
- package/dist/channels/slack/recording-sink.js +140 -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 +47 -0
- package/dist/channels/slack/reply-options.d.ts.map +1 -0
- package/dist/channels/slack/reply-options.js +193 -0
- package/dist/channels/slack/setup.d.ts +33 -0
- package/dist/channels/slack/setup.d.ts.map +1 -0
- package/dist/channels/slack/setup.js +101 -0
- package/dist/channels/slack/slack-channel.d.ts +43 -0
- package/dist/channels/slack/slack-channel.d.ts.map +1 -0
- package/dist/channels/slack/slack-channel.js +534 -0
- package/dist/channels/slack/socket-mode.d.ts +26 -0
- package/dist/channels/slack/socket-mode.d.ts.map +1 -0
- package/dist/channels/slack/socket-mode.js +114 -0
- package/dist/channels/slack/stop.d.ts +14 -0
- package/dist/channels/slack/stop.d.ts.map +1 -0
- package/dist/channels/slack/stop.js +22 -0
- package/dist/channels/slack/stream.d.ts +38 -0
- package/dist/channels/slack/stream.d.ts.map +1 -0
- package/dist/channels/slack/stream.js +30 -0
- package/dist/channels/slack/thread-context.d.ts +79 -0
- package/dist/channels/slack/thread-context.d.ts.map +1 -0
- package/dist/channels/slack/thread-context.js +396 -0
- package/dist/channels/slack/tool-approval-events.d.ts +38 -0
- package/dist/channels/slack/tool-approval-events.d.ts.map +1 -0
- package/dist/channels/slack/tool-approval-events.js +224 -0
- 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 +172 -0
- package/dist/channels/slack/tool-chip.d.ts +9 -0
- package/dist/channels/slack/tool-chip.d.ts.map +1 -0
- package/dist/channels/slack/tool-chip.js +23 -0
- package/dist/channels/slack/types.d.ts +751 -0
- package/dist/channels/slack/types.d.ts.map +1 -0
- package/dist/channels/slack/types.js +4 -0
- package/dist/channels/slack/upload.d.ts +13 -0
- package/dist/channels/slack/upload.d.ts.map +1 -0
- package/dist/channels/slack/upload.js +135 -0
- package/dist/channels.d.ts +175 -0
- package/dist/channels.d.ts.map +1 -0
- package/dist/channels.js +417 -0
- package/dist/connections.d.ts +131 -0
- package/dist/connections.d.ts.map +1 -0
- package/dist/connections.js +131 -0
- package/dist/dashboard-session-url.d.ts +15 -0
- package/dist/dashboard-session-url.d.ts.map +1 -0
- package/dist/dashboard-session-url.js +25 -0
- package/dist/docs/404.html +24 -0
- package/dist/docs/architecture.svg +59 -0
- package/dist/docs/assets/app.JVQvbRO-.js +309 -0
- package/dist/docs/assets/building-with-agents.md.-fcjCmJU.js +9 -0
- package/dist/docs/assets/building-with-agents.md.-fcjCmJU.lean.js +1 -0
- package/dist/docs/assets/chunks/@localSearchIndexroot.Caw9W1pM.js +1 -0
- package/dist/docs/assets/chunks/VPLocalSearchBox.BkhY63uW.js +9 -0
- package/dist/docs/assets/chunks/arc.Cc1unYd3.js +1 -0
- package/dist/docs/assets/chunks/architectureDiagram-Q4EWVU46.CAY_TPVz.js +36 -0
- package/dist/docs/assets/chunks/baseUniq.my2VD3Xm.js +1 -0
- package/dist/docs/assets/chunks/blockDiagram-DXYQGD6D.CQXAOmqd.js +132 -0
- package/dist/docs/assets/chunks/c4Diagram-AHTNJAMY.D0AsQFSX.js +10 -0
- package/dist/docs/assets/chunks/channel.Sapo9HJZ.js +1 -0
- package/dist/docs/assets/chunks/chunk-4BX2VUAB.DZjhgNls.js +1 -0
- package/dist/docs/assets/chunks/chunk-4TB4RGXK.qdcp_qbo.js +206 -0
- package/dist/docs/assets/chunks/chunk-55IACEB6.BHXoZhJ-.js +1 -0
- package/dist/docs/assets/chunks/chunk-EDXVE4YY.Cds_Vwj3.js +1 -0
- package/dist/docs/assets/chunks/chunk-FMBD7UC4.CSl0HknT.js +15 -0
- package/dist/docs/assets/chunks/chunk-OYMX7WX6.DJMvAt5N.js +231 -0
- package/dist/docs/assets/chunks/chunk-QZHKN3VN.BYQzBK9D.js +1 -0
- package/dist/docs/assets/chunks/chunk-YZCP3GAM.BAvw7N4o.js +1 -0
- package/dist/docs/assets/chunks/classDiagram-6PBFFD2Q.NPiy3Hz7.js +1 -0
- package/dist/docs/assets/chunks/classDiagram-v2-HSJHXN6E.NPiy3Hz7.js +1 -0
- package/dist/docs/assets/chunks/clone.zl2qolPE.js +1 -0
- package/dist/docs/assets/chunks/cose-bilkent-S5V4N54A.Buf3seya.js +1 -0
- package/dist/docs/assets/chunks/cytoscape.esm.BQk4lpUV.js +331 -0
- package/dist/docs/assets/chunks/dagre-KV5264BT.DMGGa3uA.js +4 -0
- package/dist/docs/assets/chunks/defaultLocale.C4B-KCzX.js +1 -0
- package/dist/docs/assets/chunks/diagram-5BDNPKRD.DDjnwRmY.js +10 -0
- package/dist/docs/assets/chunks/diagram-G4DWMVQ6.RwyrIr8Q.js +24 -0
- package/dist/docs/assets/chunks/diagram-MMDJMWI5.DRvQV16Z.js +43 -0
- package/dist/docs/assets/chunks/diagram-TYMM5635.Dw8W9BTk.js +24 -0
- package/dist/docs/assets/chunks/erDiagram-SMLLAGMA.DQT1Hnwf.js +85 -0
- package/dist/docs/assets/chunks/flowDiagram-DWJPFMVM.6_Aky-ut.js +162 -0
- package/dist/docs/assets/chunks/framework.BNw1pucY.js +19 -0
- package/dist/docs/assets/chunks/ganttDiagram-T4ZO3ILL.DHIwqnqN.js +292 -0
- package/dist/docs/assets/chunks/gitGraphDiagram-UUTBAWPF.CGd_eJbn.js +106 -0
- package/dist/docs/assets/chunks/graph.ChMEylkh.js +1 -0
- package/dist/docs/assets/chunks/infoDiagram-42DDH7IO.Cd4gMyAz.js +2 -0
- package/dist/docs/assets/chunks/init.Gi6I4Gst.js +1 -0
- package/dist/docs/assets/chunks/ishikawaDiagram-UXIWVN3A.D1uw7MXq.js +70 -0
- package/dist/docs/assets/chunks/journeyDiagram-VCZTEJTY.AWLXtelc.js +139 -0
- package/dist/docs/assets/chunks/kanban-definition-6JOO6SKY.CagPhAn5.js +89 -0
- package/dist/docs/assets/chunks/katex.DkKDou_j.js +257 -0
- package/dist/docs/assets/chunks/layout.ClTBBkE_.js +1 -0
- package/dist/docs/assets/chunks/linear.SwS2rOLA.js +1 -0
- package/dist/docs/assets/chunks/min.By_x-Btb.js +1 -0
- package/dist/docs/assets/chunks/mindmap-definition-QFDTVHPH.By_RAMaP.js +96 -0
- package/dist/docs/assets/chunks/ordinal.DBvzRdQf.js +1 -0
- package/dist/docs/assets/chunks/pieDiagram-DEJITSTG.DfAl9sy4.js +30 -0
- package/dist/docs/assets/chunks/quadrantDiagram-34T5L4WZ.CNuqFRBj.js +7 -0
- package/dist/docs/assets/chunks/requirementDiagram-MS252O5E.DJ5CmTCl.js +84 -0
- package/dist/docs/assets/chunks/sankeyDiagram-XADWPNL6.D-T_AKJj.js +10 -0
- package/dist/docs/assets/chunks/sequenceDiagram-FGHM5R23.BRn9WcrR.js +157 -0
- package/dist/docs/assets/chunks/stateDiagram-FHFEXIEX.F12urmm7.js +1 -0
- package/dist/docs/assets/chunks/stateDiagram-v2-QKLJ7IA2._auCa3fR.js +1 -0
- package/dist/docs/assets/chunks/theme.zK3OJa_C.js +2 -0
- package/dist/docs/assets/chunks/timeline-definition-GMOUNBTQ.B7GiQBeC.js +120 -0
- package/dist/docs/assets/chunks/vennDiagram-DHZGUBPP.D0niwuY_.js +34 -0
- package/dist/docs/assets/chunks/virtual_mermaid-config.DDnGl6nM.js +1 -0
- package/dist/docs/assets/chunks/wardley-RL74JXVD.1zhHNRsx.js +162 -0
- package/dist/docs/assets/chunks/wardleyDiagram-NUSXRM2D.DKaqpT7K.js +20 -0
- package/dist/docs/assets/chunks/xychartDiagram-5P7HB3ND.e8BPicch.js +7 -0
- package/dist/docs/assets/deployment.md.nGo1yIsa.js +32 -0
- package/dist/docs/assets/deployment.md.nGo1yIsa.lean.js +1 -0
- package/dist/docs/assets/evals.md.CCAtNcuL.js +72 -0
- package/dist/docs/assets/evals.md.CCAtNcuL.lean.js +1 -0
- package/dist/docs/assets/guides_agent-to-agent.md.CGkicXeB.js +41 -0
- package/dist/docs/assets/guides_agent-to-agent.md.CGkicXeB.lean.js +1 -0
- package/dist/docs/assets/guides_bitbucket.md.Cb3LPKt3.js +145 -0
- package/dist/docs/assets/guides_bitbucket.md.Cb3LPKt3.lean.js +1 -0
- package/dist/docs/assets/guides_cloud-agents.md.BuEkDCwo.js +10 -0
- package/dist/docs/assets/guides_cloud-agents.md.BuEkDCwo.lean.js +1 -0
- package/dist/docs/assets/guides_convert-automation.md.Ci-Y2iD5.js +43 -0
- package/dist/docs/assets/guides_convert-automation.md.Ci-Y2iD5.lean.js +1 -0
- package/dist/docs/assets/guides_github.md.C2_DSWlE.js +156 -0
- package/dist/docs/assets/guides_github.md.C2_DSWlE.lean.js +1 -0
- package/dist/docs/assets/guides_gitlab.md.BM27P43u.js +153 -0
- package/dist/docs/assets/guides_gitlab.md.BM27P43u.lean.js +1 -0
- package/dist/docs/assets/guides_grokbot-agents.md.yxVbSlKA.js +16 -0
- package/dist/docs/assets/guides_grokbot-agents.md.yxVbSlKA.lean.js +1 -0
- package/dist/docs/assets/guides_hooks.md.BWoiNaMq.js +50 -0
- package/dist/docs/assets/guides_hooks.md.BWoiNaMq.lean.js +1 -0
- package/dist/docs/assets/guides_improve.md.COtL-6ft.js +22 -0
- package/dist/docs/assets/guides_improve.md.COtL-6ft.lean.js +1 -0
- package/dist/docs/assets/guides_jev.md.BNDm14Gy.js +151 -0
- package/dist/docs/assets/guides_jev.md.BNDm14Gy.lean.js +1 -0
- package/dist/docs/assets/guides_mcp-oauth.md.Cu6mhZ_3.js +50 -0
- package/dist/docs/assets/guides_mcp-oauth.md.Cu6mhZ_3.lean.js +1 -0
- package/dist/docs/assets/guides_opentelemetry.md.DLlIB5rv.js +35 -0
- package/dist/docs/assets/guides_opentelemetry.md.DLlIB5rv.lean.js +1 -0
- package/dist/docs/assets/guides_slack.md.ZbHQETOT.js +70 -0
- package/dist/docs/assets/guides_slack.md.ZbHQETOT.lean.js +1 -0
- package/dist/docs/assets/guides_webhooks.md.D0furjw0.js +92 -0
- package/dist/docs/assets/guides_webhooks.md.D0furjw0.lean.js +1 -0
- package/dist/docs/assets/hillclimbing.md.Biy0m-Qb.js +7 -0
- package/dist/docs/assets/hillclimbing.md.Biy0m-Qb.lean.js +1 -0
- package/dist/docs/assets/index.md.CVc_oOA3.js +27 -0
- package/dist/docs/assets/index.md.CVc_oOA3.lean.js +1 -0
- package/dist/docs/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
- package/dist/docs/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
- package/dist/docs/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
- package/dist/docs/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
- package/dist/docs/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
- package/dist/docs/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
- package/dist/docs/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
- package/dist/docs/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
- package/dist/docs/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
- package/dist/docs/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
- package/dist/docs/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
- package/dist/docs/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
- package/dist/docs/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
- package/dist/docs/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
- package/dist/docs/assets/quickstart.md.wG4shE1f.js +168 -0
- package/dist/docs/assets/quickstart.md.wG4shE1f.lean.js +1 -0
- package/dist/docs/assets/reference_agent-config.md.DzGI777n.js +32 -0
- package/dist/docs/assets/reference_agent-config.md.DzGI777n.lean.js +1 -0
- package/dist/docs/assets/reference_artifacts.md.IFFC5Zln.js +18 -0
- package/dist/docs/assets/reference_artifacts.md.IFFC5Zln.lean.js +1 -0
- package/dist/docs/assets/reference_channels.md.mbNtKCiR.js +23 -0
- package/dist/docs/assets/reference_channels.md.mbNtKCiR.lean.js +1 -0
- package/dist/docs/assets/reference_cli.md.BK2QUH67.js +97 -0
- package/dist/docs/assets/reference_cli.md.BK2QUH67.lean.js +1 -0
- package/dist/docs/assets/reference_connections.md._XpKiOaF.js +63 -0
- package/dist/docs/assets/reference_connections.md._XpKiOaF.lean.js +1 -0
- package/dist/docs/assets/reference_evals.md.DqNyHMA6.js +56 -0
- package/dist/docs/assets/reference_evals.md.DqNyHMA6.lean.js +1 -0
- package/dist/docs/assets/reference_extensions.md.CPPrkWlJ.js +58 -0
- package/dist/docs/assets/reference_extensions.md.CPPrkWlJ.lean.js +1 -0
- package/dist/docs/assets/reference_hooks.md.Dg3dEugd.js +45 -0
- package/dist/docs/assets/reference_hooks.md.Dg3dEugd.lean.js +1 -0
- package/dist/docs/assets/reference_http-api.md.BZOvhCqY.js +11 -0
- package/dist/docs/assets/reference_http-api.md.BZOvhCqY.lean.js +1 -0
- package/dist/docs/assets/reference_instructions.md.Bdwscs4G.js +14 -0
- package/dist/docs/assets/reference_instructions.md.Bdwscs4G.lean.js +1 -0
- package/dist/docs/assets/reference_playground.md.BdQzRJc2.js +1 -0
- package/dist/docs/assets/reference_playground.md.BdQzRJc2.lean.js +1 -0
- package/dist/docs/assets/reference_project-layout.md.CpLQ9j4K.js +25 -0
- package/dist/docs/assets/reference_project-layout.md.CpLQ9j4K.lean.js +1 -0
- package/dist/docs/assets/reference_prompt.md.BQZB4eaV.js +9 -0
- package/dist/docs/assets/reference_prompt.md.BQZB4eaV.lean.js +1 -0
- package/dist/docs/assets/reference_schedules.md.BVU5EFbz.js +47 -0
- package/dist/docs/assets/reference_schedules.md.BVU5EFbz.lean.js +1 -0
- package/dist/docs/assets/reference_sessions.md.BtpTjZmh.js +1 -0
- package/dist/docs/assets/reference_sessions.md.BtpTjZmh.lean.js +1 -0
- package/dist/docs/assets/reference_skills.md.Cy00QMKS.js +15 -0
- package/dist/docs/assets/reference_skills.md.Cy00QMKS.lean.js +1 -0
- package/dist/docs/assets/reference_subagents.md.B8oyeXOI.js +10 -0
- package/dist/docs/assets/reference_subagents.md.B8oyeXOI.lean.js +1 -0
- package/dist/docs/assets/reference_tools.md.hg2Wi5Bq.js +90 -0
- package/dist/docs/assets/reference_tools.md.hg2Wi5Bq.lean.js +1 -0
- package/dist/docs/assets/style.C130MVnH.css +1 -0
- package/dist/docs/assets/templates_agentic-owners.md.BykUiZ8j.js +14 -0
- package/dist/docs/assets/templates_agentic-owners.md.BykUiZ8j.lean.js +1 -0
- package/dist/docs/assets/templates_security-reviewer.md.DXMmKl_o.js +2 -0
- package/dist/docs/assets/templates_security-reviewer.md.DXMmKl_o.lean.js +1 -0
- package/dist/docs/assets/templates_thermo-quality-review.md.Dg1p1MsG.js +3 -0
- package/dist/docs/assets/templates_thermo-quality-review.md.Dg1p1MsG.lean.js +1 -0
- package/dist/docs/assets/templates_thermo-review.md.Dlylkowj.js +3 -0
- package/dist/docs/assets/templates_thermo-review.md.Dlylkowj.lean.js +1 -0
- package/dist/docs/assets/templates_triage.md.CvJyx81N.js +9 -0
- package/dist/docs/assets/templates_triage.md.CvJyx81N.lean.js +1 -0
- package/dist/docs/assets/troubleshooting.md.BzZSu1Gz.js +1 -0
- package/dist/docs/assets/troubleshooting.md.BzZSu1Gz.lean.js +1 -0
- package/dist/docs/building-with-agents.html +68 -0
- package/dist/docs/building-with-agents.md +135 -0
- package/dist/docs/deployment.html +91 -0
- package/dist/docs/deployment.md +211 -0
- package/dist/docs/evals.html +131 -0
- package/dist/docs/evals.md +257 -0
- package/dist/docs/guides/agent-to-agent.html +100 -0
- package/dist/docs/guides/agent-to-agent.md +152 -0
- package/dist/docs/guides/bitbucket.html +204 -0
- package/dist/docs/guides/bitbucket.md +288 -0
- package/dist/docs/guides/cloud-agents.html +69 -0
- package/dist/docs/guides/cloud-agents.md +90 -0
- package/dist/docs/guides/convert-automation.html +102 -0
- package/dist/docs/guides/convert-automation.md +183 -0
- package/dist/docs/guides/github.html +215 -0
- package/dist/docs/guides/github.md +326 -0
- package/dist/docs/guides/gitlab.html +212 -0
- package/dist/docs/guides/gitlab.md +294 -0
- package/dist/docs/guides/grokbot-agents.html +75 -0
- package/dist/docs/guides/grokbot-agents.md +142 -0
- package/dist/docs/guides/hooks.html +109 -0
- package/dist/docs/guides/hooks.md +114 -0
- package/dist/docs/guides/improve.html +81 -0
- package/dist/docs/guides/improve.md +134 -0
- package/dist/docs/guides/jev.html +210 -0
- package/dist/docs/guides/jev.md +294 -0
- package/dist/docs/guides/mcp-oauth.html +109 -0
- package/dist/docs/guides/mcp-oauth.md +162 -0
- package/dist/docs/guides/opentelemetry.html +94 -0
- package/dist/docs/guides/opentelemetry.md +153 -0
- package/dist/docs/guides/slack.html +129 -0
- package/dist/docs/guides/slack.md +273 -0
- package/dist/docs/guides/webhooks.html +151 -0
- package/dist/docs/guides/webhooks.md +236 -0
- package/dist/docs/hashmap.json +1 -0
- package/dist/docs/hillclimbing.html +66 -0
- package/dist/docs/hillclimbing.md +137 -0
- package/dist/docs/index.html +86 -0
- package/dist/docs/index.md +114 -0
- package/dist/docs/llms-full.txt +8439 -0
- package/dist/docs/llms.txt +62 -0
- package/dist/docs/quickstart.html +227 -0
- package/dist/docs/quickstart.md +306 -0
- package/dist/docs/reference/agent-config.html +91 -0
- package/dist/docs/reference/agent-config.md +188 -0
- package/dist/docs/reference/artifacts.html +77 -0
- package/dist/docs/reference/artifacts.md +116 -0
- package/dist/docs/reference/channels.html +82 -0
- package/dist/docs/reference/channels.md +198 -0
- package/dist/docs/reference/cli.html +156 -0
- package/dist/docs/reference/cli.md +904 -0
- package/dist/docs/reference/connections.html +122 -0
- package/dist/docs/reference/connections.md +272 -0
- package/dist/docs/reference/evals.html +115 -0
- package/dist/docs/reference/evals.md +287 -0
- package/dist/docs/reference/extensions.html +117 -0
- package/dist/docs/reference/extensions.md +193 -0
- package/dist/docs/reference/hooks.html +104 -0
- package/dist/docs/reference/hooks.md +190 -0
- package/dist/docs/reference/http-api.html +70 -0
- package/dist/docs/reference/http-api.md +231 -0
- package/dist/docs/reference/instructions.html +73 -0
- package/dist/docs/reference/instructions.md +63 -0
- package/dist/docs/reference/playground.html +60 -0
- package/dist/docs/reference/playground.md +42 -0
- package/dist/docs/reference/project-layout.html +84 -0
- package/dist/docs/reference/project-layout.md +115 -0
- package/dist/docs/reference/prompt.html +68 -0
- package/dist/docs/reference/prompt.md +50 -0
- package/dist/docs/reference/schedules.html +106 -0
- package/dist/docs/reference/schedules.md +170 -0
- package/dist/docs/reference/sessions.html +60 -0
- package/dist/docs/reference/sessions.md +160 -0
- package/dist/docs/reference/skills.html +74 -0
- package/dist/docs/reference/skills.md +75 -0
- package/dist/docs/reference/subagents.html +69 -0
- package/dist/docs/reference/subagents.md +62 -0
- package/dist/docs/reference/tools.html +149 -0
- package/dist/docs/reference/tools.md +324 -0
- package/dist/docs/templates/agentic-owners.html +73 -0
- package/dist/docs/templates/agentic-owners.md +95 -0
- package/dist/docs/templates/security-reviewer.html +61 -0
- package/dist/docs/templates/security-reviewer.md +85 -0
- package/dist/docs/templates/thermo-quality-review.html +62 -0
- package/dist/docs/templates/thermo-quality-review.md +78 -0
- package/dist/docs/templates/thermo-review.html +62 -0
- package/dist/docs/templates/thermo-review.md +77 -0
- package/dist/docs/templates/triage.html +68 -0
- package/dist/docs/templates/triage.md +97 -0
- package/dist/docs/troubleshooting.html +60 -0
- package/dist/docs/troubleshooting.md +143 -0
- package/dist/docs/vp-icons.css +0 -0
- package/dist/evals/assertions.d.ts +199 -0
- package/dist/evals/assertions.d.ts.map +1 -0
- package/dist/evals/assertions.js +459 -0
- package/dist/evals/expect.d.ts +95 -0
- package/dist/evals/expect.d.ts.map +1 -0
- package/dist/evals/expect.js +187 -0
- package/dist/evals/judge.d.ts +133 -0
- package/dist/evals/judge.d.ts.map +1 -0
- package/dist/evals/judge.js +224 -0
- package/dist/evals/loaders.d.ts +51 -0
- package/dist/evals/loaders.d.ts.map +1 -0
- package/dist/evals/loaders.js +124 -0
- package/dist/evals/matchers.d.ts +95 -0
- package/dist/evals/matchers.d.ts.map +1 -0
- package/dist/evals/matchers.js +142 -0
- package/dist/evals/reporters.d.ts +70 -0
- package/dist/evals/reporters.d.ts.map +1 -0
- package/dist/evals/reporters.js +220 -0
- package/dist/evals/results.d.ts +103 -0
- package/dist/evals/results.d.ts.map +1 -0
- package/dist/evals/results.js +27 -0
- package/dist/evals/run-facts.d.ts +77 -0
- package/dist/evals/run-facts.d.ts.map +1 -0
- package/dist/evals/run-facts.js +214 -0
- package/dist/evals.d.ts +446 -0
- package/dist/evals.d.ts.map +1 -0
- package/dist/evals.js +292 -0
- package/dist/extensions/cursor-cloud-agents/extension.d.ts +57 -0
- package/dist/extensions/cursor-cloud-agents/extension.d.ts.map +1 -0
- package/dist/extensions/cursor-cloud-agents/extension.js +66 -0
- package/dist/extensions/cursor-cloud-agents/lib/backend.d.ts +78 -0
- package/dist/extensions/cursor-cloud-agents/lib/backend.d.ts.map +1 -0
- package/dist/extensions/cursor-cloud-agents/lib/backend.js +280 -0
- package/dist/extensions/cursor-cloud-agents/lib/fleet.d.ts +25 -0
- package/dist/extensions/cursor-cloud-agents/lib/fleet.d.ts.map +1 -0
- package/dist/extensions/cursor-cloud-agents/lib/fleet.js +81 -0
- package/dist/extensions/cursor-cloud-agents/lib/inputs.d.ts +5 -0
- package/dist/extensions/cursor-cloud-agents/lib/inputs.d.ts.map +1 -0
- package/dist/extensions/cursor-cloud-agents/lib/inputs.js +4 -0
- package/dist/extensions/cursor-cloud-agents/lib/repos.d.ts +15 -0
- package/dist/extensions/cursor-cloud-agents/lib/repos.d.ts.map +1 -0
- package/dist/extensions/cursor-cloud-agents/lib/repos.js +53 -0
- package/dist/extensions/cursor-cloud-agents/lib/transcript.d.ts +8 -0
- package/dist/extensions/cursor-cloud-agents/lib/transcript.d.ts.map +1 -0
- package/dist/extensions/cursor-cloud-agents/lib/transcript.js +15 -0
- package/dist/extensions/cursor-cloud-agents/skills/handoff.md +37 -0
- package/dist/extensions/cursor-cloud-agents/tools/cancel.d.ts +4 -0
- package/dist/extensions/cursor-cloud-agents/tools/cancel.d.ts.map +1 -0
- package/dist/extensions/cursor-cloud-agents/tools/cancel.js +26 -0
- package/dist/extensions/cursor-cloud-agents/tools/dump.d.ts +4 -0
- package/dist/extensions/cursor-cloud-agents/tools/dump.d.ts.map +1 -0
- package/dist/extensions/cursor-cloud-agents/tools/dump.js +54 -0
- package/dist/extensions/cursor-cloud-agents/tools/get.d.ts +4 -0
- package/dist/extensions/cursor-cloud-agents/tools/get.d.ts.map +1 -0
- package/dist/extensions/cursor-cloud-agents/tools/get.js +25 -0
- package/dist/extensions/cursor-cloud-agents/tools/launch.d.ts +4 -0
- package/dist/extensions/cursor-cloud-agents/tools/launch.d.ts.map +1 -0
- package/dist/extensions/cursor-cloud-agents/tools/launch.js +59 -0
- package/dist/extensions/cursor-cloud-agents/tools/list.d.ts +4 -0
- package/dist/extensions/cursor-cloud-agents/tools/list.d.ts.map +1 -0
- package/dist/extensions/cursor-cloud-agents/tools/list.js +36 -0
- package/dist/extensions/cursor-cloud-agents/tools/reply.d.ts +4 -0
- package/dist/extensions/cursor-cloud-agents/tools/reply.d.ts.map +1 -0
- package/dist/extensions/cursor-cloud-agents/tools/reply.js +35 -0
- package/dist/extensions/improve/extension.d.ts +48 -0
- package/dist/extensions/improve/extension.d.ts.map +1 -0
- package/dist/extensions/improve/extension.js +43 -0
- package/dist/extensions/improve/skills/yourself.d.ts +4 -0
- package/dist/extensions/improve/skills/yourself.d.ts.map +1 -0
- package/dist/extensions/improve/skills/yourself.js +43 -0
- package/dist/extensions/jev/extension.d.ts +43 -0
- package/dist/extensions/jev/extension.d.ts.map +1 -0
- package/dist/extensions/jev/extension.js +47 -0
- package/dist/extensions/jev/lib/evaluate.d.ts +101 -0
- package/dist/extensions/jev/lib/evaluate.d.ts.map +1 -0
- package/dist/extensions/jev/lib/evaluate.js +167 -0
- package/dist/extensions/jev/skills/gated-write.md +25 -0
- package/dist/extensions/jev/skills/questions.md +33 -0
- package/dist/extensions/jev/tools/evaluate.d.ts +4 -0
- package/dist/extensions/jev/tools/evaluate.d.ts.map +1 -0
- package/dist/extensions/jev/tools/evaluate.js +83 -0
- package/dist/extensions/slack-communication/extension.d.ts +28 -0
- package/dist/extensions/slack-communication/extension.d.ts.map +1 -0
- package/dist/extensions/slack-communication/extension.js +32 -0
- package/dist/extensions/slack-communication/skills/slack-voice.d.ts +5 -0
- package/dist/extensions/slack-communication/skills/slack-voice.d.ts.map +1 -0
- package/dist/extensions/slack-communication/skills/slack-voice.js +52 -0
- package/dist/extensions/slack-communication/tools/delete-message.d.ts +4 -0
- package/dist/extensions/slack-communication/tools/delete-message.d.ts.map +1 -0
- package/dist/extensions/slack-communication/tools/delete-message.js +29 -0
- package/dist/extensions/slack-communication/tools/edit-message.d.ts +4 -0
- package/dist/extensions/slack-communication/tools/edit-message.d.ts.map +1 -0
- package/dist/extensions/slack-communication/tools/edit-message.js +37 -0
- package/dist/extensions/slack-communication/tools/post-card.d.ts +4 -0
- package/dist/extensions/slack-communication/tools/post-card.d.ts.map +1 -0
- package/dist/extensions/slack-communication/tools/post-card.js +25 -0
- package/dist/extensions/slack-communication/tools/post-file.d.ts +4 -0
- package/dist/extensions/slack-communication/tools/post-file.d.ts.map +1 -0
- package/dist/extensions/slack-communication/tools/post-file.js +75 -0
- package/dist/extensions/slack-communication/tools/post-update.d.ts +4 -0
- package/dist/extensions/slack-communication/tools/post-update.d.ts.map +1 -0
- package/dist/extensions/slack-communication/tools/post-update.js +36 -0
- package/dist/extensions/slack-communication/tools/react.d.ts +4 -0
- package/dist/extensions/slack-communication/tools/react.d.ts.map +1 -0
- package/dist/extensions/slack-communication/tools/react.js +27 -0
- package/dist/extensions/slack-communication/tools/set-status.d.ts +4 -0
- package/dist/extensions/slack-communication/tools/set-status.d.ts.map +1 -0
- package/dist/extensions/slack-communication/tools/set-status.js +18 -0
- package/dist/extensions.d.ts +85 -0
- package/dist/extensions.d.ts.map +1 -0
- package/dist/extensions.js +140 -0
- package/dist/files-backends/agent-store-presigned-url.d.ts +3 -0
- package/dist/files-backends/agent-store-presigned-url.d.ts.map +1 -0
- package/dist/files-backends/agent-store-presigned-url.js +223 -0
- package/dist/files-backends/cursor-hosted.d.ts +126 -0
- package/dist/files-backends/cursor-hosted.d.ts.map +1 -0
- package/dist/files-backends/cursor-hosted.js +978 -0
- package/dist/files-backends/local-fs.d.ts +33 -0
- package/dist/files-backends/local-fs.d.ts.map +1 -0
- package/dist/files-backends/local-fs.js +195 -0
- package/dist/files.d.ts +225 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +152 -0
- package/dist/filesystem.d.ts +484 -0
- package/dist/filesystem.d.ts.map +1 -0
- package/dist/filesystem.js +1698 -0
- package/dist/hooks.d.ts +25 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +25 -0
- package/dist/index.d.ts +84 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +109 -0
- package/dist/internal/advertise-tools.d.ts +95 -0
- package/dist/internal/advertise-tools.d.ts.map +1 -0
- package/dist/internal/advertise-tools.js +265 -0
- package/dist/internal/approval-gate.d.ts +33 -0
- package/dist/internal/approval-gate.d.ts.map +1 -0
- package/dist/internal/approval-gate.js +80 -0
- package/dist/internal/artifacts-store.d.ts +125 -0
- package/dist/internal/artifacts-store.d.ts.map +1 -0
- package/dist/internal/artifacts-store.js +737 -0
- package/dist/internal/as-of.d.ts +37 -0
- package/dist/internal/as-of.d.ts.map +1 -0
- package/dist/internal/as-of.js +207 -0
- package/dist/internal/auth.d.ts +12 -0
- package/dist/internal/auth.d.ts.map +1 -0
- package/dist/internal/auth.js +34 -0
- package/dist/internal/authored-alias-hooks.d.ts +42 -0
- package/dist/internal/authored-alias-hooks.d.ts.map +1 -0
- package/dist/internal/authored-alias-hooks.js +93 -0
- package/dist/internal/authored-loaders.d.ts +50 -0
- package/dist/internal/authored-loaders.d.ts.map +1 -0
- package/dist/internal/authored-loaders.js +182 -0
- package/dist/internal/bounded-int.d.ts +11 -0
- package/dist/internal/bounded-int.d.ts.map +1 -0
- package/dist/internal/bounded-int.js +13 -0
- package/dist/internal/bounded.d.ts +7 -0
- package/dist/internal/bounded.d.ts.map +1 -0
- package/dist/internal/bounded.js +32 -0
- package/dist/internal/brand.d.ts +6 -0
- package/dist/internal/brand.d.ts.map +1 -0
- package/dist/internal/brand.js +17 -0
- package/dist/internal/browser-origin-guard.d.ts +19 -0
- package/dist/internal/browser-origin-guard.d.ts.map +1 -0
- package/dist/internal/browser-origin-guard.js +51 -0
- package/dist/internal/builtin-tools/artifacts.d.ts +10 -0
- package/dist/internal/builtin-tools/artifacts.d.ts.map +1 -0
- package/dist/internal/builtin-tools/artifacts.js +66 -0
- package/dist/internal/builtin-tools/index.d.ts +17 -0
- package/dist/internal/builtin-tools/index.d.ts.map +1 -0
- package/dist/internal/builtin-tools/index.js +40 -0
- package/dist/internal/builtin-tools/reminders.d.ts +9 -0
- package/dist/internal/builtin-tools/reminders.d.ts.map +1 -0
- package/dist/internal/builtin-tools/reminders.js +134 -0
- package/dist/internal/channel-dispatch.d.ts +43 -0
- package/dist/internal/channel-dispatch.d.ts.map +1 -0
- package/dist/internal/channel-dispatch.js +75 -0
- package/dist/internal/channel-route-dispatch.d.ts +13 -0
- package/dist/internal/channel-route-dispatch.d.ts.map +1 -0
- package/dist/internal/channel-route-dispatch.js +62 -0
- package/dist/internal/channel-state.d.ts +17 -0
- package/dist/internal/channel-state.d.ts.map +1 -0
- package/dist/internal/channel-state.js +69 -0
- package/dist/internal/chat-client.d.ts +45 -0
- package/dist/internal/chat-client.d.ts.map +1 -0
- package/dist/internal/chat-client.js +478 -0
- package/dist/internal/cli-ax.d.ts +233 -0
- package/dist/internal/cli-ax.d.ts.map +1 -0
- package/dist/internal/cli-ax.js +1864 -0
- package/dist/internal/cli-bitbucket.d.ts +33 -0
- package/dist/internal/cli-bitbucket.d.ts.map +1 -0
- package/dist/internal/cli-bitbucket.js +360 -0
- package/dist/internal/cli-convert-automation.d.ts +39 -0
- package/dist/internal/cli-convert-automation.d.ts.map +1 -0
- package/dist/internal/cli-convert-automation.js +281 -0
- package/dist/internal/cli-cursor.d.ts +38 -0
- package/dist/internal/cli-cursor.d.ts.map +1 -0
- package/dist/internal/cli-cursor.js +226 -0
- package/dist/internal/cli-deploy.d.ts +164 -0
- package/dist/internal/cli-deploy.d.ts.map +1 -0
- package/dist/internal/cli-deploy.js +1930 -0
- package/dist/internal/cli-docs.d.ts +34 -0
- package/dist/internal/cli-docs.d.ts.map +1 -0
- package/dist/internal/cli-docs.js +173 -0
- package/dist/internal/cli-github.d.ts +47 -0
- package/dist/internal/cli-github.d.ts.map +1 -0
- package/dist/internal/cli-github.js +639 -0
- package/dist/internal/cli-gitlab.d.ts +37 -0
- package/dist/internal/cli-gitlab.d.ts.map +1 -0
- package/dist/internal/cli-gitlab.js +379 -0
- package/dist/internal/cli-images.d.ts +68 -0
- package/dist/internal/cli-images.d.ts.map +1 -0
- package/dist/internal/cli-images.js +604 -0
- package/dist/internal/cli-manifest.d.ts +19 -0
- package/dist/internal/cli-manifest.d.ts.map +1 -0
- package/dist/internal/cli-manifest.js +49 -0
- package/dist/internal/cli-mcp-oauth.d.ts +51 -0
- package/dist/internal/cli-mcp-oauth.d.ts.map +1 -0
- package/dist/internal/cli-mcp-oauth.js +318 -0
- package/dist/internal/cli-mcp.d.ts +75 -0
- package/dist/internal/cli-mcp.d.ts.map +1 -0
- package/dist/internal/cli-mcp.js +684 -0
- package/dist/internal/cli-skills.d.ts +21 -0
- package/dist/internal/cli-skills.d.ts.map +1 -0
- package/dist/internal/cli-skills.js +77 -0
- package/dist/internal/cli-slack-manual.d.ts +42 -0
- package/dist/internal/cli-slack-manual.d.ts.map +1 -0
- package/dist/internal/cli-slack-manual.js +325 -0
- package/dist/internal/cli-slack.d.ts +59 -0
- package/dist/internal/cli-slack.d.ts.map +1 -0
- package/dist/internal/cli-slack.js +695 -0
- package/dist/internal/cloud-merge.d.ts +25 -0
- package/dist/internal/cloud-merge.d.ts.map +1 -0
- package/dist/internal/cloud-merge.js +36 -0
- package/dist/internal/cloud-turn-cost.d.ts +42 -0
- package/dist/internal/cloud-turn-cost.d.ts.map +1 -0
- package/dist/internal/cloud-turn-cost.js +113 -0
- package/dist/internal/continuation-channel.d.ts +48 -0
- package/dist/internal/continuation-channel.d.ts.map +1 -0
- package/dist/internal/continuation-channel.js +154 -0
- package/dist/internal/continuation-identity.d.ts +113 -0
- package/dist/internal/continuation-identity.d.ts.map +1 -0
- package/dist/internal/continuation-identity.js +749 -0
- package/dist/internal/conversation-mirror.d.ts +85 -0
- package/dist/internal/conversation-mirror.d.ts.map +1 -0
- package/dist/internal/conversation-mirror.js +342 -0
- package/dist/internal/convert-automation/convert-workflow.d.ts +10 -0
- package/dist/internal/convert-automation/convert-workflow.d.ts.map +1 -0
- package/dist/internal/convert-automation/convert-workflow.js +998 -0
- package/dist/internal/convert-automation/slug.d.ts +9 -0
- package/dist/internal/convert-automation/slug.d.ts.map +1 -0
- package/dist/internal/convert-automation/slug.js +28 -0
- package/dist/internal/convert-automation/types.d.ts +202 -0
- package/dist/internal/convert-automation/types.d.ts.map +1 -0
- package/dist/internal/convert-automation/types.js +13 -0
- package/dist/internal/cron.d.ts +17 -0
- package/dist/internal/cron.d.ts.map +1 -0
- package/dist/internal/cron.js +60 -0
- package/dist/internal/cursor/account-mcp.d.ts +135 -0
- package/dist/internal/cursor/account-mcp.d.ts.map +1 -0
- package/dist/internal/cursor/account-mcp.js +510 -0
- package/dist/internal/cursor/backend-client.d.ts +224 -0
- package/dist/internal/cursor/backend-client.d.ts.map +1 -0
- package/dist/internal/cursor/backend-client.js +720 -0
- package/dist/internal/cursor/bound-secret-file.d.ts +56 -0
- package/dist/internal/cursor/bound-secret-file.d.ts.map +1 -0
- package/dist/internal/cursor/bound-secret-file.js +88 -0
- package/dist/internal/cursor/credentials.d.ts +195 -0
- package/dist/internal/cursor/credentials.d.ts.map +1 -0
- package/dist/internal/cursor/credentials.js +475 -0
- package/dist/internal/cursor/cursor-api-transport.d.ts +37 -0
- package/dist/internal/cursor/cursor-api-transport.d.ts.map +1 -0
- package/dist/internal/cursor/cursor-api-transport.js +44 -0
- package/dist/internal/cursor/github-credentials.d.ts +44 -0
- package/dist/internal/cursor/github-credentials.d.ts.map +1 -0
- package/dist/internal/cursor/github-credentials.js +191 -0
- package/dist/internal/cursor/hosted-bound-secrets.d.ts +41 -0
- package/dist/internal/cursor/hosted-bound-secrets.d.ts.map +1 -0
- package/dist/internal/cursor/hosted-bound-secrets.js +54 -0
- package/dist/internal/cursor/hosted-deployment-secrets.d.ts +30 -0
- package/dist/internal/cursor/hosted-deployment-secrets.d.ts.map +1 -0
- package/dist/internal/cursor/hosted-deployment-secrets.js +81 -0
- package/dist/internal/cursor/hosted-store-secrets.d.ts +63 -0
- package/dist/internal/cursor/hosted-store-secrets.d.ts.map +1 -0
- package/dist/internal/cursor/hosted-store-secrets.js +112 -0
- package/dist/internal/cursor/session-token.d.ts +45 -0
- package/dist/internal/cursor/session-token.d.ts.map +1 -0
- package/dist/internal/cursor/session-token.js +97 -0
- package/dist/internal/cursor/store-api-client.d.ts +82 -0
- package/dist/internal/cursor/store-api-client.d.ts.map +1 -0
- package/dist/internal/cursor/store-api-client.js +226 -0
- package/dist/internal/cursor-account-mcp-auth.d.ts +66 -0
- package/dist/internal/cursor-account-mcp-auth.d.ts.map +1 -0
- package/dist/internal/cursor-account-mcp-auth.js +241 -0
- package/dist/internal/cursor-agent-template.d.ts +9 -0
- package/dist/internal/cursor-agent-template.d.ts.map +1 -0
- package/dist/internal/cursor-agent-template.js +43 -0
- package/dist/internal/cursor-deployment-relay.d.ts +95 -0
- package/dist/internal/cursor-deployment-relay.d.ts.map +1 -0
- package/dist/internal/cursor-deployment-relay.js +233 -0
- package/dist/internal/cursor-event-relay.d.ts +243 -0
- package/dist/internal/cursor-event-relay.d.ts.map +1 -0
- package/dist/internal/cursor-event-relay.js +424 -0
- package/dist/internal/cursor-relay-core.d.ts +160 -0
- package/dist/internal/cursor-relay-core.d.ts.map +1 -0
- package/dist/internal/cursor-relay-core.js +470 -0
- package/dist/internal/deferred-channel-session.d.ts +20 -0
- package/dist/internal/deferred-channel-session.d.ts.map +1 -0
- package/dist/internal/deferred-channel-session.js +62 -0
- package/dist/internal/deploy-client.d.ts +576 -0
- package/dist/internal/deploy-client.d.ts.map +1 -0
- package/dist/internal/deploy-client.js +987 -0
- package/dist/internal/deploy-manifest.d.ts +135 -0
- package/dist/internal/deploy-manifest.d.ts.map +1 -0
- package/dist/internal/deploy-manifest.js +231 -0
- package/dist/internal/deploy-source.d.ts +38 -0
- package/dist/internal/deploy-source.d.ts.map +1 -0
- package/dist/internal/deploy-source.js +136 -0
- package/dist/internal/describe-error.d.ts +4 -0
- package/dist/internal/describe-error.d.ts.map +1 -0
- package/dist/internal/describe-error.js +29 -0
- package/dist/internal/dev-mode.d.ts +13 -0
- package/dist/internal/dev-mode.d.ts.map +1 -0
- package/dist/internal/dev-mode.js +17 -0
- package/dist/internal/discovery/agent-config.d.ts +48 -0
- package/dist/internal/discovery/agent-config.d.ts.map +1 -0
- package/dist/internal/discovery/agent-config.js +224 -0
- package/dist/internal/discovery/agent.d.ts +16 -0
- package/dist/internal/discovery/agent.d.ts.map +1 -0
- package/dist/internal/discovery/agent.js +326 -0
- package/dist/internal/discovery/channels.d.ts +10 -0
- package/dist/internal/discovery/channels.d.ts.map +1 -0
- package/dist/internal/discovery/channels.js +97 -0
- package/dist/internal/discovery/connections.d.ts +13 -0
- package/dist/internal/discovery/connections.d.ts.map +1 -0
- package/dist/internal/discovery/connections.js +429 -0
- package/dist/internal/discovery/context.d.ts +20 -0
- package/dist/internal/discovery/context.d.ts.map +1 -0
- package/dist/internal/discovery/context.js +30 -0
- package/dist/internal/discovery/cursor-plugin.d.ts +25 -0
- package/dist/internal/discovery/cursor-plugin.d.ts.map +1 -0
- package/dist/internal/discovery/cursor-plugin.js +549 -0
- package/dist/internal/discovery/extension-overlay.d.ts +31 -0
- package/dist/internal/discovery/extension-overlay.d.ts.map +1 -0
- package/dist/internal/discovery/extension-overlay.js +173 -0
- package/dist/internal/discovery/extensions.d.ts +27 -0
- package/dist/internal/discovery/extensions.d.ts.map +1 -0
- package/dist/internal/discovery/extensions.js +352 -0
- package/dist/internal/discovery/fs.d.ts +15 -0
- package/dist/internal/discovery/fs.d.ts.map +1 -0
- package/dist/internal/discovery/fs.js +115 -0
- package/dist/internal/discovery/hooks.d.ts +7 -0
- package/dist/internal/discovery/hooks.d.ts.map +1 -0
- package/dist/internal/discovery/hooks.js +61 -0
- package/dist/internal/discovery/info.d.ts +6 -0
- package/dist/internal/discovery/info.d.ts.map +1 -0
- package/dist/internal/discovery/info.js +168 -0
- package/dist/internal/discovery/instructions.d.ts +7 -0
- package/dist/internal/discovery/instructions.d.ts.map +1 -0
- package/dist/internal/discovery/instructions.js +95 -0
- package/dist/internal/discovery/modules.d.ts +20 -0
- package/dist/internal/discovery/modules.d.ts.map +1 -0
- package/dist/internal/discovery/modules.js +104 -0
- package/dist/internal/discovery/names.d.ts +12 -0
- package/dist/internal/discovery/names.d.ts.map +1 -0
- package/dist/internal/discovery/names.js +37 -0
- package/dist/internal/discovery/project.d.ts +51 -0
- package/dist/internal/discovery/project.d.ts.map +1 -0
- package/dist/internal/discovery/project.js +290 -0
- package/dist/internal/discovery/reminders.d.ts +7 -0
- package/dist/internal/discovery/reminders.d.ts.map +1 -0
- package/dist/internal/discovery/reminders.js +65 -0
- package/dist/internal/discovery/runtime.d.ts +34 -0
- package/dist/internal/discovery/runtime.d.ts.map +1 -0
- package/dist/internal/discovery/runtime.js +97 -0
- package/dist/internal/discovery/schedules.d.ts +7 -0
- package/dist/internal/discovery/schedules.d.ts.map +1 -0
- package/dist/internal/discovery/schedules.js +118 -0
- package/dist/internal/discovery/seed-files.d.ts +7 -0
- package/dist/internal/discovery/seed-files.d.ts.map +1 -0
- package/dist/internal/discovery/seed-files.js +41 -0
- package/dist/internal/discovery/services.d.ts +26 -0
- package/dist/internal/discovery/services.d.ts.map +1 -0
- package/dist/internal/discovery/services.js +138 -0
- package/dist/internal/discovery/skills.d.ts +11 -0
- package/dist/internal/discovery/skills.d.ts.map +1 -0
- package/dist/internal/discovery/skills.js +156 -0
- package/dist/internal/discovery/subagent.d.ts +16 -0
- package/dist/internal/discovery/subagent.d.ts.map +1 -0
- package/dist/internal/discovery/subagent.js +129 -0
- package/dist/internal/discovery/tools.d.ts +18 -0
- package/dist/internal/discovery/tools.d.ts.map +1 -0
- package/dist/internal/discovery/tools.js +155 -0
- package/dist/internal/discovery.d.ts +15 -0
- package/dist/internal/discovery.d.ts.map +1 -0
- package/dist/internal/discovery.js +13 -0
- package/dist/internal/distribution.d.ts +65 -0
- package/dist/internal/distribution.d.ts.map +1 -0
- package/dist/internal/distribution.js +102 -0
- package/dist/internal/docs-site.d.ts +41 -0
- package/dist/internal/docs-site.d.ts.map +1 -0
- package/dist/internal/docs-site.js +234 -0
- package/dist/internal/duration.d.ts +8 -0
- package/dist/internal/duration.d.ts.map +1 -0
- package/dist/internal/duration.js +35 -0
- 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/eval-judge-model.d.ts +59 -0
- package/dist/internal/eval-judge-model.d.ts.map +1 -0
- package/dist/internal/eval-judge-model.js +129 -0
- package/dist/internal/eval-run-store.d.ts +119 -0
- package/dist/internal/eval-run-store.d.ts.map +1 -0
- package/dist/internal/eval-run-store.js +468 -0
- package/dist/internal/eval-runner.d.ts +98 -0
- package/dist/internal/eval-runner.d.ts.map +1 -0
- package/dist/internal/eval-runner.js +615 -0
- package/dist/internal/evals-client.d.ts +104 -0
- package/dist/internal/evals-client.d.ts.map +1 -0
- package/dist/internal/evals-client.js +303 -0
- package/dist/internal/event-log.d.ts +94 -0
- package/dist/internal/event-log.d.ts.map +1 -0
- package/dist/internal/event-log.js +347 -0
- package/dist/internal/event-mapper.d.ts +123 -0
- package/dist/internal/event-mapper.d.ts.map +1 -0
- package/dist/internal/event-mapper.js +370 -0
- package/dist/internal/expected-delivery-failure.d.ts +3 -0
- package/dist/internal/expected-delivery-failure.d.ts.map +1 -0
- package/dist/internal/expected-delivery-failure.js +28 -0
- package/dist/internal/factory-resource-channel.d.ts +33 -0
- package/dist/internal/factory-resource-channel.d.ts.map +1 -0
- package/dist/internal/factory-resource-channel.js +77 -0
- package/dist/internal/filesystem/errors.d.ts +64 -0
- package/dist/internal/filesystem/errors.d.ts.map +1 -0
- package/dist/internal/filesystem/errors.js +172 -0
- package/dist/internal/filesystem/format.d.ts +28 -0
- package/dist/internal/filesystem/format.d.ts.map +1 -0
- package/dist/internal/filesystem/format.js +212 -0
- package/dist/internal/filesystem/merge.d.ts +34 -0
- package/dist/internal/filesystem/merge.d.ts.map +1 -0
- package/dist/internal/filesystem/merge.js +196 -0
- package/dist/internal/filesystem/nearest.d.ts +14 -0
- package/dist/internal/filesystem/nearest.d.ts.map +1 -0
- package/dist/internal/filesystem/nearest.js +101 -0
- package/dist/internal/filesystem/scratch.d.ts +59 -0
- package/dist/internal/filesystem/scratch.d.ts.map +1 -0
- package/dist/internal/filesystem/scratch.js +204 -0
- package/dist/internal/filesystem/sdk-schemas.d.ts +29 -0
- package/dist/internal/filesystem/sdk-schemas.d.ts.map +1 -0
- package/dist/internal/filesystem/sdk-schemas.js +153 -0
- package/dist/internal/filesystem/tools.d.ts +42 -0
- package/dist/internal/filesystem/tools.d.ts.map +1 -0
- package/dist/internal/filesystem/tools.js +631 -0
- package/dist/internal/filesystem/walk.d.ts +77 -0
- package/dist/internal/filesystem/walk.d.ts.map +1 -0
- package/dist/internal/filesystem/walk.js +350 -0
- package/dist/internal/framework-file-storage.d.ts +75 -0
- package/dist/internal/framework-file-storage.d.ts.map +1 -0
- package/dist/internal/framework-file-storage.js +338 -0
- package/dist/internal/framework-storage-selection.d.ts +66 -0
- package/dist/internal/framework-storage-selection.d.ts.map +1 -0
- package/dist/internal/framework-storage-selection.js +123 -0
- package/dist/internal/frontmatter.d.ts +20 -0
- package/dist/internal/frontmatter.d.ts.map +1 -0
- package/dist/internal/frontmatter.js +87 -0
- package/dist/internal/github-fanout.d.ts +32 -0
- package/dist/internal/github-fanout.d.ts.map +1 -0
- package/dist/internal/github-fanout.js +120 -0
- package/dist/internal/guest-network.d.ts +98 -0
- package/dist/internal/guest-network.d.ts.map +1 -0
- package/dist/internal/guest-network.js +326 -0
- package/dist/internal/handleAgentServeTrigger.d.ts +91 -0
- package/dist/internal/handleAgentServeTrigger.d.ts.map +1 -0
- package/dist/internal/handleAgentServeTrigger.js +199 -0
- package/dist/internal/host-files.d.ts +29 -0
- package/dist/internal/host-files.d.ts.map +1 -0
- package/dist/internal/host-files.js +274 -0
- package/dist/internal/host-kv.d.ts +21 -0
- package/dist/internal/host-kv.d.ts.map +1 -0
- package/dist/internal/host-kv.js +77 -0
- package/dist/internal/host-otel.d.ts +25 -0
- package/dist/internal/host-otel.d.ts.map +1 -0
- package/dist/internal/host-otel.js +162 -0
- package/dist/internal/host-platforms.d.ts +58 -0
- package/dist/internal/host-platforms.d.ts.map +1 -0
- package/dist/internal/host-platforms.js +126 -0
- package/dist/internal/hosted-admission-adapter.d.ts +36 -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-admission-context.d.ts +20 -0
- package/dist/internal/hosted-admission-context.d.ts.map +1 -0
- package/dist/internal/hosted-admission-context.js +31 -0
- package/dist/internal/hosted-catch-protocol.d.ts +51 -0
- package/dist/internal/hosted-catch-protocol.d.ts.map +1 -0
- package/dist/internal/hosted-catch-protocol.js +87 -0
- package/dist/internal/hosted-catch.d.ts +40 -0
- package/dist/internal/hosted-catch.d.ts.map +1 -0
- package/dist/internal/hosted-catch.js +146 -0
- package/dist/internal/hosted-delivery-protocol.d.ts +199 -0
- package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -0
- package/dist/internal/hosted-delivery-protocol.js +516 -0
- package/dist/internal/hosted-delivery.d.ts +110 -0
- package/dist/internal/hosted-delivery.d.ts.map +1 -0
- package/dist/internal/hosted-delivery.js +768 -0
- package/dist/internal/hosted-execution-diag.d.ts +75 -0
- package/dist/internal/hosted-execution-diag.d.ts.map +1 -0
- package/dist/internal/hosted-execution-diag.js +220 -0
- package/dist/internal/hosted-execution-flush.d.ts +50 -0
- package/dist/internal/hosted-execution-flush.d.ts.map +1 -0
- package/dist/internal/hosted-execution-flush.js +71 -0
- package/dist/internal/hosted-execution-latency.d.ts +80 -0
- package/dist/internal/hosted-execution-latency.d.ts.map +1 -0
- package/dist/internal/hosted-execution-latency.js +218 -0
- package/dist/internal/hosted-managed-automation.d.ts +30 -0
- package/dist/internal/hosted-managed-automation.d.ts.map +1 -0
- package/dist/internal/hosted-managed-automation.js +83 -0
- package/dist/internal/hosting.d.ts +53 -0
- package/dist/internal/hosting.d.ts.map +1 -0
- package/dist/internal/hosting.js +93 -0
- package/dist/internal/http-channel-define.d.ts +11 -0
- package/dist/internal/http-channel-define.d.ts.map +1 -0
- package/dist/internal/http-channel-define.js +18 -0
- package/dist/internal/http-channel.d.ts +54 -0
- package/dist/internal/http-channel.d.ts.map +1 -0
- package/dist/internal/http-channel.js +534 -0
- package/dist/internal/http-control-plane-session.d.ts +34 -0
- package/dist/internal/http-control-plane-session.d.ts.map +1 -0
- package/dist/internal/http-control-plane-session.js +93 -0
- package/dist/internal/ids.d.ts +19 -0
- package/dist/internal/ids.d.ts.map +1 -0
- package/dist/internal/ids.js +34 -0
- package/dist/internal/incoming-headers-to-fetch.d.ts +10 -0
- package/dist/internal/incoming-headers-to-fetch.d.ts.map +1 -0
- package/dist/internal/incoming-headers-to-fetch.js +41 -0
- package/dist/internal/init-interview.d.ts +67 -0
- package/dist/internal/init-interview.d.ts.map +1 -0
- package/dist/internal/init-interview.js +295 -0
- package/dist/internal/init-project.d.ts +175 -0
- package/dist/internal/init-project.d.ts.map +1 -0
- package/dist/internal/init-project.js +531 -0
- package/dist/internal/init-scaffold.d.ts +9 -0
- package/dist/internal/init-scaffold.d.ts.map +1 -0
- package/dist/internal/init-scaffold.js +172 -0
- package/dist/internal/inline-stdio-define-stub.d.ts +3 -0
- package/dist/internal/inline-stdio-define-stub.d.ts.map +1 -0
- package/dist/internal/inline-stdio-define-stub.js +4 -0
- package/dist/internal/inline-stdio-mcp.d.ts +34 -0
- package/dist/internal/inline-stdio-mcp.d.ts.map +1 -0
- package/dist/internal/inline-stdio-mcp.js +176 -0
- package/dist/internal/install-cursor-skills.d.ts +164 -0
- package/dist/internal/install-cursor-skills.d.ts.map +1 -0
- package/dist/internal/install-cursor-skills.js +498 -0
- package/dist/internal/is-record.d.ts +2 -0
- package/dist/internal/is-record.d.ts.map +1 -0
- package/dist/internal/is-record.js +3 -0
- package/dist/internal/json-schema.d.ts +25 -0
- package/dist/internal/json-schema.d.ts.map +1 -0
- package/dist/internal/json-schema.js +76 -0
- package/dist/internal/legacy-command-guard.d.ts +22 -0
- package/dist/internal/legacy-command-guard.d.ts.map +1 -0
- package/dist/internal/legacy-command-guard.js +51 -0
- package/dist/internal/listen-port.d.ts +22 -0
- package/dist/internal/listen-port.d.ts.map +1 -0
- package/dist/internal/listen-port.js +77 -0
- package/dist/internal/local-control-plane.d.ts +59 -0
- package/dist/internal/local-control-plane.d.ts.map +1 -0
- package/dist/internal/local-control-plane.js +351 -0
- package/dist/internal/local-env.d.ts +10 -0
- package/dist/internal/local-env.d.ts.map +1 -0
- package/dist/internal/local-env.js +55 -0
- package/dist/internal/local-ingress.d.ts +35 -0
- package/dist/internal/local-ingress.d.ts.map +1 -0
- package/dist/internal/local-ingress.js +44 -0
- package/dist/internal/log-ring.d.ts +23 -0
- package/dist/internal/log-ring.d.ts.map +1 -0
- package/dist/internal/log-ring.js +42 -0
- package/dist/internal/logs-client.d.ts +79 -0
- package/dist/internal/logs-client.d.ts.map +1 -0
- package/dist/internal/logs-client.js +350 -0
- package/dist/internal/map-pool.d.ts +11 -0
- package/dist/internal/map-pool.d.ts.map +1 -0
- package/dist/internal/map-pool.js +48 -0
- package/dist/internal/mcp-endpoint.d.ts +72 -0
- package/dist/internal/mcp-endpoint.d.ts.map +1 -0
- package/dist/internal/mcp-endpoint.js +530 -0
- package/dist/internal/mcp-host.d.ts +84 -0
- package/dist/internal/mcp-host.d.ts.map +1 -0
- package/dist/internal/mcp-host.js +391 -0
- 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 +90 -0
- package/dist/internal/mcp-oauth.d.ts +118 -0
- package/dist/internal/mcp-oauth.d.ts.map +1 -0
- package/dist/internal/mcp-oauth.js +450 -0
- package/dist/internal/net.d.ts +5 -0
- package/dist/internal/net.d.ts.map +1 -0
- package/dist/internal/net.js +28 -0
- package/dist/internal/open-browser.d.ts +5 -0
- package/dist/internal/open-browser.d.ts.map +1 -0
- package/dist/internal/open-browser.js +34 -0
- package/dist/internal/otel/attrs.d.ts +7 -0
- package/dist/internal/otel/attrs.d.ts.map +1 -0
- package/dist/internal/otel/attrs.js +13 -0
- package/dist/internal/otel/collector.d.ts +43 -0
- package/dist/internal/otel/collector.d.ts.map +1 -0
- package/dist/internal/otel/collector.js +650 -0
- package/dist/internal/otel/config.d.ts +40 -0
- package/dist/internal/otel/config.d.ts.map +1 -0
- package/dist/internal/otel/config.js +231 -0
- package/dist/internal/otel/http.d.ts +13 -0
- package/dist/internal/otel/http.d.ts.map +1 -0
- package/dist/internal/otel/http.js +95 -0
- package/dist/internal/otel/mcp.d.ts +16 -0
- package/dist/internal/otel/mcp.d.ts.map +1 -0
- package/dist/internal/otel/mcp.js +55 -0
- package/dist/internal/otel/propagation.d.ts +6 -0
- package/dist/internal/otel/propagation.d.ts.map +1 -0
- package/dist/internal/otel/propagation.js +14 -0
- package/dist/internal/otel/runtime.d.ts +46 -0
- package/dist/internal/otel/runtime.d.ts.map +1 -0
- package/dist/internal/otel/runtime.js +333 -0
- package/dist/internal/package-inline-stdio.d.ts +12 -0
- package/dist/internal/package-inline-stdio.d.ts.map +1 -0
- package/dist/internal/package-inline-stdio.js +99 -0
- package/dist/internal/peer-connections.d.ts +44 -0
- package/dist/internal/peer-connections.d.ts.map +1 -0
- package/dist/internal/peer-connections.js +67 -0
- package/dist/internal/platform-schedule-sync.d.ts +17 -0
- package/dist/internal/platform-schedule-sync.d.ts.map +1 -0
- package/dist/internal/platform-schedule-sync.js +70 -0
- package/dist/internal/platform-timers.d.ts +35 -0
- package/dist/internal/platform-timers.d.ts.map +1 -0
- package/dist/internal/platform-timers.js +277 -0
- package/dist/internal/playground/static.d.ts +52 -0
- package/dist/internal/playground/static.d.ts.map +1 -0
- package/dist/internal/playground/static.js +231 -0
- package/dist/internal/playground/toolchain.d.ts +57 -0
- package/dist/internal/playground/toolchain.d.ts.map +1 -0
- package/dist/internal/playground/toolchain.js +229 -0
- package/dist/internal/playground-proxy.d.ts +69 -0
- package/dist/internal/playground-proxy.d.ts.map +1 -0
- package/dist/internal/playground-proxy.js +474 -0
- package/dist/internal/process-memory-telemetry.d.ts +89 -0
- package/dist/internal/process-memory-telemetry.d.ts.map +1 -0
- package/dist/internal/process-memory-telemetry.js +244 -0
- package/dist/internal/project-layout.d.ts +7 -0
- package/dist/internal/project-layout.d.ts.map +1 -0
- package/dist/internal/project-layout.js +24 -0
- package/dist/internal/prompt-context.d.ts +38 -0
- package/dist/internal/prompt-context.d.ts.map +1 -0
- package/dist/internal/prompt-context.js +89 -0
- package/dist/internal/reminder-control-plane-protocol.d.ts +189 -0
- package/dist/internal/reminder-control-plane-protocol.d.ts.map +1 -0
- package/dist/internal/reminder-control-plane-protocol.js +246 -0
- package/dist/internal/reminder-runner.d.ts +208 -0
- package/dist/internal/reminder-runner.d.ts.map +1 -0
- package/dist/internal/reminder-runner.js +1231 -0
- package/dist/internal/reminder-schedule.d.ts +25 -0
- package/dist/internal/reminder-schedule.d.ts.map +1 -0
- package/dist/internal/reminder-schedule.js +82 -0
- package/dist/internal/reminder-store.d.ts +78 -0
- package/dist/internal/reminder-store.d.ts.map +1 -0
- package/dist/internal/reminder-store.js +126 -0
- package/dist/internal/request-error-status.d.ts +14 -0
- package/dist/internal/request-error-status.d.ts.map +1 -0
- package/dist/internal/request-error-status.js +76 -0
- package/dist/internal/request-headers.d.ts +11 -0
- package/dist/internal/request-headers.d.ts.map +1 -0
- package/dist/internal/request-headers.js +14 -0
- package/dist/internal/resolve-prod-target.d.ts +84 -0
- package/dist/internal/resolve-prod-target.d.ts.map +1 -0
- package/dist/internal/resolve-prod-target.js +259 -0
- package/dist/internal/resolved-connections.d.ts +62 -0
- package/dist/internal/resolved-connections.d.ts.map +1 -0
- package/dist/internal/resolved-connections.js +93 -0
- package/dist/internal/router.d.ts +33 -0
- package/dist/internal/router.d.ts.map +1 -0
- package/dist/internal/router.js +93 -0
- package/dist/internal/run-client.d.ts +68 -0
- package/dist/internal/run-client.d.ts.map +1 -0
- package/dist/internal/run-client.js +239 -0
- package/dist/internal/runtime-dispatch-runner.d.ts +24 -0
- package/dist/internal/runtime-dispatch-runner.d.ts.map +1 -0
- package/dist/internal/runtime-dispatch-runner.js +54 -0
- package/dist/internal/sanitize-mcp-tool-name.d.ts +20 -0
- package/dist/internal/sanitize-mcp-tool-name.d.ts.map +1 -0
- package/dist/internal/sanitize-mcp-tool-name.js +28 -0
- package/dist/internal/schedule-runner.d.ts +31 -0
- package/dist/internal/schedule-runner.d.ts.map +1 -0
- package/dist/internal/schedule-runner.js +127 -0
- package/dist/internal/scm/pr-url.d.ts +56 -0
- package/dist/internal/scm/pr-url.d.ts.map +1 -0
- package/dist/internal/scm/pr-url.js +152 -0
- package/dist/internal/sdk-runner.d.ts +190 -0
- package/dist/internal/sdk-runner.d.ts.map +1 -0
- package/dist/internal/sdk-runner.js +714 -0
- package/dist/internal/serve-restart.d.ts +42 -0
- package/dist/internal/serve-restart.d.ts.map +1 -0
- package/dist/internal/serve-restart.js +85 -0
- package/dist/internal/server.d.ts +34 -0
- package/dist/internal/server.d.ts.map +1 -0
- package/dist/internal/server.js +2464 -0
- package/dist/internal/session-cost.d.ts +50 -0
- package/dist/internal/session-cost.d.ts.map +1 -0
- package/dist/internal/session-cost.js +157 -0
- package/dist/internal/session-engine.d.ts +912 -0
- package/dist/internal/session-engine.d.ts.map +1 -0
- package/dist/internal/session-engine.js +4493 -0
- package/dist/internal/session-links.d.ts +14 -0
- package/dist/internal/session-links.d.ts.map +1 -0
- package/dist/internal/session-links.js +67 -0
- package/dist/internal/session-run-log.d.ts +122 -0
- package/dist/internal/session-run-log.d.ts.map +1 -0
- package/dist/internal/session-run-log.js +345 -0
- package/dist/internal/session-store.d.ts +73 -0
- package/dist/internal/session-store.d.ts.map +1 -0
- package/dist/internal/session-store.js +310 -0
- package/dist/internal/session-urls.d.ts +10 -0
- package/dist/internal/session-urls.d.ts.map +1 -0
- package/dist/internal/session-urls.js +25 -0
- package/dist/internal/sessions-client.d.ts +50 -0
- package/dist/internal/sessions-client.d.ts.map +1 -0
- package/dist/internal/sessions-client.js +207 -0
- package/dist/internal/skill-catalog.d.ts +28 -0
- package/dist/internal/skill-catalog.d.ts.map +1 -0
- package/dist/internal/skill-catalog.js +41 -0
- package/dist/internal/skills-sync.d.ts +24 -0
- package/dist/internal/skills-sync.d.ts.map +1 -0
- package/dist/internal/skills-sync.js +56 -0
- package/dist/internal/slack-provision-client.d.ts +198 -0
- package/dist/internal/slack-provision-client.d.ts.map +1 -0
- package/dist/internal/slack-provision-client.js +346 -0
- package/dist/internal/slack-thread-key.d.ts +7 -0
- package/dist/internal/slack-thread-key.d.ts.map +1 -0
- package/dist/internal/slack-thread-key.js +10 -0
- package/dist/internal/sleep.d.ts +2 -0
- package/dist/internal/sleep.d.ts.map +1 -0
- package/dist/internal/sleep.js +3 -0
- package/dist/internal/start-log-store.d.ts +60 -0
- package/dist/internal/start-log-store.d.ts.map +1 -0
- package/dist/internal/start-log-store.js +149 -0
- package/dist/internal/steering.d.ts +15 -0
- package/dist/internal/steering.d.ts.map +1 -0
- package/dist/internal/steering.js +18 -0
- package/dist/internal/storage-coordinator.d.ts +203 -0
- package/dist/internal/storage-coordinator.d.ts.map +1 -0
- package/dist/internal/storage-coordinator.js +705 -0
- package/dist/internal/storage-roles.d.ts +78 -0
- package/dist/internal/storage-roles.d.ts.map +1 -0
- package/dist/internal/storage-roles.js +24 -0
- package/dist/internal/storage.d.ts +2 -0
- package/dist/internal/storage.d.ts.map +1 -0
- package/dist/internal/storage.js +1 -0
- package/dist/internal/store-api-protocol.d.ts +134 -0
- package/dist/internal/store-api-protocol.d.ts.map +1 -0
- package/dist/internal/store-api-protocol.js +124 -0
- package/dist/internal/store-mirror.d.ts +29 -0
- package/dist/internal/store-mirror.d.ts.map +1 -0
- package/dist/internal/store-mirror.js +70 -0
- package/dist/internal/stream-progress.d.ts +14 -0
- package/dist/internal/stream-progress.d.ts.map +1 -0
- package/dist/internal/stream-progress.js +229 -0
- package/dist/internal/terminal-style.d.ts +40 -0
- package/dist/internal/terminal-style.d.ts.map +1 -0
- package/dist/internal/terminal-style.js +94 -0
- package/dist/internal/testing/factory-resource-fixtures.d.ts +48 -0
- package/dist/internal/testing/factory-resource-fixtures.d.ts.map +1 -0
- package/dist/internal/testing/factory-resource-fixtures.js +41 -0
- package/dist/internal/testing/scm-continuation-fixtures.d.ts +33 -0
- package/dist/internal/testing/scm-continuation-fixtures.d.ts.map +1 -0
- package/dist/internal/testing/scm-continuation-fixtures.js +253 -0
- package/dist/internal/tool-catalog.d.ts +31 -0
- package/dist/internal/tool-catalog.d.ts.map +1 -0
- package/dist/internal/tool-catalog.js +70 -0
- package/dist/internal/tool-policy.d.ts +110 -0
- package/dist/internal/tool-policy.d.ts.map +1 -0
- package/dist/internal/tool-policy.js +138 -0
- package/dist/internal/tool-result.d.ts +20 -0
- package/dist/internal/tool-result.d.ts.map +1 -0
- package/dist/internal/tool-result.js +45 -0
- package/dist/internal/trajectory.d.ts +69 -0
- package/dist/internal/trajectory.d.ts.map +1 -0
- package/dist/internal/trajectory.js +351 -0
- package/dist/internal/turn-cost.d.ts +41 -0
- package/dist/internal/turn-cost.d.ts.map +1 -0
- package/dist/internal/turn-cost.js +109 -0
- package/dist/internal/turn-governor.d.ts +54 -0
- package/dist/internal/turn-governor.d.ts.map +1 -0
- package/dist/internal/turn-governor.js +111 -0
- package/dist/internal/turn-lease-store.d.ts +44 -0
- package/dist/internal/turn-lease-store.d.ts.map +1 -0
- package/dist/internal/turn-lease-store.js +139 -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/internal/update-check.d.ts +93 -0
- package/dist/internal/update-check.d.ts.map +1 -0
- package/dist/internal/update-check.js +427 -0
- package/dist/internal/v2-hosting.d.ts +31 -0
- package/dist/internal/v2-hosting.d.ts.map +1 -0
- package/dist/internal/v2-hosting.js +33 -0
- package/dist/internal/workspace-sync.d.ts +47 -0
- package/dist/internal/workspace-sync.d.ts.map +1 -0
- package/dist/internal/workspace-sync.js +104 -0
- package/dist/internal/workspace.d.ts +151 -0
- package/dist/internal/workspace.d.ts.map +1 -0
- package/dist/internal/workspace.js +455 -0
- package/dist/kv/memory-kv.d.ts +24 -0
- package/dist/kv/memory-kv.d.ts.map +1 -0
- package/dist/kv/memory-kv.js +104 -0
- package/dist/kv.d.ts +18 -0
- package/dist/kv.d.ts.map +1 -0
- package/dist/kv.js +16 -0
- package/dist/memory.d.ts +138 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +401 -0
- package/dist/otel-public.d.ts +7 -0
- package/dist/otel-public.d.ts.map +1 -0
- package/dist/otel-public.js +5 -0
- package/dist/otel.d.ts +131 -0
- package/dist/otel.d.ts.map +1 -0
- package/dist/otel.js +186 -0
- package/dist/playground/assets/cursor-icons-16-4RnoYH3n.woff2 +0 -0
- package/dist/playground/assets/index-BMV_IcEE.css +1 -0
- package/dist/playground/assets/index-FFdUqTt_.js +71 -0
- package/dist/playground/index.html +17 -0
- package/dist/prompt.d.ts +39 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +70 -0
- package/dist/reminders.d.ts +42 -0
- package/dist/reminders.d.ts.map +1 -0
- package/dist/reminders.js +45 -0
- package/dist/schedules.d.ts +23 -0
- package/dist/schedules.d.ts.map +1 -0
- package/dist/schedules.js +26 -0
- package/dist/skills.d.ts +22 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +22 -0
- package/dist/storage-backends/cursor-hosted-v2.d.ts +82 -0
- package/dist/storage-backends/cursor-hosted-v2.d.ts.map +1 -0
- package/dist/storage-backends/cursor-hosted-v2.js +171 -0
- package/dist/storage-backends/cursor-hosted.d.ts +107 -0
- package/dist/storage-backends/cursor-hosted.d.ts.map +1 -0
- package/dist/storage-backends/cursor-hosted.js +278 -0
- package/dist/storage-backends/file-kv.d.ts +42 -0
- package/dist/storage-backends/file-kv.d.ts.map +1 -0
- package/dist/storage-backends/file-kv.js +254 -0
- package/dist/storage-protocol.d.ts +142 -0
- package/dist/storage-protocol.d.ts.map +1 -0
- package/dist/storage-protocol.js +201 -0
- package/dist/storage.d.ts +254 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +154 -0
- package/dist/tools.d.ts +132 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +77 -0
- package/dist/types.d.ts +3375 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +189 -0
- package/docs/README.md +119 -0
- package/docs/building-with-agents.md +140 -0
- package/docs/deployment.md +216 -0
- package/docs/evals.md +262 -0
- package/docs/guides/agent-to-agent.md +157 -0
- package/docs/guides/bitbucket.md +293 -0
- package/docs/guides/cloud-agents.md +95 -0
- package/docs/guides/convert-automation.md +188 -0
- package/docs/guides/github.md +331 -0
- package/docs/guides/gitlab.md +299 -0
- package/docs/guides/grokbot-agents.md +147 -0
- package/docs/guides/hooks.md +119 -0
- package/docs/guides/improve.md +139 -0
- package/docs/guides/jev.md +299 -0
- package/docs/guides/mcp-oauth.md +167 -0
- package/docs/guides/opentelemetry.md +158 -0
- package/docs/guides/slack.md +278 -0
- package/docs/guides/webhooks.md +241 -0
- package/docs/hillclimbing.md +142 -0
- package/docs/quickstart.md +311 -0
- package/docs/reference/agent-config.md +193 -0
- package/docs/reference/artifacts.md +121 -0
- package/docs/reference/channels.md +203 -0
- package/docs/reference/cli.md +909 -0
- package/docs/reference/connections.md +277 -0
- package/docs/reference/evals.md +292 -0
- package/docs/reference/extensions.md +198 -0
- package/docs/reference/hooks.md +195 -0
- package/docs/reference/http-api.md +236 -0
- package/docs/reference/instructions.md +68 -0
- package/docs/reference/playground.md +47 -0
- package/docs/reference/project-layout.md +120 -0
- package/docs/reference/prompt.md +55 -0
- package/docs/reference/schedules.md +175 -0
- package/docs/reference/sessions.md +165 -0
- package/docs/reference/skills.md +80 -0
- package/docs/reference/subagents.md +67 -0
- package/docs/reference/tools.md +329 -0
- package/docs/templates/agentic-owners.md +100 -0
- package/docs/templates/security-reviewer.md +90 -0
- package/docs/templates/thermo-quality-review.md +83 -0
- package/docs/templates/thermo-review.md +82 -0
- package/docs/templates/triage.md +102 -0
- package/docs/troubleshooting.md +148 -0
- package/package.json +384 -0
- package/scripts/install-cursor-skills.mjs +29 -0
- package/skills/create-agent/SKILL.md +170 -0
- package/skills/debug/SKILL.md +43 -0
- package/skills/deploy/SKILL.md +169 -0
- package/skills/evals/SKILL.md +145 -0
- package/skills/framework-map/SKILL.md +125 -0
- package/skills/github/SKILL.md +110 -0
- package/skills/hillclimb/SKILL.md +63 -0
- package/skills/mcp-auth/SKILL.md +84 -0
- package/skills/otel/SKILL.md +50 -0
- package/skills/setup-slack/SKILL.md +149 -0
- package/src/artifacts.ts +65 -0
- package/src/bin/agent-serve.ts +1833 -0
- package/src/channels/bitbucket/api.ts +891 -0
- package/src/channels/bitbucket/binding.ts +417 -0
- package/src/channels/bitbucket/bitbucket-channel.ts +162 -0
- package/src/channels/bitbucket/bitbucket-webhook.ts +519 -0
- package/src/channels/bitbucket/checks.ts +146 -0
- package/src/channels/bitbucket/cursor-account.ts +106 -0
- package/src/channels/bitbucket/defaults.ts +100 -0
- package/src/channels/bitbucket/events.ts +74 -0
- package/src/channels/bitbucket/index.ts +51 -0
- package/src/channels/bitbucket/replay.ts +421 -0
- package/src/channels/bitbucket/state.ts +59 -0
- package/src/channels/bitbucket/types.ts +261 -0
- package/src/channels/change-monitors/change-monitors-channel.ts +63 -0
- package/src/channels/change-monitors/index.ts +8 -0
- package/src/channels/change-monitors/types.ts +42 -0
- package/src/channels/checks.ts +185 -0
- package/src/channels/deployments/deployments-channel.ts +224 -0
- package/src/channels/deployments/index.ts +25 -0
- package/src/channels/deployments/types.ts +92 -0
- package/src/channels/github/api.ts +526 -0
- package/src/channels/github/auth.ts +254 -0
- package/src/channels/github/binding.ts +233 -0
- package/src/channels/github/checks.ts +145 -0
- package/src/channels/github/cursor-account.ts +303 -0
- package/src/channels/github/defaults.ts +168 -0
- package/src/channels/github/events.ts +67 -0
- package/src/channels/github/forward.ts +359 -0
- package/src/channels/github/github-channel.ts +2334 -0
- package/src/channels/github/index.ts +54 -0
- package/src/channels/github/progress.ts +365 -0
- package/src/channels/github/replay.ts +372 -0
- package/src/channels/github/state.ts +124 -0
- package/src/channels/github/types.ts +590 -0
- package/src/channels/gitlab/api.ts +329 -0
- package/src/channels/gitlab/binding.ts +231 -0
- package/src/channels/gitlab/checks.ts +228 -0
- package/src/channels/gitlab/cursor-account.ts +105 -0
- package/src/channels/gitlab/defaults.ts +80 -0
- package/src/channels/gitlab/events.ts +37 -0
- package/src/channels/gitlab/gitlab-channel.ts +160 -0
- package/src/channels/gitlab/gitlab-webhook.ts +509 -0
- package/src/channels/gitlab/index.ts +44 -0
- package/src/channels/gitlab/replay.ts +328 -0
- package/src/channels/gitlab/state.ts +55 -0
- package/src/channels/gitlab/types.ts +247 -0
- package/src/channels/issues/index.ts +9 -0
- package/src/channels/issues/issues-channel.ts +45 -0
- package/src/channels/issues/types.ts +38 -0
- package/src/channels/origin/api.ts +507 -0
- package/src/channels/origin/binding.ts +114 -0
- package/src/channels/origin/checks.ts +250 -0
- package/src/channels/origin/cursor-account.ts +58 -0
- package/src/channels/origin/defaults.ts +140 -0
- package/src/channels/origin/events.ts +35 -0
- package/src/channels/origin/index.ts +35 -0
- package/src/channels/origin/origin-channel.ts +815 -0
- package/src/channels/origin/origin-webhook.ts +296 -0
- package/src/channels/origin/reactions.ts +72 -0
- package/src/channels/origin/state.ts +54 -0
- package/src/channels/origin/types.ts +295 -0
- package/src/channels/slack/agentic-delivery.ts +634 -0
- package/src/channels/slack/agentic-posted.ts +135 -0
- package/src/channels/slack/agentic-signals.ts +231 -0
- package/src/channels/slack/api.ts +761 -0
- package/src/channels/slack/approvals.ts +279 -0
- package/src/channels/slack/assistant.ts +76 -0
- package/src/channels/slack/attachments.ts +596 -0
- package/src/channels/slack/auth.ts +127 -0
- package/src/channels/slack/blocks.ts +131 -0
- package/src/channels/slack/bot-mentions.ts +318 -0
- package/src/channels/slack/channel-watch.ts +314 -0
- package/src/channels/slack/cli-project.ts +249 -0
- package/src/channels/slack/code-fences.ts +171 -0
- package/src/channels/slack/constants.ts +38 -0
- package/src/channels/slack/credentials.ts +88 -0
- package/src/channels/slack/defaults.ts +714 -0
- package/src/channels/slack/dispatch.ts +1060 -0
- package/src/channels/slack/doctor.ts +338 -0
- package/src/channels/slack/eval-directive.ts +306 -0
- package/src/channels/slack/event-id-dedupe.ts +30 -0
- package/src/channels/slack/external-policy.ts +75 -0
- package/src/channels/slack/footer.ts +106 -0
- package/src/channels/slack/inbound.ts +416 -0
- package/src/channels/slack/index.ts +53 -0
- package/src/channels/slack/init.ts +169 -0
- package/src/channels/slack/interactive.ts +406 -0
- package/src/channels/slack/links.ts +23 -0
- package/src/channels/slack/live-delivery.ts +489 -0
- package/src/channels/slack/log.ts +70 -0
- package/src/channels/slack/manifest.ts +239 -0
- package/src/channels/slack/message-body.ts +82 -0
- package/src/channels/slack/nudge-carrier.ts +224 -0
- package/src/channels/slack/nudge-store.ts +334 -0
- package/src/channels/slack/paint-start.ts +93 -0
- package/src/channels/slack/placeholder.ts +358 -0
- package/src/channels/slack/pr-ref.ts +74 -0
- package/src/channels/slack/progress-delivery.ts +138 -0
- package/src/channels/slack/reasoning-card.ts +125 -0
- package/src/channels/slack/reasoning-chip.ts +41 -0
- package/src/channels/slack/recording-sink.ts +167 -0
- package/src/channels/slack/redact.ts +6 -0
- package/src/channels/slack/reply-options.ts +279 -0
- package/src/channels/slack/setup.ts +125 -0
- package/src/channels/slack/slack-channel.ts +744 -0
- package/src/channels/slack/socket-mode.ts +140 -0
- package/src/channels/slack/stop.ts +25 -0
- package/src/channels/slack/stream.ts +66 -0
- package/src/channels/slack/thread-context.ts +485 -0
- package/src/channels/slack/tool-approval-events.ts +233 -0
- package/src/channels/slack/tool-cards.ts +198 -0
- package/src/channels/slack/tool-chip.ts +34 -0
- package/src/channels/slack/types.ts +782 -0
- package/src/channels/slack/upload.ts +137 -0
- package/src/channels.ts +543 -0
- package/src/connections.ts +153 -0
- package/src/dashboard-session-url.ts +30 -0
- package/src/evals/assertions.ts +688 -0
- package/src/evals/expect.ts +250 -0
- package/src/evals/judge.ts +329 -0
- package/src/evals/loaders.ts +111 -0
- package/src/evals/matchers.ts +223 -0
- package/src/evals/reporters.ts +257 -0
- package/src/evals/results.ts +130 -0
- package/src/evals/run-facts.ts +306 -0
- package/src/evals.ts +709 -0
- package/src/extensions/cursor-cloud-agents/extension.ts +114 -0
- package/src/extensions/cursor-cloud-agents/lib/backend.ts +386 -0
- package/src/extensions/cursor-cloud-agents/lib/fleet.ts +98 -0
- package/src/extensions/cursor-cloud-agents/lib/inputs.ts +5 -0
- package/src/extensions/cursor-cloud-agents/lib/repos.ts +68 -0
- package/src/extensions/cursor-cloud-agents/lib/transcript.ts +19 -0
- package/src/extensions/cursor-cloud-agents/skills/handoff.md +37 -0
- package/src/extensions/cursor-cloud-agents/tools/cancel.ts +19 -0
- package/src/extensions/cursor-cloud-agents/tools/dump.ts +47 -0
- package/src/extensions/cursor-cloud-agents/tools/get.ts +18 -0
- package/src/extensions/cursor-cloud-agents/tools/launch.ts +60 -0
- package/src/extensions/cursor-cloud-agents/tools/list.ts +31 -0
- package/src/extensions/cursor-cloud-agents/tools/reply.ts +34 -0
- package/src/extensions/improve/extension.ts +69 -0
- package/src/extensions/improve/skills/yourself.ts +50 -0
- package/src/extensions/jev/extension.ts +95 -0
- package/src/extensions/jev/lib/evaluate.ts +266 -0
- package/src/extensions/jev/skills/gated-write.md +25 -0
- package/src/extensions/jev/skills/questions.md +33 -0
- package/src/extensions/jev/tools/evaluate.ts +82 -0
- package/src/extensions/slack-communication/evals/harness.ts +705 -0
- package/src/extensions/slack-communication/evals/slack-communication.eval.ts +830 -0
- package/src/extensions/slack-communication/evals/slack-placeholder.eval.ts +172 -0
- package/src/extensions/slack-communication/extension.ts +44 -0
- package/src/extensions/slack-communication/skills/slack-voice.ts +56 -0
- package/src/extensions/slack-communication/tools/delete-message.ts +33 -0
- package/src/extensions/slack-communication/tools/edit-message.ts +42 -0
- package/src/extensions/slack-communication/tools/post-card.ts +28 -0
- package/src/extensions/slack-communication/tools/post-file.ts +82 -0
- package/src/extensions/slack-communication/tools/post-update.ts +40 -0
- package/src/extensions/slack-communication/tools/react.ts +30 -0
- package/src/extensions/slack-communication/tools/set-status.ts +21 -0
- package/src/extensions.ts +179 -0
- package/src/files-backends/agent-store-presigned-url.ts +11 -0
- package/src/files-backends/cursor-hosted.ts +1212 -0
- package/src/files-backends/local-fs.ts +163 -0
- package/src/files.ts +293 -0
- package/src/filesystem.ts +2005 -0
- package/src/hooks.ts +30 -0
- package/src/index.ts +259 -0
- package/src/internal/advertise-tools.ts +370 -0
- package/src/internal/approval-gate.ts +103 -0
- package/src/internal/artifacts-store.ts +825 -0
- package/src/internal/as-of.ts +257 -0
- package/src/internal/auth.ts +46 -0
- package/src/internal/authored-alias-hooks.ts +138 -0
- package/src/internal/authored-loaders.ts +188 -0
- package/src/internal/bounded-int.ts +24 -0
- package/src/internal/bounded.ts +25 -0
- package/src/internal/brand.ts +28 -0
- package/src/internal/browser-origin-guard.ts +67 -0
- package/src/internal/builtin-tools/artifacts.ts +62 -0
- package/src/internal/builtin-tools/index.ts +53 -0
- package/src/internal/builtin-tools/reminders.ts +145 -0
- package/src/internal/channel-dispatch.ts +129 -0
- package/src/internal/channel-route-dispatch.ts +58 -0
- package/src/internal/channel-state.ts +83 -0
- package/src/internal/chat-client.ts +594 -0
- package/src/internal/cli-ax.ts +2273 -0
- package/src/internal/cli-bitbucket.ts +438 -0
- package/src/internal/cli-convert-automation.ts +335 -0
- package/src/internal/cli-cursor.ts +282 -0
- package/src/internal/cli-deploy.ts +2486 -0
- package/src/internal/cli-docs.ts +192 -0
- package/src/internal/cli-github.ts +806 -0
- package/src/internal/cli-gitlab.ts +452 -0
- package/src/internal/cli-images.ts +672 -0
- package/src/internal/cli-manifest.ts +56 -0
- package/src/internal/cli-mcp-oauth.ts +395 -0
- package/src/internal/cli-mcp.ts +808 -0
- package/src/internal/cli-skills.ts +99 -0
- package/src/internal/cli-slack-manual.ts +418 -0
- package/src/internal/cli-slack.ts +874 -0
- package/src/internal/cloud-merge.ts +51 -0
- package/src/internal/cloud-turn-cost.ts +159 -0
- package/src/internal/continuation-channel.ts +233 -0
- package/src/internal/continuation-identity.ts +935 -0
- package/src/internal/conversation-mirror.ts +423 -0
- package/src/internal/convert-automation/convert-workflow.ts +1377 -0
- package/src/internal/convert-automation/slug.ts +29 -0
- package/src/internal/convert-automation/types.ts +212 -0
- package/src/internal/cron.ts +76 -0
- package/src/internal/cursor/account-mcp.ts +692 -0
- package/src/internal/cursor/backend-client.ts +978 -0
- package/src/internal/cursor/bound-secret-file.ts +101 -0
- package/src/internal/cursor/credentials.ts +560 -0
- package/src/internal/cursor/cursor-api-transport.ts +65 -0
- package/src/internal/cursor/github-credentials.ts +228 -0
- package/src/internal/cursor/hosted-bound-secrets.ts +71 -0
- package/src/internal/cursor/hosted-deployment-secrets.ts +100 -0
- package/src/internal/cursor/hosted-store-secrets.ts +152 -0
- package/src/internal/cursor/session-token.ts +111 -0
- package/src/internal/cursor/store-api-client.ts +323 -0
- package/src/internal/cursor-account-mcp-auth.ts +303 -0
- package/src/internal/cursor-agent-template.ts +50 -0
- package/src/internal/cursor-deployment-relay.ts +315 -0
- package/src/internal/cursor-event-relay.ts +675 -0
- package/src/internal/cursor-relay-core.ts +563 -0
- package/src/internal/deferred-channel-session.ts +61 -0
- package/src/internal/deploy-client.ts +1504 -0
- package/src/internal/deploy-manifest.ts +361 -0
- package/src/internal/deploy-source.ts +144 -0
- package/src/internal/describe-error.ts +32 -0
- package/src/internal/dev-mode.ts +20 -0
- package/src/internal/discovery/agent-config.ts +288 -0
- package/src/internal/discovery/agent.ts +381 -0
- package/src/internal/discovery/channels.ts +100 -0
- package/src/internal/discovery/connections.ts +505 -0
- package/src/internal/discovery/context.ts +38 -0
- package/src/internal/discovery/cursor-plugin.ts +628 -0
- package/src/internal/discovery/extension-overlay.ts +247 -0
- package/src/internal/discovery/extensions.ts +490 -0
- package/src/internal/discovery/fs.ts +103 -0
- package/src/internal/discovery/hooks.ts +63 -0
- package/src/internal/discovery/info.ts +174 -0
- package/src/internal/discovery/instructions.ts +98 -0
- package/src/internal/discovery/modules.ts +107 -0
- package/src/internal/discovery/names.ts +45 -0
- package/src/internal/discovery/project.ts +319 -0
- package/src/internal/discovery/reminders.ts +65 -0
- package/src/internal/discovery/runtime.ts +158 -0
- package/src/internal/discovery/schedules.ts +120 -0
- package/src/internal/discovery/seed-files.ts +34 -0
- package/src/internal/discovery/services.ts +151 -0
- package/src/internal/discovery/skills.ts +174 -0
- package/src/internal/discovery/subagent.ts +138 -0
- package/src/internal/discovery/tools.ts +170 -0
- package/src/internal/discovery.ts +27 -0
- package/src/internal/distribution.ts +126 -0
- package/src/internal/docs-site.ts +248 -0
- package/src/internal/duration.ts +37 -0
- package/src/internal/env-file.ts +83 -0
- package/src/internal/eval-judge-model.ts +142 -0
- package/src/internal/eval-run-store.ts +585 -0
- package/src/internal/eval-runner.ts +823 -0
- package/src/internal/evals-client.ts +475 -0
- package/src/internal/event-log.ts +342 -0
- package/src/internal/event-mapper.ts +480 -0
- package/src/internal/expected-delivery-failure.ts +34 -0
- package/src/internal/factory-resource-channel.ts +103 -0
- package/src/internal/filesystem/errors.ts +207 -0
- package/src/internal/filesystem/format.ts +253 -0
- package/src/internal/filesystem/merge.ts +192 -0
- package/src/internal/filesystem/nearest.ts +112 -0
- package/src/internal/filesystem/scratch.ts +239 -0
- package/src/internal/filesystem/sdk-schemas.ts +204 -0
- package/src/internal/filesystem/tools.ts +804 -0
- package/src/internal/filesystem/walk.ts +366 -0
- package/src/internal/fixtures/units-server.ts +52 -0
- package/src/internal/framework-file-storage.ts +357 -0
- package/src/internal/framework-storage-selection.ts +156 -0
- package/src/internal/frontmatter.ts +108 -0
- package/src/internal/github-fanout.ts +144 -0
- package/src/internal/guest-network.ts +391 -0
- package/src/internal/handleAgentServeTrigger.ts +286 -0
- package/src/internal/host-files.ts +341 -0
- package/src/internal/host-kv.ts +81 -0
- package/src/internal/host-otel.ts +231 -0
- package/src/internal/host-platforms.ts +170 -0
- package/src/internal/hosted-admission-adapter.ts +47 -0
- package/src/internal/hosted-admission-context.ts +37 -0
- package/src/internal/hosted-catch-protocol.ts +112 -0
- package/src/internal/hosted-catch.ts +179 -0
- package/src/internal/hosted-delivery-protocol.ts +787 -0
- package/src/internal/hosted-delivery.ts +1152 -0
- package/src/internal/hosted-execution-diag.ts +291 -0
- package/src/internal/hosted-execution-flush.ts +136 -0
- package/src/internal/hosted-execution-latency.ts +301 -0
- package/src/internal/hosted-managed-automation.ts +116 -0
- package/src/internal/hosting.ts +107 -0
- package/src/internal/http-channel-define.ts +25 -0
- package/src/internal/http-channel.ts +669 -0
- package/src/internal/http-control-plane-session.ts +110 -0
- package/src/internal/ids.ts +39 -0
- package/src/internal/incoming-headers-to-fetch.ts +45 -0
- package/src/internal/init-interview.ts +360 -0
- package/src/internal/init-project.ts +711 -0
- package/src/internal/init-scaffold.ts +182 -0
- package/src/internal/inline-stdio-define-stub.ts +4 -0
- package/src/internal/inline-stdio-mcp.ts +230 -0
- package/src/internal/install-cursor-skills.ts +601 -0
- package/src/internal/is-record.ts +3 -0
- package/src/internal/json-schema.ts +95 -0
- package/src/internal/legacy-command-guard.ts +54 -0
- package/src/internal/listen-port.ts +85 -0
- package/src/internal/local-control-plane.ts +442 -0
- package/src/internal/local-env.ts +59 -0
- package/src/internal/local-ingress.ts +90 -0
- package/src/internal/log-ring.ts +59 -0
- package/src/internal/logs-client.ts +477 -0
- package/src/internal/map-pool.ts +39 -0
- package/src/internal/mcp-endpoint.ts +747 -0
- package/src/internal/mcp-host.ts +481 -0
- package/src/internal/mcp-oauth-on-demand.ts +337 -0
- package/src/internal/mcp-oauth-persist.ts +118 -0
- package/src/internal/mcp-oauth.ts +549 -0
- package/src/internal/net.ts +29 -0
- package/src/internal/open-browser.ts +41 -0
- package/src/internal/otel/attrs.ts +16 -0
- package/src/internal/otel/collector.ts +937 -0
- package/src/internal/otel/config.ts +287 -0
- package/src/internal/otel/http.ts +122 -0
- package/src/internal/otel/mcp.ts +56 -0
- package/src/internal/otel/propagation.ts +17 -0
- package/src/internal/otel/runtime.ts +441 -0
- package/src/internal/package-inline-stdio.ts +114 -0
- package/src/internal/peer-connections.ts +96 -0
- package/src/internal/platform-schedule-sync.ts +75 -0
- package/src/internal/platform-timers.ts +365 -0
- package/src/internal/playground/static.ts +278 -0
- package/src/internal/playground/toolchain.ts +270 -0
- package/src/internal/playground-proxy.ts +564 -0
- package/src/internal/process-memory-telemetry.ts +334 -0
- package/src/internal/project-layout.ts +31 -0
- package/src/internal/prompt-context.ts +116 -0
- package/src/internal/reminder-control-plane-protocol.ts +482 -0
- package/src/internal/reminder-runner.ts +1559 -0
- package/src/internal/reminder-schedule.ts +104 -0
- package/src/internal/reminder-store.ts +176 -0
- package/src/internal/request-error-status.ts +95 -0
- package/src/internal/request-headers.ts +21 -0
- package/src/internal/resolve-prod-target.ts +334 -0
- package/src/internal/resolved-connections.ts +190 -0
- package/src/internal/router.ts +143 -0
- package/src/internal/run-client.ts +354 -0
- package/src/internal/runtime-dispatch-runner.ts +50 -0
- package/src/internal/sanitize-mcp-tool-name.ts +31 -0
- package/src/internal/schedule-runner.ts +154 -0
- package/src/internal/scm/pr-url.ts +245 -0
- package/src/internal/sdk-runner.ts +948 -0
- package/src/internal/serve-restart.ts +113 -0
- package/src/internal/server.ts +3135 -0
- package/src/internal/session-cost.ts +208 -0
- package/src/internal/session-engine.ts +5602 -0
- package/src/internal/session-links.ts +84 -0
- package/src/internal/session-run-log.ts +535 -0
- package/src/internal/session-store.ts +307 -0
- package/src/internal/session-urls.ts +32 -0
- package/src/internal/sessions-client.ts +239 -0
- package/src/internal/skill-catalog.ts +66 -0
- package/src/internal/skills-sync.ts +55 -0
- package/src/internal/slack-provision-client.ts +544 -0
- package/src/internal/slack-thread-key.ts +11 -0
- package/src/internal/sleep.ts +3 -0
- package/src/internal/start-log-store.ts +164 -0
- package/src/internal/steering.ts +17 -0
- package/src/internal/storage-coordinator.ts +800 -0
- package/src/internal/storage-roles.ts +86 -0
- package/src/internal/storage.ts +1 -0
- package/src/internal/store-api-protocol.ts +205 -0
- package/src/internal/store-mirror.ts +73 -0
- package/src/internal/stream-progress.ts +294 -0
- package/src/internal/terminal-style.ts +133 -0
- package/src/internal/testing/factory-resource-fixtures.ts +84 -0
- package/src/internal/testing/scm-continuation-fixtures.ts +285 -0
- package/src/internal/tool-catalog.ts +104 -0
- package/src/internal/tool-policy.ts +222 -0
- package/src/internal/tool-result.ts +50 -0
- package/src/internal/trajectory.ts +418 -0
- package/src/internal/turn-cost.ts +136 -0
- package/src/internal/turn-governor.ts +145 -0
- package/src/internal/turn-lease-store.ts +144 -0
- package/src/internal/turn-outbox.ts +77 -0
- package/src/internal/update-check.ts +517 -0
- package/src/internal/v2-hosting.ts +52 -0
- package/src/internal/workspace-sync.ts +114 -0
- package/src/internal/workspace.ts +505 -0
- package/src/kv/memory-kv.ts +94 -0
- package/src/kv.ts +18 -0
- package/src/memory.ts +487 -0
- package/src/otel-public.ts +24 -0
- package/src/otel.ts +320 -0
- package/src/prompt.ts +80 -0
- package/src/reminders.ts +65 -0
- package/src/schedules.ts +31 -0
- package/src/skills.ts +27 -0
- package/src/storage-backends/cursor-hosted-v2.ts +204 -0
- package/src/storage-backends/cursor-hosted.ts +388 -0
- package/src/storage-backends/file-kv.ts +276 -0
- package/src/storage-protocol.ts +261 -0
- package/src/storage.ts +408 -0
- package/src/tools.ts +166 -0
- package/src/types.ts +3725 -0
- package/templates/agentic-owners/README.md +76 -0
- package/templates/agentic-owners/bot/agent.ts +8 -0
- package/templates/agentic-owners/bot/channels/github.ts +63 -0
- package/templates/agentic-owners/bot/instructions.md +34 -0
- package/templates/agentic-owners/bot/lib/config.ts +5 -0
- package/templates/agentic-owners/bot/lib/github.ts +217 -0
- package/templates/agentic-owners/bot/lib/owners/README.md +50 -0
- package/templates/agentic-owners/bot/lib/owners/constants.ts +22 -0
- package/templates/agentic-owners/bot/lib/owners/decision.ts +454 -0
- package/templates/agentic-owners/bot/lib/owners/handles.ts +54 -0
- package/templates/agentic-owners/bot/lib/owners/match.ts +219 -0
- package/templates/agentic-owners/bot/lib/owners/matching.ts +78 -0
- package/templates/agentic-owners/bot/lib/owners/parse.ts +565 -0
- package/templates/agentic-owners/bot/lib/owners/policyMode.ts +57 -0
- package/templates/agentic-owners/bot/lib/owners/prompt.ts +64 -0
- package/templates/agentic-owners/bot/lib/owners/prompts/base-review.md +105 -0
- package/templates/agentic-owners/bot/lib/owners/types.ts +50 -0
- package/templates/agentic-owners/bot/lib/review.ts +722 -0
- package/templates/agentic-owners/bot/tools/record_review.ts +62 -0
- package/templates/agentic-owners/evals/evals.config.ts +5 -0
- package/templates/agentic-owners/evals/owners.eval.ts +111 -0
- package/templates/agentic-owners/package.json +20 -0
- package/templates/agentic-owners/tsconfig.json +12 -0
- package/templates/agents-md/README.md +42 -0
- package/templates/agents-md/bot/agent.ts +8 -0
- package/templates/agents-md/bot/channels/github.ts +10 -0
- package/templates/agents-md/bot/channels/queue.ts +28 -0
- package/templates/agents-md/bot/channels/slack.ts +9 -0
- package/templates/agents-md/bot/instructions.md +39 -0
- package/templates/agents-md/bot/lib/collect.ts +415 -0
- package/templates/agents-md/bot/lib/config.ts +8 -0
- package/templates/agents-md/bot/lib/paths.ts +49 -0
- package/templates/agents-md/bot/schedules/weekly.ts +17 -0
- package/templates/agents-md/bot/tools/apply_agents.ts +218 -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/templates/code-wiki/README.md +37 -0
- package/templates/code-wiki/bot/agent.ts +8 -0
- package/templates/code-wiki/bot/channels/github.ts +125 -0
- package/templates/code-wiki/bot/instructions.md +39 -0
- package/templates/code-wiki/bot/lib/repos.ts +5 -0
- package/templates/code-wiki/bot/lib/wiki.ts +22 -0
- package/templates/code-wiki/bot/tools/apply_wiki.ts +210 -0
- package/templates/code-wiki/evals/evals.config.ts +5 -0
- package/templates/code-wiki/evals/wiki.eval.ts +124 -0
- package/templates/code-wiki/init.json +25 -0
- package/templates/code-wiki/package.json +18 -0
- package/templates/code-wiki/tsconfig.json +12 -0
- package/templates/demo/README.md +40 -0
- package/templates/demo/bot/agent.ts +8 -0
- package/templates/demo/bot/channels/github.ts +48 -0
- package/templates/demo/bot/channels/queue.ts +34 -0
- package/templates/demo/bot/instructions.md +8 -0
- package/templates/demo/bot/lib/collect.ts +66 -0
- package/templates/demo/bot/lib/repos.ts +5 -0
- package/templates/demo/bot/tools/record_demo.ts +37 -0
- package/templates/demo/evals/demo.eval.ts +29 -0
- package/templates/demo/evals/evals.config.ts +5 -0
- package/templates/demo/init.json +25 -0
- package/templates/demo/package.json +18 -0
- package/templates/demo/tsconfig.json +12 -0
- package/templates/grokbot-agents/README.md +29 -0
- package/templates/grokbot-agents/bot/agent.ts +6 -0
- package/templates/grokbot-agents/bot/extensions/grokbot.ts +5 -0
- package/templates/grokbot-agents/bot/instructions.md +7 -0
- package/templates/grokbot-agents/evals/evals.config.ts +5 -0
- package/templates/grokbot-agents/evals/grokbot-agents.eval.ts +18 -0
- package/templates/grokbot-agents/init.json +18 -0
- package/templates/grokbot-agents/package.json +17 -0
- package/templates/grokbot-agents/tsconfig.json +12 -0
- package/templates/pr-autofixer/bot/agent.ts +11 -0
- package/templates/pr-autofixer/bot/channels/drive.ts +37 -0
- package/templates/pr-autofixer/bot/channels/github.ts +155 -0
- package/templates/pr-autofixer/bot/channels/slack.ts +16 -0
- package/templates/pr-autofixer/bot/hooks/bind-agent.ts +19 -0
- package/templates/pr-autofixer/bot/instructions.md +83 -0
- package/templates/pr-autofixer/bot/lib/affinity.ts +24 -0
- package/templates/pr-autofixer/bot/lib/drive-schemas.ts +18 -0
- package/templates/pr-autofixer/bot/lib/drive.ts +73 -0
- package/templates/pr-autofixer/bot/lib/merge-watch.ts +116 -0
- package/templates/pr-autofixer/bot/lib/pr-state.ts +40 -0
- package/templates/pr-autofixer/bot/lib/repos.ts +18 -0
- package/templates/pr-autofixer/bot/lib/start-drive.ts +113 -0
- package/templates/pr-autofixer/bot/lib/webhook-buffer.ts +215 -0
- package/templates/pr-autofixer/bot/lib/webhook-routing.ts +231 -0
- package/templates/pr-autofixer/bot/tools/drive_pr.ts +22 -0
- package/templates/pr-autofixer/evals/coordinator.eval.ts +16 -0
- package/templates/pr-autofixer/evals/evals.config.ts +5 -0
- package/templates/pr-autofixer/init.json +25 -0
- package/templates/pr-autofixer/package.json +18 -0
- package/templates/pr-autofixer/tsconfig.json +12 -0
- package/templates/security-help/README.md +40 -0
- package/templates/security-help/bot/agent.ts +17 -0
- package/templates/security-help/bot/channels/github.ts +13 -0
- package/templates/security-help/bot/channels/slack.ts +84 -0
- package/templates/security-help/bot/instructions.md +19 -0
- package/templates/security-help/bot/lib/pr-first-pass.ts +40 -0
- package/templates/security-help/bot/lib/repos.ts +5 -0
- package/templates/security-help/bot/skills/access-request.md +10 -0
- package/templates/security-help/bot/skills/faq.md +31 -0
- package/templates/security-help/bot/skills/security-playbooks.md +12 -0
- package/templates/security-help/bot/skills/security-pr-first-pass.md +12 -0
- package/templates/security-help/evals/evals.config.ts +5 -0
- package/templates/security-help/evals/security-help.eval.ts +51 -0
- package/templates/security-help/init.json +31 -0
- package/templates/security-help/package.json +17 -0
- package/templates/security-help/tsconfig.json +12 -0
- package/templates/security-reviewer/README.md +34 -0
- package/templates/security-reviewer/bot/agent.ts +8 -0
- package/templates/security-reviewer/bot/channels/github.ts +43 -0
- package/templates/security-reviewer/bot/instructions.md +22 -0
- package/templates/security-reviewer/bot/lib/repos.ts +5 -0
- package/templates/security-reviewer/bot/tools/post_findings.ts +68 -0
- package/templates/security-reviewer/evals/evals.config.ts +5 -0
- package/templates/security-reviewer/evals/review.eval.ts +50 -0
- package/templates/security-reviewer/init.json +25 -0
- package/templates/security-reviewer/package.json +18 -0
- package/templates/security-reviewer/tsconfig.json +12 -0
- package/templates/thermo-quality-review/README.md +38 -0
- package/templates/thermo-quality-review/bot/agent.ts +8 -0
- package/templates/thermo-quality-review/bot/channels/github.ts +39 -0
- package/templates/thermo-quality-review/bot/instructions.md +43 -0
- package/templates/thermo-quality-review/bot/tools/post_findings.ts +67 -0
- package/templates/thermo-quality-review/evals/evals.config.ts +5 -0
- package/templates/thermo-quality-review/evals/review.eval.ts +59 -0
- package/templates/thermo-quality-review/package.json +18 -0
- package/templates/thermo-quality-review/tsconfig.json +12 -0
- package/templates/thermo-review/README.md +38 -0
- package/templates/thermo-review/bot/agent.ts +8 -0
- package/templates/thermo-review/bot/channels/github.ts +39 -0
- package/templates/thermo-review/bot/instructions.md +40 -0
- package/templates/thermo-review/bot/tools/post_findings.ts +67 -0
- package/templates/thermo-review/evals/evals.config.ts +5 -0
- package/templates/thermo-review/evals/review.eval.ts +50 -0
- package/templates/thermo-review/package.json +18 -0
- package/templates/thermo-review/tsconfig.json +12 -0
- package/templates/triage/README.md +50 -0
- package/templates/triage/bot/agent.ts +8 -0
- package/templates/triage/bot/channels/queue.ts +40 -0
- package/templates/triage/bot/channels/webhook.ts +44 -0
- package/templates/triage/bot/hooks/memory.ts +3 -0
- package/templates/triage/bot/instructions.md +38 -0
- package/templates/triage/bot/lib/actuate.ts +703 -0
- package/templates/triage/bot/lib/parse-queue-event.ts +166 -0
- package/templates/triage/bot/lib/tracker.ts +5 -0
- package/templates/triage/bot/lib/types.ts +48 -0
- package/templates/triage/bot/lib/verify-webhook.ts +79 -0
- package/templates/triage/bot/skills/triage-rubric.md +44 -0
- package/templates/triage/bot/tools/record_triage.ts +29 -0
- package/templates/triage/evals/evals.config.ts +5 -0
- package/templates/triage/evals/triage.eval.ts +64 -0
- package/templates/triage/overlays/jira/README.md +24 -0
- package/templates/triage/overlays/jira/bot/lib/tracker.ts +8 -0
- package/templates/triage/overlays/jira/bot/mcp-connections/tracker.ts +6 -0
- package/templates/triage/overlays/linear/README.md +24 -0
- package/templates/triage/overlays/linear/bot/lib/tracker.ts +8 -0
- package/templates/triage/overlays/linear/bot/mcp-connections/tracker.ts +6 -0
- package/templates/triage/package.json +18 -0
- package/templates/triage/tsconfig.json +12 -0
|
@@ -0,0 +1,4493 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The session engine owns durable sessions and turn execution. A session is
|
|
3
|
+
* one Cursor agent conversation plus a materialized workspace, an event log,
|
|
4
|
+
* and channel bookkeeping (continuation keys, per-session channel state).
|
|
5
|
+
* Turns are executed by the Cursor SDK runner and their streaming updates
|
|
6
|
+
* are mapped onto the channel event vocabulary and dispatched to channel
|
|
7
|
+
* event handlers and hooks.
|
|
8
|
+
*/
|
|
9
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
10
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
11
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
12
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
13
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
14
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
15
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
19
|
+
var t = {};
|
|
20
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
21
|
+
t[p] = s[p];
|
|
22
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
23
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
24
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
25
|
+
t[p[i]] = s[p[i]];
|
|
26
|
+
}
|
|
27
|
+
return t;
|
|
28
|
+
};
|
|
29
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
30
|
+
import { access, mkdir, rm, writeFile } from "node:fs/promises";
|
|
31
|
+
import { dirname, join } from "node:path";
|
|
32
|
+
import { SLACK_CHANNEL_ID } from "../channels/slack/constants.js";
|
|
33
|
+
import { materializeSlackNudgeCarrier } from "../channels/slack/nudge-carrier.js";
|
|
34
|
+
import { SlackAskDedupeStore, SlackNudgeStore } from "../channels/slack/nudge-store.js";
|
|
35
|
+
import { resolveDedicatedSlackHostBinding } from "../channels/slack/slack-channel.js";
|
|
36
|
+
import { AGENT_SERVE_USER_STORE_FILES_SOURCE_ID, agentStoreKeys, DEPLOYMENT_STORE_MOUNT_PATH, USER_STORE_MOUNT_PATH, } from "../files.js";
|
|
37
|
+
import { cursorHostedFiles, isCursorHostedFilesAvailable, isCursorUserStoreFilesAvailable, } from "../files-backends/cursor-hosted.js";
|
|
38
|
+
import { isFileSystemToolAlias, isMutatingFileSystemTool, localFileSystem, resolveFileSystemTools, resolveHostHarnessToolOptions, tempFs, unionFs, writeWorkspaceFilesToRoot, } from "../filesystem.js";
|
|
39
|
+
import { storageKeysForArchitecture } from "../storage.js";
|
|
40
|
+
import { DEFAULT_MODEL, DIRECT_TOOL_CALL_CHANNEL_ID, isCursorAccountTransport, isHostOauthConnectionTransport, isSymbolicConnectionTransport, } from "../types.js";
|
|
41
|
+
import { resolveAdvertisedTools } from "./advertise-tools.js";
|
|
42
|
+
import { ApprovalGate } from "./approval-gate.js";
|
|
43
|
+
import { ArtifactsStore } from "./artifacts-store.js";
|
|
44
|
+
import { assertTimeArgs, normalizeAsOf, schemaTimeWindowKeys, sessionClock } from "./as-of.js";
|
|
45
|
+
import { SessionAuthError, samePrincipal } from "./auth.js";
|
|
46
|
+
import { mergeChannelStateMutation, mergeChannelStateSkipNull } from "./channel-state.js";
|
|
47
|
+
import { mergeCloudOptions, resolveSessionRuntime } from "./cloud-merge.js";
|
|
48
|
+
import { applyCloudUsageToEvents, cloudSdkAgentIdFromEvents, isUnpricedCompletedTurn, mapSdkTokenUsage, } from "./cloud-turn-cost.js";
|
|
49
|
+
import { applyAgentContinuationDefaults, channelContinuationIdentity, httpContinuationIdentity, } from "./continuation-channel.js";
|
|
50
|
+
import { continuationSource, resolveV2Key, V2EngineError } from "./continuation-identity.js";
|
|
51
|
+
import { describeError, describeErrorCode } from "./describe-error.js";
|
|
52
|
+
import { isDevMode } from "./dev-mode.js";
|
|
53
|
+
import { EventLogStore } from "./event-log.js";
|
|
54
|
+
import { TurnEventMapper } from "./event-mapper.js";
|
|
55
|
+
import { applyWebScratchUpdate, ingestHostAgentToolsScratch, remapScratchPaths, } from "./filesystem/scratch.js";
|
|
56
|
+
import { fileSystemTool, hostFileToolsFromAuthored, rootFileToolsFromAuthored, synthesizeFileSystemTools, } from "./filesystem/tools.js";
|
|
57
|
+
import { selectFrameworkStorageDefinition } from "./framework-storage-selection.js";
|
|
58
|
+
import { createHostFilesApi } from "./host-files.js";
|
|
59
|
+
import { createHostKvApi } from "./host-kv.js";
|
|
60
|
+
import { createHostOtelApi } from "./host-otel.js";
|
|
61
|
+
import { createHostPlatforms, createHostSlackClient, } from "./host-platforms.js";
|
|
62
|
+
import { HostedAdmissionLocalSendError, isDuringHostedAdmission, } from "./hosted-admission-context.js";
|
|
63
|
+
import { hostedSessionResumeKey } from "./hosted-catch-protocol.js";
|
|
64
|
+
import { formatContinuationToken, newContinuationKey, newSessionId, newToolCallId, parseContinuationToken, } from "./ids.js";
|
|
65
|
+
import { validateToolInput } from "./json-schema.js";
|
|
66
|
+
import { isMcpNeedsAuthError, McpHost } from "./mcp-host.js";
|
|
67
|
+
import { McpOauthOnDemandHub } from "./mcp-oauth-on-demand.js";
|
|
68
|
+
import { shouldStartProcessMemoryTelemetry, startProcessMemoryTelemetry, } from "./process-memory-telemetry.js";
|
|
69
|
+
import { buildSessionContextBlock, SCHEDULE_CHANNEL_PREFIX } from "./prompt-context.js";
|
|
70
|
+
import { assertCloudCanHonorAccountServersFilters } from "./resolved-connections.js";
|
|
71
|
+
import { sessionExternalLinks } from "./session-links.js";
|
|
72
|
+
import { SessionStore } from "./session-store.js";
|
|
73
|
+
import { syncSkillsToStore } from "./skills-sync.js";
|
|
74
|
+
import { steeringEnabled } from "./steering.js";
|
|
75
|
+
import { StorageCoordinator, isTurnBoundaryEvent } from "./storage-coordinator.js";
|
|
76
|
+
import { decideToolCall, directCallEffect } from "./tool-policy.js";
|
|
77
|
+
import { normalizeToolResult, toolCallErrorMessage } from "./tool-result.js";
|
|
78
|
+
import { estimateTurnCostFromApi, roundCostUsd } from "./turn-cost.js";
|
|
79
|
+
import { TurnGovernor } from "./turn-governor.js";
|
|
80
|
+
import { TurnLeaseStore } from "./turn-lease-store.js";
|
|
81
|
+
import { TurnOutbox } from "./turn-outbox.js";
|
|
82
|
+
import { resolveAgentV2Hosting } from "./v2-hosting.js";
|
|
83
|
+
import { buildAgentsMdContent, buildCloudMemorySection, buildCloudSessionWorkspaceSection, buildCloudWorkspaceSection, ensureOwnGitRoot, isNestedInGitRepo, materializeWorkspace, SERVER_TOOLS_MCP_SERVER_NAME, } from "./workspace.js";
|
|
84
|
+
import { syncSessionWorkspaceFiles, syncWorkspaceToStore, WorkspaceFileRefusal, wipeSessionWorkspace, } from "./workspace-sync.js";
|
|
85
|
+
export const HTTP_CHANNEL_ID = "http";
|
|
86
|
+
export { mergeChannelStateSkipNull } from "./channel-state.js";
|
|
87
|
+
export { SCHEDULE_CHANNEL_PREFIX } from "./prompt-context.js";
|
|
88
|
+
export class SessionBusyError extends Error {
|
|
89
|
+
constructor(sessionId) {
|
|
90
|
+
super(`Session ${sessionId} already has a running turn; wait for session.waiting before sending a follow-up`);
|
|
91
|
+
this.name = "SessionBusyError";
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Hosted enqueue-only admission could not coalesce into an active turn.
|
|
96
|
+
* Distinct from {@link SessionBusyError}: the caller asked not to start work.
|
|
97
|
+
*/
|
|
98
|
+
export class HostedEnqueueNotActiveError extends Error {
|
|
99
|
+
constructor(sessionId) {
|
|
100
|
+
super(sessionId === undefined
|
|
101
|
+
? "Hosted enqueue requires an active accepting session"
|
|
102
|
+
: `Hosted enqueue requires an active turn on session ${sessionId}`);
|
|
103
|
+
this.name = "HostedEnqueueNotActiveError";
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Defined in auth.js (HTTP/session-store callers import it without this
|
|
107
|
+
// module); re-exported here because this is its historical public home.
|
|
108
|
+
export { SessionAuthError } from "./auth.js";
|
|
109
|
+
export class UnknownChannelError extends Error {
|
|
110
|
+
constructor(channelId) {
|
|
111
|
+
super(`Unknown channel "${channelId}"`);
|
|
112
|
+
this.name = "UnknownChannelError";
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
export class UnknownToolError extends Error {
|
|
116
|
+
constructor(toolName, availableToolNames) {
|
|
117
|
+
super(`Unknown tool "${toolName}"`);
|
|
118
|
+
this.availableToolNames = availableToolNames;
|
|
119
|
+
this.name = "UnknownToolError";
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
export class ToolNotDirectlyCallableError extends Error {
|
|
123
|
+
constructor(toolName) {
|
|
124
|
+
super(`Tool "${toolName}" runs in the agent environment (execution: "agent") and cannot be called directly on the host`);
|
|
125
|
+
this.name = "ToolNotDirectlyCallableError";
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
export class InvalidContinuationTokenError extends Error {
|
|
129
|
+
constructor(detail) {
|
|
130
|
+
super(`Invalid continuation token: ${detail}`);
|
|
131
|
+
this.name = "InvalidContinuationTokenError";
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* A session-bound direct call whose harness cwd cannot be materialized and
|
|
136
|
+
* whose effect is not read: a write tool may rely on ctx.workspaceDir being
|
|
137
|
+
* the real session workspace, so it fails typed instead of silently running
|
|
138
|
+
* in a scratch directory.
|
|
139
|
+
*/
|
|
140
|
+
export class WorkspaceUnavailableError extends Error {
|
|
141
|
+
constructor(toolName, cause) {
|
|
142
|
+
super(`Session workspace unavailable for tool "${toolName}": ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
143
|
+
this.name = "WorkspaceUnavailableError";
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
export class ToolInputError extends Error {
|
|
147
|
+
constructor(args) {
|
|
148
|
+
super(`Invalid input for tool "${args.toolName}": ${args.detail}`);
|
|
149
|
+
this.name = "ToolInputError";
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
export class UnknownSessionError extends Error {
|
|
153
|
+
constructor(sessionId) {
|
|
154
|
+
super(`Unknown session ${sessionId}`);
|
|
155
|
+
this.name = "UnknownSessionError";
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
export class ApprovalNotFoundError extends Error {
|
|
159
|
+
constructor(sessionId, callId) {
|
|
160
|
+
super(`No pending approval for session ${sessionId} call ${callId}`);
|
|
161
|
+
this.name = "ApprovalNotFoundError";
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
export class ApprovalInterruptedError extends Error {
|
|
165
|
+
constructor(sessionId, callId) {
|
|
166
|
+
super(`Approval for session ${sessionId} call ${callId} is no longer live (host may have restarted while the tool was parked)`);
|
|
167
|
+
this.name = "ApprovalInterruptedError";
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
export class AuthNotFoundError extends Error {
|
|
171
|
+
constructor(sessionId, callId) {
|
|
172
|
+
super(`No pending MCP auth for session ${sessionId} call ${callId}`);
|
|
173
|
+
this.name = "AuthNotFoundError";
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
export class AuthInterruptedError extends Error {
|
|
177
|
+
constructor(sessionId, callId) {
|
|
178
|
+
super(`MCP auth for session ${sessionId} call ${callId} is no longer live (host may have restarted while the tool was parked)`);
|
|
179
|
+
this.name = "AuthInterruptedError";
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const mcpAuthAls = new AsyncLocalStorage();
|
|
183
|
+
/** Marker that we `git init`'d this shared harness so scaffold is ours to write. */
|
|
184
|
+
const OWNED_HARNESS = join(".agent-serve", "owned-harness");
|
|
185
|
+
function pathExists(path) {
|
|
186
|
+
return access(path).then(() => true, () => false);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Nested dirs get their own git root so the harness does not ripwalk the
|
|
190
|
+
* enclosing checkout. A real `workspaceDir` checkout (already a git root)
|
|
191
|
+
* is left alone.
|
|
192
|
+
*/
|
|
193
|
+
function ownSharedHarness(dir) {
|
|
194
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
195
|
+
if (yield pathExists(join(dir, OWNED_HARNESS))) {
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
if (!(yield isNestedInGitRepo(dir))) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
yield ensureOwnGitRoot(dir);
|
|
202
|
+
if (yield isNestedInGitRepo(dir)) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
yield mkdir(join(dir, ".agent-serve"), { recursive: true });
|
|
206
|
+
yield writeFile(join(dir, OWNED_HARNESS), "");
|
|
207
|
+
return true;
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
export class SessionEngine {
|
|
211
|
+
constructor(options) {
|
|
212
|
+
var _a, _b, _c, _d, _e;
|
|
213
|
+
/** In-flight lazy restores, deduped per channel + continuation key. */
|
|
214
|
+
this.restoreInFlight = new Map();
|
|
215
|
+
/** In-flight authored files → Agent Store syncs (see syncToCloudStore). */
|
|
216
|
+
this.cloudSyncs = new Map();
|
|
217
|
+
this.channelsById = new Map();
|
|
218
|
+
/** Sessions with a turn being admitted (before the run promise exists). */
|
|
219
|
+
this.pendingTurns = new Set();
|
|
220
|
+
this.activeTurns = new Map();
|
|
221
|
+
/**
|
|
222
|
+
* Sessions whose workspace a turn in this process has materialized. A
|
|
223
|
+
* session is busy from admit — before the governor slot and the turn's
|
|
224
|
+
* own materialize — so a read running alongside a turn consults this,
|
|
225
|
+
* not busy, to know whether the workspace is ready to be left alone.
|
|
226
|
+
*/
|
|
227
|
+
this.materializedWorkspaces = new Set();
|
|
228
|
+
/** Context used by the cloud turn's HTTP MCP callbacks. */
|
|
229
|
+
this.activeToolContexts = new Map();
|
|
230
|
+
/**
|
|
231
|
+
* Tool call ids the host answered instead of running, per session.
|
|
232
|
+
*
|
|
233
|
+
* The mapper emits the result event from the harness's view of a finished
|
|
234
|
+
* call, which cannot tell an answered call from a real one — so the decision
|
|
235
|
+
* point records the id here and the mapper stamps the event from it.
|
|
236
|
+
* Registered only while a turn can invoke a tool.
|
|
237
|
+
*/
|
|
238
|
+
this.stubbedCallIds = new Map();
|
|
239
|
+
/**
|
|
240
|
+
* Delivered-nudge count per session at the last tool boundary. The carrier
|
|
241
|
+
* drains out-of-process, so a growing count is how the engine learns a
|
|
242
|
+
* follow-up reached the agent.
|
|
243
|
+
*/
|
|
244
|
+
this.deliveredNudges = new Map();
|
|
245
|
+
/** Aborts the in-flight turn per session so a follow-up can preempt it. */
|
|
246
|
+
this.turnAborts = new Map();
|
|
247
|
+
/** Serializes turn admission per session (latest follow-up wins). */
|
|
248
|
+
this.admissionChains = new Map();
|
|
249
|
+
/** Live turn snapshot so a coalesced Slack wake can repaint on this process. */
|
|
250
|
+
this.liveTurns = new Map();
|
|
251
|
+
/**
|
|
252
|
+
* Per-session coalesce queue for `admission: "coalesce"` (Slack). Drained
|
|
253
|
+
* into one follow-up turn when the active turn finishes without interrupt.
|
|
254
|
+
*/
|
|
255
|
+
this.coalesceQueues = new Map();
|
|
256
|
+
/**
|
|
257
|
+
* Sessions whose turn lifecycle is draining coalesced follow-ups. Counts as
|
|
258
|
+
* busy so inbound coalesce admits enqueue instead of racing `clearCoalesced`.
|
|
259
|
+
*/
|
|
260
|
+
this.coalesceDrains = new Map();
|
|
261
|
+
this.lastOutcomes = new Map();
|
|
262
|
+
this.updateChains = new Map();
|
|
263
|
+
this.dispatchChains = new Map();
|
|
264
|
+
this.backgroundWork = new Set();
|
|
265
|
+
this.approvals = new ApprovalGate();
|
|
266
|
+
this.auths = new ApprovalGate();
|
|
267
|
+
this.oauthHub = new McpOauthOnDemandHub();
|
|
268
|
+
/** Serve-time resolved connections (see {@link setResolvedConnections}). */
|
|
269
|
+
this.resolvedConnections = [];
|
|
270
|
+
/** Sessions whose completed turns are fully priced (no further backfill). */
|
|
271
|
+
this.cloudCostBackfillDone = new Set();
|
|
272
|
+
/** Deduped in-flight cloud cost backfills, keyed by session id. */
|
|
273
|
+
this.cloudCostBackfill = new Map();
|
|
274
|
+
/** Per-session FileSystem handle cache (not a tree snapshot). */
|
|
275
|
+
this.sessionRoots = new Map();
|
|
276
|
+
/** In-flight factory resolves, so the first turn and a racing callTool share one handle. */
|
|
277
|
+
this.sessionRootInflight = new Map();
|
|
278
|
+
/** Per-turn `unionFs({ "/": authored, "/tmp": tempFs() })`. Dropped at end of turn. */
|
|
279
|
+
this.turnRoots = new Map();
|
|
280
|
+
this.project = options.project;
|
|
281
|
+
applyAgentContinuationDefaults(this.project);
|
|
282
|
+
this.stateRoot = options.stateRoot;
|
|
283
|
+
this.runner = options.runner;
|
|
284
|
+
this.logger = (_a = options.logger) !== null && _a !== void 0 ? _a : (() => { });
|
|
285
|
+
this.getCloudUsage = (_b = options.getCloudUsage) !== null && _b !== void 0 ? _b : defaultGetCloudUsage;
|
|
286
|
+
this.estimateTurnCost = (_c = options.estimateTurnCost) !== null && _c !== void 0 ? _c : estimateTurnCostFromApi;
|
|
287
|
+
this.otel = options.otel;
|
|
288
|
+
this.otelHost = createHostOtelApi({
|
|
289
|
+
agentName: options.project.name,
|
|
290
|
+
meter: options.otelMeter,
|
|
291
|
+
collector: options.otel,
|
|
292
|
+
});
|
|
293
|
+
this.turnGovernor = new TurnGovernor(options.project.agent.concurrency);
|
|
294
|
+
this.sessions = new SessionStore(join(options.stateRoot, "sessions"));
|
|
295
|
+
this.nudgeStore = new SlackNudgeStore(join(options.stateRoot, "slack-nudges"));
|
|
296
|
+
this.askDedupe = new SlackAskDedupeStore(join(options.stateRoot, "slack-asks"));
|
|
297
|
+
this.turnLeases = new TurnLeaseStore(join(options.stateRoot, "turn-leases"));
|
|
298
|
+
this.outbox = new TurnOutbox(join(options.stateRoot, "outbox"));
|
|
299
|
+
this.logs = new EventLogStore(sessionId => this.sessions.eventFilePath(sessionId), (sessionId, error) => this.logger(`[agent-sdk] failed to persist events for ${sessionId}: ${describeError(error)}`));
|
|
300
|
+
// Framework session state (records, event chunks, continuations) is
|
|
301
|
+
// mirrored to whatever this selects; see framework-storage-selection.ts
|
|
302
|
+
// for why the choice does not belong to the agent author.
|
|
303
|
+
const frameworkStorage = selectFrameworkStorageDefinition({
|
|
304
|
+
authored: options.project.storage,
|
|
305
|
+
architecture: options.project.agent.architecture,
|
|
306
|
+
});
|
|
307
|
+
this.storage =
|
|
308
|
+
frameworkStorage === undefined
|
|
309
|
+
? undefined
|
|
310
|
+
: new StorageCoordinator({
|
|
311
|
+
definition: frameworkStorage,
|
|
312
|
+
agentName: options.project.name,
|
|
313
|
+
projectRoot: options.project.rootDir,
|
|
314
|
+
logger: this.logger,
|
|
315
|
+
keys: storageKeysForArchitecture(options.project.agent.architecture),
|
|
316
|
+
});
|
|
317
|
+
this.kvApi = createHostKvApi(Object.assign({ agentName: options.project.name, stateRoot: options.stateRoot }, (this.storage === undefined ? {} : { storage: this.storage })));
|
|
318
|
+
const v2Hosting = resolveAgentV2Hosting({
|
|
319
|
+
architecture: options.project.agent.architecture,
|
|
320
|
+
});
|
|
321
|
+
const managedV2Files = v2Hosting.kind === "cursor-managed"
|
|
322
|
+
? () => cursorHostedFiles({
|
|
323
|
+
resolveStoreSourceId: () => v2Hosting.resolveStoreIds().deploymentStoreSourceId,
|
|
324
|
+
})
|
|
325
|
+
: undefined;
|
|
326
|
+
this.filesApi = createHostFilesApi({
|
|
327
|
+
agentName: options.project.name,
|
|
328
|
+
stateRoot: options.stateRoot,
|
|
329
|
+
sink: managedV2Files === null || managedV2Files === void 0 ? void 0 : managedV2Files(),
|
|
330
|
+
});
|
|
331
|
+
this.artifactsStore = new ArtifactsStore({
|
|
332
|
+
stateRoot: options.stateRoot,
|
|
333
|
+
agentName: options.project.name,
|
|
334
|
+
config: options.project.artifacts,
|
|
335
|
+
sink: managedV2Files === null || managedV2Files === void 0 ? void 0 : managedV2Files(),
|
|
336
|
+
});
|
|
337
|
+
// Peer and Cursor-account transports are symbolic until the serve host
|
|
338
|
+
// resolves them; the host-side MCP registry starts with the concrete
|
|
339
|
+
// connections and gains the rest via setResolvedConnections.
|
|
340
|
+
this.mcpHost = new McpHost([
|
|
341
|
+
...options.project.agent.connections,
|
|
342
|
+
...((_d = options.project.agent.hostConnections) !== null && _d !== void 0 ? _d : []),
|
|
343
|
+
].filter(connection => !isSymbolicConnectionTransport(connection.transport)));
|
|
344
|
+
const basePlatforms = (_e = options.platforms) !== null && _e !== void 0 ? _e : createHostPlatforms();
|
|
345
|
+
const slackBinding = resolveDedicatedSlackHostBinding(options.project.channels);
|
|
346
|
+
this.platforms =
|
|
347
|
+
slackBinding === undefined
|
|
348
|
+
? basePlatforms
|
|
349
|
+
: {
|
|
350
|
+
github: basePlatforms.github,
|
|
351
|
+
slack: createHostSlackClient({
|
|
352
|
+
credentials: slackBinding.credentials,
|
|
353
|
+
envPrefix: slackBinding.envPrefix,
|
|
354
|
+
}),
|
|
355
|
+
};
|
|
356
|
+
for (const channel of options.project.channels) {
|
|
357
|
+
this.channelsById.set(channel.id, channel.definition);
|
|
358
|
+
}
|
|
359
|
+
this.stopMemoryTelemetry = shouldStartProcessMemoryTelemetry()
|
|
360
|
+
? startProcessMemoryTelemetry({
|
|
361
|
+
agentName: options.project.name,
|
|
362
|
+
getEventLogStats: () => this.logs.memoryStats(),
|
|
363
|
+
logger: this.logger,
|
|
364
|
+
meter: options.otelMeter,
|
|
365
|
+
})
|
|
366
|
+
: undefined;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Attach serve-time resolved connections (peers, Cursor account bridge).
|
|
370
|
+
* They join the per-turn SDK `mcpServers` map (loopback URL for local
|
|
371
|
+
* turns, `cloudUrl` for cloud turns) and the host-side registry
|
|
372
|
+
* (`ctx.host.mcp`, always loopback).
|
|
373
|
+
*/
|
|
374
|
+
setResolvedConnections(connections) {
|
|
375
|
+
this.resolvedConnections = connections;
|
|
376
|
+
for (const connection of connections) {
|
|
377
|
+
const authored = this.authoredConnection(connection.name);
|
|
378
|
+
this.mcpHost.addConnection(Object.assign(Object.assign({}, (authored !== null && authored !== void 0 ? authored : { name: connection.name })), { name: connection.name, transport: {
|
|
379
|
+
url: connection.localUrl,
|
|
380
|
+
headers: connection.headers,
|
|
381
|
+
} }));
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
authoredConnection(name) {
|
|
385
|
+
var _a;
|
|
386
|
+
var _b;
|
|
387
|
+
return ((_b = this.project.agent.connections.find(item => item.name === name)) !== null && _b !== void 0 ? _b : (_a = this.project.agent.hostConnections) === null || _a === void 0 ? void 0 : _a.find(item => item.name === name));
|
|
388
|
+
}
|
|
389
|
+
setCloudServerToolsConnection(connection, resolveHeaders) {
|
|
390
|
+
this.cloudServerToolsConnection = connection;
|
|
391
|
+
this.cloudServerToolsHeaders = resolveHeaders;
|
|
392
|
+
}
|
|
393
|
+
currentCloudServerToolsConnection() {
|
|
394
|
+
const connection = this.cloudServerToolsConnection;
|
|
395
|
+
const resolveHeaders = this.cloudServerToolsHeaders;
|
|
396
|
+
if (connection === undefined || !("url" in connection) || resolveHeaders === undefined) {
|
|
397
|
+
return connection;
|
|
398
|
+
}
|
|
399
|
+
const { headers: _headers } = connection, withoutHeaders = __rest(connection, ["headers"]);
|
|
400
|
+
const headers = resolveHeaders();
|
|
401
|
+
return Object.assign(Object.assign({}, withoutHeaders), (headers === undefined ? {} : { headers }));
|
|
402
|
+
}
|
|
403
|
+
setMcpOauthCallbackUrl(url) {
|
|
404
|
+
this.oauthHub.callbackUrl = url;
|
|
405
|
+
this.mcpHost.setOAuthRedirectUrl(url);
|
|
406
|
+
}
|
|
407
|
+
setCursorAccountAuthProbe(probe) {
|
|
408
|
+
this.probeCursorAccount = probe;
|
|
409
|
+
}
|
|
410
|
+
get oauthCallbackHub() {
|
|
411
|
+
return this.oauthHub;
|
|
412
|
+
}
|
|
413
|
+
/** Shared host services (MCP / GitHub / Slack / reminders / evals / kv / files / otel / Slack nudges). */
|
|
414
|
+
get host() {
|
|
415
|
+
return {
|
|
416
|
+
mcp: this.wrapMcp(this.mcpHost),
|
|
417
|
+
github: this.platforms.github,
|
|
418
|
+
slack: this.platforms.slack,
|
|
419
|
+
reminders: this.reminderApi,
|
|
420
|
+
evals: this.evalsApi,
|
|
421
|
+
kv: this.kvApi,
|
|
422
|
+
files: this.filesApi,
|
|
423
|
+
otel: this.otelHost,
|
|
424
|
+
slackNudges: this.slackNudgeHostApi(),
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Host services with `files` and `otel` bound to one session, so a relative
|
|
429
|
+
* path resolves to that session's scope and can never read another
|
|
430
|
+
* session's. Unbound (no session) keeps `host.files` usable for
|
|
431
|
+
* `{ scope: "deployment" }` and fails loudly on session-scoped calls.
|
|
432
|
+
*/
|
|
433
|
+
hostForSession(sessionId) {
|
|
434
|
+
const host = this.host;
|
|
435
|
+
return sessionId === undefined
|
|
436
|
+
? host
|
|
437
|
+
: Object.assign(Object.assign({}, host), { files: this.filesApi.forSession(sessionId), otel: this.otelHost.forSession(sessionId) });
|
|
438
|
+
}
|
|
439
|
+
/** Unbound `ctx.artifacts` facade (channel handlers, scratch tool calls). */
|
|
440
|
+
get artifacts() {
|
|
441
|
+
return this.artifactsApi();
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Build one `ctx.artifacts` facade over the engine-owned artifacts
|
|
445
|
+
* service. With a `binding` (tools, hooks) `tag` auto-fills the bound
|
|
446
|
+
* session (and turn when known) and rejects a conflicting explicit
|
|
447
|
+
* `sessionId`; without one (channel handlers) callers may pass
|
|
448
|
+
* `sessionId` / `turnId` explicitly.
|
|
449
|
+
*/
|
|
450
|
+
artifactsApi(binding) {
|
|
451
|
+
return {
|
|
452
|
+
tag: (input) => __awaiter(this, void 0, void 0, function* () {
|
|
453
|
+
var _a;
|
|
454
|
+
if (binding === undefined) {
|
|
455
|
+
return this.tagArtifact(input);
|
|
456
|
+
}
|
|
457
|
+
if (input.sessionId !== undefined && input.sessionId !== binding.sessionId) {
|
|
458
|
+
throw new Error(`artifacts.tag: this context is bound to session ${binding.sessionId}; cannot tag for ${input.sessionId}`);
|
|
459
|
+
}
|
|
460
|
+
return this.tagArtifact(Object.assign(Object.assign({}, input), { sessionId: binding.sessionId, turnId: (_a = input.turnId) !== null && _a !== void 0 ? _a : binding.turnId }));
|
|
461
|
+
}),
|
|
462
|
+
list: filter => this.artifactsStore.list(filter),
|
|
463
|
+
page: options => this.artifactsStore.page(options),
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Single tag funnel: upsert the row, then — for session-bound tags —
|
|
468
|
+
* append `artifact.tagged` through {@link appendEvent} so persistence
|
|
469
|
+
* flush and hook fan-out are inherited. Session-less tags skip the event.
|
|
470
|
+
* An unknown `sessionId` rejects before the row is written (appending to
|
|
471
|
+
* a session that was never materialized here would mint an orphan local
|
|
472
|
+
* event log, breaking the local-over-sink restore invariant).
|
|
473
|
+
*/
|
|
474
|
+
tagArtifact(input) {
|
|
475
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
476
|
+
var _a, _b;
|
|
477
|
+
if (input.sessionId !== undefined &&
|
|
478
|
+
(yield this.getOrRestoreSession(input.sessionId)) === undefined) {
|
|
479
|
+
throw new UnknownSessionError(input.sessionId);
|
|
480
|
+
}
|
|
481
|
+
const record = yield this.artifactsStore.tag(input);
|
|
482
|
+
if (record.sessionId !== undefined) {
|
|
483
|
+
yield this.appendEvent(record.sessionId, {
|
|
484
|
+
type: "artifact.tagged",
|
|
485
|
+
turnId: record.turnId,
|
|
486
|
+
data: {
|
|
487
|
+
id: record.id,
|
|
488
|
+
kind: record.kind,
|
|
489
|
+
key: record.key,
|
|
490
|
+
title: record.title,
|
|
491
|
+
data: record.data,
|
|
492
|
+
source: record.source,
|
|
493
|
+
},
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
this.logger(`[session] artifact tagged kind=${record.kind} id=${record.id} source=${record.source} session=${(_a = record.sessionId) !== null && _a !== void 0 ? _a : "-"} key=${(_b = record.key) !== null && _b !== void 0 ? _b : "-"}`);
|
|
497
|
+
return record;
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Install the Slack nudge carrier into a local session's harness cwd, so a
|
|
502
|
+
* follow-up that arrives mid-turn reaches the running agent at its next tool
|
|
503
|
+
* boundary instead of waiting for the boundary turn.
|
|
504
|
+
*
|
|
505
|
+
* Slack-only and local-only: the carrier is a Cursor hook in the workspace,
|
|
506
|
+
* and a cloud VM has neither the workspace nor the nudge store.
|
|
507
|
+
*
|
|
508
|
+
* The harness only reads these hooks when the workspace is its own git root;
|
|
509
|
+
* nested in an outer repo it reads that repo's instead and the carrier never
|
|
510
|
+
* runs, leaving follow-ups to drain at the turn boundary as before.
|
|
511
|
+
*/
|
|
512
|
+
materializeNudgeCarrier(record, harnessCwd) {
|
|
513
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
514
|
+
if (record.channelId !== SLACK_CHANNEL_ID ||
|
|
515
|
+
record.continuationKey === null ||
|
|
516
|
+
record.continuationKey === "" ||
|
|
517
|
+
// Borrowed: a PR worktree, or a checkout shared by every session. Hooks
|
|
518
|
+
// written there dirty a tree this session did not create, and a shared
|
|
519
|
+
// cwd holds one continuation pointer for all of them — the carrier would
|
|
520
|
+
// inject one thread's follow-ups into another thread's turn. Nothing is
|
|
521
|
+
// lost: a borrowed checkout is its own git root, so the harness reads its
|
|
522
|
+
// hooks rather than these, and follow-ups drain at the turn boundary.
|
|
523
|
+
this.hasBorrowedWorkspace(record)) {
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
if (steeringEnabled()) {
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
try {
|
|
530
|
+
yield materializeSlackNudgeCarrier({
|
|
531
|
+
harnessCwd,
|
|
532
|
+
nudgeRootDir: join(this.stateRoot, "slack-nudges"),
|
|
533
|
+
continuationKey: record.continuationKey,
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
catch (error) {
|
|
537
|
+
// Best-effort: without the carrier, follow-ups still drain at the turn
|
|
538
|
+
// boundary — one turn later, never lost.
|
|
539
|
+
this.logger(`[session] slack nudge carrier not installed: ${describeError(error)}`);
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Report a mid-turn follow-up reaching the agent, as `nudge.delivered`.
|
|
545
|
+
*
|
|
546
|
+
* The workspace carrier drains `pending/` into `delivered/` out of process,
|
|
547
|
+
* so the engine watches the delivered count grow rather than the turn's own
|
|
548
|
+
* events. Counting rather than edge-detecting on "still queued" matters:
|
|
549
|
+
* the carrier runs at the same tool boundary this check does, so a nudge
|
|
550
|
+
* queued and drained between two readings is never seen as pending.
|
|
551
|
+
*
|
|
552
|
+
* Channels need this to move a live progress message below the follow-up —
|
|
553
|
+
* Slack cannot reorder messages.
|
|
554
|
+
*/
|
|
555
|
+
emitDeliveredNudges(record, emit) {
|
|
556
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
557
|
+
const delivered = yield this.readDeliveredNudges(record);
|
|
558
|
+
if (delivered === undefined) {
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
const previous = this.deliveredNudges.get(record.sessionId);
|
|
562
|
+
this.deliveredNudges.set(record.sessionId, delivered);
|
|
563
|
+
if (previous !== undefined && delivered > previous) {
|
|
564
|
+
emit({ type: "nudge.delivered", data: {} });
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Record the thread's delivered count before the turn runs. Earlier turns
|
|
570
|
+
* leave their own records behind, so only growth from here is this turn's.
|
|
571
|
+
*/
|
|
572
|
+
baselineDeliveredNudges(record) {
|
|
573
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
574
|
+
const delivered = yield this.readDeliveredNudges(record);
|
|
575
|
+
if (delivered !== undefined) {
|
|
576
|
+
this.deliveredNudges.set(record.sessionId, delivered);
|
|
577
|
+
}
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
readDeliveredNudges(record) {
|
|
581
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
582
|
+
const continuationKey = record.continuationKey;
|
|
583
|
+
// Same gate as the carrier: only Slack queues follow-ups mid-turn, and
|
|
584
|
+
// this runs on every tool boundary of every turn.
|
|
585
|
+
if (record.channelId !== SLACK_CHANNEL_ID ||
|
|
586
|
+
continuationKey === null ||
|
|
587
|
+
continuationKey === "") {
|
|
588
|
+
return undefined;
|
|
589
|
+
}
|
|
590
|
+
try {
|
|
591
|
+
return yield this.nudgeStore.deliveredCount(continuationKey);
|
|
592
|
+
}
|
|
593
|
+
catch (_a) {
|
|
594
|
+
// A store read must never break the turn it is observing.
|
|
595
|
+
return undefined;
|
|
596
|
+
}
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
slackNudgeHostApi() {
|
|
600
|
+
return {
|
|
601
|
+
append: input => this.nudgeStore.append(input),
|
|
602
|
+
drainFormatted: continuationKey => this.nudgeStore.drainFormatted(continuationKey),
|
|
603
|
+
clear: continuationKey => this.nudgeStore.clear(continuationKey),
|
|
604
|
+
hasUndelivered: continuationKey => this.nudgeStore.hasUndelivered(continuationKey),
|
|
605
|
+
isDelivered: (continuationKey, sourceTs) => this.nudgeStore.isDelivered(continuationKey, sourceTs),
|
|
606
|
+
tryClaimAsk: (channelId, messageTs) => this.askDedupe.tryClaim(channelId, messageTs),
|
|
607
|
+
releaseAsk: (channelId, messageTs) => this.askDedupe.release(channelId, messageTs),
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
/** Attach the serve-host reminder API (called from startServer). */
|
|
611
|
+
setReminders(api) {
|
|
612
|
+
this.reminderApi = api;
|
|
613
|
+
}
|
|
614
|
+
/** Attach the serve-host playground evals API (called from startServer). */
|
|
615
|
+
setEvals(api) {
|
|
616
|
+
this.evalsApi = api;
|
|
617
|
+
}
|
|
618
|
+
// ==========================================================================
|
|
619
|
+
// Sending messages
|
|
620
|
+
// ==========================================================================
|
|
621
|
+
/**
|
|
622
|
+
* Start or resume a session on a channel. When `continuationToken`
|
|
623
|
+
* addresses an existing session this is a follow-up turn; otherwise a new
|
|
624
|
+
* session is created (keyed by the provided token, or a fresh one).
|
|
625
|
+
*
|
|
626
|
+
* v2 (`/v2` selectors) refuses `continuationToken` and looks
|
|
627
|
+
* up `(agentId, cKey)` from a mailbox stamp or structured claim.
|
|
628
|
+
*/
|
|
629
|
+
send(channelId_1, message_1) {
|
|
630
|
+
return __awaiter(this, arguments, void 0, function* (channelId, message, options = {}) {
|
|
631
|
+
var _a, _b;
|
|
632
|
+
var _c, _d, _e, _f, _g, _h;
|
|
633
|
+
if (isDuringHostedAdmission()) {
|
|
634
|
+
// Only sends that bypassed the admission capture stub (typically a
|
|
635
|
+
// startup-captured `onStart` send) reach the engine inside the
|
|
636
|
+
// admission window; running one would leak an invisible local session
|
|
637
|
+
// on a sessionless pod.
|
|
638
|
+
throw new HostedAdmissionLocalSendError();
|
|
639
|
+
}
|
|
640
|
+
if (options.clearCloudOverride === true && options.cloud !== undefined) {
|
|
641
|
+
throw new Error("send options cloud and clearCloudOverride cannot be combined");
|
|
642
|
+
}
|
|
643
|
+
const identity = channelId === HTTP_CHANNEL_ID
|
|
644
|
+
? httpContinuationIdentity(this.project.httpChannel)
|
|
645
|
+
: channelContinuationIdentity(this.channelsById.get(channelId));
|
|
646
|
+
let sendOptions = options;
|
|
647
|
+
let skipContinuationLookup = false;
|
|
648
|
+
if (continuationSource(identity) === "control-plane") {
|
|
649
|
+
if (options.continuationToken !== undefined) {
|
|
650
|
+
throw new V2EngineError("v2: do not pass continuationToken; use mailboxContinuation or claim");
|
|
651
|
+
}
|
|
652
|
+
const cKey = resolveV2Key(identity, options);
|
|
653
|
+
sendOptions = Object.assign(Object.assign({}, options), { continuationToken: cKey });
|
|
654
|
+
skipContinuationLookup = cKey.startsWith("evt:");
|
|
655
|
+
}
|
|
656
|
+
const auth = (_c = sendOptions.auth) !== null && _c !== void 0 ? _c : null;
|
|
657
|
+
let existing = skipContinuationLookup || sendOptions.continuationToken === undefined
|
|
658
|
+
? undefined
|
|
659
|
+
: yield this.sessions.findByContinuation(channelId, sendOptions.continuationToken);
|
|
660
|
+
if (existing === undefined &&
|
|
661
|
+
!skipContinuationLookup &&
|
|
662
|
+
sendOptions.continuationToken !== undefined) {
|
|
663
|
+
// Local miss: the session may live only in the storage sink (it
|
|
664
|
+
// fell outside the startup restore window, or restore is lazy-only).
|
|
665
|
+
existing = yield this.restoreSessionByContinuation(channelId, sendOptions.continuationToken);
|
|
666
|
+
}
|
|
667
|
+
if (existing === undefined && sendOptions.hostedSessionId !== undefined) {
|
|
668
|
+
const byId = yield this.getOrRestoreSession(sendOptions.hostedSessionId);
|
|
669
|
+
if (byId !== undefined) {
|
|
670
|
+
// evt: skips the mailbox index; same hostedSessionId is a Temporal
|
|
671
|
+
// retry. cont:session:<id> is the explicit CP resume (keep the
|
|
672
|
+
// original key so a PR lane stays addressable by its webhooks).
|
|
673
|
+
if (byId.continuationKey !== sendOptions.continuationToken &&
|
|
674
|
+
((_a = sendOptions.mailboxContinuation) === null || _a === void 0 ? void 0 : _a.key) !==
|
|
675
|
+
hostedSessionResumeKey(sendOptions.hostedSessionId)) {
|
|
676
|
+
throw new V2EngineError(`v2: hosted session id ${sendOptions.hostedSessionId} already belongs to another mailbox`);
|
|
677
|
+
}
|
|
678
|
+
existing = byId;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
if (existing !== undefined) {
|
|
682
|
+
if (sendOptions.hostedSessionId !== undefined &&
|
|
683
|
+
existing.sessionId !== sendOptions.hostedSessionId) {
|
|
684
|
+
throw new V2EngineError(`v2: continuation belongs to session ${existing.sessionId}, not ${sendOptions.hostedSessionId}`);
|
|
685
|
+
}
|
|
686
|
+
if (!samePrincipal(existing.auth, auth)) {
|
|
687
|
+
throw new SessionAuthError(existing.sessionId);
|
|
688
|
+
}
|
|
689
|
+
// Refresh harness cwd when the caller supplies a newer worktree (e.g. PR
|
|
690
|
+
// synchronize). Leave the durable continuation key unchanged.
|
|
691
|
+
let record = existing;
|
|
692
|
+
const enqueueOnly = sendOptions.hostedEnqueueOnly === true;
|
|
693
|
+
if (!enqueueOnly) {
|
|
694
|
+
const refreshWorkspace = sendOptions.workspaceDir !== undefined &&
|
|
695
|
+
sendOptions.workspaceDir !== existing.workspaceOverride;
|
|
696
|
+
if (refreshWorkspace ||
|
|
697
|
+
sendOptions.workspaceFiles !== undefined ||
|
|
698
|
+
sendOptions.images !== undefined) {
|
|
699
|
+
record = yield this.updateRecord(existing.sessionId, current => (Object.assign(Object.assign({}, current), { workspaceOverride: refreshWorkspace
|
|
700
|
+
? sendOptions.workspaceDir
|
|
701
|
+
: current.workspaceOverride, pendingWorkspaceFiles: mergePendingWorkspaceFiles(current.pendingWorkspaceFiles, sendOptions.workspaceFiles), pendingImages: mergePendingImages(current.pendingImages, sendOptions.images) })));
|
|
702
|
+
}
|
|
703
|
+
// A caller can explicitly return a continuation to the agent's
|
|
704
|
+
// configured runtime. Omission remains sticky for compatibility:
|
|
705
|
+
// follow-ups that do not know the original attach keep it.
|
|
706
|
+
if (sendOptions.clearCloudOverride === true && record.cloudOverride !== undefined) {
|
|
707
|
+
record = yield this.updateRecord(record.sessionId, current => (Object.assign(Object.assign({}, current), { cloudOverride: undefined })));
|
|
708
|
+
}
|
|
709
|
+
else if (sendOptions.cloud !== undefined &&
|
|
710
|
+
JSON.stringify(sendOptions.cloud) !== JSON.stringify(record.cloudOverride)) {
|
|
711
|
+
record = yield this.updateRecord(record.sessionId, current => (Object.assign(Object.assign({}, current), { cloudOverride: sendOptions.cloud })));
|
|
712
|
+
}
|
|
713
|
+
// Refresh wake-derived channel state (GitHub headSha / refs) without
|
|
714
|
+
// wiping sticky fields. Null patch values are skipped.
|
|
715
|
+
if (sendOptions.refreshState !== undefined) {
|
|
716
|
+
record = yield this.updateRecord(record.sessionId, current => (Object.assign(Object.assign({}, current), { channelState: mergeChannelStateSkipNull(current.channelState, sendOptions.refreshState) })));
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
const since = (yield this.logs.get(record.sessionId)).count;
|
|
720
|
+
const coalesced = yield this.startTurn(record.sessionId, message, Object.assign(Object.assign({ admission: enqueueOnly ? "coalesce" : ((_d = sendOptions.admission) !== null && _d !== void 0 ? _d : "preempt") }, (sendOptions.coalesceSourceTs === undefined
|
|
721
|
+
? {}
|
|
722
|
+
: { sourceTs: sendOptions.coalesceSourceTs })), (enqueueOnly ? { hostedEnqueueOnly: true } : {})));
|
|
723
|
+
if (enqueueOnly && !coalesced) {
|
|
724
|
+
throw new HostedEnqueueNotActiveError(record.sessionId);
|
|
725
|
+
}
|
|
726
|
+
if (!enqueueOnly && coalesced && sendOptions.refreshState !== undefined) {
|
|
727
|
+
yield this.repaintOnCoalescedRefresh(record.sessionId, sendOptions.refreshState);
|
|
728
|
+
}
|
|
729
|
+
return this.channelSession(record.sessionId, record, false, coalesced, since);
|
|
730
|
+
}
|
|
731
|
+
if (sendOptions.hostedEnqueueOnly === true) {
|
|
732
|
+
throw new HostedEnqueueNotActiveError(sendOptions.hostedSessionId);
|
|
733
|
+
}
|
|
734
|
+
const now = new Date().toISOString();
|
|
735
|
+
const channelState = this.initialChannelState(channelId, sendOptions.state);
|
|
736
|
+
const purpose = sendOptions.purpose === "eval" ? "eval" : "live";
|
|
737
|
+
const frozenAsOf = normalizeAsOf(sendOptions.asOf);
|
|
738
|
+
assertTimeArgs(this.project.agent.tools, frozenAsOf);
|
|
739
|
+
const record = {
|
|
740
|
+
sessionId: (_e = sendOptions.hostedSessionId) !== null && _e !== void 0 ? _e : newSessionId(),
|
|
741
|
+
channelId,
|
|
742
|
+
mode: (_f = sendOptions.mode) !== null && _f !== void 0 ? _f : "chat",
|
|
743
|
+
purpose,
|
|
744
|
+
dryRun: sendOptions.dryRun === true ? true : undefined,
|
|
745
|
+
asOf: frozenAsOf,
|
|
746
|
+
continuationKey: (_g = sendOptions.continuationToken) !== null && _g !== void 0 ? _g : newContinuationKey(),
|
|
747
|
+
auth,
|
|
748
|
+
title: sendOptions.title,
|
|
749
|
+
channelState,
|
|
750
|
+
workspaceOverride: sendOptions.workspaceDir,
|
|
751
|
+
pendingWorkspaceFiles: sendOptions.workspaceFiles,
|
|
752
|
+
pendingImages: sendOptions.images,
|
|
753
|
+
cloudOverride: sendOptions.cloud,
|
|
754
|
+
sdkAgentId: sendOptions.sdkAgentId,
|
|
755
|
+
turnCount: 0,
|
|
756
|
+
createdAt: now,
|
|
757
|
+
updatedAt: now,
|
|
758
|
+
};
|
|
759
|
+
yield this.sessions.save(record);
|
|
760
|
+
(_b = this.storage) === null || _b === void 0 ? void 0 : _b.sessionRecord(record);
|
|
761
|
+
yield this.appendEvent(record.sessionId, {
|
|
762
|
+
type: "session.started",
|
|
763
|
+
data: {
|
|
764
|
+
channelId,
|
|
765
|
+
dryRun: record.dryRun === true ? true : undefined,
|
|
766
|
+
},
|
|
767
|
+
});
|
|
768
|
+
const since = (yield this.logs.get(record.sessionId)).count;
|
|
769
|
+
yield this.startTurn(record.sessionId, message, Object.assign({ admission: (_h = sendOptions.admission) !== null && _h !== void 0 ? _h : "preempt" }, (sendOptions.coalesceSourceTs === undefined
|
|
770
|
+
? {}
|
|
771
|
+
: { sourceTs: sendOptions.coalesceSourceTs })));
|
|
772
|
+
return this.channelSession(record.sessionId, record, true, false, since);
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* Atomically validate and rotate an HTTP follow-up credential, then admit
|
|
777
|
+
* the message to its exact session without changing the owning channel key.
|
|
778
|
+
*/
|
|
779
|
+
followUpHttp(sessionId, token, message) {
|
|
780
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
781
|
+
const next = newContinuationKey();
|
|
782
|
+
const stale = new Error("stale HTTP continuation");
|
|
783
|
+
let previous;
|
|
784
|
+
try {
|
|
785
|
+
yield this.updateRecord(sessionId, record => {
|
|
786
|
+
var _a;
|
|
787
|
+
const expectedChannel = record.httpContinuationKey === undefined ? record.channelId : HTTP_CHANNEL_ID;
|
|
788
|
+
const expectedKey = (_a = record.httpContinuationKey) !== null && _a !== void 0 ? _a : record.continuationKey;
|
|
789
|
+
if (token.channelId !== expectedChannel || token.key !== expectedKey) {
|
|
790
|
+
throw stale;
|
|
791
|
+
}
|
|
792
|
+
previous = record.httpContinuationKey;
|
|
793
|
+
return Object.assign(Object.assign({}, record), { httpContinuationKey: next });
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
catch (error) {
|
|
797
|
+
if (error === stale) {
|
|
798
|
+
return undefined;
|
|
799
|
+
}
|
|
800
|
+
throw error;
|
|
801
|
+
}
|
|
802
|
+
try {
|
|
803
|
+
yield this.startTurn(sessionId, message);
|
|
804
|
+
return next;
|
|
805
|
+
}
|
|
806
|
+
catch (error) {
|
|
807
|
+
yield this.updateRecord(sessionId, record => record.httpContinuationKey === next ? Object.assign(Object.assign({}, record), { httpContinuationKey: previous }) : record);
|
|
808
|
+
throw error;
|
|
809
|
+
}
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
/** Whether a continuation token already maps to a session on this channel. */
|
|
813
|
+
hasContinuationSession(channelId, continuationToken) {
|
|
814
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
815
|
+
let existing = yield this.sessions.findByContinuation(channelId, continuationToken);
|
|
816
|
+
if (existing === undefined) {
|
|
817
|
+
existing = yield this.restoreSessionByContinuation(channelId, continuationToken);
|
|
818
|
+
}
|
|
819
|
+
return existing !== undefined;
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
/** Look up a session as a channel-scoped handle. */
|
|
823
|
+
getChannelSession(channelId, sessionId) {
|
|
824
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
825
|
+
const record = yield this.sessions.get(sessionId);
|
|
826
|
+
if (record === undefined || record.channelId !== channelId) {
|
|
827
|
+
return null;
|
|
828
|
+
}
|
|
829
|
+
return this.channelSession(sessionId, record, false);
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
/** Cross-channel hand-off (schedules and channel route handlers). */
|
|
833
|
+
receive(channel, input) {
|
|
834
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
835
|
+
const channelId = this.resolveChannelId(channel);
|
|
836
|
+
const definition = this.channelsById.get(channelId);
|
|
837
|
+
if ((definition === null || definition === void 0 ? void 0 : definition.receive) !== undefined) {
|
|
838
|
+
let lastCreated;
|
|
839
|
+
const send = (message_1, ...args_1) => __awaiter(this, [message_1, ...args_1], void 0, function* (message, options = {}) {
|
|
840
|
+
const session = yield this.send(channelId, message, options);
|
|
841
|
+
lastCreated = session;
|
|
842
|
+
return session;
|
|
843
|
+
});
|
|
844
|
+
const returned = yield definition.receive(input, {
|
|
845
|
+
send,
|
|
846
|
+
artifacts: this.artifactsApi(),
|
|
847
|
+
});
|
|
848
|
+
const session = returned !== null && returned !== void 0 ? returned : lastCreated;
|
|
849
|
+
if (session === undefined) {
|
|
850
|
+
throw new Error(`Channel "${channelId}" receive hook did not start a session`);
|
|
851
|
+
}
|
|
852
|
+
return session;
|
|
853
|
+
}
|
|
854
|
+
return this.send(channelId, input.message, {
|
|
855
|
+
auth: input.auth,
|
|
856
|
+
});
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
/** Fire-and-forget task session (schedules' markdown form). */
|
|
860
|
+
runTask(args) {
|
|
861
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
862
|
+
const session = yield this.send(args.channelId, args.prompt, Object.assign({ auth: args.auth, mode: "task" }, (args.title === undefined ? {} : { title: args.title })));
|
|
863
|
+
return { sessionId: session.id, done: this.waitForCompletion(session.id) };
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
// ==========================================================================
|
|
867
|
+
// Turn execution
|
|
868
|
+
// ==========================================================================
|
|
869
|
+
/**
|
|
870
|
+
* Repaint on the follow-up wake without retargeting the live turn.
|
|
871
|
+
* Persist already took the refresh; in-flight writes stay on the carrier ts.
|
|
872
|
+
*/
|
|
873
|
+
repaintOnCoalescedRefresh(sessionId, patch) {
|
|
874
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
875
|
+
var _a;
|
|
876
|
+
var _b;
|
|
877
|
+
const live = this.liveTurns.get(sessionId);
|
|
878
|
+
if (live === undefined || patch === undefined) {
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
const repaintState = (_b = mergeChannelStateSkipNull(live.channelState, patch)) !== null && _b !== void 0 ? _b : live.channelState;
|
|
882
|
+
const record = yield this.sessions.get(sessionId);
|
|
883
|
+
if (record === undefined) {
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
const handler = (_a = this.channelEventHandlersFor(record.channelId)) === null || _a === void 0 ? void 0 : _a["message.received"];
|
|
887
|
+
if (handler === undefined) {
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
yield handler({
|
|
891
|
+
type: "message.received",
|
|
892
|
+
data: { text: "" },
|
|
893
|
+
index: 0,
|
|
894
|
+
sessionId,
|
|
895
|
+
at: new Date().toISOString(),
|
|
896
|
+
}, {
|
|
897
|
+
channelId: record.channelId,
|
|
898
|
+
state: repaintState,
|
|
899
|
+
continuationToken: record.continuationKey,
|
|
900
|
+
setContinuationToken: (token) => this.setContinuationToken(sessionId, token),
|
|
901
|
+
}, {
|
|
902
|
+
session: live.session,
|
|
903
|
+
host: this.hostForSession(sessionId),
|
|
904
|
+
artifacts: this.artifactsApi({ sessionId }),
|
|
905
|
+
});
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Admit a turn for `sessionId`.
|
|
910
|
+
*
|
|
911
|
+
* Default (`preempt`): a follow-up cancels the running turn and starts
|
|
912
|
+
* immediately (HTTP / MCP). With `admission: "coalesce"` (Slack), a busy
|
|
913
|
+
* session enqueues the message and returns without interrupting; the queue
|
|
914
|
+
* drains into one follow-up when the active turn finishes.
|
|
915
|
+
*
|
|
916
|
+
* Resolves to `true` when the message was coalesced (enqueued), `false`
|
|
917
|
+
* when a turn was started.
|
|
918
|
+
*/
|
|
919
|
+
startTurn(sessionId, message, options = {}) {
|
|
920
|
+
var _a;
|
|
921
|
+
const prior = (_a = this.admissionChains.get(sessionId)) !== null && _a !== void 0 ? _a : Promise.resolve();
|
|
922
|
+
const admitted = prior
|
|
923
|
+
.catch(() => { })
|
|
924
|
+
.then(() => __awaiter(this, void 0, void 0, function* () {
|
|
925
|
+
// Another server holds the turn: hand the message to it and coalesce.
|
|
926
|
+
// Enqueue-only must not report coalesced success without a local
|
|
927
|
+
// enqueue — the outbox drain may never run on this process.
|
|
928
|
+
if (yield this.turnLeases.runningElsewhere(sessionId)) {
|
|
929
|
+
if (options.hostedEnqueueOnly === true) {
|
|
930
|
+
throw new HostedEnqueueNotActiveError(sessionId);
|
|
931
|
+
}
|
|
932
|
+
yield this.outbox.post(sessionId, { message, options });
|
|
933
|
+
return true;
|
|
934
|
+
}
|
|
935
|
+
return this.admitTurn(sessionId, message, options);
|
|
936
|
+
}));
|
|
937
|
+
this.admissionChains.set(sessionId, admitted.catch(() => { }));
|
|
938
|
+
return admitted;
|
|
939
|
+
}
|
|
940
|
+
/** Admit what other servers handed to this turn's holder. Never awaited by the lifecycle. */
|
|
941
|
+
drainOutbox(sessionId) {
|
|
942
|
+
void this.outbox
|
|
943
|
+
.takeAll(sessionId)
|
|
944
|
+
.then(items => {
|
|
945
|
+
for (const item of items) {
|
|
946
|
+
void this.startTurn(sessionId, item.message, item.options).catch(() => { });
|
|
947
|
+
}
|
|
948
|
+
})
|
|
949
|
+
.catch(() => { });
|
|
950
|
+
}
|
|
951
|
+
enqueueCoalesced(sessionId, item) {
|
|
952
|
+
var _a;
|
|
953
|
+
const queue = (_a = this.coalesceQueues.get(sessionId)) !== null && _a !== void 0 ? _a : [];
|
|
954
|
+
if (item.sourceTs !== undefined && queue.some(entry => entry.sourceTs === item.sourceTs)) {
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
queue.push(item);
|
|
958
|
+
this.coalesceQueues.set(sessionId, queue);
|
|
959
|
+
}
|
|
960
|
+
clearCoalesced(sessionId) {
|
|
961
|
+
this.coalesceQueues.delete(sessionId);
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Drop queued follow-ups the nudge carrier already carried into the turn
|
|
965
|
+
* that just finished — the contract {@link SendMessageOptions.coalesceSourceTs}
|
|
966
|
+
* describes. Re-asking them would answer the same message twice.
|
|
967
|
+
*
|
|
968
|
+
* Items with no `sourceTs` cannot be matched and are kept.
|
|
969
|
+
*/
|
|
970
|
+
withoutNudgedFollowups(sessionId, queued) {
|
|
971
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
972
|
+
const record = yield this.sessions.get(sessionId);
|
|
973
|
+
const continuationKey = record === null || record === void 0 ? void 0 : record.continuationKey;
|
|
974
|
+
if (continuationKey === undefined || continuationKey === null) {
|
|
975
|
+
return [...queued];
|
|
976
|
+
}
|
|
977
|
+
const kept = [];
|
|
978
|
+
for (const item of queued) {
|
|
979
|
+
if (item.sourceTs === undefined) {
|
|
980
|
+
kept.push(item);
|
|
981
|
+
continue;
|
|
982
|
+
}
|
|
983
|
+
try {
|
|
984
|
+
if (!(yield this.nudgeStore.isDelivered(continuationKey, item.sourceTs))) {
|
|
985
|
+
kept.push(item);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
catch (_a) {
|
|
989
|
+
// A store read failure must not swallow the ask.
|
|
990
|
+
kept.push(item);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
return kept;
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
/** Mark drained follow-ups delivered so the next turn's carrier skips them. */
|
|
997
|
+
settleDrainedFollowups(sessionId, items) {
|
|
998
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
999
|
+
const record = yield this.sessions.get(sessionId);
|
|
1000
|
+
const continuationKey = record === null || record === void 0 ? void 0 : record.continuationKey;
|
|
1001
|
+
if (continuationKey === undefined || continuationKey === null) {
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
1004
|
+
for (const item of items) {
|
|
1005
|
+
if (item.sourceTs === undefined) {
|
|
1006
|
+
continue;
|
|
1007
|
+
}
|
|
1008
|
+
try {
|
|
1009
|
+
yield this.nudgeStore.markDelivered(continuationKey, item.sourceTs);
|
|
1010
|
+
}
|
|
1011
|
+
catch (error) {
|
|
1012
|
+
// Worst case the carrier re-injects it; the ask still gets answered.
|
|
1013
|
+
this.logger(`[session] nudge markDelivered failed for ${sessionId}: ${describeError(error)}`);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
formatCoalescedFollowups(items) {
|
|
1019
|
+
if (items.length === 1) {
|
|
1020
|
+
return items[0].message;
|
|
1021
|
+
}
|
|
1022
|
+
const parts = ["<slack_followups>", `${items.length} asks arrived while you were busy.`, ""];
|
|
1023
|
+
for (let i = 0; i < items.length; i++) {
|
|
1024
|
+
parts.push(`### Ask ${i + 1}`);
|
|
1025
|
+
parts.push(items[i].message);
|
|
1026
|
+
parts.push("");
|
|
1027
|
+
}
|
|
1028
|
+
parts.push("</slack_followups>");
|
|
1029
|
+
return parts.join("\n").trimEnd();
|
|
1030
|
+
}
|
|
1031
|
+
drainCoalescedFollowups(sessionId) {
|
|
1032
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1033
|
+
var _a;
|
|
1034
|
+
const queued = (_a = this.coalesceQueues.get(sessionId)) !== null && _a !== void 0 ? _a : [];
|
|
1035
|
+
if (queued.length === 0) {
|
|
1036
|
+
return;
|
|
1037
|
+
}
|
|
1038
|
+
this.coalesceQueues.delete(sessionId);
|
|
1039
|
+
const pending = yield this.withoutNudgedFollowups(sessionId, queued);
|
|
1040
|
+
if (pending.length === 0) {
|
|
1041
|
+
this.logger(`[session] coalesce drain skipped; all follow-ups already nudged ${sessionId}`);
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
// These become the next turn's prompt, so settle them in the nudge store
|
|
1045
|
+
// first: a surviving pending record would be injected by that turn's own
|
|
1046
|
+
// carrier, re-asking what the turn is already about.
|
|
1047
|
+
yield this.settleDrainedFollowups(sessionId, pending);
|
|
1048
|
+
const message = this.formatCoalescedFollowups(pending);
|
|
1049
|
+
this.logger(`[session] coalesce drain starting follow-up (${pending.length}) ${sessionId}`);
|
|
1050
|
+
yield this.startTurn(sessionId, message, {
|
|
1051
|
+
admission: "preempt",
|
|
1052
|
+
retainCoalesced: true,
|
|
1053
|
+
});
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
steerIntoRunningTurn(sessionId, message) {
|
|
1057
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1058
|
+
if (this.runner.steer === undefined) {
|
|
1059
|
+
return "revert_to_followup";
|
|
1060
|
+
}
|
|
1061
|
+
try {
|
|
1062
|
+
return yield this.runner.steer({ sessionId, text: message });
|
|
1063
|
+
}
|
|
1064
|
+
catch (error) {
|
|
1065
|
+
this.logger(`[session] steer failed for ${sessionId}: ${describeError(error)}`);
|
|
1066
|
+
return "revert_to_followup";
|
|
1067
|
+
}
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1070
|
+
admitTurn(sessionId_1, message_1) {
|
|
1071
|
+
return __awaiter(this, arguments, void 0, function* (sessionId, message, options = {}) {
|
|
1072
|
+
var _a;
|
|
1073
|
+
const admission = (_a = options.admission) !== null && _a !== void 0 ? _a : "preempt";
|
|
1074
|
+
if (admission === "coalesce" && steeringEnabled() && this.isTurnRunning(sessionId)) {
|
|
1075
|
+
const toolCtx = this.activeToolContexts.get(sessionId);
|
|
1076
|
+
const steerOutcome = yield this.steerIntoRunningTurn(sessionId, message);
|
|
1077
|
+
if (steerOutcome === "complete_delivered") {
|
|
1078
|
+
this.logger(`[session] steered into running turn ${sessionId}`);
|
|
1079
|
+
if (toolCtx !== undefined) {
|
|
1080
|
+
toolCtx.emit({ type: "message.received", data: { text: message } });
|
|
1081
|
+
toolCtx.emit({ type: "nudge.delivered", data: {} });
|
|
1082
|
+
}
|
|
1083
|
+
return true;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
// Re-checked after the steer: a turn that ended during it falls through
|
|
1087
|
+
// and this message starts the next turn itself. Enqueueing here instead
|
|
1088
|
+
// would orphan it — the completion drain already ran.
|
|
1089
|
+
if (admission === "coalesce" && this.isTurnRunning(sessionId)) {
|
|
1090
|
+
this.enqueueCoalesced(sessionId, {
|
|
1091
|
+
message,
|
|
1092
|
+
sourceTs: options.sourceTs,
|
|
1093
|
+
});
|
|
1094
|
+
this.logger(`[session] coalesce enqueue ${sessionId}`);
|
|
1095
|
+
return true;
|
|
1096
|
+
}
|
|
1097
|
+
// Another server owns this session, so coalesce even a preempt. No local
|
|
1098
|
+
// enqueue: `drainCoalescedFollowups` reads only this process's queue, so
|
|
1099
|
+
// the shared nudge store is the carrier. Not redundant with `acquire`
|
|
1100
|
+
// below — this returns before preempt's `clearCoalesced` drops the queue.
|
|
1101
|
+
// Enqueue-only cannot claim coalesced here: nothing joined a local turn.
|
|
1102
|
+
if (yield this.turnLeases.runningElsewhere(sessionId)) {
|
|
1103
|
+
if (options.hostedEnqueueOnly === true) {
|
|
1104
|
+
throw new HostedEnqueueNotActiveError(sessionId);
|
|
1105
|
+
}
|
|
1106
|
+
this.logger(`[session] coalesce; another server owns ${sessionId}`);
|
|
1107
|
+
return true;
|
|
1108
|
+
}
|
|
1109
|
+
// Enqueue-only must never start a turn (or preempt/interrupt). Steer and
|
|
1110
|
+
// coalesce above already returned true when they absorbed the message.
|
|
1111
|
+
if (options.hostedEnqueueOnly === true) {
|
|
1112
|
+
throw new HostedEnqueueNotActiveError(sessionId);
|
|
1113
|
+
}
|
|
1114
|
+
// Interrupt any turn in flight and wait for it to settle so the SDK run is
|
|
1115
|
+
// terminal before this turn's send (the follow-up also force-expires as a
|
|
1116
|
+
// belt-and-suspenders — see buildTurnRequest). Drain-resume skips interrupt:
|
|
1117
|
+
// the parent lifecycle already cleared activeTurns, and awaiting the in-
|
|
1118
|
+
// flight coalesce drain here would deadlock on itself.
|
|
1119
|
+
if (options.retainCoalesced !== true) {
|
|
1120
|
+
yield this.interruptActiveTurn(sessionId);
|
|
1121
|
+
}
|
|
1122
|
+
// Preempt owns the conversation — drop stale coalesced asks. Coalesce
|
|
1123
|
+
// admits and drain-resume keep the queue (retainCoalesced / coalesce path).
|
|
1124
|
+
if (admission === "preempt" && options.retainCoalesced !== true) {
|
|
1125
|
+
this.clearCoalesced(sessionId);
|
|
1126
|
+
}
|
|
1127
|
+
// A neighbour won the race; same outcome as the branch above.
|
|
1128
|
+
const lease = yield this.turnLeases.acquire(sessionId, () => {
|
|
1129
|
+
var _a;
|
|
1130
|
+
// Stalled past the stale window and a successor took over. Stand down
|
|
1131
|
+
// rather than answer the thread alongside it.
|
|
1132
|
+
this.logger(`[session] lease lost mid-turn; aborting ${sessionId}`);
|
|
1133
|
+
(_a = this.turnAborts.get(sessionId)) === null || _a === void 0 ? void 0 : _a.abort();
|
|
1134
|
+
});
|
|
1135
|
+
if (lease === undefined) {
|
|
1136
|
+
this.logger(`[session] lease lost during admit ${sessionId}`);
|
|
1137
|
+
return true;
|
|
1138
|
+
}
|
|
1139
|
+
this.pendingTurns.add(sessionId);
|
|
1140
|
+
let lifecycleInstalled = false;
|
|
1141
|
+
try {
|
|
1142
|
+
const record = yield this.updateRecord(sessionId, current => (Object.assign(Object.assign({}, current), { turnCount: current.turnCount + 1 })));
|
|
1143
|
+
const turnId = `turn_${record.turnCount}`;
|
|
1144
|
+
const isFirstTurn = record.turnCount === 1;
|
|
1145
|
+
yield this.appendEvent(sessionId, {
|
|
1146
|
+
type: "message.received",
|
|
1147
|
+
turnId,
|
|
1148
|
+
data: { text: message },
|
|
1149
|
+
});
|
|
1150
|
+
// `turn.started` is appended by executeTurn once the turn holds a
|
|
1151
|
+
// governor slot; saturated engines emit `turn.queued` first.
|
|
1152
|
+
const abort = new AbortController();
|
|
1153
|
+
this.turnAborts.set(sessionId, abort);
|
|
1154
|
+
// Lifecycle promise includes post-turn coalesce drain so waiters and
|
|
1155
|
+
// interrupt settle after follow-ups are admitted (not only executeTurn).
|
|
1156
|
+
// Assigned before the async body runs so self-identity checks typecheck.
|
|
1157
|
+
let lifecycle;
|
|
1158
|
+
lifecycle = (() => __awaiter(this, void 0, void 0, function* () {
|
|
1159
|
+
let outcome;
|
|
1160
|
+
const poll = setInterval(() => this.drainOutbox(sessionId), 1000);
|
|
1161
|
+
poll.unref();
|
|
1162
|
+
try {
|
|
1163
|
+
outcome = yield this.executeTurn(record, turnId, message, isFirstTurn, abort.signal);
|
|
1164
|
+
if (outcome.status === "cancelled") {
|
|
1165
|
+
outcome.cancellationReason = abort.signal.aborted ? "signal" : "runner";
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
catch (error) {
|
|
1169
|
+
outcome = {
|
|
1170
|
+
status: "error",
|
|
1171
|
+
errorMessage: describeError(error),
|
|
1172
|
+
errorCode: describeErrorCode(error),
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
finally {
|
|
1176
|
+
clearInterval(poll);
|
|
1177
|
+
this.liveTurns.delete(sessionId);
|
|
1178
|
+
}
|
|
1179
|
+
this.lastOutcomes.set(sessionId, outcome);
|
|
1180
|
+
const wasCurrent = this.activeTurns.get(sessionId) === lifecycle;
|
|
1181
|
+
if (wasCurrent) {
|
|
1182
|
+
this.activeTurns.delete(sessionId);
|
|
1183
|
+
}
|
|
1184
|
+
if (this.turnAborts.get(sessionId) === abort) {
|
|
1185
|
+
this.turnAborts.delete(sessionId);
|
|
1186
|
+
}
|
|
1187
|
+
// Failed/cancelled owners must not start a coalesced successor.
|
|
1188
|
+
// Hosted execute would then 503 and Temporal/FIFO would re-run the
|
|
1189
|
+
// same follow-ups as full deliveries.
|
|
1190
|
+
if (wasCurrent && !abort.signal.aborted && outcome.status === "finished") {
|
|
1191
|
+
const drain = this.drainCoalescedFollowups(sessionId).catch(error => {
|
|
1192
|
+
this.logger(`[session] coalesce drain failed for ${sessionId}: ${describeError(error)}`);
|
|
1193
|
+
});
|
|
1194
|
+
this.coalesceDrains.set(sessionId, drain);
|
|
1195
|
+
try {
|
|
1196
|
+
yield drain;
|
|
1197
|
+
}
|
|
1198
|
+
finally {
|
|
1199
|
+
if (this.coalesceDrains.get(sessionId) === drain) {
|
|
1200
|
+
this.coalesceDrains.delete(sessionId);
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
// The drain may have nested a successor turn, which took its own
|
|
1205
|
+
// reference; the lease outlives this release until that one ends too.
|
|
1206
|
+
yield lease.release();
|
|
1207
|
+
// Handed off between the last poll and the release: nobody else will.
|
|
1208
|
+
if (!abort.signal.aborted) {
|
|
1209
|
+
this.drainOutbox(sessionId);
|
|
1210
|
+
}
|
|
1211
|
+
return outcome;
|
|
1212
|
+
}))();
|
|
1213
|
+
this.activeTurns.set(sessionId, lifecycle);
|
|
1214
|
+
lifecycleInstalled = true;
|
|
1215
|
+
}
|
|
1216
|
+
finally {
|
|
1217
|
+
this.pendingTurns.delete(sessionId);
|
|
1218
|
+
if (!lifecycleInstalled) {
|
|
1219
|
+
// Nothing will run the release above, and the session would sit behind
|
|
1220
|
+
// this lease until it went stale.
|
|
1221
|
+
yield lease.release();
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
return false;
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
/**
|
|
1228
|
+
* Cancel the turn in flight for `sessionId` (if any) and wait for it to
|
|
1229
|
+
* settle, so its SDK run is terminal before the next turn's send. Safe to
|
|
1230
|
+
* call when nothing is running.
|
|
1231
|
+
*/
|
|
1232
|
+
interruptActiveTurn(sessionId) {
|
|
1233
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1234
|
+
var _a;
|
|
1235
|
+
(_a = this.turnAborts.get(sessionId)) === null || _a === void 0 ? void 0 : _a.abort();
|
|
1236
|
+
const active = this.activeTurns.get(sessionId);
|
|
1237
|
+
if (active !== undefined) {
|
|
1238
|
+
yield active.catch(() => { });
|
|
1239
|
+
}
|
|
1240
|
+
const drain = this.coalesceDrains.get(sessionId);
|
|
1241
|
+
if (drain !== undefined) {
|
|
1242
|
+
yield drain.catch(() => { });
|
|
1243
|
+
}
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
/**
|
|
1247
|
+
* Abort the in-flight turn without starting a follow-up. Returns whether
|
|
1248
|
+
* a turn was active (or pending abort) when called. Also clears any
|
|
1249
|
+
* coalesced follow-ups queued behind the session.
|
|
1250
|
+
*/
|
|
1251
|
+
interrupt(sessionId) {
|
|
1252
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1253
|
+
var _a;
|
|
1254
|
+
var _b;
|
|
1255
|
+
const hadWork = this.turnAborts.has(sessionId) ||
|
|
1256
|
+
this.activeTurns.has(sessionId) ||
|
|
1257
|
+
this.coalesceDrains.has(sessionId) ||
|
|
1258
|
+
((_b = (_a = this.coalesceQueues.get(sessionId)) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0;
|
|
1259
|
+
this.clearCoalesced(sessionId);
|
|
1260
|
+
yield this.outbox.clear(sessionId);
|
|
1261
|
+
const record = yield this.sessions.get(sessionId);
|
|
1262
|
+
const continuationKey = record === null || record === void 0 ? void 0 : record.continuationKey;
|
|
1263
|
+
if (continuationKey !== undefined && continuationKey !== null) {
|
|
1264
|
+
yield this.nudgeStore.clear(continuationKey);
|
|
1265
|
+
}
|
|
1266
|
+
yield this.interruptActiveTurn(sessionId);
|
|
1267
|
+
return hadWork;
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1270
|
+
isContinuationBusy(channelId, continuationToken) {
|
|
1271
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1272
|
+
let existing = yield this.sessions.findByContinuation(channelId, continuationToken);
|
|
1273
|
+
if (existing === undefined) {
|
|
1274
|
+
existing = yield this.restoreSessionByContinuation(channelId, continuationToken);
|
|
1275
|
+
}
|
|
1276
|
+
if (existing === undefined) {
|
|
1277
|
+
return false;
|
|
1278
|
+
}
|
|
1279
|
+
return this.isTurnRunning(existing.sessionId);
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1282
|
+
/**
|
|
1283
|
+
* Last agent-posted Slack message ts for warm `<slack_thread_delta>`, from
|
|
1284
|
+
* the session's channel state when present.
|
|
1285
|
+
*/
|
|
1286
|
+
getContinuationLastBotMessageTs(channelId, continuationToken) {
|
|
1287
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1288
|
+
let existing = yield this.sessions.findByContinuation(channelId, continuationToken);
|
|
1289
|
+
if (existing === undefined) {
|
|
1290
|
+
existing = yield this.restoreSessionByContinuation(channelId, continuationToken);
|
|
1291
|
+
}
|
|
1292
|
+
if (existing === undefined) {
|
|
1293
|
+
return undefined;
|
|
1294
|
+
}
|
|
1295
|
+
const live = this.liveTurns.get(existing.sessionId);
|
|
1296
|
+
const diskTs = lastBotMessageTsOf(existing.channelState);
|
|
1297
|
+
if (live === undefined) {
|
|
1298
|
+
return diskTs;
|
|
1299
|
+
}
|
|
1300
|
+
const liveTs = lastBotMessageTsOf(live.channelState);
|
|
1301
|
+
// This turn mutated the watermark (including a rewind to null). Live
|
|
1302
|
+
// wins so a later disk persist cannot resurrect a deleted ts.
|
|
1303
|
+
if (liveTs !== lastBotMessageTsOf(live.channelStateAtTurnStart)) {
|
|
1304
|
+
return liveTs;
|
|
1305
|
+
}
|
|
1306
|
+
// Unchanged clone: a later owner-turn persist may have a newer ts.
|
|
1307
|
+
return laterLastBotMessageTs(liveTs, diskTs);
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
interruptContinuation(channelId, continuationToken) {
|
|
1311
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1312
|
+
let existing = yield this.sessions.findByContinuation(channelId, continuationToken);
|
|
1313
|
+
if (existing === undefined) {
|
|
1314
|
+
existing = yield this.restoreSessionByContinuation(channelId, continuationToken);
|
|
1315
|
+
}
|
|
1316
|
+
if (existing === undefined) {
|
|
1317
|
+
return false;
|
|
1318
|
+
}
|
|
1319
|
+
return this.interrupt(existing.sessionId);
|
|
1320
|
+
});
|
|
1321
|
+
}
|
|
1322
|
+
hasRootOverride() {
|
|
1323
|
+
return this.project.agent.root !== undefined;
|
|
1324
|
+
}
|
|
1325
|
+
/**
|
|
1326
|
+
* FileSystem for this turn or `callTool`. Authored `root` (static or
|
|
1327
|
+
* factory) is cached per session when `cache` is set; unscoped
|
|
1328
|
+
* `callTool` passes the invocation id and does not cache, so factories
|
|
1329
|
+
* cannot share one tree across callers. A static handle is used as
|
|
1330
|
+
* authored — every session sees that same object. Host-default agents
|
|
1331
|
+
* get `localFileSystem` against the harness cwd — same path verbs as
|
|
1332
|
+
* the Cursor SDK natives, always through the synthesized tools.
|
|
1333
|
+
*/
|
|
1334
|
+
resolveFileSystem(options) {
|
|
1335
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1336
|
+
const authored = this.project.agent.root;
|
|
1337
|
+
if (authored === undefined) {
|
|
1338
|
+
return localFileSystem({ cwd: options.cwd });
|
|
1339
|
+
}
|
|
1340
|
+
const authoredRoot = yield this.resolveAuthoredFileSystem(options);
|
|
1341
|
+
if (options.cache) {
|
|
1342
|
+
const existing = this.turnRoots.get(options.sessionId);
|
|
1343
|
+
if (existing !== undefined) {
|
|
1344
|
+
return existing.root;
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
const root = unionFs({
|
|
1348
|
+
"/": authoredRoot,
|
|
1349
|
+
"/tmp": tempFs(),
|
|
1350
|
+
});
|
|
1351
|
+
if (options.cache && this.isTurnRunning(options.sessionId)) {
|
|
1352
|
+
this.turnRoots.set(options.sessionId, { root, cwd: options.cwd });
|
|
1353
|
+
}
|
|
1354
|
+
return root;
|
|
1355
|
+
});
|
|
1356
|
+
}
|
|
1357
|
+
resolveAuthoredFileSystem(options) {
|
|
1358
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1359
|
+
const authored = this.project.agent.root;
|
|
1360
|
+
if (authored === undefined) {
|
|
1361
|
+
throw new Error("resolveAuthoredFileSystem requires an authored root");
|
|
1362
|
+
}
|
|
1363
|
+
if (options.cache) {
|
|
1364
|
+
const cached = this.sessionRoots.get(options.sessionId);
|
|
1365
|
+
if (cached !== undefined) {
|
|
1366
|
+
return cached;
|
|
1367
|
+
}
|
|
1368
|
+
const inflight = this.sessionRootInflight.get(options.sessionId);
|
|
1369
|
+
if (inflight !== undefined) {
|
|
1370
|
+
return inflight;
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
const resolve = () => __awaiter(this, void 0, void 0, function* () {
|
|
1374
|
+
if (typeof authored !== "function") {
|
|
1375
|
+
return authored;
|
|
1376
|
+
}
|
|
1377
|
+
const host = this.hostForSession(options.cache ? options.sessionId : undefined);
|
|
1378
|
+
const ctx = Object.assign(Object.assign({ sessionId: options.sessionId, agentName: this.project.agent.name }, (options.continuationKey === undefined
|
|
1379
|
+
? {}
|
|
1380
|
+
: { continuationKey: options.continuationKey })), { host: { files: host.files, reminders: host.reminders, kv: host.kv } });
|
|
1381
|
+
return yield authored(ctx);
|
|
1382
|
+
});
|
|
1383
|
+
if (!options.cache) {
|
|
1384
|
+
return resolve();
|
|
1385
|
+
}
|
|
1386
|
+
const pending = resolve();
|
|
1387
|
+
this.sessionRootInflight.set(options.sessionId, pending);
|
|
1388
|
+
try {
|
|
1389
|
+
const resolved = yield pending;
|
|
1390
|
+
this.sessionRoots.set(options.sessionId, resolved);
|
|
1391
|
+
return resolved;
|
|
1392
|
+
}
|
|
1393
|
+
finally {
|
|
1394
|
+
this.sessionRootInflight.delete(options.sessionId);
|
|
1395
|
+
}
|
|
1396
|
+
});
|
|
1397
|
+
}
|
|
1398
|
+
dropTurnFileSystem(sessionId) {
|
|
1399
|
+
this.turnRoots.delete(sessionId);
|
|
1400
|
+
}
|
|
1401
|
+
prepareScratchToolInput(options) {
|
|
1402
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1403
|
+
if (options.sessionId === undefined) {
|
|
1404
|
+
return options.input;
|
|
1405
|
+
}
|
|
1406
|
+
const turn = this.turnRoots.get(options.sessionId);
|
|
1407
|
+
if (turn === undefined) {
|
|
1408
|
+
return options.input;
|
|
1409
|
+
}
|
|
1410
|
+
yield ingestHostAgentToolsScratch({
|
|
1411
|
+
cwd: turn.cwd,
|
|
1412
|
+
root: turn.root,
|
|
1413
|
+
});
|
|
1414
|
+
return remapScratchPaths(options.input, turn.cwd);
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1417
|
+
withTurnScratchTools(tools, sessionId) {
|
|
1418
|
+
const turn = this.turnRoots.get(sessionId);
|
|
1419
|
+
if (turn === undefined) {
|
|
1420
|
+
return tools;
|
|
1421
|
+
}
|
|
1422
|
+
return tools.map(tool => {
|
|
1423
|
+
const execute = tool.definition.execute;
|
|
1424
|
+
if (typeof execute !== "function") {
|
|
1425
|
+
return tool;
|
|
1426
|
+
}
|
|
1427
|
+
return Object.assign(Object.assign({}, tool), { definition: Object.assign(Object.assign({}, tool.definition), { execute: (input, ctx) => __awaiter(this, void 0, void 0, function* () {
|
|
1428
|
+
const remapped = yield this.prepareScratchToolInput({
|
|
1429
|
+
sessionId,
|
|
1430
|
+
input: input !== null && typeof input === "object" && !Array.isArray(input)
|
|
1431
|
+
? input
|
|
1432
|
+
: {},
|
|
1433
|
+
});
|
|
1434
|
+
return execute(remapped, ctx);
|
|
1435
|
+
}) }) });
|
|
1436
|
+
});
|
|
1437
|
+
}
|
|
1438
|
+
nativeFileSystemOutcome(options) {
|
|
1439
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1440
|
+
var _a;
|
|
1441
|
+
const { toolName, input, callId } = options;
|
|
1442
|
+
if (!isFileSystemToolAlias(toolName)) {
|
|
1443
|
+
return undefined;
|
|
1444
|
+
}
|
|
1445
|
+
// Same policy the authored callTool path asks: a dry-run write is
|
|
1446
|
+
// answered by the host. Native edit/delete used to return before
|
|
1447
|
+
// decideToolCall and persist through the FileSystem.
|
|
1448
|
+
const decision = decideToolCall({
|
|
1449
|
+
dryRun: options.dryRun === true,
|
|
1450
|
+
effect: isMutatingFileSystemTool(toolName) ? "write" : "read",
|
|
1451
|
+
input,
|
|
1452
|
+
});
|
|
1453
|
+
if (decision.kind === "answer") {
|
|
1454
|
+
return {
|
|
1455
|
+
callId,
|
|
1456
|
+
toolName,
|
|
1457
|
+
isError: false,
|
|
1458
|
+
result: decision.result,
|
|
1459
|
+
durationMs: 0,
|
|
1460
|
+
sessionId: options.sessionId,
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
const root = yield this.resolveFileSystem({
|
|
1464
|
+
sessionId: (_a = options.sessionId) !== null && _a !== void 0 ? _a : callId,
|
|
1465
|
+
continuationKey: options.continuationKey,
|
|
1466
|
+
cache: options.sessionId !== undefined,
|
|
1467
|
+
cwd: options.cwd,
|
|
1468
|
+
});
|
|
1469
|
+
const rawArgs = input !== null && typeof input === "object" && !Array.isArray(input)
|
|
1470
|
+
? input
|
|
1471
|
+
: {};
|
|
1472
|
+
const args = yield this.prepareScratchToolInput({
|
|
1473
|
+
sessionId: options.sessionId,
|
|
1474
|
+
input: rawArgs,
|
|
1475
|
+
});
|
|
1476
|
+
const tool = fileSystemTool(root, toolName, args);
|
|
1477
|
+
const execute = tool === null || tool === void 0 ? void 0 : tool.definition.execute;
|
|
1478
|
+
if (tool === undefined || typeof execute !== "function") {
|
|
1479
|
+
// A resolved root owns this native name. Falling through would let
|
|
1480
|
+
// an authored lookalike run, or surface UnknownToolError after we
|
|
1481
|
+
// already advertised the FileSystem tool.
|
|
1482
|
+
return {
|
|
1483
|
+
callId,
|
|
1484
|
+
toolName,
|
|
1485
|
+
isError: true,
|
|
1486
|
+
result: {
|
|
1487
|
+
status: "error",
|
|
1488
|
+
error: { message: `FileSystem cannot handle ${toolName}` },
|
|
1489
|
+
},
|
|
1490
|
+
durationMs: 0,
|
|
1491
|
+
sessionId: options.sessionId,
|
|
1492
|
+
errorMessage: `FileSystem cannot handle ${toolName}`,
|
|
1493
|
+
};
|
|
1494
|
+
}
|
|
1495
|
+
const validation = validateToolInput(tool.definition.inputSchema, args);
|
|
1496
|
+
if (!validation.ok) {
|
|
1497
|
+
return {
|
|
1498
|
+
callId,
|
|
1499
|
+
toolName,
|
|
1500
|
+
isError: true,
|
|
1501
|
+
result: {
|
|
1502
|
+
status: "error",
|
|
1503
|
+
error: { message: validation.message },
|
|
1504
|
+
},
|
|
1505
|
+
durationMs: 0,
|
|
1506
|
+
sessionId: options.sessionId,
|
|
1507
|
+
errorMessage: validation.message,
|
|
1508
|
+
};
|
|
1509
|
+
}
|
|
1510
|
+
try {
|
|
1511
|
+
const value = yield execute(validation.value, {
|
|
1512
|
+
toolCallId: callId,
|
|
1513
|
+
workspaceDir: options.cwd,
|
|
1514
|
+
});
|
|
1515
|
+
return {
|
|
1516
|
+
callId,
|
|
1517
|
+
toolName,
|
|
1518
|
+
isError: false,
|
|
1519
|
+
result: normalizeToolResult(value),
|
|
1520
|
+
durationMs: 0,
|
|
1521
|
+
sessionId: options.sessionId,
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
catch (error) {
|
|
1525
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1526
|
+
return {
|
|
1527
|
+
callId,
|
|
1528
|
+
toolName,
|
|
1529
|
+
isError: true,
|
|
1530
|
+
result: { status: "error", error: { message } },
|
|
1531
|
+
durationMs: 0,
|
|
1532
|
+
sessionId: options.sessionId,
|
|
1533
|
+
errorMessage: message,
|
|
1534
|
+
};
|
|
1535
|
+
}
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1538
|
+
/**
|
|
1539
|
+
* Local harness cwd for a session: per-send override, else the shared
|
|
1540
|
+
* `agent.local.workspaceDir`, else `<agent.local.cwd>/<sessionId>`, else
|
|
1541
|
+
* the durable session workspace.
|
|
1542
|
+
*/
|
|
1543
|
+
harnessCwd(record) {
|
|
1544
|
+
var _a, _b;
|
|
1545
|
+
if (record.workspaceOverride !== undefined) {
|
|
1546
|
+
return record.workspaceOverride;
|
|
1547
|
+
}
|
|
1548
|
+
const shared = (_a = this.project.agent.local) === null || _a === void 0 ? void 0 : _a.workspaceDir;
|
|
1549
|
+
if (shared !== undefined) {
|
|
1550
|
+
return shared;
|
|
1551
|
+
}
|
|
1552
|
+
const agentCwd = (_b = this.project.agent.local) === null || _b === void 0 ? void 0 : _b.cwd;
|
|
1553
|
+
if (agentCwd !== undefined) {
|
|
1554
|
+
return join(agentCwd, record.sessionId);
|
|
1555
|
+
}
|
|
1556
|
+
return this.sessions.workspaceDir(record.sessionId);
|
|
1557
|
+
}
|
|
1558
|
+
/**
|
|
1559
|
+
* True when the harness cwd is a directory the session does not own — a
|
|
1560
|
+
* per-send override (e.g. a PR worktree) or a workspace shared by every
|
|
1561
|
+
* session. Agent scaffold has to stay out of those, so it lands in the
|
|
1562
|
+
* durable session workspace and the instructions travel inline instead.
|
|
1563
|
+
*/
|
|
1564
|
+
hasBorrowedWorkspace(record) {
|
|
1565
|
+
var _a;
|
|
1566
|
+
return (record.workspaceOverride !== undefined || ((_a = this.project.agent.local) === null || _a === void 0 ? void 0 : _a.workspaceDir) !== undefined);
|
|
1567
|
+
}
|
|
1568
|
+
/**
|
|
1569
|
+
* Resolve the shared harness workspace during startup, so the first session
|
|
1570
|
+
* is not the one that waits for it. Only possible with
|
|
1571
|
+
* `local.workspaceDir`: every other layout gives a session its own cwd,
|
|
1572
|
+
* which is unknowable until that session exists.
|
|
1573
|
+
*
|
|
1574
|
+
* Best effort — warming is an optimization, and a turn rebuilds on its own.
|
|
1575
|
+
*/
|
|
1576
|
+
prewarmSharedWorkspace() {
|
|
1577
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1578
|
+
if (this.sharedWarm === undefined) {
|
|
1579
|
+
this.sharedWarm = this.warmSharedWorkspace();
|
|
1580
|
+
}
|
|
1581
|
+
yield this.sharedWarm;
|
|
1582
|
+
});
|
|
1583
|
+
}
|
|
1584
|
+
warmSharedWorkspace() {
|
|
1585
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1586
|
+
var _a, _b;
|
|
1587
|
+
const agent = this.project.agent;
|
|
1588
|
+
const workspaceDir = (_a = agent.local) === null || _a === void 0 ? void 0 : _a.workspaceDir;
|
|
1589
|
+
if (workspaceDir === undefined ||
|
|
1590
|
+
agent.runtime !== "local" ||
|
|
1591
|
+
this.hasRootOverride() ||
|
|
1592
|
+
this.runner.prewarm === undefined) {
|
|
1593
|
+
return;
|
|
1594
|
+
}
|
|
1595
|
+
const startedAt = Date.now();
|
|
1596
|
+
try {
|
|
1597
|
+
yield mkdir(workspaceDir, { recursive: true });
|
|
1598
|
+
if (yield ownSharedHarness(workspaceDir)) {
|
|
1599
|
+
yield materializeWorkspace({
|
|
1600
|
+
agent,
|
|
1601
|
+
workspaceDir,
|
|
1602
|
+
seed: true,
|
|
1603
|
+
stateRoot: this.stateRoot,
|
|
1604
|
+
});
|
|
1605
|
+
}
|
|
1606
|
+
const harness = resolveHostHarnessToolOptions(agent.allowedTools);
|
|
1607
|
+
const outcome = yield this.runner.prewarm(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ workspaceDir, runnerStateDir: join(this.stateRoot, "runner"), sandbox: ((_b = agent.local) === null || _b === void 0 ? void 0 : _b.sandbox) === true }, (harness.tools === undefined ? {} : { tools: harness.tools })), (harness.disallowedTools === undefined
|
|
1608
|
+
? {}
|
|
1609
|
+
: { disallowedTools: harness.disallowedTools })), {
|
|
1610
|
+
// No session yet, so auth'd connections are omitted rather than
|
|
1611
|
+
// warmed without an identity; the per-send flag still has to match
|
|
1612
|
+
// the turns' requests or the warmed executor is one no turn asks for.
|
|
1613
|
+
mcpServers: this.buildMcpServers("local") }), (this.hasAttachedAuthedConnections() ||
|
|
1614
|
+
this.project.agent.connections.some(connection => isCursorAccountTransport(connection.transport))
|
|
1615
|
+
? { perSendMcpServers: true }
|
|
1616
|
+
: {})), { subagents: buildRunnerSubagents(agent) }), (agent.model === undefined ? {} : { model: agent.model })));
|
|
1617
|
+
// Report what happened rather than that the call returned: a runner
|
|
1618
|
+
// that cannot warm resolves cleanly, and logging success there would
|
|
1619
|
+
// tell an operator the workspace is hot when nothing touched it.
|
|
1620
|
+
this.logger(outcome.warmed
|
|
1621
|
+
? `[agent-sdk] warmed ${workspaceDir} in ${Date.now() - startedAt}ms`
|
|
1622
|
+
: `[agent-sdk] did not warm ${workspaceDir}: ${outcome.reason}`);
|
|
1623
|
+
}
|
|
1624
|
+
catch (error) {
|
|
1625
|
+
this.logger(`[agent-sdk] could not warm ${workspaceDir}: ${describeError(error)}`);
|
|
1626
|
+
}
|
|
1627
|
+
});
|
|
1628
|
+
}
|
|
1629
|
+
/** Directory that receives AGENTS.md / skills / agent tools. */
|
|
1630
|
+
scaffoldDir(record) {
|
|
1631
|
+
if (this.hasBorrowedWorkspace(record)) {
|
|
1632
|
+
return this.sessions.workspaceDir(record.sessionId);
|
|
1633
|
+
}
|
|
1634
|
+
return this.harnessCwd(record);
|
|
1635
|
+
}
|
|
1636
|
+
executeTurn(record, turnId, message, isFirstTurn, signal) {
|
|
1637
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1638
|
+
var _a;
|
|
1639
|
+
var _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
1640
|
+
const sessionId = record.sessionId;
|
|
1641
|
+
const workspaceDir = this.scaffoldDir(record);
|
|
1642
|
+
const harnessCwd = this.harnessCwd(record);
|
|
1643
|
+
const turnChannelState = structuredClone((_b = record.channelState) !== null && _b !== void 0 ? _b : null);
|
|
1644
|
+
const eventContext = {
|
|
1645
|
+
channelState: structuredClone(turnChannelState),
|
|
1646
|
+
channelStateAtTurnStart: structuredClone(turnChannelState),
|
|
1647
|
+
session: this.sessionInfo(record),
|
|
1648
|
+
};
|
|
1649
|
+
this.liveTurns.set(sessionId, eventContext);
|
|
1650
|
+
// Hold the open log so tool events append synchronously. Fire-and-forget
|
|
1651
|
+
// `appendEvent` races parallel tool completions and delays the live stream
|
|
1652
|
+
// until the next await tick — often after *all* parallel tools finish.
|
|
1653
|
+
const log = yield this.logs.get(sessionId);
|
|
1654
|
+
const emit = (payload) => {
|
|
1655
|
+
try {
|
|
1656
|
+
const event = log.append(Object.assign(Object.assign({}, payload), { turnId }));
|
|
1657
|
+
this.queueDispatch(sessionId, event, eventContext);
|
|
1658
|
+
}
|
|
1659
|
+
catch (error) {
|
|
1660
|
+
this.logger(`[agent-sdk] failed to record event for ${sessionId}: ${describeError(error)}`);
|
|
1661
|
+
}
|
|
1662
|
+
};
|
|
1663
|
+
// Read by closure, so the mapper still sees it while draining after the
|
|
1664
|
+
// registry entry below is gone.
|
|
1665
|
+
const stubbed = new Set();
|
|
1666
|
+
const mapper = new TurnEventMapper(emit, callId => stubbed.has(callId));
|
|
1667
|
+
// Take a turn slot before any work happens. When every slot is busy the
|
|
1668
|
+
// turn waits FIFO — durable `turn.queued` marks the wait — and a
|
|
1669
|
+
// preempting follow-up (or interrupt) resolves the wait as cancelled
|
|
1670
|
+
// without the turn ever running.
|
|
1671
|
+
const permit = yield this.turnGovernor.acquire({
|
|
1672
|
+
sessionId,
|
|
1673
|
+
signal,
|
|
1674
|
+
onQueued: position => {
|
|
1675
|
+
var _a;
|
|
1676
|
+
const maxRunningTurns = (_a = this.turnGovernor.maxRunningTurns) !== null && _a !== void 0 ? _a : 0;
|
|
1677
|
+
emit({ type: "turn.queued", data: { position, maxRunningTurns } });
|
|
1678
|
+
this.logger(`[session] ${sessionId} ${turnId} queued at position ${position} (${maxRunningTurns} turn slot(s) busy)`);
|
|
1679
|
+
},
|
|
1680
|
+
});
|
|
1681
|
+
let outcome;
|
|
1682
|
+
if (permit === undefined) {
|
|
1683
|
+
// Preempted while waiting for a slot; settle as an interrupted turn.
|
|
1684
|
+
outcome = { status: "cancelled" };
|
|
1685
|
+
}
|
|
1686
|
+
else {
|
|
1687
|
+
emit({ type: "turn.started", data: {} });
|
|
1688
|
+
try {
|
|
1689
|
+
if (record.workspaceOverride === undefined &&
|
|
1690
|
+
((_a = this.project.agent.local) === null || _a === void 0 ? void 0 : _a.workspaceDir) !== undefined) {
|
|
1691
|
+
yield this.prewarmSharedWorkspace();
|
|
1692
|
+
}
|
|
1693
|
+
// Scaffold agent files into the session workspace even when the harness
|
|
1694
|
+
// cwd is overridden (e.g. a PR worktree).
|
|
1695
|
+
if (!this.hasRootOverride()) {
|
|
1696
|
+
yield materializeWorkspace({
|
|
1697
|
+
agent: this.project.agent,
|
|
1698
|
+
workspaceDir,
|
|
1699
|
+
seed: isFirstTurn,
|
|
1700
|
+
stateRoot: this.stateRoot,
|
|
1701
|
+
});
|
|
1702
|
+
this.materializedWorkspaces.add(sessionId);
|
|
1703
|
+
// Must precede the carrier: the harness resolves rules, skills,
|
|
1704
|
+
// `mcp.json` and hooks from its git root, so a workspace nested in an
|
|
1705
|
+
// outer repo would load that repo's hooks instead of the ones written
|
|
1706
|
+
// just below. Only for directories this session owns — a borrowed
|
|
1707
|
+
// checkout or worktree keeps its own root.
|
|
1708
|
+
if (!this.hasBorrowedWorkspace(record)) {
|
|
1709
|
+
yield ensureOwnGitRoot(harnessCwd);
|
|
1710
|
+
}
|
|
1711
|
+
yield this.materializeNudgeCarrier(record, harnessCwd);
|
|
1712
|
+
}
|
|
1713
|
+
// Baseline before the agent can run, so a follow-up that arrives and
|
|
1714
|
+
// drains before the first tool boundary still counts as an increase.
|
|
1715
|
+
yield this.baselineDeliveredNudges(record);
|
|
1716
|
+
const pendingFiles = record.pendingWorkspaceFiles;
|
|
1717
|
+
const pendingImages = record.pendingImages;
|
|
1718
|
+
const runtime = resolveSessionRuntime({
|
|
1719
|
+
agentRuntime: this.project.agent.runtime,
|
|
1720
|
+
cloudOverride: record.cloudOverride,
|
|
1721
|
+
});
|
|
1722
|
+
const hasPendingFiles = pendingFiles !== undefined && Object.keys(pendingFiles).length > 0;
|
|
1723
|
+
if (hasPendingFiles && runtime !== "cloud") {
|
|
1724
|
+
const root = yield this.resolveFileSystem({
|
|
1725
|
+
sessionId,
|
|
1726
|
+
continuationKey: (_c = record.continuationKey) !== null && _c !== void 0 ? _c : undefined,
|
|
1727
|
+
cache: true,
|
|
1728
|
+
cwd: harnessCwd,
|
|
1729
|
+
});
|
|
1730
|
+
yield writeWorkspaceFilesToRoot(root, pendingFiles);
|
|
1731
|
+
}
|
|
1732
|
+
if ((hasPendingFiles && runtime !== "cloud") ||
|
|
1733
|
+
(pendingImages !== undefined && pendingImages.length > 0)) {
|
|
1734
|
+
yield this.updateRecord(sessionId, current => (Object.assign(Object.assign({}, current), { pendingWorkspaceFiles: hasPendingFiles &&
|
|
1735
|
+
runtime !== "cloud" &&
|
|
1736
|
+
current.pendingWorkspaceFiles === pendingFiles
|
|
1737
|
+
? undefined
|
|
1738
|
+
: current.pendingWorkspaceFiles, pendingImages: current.pendingImages === pendingImages ? undefined : current.pendingImages })));
|
|
1739
|
+
}
|
|
1740
|
+
const onUpdate = (update) => __awaiter(this, void 0, void 0, function* () {
|
|
1741
|
+
const turn = this.turnRoots.get(sessionId);
|
|
1742
|
+
if (turn !== undefined) {
|
|
1743
|
+
update = yield applyWebScratchUpdate({
|
|
1744
|
+
cwd: turn.cwd,
|
|
1745
|
+
root: turn.root,
|
|
1746
|
+
update,
|
|
1747
|
+
});
|
|
1748
|
+
}
|
|
1749
|
+
mapper.handleUpdate(update);
|
|
1750
|
+
// Yield so the HTTP NDJSON writer can flush tool start/result
|
|
1751
|
+
// events while sibling parallel tools are still executing.
|
|
1752
|
+
if (update.type === "tool-call-started" ||
|
|
1753
|
+
update.type === "tool-call-completed" ||
|
|
1754
|
+
// Nested subagent progress (newer SDKs); string check keeps
|
|
1755
|
+
// typecheck green against older published `@cursor/sdk` pins.
|
|
1756
|
+
update.type === "tool-call-delta") {
|
|
1757
|
+
// Same boundary the carrier hook runs on, so a drain is visible
|
|
1758
|
+
// by the time this fires.
|
|
1759
|
+
yield this.emitDeliveredNudges(record, emit);
|
|
1760
|
+
yield yieldEventLoop();
|
|
1761
|
+
}
|
|
1762
|
+
});
|
|
1763
|
+
const request = yield this.buildTurnRequest(record, turnId, message, isFirstTurn, emit, eventContext, onUpdate, signal, pendingImages);
|
|
1764
|
+
this.activeToolContexts.set(sessionId, { record, turnId, emit });
|
|
1765
|
+
this.stubbedCallIds.set(sessionId, stubbed);
|
|
1766
|
+
try {
|
|
1767
|
+
outcome = yield this.runTurnExecute(record, turnId, () => this.runner.runTurn(request));
|
|
1768
|
+
}
|
|
1769
|
+
finally {
|
|
1770
|
+
this.activeToolContexts.delete(sessionId);
|
|
1771
|
+
this.stubbedCallIds.delete(sessionId);
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
catch (error) {
|
|
1775
|
+
this.approvals.cancelSession(sessionId, "deny");
|
|
1776
|
+
this.auths.cancelSession(sessionId, "deny");
|
|
1777
|
+
outcome = {
|
|
1778
|
+
status: "error",
|
|
1779
|
+
errorMessage: describeError(error),
|
|
1780
|
+
errorCode: describeErrorCode(error),
|
|
1781
|
+
};
|
|
1782
|
+
}
|
|
1783
|
+
finally {
|
|
1784
|
+
permit.release();
|
|
1785
|
+
this.dropTurnFileSystem(sessionId);
|
|
1786
|
+
this.approvals.cancelSession(sessionId, "deny");
|
|
1787
|
+
this.auths.cancelSession(sessionId, "deny");
|
|
1788
|
+
yield this.clearPendingApprovals(sessionId);
|
|
1789
|
+
yield this.clearPendingAuths(sessionId);
|
|
1790
|
+
// Anything still queued belongs to the next turn's boundary drain, not
|
|
1791
|
+
// to this turn's edge detection.
|
|
1792
|
+
this.deliveredNudges.delete(sessionId);
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
mapper.finish();
|
|
1796
|
+
if (mapper.title !== undefined) {
|
|
1797
|
+
const title = mapper.title;
|
|
1798
|
+
yield this.updateRecord(sessionId, current => (Object.assign(Object.assign({}, current), { title })));
|
|
1799
|
+
eventContext.session = Object.assign(Object.assign({}, eventContext.session), { title });
|
|
1800
|
+
}
|
|
1801
|
+
const finalResult = (_d = outcome.result) !== null && _d !== void 0 ? _d : mapper.finalText;
|
|
1802
|
+
const finalUsage = (_e = outcome.usage) !== null && _e !== void 0 ? _e : mapper.usage;
|
|
1803
|
+
const finalCost = finalUsage === undefined
|
|
1804
|
+
? undefined
|
|
1805
|
+
: yield this.estimateTurnCost({
|
|
1806
|
+
model: (_f = this.project.agent.model) !== null && _f !== void 0 ? _f : DEFAULT_MODEL,
|
|
1807
|
+
usage: finalUsage,
|
|
1808
|
+
agentId: record.sdkAgentId,
|
|
1809
|
+
conversationId: (_g = record.sdkAgentId) !== null && _g !== void 0 ? _g : sessionId,
|
|
1810
|
+
});
|
|
1811
|
+
if (finalCost !== undefined) {
|
|
1812
|
+
outcome = Object.assign(Object.assign({}, outcome), { cost: finalCost });
|
|
1813
|
+
}
|
|
1814
|
+
if (outcome.status === "finished") {
|
|
1815
|
+
yield this.appendEvent(sessionId, {
|
|
1816
|
+
type: "turn.completed",
|
|
1817
|
+
turnId,
|
|
1818
|
+
data: {
|
|
1819
|
+
result: finalResult,
|
|
1820
|
+
usage: finalUsage,
|
|
1821
|
+
cost: finalCost,
|
|
1822
|
+
},
|
|
1823
|
+
}, eventContext);
|
|
1824
|
+
}
|
|
1825
|
+
else {
|
|
1826
|
+
const interrupted = signal.aborted && outcome.status === "cancelled";
|
|
1827
|
+
yield this.appendEvent(sessionId, {
|
|
1828
|
+
type: "turn.failed",
|
|
1829
|
+
turnId,
|
|
1830
|
+
data: {
|
|
1831
|
+
status: outcome.status,
|
|
1832
|
+
message: interrupted
|
|
1833
|
+
? "turn interrupted"
|
|
1834
|
+
: ((_h = outcome.errorMessage) !== null && _h !== void 0 ? _h : (outcome.status === "cancelled" ? "turn cancelled" : "turn failed")),
|
|
1835
|
+
},
|
|
1836
|
+
}, eventContext);
|
|
1837
|
+
}
|
|
1838
|
+
const current = yield this.sessions.get(sessionId);
|
|
1839
|
+
const mode = (_j = current === null || current === void 0 ? void 0 : current.mode) !== null && _j !== void 0 ? _j : record.mode;
|
|
1840
|
+
if (mode === "task") {
|
|
1841
|
+
yield this.appendEvent(sessionId, outcome.status === "finished"
|
|
1842
|
+
? { type: "session.completed", turnId, data: {} }
|
|
1843
|
+
: {
|
|
1844
|
+
type: "session.failed",
|
|
1845
|
+
turnId,
|
|
1846
|
+
data: { message: (_k = outcome.errorMessage) !== null && _k !== void 0 ? _k : "task failed" },
|
|
1847
|
+
}, eventContext);
|
|
1848
|
+
yield this.wipeCloudSessionWorkspace(sessionId);
|
|
1849
|
+
}
|
|
1850
|
+
else {
|
|
1851
|
+
yield this.appendEvent(sessionId, {
|
|
1852
|
+
type: "session.waiting",
|
|
1853
|
+
turnId,
|
|
1854
|
+
data: {},
|
|
1855
|
+
}, eventContext);
|
|
1856
|
+
}
|
|
1857
|
+
// Commit live channel state here. appendEvent does not wait for
|
|
1858
|
+
// dispatch, and awaiting that chain deadlocks when an earlier handler
|
|
1859
|
+
// is waiting on the next turn (defineResult refresh). Coalesce drain
|
|
1860
|
+
// snapshots disk as soon as this returns.
|
|
1861
|
+
yield this.updateRecord(sessionId, current => (Object.assign(Object.assign({}, current), { channelState: mergeChannelStateMutation({
|
|
1862
|
+
current: current.channelState,
|
|
1863
|
+
before: eventContext.channelStateAtTurnStart,
|
|
1864
|
+
after: eventContext.channelState,
|
|
1865
|
+
}) })));
|
|
1866
|
+
return Object.assign(Object.assign({}, outcome), { result: finalResult, usage: finalUsage });
|
|
1867
|
+
});
|
|
1868
|
+
}
|
|
1869
|
+
/**
|
|
1870
|
+
* Sync authored skills onto the Agent Store at `skills/<name>/…` (hosted)
|
|
1871
|
+
* or `agent-serve/<agent>/skills/<name>/…` (personal-key local+cloud)
|
|
1872
|
+
* before the first cloud prompt, so `--agent-store-skills-dir` and the
|
|
1873
|
+
* first-turn S3 scan can discover them. False when the sync fails: the
|
|
1874
|
+
* turn then runs without store skills rather than pointing discovery at
|
|
1875
|
+
* paths that do not exist.
|
|
1876
|
+
*/
|
|
1877
|
+
ensureCloudSkillsSynced(store) {
|
|
1878
|
+
const skills = this.project.agent.skills;
|
|
1879
|
+
return this.syncToCloudStore("skills", store, skills.length > 0, () => syncSkillsToStore({
|
|
1880
|
+
sink: store.sink,
|
|
1881
|
+
agentName: this.project.name,
|
|
1882
|
+
skills,
|
|
1883
|
+
skillsPrefix: store.skillsPrefix,
|
|
1884
|
+
}));
|
|
1885
|
+
}
|
|
1886
|
+
/**
|
|
1887
|
+
* Sync `agent/sandbox/workspace/**` seeds onto the Agent Store at
|
|
1888
|
+
* `workspace/…` (hosted) or `agent-serve/<agent>/workspace/…`
|
|
1889
|
+
* (personal-key local+cloud) before the first cloud prompt, which names
|
|
1890
|
+
* the mounted directory (see buildCloudWorkspaceSection).
|
|
1891
|
+
*/
|
|
1892
|
+
ensureCloudWorkspaceSynced(store) {
|
|
1893
|
+
const seedFiles = this.project.agent.seedFiles;
|
|
1894
|
+
return this.syncToCloudStore("workspace", store, seedFiles.length > 0, () => syncWorkspaceToStore({
|
|
1895
|
+
sink: store.sink,
|
|
1896
|
+
seedFiles,
|
|
1897
|
+
workspacePrefix: store.workspacePrefix,
|
|
1898
|
+
}));
|
|
1899
|
+
}
|
|
1900
|
+
/**
|
|
1901
|
+
* Replace this session's store prefix with the pending `workspaceFiles`.
|
|
1902
|
+
* Undefined when the write did not land; the prompt must not name the
|
|
1903
|
+
* mount, matching ensureCloudWorkspaceSynced.
|
|
1904
|
+
*/
|
|
1905
|
+
syncCloudSessionWorkspaceFiles(store, sessionId, files) {
|
|
1906
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1907
|
+
const workspacePrefix = store.sessionWorkspacePrefix(sessionId);
|
|
1908
|
+
try {
|
|
1909
|
+
const relPaths = yield syncSessionWorkspaceFiles({
|
|
1910
|
+
sink: store.sink,
|
|
1911
|
+
files,
|
|
1912
|
+
workspacePrefix,
|
|
1913
|
+
});
|
|
1914
|
+
return {
|
|
1915
|
+
dir: `${store.mountPath}/${workspacePrefix}`,
|
|
1916
|
+
relPaths,
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1919
|
+
catch (error) {
|
|
1920
|
+
this.logger(`[workspace] ${store.label} session files sync failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1921
|
+
if (error instanceof WorkspaceFileRefusal) {
|
|
1922
|
+
yield this.clearPendingWorkspaceFiles(sessionId, files);
|
|
1923
|
+
}
|
|
1924
|
+
return undefined;
|
|
1925
|
+
}
|
|
1926
|
+
});
|
|
1927
|
+
}
|
|
1928
|
+
wipeCloudSessionWorkspace(sessionId) {
|
|
1929
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1930
|
+
const store = this.cloudStore();
|
|
1931
|
+
if (store === undefined) {
|
|
1932
|
+
return;
|
|
1933
|
+
}
|
|
1934
|
+
try {
|
|
1935
|
+
yield wipeSessionWorkspace({
|
|
1936
|
+
sink: store.sink,
|
|
1937
|
+
workspacePrefix: store.sessionWorkspacePrefix(sessionId),
|
|
1938
|
+
});
|
|
1939
|
+
}
|
|
1940
|
+
catch (error) {
|
|
1941
|
+
this.logger(`[workspace] ${store.label} session files wipe failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1942
|
+
}
|
|
1943
|
+
});
|
|
1944
|
+
}
|
|
1945
|
+
clearPendingWorkspaceFiles(sessionId, pending) {
|
|
1946
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1947
|
+
yield this.updateRecord(sessionId, current => (Object.assign(Object.assign({}, current), { pendingWorkspaceFiles: current.pendingWorkspaceFiles === pending ? undefined : current.pendingWorkspaceFiles })));
|
|
1948
|
+
});
|
|
1949
|
+
}
|
|
1950
|
+
/**
|
|
1951
|
+
* With files authored, every first cloud turn re-copies the authored
|
|
1952
|
+
* bytes and the memo only dedupes concurrent first turns. The mount is
|
|
1953
|
+
* writable from cloud sessions, so a once-per-process copy would let one
|
|
1954
|
+
* session's edits masquerade as agent definition for every later session
|
|
1955
|
+
* until the engine restarts; re-syncing keeps the store true to discovery
|
|
1956
|
+
* the same way local materialization always rewrites skills. Without
|
|
1957
|
+
* authored files the sync is best-effort stale cleanup and one attempt
|
|
1958
|
+
* per process is enough, success or not — a deployment without the files
|
|
1959
|
+
* surface must not fail and log on every new session for a feature it
|
|
1960
|
+
* does not use.
|
|
1961
|
+
*/
|
|
1962
|
+
syncToCloudStore(surface, store, authored, run) {
|
|
1963
|
+
const inFlight = this.cloudSyncs.get(surface);
|
|
1964
|
+
if (inFlight !== undefined) {
|
|
1965
|
+
return inFlight;
|
|
1966
|
+
}
|
|
1967
|
+
let sync = run().then(() => true, error => {
|
|
1968
|
+
this.logger(`[${surface}] ${store.label} sync failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1969
|
+
return false;
|
|
1970
|
+
});
|
|
1971
|
+
if (authored) {
|
|
1972
|
+
sync = sync.finally(() => {
|
|
1973
|
+
this.cloudSyncs.delete(surface);
|
|
1974
|
+
});
|
|
1975
|
+
}
|
|
1976
|
+
this.cloudSyncs.set(surface, sync);
|
|
1977
|
+
return sync;
|
|
1978
|
+
}
|
|
1979
|
+
/**
|
|
1980
|
+
* Hosted deployments own the deployment store. Local `serve`/`run` with
|
|
1981
|
+
* a personal API key writes into the USER store under `agent-serve/`.
|
|
1982
|
+
* A persistent deployment's version container has no cloud store at all:
|
|
1983
|
+
* its state directory is the durable copy, and its service-account key
|
|
1984
|
+
* must not be mistaken for a personal one.
|
|
1985
|
+
*/
|
|
1986
|
+
cloudStore() {
|
|
1987
|
+
const v2Hosting = resolveAgentV2Hosting({
|
|
1988
|
+
architecture: this.project.agent.architecture,
|
|
1989
|
+
});
|
|
1990
|
+
if (v2Hosting.kind === "cursor-managed-persistent") {
|
|
1991
|
+
return undefined;
|
|
1992
|
+
}
|
|
1993
|
+
const deployment = {
|
|
1994
|
+
mountPath: DEPLOYMENT_STORE_MOUNT_PATH,
|
|
1995
|
+
skillsPrefix: agentStoreKeys.skillsPrefix(),
|
|
1996
|
+
workspacePrefix: agentStoreKeys.workspacePrefix(),
|
|
1997
|
+
sessionWorkspacePrefix: (sessionId) => agentStoreKeys.sessionWorkspacePrefix(sessionId),
|
|
1998
|
+
label: "deployment store",
|
|
1999
|
+
};
|
|
2000
|
+
if (v2Hosting.kind === "cursor-managed") {
|
|
2001
|
+
return Object.assign({ sink: cursorHostedFiles({
|
|
2002
|
+
resolveStoreSourceId: () => v2Hosting.resolveStoreIds().deploymentStoreSourceId,
|
|
2003
|
+
}) }, deployment);
|
|
2004
|
+
}
|
|
2005
|
+
if (v2Hosting.kind === "not-v2" && isCursorHostedFilesAvailable()) {
|
|
2006
|
+
return Object.assign({ sink: cursorHostedFiles() }, deployment);
|
|
2007
|
+
}
|
|
2008
|
+
if (isCursorUserStoreFilesAvailable()) {
|
|
2009
|
+
return {
|
|
2010
|
+
sink: cursorHostedFiles({
|
|
2011
|
+
storeSourceId: AGENT_SERVE_USER_STORE_FILES_SOURCE_ID,
|
|
2012
|
+
}),
|
|
2013
|
+
mountPath: USER_STORE_MOUNT_PATH,
|
|
2014
|
+
skillsPrefix: agentStoreKeys.userSkillsPrefix(this.project.name),
|
|
2015
|
+
workspacePrefix: agentStoreKeys.userWorkspacePrefix(this.project.name),
|
|
2016
|
+
sessionWorkspacePrefix: (sessionId) => agentStoreKeys.userSessionWorkspacePrefix(this.project.name, sessionId),
|
|
2017
|
+
label: "user store",
|
|
2018
|
+
};
|
|
2019
|
+
}
|
|
2020
|
+
return undefined;
|
|
2021
|
+
}
|
|
2022
|
+
buildTurnRequest(record, turnId, prompt, isFirstTurn, emit, eventContext, onUpdate, signal, images) {
|
|
2023
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2024
|
+
var _a, _b, _c;
|
|
2025
|
+
var _d;
|
|
2026
|
+
const agent = this.project.agent;
|
|
2027
|
+
// Per-session cloud attach (channel/tool handoff) forces cloud runtime
|
|
2028
|
+
// even when the agent defaults to local for chat server tools.
|
|
2029
|
+
const runtime = resolveSessionRuntime({
|
|
2030
|
+
agentRuntime: agent.runtime,
|
|
2031
|
+
cloudOverride: record.cloudOverride,
|
|
2032
|
+
});
|
|
2033
|
+
// Fail closed: only the local harness can enforce the built-in tool
|
|
2034
|
+
// allowlist, and a restriction that silently does not apply is worse
|
|
2035
|
+
// than a refused turn. Non-local agents already fail discovery; this
|
|
2036
|
+
// guards hybrid agents' per-send cloud sessions.
|
|
2037
|
+
//
|
|
2038
|
+
// Factory roots omit `allowedTools` at discovery (the live handle is
|
|
2039
|
+
// resolved per session). The allowlist guard below therefore never
|
|
2040
|
+
// fires for `defineAgent({ root: (ctx) => … })` with omitted `tools`,
|
|
2041
|
+
// and a per-send `cloud` attach would skip the virtual-root allowlist
|
|
2042
|
+
// and run unrestricted on a VM. Any authored root refuses non-local turns.
|
|
2043
|
+
if (runtime !== "local" && this.hasRootOverride()) {
|
|
2044
|
+
throw new Error(`Agent "${agent.name}" declares root, which cannot be served on ${runtime} turns. Refusing the session rather than running it against the host VM.`);
|
|
2045
|
+
}
|
|
2046
|
+
if (runtime !== "local" && agent.allowedTools !== undefined) {
|
|
2047
|
+
throw new Error(`Agent "${agent.name}" declares a built-in tool allowlist (tools), which cannot be enforced on ${runtime} turns. Refusing the session rather than running it unrestricted.`);
|
|
2048
|
+
}
|
|
2049
|
+
// Same fail-closed posture for per-session connection features:
|
|
2050
|
+
// advertised tools are server tools (in-process, local turns only) and
|
|
2051
|
+
// per-session auth headers cannot reach a cloud VM's (or the Grok Bot
|
|
2052
|
+
// harness's) MCP config. A turn that silently runs without its
|
|
2053
|
+
// per-session tools or identity is worse than a refused one. Non-local
|
|
2054
|
+
// agents already fail discovery; this guards hybrid agents' per-send
|
|
2055
|
+
// cloud sessions.
|
|
2056
|
+
const perSessionConnection = agent.connections.find(connection => connection.advertiseTools === true || connection.auth !== undefined);
|
|
2057
|
+
if (runtime !== "local" && perSessionConnection !== undefined) {
|
|
2058
|
+
throw new Error(`Agent "${agent.name}" connection "${perSessionConnection.name}" uses advertiseTools/auth, which only run on local turns. Refusing the session rather than running it without the connection's tools or identity.`);
|
|
2059
|
+
}
|
|
2060
|
+
const mergedCloud = runtime === "cloud"
|
|
2061
|
+
? mergeCloudOptions({
|
|
2062
|
+
base: agent.cloud,
|
|
2063
|
+
override: record.cloudOverride,
|
|
2064
|
+
})
|
|
2065
|
+
: undefined;
|
|
2066
|
+
const authoredRoot = this.hasRootOverride();
|
|
2067
|
+
const root = runtime === "local"
|
|
2068
|
+
? yield this.resolveFileSystem({
|
|
2069
|
+
sessionId: record.sessionId,
|
|
2070
|
+
continuationKey: (_d = record.continuationKey) !== null && _d !== void 0 ? _d : undefined,
|
|
2071
|
+
cache: true,
|
|
2072
|
+
cwd: this.harnessCwd(record),
|
|
2073
|
+
})
|
|
2074
|
+
: undefined;
|
|
2075
|
+
const harnessTools = runtime === "local" && root !== undefined
|
|
2076
|
+
? authoredRoot
|
|
2077
|
+
? {
|
|
2078
|
+
tools: resolveFileSystemTools({
|
|
2079
|
+
root,
|
|
2080
|
+
tools: agent.allowedTools,
|
|
2081
|
+
}),
|
|
2082
|
+
}
|
|
2083
|
+
: resolveHostHarnessToolOptions(agent.allowedTools)
|
|
2084
|
+
: undefined;
|
|
2085
|
+
const fileTools = root === undefined
|
|
2086
|
+
? []
|
|
2087
|
+
: synthesizeFileSystemTools(root, authoredRoot
|
|
2088
|
+
? rootFileToolsFromAuthored(agent.allowedTools)
|
|
2089
|
+
: hostFileToolsFromAuthored(agent.allowedTools));
|
|
2090
|
+
// Host-default keeps native SemanticSearch (the host index).
|
|
2091
|
+
// `localFileSystem.search` is empty, so synthesizing would
|
|
2092
|
+
// replace a working index with an empty custom tool.
|
|
2093
|
+
const synthesized = root === undefined
|
|
2094
|
+
? []
|
|
2095
|
+
: this.withTurnScratchTools(authoredRoot ? fileTools : fileTools.filter(tool => tool.name !== "SemanticSearch"), record.sessionId);
|
|
2096
|
+
let turnPrompt = prompt;
|
|
2097
|
+
if (isFirstTurn) {
|
|
2098
|
+
const contextBlock = buildSessionContextBlock({
|
|
2099
|
+
sessionId: record.sessionId,
|
|
2100
|
+
channelId: record.channelId,
|
|
2101
|
+
channelKind: (_b = (_a = this.channelsById.get(record.channelId)) === null || _a === void 0 ? void 0 : _a.meta) === null || _b === void 0 ? void 0 : _b.kind,
|
|
2102
|
+
environment: isDevMode() ? "local dev server" : "hosted deployment",
|
|
2103
|
+
now: record.asOf === undefined ? undefined : sessionClock(record.asOf)(),
|
|
2104
|
+
});
|
|
2105
|
+
turnPrompt = `${contextBlock}\n\n${prompt}`;
|
|
2106
|
+
}
|
|
2107
|
+
if (runtime === "cloud") {
|
|
2108
|
+
// Cloud VMs have no materialized workspace: ship the full context,
|
|
2109
|
+
// including agent tool script bodies. Authored skills and workspace
|
|
2110
|
+
// seeds land on the store mount (skills for native discovery, seeds
|
|
2111
|
+
// for the model's file tools), per-turn workspaceFiles land under
|
|
2112
|
+
// the session prefix, and on hosted deployments the shared memory
|
|
2113
|
+
// journal is on that mount too — say where. When the turn carries
|
|
2114
|
+
// the session-bound server-tool MCP endpoint, the preamble catalogs
|
|
2115
|
+
// server tools under that MCP server name *before* the agent-tool
|
|
2116
|
+
// "write the script if missing" recipe, so a model told to "use
|
|
2117
|
+
// <server tool>" calls MCP instead of inventing a .sh.
|
|
2118
|
+
const hosted = isCursorHostedFilesAvailable();
|
|
2119
|
+
const store = hosted || isCursorUserStoreFilesAvailable() ? this.cloudStore() : undefined;
|
|
2120
|
+
let preamble = "";
|
|
2121
|
+
const sections = [];
|
|
2122
|
+
const pending = record.pendingWorkspaceFiles;
|
|
2123
|
+
if (isFirstTurn) {
|
|
2124
|
+
preamble = buildAgentsMdContent(agent, {
|
|
2125
|
+
includeScripts: true,
|
|
2126
|
+
serverToolsMcpName: this.cloudServerToolsConnection === undefined
|
|
2127
|
+
? undefined
|
|
2128
|
+
: SERVER_TOOLS_MCP_SERVER_NAME,
|
|
2129
|
+
});
|
|
2130
|
+
}
|
|
2131
|
+
if (store !== undefined) {
|
|
2132
|
+
if (isFirstTurn) {
|
|
2133
|
+
// Synced whenever a store is reachable, not just when skills or
|
|
2134
|
+
// seeds exist: removing the last one has to delete its store copies
|
|
2135
|
+
// too.
|
|
2136
|
+
const [, seedsSynced] = yield Promise.all([
|
|
2137
|
+
this.ensureCloudSkillsSynced(store),
|
|
2138
|
+
this.ensureCloudWorkspaceSynced(store),
|
|
2139
|
+
]);
|
|
2140
|
+
// Only name the mount when the bytes landed; a failed sync must
|
|
2141
|
+
// not send the model looking for files that are not there.
|
|
2142
|
+
if (seedsSynced && agent.seedFiles.length > 0) {
|
|
2143
|
+
sections.push(buildCloudWorkspaceSection(`${store.mountPath}/${store.workspacePrefix}`));
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
if (pending !== undefined && Object.keys(pending).length > 0) {
|
|
2147
|
+
const synced = yield this.syncCloudSessionWorkspaceFiles(store, record.sessionId, pending);
|
|
2148
|
+
if (synced !== undefined) {
|
|
2149
|
+
sections.push(buildCloudSessionWorkspaceSection(synced.dir, synced.relPaths));
|
|
2150
|
+
yield this.clearPendingWorkspaceFiles(record.sessionId, pending);
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
else if (pending !== undefined && Object.keys(pending).length > 0) {
|
|
2155
|
+
this.logger(`[workspace] no cloud store on this hosting; dropping workspaceFiles for session ${record.sessionId}: ${Object.keys(pending).join(", ")}`);
|
|
2156
|
+
yield this.clearPendingWorkspaceFiles(record.sessionId, pending);
|
|
2157
|
+
}
|
|
2158
|
+
if (isFirstTurn && hosted) {
|
|
2159
|
+
sections.push(buildCloudMemorySection(this.project.name));
|
|
2160
|
+
}
|
|
2161
|
+
if (preamble !== "" || sections.length > 0) {
|
|
2162
|
+
turnPrompt = [preamble, ...sections, turnPrompt].filter(part => part !== "").join("\n\n");
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
else if (isFirstTurn && runtime === "local" && authoredRoot) {
|
|
2166
|
+
// The harness reports no path for a root it never mounted.
|
|
2167
|
+
turnPrompt = `<user_info>\nWorkspace Path: /\n</user_info>\n\n${turnPrompt}`;
|
|
2168
|
+
}
|
|
2169
|
+
// A virtual root has no scaffold dir either, so its catalog carries the
|
|
2170
|
+
// synthesized file tools and embedded script bodies.
|
|
2171
|
+
const localInstructions = runtime === "local"
|
|
2172
|
+
? authoredRoot
|
|
2173
|
+
? buildAgentsMdContent(Object.assign(Object.assign({}, agent), { tools: [...agent.tools, ...synthesized] }), { includeScripts: true })
|
|
2174
|
+
: buildAgentsMdContent(agent, {
|
|
2175
|
+
includeScripts: this.hasBorrowedWorkspace(record),
|
|
2176
|
+
})
|
|
2177
|
+
: undefined;
|
|
2178
|
+
return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ sessionId: record.sessionId }, (record.sdkAgentId === undefined ? {} : { sdkAgentId: record.sdkAgentId })), { workspaceDir: this.harnessCwd(record), sandbox: authoredRoot ? false : ((_c = agent.local) === null || _c === void 0 ? void 0 : _c.sandbox) === true }), (runtime === "local" && harnessTools !== undefined
|
|
2179
|
+
? Object.assign(Object.assign({}, (harnessTools.tools === undefined ? {} : { tools: harnessTools.tools })), (harnessTools.disallowedTools === undefined
|
|
2180
|
+
? {}
|
|
2181
|
+
: { disallowedTools: harnessTools.disallowedTools })) : {})), { runnerStateDir: join(this.stateRoot, "runner"), prompt: turnPrompt, isFirstTurn }), (images !== undefined && images.length > 0 ? { images } : {})), (agent.model === undefined ? {} : { model: agent.model })), (agent.modelFallbacks === undefined ? {} : { modelFallbacks: agent.modelFallbacks })), { runtime, agentName: agent.name }), (mergedCloud === undefined ? {} : { cloud: mergedCloud })), {
|
|
2182
|
+
// Local turns send the composed instructions as the harness system
|
|
2183
|
+
// prompt; grokbot turns carry them into the hosted session, which
|
|
2184
|
+
// surfaces them in the Sand system prompt. Cloud turns inline them into
|
|
2185
|
+
// the first prompt above instead. Script bodies are embedded whenever
|
|
2186
|
+
// the harness cannot reach the scaffold dir: always for grokbot (the
|
|
2187
|
+
// box never sees the session workspace) and for a borrowed local cwd
|
|
2188
|
+
// (PR worktree, shared checkout); otherwise the catalog references the
|
|
2189
|
+
// on-disk paths under the harness cwd.
|
|
2190
|
+
instructions: runtime === "grokbot"
|
|
2191
|
+
? buildAgentsMdContent(agent, { includeScripts: true })
|
|
2192
|
+
: localInstructions }), (runtime === "cloud" && !isCursorHostedFilesAvailable()
|
|
2193
|
+
? { agentServeAgent: this.project.name }
|
|
2194
|
+
: {})), { customTools: runtime === "local" ? yield this.buildCustomTools(record, turnId, emit, synthesized) : {}, mcpServers: this.buildMcpServers(runtime, record.sessionId, yield this.evaluateConnectionAuth(record)) }), (runtime === "local" &&
|
|
2195
|
+
(this.hasAttachedAuthedConnections() ||
|
|
2196
|
+
this.project.agent.connections.some(connection => isCursorAccountTransport(connection.transport)))
|
|
2197
|
+
? { perSendMcpServers: true }
|
|
2198
|
+
: {})), { subagents: buildRunnerSubagents(agent), onUpdate,
|
|
2199
|
+
signal,
|
|
2200
|
+
// Local follow-ups expire any lingering active run so a preempted turn
|
|
2201
|
+
// (or one orphaned by a crashed process) never blocks this send.
|
|
2202
|
+
forceExpireActiveRun: runtime === "local" && !isFirstTurn, onRequestId: requestId => {
|
|
2203
|
+
emit({ type: "turn.bound", data: { requestId } });
|
|
2204
|
+
}, onSdkAgentId: (sdkAgentId) => __awaiter(this, void 0, void 0, function* () {
|
|
2205
|
+
if (record.sdkAgentId === sdkAgentId) {
|
|
2206
|
+
return;
|
|
2207
|
+
}
|
|
2208
|
+
yield this.updateRecord(record.sessionId, current => (Object.assign(Object.assign({}, current), { sdkAgentId })));
|
|
2209
|
+
record.sdkAgentId = sdkAgentId;
|
|
2210
|
+
eventContext.session = Object.assign(Object.assign({}, eventContext.session), { sdkAgentId });
|
|
2211
|
+
yield this.appendEvent(record.sessionId, {
|
|
2212
|
+
type: "agent.bound",
|
|
2213
|
+
turnId,
|
|
2214
|
+
data: Object.assign({ sdkAgentId }, (sdkAgentId.startsWith("bc-")
|
|
2215
|
+
? { url: `https://cursor.com/agents/${sdkAgentId}` }
|
|
2216
|
+
: {})),
|
|
2217
|
+
}, eventContext);
|
|
2218
|
+
}) });
|
|
2219
|
+
});
|
|
2220
|
+
}
|
|
2221
|
+
/**
|
|
2222
|
+
* Connection names a cloud turn can only reach through the host connection
|
|
2223
|
+
* bridge on the tool MCP endpoint: filtered account connections without a
|
|
2224
|
+
* public bridge URL, and host-OAuth connections whose tokens live on this
|
|
2225
|
+
* host.
|
|
2226
|
+
*/
|
|
2227
|
+
cloudBridgeableConnectionNames() {
|
|
2228
|
+
const names = [];
|
|
2229
|
+
for (const connection of this.project.agent.connections) {
|
|
2230
|
+
if (isHostOauthConnectionTransport(connection.transport)) {
|
|
2231
|
+
names.push(connection.name);
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
for (const resolved of this.resolvedConnections) {
|
|
2235
|
+
if (resolved.accountServersFilter !== undefined && resolved.cloudUrl === undefined) {
|
|
2236
|
+
names.push(resolved.name);
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
return names;
|
|
2240
|
+
}
|
|
2241
|
+
/**
|
|
2242
|
+
* True when cloud turns reach `name` through the host connection bridge —
|
|
2243
|
+
* the gate the tool MCP endpoint applies before forwarding a bridge
|
|
2244
|
+
* request to `host.mcp`, so arbitrary names stay unreachable from a VM.
|
|
2245
|
+
*/
|
|
2246
|
+
isCloudBridgedConnection(name) {
|
|
2247
|
+
if (this.cloudServerToolsConnection === undefined) {
|
|
2248
|
+
return false;
|
|
2249
|
+
}
|
|
2250
|
+
return this.cloudBridgeableConnectionNames().includes(name);
|
|
2251
|
+
}
|
|
2252
|
+
/**
|
|
2253
|
+
* Session-bound transport for one host-bridged connection: the tool MCP
|
|
2254
|
+
* endpoint with the connection named, so `tools/list` / `tools/call`
|
|
2255
|
+
* forward to the host-side client (`host.mcp`) where the credential —
|
|
2256
|
+
* account bridge secret or host OAuth token — actually lives.
|
|
2257
|
+
*/
|
|
2258
|
+
cloudBridgeTransport(sessionId, connectionName) {
|
|
2259
|
+
const connection = this.currentCloudServerToolsConnection();
|
|
2260
|
+
if (connection === undefined || !("url" in connection)) {
|
|
2261
|
+
return undefined;
|
|
2262
|
+
}
|
|
2263
|
+
const url = new URL(connection.url);
|
|
2264
|
+
url.searchParams.set("agentkitSessionId", sessionId);
|
|
2265
|
+
url.searchParams.set("connection", connectionName);
|
|
2266
|
+
return Object.assign(Object.assign({}, connection), { url: url.toString() });
|
|
2267
|
+
}
|
|
2268
|
+
/**
|
|
2269
|
+
* Authored + resolved MCP servers for a turn. Skips advertised
|
|
2270
|
+
* connections (those surface as named server tools). On cloud turns,
|
|
2271
|
+
* connections whose credential lives on this host — filtered account
|
|
2272
|
+
* connections without a public bridge URL, host-OAuth servers — mount
|
|
2273
|
+
* as host connection bridges over the tool MCP endpoint instead of
|
|
2274
|
+
* being omitted or failing the turn closed.
|
|
2275
|
+
*/
|
|
2276
|
+
buildMcpServers(runtime, sessionId, sessionAuth) {
|
|
2277
|
+
var _a;
|
|
2278
|
+
// The Grok Bot harness manages its own MCP surface; authored
|
|
2279
|
+
// connections are refused at discovery, so there is nothing to mount.
|
|
2280
|
+
if (runtime === "grokbot") {
|
|
2281
|
+
return {};
|
|
2282
|
+
}
|
|
2283
|
+
// Advertised connections surface as named server tools instead of an
|
|
2284
|
+
// attached MCP server (each tool appears exactly once).
|
|
2285
|
+
const advertisedNames = new Set(this.project.agent.connections
|
|
2286
|
+
.filter(connection => connection.advertiseTools === true)
|
|
2287
|
+
.map(connection => connection.name));
|
|
2288
|
+
const hostConnectionNames = new Set(((_a = this.project.agent.hostConnections) !== null && _a !== void 0 ? _a : []).map(connection => connection.name));
|
|
2289
|
+
const bridgedNames = new Set(runtime === "cloud" &&
|
|
2290
|
+
sessionId !== undefined &&
|
|
2291
|
+
this.cloudServerToolsConnection !== undefined &&
|
|
2292
|
+
"url" in this.cloudServerToolsConnection
|
|
2293
|
+
? this.cloudBridgeableConnectionNames()
|
|
2294
|
+
: []);
|
|
2295
|
+
assertCloudCanHonorAccountServersFilters({
|
|
2296
|
+
runtime,
|
|
2297
|
+
resolvedConnections: this.resolvedConnections.filter(resolved => !advertisedNames.has(resolved.name) && !hostConnectionNames.has(resolved.name)),
|
|
2298
|
+
bridgedNames,
|
|
2299
|
+
});
|
|
2300
|
+
const bridgeTransport = (name) => sessionId === undefined || !bridgedNames.has(name)
|
|
2301
|
+
? undefined
|
|
2302
|
+
: this.cloudBridgeTransport(sessionId, name);
|
|
2303
|
+
const servers = {};
|
|
2304
|
+
for (const connection of this.project.agent.connections) {
|
|
2305
|
+
if (advertisedNames.has(connection.name) || hostConnectionNames.has(connection.name)) {
|
|
2306
|
+
continue;
|
|
2307
|
+
}
|
|
2308
|
+
if (isSymbolicConnectionTransport(connection.transport)) {
|
|
2309
|
+
continue;
|
|
2310
|
+
}
|
|
2311
|
+
// A cloud VM cannot use a credential that lives on this host, and the
|
|
2312
|
+
// cloud API rejects the `oauth` key outright ("Unrecognized key(s) in
|
|
2313
|
+
// object: 'oauth'"), which fails the whole turn. Mount the host
|
|
2314
|
+
// connection bridge instead when serve has a cloud tools URL; omit
|
|
2315
|
+
// otherwise (host tools via `ctx.host.mcp` still reach the server).
|
|
2316
|
+
if (runtime === "cloud" && isHostOauthConnectionTransport(connection.transport)) {
|
|
2317
|
+
const bridged = bridgeTransport(connection.name);
|
|
2318
|
+
if (bridged !== undefined) {
|
|
2319
|
+
servers[connection.name] = bridged;
|
|
2320
|
+
}
|
|
2321
|
+
continue;
|
|
2322
|
+
}
|
|
2323
|
+
// Per-session auth: merge the evaluated headers over the static
|
|
2324
|
+
// ones. With no evaluation in hand (prewarm has no session) the
|
|
2325
|
+
// connection is omitted rather than attached without its identity.
|
|
2326
|
+
if (connection.auth !== undefined) {
|
|
2327
|
+
const evaluated = sessionAuth === null || sessionAuth === void 0 ? void 0 : sessionAuth.get(connection.name);
|
|
2328
|
+
if (evaluated === undefined) {
|
|
2329
|
+
continue;
|
|
2330
|
+
}
|
|
2331
|
+
const transport = connection.transport;
|
|
2332
|
+
if ("url" in transport) {
|
|
2333
|
+
servers[connection.name] = Object.assign(Object.assign({}, transport), { headers: Object.assign(Object.assign({}, transport.headers), evaluated) });
|
|
2334
|
+
}
|
|
2335
|
+
continue;
|
|
2336
|
+
}
|
|
2337
|
+
servers[connection.name] = connection.transport;
|
|
2338
|
+
}
|
|
2339
|
+
for (const resolved of this.resolvedConnections) {
|
|
2340
|
+
if (advertisedNames.has(resolved.name) || hostConnectionNames.has(resolved.name)) {
|
|
2341
|
+
continue;
|
|
2342
|
+
}
|
|
2343
|
+
const url = runtime === "cloud" ? resolved.cloudUrl : resolved.localUrl;
|
|
2344
|
+
if (url === undefined) {
|
|
2345
|
+
// Filtered account connections bridge through the host so the
|
|
2346
|
+
// `servers` filter is honored; unfiltered ones keep their documented
|
|
2347
|
+
// fallback to the account's native connectors on the VM.
|
|
2348
|
+
const bridged = bridgeTransport(resolved.name);
|
|
2349
|
+
if (bridged !== undefined) {
|
|
2350
|
+
servers[resolved.name] = bridged;
|
|
2351
|
+
}
|
|
2352
|
+
continue;
|
|
2353
|
+
}
|
|
2354
|
+
const authored = this.authoredConnection(resolved.name);
|
|
2355
|
+
if (sessionId !== undefined &&
|
|
2356
|
+
authored !== undefined &&
|
|
2357
|
+
isCursorAccountTransport(authored.transport)) {
|
|
2358
|
+
const bound = new URL(url);
|
|
2359
|
+
bound.searchParams.set("agentkitSessionId", sessionId);
|
|
2360
|
+
servers[resolved.name] = {
|
|
2361
|
+
url: bound.toString(),
|
|
2362
|
+
headers: resolved.headers,
|
|
2363
|
+
};
|
|
2364
|
+
continue;
|
|
2365
|
+
}
|
|
2366
|
+
servers[resolved.name] = {
|
|
2367
|
+
url,
|
|
2368
|
+
headers: resolved.headers,
|
|
2369
|
+
};
|
|
2370
|
+
}
|
|
2371
|
+
if (runtime === "cloud" &&
|
|
2372
|
+
sessionId !== undefined &&
|
|
2373
|
+
this.cloudServerToolsConnection !== undefined &&
|
|
2374
|
+
this.project.agent.tools.some(tool => tool.execution === "server")) {
|
|
2375
|
+
if (servers[SERVER_TOOLS_MCP_SERVER_NAME] !== undefined) {
|
|
2376
|
+
throw new Error(`MCP connection name "${SERVER_TOOLS_MCP_SERVER_NAME}" is reserved for authored server tools on cloud turns.`);
|
|
2377
|
+
}
|
|
2378
|
+
const connection = this.currentCloudServerToolsConnection();
|
|
2379
|
+
if (connection !== undefined && "url" in connection) {
|
|
2380
|
+
const url = new URL(connection.url);
|
|
2381
|
+
url.searchParams.set("agentkitSessionId", sessionId);
|
|
2382
|
+
servers[SERVER_TOOLS_MCP_SERVER_NAME] = Object.assign(Object.assign({}, connection), { url: url.toString() });
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
return servers;
|
|
2386
|
+
}
|
|
2387
|
+
/**
|
|
2388
|
+
* True when a connection with per-session `auth` attaches to the model
|
|
2389
|
+
* (not advertised). These turns set
|
|
2390
|
+
* {@link RunnerTurnRequest.perSendMcpServers} so the evaluated headers
|
|
2391
|
+
* ride every send instead of being pinned on the cached agent handle.
|
|
2392
|
+
*/
|
|
2393
|
+
hasAttachedAuthedConnections() {
|
|
2394
|
+
return this.project.agent.connections.some(connection => connection.auth !== undefined && connection.advertiseTools !== true);
|
|
2395
|
+
}
|
|
2396
|
+
/**
|
|
2397
|
+
* Evaluate per-session connection auth (`defineConnection({ auth })`)
|
|
2398
|
+
* for connections that attach to the model this turn. Advertised
|
|
2399
|
+
* connections evaluate their own auth inside the tool synthesis path. A
|
|
2400
|
+
* callback that throws fails the turn — a turn never silently runs
|
|
2401
|
+
* without the connection's identity.
|
|
2402
|
+
*/
|
|
2403
|
+
evaluateConnectionAuth(record) {
|
|
2404
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2405
|
+
const authed = this.project.agent.connections.filter(connection => connection.auth !== undefined && connection.advertiseTools !== true);
|
|
2406
|
+
if (authed.length === 0) {
|
|
2407
|
+
return undefined;
|
|
2408
|
+
}
|
|
2409
|
+
const session = this.sessionInfo(record);
|
|
2410
|
+
const evaluated = new Map();
|
|
2411
|
+
for (const connection of authed) {
|
|
2412
|
+
const auth = connection.auth;
|
|
2413
|
+
if (auth === undefined) {
|
|
2414
|
+
continue;
|
|
2415
|
+
}
|
|
2416
|
+
try {
|
|
2417
|
+
evaluated.set(connection.name, (yield auth(session)).headers);
|
|
2418
|
+
}
|
|
2419
|
+
catch (error) {
|
|
2420
|
+
throw new Error(`Connection "${connection.name}" auth failed: ${describeError(error)}`, {
|
|
2421
|
+
cause: error,
|
|
2422
|
+
});
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
return evaluated;
|
|
2426
|
+
});
|
|
2427
|
+
}
|
|
2428
|
+
buildCustomTools(record_1, turnId_1, emit_1) {
|
|
2429
|
+
return __awaiter(this, arguments, void 0, function* (record, turnId, emit, extraTools = []) {
|
|
2430
|
+
const agent = this.project.agent;
|
|
2431
|
+
const tools = {};
|
|
2432
|
+
const register = (tool) => {
|
|
2433
|
+
tools[tool.name] = Object.assign(Object.assign(Object.assign({ description: tool.description, inputSchema: tool.inputSchema }, (tool.outputSchema === undefined
|
|
2434
|
+
? {}
|
|
2435
|
+
: {
|
|
2436
|
+
outputSchema: tool.outputSchema,
|
|
2437
|
+
})), (tool.annotations === undefined ? {} : { annotations: tool.annotations })), { execute: (args, context) => __awaiter(this, void 0, void 0, function* () {
|
|
2438
|
+
return this.executeServerTool({
|
|
2439
|
+
tool,
|
|
2440
|
+
input: args,
|
|
2441
|
+
toolCallId: context.toolCallId,
|
|
2442
|
+
record,
|
|
2443
|
+
turnId,
|
|
2444
|
+
emit,
|
|
2445
|
+
});
|
|
2446
|
+
}) });
|
|
2447
|
+
};
|
|
2448
|
+
for (const tool of [...agent.tools, ...extraTools]) {
|
|
2449
|
+
if (tool.execution !== "server") {
|
|
2450
|
+
continue;
|
|
2451
|
+
}
|
|
2452
|
+
if (typeof tool.definition.execute !== "function") {
|
|
2453
|
+
continue;
|
|
2454
|
+
}
|
|
2455
|
+
register(tool);
|
|
2456
|
+
}
|
|
2457
|
+
// Advertised connections (`defineConnection({ advertiseTools })`):
|
|
2458
|
+
// named passthroughs resolved fresh on every turn — reminder fires and
|
|
2459
|
+
// post-restart follow-ups included — and a listing failure throws,
|
|
2460
|
+
// failing the turn closed.
|
|
2461
|
+
for (const tool of yield mcpAuthAls.run({
|
|
2462
|
+
sessionId: record.sessionId,
|
|
2463
|
+
turnId,
|
|
2464
|
+
callId: newToolCallId("mcp"),
|
|
2465
|
+
toolName: "mcp",
|
|
2466
|
+
emit,
|
|
2467
|
+
}, () => resolveAdvertisedTools({
|
|
2468
|
+
agent,
|
|
2469
|
+
session: this.sessionInfo(record),
|
|
2470
|
+
hostMcp: this.advertiseMcp(),
|
|
2471
|
+
}))) {
|
|
2472
|
+
register(tool);
|
|
2473
|
+
}
|
|
2474
|
+
return tools;
|
|
2475
|
+
});
|
|
2476
|
+
}
|
|
2477
|
+
/**
|
|
2478
|
+
* True while `sessionId` has a turn in flight. The host connection bridge
|
|
2479
|
+
* applies this before forwarding to `host.mcp` — the same window
|
|
2480
|
+
* {@link callActiveServerTool} enforces for authored server tools — so an
|
|
2481
|
+
* endpoint credential that outlives a turn (e.g. in a VM's MCP config)
|
|
2482
|
+
* cannot reach host-credentialed connectors between turns.
|
|
2483
|
+
*/
|
|
2484
|
+
hasActiveTurn(sessionId) {
|
|
2485
|
+
return this.activeToolContexts.has(sessionId);
|
|
2486
|
+
}
|
|
2487
|
+
callActiveServerTool(sessionId, toolName, input) {
|
|
2488
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2489
|
+
const active = this.activeToolContexts.get(sessionId);
|
|
2490
|
+
if (active === undefined) {
|
|
2491
|
+
return toolErrorResult(`Session ${sessionId} has no active turn for tool "${toolName}".`);
|
|
2492
|
+
}
|
|
2493
|
+
const tool = this.project.agent.tools.find(candidate => candidate.name === toolName && candidate.execution === "server");
|
|
2494
|
+
if (tool === undefined) {
|
|
2495
|
+
return toolErrorResult(`Unknown server tool "${toolName}".`);
|
|
2496
|
+
}
|
|
2497
|
+
const toolCallId = newToolCallId(toolName);
|
|
2498
|
+
this.logger(`[session] cloud HTTP MCP tool ${toolName} dispatch (${sessionId}, ${toolCallId})`);
|
|
2499
|
+
const result = yield this.executeServerTool({
|
|
2500
|
+
tool,
|
|
2501
|
+
input,
|
|
2502
|
+
toolCallId,
|
|
2503
|
+
record: active.record,
|
|
2504
|
+
turnId: active.turnId,
|
|
2505
|
+
emit: active.emit,
|
|
2506
|
+
});
|
|
2507
|
+
this.logger(`[session] cloud HTTP MCP tool ${toolName} complete (${sessionId}, ${toolCallId}, isError=${isErrorEnvelope(result)})`);
|
|
2508
|
+
return result;
|
|
2509
|
+
});
|
|
2510
|
+
}
|
|
2511
|
+
executeServerTool(args) {
|
|
2512
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2513
|
+
var _a;
|
|
2514
|
+
var _b;
|
|
2515
|
+
const execute = args.tool.definition.execute;
|
|
2516
|
+
if (args.tool.execution !== "server" || typeof execute !== "function") {
|
|
2517
|
+
return toolErrorResult(`Tool "${args.tool.name}" is not a server tool.`);
|
|
2518
|
+
}
|
|
2519
|
+
const validation = validateToolInput(args.tool.definition.inputSchema, args.input);
|
|
2520
|
+
if (!validation.ok) {
|
|
2521
|
+
return toolErrorResult(`Invalid input for tool "${args.tool.name}": ${validation.message}`);
|
|
2522
|
+
}
|
|
2523
|
+
const toolCallId = (_b = args.toolCallId) !== null && _b !== void 0 ? _b : newToolCallId(args.tool.name);
|
|
2524
|
+
const toolCtx = Object.assign({ toolCallId, session: this.sessionInfo(args.record), workspaceDir: this.harnessCwd(args.record), stateRoot: this.stateRoot, host: this.hostForSession(args.record.sessionId), artifacts: this.artifactsApi({
|
|
2525
|
+
sessionId: args.record.sessionId,
|
|
2526
|
+
turnId: args.turnId,
|
|
2527
|
+
}), send: (channelId, message, sendOptions = {}) => { var _a; return this.send(channelId, message, Object.assign(Object.assign({}, sendOptions), { auth: (_a = sendOptions.auth) !== null && _a !== void 0 ? _a : args.record.auth })); }, getSession: (channelId, sessionId) => this.getChannelSession(channelId, sessionId), emit: args.emit, now: sessionClock(args.record.asOf) }, (args.record.asOf === undefined ? {} : { asOf: args.record.asOf }));
|
|
2528
|
+
try {
|
|
2529
|
+
// Before the approval gate: a dry run is unattended, so parking a write
|
|
2530
|
+
// for a human would hang the turn rather than answer it.
|
|
2531
|
+
const decision = decideToolCall(Object.assign(Object.assign(Object.assign({ dryRun: args.record.dryRun === true, effect: args.tool.definition.effect }, (args.tool.advertised === true
|
|
2532
|
+
? {
|
|
2533
|
+
mcpTool: {
|
|
2534
|
+
name: args.tool.name,
|
|
2535
|
+
annotations: args.tool.annotations,
|
|
2536
|
+
},
|
|
2537
|
+
}
|
|
2538
|
+
: {})), { dryRunResult: args.tool.definition.dryRunResult, input: validation.value, asOf: args.record.asOf, timeArgs: args.tool.definition.timeArgs }), (args.tool.advertised === true && args.record.asOf !== undefined
|
|
2539
|
+
? { timeWindow: schemaTimeWindowKeys(args.tool.inputSchema) }
|
|
2540
|
+
: {})));
|
|
2541
|
+
if (decision.kind === "answer") {
|
|
2542
|
+
(_a = this.stubbedCallIds.get(args.record.sessionId)) === null || _a === void 0 ? void 0 : _a.add(toolCallId);
|
|
2543
|
+
this.logger(`[session] tool ${args.tool.name} answered by host (${toolCallId})`);
|
|
2544
|
+
return normalizeToolResult(decision.result);
|
|
2545
|
+
}
|
|
2546
|
+
if (decision.kind === "unbounded") {
|
|
2547
|
+
this.logger(`[session] tool ${args.tool.name} refused, unbounded_time (${toolCallId})`);
|
|
2548
|
+
return toolErrorResult(decision.message);
|
|
2549
|
+
}
|
|
2550
|
+
if (decision.kind === "refuse") {
|
|
2551
|
+
this.logger(`[session] tool ${args.tool.name} refused, effect unclassified (${toolCallId})`);
|
|
2552
|
+
// The coverage signal: a run reports how much of itself was runnable.
|
|
2553
|
+
args.emit({
|
|
2554
|
+
type: "action.effect_unclassified",
|
|
2555
|
+
data: { callId: toolCallId, toolName: args.tool.name },
|
|
2556
|
+
});
|
|
2557
|
+
return toolErrorResult(decision.message);
|
|
2558
|
+
}
|
|
2559
|
+
const needsGate = yield evaluateNeedsApproval(args.tool.definition.needsApproval, validation.value);
|
|
2560
|
+
if (needsGate) {
|
|
2561
|
+
const decision = yield this.parkForApproval({
|
|
2562
|
+
sessionId: args.record.sessionId,
|
|
2563
|
+
turnId: args.turnId,
|
|
2564
|
+
callId: toolCallId,
|
|
2565
|
+
toolName: args.tool.name,
|
|
2566
|
+
args: validation.value,
|
|
2567
|
+
emit: args.emit,
|
|
2568
|
+
});
|
|
2569
|
+
if (decision === "deny") {
|
|
2570
|
+
return toolErrorResult(`Tool "${args.tool.name}" was denied by a human approver.`);
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
const started = Date.now();
|
|
2574
|
+
this.logger(`[session] tool ${args.tool.name} start`);
|
|
2575
|
+
const result = yield this.runToolExecute({
|
|
2576
|
+
record: args.record,
|
|
2577
|
+
turnId: args.turnId,
|
|
2578
|
+
callId: toolCallId,
|
|
2579
|
+
toolName: args.tool.name,
|
|
2580
|
+
}, () => mcpAuthAls.run({
|
|
2581
|
+
sessionId: args.record.sessionId,
|
|
2582
|
+
turnId: args.turnId,
|
|
2583
|
+
callId: toolCallId,
|
|
2584
|
+
toolName: args.tool.name,
|
|
2585
|
+
emit: args.emit,
|
|
2586
|
+
}, () => execute(validation.value, toolCtx)));
|
|
2587
|
+
this.logger(`[session] tool ${args.tool.name} done (${Date.now() - started}ms)`);
|
|
2588
|
+
return normalizeToolResult(result);
|
|
2589
|
+
}
|
|
2590
|
+
catch (error) {
|
|
2591
|
+
this.logger(`[session] tool ${args.tool.name} failed: ${describeError(error)}`);
|
|
2592
|
+
return toolErrorResult(`Tool "${args.tool.name}" failed: ${describeError(error)}`);
|
|
2593
|
+
}
|
|
2594
|
+
});
|
|
2595
|
+
}
|
|
2596
|
+
/**
|
|
2597
|
+
* Bind outbound inject (Factory / Cursor API `traceparent`) to the
|
|
2598
|
+
* turn span for the whole harness run. Host files / VFS RPCs happen
|
|
2599
|
+
* here, not under `runInCall`. No-op when OTel is off.
|
|
2600
|
+
*/
|
|
2601
|
+
runTurnExecute(record, turnId, fn) {
|
|
2602
|
+
const otel = this.otel;
|
|
2603
|
+
const run = () => Promise.resolve(fn());
|
|
2604
|
+
if (otel === undefined) {
|
|
2605
|
+
return run();
|
|
2606
|
+
}
|
|
2607
|
+
return otel.runInTurn({
|
|
2608
|
+
sessionId: record.sessionId,
|
|
2609
|
+
session: this.sessionInfo(record),
|
|
2610
|
+
turnId,
|
|
2611
|
+
}, run);
|
|
2612
|
+
}
|
|
2613
|
+
/**
|
|
2614
|
+
* Bind outbound inject (Factory / Cursor API `traceparent`) to the
|
|
2615
|
+
* tool span. No-op when OTel is off.
|
|
2616
|
+
*/
|
|
2617
|
+
runToolExecute(args, fn) {
|
|
2618
|
+
const otel = this.otel;
|
|
2619
|
+
const run = () => Promise.resolve(fn());
|
|
2620
|
+
if (otel === undefined) {
|
|
2621
|
+
return run();
|
|
2622
|
+
}
|
|
2623
|
+
return otel.runInCall({
|
|
2624
|
+
sessionId: args.record.sessionId,
|
|
2625
|
+
session: this.sessionInfo(args.record),
|
|
2626
|
+
turnId: args.turnId,
|
|
2627
|
+
callId: args.callId,
|
|
2628
|
+
toolName: args.toolName,
|
|
2629
|
+
}, run);
|
|
2630
|
+
}
|
|
2631
|
+
/**
|
|
2632
|
+
* Park a server-tool call until a human approves or denies. Emits
|
|
2633
|
+
* `action.approval_requested` / `action.approval_resolved` on the
|
|
2634
|
+
* held turn log so the playground and stream see the gate.
|
|
2635
|
+
*/
|
|
2636
|
+
parkForApproval(args) {
|
|
2637
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2638
|
+
const requestedAt = new Date().toISOString();
|
|
2639
|
+
const pendingRecord = {
|
|
2640
|
+
callId: args.callId,
|
|
2641
|
+
turnId: args.turnId,
|
|
2642
|
+
toolName: args.toolName,
|
|
2643
|
+
args: args.args,
|
|
2644
|
+
requestedAt,
|
|
2645
|
+
};
|
|
2646
|
+
const resolutionPromise = this.approvals.wait(args.sessionId, pendingRecord);
|
|
2647
|
+
yield this.updateRecord(args.sessionId, current => { var _a; return (Object.assign(Object.assign({}, current), { pendingApprovals: [
|
|
2648
|
+
...((_a = current.pendingApprovals) !== null && _a !== void 0 ? _a : []).filter(item => item.callId !== args.callId),
|
|
2649
|
+
pendingRecord,
|
|
2650
|
+
] })); });
|
|
2651
|
+
args.emit({
|
|
2652
|
+
type: "action.approval_requested",
|
|
2653
|
+
data: {
|
|
2654
|
+
callId: args.callId,
|
|
2655
|
+
toolName: args.toolName,
|
|
2656
|
+
args: args.args,
|
|
2657
|
+
},
|
|
2658
|
+
});
|
|
2659
|
+
this.logger(`[session] tool ${args.toolName} awaiting approval (${args.callId})`);
|
|
2660
|
+
const { decision, by } = yield resolutionPromise;
|
|
2661
|
+
yield this.updateRecord(args.sessionId, current => { var _a; return (Object.assign(Object.assign({}, current), { pendingApprovals: ((_a = current.pendingApprovals) !== null && _a !== void 0 ? _a : []).filter(item => item.callId !== args.callId) })); });
|
|
2662
|
+
args.emit({
|
|
2663
|
+
type: "action.approval_resolved",
|
|
2664
|
+
data: Object.assign({ callId: args.callId, toolName: args.toolName, decision }, (by === undefined ? {} : { by })),
|
|
2665
|
+
});
|
|
2666
|
+
this.logger(`[session] tool ${args.toolName} approval ${decision} (${args.callId})`);
|
|
2667
|
+
return decision;
|
|
2668
|
+
});
|
|
2669
|
+
}
|
|
2670
|
+
clearPendingApprovals(sessionId) {
|
|
2671
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2672
|
+
const current = yield this.sessions.get(sessionId);
|
|
2673
|
+
if (current === undefined ||
|
|
2674
|
+
current.pendingApprovals === undefined ||
|
|
2675
|
+
current.pendingApprovals.length === 0) {
|
|
2676
|
+
return;
|
|
2677
|
+
}
|
|
2678
|
+
yield this.updateRecord(sessionId, record => (Object.assign(Object.assign({}, record), { pendingApprovals: [] })));
|
|
2679
|
+
});
|
|
2680
|
+
}
|
|
2681
|
+
/** Pending tool approvals for a session (live waiters ∪ persisted). */
|
|
2682
|
+
listPendingApprovals(sessionId) {
|
|
2683
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2684
|
+
var _a;
|
|
2685
|
+
const record = yield this.sessions.get(sessionId);
|
|
2686
|
+
if (record === undefined) {
|
|
2687
|
+
return [];
|
|
2688
|
+
}
|
|
2689
|
+
const live = this.approvals.list(sessionId);
|
|
2690
|
+
if (live.length > 0) {
|
|
2691
|
+
return live;
|
|
2692
|
+
}
|
|
2693
|
+
return (_a = record.pendingApprovals) !== null && _a !== void 0 ? _a : [];
|
|
2694
|
+
});
|
|
2695
|
+
}
|
|
2696
|
+
/**
|
|
2697
|
+
* Approve or deny a parked server-tool call. Unblocks the in-flight
|
|
2698
|
+
* SDK custom-tool callback for the same `callId`.
|
|
2699
|
+
*
|
|
2700
|
+
* Pass `asSessionOwner: true` only from an explicit host bridge (e.g. `--dev`
|
|
2701
|
+
* playground resolving a Slack-owned parked tool). Ownership is checked
|
|
2702
|
+
* against the stored session principal; `by` still records the clicker.
|
|
2703
|
+
*/
|
|
2704
|
+
resolveApproval(sessionId, callId, decision, auth, options) {
|
|
2705
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2706
|
+
var _a, _b;
|
|
2707
|
+
const record = yield this.sessions.get(sessionId);
|
|
2708
|
+
if (record === undefined) {
|
|
2709
|
+
throw new ApprovalNotFoundError(sessionId, callId);
|
|
2710
|
+
}
|
|
2711
|
+
const ownershipAuth = (options === null || options === void 0 ? void 0 : options.asSessionOwner) === true ? record.auth : auth;
|
|
2712
|
+
if (!samePrincipal(record.auth, ownershipAuth)) {
|
|
2713
|
+
throw new SessionAuthError(sessionId);
|
|
2714
|
+
}
|
|
2715
|
+
const pending = (_b = ((_a = record.pendingApprovals) !== null && _a !== void 0 ? _a : []).find(item => item.callId === callId)) !== null && _b !== void 0 ? _b : this.approvals.list(sessionId).find(item => item.callId === callId);
|
|
2716
|
+
if (pending === undefined) {
|
|
2717
|
+
throw new ApprovalNotFoundError(sessionId, callId);
|
|
2718
|
+
}
|
|
2719
|
+
const resolvedBy = approvalResolverLabel(auth);
|
|
2720
|
+
if (!this.approvals.resolve(sessionId, callId, decision, resolvedBy)) {
|
|
2721
|
+
yield this.removePendingApproval(sessionId, callId);
|
|
2722
|
+
throw new ApprovalInterruptedError(sessionId, callId);
|
|
2723
|
+
}
|
|
2724
|
+
return pending;
|
|
2725
|
+
});
|
|
2726
|
+
}
|
|
2727
|
+
removePendingApproval(sessionId, callId) {
|
|
2728
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2729
|
+
yield this.updateRecord(sessionId, current => { var _a; return (Object.assign(Object.assign({}, current), { pendingApprovals: ((_a = current.pendingApprovals) !== null && _a !== void 0 ? _a : []).filter(item => item.callId !== callId) })); });
|
|
2730
|
+
});
|
|
2731
|
+
}
|
|
2732
|
+
wrapMcp(inner) {
|
|
2733
|
+
return {
|
|
2734
|
+
names: () => inner.names(),
|
|
2735
|
+
listTools: name => this.withMcpAuth(name, () => inner.listTools(name)),
|
|
2736
|
+
callTool: (name, tool, args) => this.withMcpAuth(name, () => inner.callTool(name, tool, args)),
|
|
2737
|
+
};
|
|
2738
|
+
}
|
|
2739
|
+
advertiseMcp() {
|
|
2740
|
+
return {
|
|
2741
|
+
listTools: name => this.withMcpListAuth(name, () => this.mcpHost.listTools(name)),
|
|
2742
|
+
callTool: (name, tool, args) => this.withMcpAuth(name, () => this.mcpHost.callTool(name, tool, args)),
|
|
2743
|
+
oneOff: connection => {
|
|
2744
|
+
const inner = this.mcpHost.oneOff(connection);
|
|
2745
|
+
return {
|
|
2746
|
+
listTools: () => this.withMcpListAuth(connection.name, () => inner.listTools()),
|
|
2747
|
+
callTool: (tool, args) => this.withMcpAuth(connection.name, () => inner.callTool(tool, args)),
|
|
2748
|
+
};
|
|
2749
|
+
},
|
|
2750
|
+
};
|
|
2751
|
+
}
|
|
2752
|
+
withMcpListAuth(connection, run) {
|
|
2753
|
+
const ctx = mcpAuthAls.getStore();
|
|
2754
|
+
if (ctx === undefined) {
|
|
2755
|
+
return this.withMcpAuth(connection, run);
|
|
2756
|
+
}
|
|
2757
|
+
const toolName = `mcp:${connection}`;
|
|
2758
|
+
return mcpAuthAls.run(Object.assign(Object.assign({}, ctx), { callId: newToolCallId(toolName), toolName }), () => this.withMcpAuth(connection, run));
|
|
2759
|
+
}
|
|
2760
|
+
withMcpAuth(connection, run) {
|
|
2761
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2762
|
+
try {
|
|
2763
|
+
return yield run();
|
|
2764
|
+
}
|
|
2765
|
+
catch (error) {
|
|
2766
|
+
if (!isMcpNeedsAuthError(error)) {
|
|
2767
|
+
throw error;
|
|
2768
|
+
}
|
|
2769
|
+
return yield this.parkAndRetryMcp(connection, run, error);
|
|
2770
|
+
}
|
|
2771
|
+
});
|
|
2772
|
+
}
|
|
2773
|
+
parkAndRetryMcp(connection, run, error) {
|
|
2774
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2775
|
+
const ctx = mcpAuthAls.getStore();
|
|
2776
|
+
if (ctx === undefined) {
|
|
2777
|
+
throw error;
|
|
2778
|
+
}
|
|
2779
|
+
const minted = yield this.mintAuthUrl(connection, ctx.sessionId);
|
|
2780
|
+
if (minted === undefined) {
|
|
2781
|
+
throw error;
|
|
2782
|
+
}
|
|
2783
|
+
if ("authorized" in minted) {
|
|
2784
|
+
yield this.persistHostedMcpOauth(connection);
|
|
2785
|
+
yield this.mcpHost.dropClient(connection);
|
|
2786
|
+
return yield this.withMcpAuth(connection, run);
|
|
2787
|
+
}
|
|
2788
|
+
if (minted.state !== "") {
|
|
2789
|
+
const bind = this.oauthHub.bind(minted.state, ctx.sessionId, ctx.callId);
|
|
2790
|
+
if (bind === "already_done") {
|
|
2791
|
+
yield this.persistHostedMcpOauth(connection);
|
|
2792
|
+
yield this.mcpHost.dropClient(connection);
|
|
2793
|
+
return yield this.withMcpAuth(connection, run);
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
const decision = yield this.parkForAuth({
|
|
2797
|
+
sessionId: ctx.sessionId,
|
|
2798
|
+
turnId: ctx.turnId,
|
|
2799
|
+
callId: ctx.callId,
|
|
2800
|
+
toolName: ctx.toolName,
|
|
2801
|
+
connection,
|
|
2802
|
+
authUrl: minted.authUrl,
|
|
2803
|
+
emit: ctx.emit,
|
|
2804
|
+
});
|
|
2805
|
+
if (decision === "deny") {
|
|
2806
|
+
throw new Error(`MCP connection "${connection}" authorization was denied.`);
|
|
2807
|
+
}
|
|
2808
|
+
yield this.persistHostedMcpOauth(connection);
|
|
2809
|
+
yield this.mcpHost.dropClient(connection);
|
|
2810
|
+
return yield this.withMcpAuth(connection, run);
|
|
2811
|
+
});
|
|
2812
|
+
}
|
|
2813
|
+
persistHostedMcpOauth(connection) {
|
|
2814
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2815
|
+
const authored = this.authoredConnection(connection);
|
|
2816
|
+
if (authored === undefined || !isHostOauthConnectionTransport(authored.transport)) {
|
|
2817
|
+
return;
|
|
2818
|
+
}
|
|
2819
|
+
yield this.oauthHub.persistFor(connection, authored.transport.url);
|
|
2820
|
+
});
|
|
2821
|
+
}
|
|
2822
|
+
mintAuthUrl(connection, sessionId) {
|
|
2823
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2824
|
+
const authored = this.authoredConnection(connection);
|
|
2825
|
+
if (authored === undefined) {
|
|
2826
|
+
return undefined;
|
|
2827
|
+
}
|
|
2828
|
+
if (isCursorAccountTransport(authored.transport)) {
|
|
2829
|
+
const probe = this.probeCursorAccount;
|
|
2830
|
+
if (probe === undefined) {
|
|
2831
|
+
return undefined;
|
|
2832
|
+
}
|
|
2833
|
+
const authUrl = yield probe(connection);
|
|
2834
|
+
if (authUrl === undefined || authUrl === "") {
|
|
2835
|
+
return undefined;
|
|
2836
|
+
}
|
|
2837
|
+
return { authUrl, state: "" };
|
|
2838
|
+
}
|
|
2839
|
+
if (!isHostOauthConnectionTransport(authored.transport)) {
|
|
2840
|
+
return undefined;
|
|
2841
|
+
}
|
|
2842
|
+
return yield this.oauthHub.mint({
|
|
2843
|
+
connection,
|
|
2844
|
+
url: authored.transport.url,
|
|
2845
|
+
type: authored.transport.type === "sse" ? "sse" : "http",
|
|
2846
|
+
headers: authored.transport.headers,
|
|
2847
|
+
sessionId,
|
|
2848
|
+
});
|
|
2849
|
+
});
|
|
2850
|
+
}
|
|
2851
|
+
parkForAuth(args) {
|
|
2852
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2853
|
+
const requestedAt = new Date().toISOString();
|
|
2854
|
+
const pendingRecord = {
|
|
2855
|
+
callId: args.callId,
|
|
2856
|
+
turnId: args.turnId,
|
|
2857
|
+
toolName: args.toolName,
|
|
2858
|
+
connection: args.connection,
|
|
2859
|
+
authUrl: args.authUrl,
|
|
2860
|
+
requestedAt,
|
|
2861
|
+
};
|
|
2862
|
+
const resolutionPromise = this.auths.wait(args.sessionId, pendingRecord);
|
|
2863
|
+
yield this.updateRecord(args.sessionId, current => { var _a; return (Object.assign(Object.assign({}, current), { pendingAuths: [
|
|
2864
|
+
...((_a = current.pendingAuths) !== null && _a !== void 0 ? _a : []).filter(item => item.callId !== args.callId),
|
|
2865
|
+
pendingRecord,
|
|
2866
|
+
] })); });
|
|
2867
|
+
args.emit({
|
|
2868
|
+
type: "action.auth_requested",
|
|
2869
|
+
data: {
|
|
2870
|
+
callId: args.callId,
|
|
2871
|
+
toolName: args.toolName,
|
|
2872
|
+
connection: args.connection,
|
|
2873
|
+
authUrl: args.authUrl,
|
|
2874
|
+
},
|
|
2875
|
+
});
|
|
2876
|
+
this.logger(`[session] tool ${args.toolName} awaiting MCP auth ${args.connection} (${args.callId})`);
|
|
2877
|
+
const { decision, by } = yield resolutionPromise;
|
|
2878
|
+
yield this.updateRecord(args.sessionId, current => { var _a; return (Object.assign(Object.assign({}, current), { pendingAuths: ((_a = current.pendingAuths) !== null && _a !== void 0 ? _a : []).filter(item => item.callId !== args.callId) })); });
|
|
2879
|
+
args.emit({
|
|
2880
|
+
type: "action.auth_resolved",
|
|
2881
|
+
data: {
|
|
2882
|
+
callId: args.callId,
|
|
2883
|
+
toolName: args.toolName,
|
|
2884
|
+
connection: args.connection,
|
|
2885
|
+
decision,
|
|
2886
|
+
by,
|
|
2887
|
+
},
|
|
2888
|
+
});
|
|
2889
|
+
this.logger(`[session] tool ${args.toolName} MCP auth ${decision} (${args.callId})`);
|
|
2890
|
+
return decision;
|
|
2891
|
+
});
|
|
2892
|
+
}
|
|
2893
|
+
clearPendingAuths(sessionId) {
|
|
2894
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2895
|
+
yield this.oauthHub.abortSession(sessionId);
|
|
2896
|
+
const current = yield this.sessions.get(sessionId);
|
|
2897
|
+
if (current === undefined ||
|
|
2898
|
+
current.pendingAuths === undefined ||
|
|
2899
|
+
current.pendingAuths.length === 0) {
|
|
2900
|
+
return;
|
|
2901
|
+
}
|
|
2902
|
+
yield this.updateRecord(sessionId, record => (Object.assign(Object.assign({}, record), { pendingAuths: [] })));
|
|
2903
|
+
});
|
|
2904
|
+
}
|
|
2905
|
+
listPendingAuths(sessionId) {
|
|
2906
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2907
|
+
var _a;
|
|
2908
|
+
const record = yield this.sessions.get(sessionId);
|
|
2909
|
+
if (record === undefined) {
|
|
2910
|
+
return [];
|
|
2911
|
+
}
|
|
2912
|
+
const live = this.auths.list(sessionId);
|
|
2913
|
+
if (live.length > 0) {
|
|
2914
|
+
return live;
|
|
2915
|
+
}
|
|
2916
|
+
return (_a = record.pendingAuths) !== null && _a !== void 0 ? _a : [];
|
|
2917
|
+
});
|
|
2918
|
+
}
|
|
2919
|
+
resolveAuth(sessionId, callId, decision, auth, options) {
|
|
2920
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2921
|
+
var _a, _b;
|
|
2922
|
+
const record = yield this.sessions.get(sessionId);
|
|
2923
|
+
if (record === undefined) {
|
|
2924
|
+
throw new AuthNotFoundError(sessionId, callId);
|
|
2925
|
+
}
|
|
2926
|
+
const ownershipAuth = (options === null || options === void 0 ? void 0 : options.asSessionOwner) === true ? record.auth : auth;
|
|
2927
|
+
if (!samePrincipal(record.auth, ownershipAuth)) {
|
|
2928
|
+
throw new SessionAuthError(sessionId);
|
|
2929
|
+
}
|
|
2930
|
+
const pending = (_b = ((_a = record.pendingAuths) !== null && _a !== void 0 ? _a : []).find(item => item.callId === callId)) !== null && _b !== void 0 ? _b : this.auths.list(sessionId).find(item => item.callId === callId);
|
|
2931
|
+
if (pending === undefined) {
|
|
2932
|
+
throw new AuthNotFoundError(sessionId, callId);
|
|
2933
|
+
}
|
|
2934
|
+
const resolvedBy = approvalResolverLabel(auth);
|
|
2935
|
+
if (decision === "deny") {
|
|
2936
|
+
yield this.oauthHub.abortCall(sessionId, callId);
|
|
2937
|
+
}
|
|
2938
|
+
if (!this.auths.resolve(sessionId, callId, decision, resolvedBy)) {
|
|
2939
|
+
yield this.removePendingAuth(sessionId, callId);
|
|
2940
|
+
throw new AuthInterruptedError(sessionId, callId);
|
|
2941
|
+
}
|
|
2942
|
+
return pending;
|
|
2943
|
+
});
|
|
2944
|
+
}
|
|
2945
|
+
removePendingAuth(sessionId, callId) {
|
|
2946
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2947
|
+
yield this.updateRecord(sessionId, current => { var _a; return (Object.assign(Object.assign({}, current), { pendingAuths: ((_a = current.pendingAuths) !== null && _a !== void 0 ? _a : []).filter(item => item.callId !== callId) })); });
|
|
2948
|
+
});
|
|
2949
|
+
}
|
|
2950
|
+
runWithMcpPark(sessionId, toolName, fn) {
|
|
2951
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2952
|
+
const active = this.activeToolContexts.get(sessionId);
|
|
2953
|
+
if (active === undefined) {
|
|
2954
|
+
return fn();
|
|
2955
|
+
}
|
|
2956
|
+
return mcpAuthAls.run({
|
|
2957
|
+
sessionId,
|
|
2958
|
+
turnId: active.turnId,
|
|
2959
|
+
callId: newToolCallId(toolName),
|
|
2960
|
+
toolName,
|
|
2961
|
+
emit: active.emit,
|
|
2962
|
+
}, fn);
|
|
2963
|
+
});
|
|
2964
|
+
}
|
|
2965
|
+
// ==========================================================================
|
|
2966
|
+
// Direct tool calls (deterministic invocation, no model turn)
|
|
2967
|
+
// ==========================================================================
|
|
2968
|
+
/**
|
|
2969
|
+
* Call an authored server tool deterministically: validate `input`
|
|
2970
|
+
* against the tool's schema (zod schemas validate and transform; plain
|
|
2971
|
+
* JSON Schema inputs pass through unvalidated, matching the model path),
|
|
2972
|
+
* run `execute` in-process, and return the normalized result exactly as
|
|
2973
|
+
* the model would receive it.
|
|
2974
|
+
*
|
|
2975
|
+
* With `options.sessionId` the tool runs inside that session (its info
|
|
2976
|
+
* and materialized workspace) and the call is recorded on the session's
|
|
2977
|
+
* event stream as `actions.requested` / `action.result` under a
|
|
2978
|
+
* per-call `turnId` (the call id), so it shows up in trajectories.
|
|
2979
|
+
* While a model turn is running, a read-effect call runs alongside it
|
|
2980
|
+
* (the turn's workspace is read as-is) and a write-effect call is
|
|
2981
|
+
* rejected with {@link SessionBusyError}. Without a session, the tool
|
|
2982
|
+
* runs against an **ephemeral** scratch
|
|
2983
|
+
* workspace under `<stateRoot>/tool-calls/<callId>` (materialized with
|
|
2984
|
+
* seed files, removed after the call returns).
|
|
2985
|
+
*
|
|
2986
|
+
* Throws {@link UnknownToolError}, {@link ToolNotDirectlyCallableError},
|
|
2987
|
+
* {@link ToolInputError}, or {@link UnknownSessionError} before the tool
|
|
2988
|
+
* body runs; a tool body that throws is reported as an `isError` outcome
|
|
2989
|
+
* with the same `{ content, isError }` envelope the model would see, not
|
|
2990
|
+
* an exception.
|
|
2991
|
+
*/
|
|
2992
|
+
/**
|
|
2993
|
+
* The session identity a direct call (or catalog listing) runs under:
|
|
2994
|
+
* an explicit sessionId, a continuation token resolved to a live (or
|
|
2995
|
+
* restorable) session with the same owner/busy checks a sessionId call
|
|
2996
|
+
* gets, or — when neither maps to a session — a synthetic identity
|
|
2997
|
+
* carrying the token's channel id and continuation key so binding
|
|
2998
|
+
* resolvers (e.g. VFS mounts) can still serve the call.
|
|
2999
|
+
*/
|
|
3000
|
+
resolveDirectCallIdentity(callId, options) {
|
|
3001
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3002
|
+
var _a, _b, _c;
|
|
3003
|
+
if (options.sessionId !== undefined && options.continuationToken !== undefined) {
|
|
3004
|
+
throw new InvalidContinuationTokenError("mutually exclusive with sessionId — a real session already carries its own continuation key");
|
|
3005
|
+
}
|
|
3006
|
+
const auth = (_a = options.auth) !== null && _a !== void 0 ? _a : null;
|
|
3007
|
+
let record;
|
|
3008
|
+
let continuation = null;
|
|
3009
|
+
if (options.sessionId !== undefined) {
|
|
3010
|
+
record = yield this.sessions.get(options.sessionId);
|
|
3011
|
+
if (record === undefined) {
|
|
3012
|
+
throw new UnknownSessionError(options.sessionId);
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
else if (options.continuationToken !== undefined) {
|
|
3016
|
+
continuation = parseContinuationToken(options.continuationToken);
|
|
3017
|
+
if (continuation === null) {
|
|
3018
|
+
throw new InvalidContinuationTokenError(`expected "<channelId>:<key>" (as /v1/sessions lists it), got "${options.continuationToken}"`);
|
|
3019
|
+
}
|
|
3020
|
+
// A token that maps to a live (or restorable) session takes the
|
|
3021
|
+
// session path: same owner check, same busy semantics, same event
|
|
3022
|
+
// recording as an explicit sessionId. Skipping those just because
|
|
3023
|
+
// the caller addressed the session by token would fail open.
|
|
3024
|
+
record = (_b = (yield this.sessions.findByContinuation(continuation.channelId, continuation.key))) !== null && _b !== void 0 ? _b : (yield this.restoreSessionByContinuation(continuation.channelId, continuation.key));
|
|
3025
|
+
if (record !== undefined && !samePrincipal(record.auth, auth)) {
|
|
3026
|
+
throw new SessionAuthError(record.sessionId);
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
if (record !== undefined) {
|
|
3030
|
+
return {
|
|
3031
|
+
record,
|
|
3032
|
+
session: this.sessionInfo(record),
|
|
3033
|
+
workspaceDir: this.harnessCwd(record),
|
|
3034
|
+
scratchDir: undefined,
|
|
3035
|
+
busy: this.activeTurns.has(record.sessionId) || this.pendingTurns.has(record.sessionId),
|
|
3036
|
+
};
|
|
3037
|
+
}
|
|
3038
|
+
// No session behind the token (or no token at all): a synthetic
|
|
3039
|
+
// session carries the token's channel id and key, so binding resolvers
|
|
3040
|
+
// keyed on the continuation key (e.g. VFS mounts) can serve the call
|
|
3041
|
+
// with no live session. The workspace stays scratch and nothing is
|
|
3042
|
+
// recorded on a session event stream.
|
|
3043
|
+
const scratchDir = join(this.stateRoot, "tool-calls", callId);
|
|
3044
|
+
return {
|
|
3045
|
+
record: undefined,
|
|
3046
|
+
session: {
|
|
3047
|
+
id: callId,
|
|
3048
|
+
channelId: (_c = continuation === null || continuation === void 0 ? void 0 : continuation.channelId) !== null && _c !== void 0 ? _c : DIRECT_TOOL_CALL_CHANNEL_ID,
|
|
3049
|
+
mode: "task",
|
|
3050
|
+
continuationKey: continuation === null || continuation === void 0 ? void 0 : continuation.key,
|
|
3051
|
+
auth,
|
|
3052
|
+
},
|
|
3053
|
+
workspaceDir: scratchDir,
|
|
3054
|
+
scratchDir,
|
|
3055
|
+
busy: false,
|
|
3056
|
+
};
|
|
3057
|
+
});
|
|
3058
|
+
}
|
|
3059
|
+
/**
|
|
3060
|
+
* The live tool catalog for one session identity: authored server tools
|
|
3061
|
+
* plus the advertised passthroughs a turn under the same identity would
|
|
3062
|
+
* see, under their model-facing names. A connection whose listing fails
|
|
3063
|
+
* is skipped and reported in `connectionErrors` — the inspection surface
|
|
3064
|
+
* degrades legibly instead of hiding every other tool behind one broken
|
|
3065
|
+
* connector.
|
|
3066
|
+
*/
|
|
3067
|
+
listCatalogTools() {
|
|
3068
|
+
return __awaiter(this, arguments, void 0, function* (options = {}) {
|
|
3069
|
+
const { session } = yield this.resolveDirectCallIdentity(newToolCallId("catalog"), options);
|
|
3070
|
+
const connectionErrors = [];
|
|
3071
|
+
const advertised = yield resolveAdvertisedTools({
|
|
3072
|
+
agent: this.project.agent,
|
|
3073
|
+
session,
|
|
3074
|
+
hostMcp: this.mcpHost,
|
|
3075
|
+
onConnectionError: (connection, error) => {
|
|
3076
|
+
connectionErrors.push({
|
|
3077
|
+
connection,
|
|
3078
|
+
message: error instanceof Error ? error.message : String(error),
|
|
3079
|
+
});
|
|
3080
|
+
},
|
|
3081
|
+
});
|
|
3082
|
+
return {
|
|
3083
|
+
tools: [...this.project.agent.tools, ...advertised],
|
|
3084
|
+
connectionErrors,
|
|
3085
|
+
};
|
|
3086
|
+
});
|
|
3087
|
+
}
|
|
3088
|
+
/**
|
|
3089
|
+
* One catalog tool by name. Authored tools are local — their schema does
|
|
3090
|
+
* not depend on session identity or connection listings — so a detail
|
|
3091
|
+
* fetch for `ls` must not wait on every advertised MCP server.
|
|
3092
|
+
*/
|
|
3093
|
+
describeCatalogTool(toolName_1) {
|
|
3094
|
+
return __awaiter(this, arguments, void 0, function* (toolName, options = {}) {
|
|
3095
|
+
const authored = this.project.agent.tools.find(tool => tool.name === toolName);
|
|
3096
|
+
if (authored !== undefined) {
|
|
3097
|
+
return { tool: authored };
|
|
3098
|
+
}
|
|
3099
|
+
const catalog = yield this.listCatalogTools(options);
|
|
3100
|
+
const tool = catalog.tools.find(entry => entry.name === toolName);
|
|
3101
|
+
if (tool === undefined) {
|
|
3102
|
+
return {
|
|
3103
|
+
tool: undefined,
|
|
3104
|
+
availableTools: catalog.tools.map(entry => entry.name),
|
|
3105
|
+
connectionErrors: catalog.connectionErrors,
|
|
3106
|
+
};
|
|
3107
|
+
}
|
|
3108
|
+
return { tool };
|
|
3109
|
+
});
|
|
3110
|
+
}
|
|
3111
|
+
callTool(toolName_1) {
|
|
3112
|
+
return __awaiter(this, arguments, void 0, function* (toolName, input = {}, options = {}) {
|
|
3113
|
+
var _a;
|
|
3114
|
+
var _b, _c;
|
|
3115
|
+
const callId = newToolCallId(toolName);
|
|
3116
|
+
// Identity resolves before the tool lookup: advertised connection tools
|
|
3117
|
+
// only exist relative to a session (per-session auth), so finding them
|
|
3118
|
+
// needs the same identity the call runs under.
|
|
3119
|
+
const identity = yield this.resolveDirectCallIdentity(callId, options);
|
|
3120
|
+
const record = identity.record;
|
|
3121
|
+
const session = identity.session;
|
|
3122
|
+
let workspaceDir = identity.workspaceDir;
|
|
3123
|
+
let scratchDir = identity.scratchDir;
|
|
3124
|
+
if (isFileSystemToolAlias(toolName) &&
|
|
3125
|
+
identity.busy &&
|
|
3126
|
+
record !== undefined &&
|
|
3127
|
+
isMutatingFileSystemTool(toolName)) {
|
|
3128
|
+
throw new SessionBusyError(record.sessionId);
|
|
3129
|
+
}
|
|
3130
|
+
const native = yield this.nativeFileSystemOutcome({
|
|
3131
|
+
toolName,
|
|
3132
|
+
input,
|
|
3133
|
+
callId,
|
|
3134
|
+
sessionId: record === null || record === void 0 ? void 0 : record.sessionId,
|
|
3135
|
+
continuationKey: (_c = (_b = record === null || record === void 0 ? void 0 : record.continuationKey) !== null && _b !== void 0 ? _b : session.continuationKey) !== null && _c !== void 0 ? _c : undefined,
|
|
3136
|
+
cwd: workspaceDir,
|
|
3137
|
+
dryRun: (record === null || record === void 0 ? void 0 : record.dryRun) === true,
|
|
3138
|
+
});
|
|
3139
|
+
if (native !== undefined) {
|
|
3140
|
+
return native;
|
|
3141
|
+
}
|
|
3142
|
+
let tool = this.project.agent.tools.find(t => t.name === toolName);
|
|
3143
|
+
if (tool === undefined && this.project.agent.connections.some(c => c.advertiseTools === true)) {
|
|
3144
|
+
// Advertised passthroughs are ordinary server tools in the session's
|
|
3145
|
+
// namespace; resolve the live listing so direct calls address them by
|
|
3146
|
+
// the same model-facing name a turn would. A listing failure fails
|
|
3147
|
+
// the call closed, like it fails a turn.
|
|
3148
|
+
const advertised = yield resolveAdvertisedTools({
|
|
3149
|
+
agent: this.project.agent,
|
|
3150
|
+
session,
|
|
3151
|
+
hostMcp: this.advertiseMcp(),
|
|
3152
|
+
});
|
|
3153
|
+
tool = advertised.find(t => t.name === toolName);
|
|
3154
|
+
}
|
|
3155
|
+
if (tool === undefined) {
|
|
3156
|
+
throw new UnknownToolError(toolName, this.project.agent.tools.filter(t => t.execution === "server").map(t => t.name));
|
|
3157
|
+
}
|
|
3158
|
+
const execute = tool.definition.execute;
|
|
3159
|
+
if (tool.execution !== "server" || typeof execute !== "function") {
|
|
3160
|
+
throw new ToolNotDirectlyCallableError(toolName);
|
|
3161
|
+
}
|
|
3162
|
+
const validation = validateToolInput(tool.definition.inputSchema, input);
|
|
3163
|
+
if (!validation.ok) {
|
|
3164
|
+
throw new ToolInputError({ toolName, detail: validation.message });
|
|
3165
|
+
}
|
|
3166
|
+
// A running model turn owns the session workspace: a direct write would
|
|
3167
|
+
// race the harness's own edits, so it waits (409). A read runs alongside
|
|
3168
|
+
// the turn — the operator inspecting a live session is the point — and
|
|
3169
|
+
// its events stay separable by their per-call turnId. The effect can
|
|
3170
|
+
// depend on the input, hence deciding after validation; an MCP tool
|
|
3171
|
+
// nothing classifies is a write here, fail-closed.
|
|
3172
|
+
if (identity.busy &&
|
|
3173
|
+
record !== undefined &&
|
|
3174
|
+
directCallEffect(tool, validation.value) !== "read") {
|
|
3175
|
+
throw new SessionBusyError(record.sessionId);
|
|
3176
|
+
}
|
|
3177
|
+
let workspaceUnavailable = false;
|
|
3178
|
+
try {
|
|
3179
|
+
yield mkdir(workspaceDir, { recursive: true });
|
|
3180
|
+
}
|
|
3181
|
+
catch (error) {
|
|
3182
|
+
if (record === undefined) {
|
|
3183
|
+
throw error;
|
|
3184
|
+
}
|
|
3185
|
+
// A session's harness cwd can be a path the serving process cannot
|
|
3186
|
+
// create (e.g. an absolute `local.cwd` like /repo on a hosted pod
|
|
3187
|
+
// where the sandboxed harness would have made it). Read-effect calls
|
|
3188
|
+
// fall back to a scratch workspace instead of failing: tools that
|
|
3189
|
+
// resolve their state from the session identity (VFS mounts) never
|
|
3190
|
+
// touch ctx.workspaceDir. Advertised passthroughs are relocatable by
|
|
3191
|
+
// construction: their execute body forwards to the connection and
|
|
3192
|
+
// never reads the workspace. A write (or undeclared, which resolves
|
|
3193
|
+
// to write) authored tool may rely on ctx.workspaceDir being the
|
|
3194
|
+
// real session workspace, so silently relocating it is not safe —
|
|
3195
|
+
// fail typed instead.
|
|
3196
|
+
//
|
|
3197
|
+
// A shared `local.workspaceDir` (change-monitor's phantom /repo) is
|
|
3198
|
+
// that path by design. Relocating it to scratch would flag a false
|
|
3199
|
+
// session-state warning; keep the borrowed cwd and skip the flag.
|
|
3200
|
+
const relocatable = tool.advertised === true || directCallEffect(tool, validation.value) === "read";
|
|
3201
|
+
if (!relocatable) {
|
|
3202
|
+
throw new WorkspaceUnavailableError(toolName, error);
|
|
3203
|
+
}
|
|
3204
|
+
if (((_a = this.project.agent.local) === null || _a === void 0 ? void 0 : _a.workspaceDir) !== undefined) {
|
|
3205
|
+
this.logger(`[session] tool ${toolName} harness cwd unavailable (direct ${callId}); keeping borrowed workspace`);
|
|
3206
|
+
}
|
|
3207
|
+
else {
|
|
3208
|
+
this.logger(`[session] tool ${toolName} workspace unavailable (direct ${callId}); using scratch: ${error instanceof Error ? error.message : String(error)}`);
|
|
3209
|
+
scratchDir = join(this.stateRoot, "tool-calls", callId);
|
|
3210
|
+
workspaceDir = scratchDir;
|
|
3211
|
+
workspaceUnavailable = true;
|
|
3212
|
+
yield mkdir(workspaceDir, { recursive: true });
|
|
3213
|
+
}
|
|
3214
|
+
}
|
|
3215
|
+
// Match what a model turn would see: session workspaces get the same
|
|
3216
|
+
// scaffold as executeTurn (seeds only before the first turn); scratch
|
|
3217
|
+
// workspaces are materialized fresh. A read running alongside a turn
|
|
3218
|
+
// that has materialized its workspace leaves it alone — re-seeding
|
|
3219
|
+
// would restore files the agent deleted mid-turn. A session is busy
|
|
3220
|
+
// from admit, though, so a read that lands while the first turn is
|
|
3221
|
+
// still queued or scaffolding materializes the workspace itself, seeds
|
|
3222
|
+
// included (turnCount is already 1 for that pending first turn).
|
|
3223
|
+
const turnMaterialized = record !== undefined && identity.busy && this.materializedWorkspaces.has(record.sessionId);
|
|
3224
|
+
if (!this.hasRootOverride() &&
|
|
3225
|
+
(record === undefined || workspaceUnavailable || !turnMaterialized)) {
|
|
3226
|
+
yield materializeWorkspace({
|
|
3227
|
+
agent: this.project.agent,
|
|
3228
|
+
workspaceDir: record === undefined || workspaceUnavailable ? workspaceDir : this.scaffoldDir(record),
|
|
3229
|
+
seed: record === undefined ||
|
|
3230
|
+
workspaceUnavailable ||
|
|
3231
|
+
record.turnCount === 0 ||
|
|
3232
|
+
(identity.busy && record.turnCount === 1),
|
|
3233
|
+
stateRoot: this.stateRoot,
|
|
3234
|
+
});
|
|
3235
|
+
}
|
|
3236
|
+
// A per-call turnId keeps these events visible in trajectories (which
|
|
3237
|
+
// drop turn-less action events) without attributing them to a model turn.
|
|
3238
|
+
if (record !== undefined) {
|
|
3239
|
+
yield this.appendEvent(record.sessionId, {
|
|
3240
|
+
type: "actions.requested",
|
|
3241
|
+
turnId: callId,
|
|
3242
|
+
data: { calls: [{ callId, toolName, args: validation.value }] },
|
|
3243
|
+
});
|
|
3244
|
+
}
|
|
3245
|
+
// A separate body from the model path, so it asks the same policy: a
|
|
3246
|
+
// session-bound `POST /v1/tools/:name`, `call --session`, or
|
|
3247
|
+
// `handle.callTool` performs a write just as readily.
|
|
3248
|
+
const decision = decideToolCall(Object.assign(Object.assign(Object.assign({ dryRun: (record === null || record === void 0 ? void 0 : record.dryRun) === true, effect: tool.definition.effect }, (tool.advertised === true
|
|
3249
|
+
? { mcpTool: { name: tool.name, annotations: tool.annotations } }
|
|
3250
|
+
: {})), { dryRunResult: tool.definition.dryRunResult, input: validation.value, asOf: record === null || record === void 0 ? void 0 : record.asOf, timeArgs: tool.definition.timeArgs }), (tool.advertised === true && (record === null || record === void 0 ? void 0 : record.asOf) !== undefined
|
|
3251
|
+
? { timeWindow: schemaTimeWindowKeys(tool.inputSchema) }
|
|
3252
|
+
: {})));
|
|
3253
|
+
const started = Date.now();
|
|
3254
|
+
this.logger(`[session] tool ${toolName} start (direct ${callId})`);
|
|
3255
|
+
let result;
|
|
3256
|
+
let isError;
|
|
3257
|
+
const stubbed = decision.kind === "answer";
|
|
3258
|
+
const live = record;
|
|
3259
|
+
const emit = live === undefined
|
|
3260
|
+
? undefined
|
|
3261
|
+
: (payload) => {
|
|
3262
|
+
void this.appendEvent(live.sessionId, Object.assign(Object.assign({}, payload), { turnId: callId })).catch(() => { });
|
|
3263
|
+
};
|
|
3264
|
+
try {
|
|
3265
|
+
if (decision.kind === "unbounded") {
|
|
3266
|
+
this.logger(`[session] tool ${toolName} refused, unbounded_time (direct ${callId})`);
|
|
3267
|
+
result = normalizeToolResult(toolErrorResult(decision.message));
|
|
3268
|
+
isError = true;
|
|
3269
|
+
}
|
|
3270
|
+
else if (decision.kind === "refuse") {
|
|
3271
|
+
this.logger(`[session] tool ${toolName} refused, effect unclassified (direct ${callId})`);
|
|
3272
|
+
emit === null || emit === void 0 ? void 0 : emit({
|
|
3273
|
+
type: "action.effect_unclassified",
|
|
3274
|
+
data: { callId, toolName },
|
|
3275
|
+
});
|
|
3276
|
+
result = normalizeToolResult(toolErrorResult(decision.message));
|
|
3277
|
+
isError = true;
|
|
3278
|
+
}
|
|
3279
|
+
else if (decision.kind === "answer") {
|
|
3280
|
+
this.logger(`[session] tool ${toolName} answered by host (direct ${callId})`);
|
|
3281
|
+
result = normalizeToolResult(decision.result);
|
|
3282
|
+
isError = false;
|
|
3283
|
+
}
|
|
3284
|
+
else {
|
|
3285
|
+
const run = () => execute(validation.value, Object.assign({ toolCallId: callId, session,
|
|
3286
|
+
workspaceDir, stateRoot: this.stateRoot, host: this.hostForSession(record === null || record === void 0 ? void 0 : record.sessionId),
|
|
3287
|
+
// Scratch calls have no real session; their facade stays unbound.
|
|
3288
|
+
artifacts: record === undefined
|
|
3289
|
+
? this.artifactsApi()
|
|
3290
|
+
: this.artifactsApi({
|
|
3291
|
+
sessionId: record.sessionId,
|
|
3292
|
+
turnId: callId,
|
|
3293
|
+
}), send: (channelId, message, sendOptions = {}) => this.send(channelId, message, Object.assign(Object.assign({}, sendOptions), { auth: sendOptions.auth !== undefined ? sendOptions.auth : session.auth })), getSession: (channelId, sessionId) => this.getChannelSession(channelId, sessionId), emit, now: sessionClock(record === null || record === void 0 ? void 0 : record.asOf) }, ((record === null || record === void 0 ? void 0 : record.asOf) === undefined ? {} : { asOf: record.asOf })));
|
|
3294
|
+
const executeBody = record !== undefined && emit !== undefined
|
|
3295
|
+
? () => mcpAuthAls.run({
|
|
3296
|
+
sessionId: record.sessionId,
|
|
3297
|
+
turnId: callId,
|
|
3298
|
+
callId,
|
|
3299
|
+
toolName,
|
|
3300
|
+
emit,
|
|
3301
|
+
}, run)
|
|
3302
|
+
: run;
|
|
3303
|
+
result = normalizeToolResult(record !== undefined
|
|
3304
|
+
? yield this.runToolExecute({
|
|
3305
|
+
record,
|
|
3306
|
+
turnId: callId,
|
|
3307
|
+
callId,
|
|
3308
|
+
toolName,
|
|
3309
|
+
}, executeBody)
|
|
3310
|
+
: yield executeBody());
|
|
3311
|
+
isError = isErrorEnvelope(result);
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
catch (error) {
|
|
3315
|
+
this.logger(`[session] tool ${toolName} failed (direct ${callId}): ${describeError(error)}`);
|
|
3316
|
+
// Same envelope the model path returns for a throwing tool body.
|
|
3317
|
+
result = {
|
|
3318
|
+
content: [
|
|
3319
|
+
{
|
|
3320
|
+
type: "text",
|
|
3321
|
+
text: `Tool "${toolName}" failed: ${describeError(error)}`,
|
|
3322
|
+
},
|
|
3323
|
+
],
|
|
3324
|
+
isError: true,
|
|
3325
|
+
};
|
|
3326
|
+
isError = true;
|
|
3327
|
+
}
|
|
3328
|
+
finally {
|
|
3329
|
+
if (scratchDir !== undefined) {
|
|
3330
|
+
yield rm(scratchDir, { recursive: true, force: true }).catch(() => { });
|
|
3331
|
+
}
|
|
3332
|
+
}
|
|
3333
|
+
const durationMs = Date.now() - started;
|
|
3334
|
+
this.logger(`[session] tool ${toolName} done (direct ${callId}, ${durationMs}ms)`);
|
|
3335
|
+
if (record !== undefined) {
|
|
3336
|
+
yield this.appendEvent(record.sessionId, {
|
|
3337
|
+
type: "action.result",
|
|
3338
|
+
turnId: callId,
|
|
3339
|
+
data: Object.assign({ callId,
|
|
3340
|
+
toolName, output: result, isError }, (stubbed ? { stubbed: true } : {})),
|
|
3341
|
+
});
|
|
3342
|
+
}
|
|
3343
|
+
const errorMessage = isError ? toolCallErrorMessage(result) : undefined;
|
|
3344
|
+
return {
|
|
3345
|
+
toolName,
|
|
3346
|
+
callId,
|
|
3347
|
+
isError,
|
|
3348
|
+
result,
|
|
3349
|
+
durationMs,
|
|
3350
|
+
errorMessage,
|
|
3351
|
+
sessionId: record === null || record === void 0 ? void 0 : record.sessionId,
|
|
3352
|
+
scratchWorkspace: workspaceUnavailable ? true : undefined,
|
|
3353
|
+
};
|
|
3354
|
+
});
|
|
3355
|
+
}
|
|
3356
|
+
/**
|
|
3357
|
+
* Whether the host answers a bridged MCP call, for the cloud host bridge —
|
|
3358
|
+
* the one tool path that does not reach {@link executeServerTool}, because
|
|
3359
|
+
* it forwards to `host.mcp` instead of invoking a tool body.
|
|
3360
|
+
*
|
|
3361
|
+
* A frozen session refuses forward-looking time args here even when it is
|
|
3362
|
+
* not a dry run. A bridged tool carries no `defineTool`, so it declares no
|
|
3363
|
+
* effect, and the policy reads an undeclared tool as a write. In a dry run
|
|
3364
|
+
* that answers bridged reads too; resolving effects from a server's own
|
|
3365
|
+
* listing is the next unit's work.
|
|
3366
|
+
*/
|
|
3367
|
+
bridgedCallAnswer(args) {
|
|
3368
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3369
|
+
// Read off the in-flight turn's context rather than the session store:
|
|
3370
|
+
// the bridge already refuses a call with no active turn, so the record is
|
|
3371
|
+
// in memory. The listing below is only paid inside a dry run.
|
|
3372
|
+
const context = this.activeToolContexts.get(args.sessionId);
|
|
3373
|
+
if ((context === null || context === void 0 ? void 0 : context.record.asOf) !== undefined) {
|
|
3374
|
+
const decision = decideToolCall({
|
|
3375
|
+
dryRun: false,
|
|
3376
|
+
effect: undefined,
|
|
3377
|
+
input: args.input,
|
|
3378
|
+
asOf: context.record.asOf,
|
|
3379
|
+
});
|
|
3380
|
+
if (decision.kind === "unbounded") {
|
|
3381
|
+
this.logger(`[session] bridged tool ${args.toolName} refused, unbounded_time (${args.sessionId})`);
|
|
3382
|
+
return { answered: true, result: toolErrorResult(decision.message) };
|
|
3383
|
+
}
|
|
3384
|
+
}
|
|
3385
|
+
if ((context === null || context === void 0 ? void 0 : context.record.dryRun) !== true) {
|
|
3386
|
+
return { answered: false };
|
|
3387
|
+
}
|
|
3388
|
+
// A bridged tool carries no `defineTool`, so its effect comes from the
|
|
3389
|
+
// server's own listing annotations; a tool the listing cannot classify
|
|
3390
|
+
// refuses rather than being stubbed. A failed listing classifies nothing,
|
|
3391
|
+
// which lands on the same refusal — never on a run.
|
|
3392
|
+
const info = yield this.bridgedToolInfo(args.connectionName, args.toolName);
|
|
3393
|
+
const decision = decideToolCall(Object.assign({ dryRun: true, effect: undefined, mcpTool: { name: args.toolName, annotations: info === null || info === void 0 ? void 0 : info.annotations }, input: args.input, asOf: context.record.asOf }, (context.record.asOf !== undefined
|
|
3394
|
+
? { timeWindow: schemaTimeWindowKeys(info === null || info === void 0 ? void 0 : info.inputSchema) }
|
|
3395
|
+
: {})));
|
|
3396
|
+
if (decision.kind === "run") {
|
|
3397
|
+
return { answered: false };
|
|
3398
|
+
}
|
|
3399
|
+
if (decision.kind === "unbounded") {
|
|
3400
|
+
this.logger(`[session] bridged tool ${args.toolName} refused, unbounded_time (${args.sessionId})`);
|
|
3401
|
+
return { answered: true, result: toolErrorResult(decision.message) };
|
|
3402
|
+
}
|
|
3403
|
+
if (decision.kind === "refuse") {
|
|
3404
|
+
this.logger(`[session] bridged tool ${args.toolName} refused, effect unclassified (${args.sessionId})`);
|
|
3405
|
+
context.emit({
|
|
3406
|
+
type: "action.effect_unclassified",
|
|
3407
|
+
data: {
|
|
3408
|
+
callId: newToolCallId(args.toolName),
|
|
3409
|
+
toolName: args.toolName,
|
|
3410
|
+
connection: args.connectionName,
|
|
3411
|
+
},
|
|
3412
|
+
});
|
|
3413
|
+
return { answered: true, result: toolErrorResult(decision.message) };
|
|
3414
|
+
}
|
|
3415
|
+
this.logger(`[session] bridged tool ${args.toolName} answered by host (${args.sessionId})`);
|
|
3416
|
+
return { answered: true, result: normalizeToolResult(decision.result) };
|
|
3417
|
+
});
|
|
3418
|
+
}
|
|
3419
|
+
/**
|
|
3420
|
+
* The bounded annotations a connection's listing declares for one tool, or
|
|
3421
|
+
* undefined when the tool is unlisted, unannotated, or the listing fails —
|
|
3422
|
+
* all of which the policy reads as unclassified, the refusing state.
|
|
3423
|
+
*/
|
|
3424
|
+
bridgedToolInfo(connectionName, toolName) {
|
|
3425
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3426
|
+
try {
|
|
3427
|
+
const listed = yield this.host.mcp.listTools(connectionName);
|
|
3428
|
+
return listed.find(tool => tool.name === toolName);
|
|
3429
|
+
}
|
|
3430
|
+
catch (_a) {
|
|
3431
|
+
return undefined;
|
|
3432
|
+
}
|
|
3433
|
+
});
|
|
3434
|
+
}
|
|
3435
|
+
// ==========================================================================
|
|
3436
|
+
// Events: append, dispatch to channel handlers + hooks
|
|
3437
|
+
// ==========================================================================
|
|
3438
|
+
appendEvent(sessionId, payload, context) {
|
|
3439
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3440
|
+
const log = yield this.logs.get(sessionId);
|
|
3441
|
+
const event = log.append(payload);
|
|
3442
|
+
this.queueDispatch(sessionId, event, context);
|
|
3443
|
+
return event;
|
|
3444
|
+
});
|
|
3445
|
+
}
|
|
3446
|
+
queueDispatch(sessionId, event, context) {
|
|
3447
|
+
var _a;
|
|
3448
|
+
var _b;
|
|
3449
|
+
// Single funnel for both append paths (appendEvent + the held-log emit),
|
|
3450
|
+
// so the storage sink sees every durable event exactly once.
|
|
3451
|
+
(_a = this.storage) === null || _a === void 0 ? void 0 : _a.event(event);
|
|
3452
|
+
const chain = (_b = this.dispatchChains.get(sessionId)) !== null && _b !== void 0 ? _b : Promise.resolve();
|
|
3453
|
+
const next = chain
|
|
3454
|
+
.then(() => this.dispatchEvent(sessionId, event, context))
|
|
3455
|
+
.catch(error => {
|
|
3456
|
+
this.logger(`[agent-sdk] event dispatch failed for ${sessionId}: ${describeError(error)}`);
|
|
3457
|
+
})
|
|
3458
|
+
.then(() => {
|
|
3459
|
+
var _a;
|
|
3460
|
+
// Turn-end flush happens here — after the boundary event's handlers
|
|
3461
|
+
// and their record updates settled — so the persisted record carries
|
|
3462
|
+
// the turn's final channel state and continuation token.
|
|
3463
|
+
(_a = this.storage) === null || _a === void 0 ? void 0 : _a.eventDispatched(event);
|
|
3464
|
+
});
|
|
3465
|
+
this.dispatchChains.set(sessionId, next);
|
|
3466
|
+
}
|
|
3467
|
+
dispatchEvent(sessionId, event, context) {
|
|
3468
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3469
|
+
var _a;
|
|
3470
|
+
var _b, _c;
|
|
3471
|
+
const record = yield this.sessions.get(sessionId);
|
|
3472
|
+
if (record === undefined) {
|
|
3473
|
+
return;
|
|
3474
|
+
}
|
|
3475
|
+
const channelState = context === undefined ? ((_b = record.channelState) !== null && _b !== void 0 ? _b : null) : context.channelState;
|
|
3476
|
+
const sessionCtx = {
|
|
3477
|
+
session: (_c = context === null || context === void 0 ? void 0 : context.session) !== null && _c !== void 0 ? _c : this.sessionInfo(record),
|
|
3478
|
+
host: this.hostForSession(record.sessionId),
|
|
3479
|
+
artifacts: this.artifactsApi({
|
|
3480
|
+
sessionId: record.sessionId,
|
|
3481
|
+
turnId: event.turnId,
|
|
3482
|
+
}),
|
|
3483
|
+
};
|
|
3484
|
+
const channelEvents = this.channelEventHandlersFor(record.channelId);
|
|
3485
|
+
if (channelEvents !== undefined) {
|
|
3486
|
+
// Handlers are declared against the narrowed event for their key; the
|
|
3487
|
+
// dispatch key always matches, so widening here is sound.
|
|
3488
|
+
const handlers = [
|
|
3489
|
+
channelEvents[event.type],
|
|
3490
|
+
channelEvents["*"],
|
|
3491
|
+
];
|
|
3492
|
+
const channelCtx = {
|
|
3493
|
+
channelId: record.channelId,
|
|
3494
|
+
state: channelState,
|
|
3495
|
+
continuationToken: record.continuationKey,
|
|
3496
|
+
setContinuationToken: (token) => this.setContinuationToken(sessionId, token),
|
|
3497
|
+
};
|
|
3498
|
+
for (const handler of handlers) {
|
|
3499
|
+
if (handler === undefined) {
|
|
3500
|
+
continue;
|
|
3501
|
+
}
|
|
3502
|
+
try {
|
|
3503
|
+
yield handler(event, channelCtx, sessionCtx);
|
|
3504
|
+
}
|
|
3505
|
+
catch (error) {
|
|
3506
|
+
this.logger(`[agent-sdk] channel "${record.channelId}" handler for ${event.type} threw: ${describeError(error)}`);
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
if (context !== undefined) {
|
|
3510
|
+
context.channelState = channelCtx.state;
|
|
3511
|
+
}
|
|
3512
|
+
// Persist at turn boundaries only. Mid-turn mutations stay on the live
|
|
3513
|
+
// context; merge turn-start → live so a last-event before/after cannot
|
|
3514
|
+
// drop earlier handler writes.
|
|
3515
|
+
if (context === undefined) {
|
|
3516
|
+
yield this.updateRecord(sessionId, current => (Object.assign({}, current)));
|
|
3517
|
+
}
|
|
3518
|
+
else if (isTurnBoundaryEvent(event.type)) {
|
|
3519
|
+
yield this.updateRecord(sessionId, current => (Object.assign(Object.assign({}, current), { channelState: mergeChannelStateMutation({
|
|
3520
|
+
current: current.channelState,
|
|
3521
|
+
before: context.channelStateAtTurnStart,
|
|
3522
|
+
after: context.channelState,
|
|
3523
|
+
}) })));
|
|
3524
|
+
}
|
|
3525
|
+
}
|
|
3526
|
+
for (const hook of this.project.hooks) {
|
|
3527
|
+
const handlers = [
|
|
3528
|
+
hook.definition.events[event.type],
|
|
3529
|
+
hook.definition.events["*"],
|
|
3530
|
+
];
|
|
3531
|
+
for (const handler of handlers) {
|
|
3532
|
+
if (handler === undefined) {
|
|
3533
|
+
continue;
|
|
3534
|
+
}
|
|
3535
|
+
try {
|
|
3536
|
+
yield handler(event, Object.assign(Object.assign({}, sessionCtx), { agent: { name: this.project.name }, channel: {
|
|
3537
|
+
id: record.channelId,
|
|
3538
|
+
continuationToken: record.continuationKey,
|
|
3539
|
+
}, channelState, stateRoot: this.stateRoot }));
|
|
3540
|
+
}
|
|
3541
|
+
catch (error) {
|
|
3542
|
+
this.logger(`[agent-sdk] hook "${hook.name}" handler for ${event.type} threw: ${describeError(error)}`);
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3546
|
+
(_a = this.otel) === null || _a === void 0 ? void 0 : _a.onEvent(event, sessionCtx.session);
|
|
3547
|
+
});
|
|
3548
|
+
}
|
|
3549
|
+
channelEventHandlersFor(channelId) {
|
|
3550
|
+
var _a;
|
|
3551
|
+
if (channelId === HTTP_CHANNEL_ID) {
|
|
3552
|
+
return this.project.httpChannel.events;
|
|
3553
|
+
}
|
|
3554
|
+
return (_a = this.channelsById.get(channelId)) === null || _a === void 0 ? void 0 : _a.events;
|
|
3555
|
+
}
|
|
3556
|
+
// ==========================================================================
|
|
3557
|
+
// Session handles
|
|
3558
|
+
// ==========================================================================
|
|
3559
|
+
/**
|
|
3560
|
+
* Wait until everything this session has already emitted is durably
|
|
3561
|
+
* mirrored: its dispatch chain first (the chain's tail runs the turn-end
|
|
3562
|
+
* storage flush, so the record carries the boundary event's handler
|
|
3563
|
+
* mutations), then the storage delivery queue. Hosted v2 awaits this
|
|
3564
|
+
* before the execution reply — the pod can be parked the moment the
|
|
3565
|
+
* reply lands, and an unflushed session record would strand a
|
|
3566
|
+
* conversation whose blobs persisted but whose record (which selects the
|
|
3567
|
+
* conversation to hydrate) never reached the sink. No-op without a
|
|
3568
|
+
* storage mirror.
|
|
3569
|
+
*
|
|
3570
|
+
* Under a debounced event policy the turn-end flush is a timer, not the
|
|
3571
|
+
* chain's tail, so flush explicitly before waiting on the queue; a no-op
|
|
3572
|
+
* when the chain already flushed.
|
|
3573
|
+
*/
|
|
3574
|
+
drainSessionDurability(sessionId) {
|
|
3575
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3576
|
+
if (this.storage === undefined) {
|
|
3577
|
+
return;
|
|
3578
|
+
}
|
|
3579
|
+
const chain = this.dispatchChains.get(sessionId);
|
|
3580
|
+
if (chain !== undefined) {
|
|
3581
|
+
yield chain.catch(() => { });
|
|
3582
|
+
}
|
|
3583
|
+
this.storage.flushSession(sessionId);
|
|
3584
|
+
yield this.storage.whenIdle();
|
|
3585
|
+
});
|
|
3586
|
+
}
|
|
3587
|
+
waitForCompletion(sessionId) {
|
|
3588
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3589
|
+
var _a;
|
|
3590
|
+
// Drain admission first so pendingTurns → activeTurns is visible before we
|
|
3591
|
+
// decide the session is idle (reminders use this for queueBehindTurn).
|
|
3592
|
+
// Wait for coalesce drain and any successor turn so hosted execute does
|
|
3593
|
+
// not park mid-follow-up, but report the root turn's outcome: a drained
|
|
3594
|
+
// successor failure must not un-ACK an already-finished owner.
|
|
3595
|
+
let root;
|
|
3596
|
+
for (;;) {
|
|
3597
|
+
const chain = this.admissionChains.get(sessionId);
|
|
3598
|
+
if (chain !== undefined) {
|
|
3599
|
+
yield chain.catch(() => { });
|
|
3600
|
+
}
|
|
3601
|
+
const active = this.activeTurns.get(sessionId);
|
|
3602
|
+
if (active !== undefined) {
|
|
3603
|
+
const outcome = yield active;
|
|
3604
|
+
root !== null && root !== void 0 ? root : (root = outcome);
|
|
3605
|
+
continue;
|
|
3606
|
+
}
|
|
3607
|
+
const dispatch = this.dispatchChains.get(sessionId);
|
|
3608
|
+
if (dispatch !== undefined) {
|
|
3609
|
+
yield dispatch.catch(() => { });
|
|
3610
|
+
}
|
|
3611
|
+
return (_a = root !== null && root !== void 0 ? root : this.lastOutcomes.get(sessionId)) !== null && _a !== void 0 ? _a : { status: "finished" };
|
|
3612
|
+
}
|
|
3613
|
+
});
|
|
3614
|
+
}
|
|
3615
|
+
/**
|
|
3616
|
+
* Sessions visible to `caller`, newest first (`GET /v1/sessions`).
|
|
3617
|
+
* Callers only see sessions whose stored principal matches — except when
|
|
3618
|
+
* `includeAll` is set (dev mode), where the listing becomes a full
|
|
3619
|
+
* observability surface across every principal (channel-driven runs,
|
|
3620
|
+
* schedule/task sessions, other callers). The route auth chain still
|
|
3621
|
+
* gates who may call this at all.
|
|
3622
|
+
*
|
|
3623
|
+
* When the project uses `cursorHostedStorage`, prefers durable Bugbot rows
|
|
3624
|
+
* (merged with any newer local records not flushed yet). Falls back to the
|
|
3625
|
+
* local `--state-root` store when the sink is unavailable.
|
|
3626
|
+
*/
|
|
3627
|
+
listSessions(caller, options) {
|
|
3628
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3629
|
+
return (yield this.listSessionsWithSource(caller, options)).sessions;
|
|
3630
|
+
});
|
|
3631
|
+
}
|
|
3632
|
+
/**
|
|
3633
|
+
* One artifacts page for `GET /v1/artifacts`, newest-updated first.
|
|
3634
|
+
* Without `includeAll`, the sessions-list ownership rule applies — only
|
|
3635
|
+
* artifacts tagged on a session whose principal matches the caller — as
|
|
3636
|
+
* a predicate inside the page scan, so visibility never forces a wider
|
|
3637
|
+
* read than the page itself. Session-less artifacts are agent-scoped and
|
|
3638
|
+
* visible only with `includeAll`.
|
|
3639
|
+
*/
|
|
3640
|
+
pageArtifacts(caller, options) {
|
|
3641
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3642
|
+
var _a, _b;
|
|
3643
|
+
let matches;
|
|
3644
|
+
if ((options === null || options === void 0 ? void 0 : options.includeAll) !== true) {
|
|
3645
|
+
const owned = yield this.ownedSessionIds(caller);
|
|
3646
|
+
matches = record => record.sessionId !== undefined && owned.has(record.sessionId);
|
|
3647
|
+
}
|
|
3648
|
+
return this.artifactsStore.page({
|
|
3649
|
+
kind: (_a = options === null || options === void 0 ? void 0 : options.filter) === null || _a === void 0 ? void 0 : _a.kind,
|
|
3650
|
+
sessionId: (_b = options === null || options === void 0 ? void 0 : options.filter) === null || _b === void 0 ? void 0 : _b.sessionId,
|
|
3651
|
+
limit: options === null || options === void 0 ? void 0 : options.limit,
|
|
3652
|
+
cursor: options === null || options === void 0 ? void 0 : options.cursor,
|
|
3653
|
+
matches,
|
|
3654
|
+
});
|
|
3655
|
+
});
|
|
3656
|
+
}
|
|
3657
|
+
/**
|
|
3658
|
+
* One artifact's file/blob payload for `GET /v1/artifacts/:id/content`.
|
|
3659
|
+
* Same visibility rule as {@link listArtifacts}. Undefined when the
|
|
3660
|
+
* artifact is missing, invisible to the caller, or carries no payload.
|
|
3661
|
+
*/
|
|
3662
|
+
getArtifactContent(caller, id, options) {
|
|
3663
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3664
|
+
const record = yield this.artifactsStore.get(id);
|
|
3665
|
+
if (record === undefined || record.content === undefined) {
|
|
3666
|
+
return undefined;
|
|
3667
|
+
}
|
|
3668
|
+
if ((options === null || options === void 0 ? void 0 : options.includeAll) !== true) {
|
|
3669
|
+
if (record.sessionId === undefined) {
|
|
3670
|
+
return undefined;
|
|
3671
|
+
}
|
|
3672
|
+
const owned = yield this.ownedSessionIds(caller);
|
|
3673
|
+
if (!owned.has(record.sessionId)) {
|
|
3674
|
+
return undefined;
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
const body = yield this.artifactsStore.getContent(id);
|
|
3678
|
+
return body === undefined ? undefined : { record, body };
|
|
3679
|
+
});
|
|
3680
|
+
}
|
|
3681
|
+
ownedSessionIds(caller) {
|
|
3682
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3683
|
+
const sessions = yield this.sessions.list();
|
|
3684
|
+
return new Set(sessions.filter(record => samePrincipal(record.auth, caller)).map(record => record.sessionId));
|
|
3685
|
+
});
|
|
3686
|
+
}
|
|
3687
|
+
/**
|
|
3688
|
+
* Same as {@link listSessions}, plus which store answered
|
|
3689
|
+
* (`cursor-hosted` = durable DB via the control-plane proxy; `local` =
|
|
3690
|
+
* `--state-root`).
|
|
3691
|
+
*/
|
|
3692
|
+
listSessionsWithSource(caller, options) {
|
|
3693
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3694
|
+
var _a;
|
|
3695
|
+
var _b;
|
|
3696
|
+
const includeAll = (_b = options === null || options === void 0 ? void 0 : options.includeAll) !== null && _b !== void 0 ? _b : false;
|
|
3697
|
+
if (((_a = this.storage) === null || _a === void 0 ? void 0 : _a.isCursorHosted) === true) {
|
|
3698
|
+
try {
|
|
3699
|
+
const records = yield this.mergeStoredAndLocalSessions();
|
|
3700
|
+
return {
|
|
3701
|
+
sessions: this.summarizeSessionRecords(records, caller, includeAll),
|
|
3702
|
+
source: "cursor-hosted",
|
|
3703
|
+
};
|
|
3704
|
+
}
|
|
3705
|
+
catch (error) {
|
|
3706
|
+
this.logger(`[agent-sdk] cursor-hosted session list failed; falling back to local: ${describeError(error)}`);
|
|
3707
|
+
}
|
|
3708
|
+
}
|
|
3709
|
+
const records = yield this.sessions.list();
|
|
3710
|
+
// Local store is createdAt-asc; reverse so newest created leads.
|
|
3711
|
+
return {
|
|
3712
|
+
sessions: this.summarizeSessionRecords([...records].reverse(), caller, includeAll),
|
|
3713
|
+
source: "local",
|
|
3714
|
+
};
|
|
3715
|
+
});
|
|
3716
|
+
}
|
|
3717
|
+
/**
|
|
3718
|
+
* The single newest session visible to `caller` (`GET /v1/sessions?latest=1`).
|
|
3719
|
+
*
|
|
3720
|
+
* Short-circuits the full listing: the local store already holds every
|
|
3721
|
+
* session this host created plus the startup bulk restore, so the common
|
|
3722
|
+
* case answers from memory without a sink round trip. Only when the local
|
|
3723
|
+
* store has nothing for the caller (fresh host, `restore: "off"`) does it
|
|
3724
|
+
* ask the sink — for a bounded newest-written window when the backend
|
|
3725
|
+
* can order by write time, else the full list. A session written by
|
|
3726
|
+
* another replica moments ago can lose to a local one; callers that need
|
|
3727
|
+
* the exact newest row should use the full listing.
|
|
3728
|
+
*/
|
|
3729
|
+
latestSessionWithSource(caller, options) {
|
|
3730
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3731
|
+
var _a;
|
|
3732
|
+
var _b;
|
|
3733
|
+
const includeAll = (_b = options === null || options === void 0 ? void 0 : options.includeAll) !== null && _b !== void 0 ? _b : false;
|
|
3734
|
+
const newestVisible = (records) => {
|
|
3735
|
+
let newest;
|
|
3736
|
+
for (const record of records) {
|
|
3737
|
+
if (!(includeAll || samePrincipal(record.auth, caller))) {
|
|
3738
|
+
continue;
|
|
3739
|
+
}
|
|
3740
|
+
if (newest === undefined || record.updatedAt.localeCompare(newest.updatedAt) > 0) {
|
|
3741
|
+
newest = record;
|
|
3742
|
+
}
|
|
3743
|
+
}
|
|
3744
|
+
return newest;
|
|
3745
|
+
};
|
|
3746
|
+
const local = newestVisible(yield this.sessions.list());
|
|
3747
|
+
if (local !== undefined) {
|
|
3748
|
+
return {
|
|
3749
|
+
sessions: this.summarizeSessionRecords([local], caller, includeAll),
|
|
3750
|
+
source: "local",
|
|
3751
|
+
};
|
|
3752
|
+
}
|
|
3753
|
+
if (((_a = this.storage) === null || _a === void 0 ? void 0 : _a.isCursorHosted) === true) {
|
|
3754
|
+
try {
|
|
3755
|
+
const window = yield this.storage.listNewestSessionsFromSink(LATEST_SESSION_SINK_WINDOW);
|
|
3756
|
+
if (window !== undefined) {
|
|
3757
|
+
const stored = newestVisible(window);
|
|
3758
|
+
if (stored !== undefined) {
|
|
3759
|
+
return {
|
|
3760
|
+
sessions: this.summarizeSessionRecords([stored], caller, includeAll),
|
|
3761
|
+
source: "cursor-hosted",
|
|
3762
|
+
};
|
|
3763
|
+
}
|
|
3764
|
+
}
|
|
3765
|
+
}
|
|
3766
|
+
catch (error) {
|
|
3767
|
+
this.logger(`[agent-sdk] cursor-hosted newest-session lookup failed; falling back to the full list: ${describeError(error)}`);
|
|
3768
|
+
}
|
|
3769
|
+
}
|
|
3770
|
+
const listed = yield this.listSessionsWithSource(caller, options);
|
|
3771
|
+
return { sessions: listed.sessions.slice(0, 1), source: listed.source };
|
|
3772
|
+
});
|
|
3773
|
+
}
|
|
3774
|
+
/**
|
|
3775
|
+
* Durable sink rows plus local records (local wins on the same id when
|
|
3776
|
+
* newer — covers debounce windows that have not flushed yet). Newest
|
|
3777
|
+
* `updatedAt` first.
|
|
3778
|
+
*/
|
|
3779
|
+
mergeStoredAndLocalSessions() {
|
|
3780
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3781
|
+
const storage = this.storage;
|
|
3782
|
+
if (storage === undefined) {
|
|
3783
|
+
throw new Error("storage is not configured");
|
|
3784
|
+
}
|
|
3785
|
+
const stored = yield storage.listSessionsFromSink();
|
|
3786
|
+
const local = yield this.sessions.list();
|
|
3787
|
+
const byId = new Map();
|
|
3788
|
+
for (const record of stored) {
|
|
3789
|
+
byId.set(record.sessionId, record);
|
|
3790
|
+
}
|
|
3791
|
+
for (const record of local) {
|
|
3792
|
+
const existing = byId.get(record.sessionId);
|
|
3793
|
+
if (existing === undefined || record.updatedAt.localeCompare(existing.updatedAt) >= 0) {
|
|
3794
|
+
byId.set(record.sessionId, record);
|
|
3795
|
+
}
|
|
3796
|
+
}
|
|
3797
|
+
return [...byId.values()].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
3798
|
+
});
|
|
3799
|
+
}
|
|
3800
|
+
summarizeSessionRecords(records, caller, includeAll) {
|
|
3801
|
+
return records
|
|
3802
|
+
.filter(record => includeAll || samePrincipal(record.auth, caller))
|
|
3803
|
+
.map(record => {
|
|
3804
|
+
var _a;
|
|
3805
|
+
const links = sessionExternalLinks(record);
|
|
3806
|
+
return {
|
|
3807
|
+
sessionId: record.sessionId,
|
|
3808
|
+
channelId: record.channelId,
|
|
3809
|
+
mode: record.mode,
|
|
3810
|
+
title: record.title,
|
|
3811
|
+
turnCount: record.turnCount,
|
|
3812
|
+
running: this.activeTurns.has(record.sessionId) || this.pendingTurns.has(record.sessionId),
|
|
3813
|
+
queued: this.turnGovernor.limited
|
|
3814
|
+
? this.turnGovernor.isQueued(record.sessionId)
|
|
3815
|
+
: undefined,
|
|
3816
|
+
createdAt: record.createdAt,
|
|
3817
|
+
updatedAt: record.updatedAt,
|
|
3818
|
+
dryRun: record.dryRun === true ? true : undefined,
|
|
3819
|
+
continuationToken: record.httpContinuationKey !== undefined
|
|
3820
|
+
? formatContinuationToken(HTTP_CHANNEL_ID, record.httpContinuationKey)
|
|
3821
|
+
: record.continuationKey === null
|
|
3822
|
+
? null
|
|
3823
|
+
: formatContinuationToken(record.channelId, record.continuationKey),
|
|
3824
|
+
externalUrl: (_a = links.slack) !== null && _a !== void 0 ? _a : links.github,
|
|
3825
|
+
prUrl: links.github,
|
|
3826
|
+
};
|
|
3827
|
+
});
|
|
3828
|
+
}
|
|
3829
|
+
eventStream(sessionId_1) {
|
|
3830
|
+
return __awaiter(this, arguments, void 0, function* (sessionId, startIndex = 0) {
|
|
3831
|
+
// Playground Trace replays this stream; backfill before the snapshot
|
|
3832
|
+
// so historical Sherlock-style cloud turns can show billed cost.
|
|
3833
|
+
yield this.backfillCloudTurnCosts(sessionId);
|
|
3834
|
+
const log = yield this.logs.get(sessionId);
|
|
3835
|
+
return log.stream(startIndex);
|
|
3836
|
+
});
|
|
3837
|
+
}
|
|
3838
|
+
/**
|
|
3839
|
+
* Append an assistant message to a session transcript without a model turn
|
|
3840
|
+
* and without a user `message.received` bubble. Used by host-driven channel
|
|
3841
|
+
* tasks (e.g. webhook stages) to publish a final summary.
|
|
3842
|
+
*/
|
|
3843
|
+
emitAssistantMessage(sessionId, text) {
|
|
3844
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3845
|
+
if (this.isTurnRunning(sessionId)) {
|
|
3846
|
+
throw new SessionBusyError(sessionId);
|
|
3847
|
+
}
|
|
3848
|
+
const existing = yield this.sessions.get(sessionId);
|
|
3849
|
+
if (existing === undefined) {
|
|
3850
|
+
throw new Error(`Unknown session: ${sessionId}`);
|
|
3851
|
+
}
|
|
3852
|
+
const record = yield this.updateRecord(sessionId, current => (Object.assign(Object.assign({}, current), { turnCount: current.turnCount + 1 })));
|
|
3853
|
+
const turnId = `turn_${record.turnCount}`;
|
|
3854
|
+
yield this.appendEvent(sessionId, {
|
|
3855
|
+
type: "turn.started",
|
|
3856
|
+
turnId,
|
|
3857
|
+
data: {},
|
|
3858
|
+
});
|
|
3859
|
+
yield this.appendEvent(sessionId, {
|
|
3860
|
+
type: "message.completed",
|
|
3861
|
+
turnId,
|
|
3862
|
+
data: { text, finishReason: "stop" },
|
|
3863
|
+
});
|
|
3864
|
+
yield this.appendEvent(sessionId, {
|
|
3865
|
+
type: "turn.completed",
|
|
3866
|
+
turnId,
|
|
3867
|
+
data: { result: text },
|
|
3868
|
+
});
|
|
3869
|
+
yield this.appendEvent(sessionId, {
|
|
3870
|
+
type: "session.waiting",
|
|
3871
|
+
data: {},
|
|
3872
|
+
});
|
|
3873
|
+
this.lastOutcomes.set(sessionId, {
|
|
3874
|
+
status: "finished",
|
|
3875
|
+
result: text,
|
|
3876
|
+
});
|
|
3877
|
+
});
|
|
3878
|
+
}
|
|
3879
|
+
/**
|
|
3880
|
+
* Billed/catalog cost for a session. Powers playground, traces, and
|
|
3881
|
+
* `agent-sdk cost` via `GET /v1/session/:id/cost`.
|
|
3882
|
+
*/
|
|
3883
|
+
sessionCost(sessionId) {
|
|
3884
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3885
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
3886
|
+
const record = yield this.getOrRestoreSession(sessionId);
|
|
3887
|
+
if (record === undefined) {
|
|
3888
|
+
throw new Error(`Unknown session: ${sessionId}`);
|
|
3889
|
+
}
|
|
3890
|
+
yield this.backfillCloudTurnCosts(sessionId);
|
|
3891
|
+
const events = yield this.listEvents(sessionId);
|
|
3892
|
+
const sdkAgentId = (_a = (record.sdkAgentId !== undefined && record.sdkAgentId !== ""
|
|
3893
|
+
? record.sdkAgentId
|
|
3894
|
+
: undefined)) !== null && _a !== void 0 ? _a : cloudSdkAgentIdFromEvents(events);
|
|
3895
|
+
const model = (_b = this.project.agent.model) !== null && _b !== void 0 ? _b : DEFAULT_MODEL;
|
|
3896
|
+
const priced = yield this.estimateTurnCost({
|
|
3897
|
+
model,
|
|
3898
|
+
agentId: sdkAgentId,
|
|
3899
|
+
conversationId: sdkAgentId !== null && sdkAgentId !== void 0 ? sdkAgentId : sessionId,
|
|
3900
|
+
});
|
|
3901
|
+
if (priced !== undefined && (priced.source === "usage" || priced.totalUsd > 0)) {
|
|
3902
|
+
return {
|
|
3903
|
+
sessionId,
|
|
3904
|
+
source: priced.source,
|
|
3905
|
+
cost: {
|
|
3906
|
+
rawCostCents: (_d = (_c = priced.rawCostCents) !== null && _c !== void 0 ? _c : priced.chargedCents) !== null && _d !== void 0 ? _d : 0,
|
|
3907
|
+
chargedCents: (_e = priced.chargedCents) !== null && _e !== void 0 ? _e : priced.totalUsd * 100,
|
|
3908
|
+
},
|
|
3909
|
+
costUsd: priced.totalUsd,
|
|
3910
|
+
rawCostUsd: roundCostUsd(((_g = (_f = priced.rawCostCents) !== null && _f !== void 0 ? _f : priced.chargedCents) !== null && _g !== void 0 ? _g : priced.totalUsd * 100) / 100),
|
|
3911
|
+
model: priced.model,
|
|
3912
|
+
};
|
|
3913
|
+
}
|
|
3914
|
+
let costUsd = 0;
|
|
3915
|
+
let rawCostUsd = 0;
|
|
3916
|
+
let source = "unavailable";
|
|
3917
|
+
let modelId;
|
|
3918
|
+
for (const event of events) {
|
|
3919
|
+
if (event.type !== "turn.completed" || event.data.cost === undefined) {
|
|
3920
|
+
continue;
|
|
3921
|
+
}
|
|
3922
|
+
costUsd = roundCostUsd(costUsd + event.data.cost.totalUsd);
|
|
3923
|
+
const rawCents = event.data.cost.rawCostCents;
|
|
3924
|
+
rawCostUsd = roundCostUsd(rawCostUsd + (rawCents !== undefined ? rawCents / 100 : event.data.cost.totalUsd));
|
|
3925
|
+
source = event.data.cost.source;
|
|
3926
|
+
modelId = event.data.cost.model;
|
|
3927
|
+
}
|
|
3928
|
+
return {
|
|
3929
|
+
sessionId,
|
|
3930
|
+
source,
|
|
3931
|
+
cost: {
|
|
3932
|
+
rawCostCents: rawCostUsd * 100,
|
|
3933
|
+
chargedCents: costUsd * 100,
|
|
3934
|
+
},
|
|
3935
|
+
costUsd,
|
|
3936
|
+
rawCostUsd,
|
|
3937
|
+
model: modelId,
|
|
3938
|
+
};
|
|
3939
|
+
});
|
|
3940
|
+
}
|
|
3941
|
+
/** Snapshot of recorded events (for download / offline trace). */
|
|
3942
|
+
listEvents(sessionId_1) {
|
|
3943
|
+
return __awaiter(this, arguments, void 0, function* (sessionId, startIndex = 0, options) {
|
|
3944
|
+
if ((options === null || options === void 0 ? void 0 : options.backfillCloudCost) === true) {
|
|
3945
|
+
yield this.backfillCloudTurnCosts(sessionId);
|
|
3946
|
+
}
|
|
3947
|
+
const log = yield this.logs.get(sessionId);
|
|
3948
|
+
return log.snapshot(startIndex);
|
|
3949
|
+
});
|
|
3950
|
+
}
|
|
3951
|
+
/**
|
|
3952
|
+
* Historical cloud turns may lack usage/cost on `turn.completed` (pre-0.1.29
|
|
3953
|
+
* engines, or getUsage still zero at turn end). Fill them from billed usage
|
|
3954
|
+
* before the playground replays the stream so Trace can show cost.
|
|
3955
|
+
*/
|
|
3956
|
+
backfillCloudTurnCosts(sessionId) {
|
|
3957
|
+
if (this.cloudCostBackfillDone.has(sessionId)) {
|
|
3958
|
+
return Promise.resolve();
|
|
3959
|
+
}
|
|
3960
|
+
const pending = this.cloudCostBackfill.get(sessionId);
|
|
3961
|
+
if (pending !== undefined) {
|
|
3962
|
+
return pending;
|
|
3963
|
+
}
|
|
3964
|
+
const work = this.runCloudTurnCostBackfill(sessionId)
|
|
3965
|
+
.catch(error => {
|
|
3966
|
+
this.logger(`[agent-sdk] cloud cost backfill failed for ${sessionId}: ${describeError(error)}`);
|
|
3967
|
+
})
|
|
3968
|
+
.finally(() => {
|
|
3969
|
+
this.cloudCostBackfill.delete(sessionId);
|
|
3970
|
+
});
|
|
3971
|
+
this.cloudCostBackfill.set(sessionId, work);
|
|
3972
|
+
return work;
|
|
3973
|
+
}
|
|
3974
|
+
runCloudTurnCostBackfill(sessionId) {
|
|
3975
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3976
|
+
var _a;
|
|
3977
|
+
var _b, _c;
|
|
3978
|
+
const log = yield this.logs.get(sessionId);
|
|
3979
|
+
const events = yield log.snapshot(0);
|
|
3980
|
+
if (!events.some(event => isUnpricedCompletedTurn(event))) {
|
|
3981
|
+
if (!this.isTurnRunning(sessionId) && events.some(event => event.type === "turn.completed")) {
|
|
3982
|
+
this.cloudCostBackfillDone.add(sessionId);
|
|
3983
|
+
}
|
|
3984
|
+
return;
|
|
3985
|
+
}
|
|
3986
|
+
const record = yield this.sessions.get(sessionId);
|
|
3987
|
+
const sdkAgentId = (_b = (((_a = record === null || record === void 0 ? void 0 : record.sdkAgentId) === null || _a === void 0 ? void 0 : _a.startsWith("bc-")) ? record.sdkAgentId : undefined)) !== null && _b !== void 0 ? _b : cloudSdkAgentIdFromEvents(events);
|
|
3988
|
+
if (sdkAgentId === undefined) {
|
|
3989
|
+
return;
|
|
3990
|
+
}
|
|
3991
|
+
const billed = yield this.getCloudUsage(sdkAgentId);
|
|
3992
|
+
if (billed === undefined) {
|
|
3993
|
+
return;
|
|
3994
|
+
}
|
|
3995
|
+
const model = (_c = this.project.agent.model) !== null && _c !== void 0 ? _c : DEFAULT_MODEL;
|
|
3996
|
+
const patched = applyCloudUsageToEvents(events, billed, model);
|
|
3997
|
+
if (patched === undefined) {
|
|
3998
|
+
return;
|
|
3999
|
+
}
|
|
4000
|
+
for (let i = 0; i < patched.length; i++) {
|
|
4001
|
+
const next = patched[i];
|
|
4002
|
+
const prev = events[i];
|
|
4003
|
+
if (next === undefined || prev === next) {
|
|
4004
|
+
continue;
|
|
4005
|
+
}
|
|
4006
|
+
log.replace(i, next);
|
|
4007
|
+
}
|
|
4008
|
+
if (!(yield log.snapshot(0)).some(event => isUnpricedCompletedTurn(event))) {
|
|
4009
|
+
this.cloudCostBackfillDone.add(sessionId);
|
|
4010
|
+
}
|
|
4011
|
+
});
|
|
4012
|
+
}
|
|
4013
|
+
setContinuationToken(sessionId, token) {
|
|
4014
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4015
|
+
var _a;
|
|
4016
|
+
const previous = yield this.sessions.get(sessionId);
|
|
4017
|
+
yield this.updateRecord(sessionId, current => (Object.assign(Object.assign({}, current), { continuationKey: token })));
|
|
4018
|
+
// Keep armed reminders addressed at the conversation across channel
|
|
4019
|
+
// continuation rotation.
|
|
4020
|
+
if ((previous === null || previous === void 0 ? void 0 : previous.continuationKey) != null && token !== null && token !== previous.continuationKey) {
|
|
4021
|
+
yield (_a = this.reminderApi) === null || _a === void 0 ? void 0 : _a.rekeyContinuation(previous.channelId, previous.continuationKey, token);
|
|
4022
|
+
}
|
|
4023
|
+
});
|
|
4024
|
+
}
|
|
4025
|
+
isTurnRunning(sessionId) {
|
|
4026
|
+
// Include the admit window (pendingTurns) and coalesce drain so busy
|
|
4027
|
+
// policies do not treat a turn that has finished executeTurn but not yet
|
|
4028
|
+
// resumed queued follow-ups as idle (avoids clearCoalesced races).
|
|
4029
|
+
return (this.activeTurns.has(sessionId) ||
|
|
4030
|
+
this.pendingTurns.has(sessionId) ||
|
|
4031
|
+
this.coalesceDrains.has(sessionId));
|
|
4032
|
+
}
|
|
4033
|
+
sessionInfo(record) {
|
|
4034
|
+
return Object.assign(Object.assign({ id: record.sessionId, channelId: record.channelId, mode: record.mode, purpose: record.purpose === "eval" ? "eval" : "live", dryRun: record.dryRun === true ? true : undefined, asOf: record.asOf, auth: record.auth, continuationKey: record.continuationKey }, (record.title === undefined ? {} : { title: record.title })), (record.sdkAgentId === undefined ? {} : { sdkAgentId: record.sdkAgentId }));
|
|
4035
|
+
}
|
|
4036
|
+
channelSession(sessionId, record, isNew, coalesced = false, since = 0) {
|
|
4037
|
+
return Object.assign(Object.assign({ id: sessionId, continuationToken: record.continuationKey, isNew }, (coalesced ? { coalesced: true } : {})), { info: this.sessionInfo(record), getEventStream: (options) => { var _a; return this.eventStream(sessionId, (_a = options === null || options === void 0 ? void 0 : options.startIndex) !== null && _a !== void 0 ? _a : since); }, waitForCompletion: () => this.waitForCompletion(sessionId), setContinuationToken: (token) => this.setContinuationToken(sessionId, token) });
|
|
4038
|
+
}
|
|
4039
|
+
resolveChannelId(channel) {
|
|
4040
|
+
if (typeof channel === "string") {
|
|
4041
|
+
if (channel !== HTTP_CHANNEL_ID &&
|
|
4042
|
+
!this.channelsById.has(channel) &&
|
|
4043
|
+
!channel.startsWith(SCHEDULE_CHANNEL_PREFIX)) {
|
|
4044
|
+
throw new UnknownChannelError(channel);
|
|
4045
|
+
}
|
|
4046
|
+
return channel;
|
|
4047
|
+
}
|
|
4048
|
+
if (channel.__agentServe === "http-channel") {
|
|
4049
|
+
return HTTP_CHANNEL_ID;
|
|
4050
|
+
}
|
|
4051
|
+
for (const [id, definition] of this.channelsById) {
|
|
4052
|
+
if (definition === channel) {
|
|
4053
|
+
return id;
|
|
4054
|
+
}
|
|
4055
|
+
}
|
|
4056
|
+
throw new UnknownChannelError("<unregistered channel definition>");
|
|
4057
|
+
}
|
|
4058
|
+
initialChannelState(channelId, override) {
|
|
4059
|
+
if (override !== undefined) {
|
|
4060
|
+
return structuredClone(override);
|
|
4061
|
+
}
|
|
4062
|
+
const definition = this.channelsById.get(channelId);
|
|
4063
|
+
if ((definition === null || definition === void 0 ? void 0 : definition.state) === undefined) {
|
|
4064
|
+
return undefined;
|
|
4065
|
+
}
|
|
4066
|
+
return structuredClone(definition.state);
|
|
4067
|
+
}
|
|
4068
|
+
/** Serialized, immutable record updates (latest-wins per session). */
|
|
4069
|
+
updateRecord(sessionId, updater) {
|
|
4070
|
+
var _a;
|
|
4071
|
+
const chain = (_a = this.updateChains.get(sessionId)) !== null && _a !== void 0 ? _a : Promise.resolve();
|
|
4072
|
+
const next = chain.then(() => __awaiter(this, void 0, void 0, function* () {
|
|
4073
|
+
var _a;
|
|
4074
|
+
const current = yield this.sessions.get(sessionId);
|
|
4075
|
+
if (current === undefined) {
|
|
4076
|
+
throw new Error(`Session ${sessionId} not found`);
|
|
4077
|
+
}
|
|
4078
|
+
const updated = Object.assign(Object.assign({}, updater(current)), { updatedAt: new Date().toISOString() });
|
|
4079
|
+
yield this.sessions.save(updated);
|
|
4080
|
+
(_a = this.storage) === null || _a === void 0 ? void 0 : _a.sessionRecord(updated);
|
|
4081
|
+
return updated;
|
|
4082
|
+
}));
|
|
4083
|
+
this.updateChains.set(sessionId, next.catch(() => { }));
|
|
4084
|
+
return next;
|
|
4085
|
+
}
|
|
4086
|
+
// ==========================================================================
|
|
4087
|
+
// Lifecycle
|
|
4088
|
+
// ==========================================================================
|
|
4089
|
+
/**
|
|
4090
|
+
* Restore sessions from the authored storage sink. Called once at
|
|
4091
|
+
* serve start, before channels accept traffic: any persisted session
|
|
4092
|
+
* missing from the local `--state-root` is materialized there — events
|
|
4093
|
+
* as `events.ndjson`, record as `session.json` — after which every
|
|
4094
|
+
* existing read path (continuation lookup, event replay/streams) works
|
|
4095
|
+
* unchanged. Local state always wins over the sink, so restarts on the
|
|
4096
|
+
* same host are no-ops.
|
|
4097
|
+
*
|
|
4098
|
+
* Failures are logged and skipped; a broken store (including rows that
|
|
4099
|
+
* violate the SessionRecord shape) never blocks serve start.
|
|
4100
|
+
*/
|
|
4101
|
+
restoreStoredSessions() {
|
|
4102
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4103
|
+
const storage = this.storage;
|
|
4104
|
+
if (storage === undefined || !storage.canBulkRestore) {
|
|
4105
|
+
return;
|
|
4106
|
+
}
|
|
4107
|
+
const policy = storage.policy.restore;
|
|
4108
|
+
if (policy === "off") {
|
|
4109
|
+
return;
|
|
4110
|
+
}
|
|
4111
|
+
try {
|
|
4112
|
+
yield this.runBulkRestore(storage, policy);
|
|
4113
|
+
}
|
|
4114
|
+
catch (error) {
|
|
4115
|
+
this.logger(`[agent-sdk] session restore failed: ${describeError(error)}`);
|
|
4116
|
+
}
|
|
4117
|
+
});
|
|
4118
|
+
}
|
|
4119
|
+
runBulkRestore(storage, policy) {
|
|
4120
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4121
|
+
const loaded = yield storage.listSessions();
|
|
4122
|
+
// Guardrails on whatever the sink returned: drop invalid rows and
|
|
4123
|
+
// sessions older than the age window, then restore newest-first under
|
|
4124
|
+
// the count cap. Anything filtered out here stays reachable through
|
|
4125
|
+
// the lazy continuation-lookup path.
|
|
4126
|
+
const cutoff = Date.now() - policy.maxAgeMs;
|
|
4127
|
+
const candidates = loaded
|
|
4128
|
+
.map(record => ({
|
|
4129
|
+
record,
|
|
4130
|
+
// Numeric timestamp for both the age filter and the sort; a
|
|
4131
|
+
// non-string updatedAt (e.g. a raw DB Date from an untyped sink)
|
|
4132
|
+
// parses to NaN and is dropped instead of crashing serve start.
|
|
4133
|
+
updatedAt: typeof (record === null || record === void 0 ? void 0 : record.updatedAt) === "string" ? Date.parse(record.updatedAt) : Number.NaN,
|
|
4134
|
+
}))
|
|
4135
|
+
.filter(({ record, updatedAt }) => typeof (record === null || record === void 0 ? void 0 : record.sessionId) === "string" && record.sessionId !== "" && updatedAt >= cutoff)
|
|
4136
|
+
.sort((a, b) => b.updatedAt - a.updatedAt)
|
|
4137
|
+
.slice(0, policy.maxSessions)
|
|
4138
|
+
.map(({ record }) => record);
|
|
4139
|
+
let restored = 0;
|
|
4140
|
+
let budget = policy.maxTotalBytes;
|
|
4141
|
+
// Sink round trips dominate restore latency, so event streams are
|
|
4142
|
+
// fetched with bounded parallelism; writes stay sequential, newest
|
|
4143
|
+
// first, so the byte budget is enforced deterministically.
|
|
4144
|
+
outer: for (let i = 0; i < candidates.length; i += RESTORE_FETCH_CONCURRENCY) {
|
|
4145
|
+
const chunk = candidates.slice(i, i + RESTORE_FETCH_CONCURRENCY);
|
|
4146
|
+
const fetched = yield Promise.all(chunk.map((record) => __awaiter(this, void 0, void 0, function* () {
|
|
4147
|
+
try {
|
|
4148
|
+
// Local state always wins over the sink.
|
|
4149
|
+
if ((yield this.sessions.get(record.sessionId)) !== undefined) {
|
|
4150
|
+
return undefined;
|
|
4151
|
+
}
|
|
4152
|
+
const events = yield storage.listSessionEvents(record.sessionId);
|
|
4153
|
+
return { record, events };
|
|
4154
|
+
}
|
|
4155
|
+
catch (error) {
|
|
4156
|
+
this.logger(`[agent-sdk] failed to restore session ${record.sessionId}: ${describeError(error)}`);
|
|
4157
|
+
return undefined;
|
|
4158
|
+
}
|
|
4159
|
+
})));
|
|
4160
|
+
for (const item of fetched) {
|
|
4161
|
+
if (item === undefined) {
|
|
4162
|
+
continue;
|
|
4163
|
+
}
|
|
4164
|
+
const ndjson = eventsNdjson(item.events);
|
|
4165
|
+
// Budget is checked BEFORE anything is written: one oversized
|
|
4166
|
+
// session cannot blow past maxTotalBytes on disk. Skipped sessions
|
|
4167
|
+
// stay reachable through the lazy path.
|
|
4168
|
+
const bytes = Buffer.byteLength(JSON.stringify(item.record), "utf8") +
|
|
4169
|
+
(ndjson === undefined ? 0 : Buffer.byteLength(ndjson, "utf8"));
|
|
4170
|
+
if (bytes > budget) {
|
|
4171
|
+
this.logger(`[agent-sdk] session restore stopped at the ${policy.maxTotalBytes}-byte budget after ${restored} session(s); remaining sessions restore lazily on resume`);
|
|
4172
|
+
break outer;
|
|
4173
|
+
}
|
|
4174
|
+
try {
|
|
4175
|
+
yield this.materializeSession(item.record, ndjson);
|
|
4176
|
+
budget -= bytes;
|
|
4177
|
+
restored += 1;
|
|
4178
|
+
}
|
|
4179
|
+
catch (error) {
|
|
4180
|
+
this.logger(`[agent-sdk] failed to restore session ${item.record.sessionId}: ${describeError(error)}`);
|
|
4181
|
+
}
|
|
4182
|
+
}
|
|
4183
|
+
}
|
|
4184
|
+
if (restored > 0) {
|
|
4185
|
+
this.logger(`[agent-sdk] restored ${restored} session(s) from storage`);
|
|
4186
|
+
}
|
|
4187
|
+
});
|
|
4188
|
+
}
|
|
4189
|
+
/**
|
|
4190
|
+
* Local session lookup with lazy restore by session id.
|
|
4191
|
+
*
|
|
4192
|
+
* `GET /v1/sessions` (cursor-hosted) can list durable rows that were not
|
|
4193
|
+
* bulk-restored into `--state-root` (restore caps, `restore: "off"`, or
|
|
4194
|
+
* eval Open-trace by id). Events/stream/follow-up must materialize that
|
|
4195
|
+
* one session before answering, or the playground shows an empty timeline
|
|
4196
|
+
* after redeploy.
|
|
4197
|
+
*/
|
|
4198
|
+
getOrRestoreSession(sessionId) {
|
|
4199
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4200
|
+
const local = yield this.sessions.get(sessionId);
|
|
4201
|
+
if (local !== undefined) {
|
|
4202
|
+
return local;
|
|
4203
|
+
}
|
|
4204
|
+
return this.restoreSessionById(sessionId);
|
|
4205
|
+
});
|
|
4206
|
+
}
|
|
4207
|
+
/**
|
|
4208
|
+
* Lazy restore: a continuation lookup missed locally, so ask the sink
|
|
4209
|
+
* for that one session and materialize it before resuming. Disk grows
|
|
4210
|
+
* with sessions that are actually resumed, not with history.
|
|
4211
|
+
*
|
|
4212
|
+
* Concurrent misses for the same key (webhook redeliveries, retries)
|
|
4213
|
+
* share one in-flight restore — a second materialization racing the
|
|
4214
|
+
* first resumed turn could truncate events the turn already appended.
|
|
4215
|
+
* Sink errors propagate and fail the send (see
|
|
4216
|
+
* {@link StorageCoordinator.getSessionByContinuation}).
|
|
4217
|
+
*/
|
|
4218
|
+
restoreSessionByContinuation(channelId, continuationKey) {
|
|
4219
|
+
const storage = this.storage;
|
|
4220
|
+
if (storage === undefined) {
|
|
4221
|
+
return Promise.resolve(undefined);
|
|
4222
|
+
}
|
|
4223
|
+
const key = `${channelId}\u0000${continuationKey}`;
|
|
4224
|
+
let pending = this.restoreInFlight.get(key);
|
|
4225
|
+
if (pending === undefined) {
|
|
4226
|
+
pending = this.loadAndMaterializeByContinuation(storage, channelId, continuationKey).finally(() => this.restoreInFlight.delete(key));
|
|
4227
|
+
this.restoreInFlight.set(key, pending);
|
|
4228
|
+
}
|
|
4229
|
+
return pending;
|
|
4230
|
+
}
|
|
4231
|
+
restoreSessionById(sessionId) {
|
|
4232
|
+
const storage = this.storage;
|
|
4233
|
+
if (storage === undefined) {
|
|
4234
|
+
return Promise.resolve(undefined);
|
|
4235
|
+
}
|
|
4236
|
+
const key = `id\u0000${sessionId}`;
|
|
4237
|
+
let pending = this.restoreInFlight.get(key);
|
|
4238
|
+
if (pending === undefined) {
|
|
4239
|
+
pending = this.loadAndMaterializeById(storage, sessionId).finally(() => this.restoreInFlight.delete(key));
|
|
4240
|
+
this.restoreInFlight.set(key, pending);
|
|
4241
|
+
}
|
|
4242
|
+
return pending;
|
|
4243
|
+
}
|
|
4244
|
+
loadAndMaterializeByContinuation(storage, channelId, continuationKey) {
|
|
4245
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4246
|
+
const record = yield storage.getSessionByContinuation(channelId, continuationKey);
|
|
4247
|
+
if (record === undefined) {
|
|
4248
|
+
// Definitive miss: the caller starts a fresh session under this token.
|
|
4249
|
+
return undefined;
|
|
4250
|
+
}
|
|
4251
|
+
return this.materializeRestoredRecord(record);
|
|
4252
|
+
});
|
|
4253
|
+
}
|
|
4254
|
+
loadAndMaterializeById(storage, sessionId) {
|
|
4255
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4256
|
+
const record = yield storage.getSessionById(sessionId);
|
|
4257
|
+
if (record === undefined) {
|
|
4258
|
+
return undefined;
|
|
4259
|
+
}
|
|
4260
|
+
return this.materializeRestoredRecord(record);
|
|
4261
|
+
});
|
|
4262
|
+
}
|
|
4263
|
+
materializeRestoredRecord(record) {
|
|
4264
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4265
|
+
const local = yield this.sessions.get(record.sessionId);
|
|
4266
|
+
if (local !== undefined) {
|
|
4267
|
+
return local;
|
|
4268
|
+
}
|
|
4269
|
+
const storage = this.storage;
|
|
4270
|
+
if (storage === undefined) {
|
|
4271
|
+
return record;
|
|
4272
|
+
}
|
|
4273
|
+
const events = yield storage.listSessionEvents(record.sessionId);
|
|
4274
|
+
// The durable record's bookkeeping may lag the event stream (the
|
|
4275
|
+
// storage coordinator skips volatile-only record rewrites, bounded).
|
|
4276
|
+
// New turnIds mint from turnCount, so clamp it to the highest turn the
|
|
4277
|
+
// stream already used — otherwise a replaced pod re-mints turn_N ids
|
|
4278
|
+
// and trajectory/cost grouping merges unrelated turns.
|
|
4279
|
+
const restored = withTurnCountClampedToEvents(record, events);
|
|
4280
|
+
yield this.materializeSession(restored, eventsNdjson(events));
|
|
4281
|
+
this.logger(`[agent-sdk] lazily restored session ${restored.sessionId} from storage`);
|
|
4282
|
+
return restored;
|
|
4283
|
+
});
|
|
4284
|
+
}
|
|
4285
|
+
/**
|
|
4286
|
+
* Write one restored session to the local state root. Events land on
|
|
4287
|
+
* disk before the record: a session becomes visible (and addressable via
|
|
4288
|
+
* its continuation key) only with its full stream in place, and the
|
|
4289
|
+
* lazily opened event log hydrates from this file.
|
|
4290
|
+
*/
|
|
4291
|
+
materializeSession(record, eventsNdjson) {
|
|
4292
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4293
|
+
if (eventsNdjson !== undefined) {
|
|
4294
|
+
const filePath = this.sessions.eventFilePath(record.sessionId);
|
|
4295
|
+
yield mkdir(dirname(filePath), { recursive: true });
|
|
4296
|
+
yield writeFile(filePath, eventsNdjson, "utf8");
|
|
4297
|
+
}
|
|
4298
|
+
yield this.sessions.save(record);
|
|
4299
|
+
});
|
|
4300
|
+
}
|
|
4301
|
+
trackBackground(promise) {
|
|
4302
|
+
this.backgroundWork.add(promise);
|
|
4303
|
+
void promise
|
|
4304
|
+
.catch(() => { })
|
|
4305
|
+
.finally(() => {
|
|
4306
|
+
this.backgroundWork.delete(promise);
|
|
4307
|
+
});
|
|
4308
|
+
}
|
|
4309
|
+
/** In-process event-log residency (OOM-suspect cache vs durable count). */
|
|
4310
|
+
eventLogMemoryStats() {
|
|
4311
|
+
return this.logs.memoryStats();
|
|
4312
|
+
}
|
|
4313
|
+
/** Drain running turns, dispatches, and pending writes, then dispose. */
|
|
4314
|
+
close() {
|
|
4315
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4316
|
+
var _a, _b, _c, _d, _e;
|
|
4317
|
+
(_a = this.stopMemoryTelemetry) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
4318
|
+
this.approvals.cancelAll("deny");
|
|
4319
|
+
this.auths.cancelAll("deny");
|
|
4320
|
+
yield this.oauthHub.close();
|
|
4321
|
+
yield Promise.allSettled([...this.activeTurns.values()]);
|
|
4322
|
+
yield Promise.allSettled([...this.backgroundWork]);
|
|
4323
|
+
yield Promise.allSettled([...this.dispatchChains.values()]);
|
|
4324
|
+
yield Promise.allSettled([...this.updateChains.values()]);
|
|
4325
|
+
// After the drain, so a successor need not wait out the heartbeat TTL.
|
|
4326
|
+
yield this.turnLeases.releaseAll().catch(() => { });
|
|
4327
|
+
yield this.logs.flushAll();
|
|
4328
|
+
yield (_b = this.storage) === null || _b === void 0 ? void 0 : _b.close();
|
|
4329
|
+
yield this.mcpHost.close();
|
|
4330
|
+
yield (_d = (_c = this.runner).dispose) === null || _d === void 0 ? void 0 : _d.call(_c);
|
|
4331
|
+
(_e = this.otel) === null || _e === void 0 ? void 0 : _e.close();
|
|
4332
|
+
});
|
|
4333
|
+
}
|
|
4334
|
+
}
|
|
4335
|
+
/** Parallel event-stream loads during the startup bulk restore. */
|
|
4336
|
+
const RESTORE_FETCH_CONCURRENCY = 8;
|
|
4337
|
+
/**
|
|
4338
|
+
* Newest-written sink rows fetched for `GET /v1/sessions?latest=1` when the
|
|
4339
|
+
* local store has nothing for the caller. Wide enough that the caller's own
|
|
4340
|
+
* session is normally inside it even when other principals are busier.
|
|
4341
|
+
*/
|
|
4342
|
+
const LATEST_SESSION_SINK_WINDOW = 50;
|
|
4343
|
+
/** NDJSON payload for a restored event stream; undefined when empty. */
|
|
4344
|
+
function mergePendingImages(current, incoming) {
|
|
4345
|
+
if (incoming === undefined) {
|
|
4346
|
+
return current;
|
|
4347
|
+
}
|
|
4348
|
+
if (current === undefined || current.length === 0) {
|
|
4349
|
+
return incoming;
|
|
4350
|
+
}
|
|
4351
|
+
return [...current, ...incoming];
|
|
4352
|
+
}
|
|
4353
|
+
function mergePendingWorkspaceFiles(current, incoming) {
|
|
4354
|
+
if (incoming === undefined) {
|
|
4355
|
+
return current;
|
|
4356
|
+
}
|
|
4357
|
+
if (current === undefined) {
|
|
4358
|
+
return incoming;
|
|
4359
|
+
}
|
|
4360
|
+
return Object.assign(Object.assign({}, current), incoming);
|
|
4361
|
+
}
|
|
4362
|
+
function eventsNdjson(events) {
|
|
4363
|
+
if (events.length === 0) {
|
|
4364
|
+
return undefined;
|
|
4365
|
+
}
|
|
4366
|
+
return `${events.map(event => JSON.stringify(event)).join("\n")}\n`;
|
|
4367
|
+
}
|
|
4368
|
+
/**
|
|
4369
|
+
* Restore-time turn-id fence: the durable record's `turnCount` may lag the
|
|
4370
|
+
* event stream, and the next turn's id is `turn_${turnCount + 1}`, so take
|
|
4371
|
+
* the highest `turn_<n>` the stream already recorded when it is ahead.
|
|
4372
|
+
*/
|
|
4373
|
+
export function withTurnCountClampedToEvents(record, events) {
|
|
4374
|
+
let maxTurn = 0;
|
|
4375
|
+
for (const event of events) {
|
|
4376
|
+
const turnId = event.turnId;
|
|
4377
|
+
if (typeof turnId !== "string") {
|
|
4378
|
+
continue;
|
|
4379
|
+
}
|
|
4380
|
+
const match = /^turn_(\d+)$/.exec(turnId);
|
|
4381
|
+
if ((match === null || match === void 0 ? void 0 : match[1]) !== undefined) {
|
|
4382
|
+
const turn = Number(match[1]);
|
|
4383
|
+
if (Number.isSafeInteger(turn) && turn > maxTurn) {
|
|
4384
|
+
maxTurn = turn;
|
|
4385
|
+
}
|
|
4386
|
+
}
|
|
4387
|
+
}
|
|
4388
|
+
return maxTurn > record.turnCount ? Object.assign(Object.assign({}, record), { turnCount: maxTurn }) : record;
|
|
4389
|
+
}
|
|
4390
|
+
function subagentPrompt(subagent) {
|
|
4391
|
+
var _a;
|
|
4392
|
+
if (subagent.instructions !== undefined && subagent.instructions !== "") {
|
|
4393
|
+
return subagent.instructions;
|
|
4394
|
+
}
|
|
4395
|
+
return (_a = subagent.description) !== null && _a !== void 0 ? _a : subagent.name;
|
|
4396
|
+
}
|
|
4397
|
+
/**
|
|
4398
|
+
* Subagents as the runner takes them. Shared by turns and by workspace
|
|
4399
|
+
* warming, which has to describe the same agent a turn would to reach the
|
|
4400
|
+
* executor that turn will run on.
|
|
4401
|
+
*/
|
|
4402
|
+
function buildRunnerSubagents(agent) {
|
|
4403
|
+
return agent.subagents.map(subagent => { var _a; return (Object.assign({ name: subagent.name, description: (_a = subagent.description) !== null && _a !== void 0 ? _a : subagent.name, prompt: subagentPrompt(subagent) }, (subagent.model === undefined ? {} : { model: subagent.model }))); });
|
|
4404
|
+
}
|
|
4405
|
+
function toolErrorResult(message) {
|
|
4406
|
+
return {
|
|
4407
|
+
content: [{ type: "text", text: message }],
|
|
4408
|
+
isError: true,
|
|
4409
|
+
};
|
|
4410
|
+
}
|
|
4411
|
+
function defaultGetCloudUsage(sdkAgentId) {
|
|
4412
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4413
|
+
try {
|
|
4414
|
+
const { Agent } = yield import("@cursor/sdk");
|
|
4415
|
+
const billed = yield Agent.getUsage(sdkAgentId);
|
|
4416
|
+
return {
|
|
4417
|
+
usage: mapSdkTokenUsage(billed.usage),
|
|
4418
|
+
cost: billed.cost,
|
|
4419
|
+
runs: billed.runs.map(run => ({
|
|
4420
|
+
runId: run.runId,
|
|
4421
|
+
usage: mapSdkTokenUsage(run.usage),
|
|
4422
|
+
cost: run.cost,
|
|
4423
|
+
})),
|
|
4424
|
+
};
|
|
4425
|
+
}
|
|
4426
|
+
catch (_a) {
|
|
4427
|
+
return undefined;
|
|
4428
|
+
}
|
|
4429
|
+
});
|
|
4430
|
+
}
|
|
4431
|
+
/** Detects the `{ content, isError: true }` rich-result error envelope. */
|
|
4432
|
+
function isErrorEnvelope(result) {
|
|
4433
|
+
return (typeof result === "object" &&
|
|
4434
|
+
result !== null &&
|
|
4435
|
+
!Array.isArray(result) &&
|
|
4436
|
+
Array.isArray(result.content) &&
|
|
4437
|
+
result.isError === true);
|
|
4438
|
+
}
|
|
4439
|
+
function evaluateNeedsApproval(needsApproval, input) {
|
|
4440
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4441
|
+
if (needsApproval === undefined || needsApproval === false) {
|
|
4442
|
+
return false;
|
|
4443
|
+
}
|
|
4444
|
+
if (needsApproval === true) {
|
|
4445
|
+
return true;
|
|
4446
|
+
}
|
|
4447
|
+
return Boolean(yield needsApproval(input));
|
|
4448
|
+
});
|
|
4449
|
+
}
|
|
4450
|
+
function approvalResolverLabel(auth) {
|
|
4451
|
+
var _a;
|
|
4452
|
+
if (auth === null) {
|
|
4453
|
+
return undefined;
|
|
4454
|
+
}
|
|
4455
|
+
const slackUser = (_a = auth.attributes) === null || _a === void 0 ? void 0 : _a.user_id;
|
|
4456
|
+
if (auth.authenticator === "slack" && slackUser !== undefined && slackUser !== "") {
|
|
4457
|
+
return slackUser;
|
|
4458
|
+
}
|
|
4459
|
+
return auth.principalId;
|
|
4460
|
+
}
|
|
4461
|
+
/** Let the HTTP NDJSON writer flush before the next SDK update. */
|
|
4462
|
+
function yieldEventLoop() {
|
|
4463
|
+
return new Promise(resolve => {
|
|
4464
|
+
setImmediate(resolve);
|
|
4465
|
+
});
|
|
4466
|
+
}
|
|
4467
|
+
function lastBotMessageTsOf(state) {
|
|
4468
|
+
if (state === null || state === undefined || typeof state !== "object") {
|
|
4469
|
+
return undefined;
|
|
4470
|
+
}
|
|
4471
|
+
const ts = state.lastBotMessageTs;
|
|
4472
|
+
return typeof ts === "string" && ts.trim() !== "" ? ts.trim() : undefined;
|
|
4473
|
+
}
|
|
4474
|
+
function laterLastBotMessageTs(a, b) {
|
|
4475
|
+
if (a === undefined) {
|
|
4476
|
+
return b;
|
|
4477
|
+
}
|
|
4478
|
+
if (b === undefined) {
|
|
4479
|
+
return a;
|
|
4480
|
+
}
|
|
4481
|
+
const aN = Number(a);
|
|
4482
|
+
const bN = Number(b);
|
|
4483
|
+
if (Number.isFinite(aN) && Number.isFinite(bN)) {
|
|
4484
|
+
return aN >= bN ? a : b;
|
|
4485
|
+
}
|
|
4486
|
+
if (Number.isFinite(aN)) {
|
|
4487
|
+
return a;
|
|
4488
|
+
}
|
|
4489
|
+
if (Number.isFinite(bN)) {
|
|
4490
|
+
return b;
|
|
4491
|
+
}
|
|
4492
|
+
return a;
|
|
4493
|
+
}
|