@cursor/july 0.1.101 → 0.1.102
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/README.md +27 -0
- package/dist/channels/slack/api.d.ts +1 -1
- package/dist/channels/slack/api.d.ts.map +1 -1
- package/dist/channels/slack/api.js +3 -1
- package/dist/channels/slack/assistant.d.ts +1 -1
- package/dist/channels/slack/assistant.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.d.ts +3 -2
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +28 -8
- package/dist/channels/slack/inbound.d.ts +8 -0
- package/dist/channels/slack/inbound.d.ts.map +1 -1
- package/dist/channels/slack/inbound.js +28 -0
- package/dist/channels/slack/manifest.d.ts.map +1 -1
- package/dist/channels/slack/manifest.js +2 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.BASO9Kih.js → app.CLA03RCz.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.FAqdWIXo.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.DwkqxoE0.js → VPLocalSearchBox.ifxv-cJV.js} +1 -1
- package/dist/docs/assets/chunks/{theme.CCil_13e.js → theme.DWCwM6p-.js} +2 -2
- package/dist/docs/assets/concepts.md.rPzW2HZN.js +1 -0
- package/dist/docs/assets/guides_cloud-agents.md.DumwWXba.js +9 -0
- package/dist/docs/assets/guides_cloud-agents.md.DumwWXba.lean.js +1 -0
- package/dist/docs/assets/{guides_cloud-runtime.md.CkYbjnAX.js → guides_cloud-runtime.md.BRirbRAN.js} +1 -1
- package/dist/docs/assets/index.md.Kiht7ell.js +5 -0
- package/dist/docs/assets/{index.md.Bb4k8kUm.lean.js → index.md.Kiht7ell.lean.js} +1 -1
- package/dist/docs/assets/reference_extensions.md.9WQMZe1l.js +56 -0
- package/dist/docs/assets/reference_extensions.md.9WQMZe1l.lean.js +1 -0
- package/dist/docs/assets/{reference_project-layout.md.Bv4KOtlB.js → reference_project-layout.md.CA6QP6Lw.js} +2 -2
- package/dist/docs/assets/{reference_project-layout.md.Bv4KOtlB.lean.js → reference_project-layout.md.CA6QP6Lw.lean.js} +1 -1
- package/dist/docs/assets/{reference_skills.md.BFW9retM.js → reference_skills.md.l2ezAcaN.js} +1 -1
- package/dist/docs/assets/{reference_tools.md.CvAHsdSp.js → reference_tools.md.B1up2r9W.js} +1 -1
- package/dist/docs/assets/skills_framework-map.md.DDa4MeEW.js +1 -0
- package/dist/docs/assets/{skills_framework-map.md.BTi817yv.lean.js → skills_framework-map.md.DDa4MeEW.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +4 -4
- package/dist/docs/concepts.html +5 -5
- package/dist/docs/concepts.md +3 -3
- package/dist/docs/deployment.html +4 -4
- package/dist/docs/evals.html +4 -4
- package/dist/docs/guides/agent-to-agent.html +4 -4
- package/dist/docs/guides/cloud-agents.html +35 -0
- package/dist/docs/guides/cloud-agents.md +93 -0
- package/dist/docs/guides/cloud-runtime.html +6 -6
- package/dist/docs/guides/cloud-runtime.md +2 -0
- package/dist/docs/guides/convert-automation.html +4 -4
- package/dist/docs/guides/github.html +4 -4
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/mcp-oauth.html +4 -4
- package/dist/docs/guides/opentelemetry.html +5 -5
- package/dist/docs/guides/slack.html +4 -4
- package/dist/docs/guides/webhooks.html +4 -4
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +4 -4
- package/dist/docs/index.html +6 -6
- package/dist/docs/index.md +5 -1
- package/dist/docs/llms-full.txt +321 -4
- package/dist/docs/llms.txt +2 -0
- package/dist/docs/quickstart.html +4 -4
- package/dist/docs/reference/agent-config.html +4 -4
- package/dist/docs/reference/artifacts.html +4 -4
- package/dist/docs/reference/channels.html +5 -5
- package/dist/docs/reference/cli.html +4 -4
- package/dist/docs/reference/connections.html +4 -4
- package/dist/docs/reference/extensions.html +82 -0
- package/dist/docs/reference/extensions.md +200 -0
- package/dist/docs/reference/hooks.html +4 -4
- package/dist/docs/reference/http-api.html +4 -4
- package/dist/docs/reference/instructions.html +4 -4
- package/dist/docs/reference/playground.html +4 -4
- package/dist/docs/reference/project-layout.html +6 -6
- package/dist/docs/reference/project-layout.md +3 -0
- package/dist/docs/reference/prompt.html +4 -4
- package/dist/docs/reference/schedules.html +4 -4
- package/dist/docs/reference/sessions.html +4 -4
- package/dist/docs/reference/skills.html +6 -6
- package/dist/docs/reference/skills.md +2 -0
- package/dist/docs/reference/subagents.html +5 -5
- package/dist/docs/reference/tools.html +6 -6
- package/dist/docs/reference/tools.md +2 -0
- package/dist/docs/scaffolding-agents.html +4 -4
- package/dist/docs/skills/ab.html +4 -4
- package/dist/docs/skills/create-agent.html +4 -4
- package/dist/docs/skills/debug.html +4 -4
- package/dist/docs/skills/evals.html +4 -4
- package/dist/docs/skills/framework-map.html +5 -5
- package/dist/docs/skills/framework-map.md +1 -0
- package/dist/docs/skills/github.html +4 -4
- package/dist/docs/skills/hillclimb.html +4 -4
- package/dist/docs/skills/index.html +4 -4
- package/dist/docs/skills/mcp-auth.html +4 -4
- package/dist/docs/skills/otel.html +4 -4
- package/dist/docs/skills/setup-slack.html +4 -4
- package/dist/docs/storage.html +4 -4
- package/dist/docs/templates/agentic-owners.html +4 -4
- package/dist/docs/templates/agents-md.html +4 -4
- package/dist/docs/templates/code-wiki.html +4 -4
- package/dist/docs/templates/demo.html +4 -4
- package/dist/docs/templates/pr-autofixer.html +4 -4
- package/dist/docs/templates/security-help.html +4 -4
- package/dist/docs/templates/security-reviewer.html +4 -4
- package/dist/docs/templates/triage.html +4 -4
- package/dist/docs/troubleshooting.html +4 -4
- package/dist/extensions/cursor-cloud-agents/extension.d.ts +9 -7
- package/dist/extensions/cursor-cloud-agents/extension.d.ts.map +1 -1
- package/dist/extensions/cursor-cloud-agents/extension.js +9 -7
- 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 +286 -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/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 +21 -7
- 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.map +1 -1
- package/dist/extensions/cursor-cloud-agents/tools/launch.js +14 -38
- package/dist/extensions/cursor-cloud-agents/tools/list.js +1 -1
- 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/{follow_up.js → reply.js} +12 -14
- package/dist/extensions.d.ts +38 -14
- package/dist/extensions.d.ts.map +1 -1
- package/dist/extensions.js +49 -12
- package/dist/files-backends/cursor-hosted.d.ts +0 -8
- package/dist/files-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/files-backends/cursor-hosted.js +1 -11
- package/dist/files.d.ts +5 -1
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +22 -18
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/internal/conversation-mirror.d.ts.map +1 -1
- package/dist/internal/conversation-mirror.js +13 -15
- package/dist/internal/cursor/backend-client.d.ts +8 -1
- package/dist/internal/cursor/backend-client.d.ts.map +1 -1
- package/dist/internal/cursor/backend-client.js +25 -9
- package/dist/internal/cursor/hosted-store-secrets.d.ts +11 -0
- package/dist/internal/cursor/hosted-store-secrets.d.ts.map +1 -1
- package/dist/internal/cursor/hosted-store-secrets.js +15 -0
- package/dist/internal/discovery/ab.d.ts +9 -0
- package/dist/internal/discovery/ab.d.ts.map +1 -0
- package/dist/internal/discovery/ab.js +101 -0
- package/dist/internal/discovery/agent-config.d.ts +35 -0
- package/dist/internal/discovery/agent-config.d.ts.map +1 -0
- package/dist/internal/discovery/agent-config.js +173 -0
- package/dist/internal/discovery/agent.d.ts +11 -0
- package/dist/internal/discovery/agent.d.ts.map +1 -0
- package/dist/internal/discovery/agent.js +306 -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 +17 -0
- package/dist/internal/discovery/connections.d.ts.map +1 -0
- package/dist/internal/discovery/connections.js +436 -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 +570 -0
- package/dist/internal/discovery/extensions.d.ts +19 -0
- package/dist/internal/discovery/extensions.d.ts.map +1 -0
- package/dist/internal/discovery/extensions.js +238 -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 +48 -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 +141 -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 +101 -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 +36 -0
- package/dist/internal/discovery/project.d.ts +53 -0
- package/dist/internal/discovery/project.d.ts.map +1 -0
- package/dist/internal/discovery/project.js +239 -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 +98 -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 +97 -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 +17 -0
- package/dist/internal/discovery/services.d.ts.map +1 -0
- package/dist/internal/discovery/services.js +104 -0
- package/dist/internal/discovery/skills.d.ts +16 -0
- package/dist/internal/discovery/skills.d.ts.map +1 -0
- package/dist/internal/discovery/skills.js +151 -0
- package/dist/internal/discovery/subagent.d.ts +13 -0
- package/dist/internal/discovery/subagent.d.ts.map +1 -0
- package/dist/internal/discovery/subagent.js +19 -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 +159 -0
- package/dist/internal/discovery.d.ts +8 -62
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +8 -2505
- package/dist/internal/framework-file-storage.js +6 -6
- package/dist/internal/framework-storage-selection.d.ts.map +1 -1
- package/dist/internal/framework-storage-selection.js +8 -7
- package/dist/internal/frontmatter.d.ts +11 -3
- package/dist/internal/frontmatter.d.ts.map +1 -1
- package/dist/internal/frontmatter.js +29 -3
- package/dist/internal/mcp-host.d.ts.map +1 -1
- package/dist/internal/mcp-host.js +37 -20
- package/dist/internal/otel/collector.d.ts +13 -0
- package/dist/internal/otel/collector.d.ts.map +1 -1
- package/dist/internal/otel/collector.js +84 -17
- package/dist/internal/otel/http.d.ts.map +1 -1
- package/dist/internal/otel/http.js +2 -10
- 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 +68 -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.map +1 -1
- package/dist/internal/otel/runtime.js +2 -0
- package/dist/internal/session-engine.d.ts +5 -0
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +68 -32
- package/dist/internal/session-run-log.d.ts +2 -1
- package/dist/internal/session-run-log.d.ts.map +1 -1
- package/dist/internal/session-run-log.js +13 -14
- package/dist/internal/start-log-store.d.ts +1 -1
- package/dist/internal/start-log-store.d.ts.map +1 -1
- package/dist/internal/start-log-store.js +12 -14
- package/dist/internal/storage-coordinator.d.ts +4 -1
- package/dist/internal/storage-coordinator.d.ts.map +1 -1
- package/dist/internal/storage-coordinator.js +15 -13
- package/dist/internal/tool-catalog.d.ts.map +1 -1
- package/dist/internal/tool-catalog.js +9 -6
- package/dist/internal/turn-cost.d.ts +14 -0
- package/dist/internal/turn-cost.d.ts.map +1 -1
- package/dist/internal/turn-cost.js +41 -9
- package/dist/internal/v2-hosting.d.ts +22 -0
- package/dist/internal/v2-hosting.d.ts.map +1 -0
- package/dist/internal/v2-hosting.js +23 -0
- package/dist/otel.d.ts +16 -0
- package/dist/otel.d.ts.map +1 -1
- package/dist/otel.js +26 -0
- package/dist/playground/assets/index-B6C9S9nG.css +1 -0
- package/dist/playground/index.html +2 -2
- package/dist/storage-protocol.d.ts +24 -23
- package/dist/storage-protocol.d.ts.map +1 -1
- package/dist/storage-protocol.js +68 -65
- package/dist/storage.d.ts +2 -2
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +1 -1
- package/dist/tools.d.ts +9 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +9 -0
- package/dist/types.d.ts +39 -3
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +5 -1
- package/docs/concepts.md +3 -3
- package/docs/guides/cloud-agents.md +98 -0
- package/docs/guides/cloud-runtime.md +2 -0
- package/docs/reference/extensions.md +205 -0
- package/docs/reference/project-layout.md +3 -0
- package/docs/reference/skills.md +2 -0
- package/docs/reference/tools.md +2 -0
- package/package.json +1 -1
- package/skills/framework-map/SKILL.md +1 -0
- package/src/channels/slack/api.ts +4 -2
- package/src/channels/slack/assistant.ts +1 -1
- package/src/channels/slack/dispatch.ts +27 -2
- package/src/channels/slack/inbound.ts +37 -0
- package/src/channels/slack/manifest.ts +2 -1
- package/src/extensions/cursor-cloud-agents/extension.ts +9 -7
- package/src/extensions/cursor-cloud-agents/lib/backend.ts +395 -0
- package/src/extensions/cursor-cloud-agents/lib/inputs.ts +5 -0
- package/src/extensions/cursor-cloud-agents/lib/transcript.ts +23 -0
- package/src/extensions/cursor-cloud-agents/skills/handoff.md +21 -7
- package/src/extensions/cursor-cloud-agents/tools/cancel.ts +19 -0
- package/src/extensions/cursor-cloud-agents/tools/dump.ts +51 -0
- package/src/extensions/cursor-cloud-agents/tools/get.ts +18 -0
- package/src/extensions/cursor-cloud-agents/tools/launch.ts +22 -42
- package/src/extensions/cursor-cloud-agents/tools/list.ts +1 -1
- package/src/extensions/cursor-cloud-agents/tools/reply.ts +34 -0
- package/src/extensions.ts +55 -12
- package/src/files-backends/cursor-hosted.ts +1 -17
- package/src/files.ts +43 -34
- package/src/index.ts +10 -1
- package/src/internal/conversation-mirror.ts +23 -18
- package/src/internal/cursor/backend-client.ts +32 -10
- package/src/internal/cursor/hosted-store-secrets.ts +27 -0
- package/src/internal/discovery/ab.ts +111 -0
- package/src/internal/discovery/agent-config.ts +222 -0
- package/src/internal/discovery/agent.ts +360 -0
- package/src/internal/discovery/channels.ts +103 -0
- package/src/internal/discovery/connections.ts +540 -0
- package/src/internal/discovery/context.ts +46 -0
- package/src/internal/discovery/cursor-plugin.ts +708 -0
- package/src/internal/discovery/extensions.ts +348 -0
- package/src/internal/discovery/fs.ts +108 -0
- package/src/internal/discovery/hooks.ts +45 -0
- package/src/internal/discovery/info.ts +179 -0
- package/src/internal/discovery/instructions.ts +113 -0
- package/src/internal/discovery/modules.ts +107 -0
- package/src/internal/discovery/names.ts +44 -0
- package/src/internal/discovery/project.ts +270 -0
- package/src/internal/discovery/runtime.ts +159 -0
- package/src/internal/discovery/schedules.ts +94 -0
- package/src/internal/discovery/seed-files.ts +37 -0
- package/src/internal/discovery/services.ts +124 -0
- package/src/internal/discovery/skills.ts +176 -0
- package/src/internal/discovery/subagent.ts +29 -0
- package/src/internal/discovery/tools.ts +182 -0
- package/src/internal/discovery.ts +20 -3081
- package/src/internal/framework-file-storage.ts +6 -6
- package/src/internal/framework-storage-selection.ts +9 -9
- package/src/internal/frontmatter.ts +36 -3
- package/src/internal/mcp-host.ts +84 -31
- package/src/internal/otel/collector.ts +125 -25
- package/src/internal/otel/http.ts +2 -12
- package/src/internal/otel/mcp.ts +69 -0
- package/src/internal/otel/propagation.ts +17 -0
- package/src/internal/otel/runtime.ts +2 -0
- package/src/internal/session-engine.ts +96 -11
- package/src/internal/session-run-log.ts +17 -16
- package/src/internal/start-log-store.ts +13 -16
- package/src/internal/storage-coordinator.ts +19 -16
- package/src/internal/tool-catalog.ts +9 -6
- package/src/internal/turn-cost.ts +47 -4
- package/src/internal/v2-hosting.ts +38 -0
- package/src/otel.ts +38 -0
- package/src/storage-protocol.ts +101 -91
- package/src/storage.ts +4 -0
- package/src/tools.ts +18 -0
- package/src/types.ts +43 -2
- package/dist/docs/assets/chunks/@localSearchIndexroot.DbFXINq2.js +0 -1
- package/dist/docs/assets/concepts.md.lwAgBIMI.js +0 -1
- package/dist/docs/assets/index.md.Bb4k8kUm.js +0 -5
- package/dist/docs/assets/skills_framework-map.md.BTi817yv.js +0 -1
- package/dist/extensions/cursor-cloud-agents/lib/cloud.d.ts +0 -30
- package/dist/extensions/cursor-cloud-agents/lib/cloud.d.ts.map +0 -1
- package/dist/extensions/cursor-cloud-agents/lib/cloud.js +0 -45
- package/dist/extensions/cursor-cloud-agents/tools/follow_up.d.ts +0 -4
- package/dist/extensions/cursor-cloud-agents/tools/follow_up.d.ts.map +0 -1
- package/dist/extensions/cursor-cloud-agents/tools/status.d.ts +0 -4
- package/dist/extensions/cursor-cloud-agents/tools/status.d.ts.map +0 -1
- package/dist/extensions/cursor-cloud-agents/tools/status.js +0 -41
- package/dist/playground/assets/index-DBQCwFp0.css +0 -1
- package/src/extensions/cursor-cloud-agents/lib/cloud.ts +0 -79
- package/src/extensions/cursor-cloud-agents/tools/follow_up.ts +0 -29
- package/src/extensions/cursor-cloud-agents/tools/status.ts +0 -39
- /package/dist/docs/assets/{concepts.md.lwAgBIMI.lean.js → concepts.md.rPzW2HZN.lean.js} +0 -0
- /package/dist/docs/assets/{guides_cloud-runtime.md.CkYbjnAX.lean.js → guides_cloud-runtime.md.BRirbRAN.lean.js} +0 -0
- /package/dist/docs/assets/{reference_skills.md.BFW9retM.lean.js → reference_skills.md.l2ezAcaN.lean.js} +0 -0
- /package/dist/docs/assets/{reference_tools.md.CvAHsdSp.lean.js → reference_tools.md.B1up2r9W.lean.js} +0 -0
- /package/dist/playground/assets/{index-BnNN1-EK.js → index-ZqNoT8Ie.js} +0 -0
|
@@ -4,2508 +4,11 @@
|
|
|
4
4
|
* manifest. Identity comes from the path — a tool at `agent/tools/foo.ts`
|
|
5
5
|
* is named `foo`, a subagent at `agent/subagents/researcher/` is named
|
|
6
6
|
* `researcher` — so no definition carries a `name` field.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
});
|
|
16
|
-
};
|
|
17
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
18
|
-
var t = {};
|
|
19
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
20
|
-
t[p] = s[p];
|
|
21
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
22
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
23
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
24
|
-
t[p[i]] = s[p[i]];
|
|
25
|
-
}
|
|
26
|
-
return t;
|
|
27
|
-
};
|
|
28
|
-
import { readdir, readFile, stat } from "node:fs/promises";
|
|
29
|
-
import { basename, extname, isAbsolute, join, relative, resolve, } from "node:path";
|
|
30
|
-
import { pathToFileURL } from "node:url";
|
|
31
|
-
import { resolveABMaxPlaygroundSessions } from "../ab.js";
|
|
32
|
-
import { resolveArtifactsMax } from "../artifacts.js";
|
|
33
|
-
import { httpChannel } from "../channels.js";
|
|
34
|
-
import { cursorAccountServersHasMixedWildcard, DEFAULT_MODEL, isInlineStdioConnection, isToolEffect, MAX_RUNNING_TURNS_LIMIT, normalizeConnectionEffects, normalizeConnectionTools, normalizeCursorAccountServers, toPublicCloudInfo, } from "../types.js";
|
|
35
|
-
import { registerAuthoredModuleLoaders } from "./authored-loaders.js";
|
|
36
|
-
import { normalizePositiveInt } from "./bounded-int.js";
|
|
37
|
-
import { getDefinitionKind } from "./brand.js";
|
|
38
|
-
import { artifactBuiltinTool, builtinTools, TAG_ARTIFACT_TOOL_NAME, } from "./builtin-tools/index.js";
|
|
39
|
-
import { applyAgentContinuationDefaults } from "./continuation-channel.js";
|
|
40
|
-
import { isValidCron } from "./cron.js";
|
|
41
|
-
import { parseFrontmatter } from "./frontmatter.js";
|
|
42
|
-
import { isReservedSecretName, MAX_EGRESS_DOMAINS, validateEgressDomain, validateSecretName, } from "./hosting.js";
|
|
43
|
-
import { toolInputJsonSchema, toolOutputJsonSchema } from "./json-schema.js";
|
|
44
|
-
import { MCP_ENDPOINT_PATH, mcpEndpointToolNames } from "./mcp-endpoint.js";
|
|
45
|
-
import { packageInlineStdioConnection } from "./package-inline-stdio.js";
|
|
46
|
-
import { defaultLocalHarnessCwd, isNestedInGitRepo, withIdentityPreamble, } from "./workspace.js";
|
|
47
|
-
const MODULE_EXTENSIONS = new Set([".ts", ".mts", ".js", ".mjs"]);
|
|
48
|
-
const NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
|
|
49
|
-
const SKILL_DESCRIPTION_FALLBACK = (name) => `Instructions for the ${name} skill.`;
|
|
50
|
-
function defaultImportModule(absolutePath) {
|
|
51
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
52
|
-
return (yield import(pathToFileURL(absolutePath).href));
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* True when `dir` looks like an agent project root: it has a nested `agent/`
|
|
57
|
-
* directory, or flat-layout markers (`agent.ts` / `instructions.md`) directly
|
|
58
|
-
* inside it.
|
|
59
|
-
*/
|
|
60
|
-
export function isAgentProjectDir(dir) {
|
|
61
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
62
|
-
const resolved = resolve(dir);
|
|
63
|
-
if (!(yield isDirectory(resolved))) {
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
if (yield isDirectory(join(resolved, "agent"))) {
|
|
67
|
-
return true;
|
|
68
|
-
}
|
|
69
|
-
return hasFlatLayoutMarkers(resolved);
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* List agent projects directly under `rootDir` (one level deep) without
|
|
74
|
-
* loading them. Empty when the path is missing or has no child agents.
|
|
75
|
-
* Slug rules match {@link loadAgentProjects} / multi-agent serve.
|
|
76
|
-
*/
|
|
77
|
-
export function listChildAgentDirs(rootDir) {
|
|
78
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
79
|
-
const resolved = resolve(rootDir);
|
|
80
|
-
if (!(yield isDirectory(resolved))) {
|
|
81
|
-
return [];
|
|
82
|
-
}
|
|
83
|
-
const results = [];
|
|
84
|
-
const seenSlugs = new Set();
|
|
85
|
-
for (const entry of yield sortedEntries(resolved)) {
|
|
86
|
-
if (!entry.isDirectory() || entry.name.startsWith(".")) {
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
if (entry.name === "node_modules") {
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
const childDir = join(resolved, entry.name);
|
|
93
|
-
if (!(yield isAgentProjectDir(childDir))) {
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
const slug = assertSlug(entry.name, resolved);
|
|
97
|
-
if (seenSlugs.has(slug)) {
|
|
98
|
-
throw new Error(`Duplicate agent slug "${slug}" under ${resolved}.`);
|
|
99
|
-
}
|
|
100
|
-
seenSlugs.add(slug);
|
|
101
|
-
results.push({ slug, dir: childDir });
|
|
102
|
-
}
|
|
103
|
-
return results;
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Discover the agent projects directly under `rootDir` (one level deep):
|
|
108
|
-
* each immediate child directory that is itself an agent project becomes a
|
|
109
|
-
* mounted agent, keyed by a slug derived from its directory name. Used for
|
|
110
|
-
* multi-agent serving, where one server hosts many agents under
|
|
111
|
-
* `/<slug>/...`. Throws when no child projects are found.
|
|
112
|
-
*/
|
|
113
|
-
export function loadAgentProjects(rootDir_1) {
|
|
114
|
-
return __awaiter(this, arguments, void 0, function* (rootDir, options = {}) {
|
|
115
|
-
const resolved = resolve(rootDir);
|
|
116
|
-
if (!(yield isDirectory(resolved))) {
|
|
117
|
-
throw new Error(`Agent projects directory does not exist: ${resolved}`);
|
|
118
|
-
}
|
|
119
|
-
const children = yield listChildAgentDirs(resolved);
|
|
120
|
-
if (children.length === 0) {
|
|
121
|
-
throw new Error(`No agent projects found directly under ${resolved}. Each agent should be a subdirectory with its own agent/ directory (or flat-layout markers).`);
|
|
122
|
-
}
|
|
123
|
-
const results = [];
|
|
124
|
-
// Storage namespaces (session mirror, host kv) key on the project
|
|
125
|
-
// NAME, not the mount slug — two co-hosted agents sharing a name would
|
|
126
|
-
// silently share sessions and KV state.
|
|
127
|
-
const slugByName = new Map();
|
|
128
|
-
for (const child of children) {
|
|
129
|
-
const project = yield loadAgentProject(child.dir, options);
|
|
130
|
-
const priorSlug = slugByName.get(project.name);
|
|
131
|
-
if (priorSlug !== undefined) {
|
|
132
|
-
throw new Error(`Duplicate agent name "${project.name}" under ${resolved}: agents "${priorSlug}" and "${child.slug}" both resolve to it (package.json "name", or the directory name when unset). Co-hosted agents must have distinct names — their storage and session namespaces key on the name.`);
|
|
133
|
-
}
|
|
134
|
-
slugByName.set(project.name, child.slug);
|
|
135
|
-
results.push({ slug: child.slug, project });
|
|
136
|
-
}
|
|
137
|
-
return results;
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Resolve mounts for `serve` / CLI. Defaults to multi-agent layout:
|
|
142
|
-
* - `mode: "single"` — one agent at `/v1/*` (empty slug).
|
|
143
|
-
* - otherwise, if `rootDir` is itself an agent project — one agent under
|
|
144
|
-
* `/<dirname>/...`.
|
|
145
|
-
* - otherwise, every immediate child agent project under `/<slug>/...`.
|
|
146
|
-
*/
|
|
147
|
-
export function resolveAgentMounts(rootDir_1) {
|
|
148
|
-
return __awaiter(this, arguments, void 0, function* (rootDir, options = {}) {
|
|
149
|
-
const { mode = "multi" } = options, discovery = __rest(options, ["mode"]);
|
|
150
|
-
if (mode === "single") {
|
|
151
|
-
return [{ slug: "", project: yield loadAgentProject(rootDir, discovery) }];
|
|
152
|
-
}
|
|
153
|
-
if (yield isAgentProjectDir(rootDir)) {
|
|
154
|
-
const resolved = resolve(rootDir);
|
|
155
|
-
const slug = assertSlug(basename(resolved), resolved);
|
|
156
|
-
return [
|
|
157
|
-
{
|
|
158
|
-
slug,
|
|
159
|
-
project: yield loadAgentProject(rootDir, discovery),
|
|
160
|
-
},
|
|
161
|
-
];
|
|
162
|
-
}
|
|
163
|
-
return loadAgentProjects(rootDir, discovery);
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
function assertSlug(name, parentDir) {
|
|
167
|
-
if (!NAME_PATTERN.test(name) || RESERVED_SLUGS.has(name)) {
|
|
168
|
-
throw new Error(`Cannot mount agent directory "${name}" under ${parentDir}: its name must match ${NAME_PATTERN} and not be one of ${[...RESERVED_SLUGS].join(", ")}.`);
|
|
169
|
-
}
|
|
170
|
-
return name;
|
|
171
|
-
}
|
|
172
|
-
/** URL path segments reserved by the server; agents cannot use them as slugs. */
|
|
173
|
-
export const RESERVED_SLUGS = new Set([
|
|
174
|
-
"v1",
|
|
175
|
-
"playground",
|
|
176
|
-
"docs",
|
|
177
|
-
]);
|
|
178
|
-
export function loadAgentProject(dir_1) {
|
|
179
|
-
return __awaiter(this, arguments, void 0, function* (dir, options = {}) {
|
|
180
|
-
var _a, _b;
|
|
181
|
-
// Public API: embedders can call discovery directly (without `serve()` or
|
|
182
|
-
// the CLI), and it dynamically imports authored TypeScript.
|
|
183
|
-
yield registerAuthoredModuleLoaders();
|
|
184
|
-
const rootDir = resolve(dir);
|
|
185
|
-
if (!(yield isDirectory(rootDir))) {
|
|
186
|
-
throw new Error(`Agent project directory does not exist: ${rootDir}`);
|
|
187
|
-
}
|
|
188
|
-
const nestedAgentDir = join(rootDir, "agent");
|
|
189
|
-
let agentDir;
|
|
190
|
-
if (yield isDirectory(nestedAgentDir)) {
|
|
191
|
-
agentDir = nestedAgentDir;
|
|
192
|
-
}
|
|
193
|
-
else if (yield hasFlatLayoutMarkers(rootDir)) {
|
|
194
|
-
agentDir = rootDir;
|
|
195
|
-
}
|
|
196
|
-
else {
|
|
197
|
-
throw new Error(`No agent found under ${rootDir}: expected an agent/ directory (or a flat layout with agent.ts / instructions.md at the root)`);
|
|
198
|
-
}
|
|
199
|
-
const diagnostics = [];
|
|
200
|
-
const ctx = {
|
|
201
|
-
rootDir,
|
|
202
|
-
diagnostics,
|
|
203
|
-
importModule: (_a = options.importModule) !== null && _a !== void 0 ? _a : defaultImportModule,
|
|
204
|
-
};
|
|
205
|
-
const name = (_b = (yield readPackageName(rootDir))) !== null && _b !== void 0 ? _b : basename(rootDir);
|
|
206
|
-
const agent = yield loadAgent(ctx, agentDir, {
|
|
207
|
-
name,
|
|
208
|
-
isRoot: true,
|
|
209
|
-
});
|
|
210
|
-
const { channels, httpChannelDefinition } = yield loadChannels(ctx, join(agentDir, "channels"));
|
|
211
|
-
const schedules = yield loadSchedules(ctx, join(agentDir, "schedules"));
|
|
212
|
-
const hooks = yield loadHooks(ctx, join(agentDir, "hooks"));
|
|
213
|
-
const abs = yield loadABs(ctx, agentDir);
|
|
214
|
-
const abConfig = yield loadABConfig(ctx, agentDir);
|
|
215
|
-
const storage = yield loadStorage(ctx, agentDir, agent.architecture);
|
|
216
|
-
const artifacts = yield loadArtifacts(ctx, agentDir);
|
|
217
|
-
const otel = yield loadOtel(ctx, agentDir);
|
|
218
|
-
yield appendArtifactTool(ctx, agentDir, agent, artifacts);
|
|
219
|
-
if (agentDir !== rootDir) {
|
|
220
|
-
yield warnUnknownSlots(ctx, agentDir);
|
|
221
|
-
}
|
|
222
|
-
const resolvedHttpChannel = httpChannelDefinition !== null && httpChannelDefinition !== void 0 ? httpChannelDefinition : httpChannel();
|
|
223
|
-
applyAgentContinuationDefaults({
|
|
224
|
-
agent,
|
|
225
|
-
channels,
|
|
226
|
-
httpChannel: resolvedHttpChannel,
|
|
227
|
-
});
|
|
228
|
-
return {
|
|
229
|
-
rootDir,
|
|
230
|
-
agentDir,
|
|
231
|
-
name: agent.name,
|
|
232
|
-
agent,
|
|
233
|
-
httpChannel: resolvedHttpChannel,
|
|
234
|
-
channels,
|
|
235
|
-
schedules,
|
|
236
|
-
hooks,
|
|
237
|
-
abs,
|
|
238
|
-
abConfig,
|
|
239
|
-
storage,
|
|
240
|
-
artifacts,
|
|
241
|
-
otel,
|
|
242
|
-
diagnostics,
|
|
243
|
-
};
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
/** True when the project has diagnostics that must block serving. */
|
|
247
|
-
export function hasErrorDiagnostics(project) {
|
|
248
|
-
return project.diagnostics.some((d) => d.severity === "error");
|
|
249
|
-
}
|
|
250
|
-
export function formatDiagnostics(diagnostics) {
|
|
251
|
-
return diagnostics
|
|
252
|
-
.map((d) => {
|
|
253
|
-
const location = d.path === undefined ? "" : ` (${d.path})`;
|
|
254
|
-
return `${d.severity}: ${d.message}${location}`;
|
|
255
|
-
})
|
|
256
|
-
.join("\n");
|
|
257
|
-
}
|
|
258
|
-
// ============================================================================
|
|
259
|
-
// Agents (root + subagents)
|
|
260
|
-
// ============================================================================
|
|
261
|
-
function loadAgent(ctx, agentDir, options) {
|
|
262
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
263
|
-
var _a, _b, _c, _d;
|
|
264
|
-
var _e;
|
|
265
|
-
const config = yield loadAgentConfig(ctx, agentDir, options.isRoot);
|
|
266
|
-
const name = (_e = config === null || config === void 0 ? void 0 : config.name) !== null && _e !== void 0 ? _e : options.name;
|
|
267
|
-
const authoredInstructions = yield loadInstructions(ctx, agentDir, config, options.isRoot);
|
|
268
|
-
// Root agents without authored instructions stay undefined (diagnostic
|
|
269
|
-
// already recorded). Subagents still get the identity line alone.
|
|
270
|
-
const instructions = authoredInstructions === undefined && options.isRoot
|
|
271
|
-
? undefined
|
|
272
|
-
: withIdentityPreamble(name, authoredInstructions);
|
|
273
|
-
// Subagents run as Cursor SDK custom subagents (prompt + description +
|
|
274
|
-
// model); their other slots are inert today, so don't load them — just
|
|
275
|
-
// warn that they exist.
|
|
276
|
-
if (!options.isRoot) {
|
|
277
|
-
yield warnUnsupportedSubagentSlots(ctx, agentDir);
|
|
278
|
-
if ((config === null || config === void 0 ? void 0 : config.builtinTools) !== undefined) {
|
|
279
|
-
ctx.diagnostics.push({
|
|
280
|
-
severity: "warning",
|
|
281
|
-
path: displayPath(ctx, join(agentDir, "agent.ts")),
|
|
282
|
-
message: "builtinTools is not supported on subagents; ignoring.",
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
if ((config === null || config === void 0 ? void 0 : config.tools) !== undefined) {
|
|
286
|
-
ctx.diagnostics.push({
|
|
287
|
-
severity: "warning",
|
|
288
|
-
path: displayPath(ctx, join(agentDir, "agent.ts")),
|
|
289
|
-
message: "tools is not supported on subagents (they keep the harness task-tool toolset); ignoring.",
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
if ((config === null || config === void 0 ? void 0 : config.architecture) !== undefined) {
|
|
293
|
-
ctx.diagnostics.push({
|
|
294
|
-
severity: "warning",
|
|
295
|
-
path: displayPath(ctx, join(agentDir, "agent.ts")),
|
|
296
|
-
message: "architecture is not supported on subagents; ignoring.",
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
const artifactsPath = yield findModuleFile(agentDir, "artifacts");
|
|
300
|
-
if (artifactsPath !== undefined) {
|
|
301
|
-
ctx.diagnostics.push({
|
|
302
|
-
severity: "warning",
|
|
303
|
-
path: displayPath(ctx, artifactsPath),
|
|
304
|
-
message: "artifacts are not supported on subagents; ignoring.",
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
return Object.assign(Object.assign(Object.assign(Object.assign({ name }, ((config === null || config === void 0 ? void 0 : config.description) === undefined
|
|
308
|
-
? {}
|
|
309
|
-
: { description: config.description })), ((config === null || config === void 0 ? void 0 : config.model) === undefined ? {} : { model: config.model })), (instructions === undefined ? {} : { instructions })), { runtime: "local", tools: [], skills: [], connections: [], hostConnections: [], subagents: [], seedFiles: [] });
|
|
310
|
-
}
|
|
311
|
-
const tools = yield loadTools(ctx, join(agentDir, "tools"));
|
|
312
|
-
const skills = yield loadSkills(ctx, join(agentDir, "skills"));
|
|
313
|
-
const extensions = yield appendExtensions(ctx, join(agentDir, "extensions"), tools, skills);
|
|
314
|
-
const connections = yield loadConnections(ctx, yield resolveMcpConnectionsDir(ctx, agentDir));
|
|
315
|
-
const hostConnections = yield loadHostConnections(ctx, agentDir, connections);
|
|
316
|
-
const seedFiles = yield loadSeedFiles(ctx, agentDir);
|
|
317
|
-
const subagents = yield loadSubagents(ctx, join(agentDir, "subagents"));
|
|
318
|
-
const runtime = resolveRuntime(ctx, agentDir, config === null || config === void 0 ? void 0 : config.runtime);
|
|
319
|
-
// Keep authored `cloud` even when default runtime is local — per-session
|
|
320
|
-
// `send({ cloud })` merges it under cloudOverride for handoff sessions.
|
|
321
|
-
const cloud = config === null || config === void 0 ? void 0 : config.cloud;
|
|
322
|
-
const localWorkspaceDir = resolveAgentCwd(ctx, (_a = config === null || config === void 0 ? void 0 : config.local) === null || _a === void 0 ? void 0 : _a.workspaceDir);
|
|
323
|
-
const localCwd = yield resolveLocalCwd(ctx, (_b = config === null || config === void 0 ? void 0 : config.local) === null || _b === void 0 ? void 0 : _b.cwd, (_c = config === null || config === void 0 ? void 0 : config.local) === null || _c === void 0 ? void 0 : _c.workspaceDir);
|
|
324
|
-
const localSandbox = ((_d = config === null || config === void 0 ? void 0 : config.local) === null || _d === void 0 ? void 0 : _d.sandbox) === true;
|
|
325
|
-
// Built-in tool allowlist (top-level `tools`). `[]` is meaningful (no
|
|
326
|
-
// native built-in tools), so presence is keyed on the field, not on
|
|
327
|
-
// emptiness. "mcp" is always granted alongside a configured allowlist:
|
|
328
|
-
// it is transport, not capability — this kit's own server tools ride the
|
|
329
|
-
// SDK custom-tool MCP path, so omitting it silently breaks
|
|
330
|
-
// `agent/tools/`, and what MCP actually exposes stays controlled by the
|
|
331
|
-
// second layer (authored tools, mcp-connections, the harness cwd's
|
|
332
|
-
// ambient config).
|
|
333
|
-
const authoredAllowlist = config === null || config === void 0 ? void 0 : config.tools;
|
|
334
|
-
const allowedTools = authoredAllowlist === undefined || authoredAllowlist.includes("mcp")
|
|
335
|
-
? authoredAllowlist
|
|
336
|
-
: [...authoredAllowlist, "mcp"];
|
|
337
|
-
// The allowlist states runtime-agnostic intent, but only the local
|
|
338
|
-
// harness can enforce it (the Cursor SDK rejects tool restrictions on
|
|
339
|
-
// cloud agents, and the Grok Bot harness has its own fixed toolset) — a
|
|
340
|
-
// non-local agent that declares one must fail at serve start, never run
|
|
341
|
-
// unrestricted. (Hybrid per-send cloud sessions fail closed in the
|
|
342
|
-
// session engine.)
|
|
343
|
-
if (allowedTools !== undefined && runtime !== "local") {
|
|
344
|
-
ctx.diagnostics.push({
|
|
345
|
-
severity: "error",
|
|
346
|
-
path: displayPath(ctx, join(agentDir, "agent.ts")),
|
|
347
|
-
message: `tools cannot be enforced on runtime: "${runtime}". Remove \`tools\` or run the agent on the local runtime.`,
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
// Per-session connection features (defineConnection auth/advertiseTools)
|
|
351
|
-
// only run on local turns: advertised tools are in-process server tools,
|
|
352
|
-
// and per-session headers cannot reach a cloud VM's MCP config. A
|
|
353
|
-
// non-local agent that declares them must fail at serve start — never
|
|
354
|
-
// run without the connection's tools or identity. (Hybrid per-send
|
|
355
|
-
// cloud sessions fail closed in the session engine.)
|
|
356
|
-
const perSessionConnection = connections.find((connection) => connection.advertiseTools === true || connection.auth !== undefined);
|
|
357
|
-
if (perSessionConnection !== undefined && runtime !== "local") {
|
|
358
|
-
ctx.diagnostics.push({
|
|
359
|
-
severity: "error",
|
|
360
|
-
path: displayPath(ctx, join(agentDir, "agent.ts")),
|
|
361
|
-
message: `MCP connection "${perSessionConnection.name}" uses advertiseTools/auth, which only run on runtime: "local". Remove them or run the agent on the local runtime.`,
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
// `execution: "agent"` tool scripts run through the shell tool. Shell is
|
|
365
|
-
// a real capability, so it is never injected — but an allowlist that
|
|
366
|
-
// strands authored scripts deserves a loud diagnostic, not silence.
|
|
367
|
-
if (allowedTools !== undefined &&
|
|
368
|
-
!allowedTools.includes("shell") &&
|
|
369
|
-
tools.some((tool) => tool.execution === "agent")) {
|
|
370
|
-
ctx.diagnostics.push({
|
|
371
|
-
severity: "warning",
|
|
372
|
-
path: displayPath(ctx, join(agentDir, "agent.ts")),
|
|
373
|
-
message: '`tools` omits "shell" but this agent has execution: "agent" tools, which run as shell scripts; the model will not be able to run them.',
|
|
374
|
-
});
|
|
375
|
-
}
|
|
376
|
-
if (localCwd !== undefined && localWorkspaceDir !== undefined) {
|
|
377
|
-
ctx.diagnostics.push({
|
|
378
|
-
severity: "warning",
|
|
379
|
-
path: displayPath(ctx, join(agentDir, "agent.ts")),
|
|
380
|
-
message: "local.workspaceDir pins every session to one directory, so local.cwd is unused. Drop one of them.",
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
const local = localCwd === undefined && localWorkspaceDir === undefined && !localSandbox
|
|
384
|
-
? undefined
|
|
385
|
-
: {
|
|
386
|
-
cwd: localCwd,
|
|
387
|
-
workspaceDir: localWorkspaceDir,
|
|
388
|
-
sandbox: localSandbox,
|
|
389
|
-
};
|
|
390
|
-
const hosting = resolveHosting(ctx, agentDir, config === null || config === void 0 ? void 0 : config.hosting);
|
|
391
|
-
const concurrency = resolveConcurrency(ctx, agentDir, config === null || config === void 0 ? void 0 : config.concurrency);
|
|
392
|
-
const architecture = resolveArchitecture(ctx, agentDir, config === null || config === void 0 ? void 0 : config.architecture);
|
|
393
|
-
const runtimeArchitectureError = runtimeArchitectureCompatibilityError({
|
|
394
|
-
runtime,
|
|
395
|
-
architecture,
|
|
396
|
-
});
|
|
397
|
-
if (runtimeArchitectureError !== undefined) {
|
|
398
|
-
ctx.diagnostics.push({
|
|
399
|
-
severity: "error",
|
|
400
|
-
path: displayPath(ctx, join(agentDir, "agent.ts")),
|
|
401
|
-
message: runtimeArchitectureError,
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
if (runtime === "cloud") {
|
|
405
|
-
warnCloudRuntimeLimitations(ctx, agentDir, {
|
|
406
|
-
agentTools: tools.filter((tool) => tool.execution === "agent").length,
|
|
407
|
-
skills: skills.length,
|
|
408
|
-
seedFiles: seedFiles.length,
|
|
409
|
-
hasCloudOptions: cloud !== undefined,
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
|
-
appendBuiltinTools(ctx, agentDir, tools, config === null || config === void 0 ? void 0 : config.builtinTools);
|
|
413
|
-
// After appendBuiltinTools so opted-in built-ins (server-executed) count.
|
|
414
|
-
if (runtime === "grokbot") {
|
|
415
|
-
checkGrokBotRuntimeLimitations(ctx, agentDir, {
|
|
416
|
-
serverTools: tools.filter((tool) => tool.execution === "server").length,
|
|
417
|
-
agentTools: tools.filter((tool) => tool.execution === "agent").length,
|
|
418
|
-
skills: skills.length,
|
|
419
|
-
seedFiles: seedFiles.length,
|
|
420
|
-
connections: connections.length + hostConnections.length,
|
|
421
|
-
subagents: subagents.length,
|
|
422
|
-
hasCloudOptions: cloud !== undefined,
|
|
423
|
-
hasLocalSandbox: localSandbox,
|
|
424
|
-
});
|
|
425
|
-
}
|
|
426
|
-
return Object.assign(Object.assign(Object.assign(Object.assign({ name, description: config === null || config === void 0 ? void 0 : config.description,
|
|
427
|
-
// Root agents always get a concrete model: authored value or DEFAULT_MODEL.
|
|
428
|
-
model: isValidModelSetting(config === null || config === void 0 ? void 0 : config.model) ? config.model : DEFAULT_MODEL, instructions,
|
|
429
|
-
runtime,
|
|
430
|
-
cloud,
|
|
431
|
-
local }, (allowedTools === undefined ? {} : { allowedTools })), { hosting,
|
|
432
|
-
concurrency }), (architecture === undefined ? {} : { architecture })), { tools,
|
|
433
|
-
skills,
|
|
434
|
-
extensions,
|
|
435
|
-
connections,
|
|
436
|
-
hostConnections,
|
|
437
|
-
subagents,
|
|
438
|
-
seedFiles });
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
export function runtimeArchitectureCompatibilityError(agent) {
|
|
442
|
-
return agent.architecture === "v2" && agent.runtime === "grokbot"
|
|
443
|
-
? 'architecture: "v2" cannot use runtime: "grokbot": the grokbot runtime executes through the signed-in account\'s hosted /v0/grokbot box, not a deployment-scoped v2 runtime.'
|
|
444
|
-
: undefined;
|
|
445
|
-
}
|
|
446
|
-
function resolveArchitecture(ctx, agentDir, architecture) {
|
|
447
|
-
if (architecture === undefined ||
|
|
448
|
-
architecture === "v1" ||
|
|
449
|
-
architecture === "v2") {
|
|
450
|
-
return architecture;
|
|
451
|
-
}
|
|
452
|
-
ctx.diagnostics.push({
|
|
453
|
-
severity: "error",
|
|
454
|
-
path: displayPath(ctx, join(agentDir, "agent.ts")),
|
|
455
|
-
message: `architecture must be "v1" or "v2"; got ${JSON.stringify(architecture)}.`,
|
|
456
|
-
});
|
|
457
|
-
return undefined;
|
|
458
|
-
}
|
|
459
|
-
/** Absolute harness cwd parent, or undefined when unset / blank. */
|
|
460
|
-
function resolveAgentCwd(ctx, authored) {
|
|
461
|
-
if (authored === undefined) {
|
|
462
|
-
return undefined;
|
|
463
|
-
}
|
|
464
|
-
const trimmed = authored.trim();
|
|
465
|
-
if (trimmed === "") {
|
|
466
|
-
return undefined;
|
|
467
|
-
}
|
|
468
|
-
return isAbsolute(trimmed) ? trimmed : resolve(ctx.rootDir, trimmed);
|
|
469
|
-
}
|
|
470
|
-
/**
|
|
471
|
-
* Nested git checkouts inherit ancestor AGENTS.md / .cursor rules and MCP.
|
|
472
|
-
* Point those harnesses at ~/.cache/agent-serve/<dir> unless the agent
|
|
473
|
-
* already set cwd or workspaceDir.
|
|
474
|
-
*/
|
|
475
|
-
function resolveLocalCwd(ctx, authoredCwd, authoredWorkspaceDir) {
|
|
476
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
477
|
-
const cwd = resolveAgentCwd(ctx, authoredCwd);
|
|
478
|
-
if (cwd !== undefined) {
|
|
479
|
-
return cwd;
|
|
480
|
-
}
|
|
481
|
-
if (resolveAgentCwd(ctx, authoredWorkspaceDir) !== undefined) {
|
|
482
|
-
return undefined;
|
|
483
|
-
}
|
|
484
|
-
if (!(yield isNestedInGitRepo(ctx.rootDir))) {
|
|
485
|
-
return undefined;
|
|
486
|
-
}
|
|
487
|
-
return defaultLocalHarnessCwd(basename(ctx.rootDir));
|
|
488
|
-
});
|
|
489
|
-
}
|
|
490
|
-
/**
|
|
491
|
-
* Validate the authored `hosting` block against the same grammar the
|
|
492
|
-
* backend enforces on deploy. Violations are warning diagnostics (reported
|
|
493
|
-
* by `agent-sdk validate`), not hard failures: the backend stays
|
|
494
|
-
* authoritative and `agent-sdk deploy` re-checks before any network call.
|
|
495
|
-
*/
|
|
496
|
-
function resolveHosting(ctx, agentDir, hosting) {
|
|
497
|
-
var _a, _b;
|
|
498
|
-
if (hosting === undefined) {
|
|
499
|
-
return undefined;
|
|
500
|
-
}
|
|
501
|
-
const agentPath = displayPath(ctx, join(agentDir, "agent.ts"));
|
|
502
|
-
const warn = (message) => {
|
|
503
|
-
ctx.diagnostics.push({ severity: "warning", path: agentPath, message });
|
|
504
|
-
};
|
|
505
|
-
const egressDomains = (_a = hosting.egressDomains) !== null && _a !== void 0 ? _a : [];
|
|
506
|
-
for (const domain of egressDomains) {
|
|
507
|
-
const domainError = validateEgressDomain(domain);
|
|
508
|
-
if (domainError !== undefined) {
|
|
509
|
-
warn(`hosting.egressDomains: ${domainError}`);
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
if (egressDomains.length > MAX_EGRESS_DOMAINS) {
|
|
513
|
-
warn(`hosting.egressDomains lists ${egressDomains.length} domains; deploys are capped at ${MAX_EGRESS_DOMAINS}.`);
|
|
514
|
-
}
|
|
515
|
-
for (const name of (_b = hosting.secretNames) !== null && _b !== void 0 ? _b : []) {
|
|
516
|
-
const nameError = validateSecretName(name);
|
|
517
|
-
if (nameError !== undefined) {
|
|
518
|
-
warn(`hosting.secretNames: ${nameError}`);
|
|
519
|
-
}
|
|
520
|
-
else if (isReservedSecretName(name)) {
|
|
521
|
-
warn(`hosting.secretNames: ${JSON.stringify(name)} is platform-managed (CURSOR_* / reserved hosted-storage names); omit it from secretNames.`);
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
return Object.assign(Object.assign({}, (hosting.egressDomains === undefined
|
|
525
|
-
? {}
|
|
526
|
-
: { egressDomains: [...hosting.egressDomains] })), (hosting.secretNames === undefined
|
|
527
|
-
? {}
|
|
528
|
-
: { secretNames: [...hosting.secretNames] }));
|
|
529
|
-
}
|
|
530
|
-
/**
|
|
531
|
-
* Validate the authored `concurrency` block. Invalid values are **error**
|
|
532
|
-
* diagnostics (serve refuses to start): silently dropping a turn ceiling
|
|
533
|
-
* would fail open to unlimited parallelism, the opposite of what the
|
|
534
|
-
* author asked for.
|
|
535
|
-
*/
|
|
536
|
-
function resolveConcurrency(ctx, agentDir, concurrency) {
|
|
537
|
-
if (concurrency === undefined || concurrency.maxRunningTurns === undefined) {
|
|
538
|
-
return undefined;
|
|
539
|
-
}
|
|
540
|
-
try {
|
|
541
|
-
return {
|
|
542
|
-
maxRunningTurns: normalizePositiveInt(concurrency.maxRunningTurns, {
|
|
543
|
-
label: "concurrency.maxRunningTurns",
|
|
544
|
-
max: MAX_RUNNING_TURNS_LIMIT,
|
|
545
|
-
}),
|
|
546
|
-
};
|
|
547
|
-
}
|
|
548
|
-
catch (error) {
|
|
549
|
-
ctx.diagnostics.push({
|
|
550
|
-
severity: "error",
|
|
551
|
-
path: displayPath(ctx, join(agentDir, "agent.ts")),
|
|
552
|
-
message: error instanceof Error ? error.message : String(error),
|
|
553
|
-
});
|
|
554
|
-
return undefined;
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
function loadAgentConfig(ctx, agentDir, isRoot) {
|
|
558
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
559
|
-
const configPath = yield findModuleFile(agentDir, "agent");
|
|
560
|
-
if (configPath === undefined) {
|
|
561
|
-
ctx.diagnostics.push({
|
|
562
|
-
severity: "error",
|
|
563
|
-
path: displayPath(ctx, join(agentDir, "agent.ts")),
|
|
564
|
-
message: isRoot
|
|
565
|
-
? "agent.ts is required on the root agent: default-export defineAgent({ ... })"
|
|
566
|
-
: "agent.ts is required on a subagent: default-export defineAgent({ description: ... })",
|
|
567
|
-
});
|
|
568
|
-
return undefined;
|
|
569
|
-
}
|
|
570
|
-
const definition = yield importDefault(ctx, configPath, "agent");
|
|
571
|
-
if (definition === undefined) {
|
|
572
|
-
return undefined;
|
|
573
|
-
}
|
|
574
|
-
const config = definition;
|
|
575
|
-
if (!isRoot && isBlank(config.description)) {
|
|
576
|
-
ctx.diagnostics.push({
|
|
577
|
-
severity: "error",
|
|
578
|
-
path: displayPath(ctx, configPath),
|
|
579
|
-
message: "Subagents require a description; the parent model reads it to decide when to delegate.",
|
|
580
|
-
});
|
|
581
|
-
}
|
|
582
|
-
return config;
|
|
583
|
-
});
|
|
584
|
-
}
|
|
585
|
-
function loadInstructions(ctx, agentDir, config, isRoot) {
|
|
586
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
587
|
-
const parts = [];
|
|
588
|
-
const markdownPath = join(agentDir, "instructions.md");
|
|
589
|
-
if (yield isFile(markdownPath)) {
|
|
590
|
-
parts.push((yield readFile(markdownPath, "utf8")).trim());
|
|
591
|
-
}
|
|
592
|
-
const modulePath = yield findModuleFile(agentDir, "instructions");
|
|
593
|
-
if (modulePath !== undefined) {
|
|
594
|
-
const loaded = yield loadInstructionsModule(ctx, modulePath);
|
|
595
|
-
if (loaded !== undefined) {
|
|
596
|
-
parts.push(loaded);
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
const instructionsDir = join(agentDir, "instructions");
|
|
600
|
-
if (yield isDirectory(instructionsDir)) {
|
|
601
|
-
for (const entry of yield sortedEntries(instructionsDir)) {
|
|
602
|
-
if (!entry.isFile()) {
|
|
603
|
-
continue;
|
|
604
|
-
}
|
|
605
|
-
const entryPath = join(instructionsDir, entry.name);
|
|
606
|
-
if (entry.name.endsWith(".md")) {
|
|
607
|
-
parts.push((yield readFile(entryPath, "utf8")).trim());
|
|
608
|
-
}
|
|
609
|
-
else if (isModuleFile(entry.name)) {
|
|
610
|
-
const loaded = yield loadInstructionsModule(ctx, entryPath);
|
|
611
|
-
if (loaded !== undefined) {
|
|
612
|
-
parts.push(loaded);
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
if ((config === null || config === void 0 ? void 0 : config.instructions) !== undefined && !isBlank(config.instructions)) {
|
|
618
|
-
parts.push(config.instructions.trim());
|
|
619
|
-
}
|
|
620
|
-
const combined = parts.filter((part) => part !== "").join("\n\n");
|
|
621
|
-
if (combined === "") {
|
|
622
|
-
if (isRoot) {
|
|
623
|
-
ctx.diagnostics.push({
|
|
624
|
-
severity: "error",
|
|
625
|
-
path: displayPath(ctx, markdownPath),
|
|
626
|
-
message: "instructions.md is required on the root agent (the always-on system prompt).",
|
|
627
|
-
});
|
|
628
|
-
}
|
|
629
|
-
return undefined;
|
|
630
|
-
}
|
|
631
|
-
return combined;
|
|
632
|
-
});
|
|
633
|
-
}
|
|
634
|
-
function loadInstructionsModule(ctx, modulePath) {
|
|
635
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
636
|
-
let mod;
|
|
637
|
-
try {
|
|
638
|
-
mod = yield ctx.importModule(modulePath);
|
|
639
|
-
}
|
|
640
|
-
catch (error) {
|
|
641
|
-
ctx.diagnostics.push({
|
|
642
|
-
severity: "error",
|
|
643
|
-
path: displayPath(ctx, modulePath),
|
|
644
|
-
message: `Failed to load module: ${errorMessage(error)}`,
|
|
645
|
-
});
|
|
646
|
-
return undefined;
|
|
647
|
-
}
|
|
648
|
-
const value = mod.default;
|
|
649
|
-
if (typeof value === "string") {
|
|
650
|
-
return value.trim();
|
|
651
|
-
}
|
|
652
|
-
if (getDefinitionKind(value) === "instructions") {
|
|
653
|
-
return value.markdown.trim();
|
|
654
|
-
}
|
|
655
|
-
ctx.diagnostics.push({
|
|
656
|
-
severity: "error",
|
|
657
|
-
path: displayPath(ctx, modulePath),
|
|
658
|
-
message: "Instructions modules must default-export a string or defineInstructions({ markdown }).",
|
|
659
|
-
});
|
|
660
|
-
return undefined;
|
|
661
|
-
});
|
|
662
|
-
}
|
|
663
|
-
// ============================================================================
|
|
664
|
-
// Tools
|
|
665
|
-
// ============================================================================
|
|
666
|
-
function loadTools(ctx, toolsDir) {
|
|
667
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
668
|
-
const tools = [];
|
|
669
|
-
for (const { name, path } of yield toolModuleEntries(ctx, toolsDir)) {
|
|
670
|
-
const definition = yield importDefault(ctx, path, "tool");
|
|
671
|
-
if (definition === undefined) {
|
|
672
|
-
continue;
|
|
673
|
-
}
|
|
674
|
-
const tool = compileTool(ctx, path, name, definition);
|
|
675
|
-
if (tool !== undefined) {
|
|
676
|
-
tools.push(tool);
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
return tools;
|
|
680
|
-
});
|
|
681
|
-
}
|
|
682
|
-
/** The module files of a `tools/` directory whose filenames are valid tool names. */
|
|
683
|
-
function toolModuleEntries(ctx, toolsDir) {
|
|
684
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
685
|
-
if (!(yield isDirectory(toolsDir))) {
|
|
686
|
-
return [];
|
|
687
|
-
}
|
|
688
|
-
const entries = [];
|
|
689
|
-
for (const entry of yield sortedEntries(toolsDir)) {
|
|
690
|
-
const path = join(toolsDir, entry.name);
|
|
691
|
-
if (entry.isDirectory()) {
|
|
692
|
-
ctx.diagnostics.push({
|
|
693
|
-
severity: "warning",
|
|
694
|
-
path: displayPath(ctx, path),
|
|
695
|
-
message: "Nested tool directories are not supported; ignoring.",
|
|
696
|
-
});
|
|
697
|
-
continue;
|
|
698
|
-
}
|
|
699
|
-
if (!entry.isFile() || !isModuleFile(entry.name)) {
|
|
700
|
-
continue;
|
|
701
|
-
}
|
|
702
|
-
const name = moduleStem(entry.name);
|
|
703
|
-
if (validateName(ctx, "tool", name, path)) {
|
|
704
|
-
entries.push({ name, path });
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
return entries;
|
|
708
|
-
});
|
|
709
|
-
}
|
|
710
|
-
/**
|
|
711
|
-
* Validate one tool definition against the tool contract and convert its
|
|
712
|
-
* schemas. Diagnostics point at `entryPath` (the authored file, or the
|
|
713
|
-
* extension mount that contributed the tool).
|
|
714
|
-
*/
|
|
715
|
-
function compileTool(ctx, entryPath, name, tool) {
|
|
716
|
-
if (isBlank(tool.description)) {
|
|
717
|
-
ctx.diagnostics.push({
|
|
718
|
-
severity: "error",
|
|
719
|
-
path: displayPath(ctx, entryPath),
|
|
720
|
-
message: "Tools require a non-empty description.",
|
|
721
|
-
});
|
|
722
|
-
return undefined;
|
|
723
|
-
}
|
|
724
|
-
const execution = tool.execution === "agent" ? "agent" : "server";
|
|
725
|
-
const needsApprovalConfigured = tool.needsApproval === true || typeof tool.needsApproval === "function";
|
|
726
|
-
const effect = declaredEffect(tool);
|
|
727
|
-
if (execution === "agent") {
|
|
728
|
-
if (isBlank(tool.script)) {
|
|
729
|
-
ctx.diagnostics.push({
|
|
730
|
-
severity: "error",
|
|
731
|
-
path: displayPath(ctx, entryPath),
|
|
732
|
-
message: 'Tools with execution: "agent" require a non-empty `script` (shell body; JSON on stdin, result on stdout).',
|
|
733
|
-
});
|
|
734
|
-
return undefined;
|
|
735
|
-
}
|
|
736
|
-
if (typeof tool.execute === "function") {
|
|
737
|
-
ctx.diagnostics.push({
|
|
738
|
-
severity: "warning",
|
|
739
|
-
path: displayPath(ctx, entryPath),
|
|
740
|
-
message: '`execute` is ignored when execution is "agent"; the Cursor agent runs `script` instead.',
|
|
741
|
-
});
|
|
742
|
-
}
|
|
743
|
-
if (needsApprovalConfigured) {
|
|
744
|
-
ctx.diagnostics.push({
|
|
745
|
-
severity: "error",
|
|
746
|
-
path: displayPath(ctx, entryPath),
|
|
747
|
-
message: '`needsApproval` is only supported for execution: "server" tools (in-process).',
|
|
748
|
-
});
|
|
749
|
-
return undefined;
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
else if (typeof tool.execute !== "function") {
|
|
753
|
-
ctx.diagnostics.push({
|
|
754
|
-
severity: "error",
|
|
755
|
-
path: displayPath(ctx, entryPath),
|
|
756
|
-
message: 'Tools require an execute(input, ctx) function (or set execution: "agent" with a script).',
|
|
757
|
-
});
|
|
758
|
-
return undefined;
|
|
759
|
-
}
|
|
760
|
-
let inputSchema;
|
|
761
|
-
try {
|
|
762
|
-
inputSchema = toolInputJsonSchema(tool.inputSchema);
|
|
763
|
-
}
|
|
764
|
-
catch (error) {
|
|
765
|
-
ctx.diagnostics.push({
|
|
766
|
-
severity: "error",
|
|
767
|
-
path: displayPath(ctx, entryPath),
|
|
768
|
-
message: `Failed to convert inputSchema to JSON Schema: ${errorMessage(error)}`,
|
|
769
|
-
});
|
|
770
|
-
return undefined;
|
|
771
|
-
}
|
|
772
|
-
let outputSchema;
|
|
773
|
-
try {
|
|
774
|
-
outputSchema = toolOutputJsonSchema(tool.outputSchema);
|
|
775
|
-
}
|
|
776
|
-
catch (error) {
|
|
777
|
-
ctx.diagnostics.push({
|
|
778
|
-
severity: "error",
|
|
779
|
-
path: displayPath(ctx, entryPath),
|
|
780
|
-
message: `Failed to convert outputSchema to JSON Schema: ${errorMessage(error)}`,
|
|
781
|
-
});
|
|
782
|
-
return undefined;
|
|
783
|
-
}
|
|
784
|
-
return {
|
|
785
|
-
name,
|
|
786
|
-
description: tool.description,
|
|
787
|
-
inputSchema,
|
|
788
|
-
outputSchema,
|
|
789
|
-
execution,
|
|
790
|
-
needsApproval: needsApprovalConfigured,
|
|
791
|
-
effect,
|
|
792
|
-
script: execution === "agent" ? tool.script : undefined,
|
|
793
|
-
definition: tool,
|
|
794
|
-
};
|
|
795
|
-
}
|
|
796
|
-
/**
|
|
797
|
-
* Static view of a tool's `effect` for the manifest: the literal when it is
|
|
798
|
-
* one, `"dynamic"` for a per-call predicate, `undefined` when undeclared. A
|
|
799
|
-
* malformed value is left undeclared rather than guessed — undeclared is the
|
|
800
|
-
* safe state (a dry run stubs it), where a guess could be the unsafe one.
|
|
801
|
-
*/
|
|
802
|
-
function declaredEffect(tool) {
|
|
803
|
-
if (typeof tool.effect === "function") {
|
|
804
|
-
return "dynamic";
|
|
805
|
-
}
|
|
806
|
-
return tool.effect === "read" || tool.effect === "write"
|
|
807
|
-
? tool.effect
|
|
808
|
-
: undefined;
|
|
809
|
-
}
|
|
810
|
-
// ============================================================================
|
|
811
|
-
// Skills
|
|
812
|
-
// ============================================================================
|
|
813
|
-
function loadSkills(ctx, skillsDir) {
|
|
814
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
815
|
-
var _a, _b;
|
|
816
|
-
if (!(yield isDirectory(skillsDir))) {
|
|
817
|
-
return [];
|
|
818
|
-
}
|
|
819
|
-
const skills = [];
|
|
820
|
-
for (const entry of yield sortedEntries(skillsDir)) {
|
|
821
|
-
const entryPath = join(skillsDir, entry.name);
|
|
822
|
-
if (entry.isDirectory()) {
|
|
823
|
-
const skill = yield loadPackagedSkill(ctx, entryPath, entry.name);
|
|
824
|
-
if (skill !== undefined) {
|
|
825
|
-
skills.push(skill);
|
|
826
|
-
}
|
|
827
|
-
continue;
|
|
828
|
-
}
|
|
829
|
-
if (!entry.isFile()) {
|
|
830
|
-
continue;
|
|
831
|
-
}
|
|
832
|
-
if (entry.name.endsWith(".md")) {
|
|
833
|
-
const name = entry.name.slice(0, -".md".length);
|
|
834
|
-
if (!validateName(ctx, "skill", name, entryPath)) {
|
|
835
|
-
continue;
|
|
836
|
-
}
|
|
837
|
-
const { data, body } = parseFrontmatter(yield readFile(entryPath, "utf8"));
|
|
838
|
-
skills.push({
|
|
839
|
-
name,
|
|
840
|
-
description: (_b = (_a = data.description) !== null && _a !== void 0 ? _a : deriveSkillDescription(body)) !== null && _b !== void 0 ? _b : SKILL_DESCRIPTION_FALLBACK(name),
|
|
841
|
-
markdown: body.trim(),
|
|
842
|
-
files: {},
|
|
843
|
-
});
|
|
844
|
-
continue;
|
|
845
|
-
}
|
|
846
|
-
if (isModuleFile(entry.name)) {
|
|
847
|
-
const name = moduleStem(entry.name);
|
|
848
|
-
if (!validateName(ctx, "skill", name, entryPath)) {
|
|
849
|
-
continue;
|
|
850
|
-
}
|
|
851
|
-
const definition = yield importDefault(ctx, entryPath, "skill");
|
|
852
|
-
if (definition === undefined) {
|
|
853
|
-
continue;
|
|
854
|
-
}
|
|
855
|
-
skills.push(compileSkill(name, definition));
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
return skills;
|
|
859
|
-
});
|
|
860
|
-
}
|
|
861
|
-
/** A skill whose body and sibling files are in memory (`defineSkill`, extensions). */
|
|
862
|
-
function compileSkill(name, skill) {
|
|
863
|
-
var _a, _b, _c;
|
|
864
|
-
const files = {};
|
|
865
|
-
for (const [rel, contents] of Object.entries((_a = skill.files) !== null && _a !== void 0 ? _a : {})) {
|
|
866
|
-
files[rel] = { contents };
|
|
867
|
-
}
|
|
868
|
-
return {
|
|
869
|
-
name,
|
|
870
|
-
description: (_c = (_b = skill.description) !== null && _b !== void 0 ? _b : deriveSkillDescription(skill.markdown)) !== null && _c !== void 0 ? _c : SKILL_DESCRIPTION_FALLBACK(name),
|
|
871
|
-
markdown: skill.markdown.trim(),
|
|
872
|
-
files,
|
|
873
|
-
};
|
|
874
|
-
}
|
|
875
|
-
// ============================================================================
|
|
876
|
-
// Extensions (agent/extensions/<ns>.ts | agent/extensions/<ns>/extension.ts)
|
|
877
|
-
// ============================================================================
|
|
878
|
-
/**
|
|
879
|
-
* Mount every extension under `agent/extensions/` and append its tools and
|
|
880
|
-
* skills to the authored lists as `<ns>__<name>`, stamped with `extension`.
|
|
881
|
-
* The namespace is the mount's filename stem or directory name.
|
|
882
|
-
*/
|
|
883
|
-
function appendExtensions(ctx, extensionsDir, tools, skills) {
|
|
884
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
885
|
-
if (!(yield isDirectory(extensionsDir))) {
|
|
886
|
-
return [];
|
|
887
|
-
}
|
|
888
|
-
const extensions = [];
|
|
889
|
-
for (const entry of yield sortedEntries(extensionsDir)) {
|
|
890
|
-
const path = join(extensionsDir, entry.name);
|
|
891
|
-
const site = yield resolveMountSite(ctx, path, entry);
|
|
892
|
-
if (site === undefined) {
|
|
893
|
-
continue;
|
|
894
|
-
}
|
|
895
|
-
if (extensions.some((extension) => extension.name === site.ns)) {
|
|
896
|
-
ctx.diagnostics.push({
|
|
897
|
-
severity: "error",
|
|
898
|
-
path: displayPath(ctx, path),
|
|
899
|
-
message: `Extension "${site.ns}" is mounted twice (as a file and as a directory).`,
|
|
900
|
-
});
|
|
901
|
-
continue;
|
|
902
|
-
}
|
|
903
|
-
const mount = yield importDefault(ctx, site.mountPath, "extension");
|
|
904
|
-
if (mount === undefined) {
|
|
905
|
-
continue;
|
|
906
|
-
}
|
|
907
|
-
extensions.push(yield mountExtension(ctx, site, mount, tools, skills));
|
|
908
|
-
}
|
|
909
|
-
return extensions;
|
|
910
|
-
});
|
|
911
|
-
}
|
|
912
|
-
function resolveMountSite(ctx, path, entry) {
|
|
913
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
914
|
-
if (entry.isDirectory()) {
|
|
915
|
-
if (!validateName(ctx, "extension", entry.name, path)) {
|
|
916
|
-
return undefined;
|
|
917
|
-
}
|
|
918
|
-
const mountPath = yield findModuleFile(path, "extension");
|
|
919
|
-
if (mountPath === undefined) {
|
|
920
|
-
ctx.diagnostics.push({
|
|
921
|
-
severity: "error",
|
|
922
|
-
path: displayPath(ctx, path),
|
|
923
|
-
message: "A directory mount needs an extension.ts default-exporting the mount (`export default crm({ ... })`).",
|
|
924
|
-
});
|
|
925
|
-
return undefined;
|
|
926
|
-
}
|
|
927
|
-
return { ns: entry.name, mountPath, overlayDir: path };
|
|
928
|
-
}
|
|
929
|
-
if (!entry.isFile() || !isModuleFile(entry.name)) {
|
|
930
|
-
return undefined;
|
|
931
|
-
}
|
|
932
|
-
const ns = moduleStem(entry.name);
|
|
933
|
-
return validateName(ctx, "extension", ns, path)
|
|
934
|
-
? { ns, mountPath: path }
|
|
935
|
-
: undefined;
|
|
936
|
-
});
|
|
937
|
-
}
|
|
938
|
-
/**
|
|
939
|
-
* Read the extension's tree with the agent loaders, lay the mount's overlay
|
|
940
|
-
* on top (a same-named file replaces, `disableTool()` removes), then
|
|
941
|
-
* namespace what is left.
|
|
942
|
-
*/
|
|
943
|
-
function mountExtension(ctx, site, mount, tools, skills) {
|
|
944
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
945
|
-
const mounted = {
|
|
946
|
-
name: site.ns,
|
|
947
|
-
description: mount.extension.description,
|
|
948
|
-
tools: [],
|
|
949
|
-
skills: [],
|
|
950
|
-
};
|
|
951
|
-
const root = mount.extension.root;
|
|
952
|
-
if (!(yield isDirectory(root))) {
|
|
953
|
-
ctx.diagnostics.push({
|
|
954
|
-
severity: "error",
|
|
955
|
-
path: displayPath(ctx, site.mountPath),
|
|
956
|
-
message: `Extension "${site.ns}" declares root ${root}, which is not a directory. Pass \`import.meta.dirname\` from extension.ts.`,
|
|
957
|
-
});
|
|
958
|
-
return mounted;
|
|
959
|
-
}
|
|
960
|
-
yield warnUnsupportedExtensionSlots(ctx, root);
|
|
961
|
-
const contributedTools = new Map((yield loadTools(ctx, join(root, "tools"))).map((tool) => [tool.name, tool]));
|
|
962
|
-
const contributedSkills = new Map((yield loadSkills(ctx, join(root, "skills"))).map((skill) => [
|
|
963
|
-
skill.name,
|
|
964
|
-
skill,
|
|
965
|
-
]));
|
|
966
|
-
if (site.overlayDir !== undefined) {
|
|
967
|
-
const overlay = yield loadOverlayTools(ctx, join(site.overlayDir, "tools"));
|
|
968
|
-
for (const tool of overlay.tools) {
|
|
969
|
-
contributedTools.set(tool.name, tool);
|
|
970
|
-
}
|
|
971
|
-
for (const disabled of overlay.disabled) {
|
|
972
|
-
if (!contributedTools.delete(disabled.name)) {
|
|
973
|
-
ctx.diagnostics.push({
|
|
974
|
-
severity: "error",
|
|
975
|
-
path: displayPath(ctx, disabled.path),
|
|
976
|
-
message: `disableTool() targets "${disabled.name}", which extension "${site.ns}" does not contribute.`,
|
|
977
|
-
});
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
for (const skill of yield loadSkills(ctx, join(site.overlayDir, "skills"))) {
|
|
981
|
-
contributedSkills.set(skill.name, skill);
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
for (const [name, tool] of contributedTools) {
|
|
985
|
-
const composed = `${site.ns}__${name}`;
|
|
986
|
-
tools.push(Object.assign(Object.assign({}, tool), { name: composed, extension: site.ns }));
|
|
987
|
-
mounted.tools.push(composed);
|
|
988
|
-
}
|
|
989
|
-
for (const [name, skill] of contributedSkills) {
|
|
990
|
-
const composed = `${site.ns}__${name}`;
|
|
991
|
-
skills.push(Object.assign(Object.assign({}, skill), { name: composed, extension: site.ns }));
|
|
992
|
-
mounted.skills.push(composed);
|
|
993
|
-
}
|
|
994
|
-
return mounted;
|
|
995
|
-
});
|
|
996
|
-
}
|
|
997
|
-
/** A directory mount's `tools/`: replacements plus `disableTool()` markers. */
|
|
998
|
-
function loadOverlayTools(ctx, toolsDir) {
|
|
999
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1000
|
-
const tools = [];
|
|
1001
|
-
const disabled = [];
|
|
1002
|
-
for (const { name, path } of yield toolModuleEntries(ctx, toolsDir)) {
|
|
1003
|
-
let mod;
|
|
1004
|
-
try {
|
|
1005
|
-
mod = yield ctx.importModule(path);
|
|
1006
|
-
}
|
|
1007
|
-
catch (error) {
|
|
1008
|
-
ctx.diagnostics.push({
|
|
1009
|
-
severity: "error",
|
|
1010
|
-
path: displayPath(ctx, path),
|
|
1011
|
-
message: `Failed to load module: ${errorMessage(error)}`,
|
|
1012
|
-
});
|
|
1013
|
-
continue;
|
|
1014
|
-
}
|
|
1015
|
-
const kind = getDefinitionKind(mod.default);
|
|
1016
|
-
if (kind === "disabled-tool") {
|
|
1017
|
-
disabled.push({ name, path });
|
|
1018
|
-
continue;
|
|
1019
|
-
}
|
|
1020
|
-
if (kind !== "tool") {
|
|
1021
|
-
ctx.diagnostics.push({
|
|
1022
|
-
severity: "error",
|
|
1023
|
-
path: displayPath(ctx, path),
|
|
1024
|
-
message: "An override must default-export defineTool({ ... }) or disableTool().",
|
|
1025
|
-
});
|
|
1026
|
-
continue;
|
|
1027
|
-
}
|
|
1028
|
-
const tool = compileTool(ctx, path, name, mod.default);
|
|
1029
|
-
if (tool !== undefined) {
|
|
1030
|
-
tools.push(tool);
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
return { tools, disabled };
|
|
1034
|
-
});
|
|
1035
|
-
}
|
|
1036
|
-
/** Slots an agent has that an extension tree may not contribute (yet). */
|
|
1037
|
-
function warnUnsupportedExtensionSlots(ctx, root) {
|
|
1038
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1039
|
-
const unsupported = [
|
|
1040
|
-
["instructions.md", "instructions"],
|
|
1041
|
-
["connections", "connections"],
|
|
1042
|
-
["mcp-connections", "MCP connections"],
|
|
1043
|
-
["hooks", "hooks"],
|
|
1044
|
-
["subagents", "subagents"],
|
|
1045
|
-
["channels", "channels"],
|
|
1046
|
-
["schedules", "schedules"],
|
|
1047
|
-
["extensions", "nested extensions"],
|
|
1048
|
-
];
|
|
1049
|
-
for (const [slot, label] of unsupported) {
|
|
1050
|
-
const path = join(root, slot);
|
|
1051
|
-
if ((yield isDirectory(path)) || (yield isFile(path))) {
|
|
1052
|
-
ctx.diagnostics.push({
|
|
1053
|
-
severity: "warning",
|
|
1054
|
-
path: displayPath(ctx, path),
|
|
1055
|
-
message: `Extension ${label} are not supported yet; only tools/ and skills/ are mounted. Ignoring.`,
|
|
1056
|
-
});
|
|
1057
|
-
}
|
|
1058
|
-
}
|
|
1059
|
-
});
|
|
1060
|
-
}
|
|
1061
|
-
function loadPackagedSkill(ctx, skillDir, name) {
|
|
1062
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1063
|
-
const skillMdPath = join(skillDir, "SKILL.md");
|
|
1064
|
-
if (!(yield isFile(skillMdPath))) {
|
|
1065
|
-
ctx.diagnostics.push({
|
|
1066
|
-
severity: "warning",
|
|
1067
|
-
path: displayPath(ctx, skillDir),
|
|
1068
|
-
message: "Skill directories must contain a SKILL.md; ignoring.",
|
|
1069
|
-
});
|
|
1070
|
-
return undefined;
|
|
1071
|
-
}
|
|
1072
|
-
if (!validateName(ctx, "skill", name, skillDir)) {
|
|
1073
|
-
return undefined;
|
|
1074
|
-
}
|
|
1075
|
-
const { data, body } = parseFrontmatter(yield readFile(skillMdPath, "utf8"));
|
|
1076
|
-
if (data.description === undefined || isBlank(data.description)) {
|
|
1077
|
-
ctx.diagnostics.push({
|
|
1078
|
-
severity: "error",
|
|
1079
|
-
path: displayPath(ctx, skillMdPath),
|
|
1080
|
-
message: "Packaged skills require `description` frontmatter in SKILL.md (there is no filename slug to fall back on).",
|
|
1081
|
-
});
|
|
1082
|
-
return undefined;
|
|
1083
|
-
}
|
|
1084
|
-
const files = {};
|
|
1085
|
-
for (const filePath of yield walkFiles(skillDir)) {
|
|
1086
|
-
const rel = toPosix(relative(skillDir, filePath));
|
|
1087
|
-
if (rel === "SKILL.md") {
|
|
1088
|
-
continue;
|
|
1089
|
-
}
|
|
1090
|
-
files[rel] = { sourcePath: filePath };
|
|
1091
|
-
}
|
|
1092
|
-
return {
|
|
1093
|
-
name,
|
|
1094
|
-
description: data.description,
|
|
1095
|
-
markdown: body.trim(),
|
|
1096
|
-
files,
|
|
1097
|
-
};
|
|
1098
|
-
});
|
|
1099
|
-
}
|
|
1100
|
-
/** One-line `/v1/info` label for a connection transport. */
|
|
1101
|
-
function connectionTransportLabel(transport) {
|
|
1102
|
-
if ("cursorAccount" in transport) {
|
|
1103
|
-
return transport.servers === undefined
|
|
1104
|
-
? "cursor-account"
|
|
1105
|
-
: `cursor-account:${transport.servers.join(",")}`;
|
|
1106
|
-
}
|
|
1107
|
-
if ("agent" in transport) {
|
|
1108
|
-
return `agent:${transport.agent}`;
|
|
1109
|
-
}
|
|
1110
|
-
if ("url" in transport) {
|
|
1111
|
-
return transport.oauth === true
|
|
1112
|
-
? `${transport.url} (oauth)`
|
|
1113
|
-
: transport.url;
|
|
1114
|
-
}
|
|
1115
|
-
return transport.command;
|
|
1116
|
-
}
|
|
1117
|
-
function hasAuthorizationHeader(headers) {
|
|
1118
|
-
if (headers === undefined) {
|
|
1119
|
-
return false;
|
|
1120
|
-
}
|
|
1121
|
-
return Object.keys(headers).some((key) => key.toLowerCase() === "authorization");
|
|
1122
|
-
}
|
|
1123
|
-
function parseConnectionEffects(effects) {
|
|
1124
|
-
if (effects === undefined) {
|
|
1125
|
-
return { ok: true, value: undefined };
|
|
1126
|
-
}
|
|
1127
|
-
const raw = typeof effects === "string" ? [effects] : effects;
|
|
1128
|
-
if (!Array.isArray(raw) || raw.length === 0) {
|
|
1129
|
-
return { ok: false };
|
|
1130
|
-
}
|
|
1131
|
-
const parsed = [];
|
|
1132
|
-
for (const value of raw) {
|
|
1133
|
-
if (!isToolEffect(value)) {
|
|
1134
|
-
return { ok: false };
|
|
1135
|
-
}
|
|
1136
|
-
parsed.push(value);
|
|
1137
|
-
}
|
|
1138
|
-
return { ok: true, value: normalizeConnectionEffects(parsed) };
|
|
1139
|
-
}
|
|
1140
|
-
function parseConnectionTools(tools) {
|
|
1141
|
-
if (tools === undefined) {
|
|
1142
|
-
return { ok: true, value: undefined };
|
|
1143
|
-
}
|
|
1144
|
-
if (!Array.isArray(tools) || tools.length === 0) {
|
|
1145
|
-
return { ok: false };
|
|
1146
|
-
}
|
|
1147
|
-
if (tools.some((name) => typeof name !== "string")) {
|
|
1148
|
-
return { ok: false };
|
|
1149
|
-
}
|
|
1150
|
-
const normalized = normalizeConnectionTools(tools);
|
|
1151
|
-
if (normalized.length === 0) {
|
|
1152
|
-
return { ok: false };
|
|
1153
|
-
}
|
|
1154
|
-
return { ok: true, value: normalized };
|
|
1155
|
-
}
|
|
1156
|
-
function connectionHasSpawnOrRemote(connection) {
|
|
1157
|
-
if ("cursorAccount" in connection && connection.cursorAccount === true) {
|
|
1158
|
-
return true;
|
|
1159
|
-
}
|
|
1160
|
-
if ("agent" in connection && !isBlank(connection.agent)) {
|
|
1161
|
-
return true;
|
|
1162
|
-
}
|
|
1163
|
-
if ("url" in connection && !isBlank(connection.url)) {
|
|
1164
|
-
return true;
|
|
1165
|
-
}
|
|
1166
|
-
return "command" in connection && !isBlank(connection.command);
|
|
1167
|
-
}
|
|
1168
|
-
function connectionTransport(connection) {
|
|
1169
|
-
if ("cursorAccount" in connection && connection.cursorAccount === true) {
|
|
1170
|
-
const servers = normalizeCursorAccountServers(connection.servers);
|
|
1171
|
-
return Object.assign({ cursorAccount: true }, (servers === undefined ? {} : { servers }));
|
|
1172
|
-
}
|
|
1173
|
-
if ("agent" in connection && !isBlank(connection.agent)) {
|
|
1174
|
-
return { agent: connection.agent };
|
|
1175
|
-
}
|
|
1176
|
-
if ("url" in connection && !isBlank(connection.url)) {
|
|
1177
|
-
return {
|
|
1178
|
-
type: connection.type,
|
|
1179
|
-
url: connection.url,
|
|
1180
|
-
headers: connection.headers,
|
|
1181
|
-
oauth: connection.oauth === true ? true : undefined,
|
|
1182
|
-
};
|
|
1183
|
-
}
|
|
1184
|
-
if ("command" in connection && !isBlank(connection.command)) {
|
|
1185
|
-
return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (connection.type === undefined ? {} : { type: connection.type })), { command: connection.command }), (connection.args === undefined ? {} : { args: connection.args })), (connection.env === undefined ? {} : { env: connection.env })), (connection.cwd === undefined ? {} : { cwd: connection.cwd }));
|
|
1186
|
-
}
|
|
1187
|
-
return undefined;
|
|
1188
|
-
}
|
|
1189
|
-
/**
|
|
1190
|
-
* Filenames become the names the model sees. `__` is reserved: it joins an
|
|
1191
|
-
* extension's namespace to its contribution (`crm__search`).
|
|
1192
|
-
*/
|
|
1193
|
-
function validateName(ctx, kind, name, entryPath) {
|
|
1194
|
-
const label = { skill: "Skill", tool: "Tool", extension: "Extension" }[kind];
|
|
1195
|
-
if (!NAME_PATTERN.test(name)) {
|
|
1196
|
-
ctx.diagnostics.push({
|
|
1197
|
-
severity: "error",
|
|
1198
|
-
path: displayPath(ctx, entryPath),
|
|
1199
|
-
message: `${label} name "${name}" must match ${NAME_PATTERN}.`,
|
|
1200
|
-
});
|
|
1201
|
-
return false;
|
|
1202
|
-
}
|
|
1203
|
-
if (name.includes("__")) {
|
|
1204
|
-
ctx.diagnostics.push({
|
|
1205
|
-
severity: "error",
|
|
1206
|
-
path: displayPath(ctx, entryPath),
|
|
1207
|
-
message: `${label} name "${name}" may not contain "__"; it is reserved for extension contributions (\`<ns>__<name>\`).`,
|
|
1208
|
-
});
|
|
1209
|
-
return false;
|
|
1210
|
-
}
|
|
1211
|
-
return true;
|
|
1212
|
-
}
|
|
1213
|
-
/**
|
|
1214
|
-
* Advertise the first non-empty, non-code-fence line of the body, with any
|
|
1215
|
-
* leading `#`, `>`, `*`, or `-` markers stripped.
|
|
1216
|
-
*/
|
|
1217
|
-
export function deriveSkillDescription(markdown) {
|
|
1218
|
-
let inFence = false;
|
|
1219
|
-
for (const rawLine of markdown.split(/\r?\n/)) {
|
|
1220
|
-
const line = rawLine.trim();
|
|
1221
|
-
if (line.startsWith("```")) {
|
|
1222
|
-
inFence = !inFence;
|
|
1223
|
-
continue;
|
|
1224
|
-
}
|
|
1225
|
-
if (inFence || line === "") {
|
|
1226
|
-
continue;
|
|
1227
|
-
}
|
|
1228
|
-
const stripped = line.replace(/^[#>*\s-]+/, "").trim();
|
|
1229
|
-
if (stripped !== "") {
|
|
1230
|
-
return stripped.length > 200 ? `${stripped.slice(0, 197)}...` : stripped;
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
return undefined;
|
|
1234
|
-
}
|
|
1235
|
-
// ============================================================================
|
|
1236
|
-
// MCP connections
|
|
1237
|
-
// ============================================================================
|
|
1238
|
-
/**
|
|
1239
|
-
* Prefer `agent/mcp-connections/`. Keep loading `agent/connections/` with a
|
|
1240
|
-
* deprecation warning so existing agents keep working during the rename.
|
|
1241
|
-
*/
|
|
1242
|
-
function resolveMcpConnectionsDir(ctx, agentDir) {
|
|
1243
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1244
|
-
const preferred = join(agentDir, "mcp-connections");
|
|
1245
|
-
const legacy = join(agentDir, "connections");
|
|
1246
|
-
const hasPreferred = yield isDirectory(preferred);
|
|
1247
|
-
const hasLegacy = yield isDirectory(legacy);
|
|
1248
|
-
if (hasPreferred && hasLegacy) {
|
|
1249
|
-
ctx.diagnostics.push({
|
|
1250
|
-
severity: "warning",
|
|
1251
|
-
path: displayPath(ctx, legacy),
|
|
1252
|
-
message: "Both agent/mcp-connections/ and agent/connections/ exist; using mcp-connections/ and ignoring connections/. Rename or remove the legacy directory.",
|
|
1253
|
-
});
|
|
1254
|
-
return preferred;
|
|
1255
|
-
}
|
|
1256
|
-
if (hasLegacy) {
|
|
1257
|
-
ctx.diagnostics.push({
|
|
1258
|
-
severity: "warning",
|
|
1259
|
-
path: displayPath(ctx, legacy),
|
|
1260
|
-
message: "agent/connections/ is deprecated; rename it to agent/mcp-connections/. Loading from the legacy path for now.",
|
|
1261
|
-
});
|
|
1262
|
-
return legacy;
|
|
1263
|
-
}
|
|
1264
|
-
return preferred;
|
|
1265
|
-
});
|
|
1266
|
-
}
|
|
1267
|
-
function loadHostConnections(ctx, agentDir, modelConnections) {
|
|
1268
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1269
|
-
const hostDir = join(agentDir, "host-connections");
|
|
1270
|
-
const loaded = yield loadConnections(ctx, hostDir, "host");
|
|
1271
|
-
const modelNames = new Set(modelConnections.map((connection) => connection.name));
|
|
1272
|
-
const hostConnections = [];
|
|
1273
|
-
for (const connection of loaded) {
|
|
1274
|
-
if (modelNames.has(connection.name)) {
|
|
1275
|
-
ctx.diagnostics.push({
|
|
1276
|
-
severity: "error",
|
|
1277
|
-
path: displayPath(ctx, join(hostDir, `${connection.name}.ts`)),
|
|
1278
|
-
message: `Host connection "${connection.name}" collides with agent/mcp-connections/${connection.name}. Rename one of them.`,
|
|
1279
|
-
});
|
|
1280
|
-
continue;
|
|
1281
|
-
}
|
|
1282
|
-
if (connection.advertiseTools === true) {
|
|
1283
|
-
ctx.diagnostics.push({
|
|
1284
|
-
severity: "error",
|
|
1285
|
-
path: displayPath(ctx, join(hostDir, `${connection.name}.ts`)),
|
|
1286
|
-
message: "Host connections are off the model. `advertiseTools` cannot apply. Drop it.",
|
|
1287
|
-
});
|
|
1288
|
-
continue;
|
|
1289
|
-
}
|
|
1290
|
-
if (connection.auth !== undefined) {
|
|
1291
|
-
ctx.diagnostics.push({
|
|
1292
|
-
severity: "error",
|
|
1293
|
-
path: displayPath(ctx, join(hostDir, `${connection.name}.ts`)),
|
|
1294
|
-
message: "Host connections are off the model. `auth` cannot apply. Use static headers or `oauth: true`.",
|
|
1295
|
-
});
|
|
1296
|
-
continue;
|
|
1297
|
-
}
|
|
1298
|
-
hostConnections.push(connection);
|
|
1299
|
-
}
|
|
1300
|
-
return hostConnections;
|
|
1301
|
-
});
|
|
1302
|
-
}
|
|
1303
|
-
function loadConnections(ctx_1, connectionsDir_1) {
|
|
1304
|
-
return __awaiter(this, arguments, void 0, function* (ctx, connectionsDir, role = "model") {
|
|
1305
|
-
if (!(yield isDirectory(connectionsDir))) {
|
|
1306
|
-
return [];
|
|
1307
|
-
}
|
|
1308
|
-
const connections = [];
|
|
1309
|
-
for (const entry of yield sortedEntries(connectionsDir)) {
|
|
1310
|
-
const entryPath = join(connectionsDir, entry.name);
|
|
1311
|
-
if (!entry.isFile() || !isModuleFile(entry.name)) {
|
|
1312
|
-
continue;
|
|
1313
|
-
}
|
|
1314
|
-
const name = moduleStem(entry.name);
|
|
1315
|
-
if (!NAME_PATTERN.test(name)) {
|
|
1316
|
-
ctx.diagnostics.push({
|
|
1317
|
-
severity: "error",
|
|
1318
|
-
path: displayPath(ctx, entryPath),
|
|
1319
|
-
message: `MCP connection name "${name}" must match ${NAME_PATTERN}.`,
|
|
1320
|
-
});
|
|
1321
|
-
continue;
|
|
1322
|
-
}
|
|
1323
|
-
const definition = yield importDefault(ctx, entryPath, "connection");
|
|
1324
|
-
if (definition === undefined) {
|
|
1325
|
-
continue;
|
|
1326
|
-
}
|
|
1327
|
-
const connection = definition;
|
|
1328
|
-
if (Object.hasOwn(connection, "hostOnly")) {
|
|
1329
|
-
ctx.diagnostics.push({
|
|
1330
|
-
severity: "error",
|
|
1331
|
-
path: displayPath(ctx, entryPath),
|
|
1332
|
-
message: "MCP connection `hostOnly` was removed. Files under `agent/mcp-connections/` are always visible to the model. Put privileged servers in `agent/host-connections/` so only host tools can call them.",
|
|
1333
|
-
});
|
|
1334
|
-
continue;
|
|
1335
|
-
}
|
|
1336
|
-
if (isInlineStdioConnection(connection) &&
|
|
1337
|
-
connectionHasSpawnOrRemote(connection)) {
|
|
1338
|
-
ctx.diagnostics.push({
|
|
1339
|
-
severity: "error",
|
|
1340
|
-
path: displayPath(ctx, entryPath),
|
|
1341
|
-
message: "MCP connection `tools` cannot be combined with `command`, `url`, `agent`, or `cursorAccount`. Author TypeScript tools, or point at an existing server, not both.",
|
|
1342
|
-
});
|
|
1343
|
-
continue;
|
|
1344
|
-
}
|
|
1345
|
-
if (connection.auth !== undefined &&
|
|
1346
|
-
typeof connection.auth !== "function") {
|
|
1347
|
-
ctx.diagnostics.push({
|
|
1348
|
-
severity: "error",
|
|
1349
|
-
path: displayPath(ctx, entryPath),
|
|
1350
|
-
message: "MCP connection `auth` must be a function: (session) => { headers }.",
|
|
1351
|
-
});
|
|
1352
|
-
continue;
|
|
1353
|
-
}
|
|
1354
|
-
if (connection.auth !== undefined && !("url" in connection)) {
|
|
1355
|
-
ctx.diagnostics.push({
|
|
1356
|
-
severity: "error",
|
|
1357
|
-
path: displayPath(ctx, entryPath),
|
|
1358
|
-
message: "MCP connection `auth` (per-session headers) is only supported on http/sse connections (`url`).",
|
|
1359
|
-
});
|
|
1360
|
-
continue;
|
|
1361
|
-
}
|
|
1362
|
-
if (connection.advertiseTools !== undefined &&
|
|
1363
|
-
typeof connection.advertiseTools !== "boolean") {
|
|
1364
|
-
ctx.diagnostics.push({
|
|
1365
|
-
severity: "error",
|
|
1366
|
-
path: displayPath(ctx, entryPath),
|
|
1367
|
-
message: "MCP connection `advertiseTools` must be a boolean.",
|
|
1368
|
-
});
|
|
1369
|
-
continue;
|
|
1370
|
-
}
|
|
1371
|
-
const parsedEffects = parseConnectionEffects(connection.effects);
|
|
1372
|
-
if (!parsedEffects.ok) {
|
|
1373
|
-
ctx.diagnostics.push({
|
|
1374
|
-
severity: "error",
|
|
1375
|
-
path: displayPath(ctx, entryPath),
|
|
1376
|
-
message: 'MCP connection `effects` must be "read", "write", or a non-empty array of those.',
|
|
1377
|
-
});
|
|
1378
|
-
continue;
|
|
1379
|
-
}
|
|
1380
|
-
if (parsedEffects.value !== undefined &&
|
|
1381
|
-
connection.advertiseTools !== true) {
|
|
1382
|
-
ctx.diagnostics.push({
|
|
1383
|
-
severity: "error",
|
|
1384
|
-
path: displayPath(ctx, entryPath),
|
|
1385
|
-
message: "MCP connection `effects` filters advertised tools; set `advertiseTools: true` or drop `effects`.",
|
|
1386
|
-
});
|
|
1387
|
-
continue;
|
|
1388
|
-
}
|
|
1389
|
-
const parsedTools = isInlineStdioConnection(connection)
|
|
1390
|
-
? { ok: true, value: undefined }
|
|
1391
|
-
: parseConnectionTools(connection.tools);
|
|
1392
|
-
if (!parsedTools.ok) {
|
|
1393
|
-
ctx.diagnostics.push({
|
|
1394
|
-
severity: "error",
|
|
1395
|
-
path: displayPath(ctx, entryPath),
|
|
1396
|
-
message: "MCP connection `tools` allowlist must be a non-empty array of tool names.",
|
|
1397
|
-
});
|
|
1398
|
-
continue;
|
|
1399
|
-
}
|
|
1400
|
-
if (isInlineStdioConnection(connection) &&
|
|
1401
|
-
connection.disallowedTools !== undefined) {
|
|
1402
|
-
ctx.diagnostics.push({
|
|
1403
|
-
severity: "error",
|
|
1404
|
-
path: displayPath(ctx, entryPath),
|
|
1405
|
-
message: "MCP connection `disallowedTools` is not supported on inline `tools` connections — author only the tools you want.",
|
|
1406
|
-
});
|
|
1407
|
-
continue;
|
|
1408
|
-
}
|
|
1409
|
-
const parsedDisallowed = isInlineStdioConnection(connection)
|
|
1410
|
-
? { ok: true, value: undefined }
|
|
1411
|
-
: parseConnectionTools(connection.disallowedTools);
|
|
1412
|
-
if (!parsedDisallowed.ok) {
|
|
1413
|
-
ctx.diagnostics.push({
|
|
1414
|
-
severity: "error",
|
|
1415
|
-
path: displayPath(ctx, entryPath),
|
|
1416
|
-
message: "MCP connection `disallowedTools` must be a non-empty array of tool names.",
|
|
1417
|
-
});
|
|
1418
|
-
continue;
|
|
1419
|
-
}
|
|
1420
|
-
if ((parsedTools.value !== undefined ||
|
|
1421
|
-
parsedDisallowed.value !== undefined) &&
|
|
1422
|
-
role === "model" &&
|
|
1423
|
-
connection.advertiseTools !== true) {
|
|
1424
|
-
ctx.diagnostics.push({
|
|
1425
|
-
severity: "error",
|
|
1426
|
-
path: displayPath(ctx, entryPath),
|
|
1427
|
-
message: "MCP connection `tools` / `disallowedTools` filter advertised tools; set `advertiseTools: true` or drop the name list. Put the connection in `agent/host-connections/` to restrict `host.mcp` only.",
|
|
1428
|
-
});
|
|
1429
|
-
continue;
|
|
1430
|
-
}
|
|
1431
|
-
if (connection.advertiseTools === true &&
|
|
1432
|
-
isInlineStdioConnection(connection)) {
|
|
1433
|
-
ctx.diagnostics.push({
|
|
1434
|
-
severity: "error",
|
|
1435
|
-
path: displayPath(ctx, entryPath),
|
|
1436
|
-
message: "MCP connection `advertiseTools` is not supported on inline `tools` connections — author those as agent/tools/ server tools instead.",
|
|
1437
|
-
});
|
|
1438
|
-
continue;
|
|
1439
|
-
}
|
|
1440
|
-
if (connection.auth !== undefined &&
|
|
1441
|
-
"url" in connection &&
|
|
1442
|
-
connection.oauth === true) {
|
|
1443
|
-
ctx.diagnostics.push({
|
|
1444
|
-
severity: "error",
|
|
1445
|
-
path: displayPath(ctx, entryPath),
|
|
1446
|
-
message: "MCP connection `oauth: true` cannot be combined with `auth` (per-session headers): the host OAuth provider owns the Authorization header. Drop one of them.",
|
|
1447
|
-
});
|
|
1448
|
-
continue;
|
|
1449
|
-
}
|
|
1450
|
-
if (isInlineStdioConnection(connection)) {
|
|
1451
|
-
const packed = yield loadInlineStdioConnection(ctx, name, entryPath, connection);
|
|
1452
|
-
if (packed === undefined) {
|
|
1453
|
-
continue;
|
|
1454
|
-
}
|
|
1455
|
-
connections.push(packed);
|
|
1456
|
-
continue;
|
|
1457
|
-
}
|
|
1458
|
-
if ("url" in connection &&
|
|
1459
|
-
connection.oauth === true &&
|
|
1460
|
-
hasAuthorizationHeader(connection.headers)) {
|
|
1461
|
-
ctx.diagnostics.push({
|
|
1462
|
-
severity: "error",
|
|
1463
|
-
path: displayPath(ctx, entryPath),
|
|
1464
|
-
message: "MCP connection `oauth: true` cannot set an Authorization header. Drop the header or remove `oauth`.",
|
|
1465
|
-
});
|
|
1466
|
-
continue;
|
|
1467
|
-
}
|
|
1468
|
-
if ("cursorAccount" in connection &&
|
|
1469
|
-
connection.cursorAccount === true &&
|
|
1470
|
-
cursorAccountServersHasMixedWildcard(connection.servers)) {
|
|
1471
|
-
ctx.diagnostics.push({
|
|
1472
|
-
severity: "error",
|
|
1473
|
-
path: displayPath(ctx, entryPath),
|
|
1474
|
-
message: 'Cursor account MCP connection `servers` cannot mix `"*"` with connector names. Use `"*"` / `["*"]` (or omit `servers`) for every connected server, or list only concrete connector names.',
|
|
1475
|
-
});
|
|
1476
|
-
continue;
|
|
1477
|
-
}
|
|
1478
|
-
const transport = connectionTransport(connection);
|
|
1479
|
-
if (transport === undefined) {
|
|
1480
|
-
ctx.diagnostics.push({
|
|
1481
|
-
severity: "error",
|
|
1482
|
-
path: displayPath(ctx, entryPath),
|
|
1483
|
-
message: "MCP connections require a `url` (http/sse), a `command` (stdio), `tools` (TypeScript packaged as stdio), an `agent` (peer agent slug on the same serve host), or `cursorAccount: true` (the Cursor account's MCP connectors).",
|
|
1484
|
-
});
|
|
1485
|
-
continue;
|
|
1486
|
-
}
|
|
1487
|
-
if ("cursorAccount" in transport &&
|
|
1488
|
-
transport.servers !== undefined &&
|
|
1489
|
-
(transport.servers.length === 0 ||
|
|
1490
|
-
transport.servers.some((server) => isBlank(server)))) {
|
|
1491
|
-
ctx.diagnostics.push({
|
|
1492
|
-
severity: "error",
|
|
1493
|
-
path: displayPath(ctx, entryPath),
|
|
1494
|
-
message: 'Cursor account MCP connection `servers` must be a non-empty list of connector names, `"*"` / `["*"]` for every connected server, or omitted for the same all-connectors behavior.',
|
|
1495
|
-
});
|
|
1496
|
-
continue;
|
|
1497
|
-
}
|
|
1498
|
-
if ("agent" in transport && !NAME_PATTERN.test(transport.agent)) {
|
|
1499
|
-
ctx.diagnostics.push({
|
|
1500
|
-
severity: "error",
|
|
1501
|
-
path: displayPath(ctx, entryPath),
|
|
1502
|
-
message: `Peer MCP connection agent "${transport.agent}" must be a mount slug matching ${NAME_PATTERN}.`,
|
|
1503
|
-
});
|
|
1504
|
-
continue;
|
|
1505
|
-
}
|
|
1506
|
-
connections.push({
|
|
1507
|
-
name,
|
|
1508
|
-
transport,
|
|
1509
|
-
description: connection.description,
|
|
1510
|
-
auth: connection.auth,
|
|
1511
|
-
advertiseTools: connection.advertiseTools,
|
|
1512
|
-
effects: parsedEffects.value,
|
|
1513
|
-
tools: parsedTools.value,
|
|
1514
|
-
disallowedTools: parsedDisallowed.value,
|
|
1515
|
-
});
|
|
1516
|
-
}
|
|
1517
|
-
return connections;
|
|
1518
|
-
});
|
|
1519
|
-
}
|
|
1520
|
-
function loadInlineStdioConnection(ctx, name, entryPath, connection) {
|
|
1521
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1522
|
-
const tools = connection.tools;
|
|
1523
|
-
const names = Object.keys(tools);
|
|
1524
|
-
if (names.length === 0) {
|
|
1525
|
-
ctx.diagnostics.push({
|
|
1526
|
-
severity: "error",
|
|
1527
|
-
path: displayPath(ctx, entryPath),
|
|
1528
|
-
message: "MCP connection `tools` must declare at least one tool.",
|
|
1529
|
-
});
|
|
1530
|
-
return undefined;
|
|
1531
|
-
}
|
|
1532
|
-
for (const toolName of names) {
|
|
1533
|
-
if (!NAME_PATTERN.test(toolName)) {
|
|
1534
|
-
ctx.diagnostics.push({
|
|
1535
|
-
severity: "error",
|
|
1536
|
-
path: displayPath(ctx, entryPath),
|
|
1537
|
-
message: `MCP tool name "${toolName}" must match ${NAME_PATTERN}.`,
|
|
1538
|
-
});
|
|
1539
|
-
return undefined;
|
|
1540
|
-
}
|
|
1541
|
-
const tool = tools[toolName];
|
|
1542
|
-
if (tool === undefined || isBlank(tool.description)) {
|
|
1543
|
-
ctx.diagnostics.push({
|
|
1544
|
-
severity: "error",
|
|
1545
|
-
path: displayPath(ctx, entryPath),
|
|
1546
|
-
message: `MCP tool "${toolName}" requires a non-empty description.`,
|
|
1547
|
-
});
|
|
1548
|
-
return undefined;
|
|
1549
|
-
}
|
|
1550
|
-
if (typeof tool.execute !== "function") {
|
|
1551
|
-
ctx.diagnostics.push({
|
|
1552
|
-
severity: "error",
|
|
1553
|
-
path: displayPath(ctx, entryPath),
|
|
1554
|
-
message: `MCP tool "${toolName}" requires an execute(input) function.`,
|
|
1555
|
-
});
|
|
1556
|
-
return undefined;
|
|
1557
|
-
}
|
|
1558
|
-
}
|
|
1559
|
-
try {
|
|
1560
|
-
const transport = yield packageInlineStdioConnection({
|
|
1561
|
-
name,
|
|
1562
|
-
entryPath,
|
|
1563
|
-
tools,
|
|
1564
|
-
absWorkingDir: ctx.rootDir,
|
|
1565
|
-
});
|
|
1566
|
-
return {
|
|
1567
|
-
name,
|
|
1568
|
-
transport,
|
|
1569
|
-
description: connection.description,
|
|
1570
|
-
};
|
|
1571
|
-
}
|
|
1572
|
-
catch (error) {
|
|
1573
|
-
ctx.diagnostics.push({
|
|
1574
|
-
severity: "error",
|
|
1575
|
-
path: displayPath(ctx, entryPath),
|
|
1576
|
-
message: `Failed to package inline stdio MCP connection: ${errorMessage(error)}`,
|
|
1577
|
-
});
|
|
1578
|
-
return undefined;
|
|
1579
|
-
}
|
|
1580
|
-
});
|
|
1581
|
-
}
|
|
1582
|
-
// ============================================================================
|
|
1583
|
-
// Subagents
|
|
1584
|
-
// ============================================================================
|
|
1585
|
-
function loadSubagents(ctx, subagentsDir) {
|
|
1586
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1587
|
-
if (!(yield isDirectory(subagentsDir))) {
|
|
1588
|
-
return [];
|
|
1589
|
-
}
|
|
1590
|
-
const subagents = [];
|
|
1591
|
-
for (const entry of yield sortedEntries(subagentsDir)) {
|
|
1592
|
-
if (!entry.isDirectory()) {
|
|
1593
|
-
continue;
|
|
1594
|
-
}
|
|
1595
|
-
const subagentDir = join(subagentsDir, entry.name);
|
|
1596
|
-
if (!NAME_PATTERN.test(entry.name)) {
|
|
1597
|
-
ctx.diagnostics.push({
|
|
1598
|
-
severity: "error",
|
|
1599
|
-
path: displayPath(ctx, subagentDir),
|
|
1600
|
-
message: `Subagent name "${entry.name}" must match ${NAME_PATTERN}.`,
|
|
1601
|
-
});
|
|
1602
|
-
continue;
|
|
1603
|
-
}
|
|
1604
|
-
subagents.push(yield loadAgent(ctx, subagentDir, { name: entry.name, isRoot: false }));
|
|
1605
|
-
}
|
|
1606
|
-
return subagents;
|
|
1607
|
-
});
|
|
1608
|
-
}
|
|
1609
|
-
function warnUnsupportedSubagentSlots(ctx, subagentDir) {
|
|
1610
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1611
|
-
const unsupported = [
|
|
1612
|
-
["tools", "tools"],
|
|
1613
|
-
["skills", "skills"],
|
|
1614
|
-
["extensions", "extensions"],
|
|
1615
|
-
["mcp-connections", "MCP connections"],
|
|
1616
|
-
["host-connections", "host MCP connections"],
|
|
1617
|
-
["connections", "MCP connections"],
|
|
1618
|
-
["subagents", "nested subagents"],
|
|
1619
|
-
["channels", "channels"],
|
|
1620
|
-
["schedules", "schedules"],
|
|
1621
|
-
["hooks", "hooks"],
|
|
1622
|
-
["sandbox", "sandbox files"],
|
|
1623
|
-
];
|
|
1624
|
-
for (const [slot, label] of unsupported) {
|
|
1625
|
-
if (yield isDirectory(join(subagentDir, slot))) {
|
|
1626
|
-
ctx.diagnostics.push({
|
|
1627
|
-
severity: "warning",
|
|
1628
|
-
path: displayPath(ctx, join(subagentDir, slot)),
|
|
1629
|
-
message: `Subagent ${label} are not supported on the Cursor harness yet; the subagent runs with the parent's execution surface. Ignoring.`,
|
|
1630
|
-
});
|
|
1631
|
-
}
|
|
1632
|
-
}
|
|
1633
|
-
});
|
|
1634
|
-
}
|
|
1635
|
-
/**
|
|
1636
|
-
* Append enabled built-in tools after authored tools. Authored tools with
|
|
1637
|
-
* the same name win.
|
|
1638
|
-
*/
|
|
1639
|
-
function appendBuiltinTools(ctx, agentDir, tools, config) {
|
|
1640
|
-
const builtin = builtinTools(config);
|
|
1641
|
-
if (builtin.length === 0) {
|
|
1642
|
-
return;
|
|
1643
|
-
}
|
|
1644
|
-
const agentPath = displayPath(ctx, join(agentDir, "agent.ts"));
|
|
1645
|
-
for (const tool of builtin) {
|
|
1646
|
-
const existing = tools.find((t) => t.name === tool.name);
|
|
1647
|
-
if (existing !== undefined) {
|
|
1648
|
-
const owner = existing.extension === undefined
|
|
1649
|
-
? "Authored"
|
|
1650
|
-
: `Extension "${existing.extension}"`;
|
|
1651
|
-
ctx.diagnostics.push({
|
|
1652
|
-
severity: "warning",
|
|
1653
|
-
path: agentPath,
|
|
1654
|
-
message: `${owner} tool "${tool.name}" shadows the built-in tool of the same name; the built-in is skipped.`,
|
|
1655
|
-
});
|
|
1656
|
-
continue;
|
|
1657
|
-
}
|
|
1658
|
-
tools.push(tool);
|
|
1659
|
-
}
|
|
1660
|
-
}
|
|
1661
|
-
/**
|
|
1662
|
-
* Validate the authored runtime. An unrecognized value is an error
|
|
1663
|
-
* diagnostic and falls back to local — a typo must not silently pick a
|
|
1664
|
-
* different harness for every turn.
|
|
1665
|
-
*/
|
|
1666
|
-
function resolveRuntime(ctx, agentDir, runtime) {
|
|
1667
|
-
if (runtime === undefined ||
|
|
1668
|
-
runtime === "local" ||
|
|
1669
|
-
runtime === "cloud" ||
|
|
1670
|
-
runtime === "grokbot") {
|
|
1671
|
-
return runtime !== null && runtime !== void 0 ? runtime : "local";
|
|
1672
|
-
}
|
|
1673
|
-
ctx.diagnostics.push({
|
|
1674
|
-
severity: "error",
|
|
1675
|
-
path: displayPath(ctx, join(agentDir, "agent.ts")),
|
|
1676
|
-
message: `runtime must be "local", "cloud", or "grokbot"; got ${JSON.stringify(runtime)}.`,
|
|
1677
|
-
});
|
|
1678
|
-
return "local";
|
|
1679
|
-
}
|
|
1680
|
-
/**
|
|
1681
|
-
* Grok Bot turns run on the Sand harness with its own toolset: authored
|
|
1682
|
-
* server tools, MCP connections, and subagents have no path onto that
|
|
1683
|
-
* toolset yet, so an agent declaring them must fail at serve start rather
|
|
1684
|
-
* than run with capabilities silently missing.
|
|
1685
|
-
*/
|
|
1686
|
-
function checkGrokBotRuntimeLimitations(ctx, agentDir, counts) {
|
|
1687
|
-
const agentPath = displayPath(ctx, join(agentDir, "agent.ts"));
|
|
1688
|
-
const error = (path, message) => {
|
|
1689
|
-
ctx.diagnostics.push({ severity: "error", path, message });
|
|
1690
|
-
};
|
|
1691
|
-
const warn = (path, message) => {
|
|
1692
|
-
ctx.diagnostics.push({ severity: "warning", path, message });
|
|
1693
|
-
};
|
|
1694
|
-
if (counts.serverTools > 0) {
|
|
1695
|
-
error(displayPath(ctx, join(agentDir, "tools")), 'Server tools (execution: "server", including opted-in builtinTools) are not reachable from grokbot turns yet. Remove them or run the agent on the local runtime.');
|
|
1696
|
-
}
|
|
1697
|
-
if (counts.connections > 0) {
|
|
1698
|
-
error(displayPath(ctx, agentDir), "MCP connections (agent/mcp-connections/ or agent/host-connections/) are not wired into grokbot turns yet. Remove them or run the agent on the local runtime.");
|
|
1699
|
-
}
|
|
1700
|
-
if (counts.subagents > 0) {
|
|
1701
|
-
error(displayPath(ctx, join(agentDir, "subagents")), "Authored subagents are not available on grokbot turns (the Grok Bot harness runs its own subagent set). Remove them or run the agent on the local runtime.");
|
|
1702
|
-
}
|
|
1703
|
-
// The remaining declarations mirror the cloud runtime's warnings: the
|
|
1704
|
-
// turn runs on a hosted machine (the account's box), so local-workspace
|
|
1705
|
-
// materialization never reaches it.
|
|
1706
|
-
if (counts.agentTools > 0) {
|
|
1707
|
-
warn(displayPath(ctx, join(agentDir, "tools")), 'Agent tools (execution: "agent") are described in the hosted session\'s instructions (with script bodies); they are not written onto the box filesystem automatically.');
|
|
1708
|
-
}
|
|
1709
|
-
if (counts.skills > 0) {
|
|
1710
|
-
warn(displayPath(ctx, join(agentDir, "skills")), "Authored skills are materialized into the local session workspace only; the hosted Grok Bot box does not see them.");
|
|
1711
|
-
}
|
|
1712
|
-
if (counts.seedFiles > 0) {
|
|
1713
|
-
warn(displayPath(ctx, join(agentDir, "sandbox")), "sandbox/workspace seed files apply to the local session workspace only; grokbot turns run on the account's hosted box.");
|
|
1714
|
-
}
|
|
1715
|
-
if (counts.hasCloudOptions) {
|
|
1716
|
-
warn(agentPath, 'The `cloud` block is unused on runtime: "grokbot", and grokbot sessions refuse per-session cloud attach.');
|
|
1717
|
-
}
|
|
1718
|
-
if (counts.hasLocalSandbox) {
|
|
1719
|
-
warn(agentPath, 'local.sandbox does not apply to runtime: "grokbot"; the box is the account\'s Cursor-hosted computer.');
|
|
1720
|
-
}
|
|
1721
|
-
}
|
|
1722
|
-
function warnCloudRuntimeLimitations(ctx, agentDir, counts) {
|
|
1723
|
-
const agentPath = displayPath(ctx, join(agentDir, "agent.ts"));
|
|
1724
|
-
if (!counts.hasCloudOptions) {
|
|
1725
|
-
ctx.diagnostics.push({
|
|
1726
|
-
severity: "warning",
|
|
1727
|
-
path: agentPath,
|
|
1728
|
-
message: 'runtime: "cloud" usually needs a `cloud` block (e.g. repos / env). Without it the Cursor cloud API may reject create.',
|
|
1729
|
-
});
|
|
1730
|
-
}
|
|
1731
|
-
if (counts.agentTools > 0) {
|
|
1732
|
-
ctx.diagnostics.push({
|
|
1733
|
-
severity: "warning",
|
|
1734
|
-
path: displayPath(ctx, join(agentDir, "tools")),
|
|
1735
|
-
message: 'Agent tools (execution: "agent") are described on the first cloud prompt (with script bodies); they are not written into the cloud VM filesystem automatically.',
|
|
1736
|
-
});
|
|
1737
|
-
}
|
|
1738
|
-
if (counts.skills > 0) {
|
|
1739
|
-
ctx.diagnostics.push({
|
|
1740
|
-
severity: "warning",
|
|
1741
|
-
path: displayPath(ctx, join(agentDir, "skills")),
|
|
1742
|
-
message: "Authored skills reach cloud agents through native store discovery: hosted deployments write the deployment store's skills/ directory; local serve/run with a personal API key writes agent-serve/<agent>/skills/ on the USER store. Without a reachable store, only skills already in the cloud repo are visible.",
|
|
1743
|
-
});
|
|
1744
|
-
}
|
|
1745
|
-
if (counts.seedFiles > 0) {
|
|
1746
|
-
ctx.diagnostics.push({
|
|
1747
|
-
severity: "warning",
|
|
1748
|
-
path: displayPath(ctx, join(agentDir, "sandbox")),
|
|
1749
|
-
message: "sandbox/workspace seed files apply to the local session workspace only; cloud agents use the configured cloud repos.",
|
|
1750
|
-
});
|
|
1751
|
-
}
|
|
1752
|
-
}
|
|
1753
|
-
// ============================================================================
|
|
1754
|
-
// Channels
|
|
1755
|
-
// ============================================================================
|
|
1756
|
-
function loadChannels(ctx, channelsDir) {
|
|
1757
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1758
|
-
var _a;
|
|
1759
|
-
var _b;
|
|
1760
|
-
const channels = [];
|
|
1761
|
-
let httpChannelDefinition;
|
|
1762
|
-
if (!(yield isDirectory(channelsDir))) {
|
|
1763
|
-
return { channels, httpChannelDefinition };
|
|
1764
|
-
}
|
|
1765
|
-
for (const entry of yield sortedEntries(channelsDir)) {
|
|
1766
|
-
const entryPath = join(channelsDir, entry.name);
|
|
1767
|
-
if (!entry.isFile() || !isModuleFile(entry.name)) {
|
|
1768
|
-
continue;
|
|
1769
|
-
}
|
|
1770
|
-
const id = moduleStem(entry.name);
|
|
1771
|
-
if (!NAME_PATTERN.test(id)) {
|
|
1772
|
-
ctx.diagnostics.push({
|
|
1773
|
-
severity: "error",
|
|
1774
|
-
path: displayPath(ctx, entryPath),
|
|
1775
|
-
message: `Channel id "${id}" must match ${NAME_PATTERN}.`,
|
|
1776
|
-
});
|
|
1777
|
-
continue;
|
|
1778
|
-
}
|
|
1779
|
-
let mod;
|
|
1780
|
-
try {
|
|
1781
|
-
mod = yield ctx.importModule(entryPath);
|
|
1782
|
-
}
|
|
1783
|
-
catch (error) {
|
|
1784
|
-
ctx.diagnostics.push({
|
|
1785
|
-
severity: "error",
|
|
1786
|
-
path: displayPath(ctx, entryPath),
|
|
1787
|
-
message: `Failed to load module: ${errorMessage(error)}`,
|
|
1788
|
-
});
|
|
1789
|
-
continue;
|
|
1790
|
-
}
|
|
1791
|
-
const kind = getDefinitionKind(mod.default);
|
|
1792
|
-
if (kind === "http-channel") {
|
|
1793
|
-
if (id !== "http") {
|
|
1794
|
-
ctx.diagnostics.push({
|
|
1795
|
-
severity: "error",
|
|
1796
|
-
path: displayPath(ctx, entryPath),
|
|
1797
|
-
message: "httpChannel() configures the built-in HTTP channel and must be authored at channels/http.ts.",
|
|
1798
|
-
});
|
|
1799
|
-
continue;
|
|
1800
|
-
}
|
|
1801
|
-
httpChannelDefinition = mod.default;
|
|
1802
|
-
continue;
|
|
1803
|
-
}
|
|
1804
|
-
if (kind !== "channel") {
|
|
1805
|
-
ctx.diagnostics.push({
|
|
1806
|
-
severity: "error",
|
|
1807
|
-
path: displayPath(ctx, entryPath),
|
|
1808
|
-
message: id === "http"
|
|
1809
|
-
? "channels/http.ts must default-export httpChannel({ ... })."
|
|
1810
|
-
: "Channel modules must default-export defineChannel({ ... }).",
|
|
1811
|
-
});
|
|
1812
|
-
continue;
|
|
1813
|
-
}
|
|
1814
|
-
if (id === "http") {
|
|
1815
|
-
ctx.diagnostics.push({
|
|
1816
|
-
severity: "error",
|
|
1817
|
-
path: displayPath(ctx, entryPath),
|
|
1818
|
-
message: "The channel id `http` is reserved for the built-in HTTP channel; use httpChannel() to configure it.",
|
|
1819
|
-
});
|
|
1820
|
-
continue;
|
|
1821
|
-
}
|
|
1822
|
-
const definition = mod.default;
|
|
1823
|
-
for (const notice of (_b = (_a = definition.meta) === null || _a === void 0 ? void 0 : _a.deprecations) !== null && _b !== void 0 ? _b : []) {
|
|
1824
|
-
ctx.diagnostics.push({
|
|
1825
|
-
severity: "warning",
|
|
1826
|
-
path: displayPath(ctx, entryPath),
|
|
1827
|
-
message: notice,
|
|
1828
|
-
});
|
|
1829
|
-
}
|
|
1830
|
-
channels.push({ id, definition });
|
|
1831
|
-
}
|
|
1832
|
-
return { channels, httpChannelDefinition };
|
|
1833
|
-
});
|
|
1834
|
-
}
|
|
1835
|
-
// ============================================================================
|
|
1836
|
-
// Schedules
|
|
1837
|
-
// ============================================================================
|
|
1838
|
-
function loadSchedules(ctx, schedulesDir) {
|
|
1839
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1840
|
-
if (!(yield isDirectory(schedulesDir))) {
|
|
1841
|
-
return [];
|
|
1842
|
-
}
|
|
1843
|
-
const schedules = [];
|
|
1844
|
-
for (const filePath of yield walkFiles(schedulesDir)) {
|
|
1845
|
-
const rel = toPosix(relative(schedulesDir, filePath));
|
|
1846
|
-
if (rel.endsWith(".md")) {
|
|
1847
|
-
const name = rel.slice(0, -".md".length);
|
|
1848
|
-
const { data, body } = parseFrontmatter(yield readFile(filePath, "utf8"));
|
|
1849
|
-
const cron = data.cron;
|
|
1850
|
-
if (cron === undefined || !isValidCron(cron)) {
|
|
1851
|
-
ctx.diagnostics.push({
|
|
1852
|
-
severity: "error",
|
|
1853
|
-
path: displayPath(ctx, filePath),
|
|
1854
|
-
message: cron === undefined
|
|
1855
|
-
? "Markdown schedules require `cron` frontmatter."
|
|
1856
|
-
: `Invalid cron expression: "${cron}".`,
|
|
1857
|
-
});
|
|
1858
|
-
continue;
|
|
1859
|
-
}
|
|
1860
|
-
const markdown = body.trim();
|
|
1861
|
-
if (markdown === "") {
|
|
1862
|
-
ctx.diagnostics.push({
|
|
1863
|
-
severity: "error",
|
|
1864
|
-
path: displayPath(ctx, filePath),
|
|
1865
|
-
message: "Markdown schedules require a non-empty prompt body.",
|
|
1866
|
-
});
|
|
1867
|
-
continue;
|
|
1868
|
-
}
|
|
1869
|
-
schedules.push({
|
|
1870
|
-
name,
|
|
1871
|
-
cron,
|
|
1872
|
-
kind: "markdown",
|
|
1873
|
-
definition: {
|
|
1874
|
-
__agentServe: "schedule",
|
|
1875
|
-
cron,
|
|
1876
|
-
markdown,
|
|
1877
|
-
},
|
|
1878
|
-
});
|
|
1879
|
-
continue;
|
|
1880
|
-
}
|
|
1881
|
-
if (!isModuleFile(basename(filePath))) {
|
|
1882
|
-
continue;
|
|
1883
|
-
}
|
|
1884
|
-
const name = rel.slice(0, -extname(rel).length);
|
|
1885
|
-
const definition = yield importDefault(ctx, filePath, "schedule");
|
|
1886
|
-
if (definition === undefined) {
|
|
1887
|
-
continue;
|
|
1888
|
-
}
|
|
1889
|
-
const schedule = definition;
|
|
1890
|
-
if ((schedule.markdown === undefined) === (schedule.run === undefined)) {
|
|
1891
|
-
ctx.diagnostics.push({
|
|
1892
|
-
severity: "error",
|
|
1893
|
-
path: displayPath(ctx, filePath),
|
|
1894
|
-
message: "Schedules require exactly one of `markdown` or `run`.",
|
|
1895
|
-
});
|
|
1896
|
-
continue;
|
|
1897
|
-
}
|
|
1898
|
-
if (!isValidCron(schedule.cron)) {
|
|
1899
|
-
ctx.diagnostics.push({
|
|
1900
|
-
severity: "error",
|
|
1901
|
-
path: displayPath(ctx, filePath),
|
|
1902
|
-
message: `Invalid cron expression: "${schedule.cron}".`,
|
|
1903
|
-
});
|
|
1904
|
-
continue;
|
|
1905
|
-
}
|
|
1906
|
-
schedules.push({
|
|
1907
|
-
name,
|
|
1908
|
-
cron: schedule.cron,
|
|
1909
|
-
kind: schedule.run === undefined ? "markdown" : "run",
|
|
1910
|
-
definition: schedule,
|
|
1911
|
-
});
|
|
1912
|
-
}
|
|
1913
|
-
return schedules;
|
|
1914
|
-
});
|
|
1915
|
-
}
|
|
1916
|
-
// ============================================================================
|
|
1917
|
-
// Hooks
|
|
1918
|
-
// ============================================================================
|
|
1919
|
-
function loadHooks(ctx, hooksDir) {
|
|
1920
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1921
|
-
if (!(yield isDirectory(hooksDir))) {
|
|
1922
|
-
return [];
|
|
1923
|
-
}
|
|
1924
|
-
const hooks = [];
|
|
1925
|
-
for (const filePath of yield walkFiles(hooksDir)) {
|
|
1926
|
-
if (!isModuleFile(basename(filePath))) {
|
|
1927
|
-
continue;
|
|
1928
|
-
}
|
|
1929
|
-
const rel = toPosix(relative(hooksDir, filePath));
|
|
1930
|
-
const name = rel.slice(0, -extname(rel).length);
|
|
1931
|
-
const definition = yield importDefault(ctx, filePath, "hook");
|
|
1932
|
-
if (definition === undefined) {
|
|
1933
|
-
continue;
|
|
1934
|
-
}
|
|
1935
|
-
const hook = definition;
|
|
1936
|
-
if (typeof hook.events !== "object" ||
|
|
1937
|
-
hook.events === null ||
|
|
1938
|
-
Object.keys(hook.events).length === 0) {
|
|
1939
|
-
ctx.diagnostics.push({
|
|
1940
|
-
severity: "warning",
|
|
1941
|
-
path: displayPath(ctx, filePath),
|
|
1942
|
-
message: "Hook declares no event handlers.",
|
|
1943
|
-
});
|
|
1944
|
-
continue;
|
|
1945
|
-
}
|
|
1946
|
-
hooks.push({ name, definition: hook });
|
|
1947
|
-
}
|
|
1948
|
-
return hooks;
|
|
1949
|
-
});
|
|
1950
|
-
}
|
|
1951
|
-
// ============================================================================
|
|
1952
|
-
// A/B metrics (agent/ab.ts or agent/ab/*.ts) + agent/ab.config.ts
|
|
1953
|
-
// ============================================================================
|
|
1954
|
-
function loadABs(ctx, agentDir) {
|
|
1955
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1956
|
-
const abs = [];
|
|
1957
|
-
const seen = new Set();
|
|
1958
|
-
const single = yield findModuleFile(agentDir, "ab");
|
|
1959
|
-
if (single !== undefined) {
|
|
1960
|
-
const definition = yield importDefault(ctx, single, "ab");
|
|
1961
|
-
if (definition !== undefined) {
|
|
1962
|
-
const name = resolveABName(definition, "ab");
|
|
1963
|
-
seen.add(name);
|
|
1964
|
-
abs.push({
|
|
1965
|
-
name,
|
|
1966
|
-
definition: definition,
|
|
1967
|
-
});
|
|
1968
|
-
}
|
|
1969
|
-
}
|
|
1970
|
-
const absDir = join(agentDir, "ab");
|
|
1971
|
-
if (yield isDirectory(absDir)) {
|
|
1972
|
-
for (const filePath of yield walkFiles(absDir)) {
|
|
1973
|
-
if (!isModuleFile(basename(filePath))) {
|
|
1974
|
-
continue;
|
|
1975
|
-
}
|
|
1976
|
-
const rel = toPosix(relative(absDir, filePath));
|
|
1977
|
-
const stem = rel.slice(0, -extname(rel).length);
|
|
1978
|
-
const definition = yield importDefault(ctx, filePath, "ab");
|
|
1979
|
-
if (definition === undefined) {
|
|
1980
|
-
continue;
|
|
1981
|
-
}
|
|
1982
|
-
const name = resolveABName(definition, stem);
|
|
1983
|
-
if (seen.has(name)) {
|
|
1984
|
-
ctx.diagnostics.push({
|
|
1985
|
-
severity: "error",
|
|
1986
|
-
path: displayPath(ctx, filePath),
|
|
1987
|
-
message: `Duplicate A/B experiment name ${JSON.stringify(name)}.`,
|
|
1988
|
-
});
|
|
1989
|
-
continue;
|
|
1990
|
-
}
|
|
1991
|
-
seen.add(name);
|
|
1992
|
-
abs.push({
|
|
1993
|
-
name,
|
|
1994
|
-
definition: definition,
|
|
1995
|
-
});
|
|
1996
|
-
}
|
|
1997
|
-
}
|
|
1998
|
-
return abs;
|
|
1999
|
-
});
|
|
2000
|
-
}
|
|
2001
|
-
function loadABConfig(ctx, agentDir) {
|
|
2002
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2003
|
-
const path = yield findModuleFile(agentDir, "ab.config");
|
|
2004
|
-
if (path === undefined) {
|
|
2005
|
-
return undefined;
|
|
2006
|
-
}
|
|
2007
|
-
try {
|
|
2008
|
-
const mod = yield ctx.importModule(path);
|
|
2009
|
-
if (mod.default === null || typeof mod.default !== "object") {
|
|
2010
|
-
ctx.diagnostics.push({
|
|
2011
|
-
severity: "error",
|
|
2012
|
-
path: displayPath(ctx, path),
|
|
2013
|
-
message: "ab.config.ts must default-export defineABConfig({ ... }) from @cursor/july/ab.",
|
|
2014
|
-
});
|
|
2015
|
-
return undefined;
|
|
2016
|
-
}
|
|
2017
|
-
return mod.default;
|
|
2018
|
-
}
|
|
2019
|
-
catch (error) {
|
|
2020
|
-
ctx.diagnostics.push({
|
|
2021
|
-
severity: "error",
|
|
2022
|
-
path: displayPath(ctx, path),
|
|
2023
|
-
message: `Failed to load ab.config: ${error instanceof Error ? error.message : String(error)}`,
|
|
2024
|
-
});
|
|
2025
|
-
return undefined;
|
|
2026
|
-
}
|
|
2027
|
-
});
|
|
2028
|
-
}
|
|
2029
|
-
// ============================================================================
|
|
2030
|
-
// Storage (agent/storage.ts)
|
|
2031
|
-
// ============================================================================
|
|
2032
|
-
function loadStorage(ctx, agentDir, architecture) {
|
|
2033
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2034
|
-
const path = yield findModuleFile(agentDir, "storage");
|
|
2035
|
-
if (path === undefined) {
|
|
2036
|
-
return undefined;
|
|
2037
|
-
}
|
|
2038
|
-
if (architecture === "v2") {
|
|
2039
|
-
// Storage is a hosting-mode decision on v2, made by
|
|
2040
|
-
// framework-storage-selection.ts: the state root when self-hosted,
|
|
2041
|
-
// Cursor-managed storage on Cursor hosting. Fail early (error
|
|
2042
|
-
// diagnostics refuse serve and deploy) rather than silently running
|
|
2043
|
-
// with storage the author believes they configured. Not imported.
|
|
2044
|
-
ctx.diagnostics.push({
|
|
2045
|
-
severity: "error",
|
|
2046
|
-
path: displayPath(ctx, path),
|
|
2047
|
-
message: 'agent/storage.ts is not supported on architecture "v2": the platform selects storage (the local state root when self-hosted; Cursor-managed storage on Cursor hosting). Delete the file.',
|
|
2048
|
-
});
|
|
2049
|
-
return undefined;
|
|
2050
|
-
}
|
|
2051
|
-
// v1 keeps loading the authored sink, with notice: defineStorage is going
|
|
2052
|
-
// away with the v1 control plane.
|
|
2053
|
-
ctx.diagnostics.push({
|
|
2054
|
-
severity: "warning",
|
|
2055
|
-
path: displayPath(ctx, path),
|
|
2056
|
-
message: 'agent/storage.ts (defineStorage) is deprecated and going away: architecture "v2" selects storage automatically (the local state root when self-hosted; Cursor-managed storage on Cursor hosting). Still honored on v1.',
|
|
2057
|
-
});
|
|
2058
|
-
const definition = yield importDefault(ctx, path, "storage");
|
|
2059
|
-
return definition;
|
|
2060
|
-
});
|
|
2061
|
-
}
|
|
2062
|
-
// ============================================================================
|
|
2063
|
-
// OpenTelemetry (agent/otel.ts)
|
|
2064
|
-
// ============================================================================
|
|
2065
|
-
function loadOtel(ctx, agentDir) {
|
|
2066
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2067
|
-
const path = yield findModuleFile(agentDir, "otel");
|
|
2068
|
-
if (path === undefined) {
|
|
2069
|
-
return undefined;
|
|
2070
|
-
}
|
|
2071
|
-
const definition = yield importDefault(ctx, path, "otel");
|
|
2072
|
-
return definition;
|
|
2073
|
-
});
|
|
2074
|
-
}
|
|
2075
|
-
// ============================================================================
|
|
2076
|
-
// Artifacts (agent/artifacts.ts)
|
|
2077
|
-
// ============================================================================
|
|
2078
|
-
function loadArtifacts(ctx, agentDir) {
|
|
2079
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2080
|
-
var _a;
|
|
2081
|
-
const path = yield findModuleFile(agentDir, "artifacts");
|
|
2082
|
-
if (path === undefined) {
|
|
2083
|
-
return undefined;
|
|
2084
|
-
}
|
|
2085
|
-
const imported = yield importDefault(ctx, path, "artifacts");
|
|
2086
|
-
if (imported === undefined) {
|
|
2087
|
-
return undefined;
|
|
2088
|
-
}
|
|
2089
|
-
const definition = imported;
|
|
2090
|
-
if (definition.agentTool === true &&
|
|
2091
|
-
Object.keys((_a = definition.kinds) !== null && _a !== void 0 ? _a : {}).length === 0) {
|
|
2092
|
-
ctx.diagnostics.push({
|
|
2093
|
-
severity: "error",
|
|
2094
|
-
path: displayPath(ctx, path),
|
|
2095
|
-
message: "artifacts.agentTool requires kinds: the tag_artifact tool needs at least one declared kind; its descriptions are the tool prompt.",
|
|
2096
|
-
});
|
|
2097
|
-
}
|
|
2098
|
-
return definition;
|
|
2099
|
-
});
|
|
2100
|
-
}
|
|
2101
|
-
/**
|
|
2102
|
-
* Materialize the `tag_artifact` built-in when `defineArtifacts` set
|
|
2103
|
-
* `agentTool: true`. Appended after `loadAgent` because the tool is
|
|
2104
|
-
* generated from the artifacts kinds registry, not `AgentConfig.builtinTools`.
|
|
2105
|
-
*/
|
|
2106
|
-
function appendArtifactTool(ctx, agentDir, agent, artifacts) {
|
|
2107
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2108
|
-
var _a;
|
|
2109
|
-
const tool = artifactBuiltinTool(artifacts);
|
|
2110
|
-
if (tool === undefined) {
|
|
2111
|
-
return;
|
|
2112
|
-
}
|
|
2113
|
-
const artifactsPath = displayPath(ctx, (_a = (yield findModuleFile(agentDir, "artifacts"))) !== null && _a !== void 0 ? _a : join(agentDir, "artifacts.ts"));
|
|
2114
|
-
if (agent.tools.some((t) => t.name === TAG_ARTIFACT_TOOL_NAME)) {
|
|
2115
|
-
ctx.diagnostics.push({
|
|
2116
|
-
severity: "warning",
|
|
2117
|
-
path: artifactsPath,
|
|
2118
|
-
message: `Authored tool "${TAG_ARTIFACT_TOOL_NAME}" shadows the built-in tool of the same name; the authored tool wins.`,
|
|
2119
|
-
});
|
|
2120
|
-
return;
|
|
2121
|
-
}
|
|
2122
|
-
agent.tools.push(tool);
|
|
2123
|
-
});
|
|
2124
|
-
}
|
|
2125
|
-
function resolveABName(definition, fallback) {
|
|
2126
|
-
if (typeof definition === "object" &&
|
|
2127
|
-
definition !== null &&
|
|
2128
|
-
"name" in definition &&
|
|
2129
|
-
typeof definition.name === "string" &&
|
|
2130
|
-
definition.name.length > 0) {
|
|
2131
|
-
return definition.name;
|
|
2132
|
-
}
|
|
2133
|
-
return fallback;
|
|
2134
|
-
}
|
|
2135
|
-
// ============================================================================
|
|
2136
|
-
// Sandbox seed files
|
|
2137
|
-
// ============================================================================
|
|
2138
|
-
function loadSeedFiles(ctx, agentDir) {
|
|
2139
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2140
|
-
const sandboxDir = join(agentDir, "sandbox");
|
|
2141
|
-
for (const candidate of ["sandbox.ts", join("sandbox", "sandbox.ts")]) {
|
|
2142
|
-
if (yield isFile(join(agentDir, candidate))) {
|
|
2143
|
-
ctx.diagnostics.push({
|
|
2144
|
-
severity: "warning",
|
|
2145
|
-
path: displayPath(ctx, join(agentDir, candidate)),
|
|
2146
|
-
message: "Custom sandbox backends are not supported; the agent runs in the session workspace directory. Ignoring.",
|
|
2147
|
-
});
|
|
2148
|
-
}
|
|
2149
|
-
}
|
|
2150
|
-
const workspaceDir = join(sandboxDir, "workspace");
|
|
2151
|
-
if (!(yield isDirectory(workspaceDir))) {
|
|
2152
|
-
return [];
|
|
2153
|
-
}
|
|
2154
|
-
const seedFiles = [];
|
|
2155
|
-
for (const filePath of yield walkFiles(workspaceDir)) {
|
|
2156
|
-
seedFiles.push({
|
|
2157
|
-
relativePath: toPosix(relative(workspaceDir, filePath)),
|
|
2158
|
-
sourcePath: filePath,
|
|
2159
|
-
});
|
|
2160
|
-
}
|
|
2161
|
-
return seedFiles;
|
|
2162
|
-
});
|
|
2163
|
-
}
|
|
2164
|
-
// ============================================================================
|
|
2165
|
-
// Info projection
|
|
2166
|
-
// ============================================================================
|
|
2167
|
-
export function projectInfo(project) {
|
|
2168
|
-
var _a, _b, _c, _d;
|
|
2169
|
-
var _e, _f;
|
|
2170
|
-
const agent = project.agent;
|
|
2171
|
-
const httpRoutes = [
|
|
2172
|
-
{ method: "POST", path: "/v1/session", url: "/v1/session" },
|
|
2173
|
-
{
|
|
2174
|
-
method: "POST",
|
|
2175
|
-
path: "/v1/session/:sessionId",
|
|
2176
|
-
url: "/v1/session/:sessionId",
|
|
2177
|
-
},
|
|
2178
|
-
{
|
|
2179
|
-
method: "POST",
|
|
2180
|
-
path: "/v1/session/:sessionId/stop",
|
|
2181
|
-
url: "/v1/session/:sessionId/stop",
|
|
2182
|
-
},
|
|
2183
|
-
{
|
|
2184
|
-
method: "GET",
|
|
2185
|
-
path: "/v1/session/:sessionId/stream",
|
|
2186
|
-
url: "/v1/session/:sessionId/stream",
|
|
2187
|
-
},
|
|
2188
|
-
{
|
|
2189
|
-
method: "GET",
|
|
2190
|
-
path: "/v1/session/:sessionId/approvals",
|
|
2191
|
-
url: "/v1/session/:sessionId/approvals",
|
|
2192
|
-
},
|
|
2193
|
-
{
|
|
2194
|
-
method: "POST",
|
|
2195
|
-
path: "/v1/session/:sessionId/approvals/:callId",
|
|
2196
|
-
url: "/v1/session/:sessionId/approvals/:callId",
|
|
2197
|
-
},
|
|
2198
|
-
];
|
|
2199
|
-
return Object.assign(Object.assign({ name: project.name, description: agent.description, model: agent.model, runtime: agent.runtime, cloud: agent.cloud === undefined ? undefined : toPublicCloudInfo(agent.cloud), local: agent.local, hosting: agent.hosting, concurrency: agent.concurrency, mcp: { path: MCP_ENDPOINT_PATH, tools: mcpEndpointToolNames(project) }, instructions: agent.instructions === undefined
|
|
2200
|
-
? undefined
|
|
2201
|
-
: {
|
|
2202
|
-
chars: agent.instructions.length,
|
|
2203
|
-
preview: agent.instructions.length > 280
|
|
2204
|
-
? `${agent.instructions.slice(0, 277)}...`
|
|
2205
|
-
: agent.instructions,
|
|
2206
|
-
}, tools: agent.tools.map((tool) => ({
|
|
2207
|
-
name: tool.name,
|
|
2208
|
-
description: tool.description,
|
|
2209
|
-
execution: tool.execution,
|
|
2210
|
-
needsApproval: tool.needsApproval,
|
|
2211
|
-
effect: tool.effect,
|
|
2212
|
-
inputSchema: tool.inputSchema,
|
|
2213
|
-
})), skills: agent.skills.map((skill) => ({
|
|
2214
|
-
name: skill.name,
|
|
2215
|
-
description: skill.description,
|
|
2216
|
-
})), extensions: (_e = agent.extensions) !== null && _e !== void 0 ? _e : [], connections: agent.connections.map((connection) => { var _a; return ({
|
|
2217
|
-
name: connection.name,
|
|
2218
|
-
transport: connectionTransportLabel(connection.transport),
|
|
2219
|
-
advertiseTools: connection.advertiseTools,
|
|
2220
|
-
effects: connection.effects,
|
|
2221
|
-
tools: connection.tools,
|
|
2222
|
-
disallowedTools: connection.disallowedTools,
|
|
2223
|
-
cursorAccountServers: "cursorAccount" in connection.transport
|
|
2224
|
-
? ((_a = connection.transport.servers) !== null && _a !== void 0 ? _a : "*")
|
|
2225
|
-
: undefined,
|
|
2226
|
-
}); }), subagents: agent.subagents.map((subagent) => ({
|
|
2227
|
-
name: subagent.name,
|
|
2228
|
-
description: subagent.description,
|
|
2229
|
-
model: subagent.model,
|
|
2230
|
-
})), channels: [
|
|
2231
|
-
{ id: "http", routes: httpRoutes },
|
|
2232
|
-
...project.channels.map((channel) => {
|
|
2233
|
-
var _a;
|
|
2234
|
-
const info = {
|
|
2235
|
-
id: channel.id,
|
|
2236
|
-
routes: ((_a = channel.definition.routes) !== null && _a !== void 0 ? _a : []).map((r) => {
|
|
2237
|
-
const routeInfo = {
|
|
2238
|
-
method: r.method,
|
|
2239
|
-
path: r.path,
|
|
2240
|
-
url: `/v1/channels/${channel.id}${r.path}`,
|
|
2241
|
-
};
|
|
2242
|
-
if (r.description !== undefined) {
|
|
2243
|
-
routeInfo.description = r.description;
|
|
2244
|
-
}
|
|
2245
|
-
if (r.bodySchema !== undefined) {
|
|
2246
|
-
routeInfo.bodySchema = toolInputJsonSchema(r.bodySchema);
|
|
2247
|
-
}
|
|
2248
|
-
if (r.querySchema !== undefined) {
|
|
2249
|
-
routeInfo.querySchema = toolInputJsonSchema(r.querySchema);
|
|
2250
|
-
}
|
|
2251
|
-
return routeInfo;
|
|
2252
|
-
}),
|
|
2253
|
-
};
|
|
2254
|
-
const meta = channel.definition.meta;
|
|
2255
|
-
if ((meta === null || meta === void 0 ? void 0 : meta.kind) !== undefined) {
|
|
2256
|
-
info.kind = meta.kind;
|
|
2257
|
-
}
|
|
2258
|
-
if ((meta === null || meta === void 0 ? void 0 : meta.githubWebhookEvents) !== undefined) {
|
|
2259
|
-
info.githubWebhookEvents = meta.githubWebhookEvents;
|
|
2260
|
-
}
|
|
2261
|
-
if ((meta === null || meta === void 0 ? void 0 : meta.originWebhookEvents) !== undefined) {
|
|
2262
|
-
info.originWebhookEvents = meta.originWebhookEvents;
|
|
2263
|
-
}
|
|
2264
|
-
if ((meta === null || meta === void 0 ? void 0 : meta.deploymentEventTypes) !== undefined) {
|
|
2265
|
-
info.deploymentEventTypes = meta.deploymentEventTypes;
|
|
2266
|
-
}
|
|
2267
|
-
if ((meta === null || meta === void 0 ? void 0 : meta.slackChannelPosts) !== undefined) {
|
|
2268
|
-
info.slackChannelPosts = meta.slackChannelPosts;
|
|
2269
|
-
}
|
|
2270
|
-
return info;
|
|
2271
|
-
}),
|
|
2272
|
-
], schedules: project.schedules.map((schedule) => ({
|
|
2273
|
-
name: schedule.name,
|
|
2274
|
-
cron: schedule.cron,
|
|
2275
|
-
kind: schedule.kind,
|
|
2276
|
-
})), hooks: project.hooks.map((hook) => hook.name), abs: project.abs.map((experiment) => experiment.name) }, (project.abConfig === undefined && project.abs.length === 0
|
|
2277
|
-
? {}
|
|
2278
|
-
: {
|
|
2279
|
-
abConfig: {
|
|
2280
|
-
maxPlaygroundSessions: resolveABMaxPlaygroundSessions((_a = project.abConfig) === null || _a === void 0 ? void 0 : _a.maxPlaygroundSessions),
|
|
2281
|
-
durableSamples: ((_b = project.storage) === null || _b === void 0 ? void 0 : _b.abs) !== undefined,
|
|
2282
|
-
durableSnapshots: ((_d = (_c = project.storage) === null || _c === void 0 ? void 0 : _c.abs) === null || _d === void 0 ? void 0 : _d.putSnapshot) !== undefined,
|
|
2283
|
-
},
|
|
2284
|
-
})), { storage: project.storage === undefined
|
|
2285
|
-
? undefined
|
|
2286
|
-
: { name: project.storage.name }, artifacts: project.artifacts === undefined
|
|
2287
|
-
? undefined
|
|
2288
|
-
: {
|
|
2289
|
-
kinds: Object.keys((_f = project.artifacts.kinds) !== null && _f !== void 0 ? _f : {}),
|
|
2290
|
-
agentTool: project.artifacts.agentTool === true,
|
|
2291
|
-
max: resolveArtifactsMax(project.artifacts),
|
|
2292
|
-
}, otel: project.otel === undefined
|
|
2293
|
-
? undefined
|
|
2294
|
-
: {
|
|
2295
|
-
enabled: true,
|
|
2296
|
-
serviceName: project.otel.serviceName,
|
|
2297
|
-
}, diagnostics: project.diagnostics });
|
|
2298
|
-
}
|
|
2299
|
-
// ============================================================================
|
|
2300
|
-
// Shared helpers
|
|
2301
|
-
// ============================================================================
|
|
2302
|
-
function importDefault(ctx, modulePath, expectedKind) {
|
|
2303
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2304
|
-
let mod;
|
|
2305
|
-
try {
|
|
2306
|
-
mod = yield ctx.importModule(modulePath);
|
|
2307
|
-
}
|
|
2308
|
-
catch (error) {
|
|
2309
|
-
ctx.diagnostics.push({
|
|
2310
|
-
severity: "error",
|
|
2311
|
-
path: displayPath(ctx, modulePath),
|
|
2312
|
-
message: `Failed to load module: ${errorMessage(error)}`,
|
|
2313
|
-
});
|
|
2314
|
-
return undefined;
|
|
2315
|
-
}
|
|
2316
|
-
const kind = getDefinitionKind(mod.default);
|
|
2317
|
-
if (kind !== expectedKind) {
|
|
2318
|
-
const helper = DEFINE_HELPER_NAME[expectedKind];
|
|
2319
|
-
ctx.diagnostics.push({
|
|
2320
|
-
severity: "error",
|
|
2321
|
-
path: displayPath(ctx, modulePath),
|
|
2322
|
-
message: kind === undefined
|
|
2323
|
-
? `Module must default-export ${helper}({ ... }).`
|
|
2324
|
-
: `Expected a ${expectedKind} definition but found "${kind}" (wrong authored slot?).`,
|
|
2325
|
-
});
|
|
2326
|
-
return undefined;
|
|
2327
|
-
}
|
|
2328
|
-
return mod.default;
|
|
2329
|
-
});
|
|
2330
|
-
}
|
|
2331
|
-
const DEFINE_HELPER_NAME = {
|
|
2332
|
-
agent: "defineAgent",
|
|
2333
|
-
instructions: "defineInstructions",
|
|
2334
|
-
tool: "defineTool",
|
|
2335
|
-
skill: "defineSkill",
|
|
2336
|
-
extension: "defineExtension",
|
|
2337
|
-
connection: "defineConnection",
|
|
2338
|
-
channel: "defineChannel",
|
|
2339
|
-
"http-channel": "httpChannel",
|
|
2340
|
-
schedule: "defineSchedule",
|
|
2341
|
-
hook: "defineHook",
|
|
2342
|
-
ab: "defineAB",
|
|
2343
|
-
storage: "defineStorage",
|
|
2344
|
-
artifacts: "defineArtifacts",
|
|
2345
|
-
otel: "defineOtel",
|
|
2346
|
-
};
|
|
2347
|
-
function warnUnknownSlots(ctx, agentDir) {
|
|
2348
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2349
|
-
const known = new Set([
|
|
2350
|
-
"agent",
|
|
2351
|
-
"instructions",
|
|
2352
|
-
"tools",
|
|
2353
|
-
"skills",
|
|
2354
|
-
"extensions",
|
|
2355
|
-
"mcp-connections",
|
|
2356
|
-
"host-connections",
|
|
2357
|
-
// Legacy slot name; resolveMcpConnectionsDir warns and still loads it.
|
|
2358
|
-
"connections",
|
|
2359
|
-
"subagents",
|
|
2360
|
-
"channels",
|
|
2361
|
-
"schedules",
|
|
2362
|
-
"hooks",
|
|
2363
|
-
"ab",
|
|
2364
|
-
"sandbox",
|
|
2365
|
-
"lib",
|
|
2366
|
-
// Vite playground custom tool chips (agent/playground/tools).
|
|
2367
|
-
"playground",
|
|
2368
|
-
]);
|
|
2369
|
-
for (const entry of yield sortedEntries(agentDir)) {
|
|
2370
|
-
if (!entry.isDirectory() || entry.name.startsWith(".")) {
|
|
2371
|
-
continue;
|
|
2372
|
-
}
|
|
2373
|
-
if (entry.name === "node_modules" || known.has(entry.name)) {
|
|
2374
|
-
continue;
|
|
2375
|
-
}
|
|
2376
|
-
// Footgun: evals live at project-root `evals/`, not under `agent/`.
|
|
2377
|
-
if (entry.name === "evals") {
|
|
2378
|
-
ctx.diagnostics.push({
|
|
2379
|
-
severity: "warning",
|
|
2380
|
-
path: displayPath(ctx, join(agentDir, entry.name)),
|
|
2381
|
-
message: 'Directory "evals" under agent/ is ignored — put defineEval files at project-root evals/ instead.',
|
|
2382
|
-
});
|
|
2383
|
-
continue;
|
|
2384
|
-
}
|
|
2385
|
-
ctx.diagnostics.push({
|
|
2386
|
-
severity: "warning",
|
|
2387
|
-
path: displayPath(ctx, join(agentDir, entry.name)),
|
|
2388
|
-
message: `Unrecognized directory "${entry.name}" under agent/ (not an authored slot; use lib/ for shared code).`,
|
|
2389
|
-
});
|
|
2390
|
-
}
|
|
2391
|
-
});
|
|
2392
|
-
}
|
|
2393
|
-
function hasFlatLayoutMarkers(rootDir) {
|
|
2394
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2395
|
-
if (yield isFile(join(rootDir, "instructions.md"))) {
|
|
2396
|
-
return true;
|
|
2397
|
-
}
|
|
2398
|
-
return (yield findModuleFile(rootDir, "agent")) !== undefined;
|
|
2399
|
-
});
|
|
2400
|
-
}
|
|
2401
|
-
function readPackageName(rootDir) {
|
|
2402
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2403
|
-
const packageJsonPath = join(rootDir, "package.json");
|
|
2404
|
-
if (!(yield isFile(packageJsonPath))) {
|
|
2405
|
-
return undefined;
|
|
2406
|
-
}
|
|
2407
|
-
try {
|
|
2408
|
-
const parsed = JSON.parse(yield readFile(packageJsonPath, "utf8"));
|
|
2409
|
-
if (typeof parsed.name === "string" && parsed.name !== "") {
|
|
2410
|
-
return parsed.name;
|
|
2411
|
-
}
|
|
2412
|
-
}
|
|
2413
|
-
catch (_a) {
|
|
2414
|
-
// Malformed package.json falls back to the directory name.
|
|
2415
|
-
}
|
|
2416
|
-
return undefined;
|
|
2417
|
-
});
|
|
2418
|
-
}
|
|
2419
|
-
function findModuleFile(dir, stem) {
|
|
2420
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2421
|
-
for (const extension of [".ts", ".mts", ".js", ".mjs"]) {
|
|
2422
|
-
const candidate = join(dir, `${stem}${extension}`);
|
|
2423
|
-
if (yield isFile(candidate)) {
|
|
2424
|
-
return candidate;
|
|
2425
|
-
}
|
|
2426
|
-
}
|
|
2427
|
-
return undefined;
|
|
2428
|
-
});
|
|
2429
|
-
}
|
|
2430
|
-
function isModuleFile(fileName) {
|
|
2431
|
-
if (fileName.endsWith(".d.ts") || fileName.startsWith(".")) {
|
|
2432
|
-
return false;
|
|
2433
|
-
}
|
|
2434
|
-
if (!MODULE_EXTENSIONS.has(extname(fileName))) {
|
|
2435
|
-
return false;
|
|
2436
|
-
}
|
|
2437
|
-
// Colocated unit tests (Vitest/Jest convention) are not definitions.
|
|
2438
|
-
// Without this, `channels/slack.test.ts` is scanned as a channel named
|
|
2439
|
-
// "slack.test", fails NAME_PATTERN, and the whole agent refuses to serve.
|
|
2440
|
-
const stem = moduleStem(fileName);
|
|
2441
|
-
return !stem.endsWith(".test") && !stem.endsWith(".spec");
|
|
2442
|
-
}
|
|
2443
|
-
function moduleStem(fileName) {
|
|
2444
|
-
return fileName.slice(0, -extname(fileName).length);
|
|
2445
|
-
}
|
|
2446
|
-
function sortedEntries(dir) {
|
|
2447
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2448
|
-
const entries = yield readdir(dir, { withFileTypes: true });
|
|
2449
|
-
return entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
2450
|
-
});
|
|
2451
|
-
}
|
|
2452
|
-
function walkFiles(dir) {
|
|
2453
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2454
|
-
const files = [];
|
|
2455
|
-
for (const entry of yield sortedEntries(dir)) {
|
|
2456
|
-
if (entry.name.startsWith(".")) {
|
|
2457
|
-
continue;
|
|
2458
|
-
}
|
|
2459
|
-
const entryPath = join(dir, entry.name);
|
|
2460
|
-
if (entry.isDirectory()) {
|
|
2461
|
-
files.push(...(yield walkFiles(entryPath)));
|
|
2462
|
-
}
|
|
2463
|
-
else if (entry.isFile()) {
|
|
2464
|
-
files.push(entryPath);
|
|
2465
|
-
}
|
|
2466
|
-
}
|
|
2467
|
-
return files;
|
|
2468
|
-
});
|
|
2469
|
-
}
|
|
2470
|
-
function isDirectory(path) {
|
|
2471
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2472
|
-
try {
|
|
2473
|
-
return (yield stat(path)).isDirectory();
|
|
2474
|
-
}
|
|
2475
|
-
catch (_a) {
|
|
2476
|
-
return false;
|
|
2477
|
-
}
|
|
2478
|
-
});
|
|
2479
|
-
}
|
|
2480
|
-
function isFile(path) {
|
|
2481
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2482
|
-
try {
|
|
2483
|
-
return (yield stat(path)).isFile();
|
|
2484
|
-
}
|
|
2485
|
-
catch (_a) {
|
|
2486
|
-
return false;
|
|
2487
|
-
}
|
|
2488
|
-
});
|
|
2489
|
-
}
|
|
2490
|
-
function isValidModelSetting(model) {
|
|
2491
|
-
if (typeof model === "string") {
|
|
2492
|
-
return model.trim() !== "";
|
|
2493
|
-
}
|
|
2494
|
-
return (typeof model === "object" &&
|
|
2495
|
-
model !== null &&
|
|
2496
|
-
typeof model.id === "string" &&
|
|
2497
|
-
model.id.trim() !== "");
|
|
2498
|
-
}
|
|
2499
|
-
function isBlank(value) {
|
|
2500
|
-
return typeof value !== "string" || value.trim() === "";
|
|
2501
|
-
}
|
|
2502
|
-
function toPosix(path) {
|
|
2503
|
-
return path.split("\\").join("/");
|
|
2504
|
-
}
|
|
2505
|
-
function displayPath(ctx, absolutePath) {
|
|
2506
|
-
const rel = relative(ctx.rootDir, absolutePath);
|
|
2507
|
-
return rel.startsWith("..") ? absolutePath : toPosix(rel);
|
|
2508
|
-
}
|
|
2509
|
-
function errorMessage(error) {
|
|
2510
|
-
return error instanceof Error ? error.message : String(error);
|
|
2511
|
-
}
|
|
7
|
+
*
|
|
8
|
+
* The loaders live under `./discovery/`, one module per authored slot;
|
|
9
|
+
* this module is the public surface the CLI, server, and tests import.
|
|
10
|
+
*/
|
|
11
|
+
export { runtimeArchitectureCompatibilityError } from "./discovery/agent-config.js";
|
|
12
|
+
export { projectInfo } from "./discovery/info.js";
|
|
13
|
+
export { formatDiagnostics, hasErrorDiagnostics, isAgentProjectDir, listChildAgentDirs, loadAgentProject, loadAgentProjects, RESERVED_SLUGS, resolveAgentMounts, } from "./discovery/project.js";
|
|
14
|
+
export { deriveSkillDescription } from "./discovery/skills.js";
|