@cursor/july 0.1.107 → 0.1.108
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 +10 -5
- package/dist/bin/agent-serve.js +30 -8
- package/dist/channels/github/github-channel.js +8 -5
- package/dist/channels/github/types.d.ts +5 -1
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/channels/origin/origin-channel.d.ts.map +1 -1
- package/dist/channels/origin/origin-channel.js +27 -13
- package/dist/channels/origin/types.d.ts +5 -1
- package/dist/channels/origin/types.d.ts.map +1 -1
- package/dist/channels.d.ts +45 -0
- package/dist/channels.d.ts.map +1 -1
- package/dist/channels.js +106 -7
- package/dist/connections.d.ts +2 -1
- package/dist/connections.d.ts.map +1 -1
- package/dist/connections.js +2 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.CtuckIiL.js → app.BASKM3M4.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.BHZYsNVi.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.Bkkhnu8K.js → VPLocalSearchBox.BOKxlYGP.js} +1 -1
- package/dist/docs/assets/chunks/{theme.DhpIe0Pa.js → theme.DbDZW-zb.js} +2 -2
- package/dist/docs/assets/{deployment.md.MFPKqsqI.js → deployment.md.JenwuCCB.js} +4 -4
- package/dist/docs/assets/{deployment.md.MFPKqsqI.lean.js → deployment.md.JenwuCCB.lean.js} +1 -1
- package/dist/docs/assets/{evals.md.CbMoebP1.js → evals.md.C1ekS3k2.js} +1 -1
- package/dist/docs/assets/guides_cloud-agents.md.BsloyHdY.js +9 -0
- package/dist/docs/assets/{guides_cloud-agents.md.DS8RIjwx.lean.js → guides_cloud-agents.md.BsloyHdY.lean.js} +1 -1
- package/dist/docs/assets/{guides_github.md.c0gdGJ-D.js → guides_github.md.BH33UEBJ.js} +1 -1
- package/dist/docs/assets/{guides_grokbot-agents.md.DVqdhBKb.js → guides_grokbot-agents.md.DyV-WArv.js} +1 -1
- package/dist/docs/assets/guides_improve.md.BGVk32bK.js +14 -0
- package/dist/docs/assets/guides_improve.md.BGVk32bK.lean.js +1 -0
- package/dist/docs/assets/{guides_slack.md.9oHPye9o.js → guides_slack.md.D4RVMM4G.js} +3 -3
- package/dist/docs/assets/{guides_slack.md.9oHPye9o.lean.js → guides_slack.md.D4RVMM4G.lean.js} +1 -1
- package/dist/docs/assets/index.md.BjH1w2ZW.js +5 -0
- package/dist/docs/assets/{reference_agent-config.md.CHNpiyp4.js → reference_agent-config.md.CvoL6pof.js} +1 -1
- package/dist/docs/assets/{reference_cli.md.Dm67hd2D.js → reference_cli.md.Ca26u0Es.js} +6 -6
- package/dist/docs/assets/{reference_cli.md.Dm67hd2D.lean.js → reference_cli.md.Ca26u0Es.lean.js} +1 -1
- package/dist/docs/assets/{reference_connections.md.Di6jJAXF.js → reference_connections.md.BojkC6c5.js} +1 -1
- package/dist/docs/assets/{reference_extensions.md.CGmMLblt.js → reference_extensions.md.CPWt00ds.js} +1 -1
- package/dist/docs/assets/{scaffolding-agents.md.em43xlY1.js → scaffolding-agents.md.Kctn3OVb.js} +1 -1
- package/dist/docs/assets/{skills_create-agent.md.BVoWPcan.js → skills_create-agent.md.Q3h6Je-e.js} +1 -1
- package/dist/docs/assets/{skills_debug.md.CDbPhHfg.js → skills_debug.md.CVjCXMFF.js} +1 -1
- package/dist/docs/assets/{skills_debug.md.CDbPhHfg.lean.js → skills_debug.md.CVjCXMFF.lean.js} +1 -1
- package/dist/docs/assets/{skills_evals.md.723kpUmA.js → skills_evals.md.BhovOvrl.js} +1 -1
- package/dist/docs/assets/skills_framework-map.md.D-tFZhFS.js +1 -0
- package/dist/docs/assets/{skills_framework-map.md.haibFyoB.lean.js → skills_framework-map.md.D-tFZhFS.lean.js} +1 -1
- package/dist/docs/assets/{skills_github.md.D0JahM8c.js → skills_github.md.hgFX_oKY.js} +1 -1
- package/dist/docs/assets/troubleshooting.md.CYEAO9bM.js +1 -0
- package/dist/docs/building-with-agents.html +4 -4
- package/dist/docs/concepts.html +4 -4
- package/dist/docs/deployment.html +7 -7
- package/dist/docs/deployment.md +7 -2
- package/dist/docs/evals.html +6 -6
- package/dist/docs/evals.md +3 -2
- package/dist/docs/guides/agent-to-agent.html +4 -4
- package/dist/docs/guides/cloud-agents.html +6 -6
- package/dist/docs/guides/cloud-agents.md +4 -3
- package/dist/docs/guides/convert-automation.html +4 -4
- package/dist/docs/guides/github.html +5 -5
- package/dist/docs/guides/github.md +2 -1
- package/dist/docs/guides/grokbot-agents.html +6 -6
- package/dist/docs/guides/grokbot-agents.md +2 -2
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/improve.html +40 -0
- package/dist/docs/guides/improve.md +91 -0
- package/dist/docs/guides/mcp-oauth.html +4 -4
- package/dist/docs/guides/opentelemetry.html +5 -5
- package/dist/docs/guides/slack.html +6 -6
- package/dist/docs/guides/slack.md +2 -1
- 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 +4 -0
- package/dist/docs/llms-full.txt +158 -33
- package/dist/docs/llms.txt +1 -0
- package/dist/docs/quickstart.html +4 -4
- package/dist/docs/reference/agent-config.html +6 -6
- package/dist/docs/reference/agent-config.md +5 -2
- package/dist/docs/reference/artifacts.html +4 -4
- package/dist/docs/reference/channels.html +4 -4
- package/dist/docs/reference/cli.html +10 -10
- package/dist/docs/reference/cli.md +18 -11
- package/dist/docs/reference/connections.html +6 -6
- package/dist/docs/reference/connections.md +2 -1
- package/dist/docs/reference/extensions.html +6 -6
- package/dist/docs/reference/extensions.md +2 -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 +4 -4
- package/dist/docs/reference/prompt.html +4 -4
- package/dist/docs/reference/result.html +4 -4
- package/dist/docs/reference/schedules.html +4 -4
- package/dist/docs/reference/sessions.html +4 -4
- package/dist/docs/reference/skills.html +4 -4
- package/dist/docs/reference/subagents.html +4 -4
- package/dist/docs/reference/tools.html +4 -4
- package/dist/docs/scaffolding-agents.html +5 -5
- package/dist/docs/scaffolding-agents.md +2 -1
- package/dist/docs/skills/ab.html +4 -4
- package/dist/docs/skills/create-agent.html +6 -6
- package/dist/docs/skills/create-agent.md +1 -1
- package/dist/docs/skills/debug.html +5 -5
- package/dist/docs/skills/debug.md +1 -1
- package/dist/docs/skills/evals.html +6 -6
- package/dist/docs/skills/evals.md +1 -1
- package/dist/docs/skills/framework-map.html +5 -5
- package/dist/docs/skills/framework-map.md +4 -2
- package/dist/docs/skills/github.html +6 -6
- package/dist/docs/skills/github.md +1 -1
- 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/grokbot-agents.html +4 -4
- package/dist/docs/templates/pr-autofixer.html +4 -4
- package/dist/docs/templates/security-help.html +4 -4
- package/dist/docs/templates/security-reviewer.html +4 -4
- package/dist/docs/templates/triage.html +4 -4
- package/dist/docs/troubleshooting.html +5 -5
- package/dist/docs/troubleshooting.md +1 -1
- package/dist/extensions/improve/extension.d.ts +46 -0
- package/dist/extensions/improve/extension.d.ts.map +1 -0
- package/dist/extensions/improve/extension.js +41 -0
- package/dist/extensions/improve/skills/yourself.d.ts +4 -0
- package/dist/extensions/improve/skills/yourself.d.ts.map +1 -0
- package/dist/extensions/improve/skills/yourself.js +43 -0
- package/dist/internal/builtin-tools/artifacts.d.ts.map +1 -1
- package/dist/internal/builtin-tools/artifacts.js +2 -3
- package/dist/internal/channel-route-dispatch.d.ts +13 -0
- package/dist/internal/channel-route-dispatch.d.ts.map +1 -0
- package/dist/internal/channel-route-dispatch.js +62 -0
- package/dist/internal/channel-state.d.ts +17 -0
- package/dist/internal/channel-state.d.ts.map +1 -0
- package/dist/internal/channel-state.js +78 -0
- package/dist/internal/cli-ax.d.ts +7 -0
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +99 -1
- package/dist/internal/cli-cursor.d.ts.map +1 -1
- package/dist/internal/cli-cursor.js +2 -0
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +38 -14
- package/dist/internal/cli-mcp-oauth.d.ts.map +1 -1
- package/dist/internal/cli-mcp-oauth.js +15 -0
- package/dist/internal/cli-mcp.d.ts.map +1 -1
- package/dist/internal/cli-mcp.js +12 -0
- package/dist/internal/cli-slack.d.ts.map +1 -1
- package/dist/internal/cli-slack.js +8 -2
- package/dist/internal/cursor/credentials.d.ts +8 -2
- package/dist/internal/cursor/credentials.d.ts.map +1 -1
- package/dist/internal/cursor/credentials.js +27 -5
- package/dist/internal/deploy-client.d.ts +27 -0
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +32 -0
- package/dist/internal/deploy-manifest.d.ts +23 -0
- package/dist/internal/deploy-manifest.d.ts.map +1 -1
- package/dist/internal/deploy-manifest.js +84 -1
- package/dist/internal/describe-error.d.ts +1 -0
- package/dist/internal/describe-error.d.ts.map +1 -1
- package/dist/internal/describe-error.js +25 -0
- package/dist/internal/guest-network.d.ts +4 -10
- package/dist/internal/guest-network.d.ts.map +1 -1
- package/dist/internal/guest-network.js +42 -26
- package/dist/internal/hosted-admission-adapter.d.ts +2 -0
- package/dist/internal/hosted-admission-adapter.d.ts.map +1 -1
- package/dist/internal/hosted-catch-protocol.d.ts +51 -0
- package/dist/internal/hosted-catch-protocol.d.ts.map +1 -0
- package/dist/internal/hosted-catch-protocol.js +103 -0
- package/dist/internal/hosted-catch.d.ts +40 -0
- package/dist/internal/hosted-catch.d.ts.map +1 -0
- package/dist/internal/hosted-catch.js +149 -0
- package/dist/internal/hosted-delivery-protocol.d.ts +41 -0
- package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
- package/dist/internal/hosted-delivery-protocol.js +249 -0
- package/dist/internal/hosted-delivery.d.ts +10 -1
- package/dist/internal/hosted-delivery.d.ts.map +1 -1
- package/dist/internal/hosted-delivery.js +100 -22
- package/dist/internal/hosted-execution-diag.d.ts +4 -1
- package/dist/internal/hosted-execution-diag.d.ts.map +1 -1
- package/dist/internal/hosted-execution-diag.js +13 -4
- package/dist/internal/hosted-execution-flush.d.ts +3 -0
- package/dist/internal/hosted-execution-flush.d.ts.map +1 -1
- package/dist/internal/hosted-execution-flush.js +2 -4
- package/dist/internal/http-control-plane-session.d.ts +30 -0
- package/dist/internal/http-control-plane-session.d.ts.map +1 -0
- package/dist/internal/http-control-plane-session.js +83 -0
- package/dist/internal/legacy-command-guard.d.ts +22 -0
- package/dist/internal/legacy-command-guard.d.ts.map +1 -0
- package/dist/internal/legacy-command-guard.js +51 -0
- package/dist/internal/platform-timers.d.ts +7 -0
- package/dist/internal/platform-timers.d.ts.map +1 -1
- package/dist/internal/platform-timers.js +139 -0
- package/dist/internal/reminder-control-plane-protocol.d.ts +2 -0
- package/dist/internal/reminder-control-plane-protocol.d.ts.map +1 -1
- package/dist/internal/reminder-control-plane-protocol.js +9 -2
- package/dist/internal/reminder-runner.d.ts +8 -0
- package/dist/internal/reminder-runner.d.ts.map +1 -1
- package/dist/internal/reminder-runner.js +36 -9
- package/dist/internal/resolve-prod-target.d.ts +2 -0
- package/dist/internal/resolve-prod-target.d.ts.map +1 -1
- package/dist/internal/resolve-prod-target.js +13 -0
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +4 -3
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +57 -56
- package/dist/internal/session-engine.d.ts +2 -6
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +95 -67
- package/dist/playground/assets/{index-Bq2HpEQB.js → index-BMDqAeXu.js} +28 -28
- package/dist/playground/assets/index-LUgJoWdL.css +1 -0
- package/dist/playground/index.html +2 -2
- package/dist/storage-backends/cursor-hosted.d.ts +6 -0
- package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/storage-backends/cursor-hosted.js +29 -1
- package/dist/types.d.ts +33 -5
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +4 -0
- package/docs/deployment.md +7 -2
- package/docs/evals.md +3 -2
- package/docs/guides/cloud-agents.md +4 -3
- package/docs/guides/github.md +2 -1
- package/docs/guides/grokbot-agents.md +2 -2
- package/docs/guides/improve.md +96 -0
- package/docs/guides/slack.md +2 -1
- package/docs/reference/agent-config.md +5 -2
- package/docs/reference/cli.md +18 -11
- package/docs/reference/connections.md +2 -1
- package/docs/reference/extensions.md +2 -0
- package/docs/scaffolding-agents.md +2 -1
- package/docs/troubleshooting.md +1 -1
- package/package.json +8 -1
- package/skills/create-agent/SKILL.md +1 -1
- package/skills/debug/SKILL.md +1 -1
- package/skills/evals/SKILL.md +1 -1
- package/skills/framework-map/SKILL.md +4 -2
- package/skills/github/SKILL.md +1 -1
- package/src/bin/agent-serve.ts +27 -2
- package/src/channels/github/github-channel.ts +8 -8
- package/src/channels/github/types.ts +5 -0
- package/src/channels/origin/origin-channel.ts +29 -13
- package/src/channels/origin/types.ts +5 -0
- package/src/channels.ts +157 -10
- package/src/connections.ts +2 -1
- package/src/extensions/improve/extension.ts +70 -0
- package/src/extensions/improve/skills/yourself.ts +50 -0
- package/src/internal/builtin-tools/artifacts.ts +2 -3
- package/src/internal/channel-route-dispatch.ts +66 -0
- package/src/internal/channel-state.ts +96 -0
- package/src/internal/cli-ax.ts +110 -1
- package/src/internal/cli-cursor.ts +4 -1
- package/src/internal/cli-deploy.ts +50 -10
- package/src/internal/cli-mcp-oauth.ts +18 -0
- package/src/internal/cli-mcp.ts +11 -0
- package/src/internal/cli-slack.ts +15 -2
- package/src/internal/cursor/credentials.ts +35 -7
- package/src/internal/deploy-client.ts +54 -0
- package/src/internal/deploy-manifest.ts +115 -1
- package/src/internal/describe-error.ts +28 -0
- package/src/internal/guest-network.ts +43 -29
- package/src/internal/hosted-admission-adapter.ts +2 -0
- package/src/internal/hosted-catch-protocol.ts +130 -0
- package/src/internal/hosted-catch.ts +192 -0
- package/src/internal/hosted-delivery-protocol.ts +387 -0
- package/src/internal/hosted-delivery.ts +155 -22
- package/src/internal/hosted-execution-diag.ts +21 -3
- package/src/internal/hosted-execution-flush.ts +6 -3
- package/src/internal/http-control-plane-session.ts +104 -0
- package/src/internal/legacy-command-guard.ts +59 -0
- package/src/internal/platform-timers.ts +191 -0
- package/src/internal/reminder-control-plane-protocol.ts +15 -2
- package/src/internal/reminder-runner.ts +60 -9
- package/src/internal/resolve-prod-target.ts +15 -0
- package/src/internal/sdk-runner.ts +2 -1
- package/src/internal/server.ts +89 -79
- package/src/internal/session-engine.ts +141 -83
- package/src/storage-backends/cursor-hosted.ts +40 -1
- package/src/types.ts +39 -6
- package/dist/docs/assets/chunks/@localSearchIndexroot.DXXZxiMv.js +0 -1
- package/dist/docs/assets/guides_cloud-agents.md.DS8RIjwx.js +0 -9
- package/dist/docs/assets/index.md.BW_6tOgR.js +0 -5
- package/dist/docs/assets/skills_framework-map.md.haibFyoB.js +0 -1
- package/dist/docs/assets/troubleshooting.md.Cus_YZga.js +0 -1
- package/dist/playground/assets/index-CZKKNlmb.css +0 -1
- /package/dist/docs/assets/{evals.md.CbMoebP1.lean.js → evals.md.C1ekS3k2.lean.js} +0 -0
- /package/dist/docs/assets/{guides_github.md.c0gdGJ-D.lean.js → guides_github.md.BH33UEBJ.lean.js} +0 -0
- /package/dist/docs/assets/{guides_grokbot-agents.md.DVqdhBKb.lean.js → guides_grokbot-agents.md.DyV-WArv.lean.js} +0 -0
- /package/dist/docs/assets/{index.md.BW_6tOgR.lean.js → index.md.BjH1w2ZW.lean.js} +0 -0
- /package/dist/docs/assets/{reference_agent-config.md.CHNpiyp4.lean.js → reference_agent-config.md.CvoL6pof.lean.js} +0 -0
- /package/dist/docs/assets/{reference_connections.md.Di6jJAXF.lean.js → reference_connections.md.BojkC6c5.lean.js} +0 -0
- /package/dist/docs/assets/{reference_extensions.md.CGmMLblt.lean.js → reference_extensions.md.CPWt00ds.lean.js} +0 -0
- /package/dist/docs/assets/{scaffolding-agents.md.em43xlY1.lean.js → scaffolding-agents.md.Kctn3OVb.lean.js} +0 -0
- /package/dist/docs/assets/{skills_create-agent.md.BVoWPcan.lean.js → skills_create-agent.md.Q3h6Je-e.lean.js} +0 -0
- /package/dist/docs/assets/{skills_evals.md.723kpUmA.lean.js → skills_evals.md.BhovOvrl.lean.js} +0 -0
- /package/dist/docs/assets/{skills_github.md.D0JahM8c.lean.js → skills_github.md.hgFX_oKY.lean.js} +0 -0
- /package/dist/docs/assets/{troubleshooting.md.Cus_YZga.lean.js → troubleshooting.md.CYEAO9bM.lean.js} +0 -0
package/src/channels.ts
CHANGED
|
@@ -64,9 +64,11 @@ export {
|
|
|
64
64
|
} from "./channels/checks.js";
|
|
65
65
|
|
|
66
66
|
import { Buffer } from "node:buffer";
|
|
67
|
-
import { timingSafeEqual } from "node:crypto";
|
|
67
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
68
68
|
import { brandDefinition } from "./internal/brand.js";
|
|
69
69
|
import { normalizeIdentity } from "./internal/continuation-identity.js";
|
|
70
|
+
import { attachCatchAdmission } from "./internal/hosted-catch.js";
|
|
71
|
+
import { attachHttpControlPlaneAdmission } from "./internal/http-control-plane-session.js";
|
|
70
72
|
import { isLoopbackAddress, isLoopbackHostname } from "./internal/net.js";
|
|
71
73
|
import type {
|
|
72
74
|
AuthContext,
|
|
@@ -117,7 +119,16 @@ export type {
|
|
|
117
119
|
export function defineChannel<TState = JsonValue>(
|
|
118
120
|
config: ChannelConfig<TState>
|
|
119
121
|
): ChannelDefinition<TState> {
|
|
120
|
-
|
|
122
|
+
const definition = brandDefinition("channel", config);
|
|
123
|
+
if (config.routes !== undefined && config.routes.length > 0) {
|
|
124
|
+
// Hosted: the control plane catches declared routes and replays them
|
|
125
|
+
// here. Provider packs attach their own admission afterwards.
|
|
126
|
+
attachCatchAdmission(
|
|
127
|
+
definition as ChannelDefinition<unknown>,
|
|
128
|
+
config.routes
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
return definition;
|
|
121
132
|
}
|
|
122
133
|
|
|
123
134
|
/**
|
|
@@ -127,13 +138,15 @@ export function defineChannel<TState = JsonValue>(
|
|
|
127
138
|
export function httpChannel(
|
|
128
139
|
config: HttpChannelConfig = {}
|
|
129
140
|
): HttpChannelDefinition {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
141
|
+
const definition =
|
|
142
|
+
config.continuation !== undefined
|
|
143
|
+
? brandDefinition("http-channel", {
|
|
144
|
+
...config,
|
|
145
|
+
continuation: normalizeIdentity(config.continuation, "http"),
|
|
146
|
+
})
|
|
147
|
+
: brandDefinition("http-channel", config);
|
|
148
|
+
attachHttpControlPlaneAdmission(definition);
|
|
149
|
+
return definition;
|
|
137
150
|
}
|
|
138
151
|
|
|
139
152
|
// ============================================================================
|
|
@@ -432,7 +445,13 @@ export function publicEndpoint(): AuthPolicy {
|
|
|
432
445
|
principalId: "anonymous",
|
|
433
446
|
principalType: "user",
|
|
434
447
|
});
|
|
435
|
-
|
|
448
|
+
// On the v2 control plane this also makes the channel's routes catchable
|
|
449
|
+
// with handler-side verification (the v1 alias posture): the CP admits
|
|
450
|
+
// every request on a declared route and the handler rejects bad ones.
|
|
451
|
+
return markCatchAuth(
|
|
452
|
+
Object.assign(policy, { [PUBLIC_ENDPOINT_MARKER]: true as const }),
|
|
453
|
+
{ kind: "public" }
|
|
454
|
+
);
|
|
436
455
|
}
|
|
437
456
|
|
|
438
457
|
/** True when a policy was created by {@link publicEndpoint}. */
|
|
@@ -443,3 +462,131 @@ export function isPublicEndpointPolicy(policy: AuthPolicy): boolean {
|
|
|
443
462
|
] === true
|
|
444
463
|
);
|
|
445
464
|
}
|
|
465
|
+
|
|
466
|
+
const CATCH_AUTH_MARKER = Symbol.for("agent-serve.catch-auth");
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* What `deploy` extracts from a {@link sharedSecretAuth} /
|
|
470
|
+
* {@link hmacSignatureAuth} policy into the manifest: the control plane runs
|
|
471
|
+
* the same check with the deployment secret of the same name before any
|
|
472
|
+
* compute starts. `secret` is a name, never a value.
|
|
473
|
+
*/
|
|
474
|
+
export type CatchAuthDeclaration =
|
|
475
|
+
| { kind: "header"; header: string; secret: string }
|
|
476
|
+
| { kind: "hmac-sha256"; header: string; secret: string; prefix?: string }
|
|
477
|
+
/** `publicEndpoint()`: the handler verifies; the control plane only rate limits. */
|
|
478
|
+
| { kind: "public" };
|
|
479
|
+
|
|
480
|
+
function markCatchAuth(
|
|
481
|
+
policy: AuthPolicy,
|
|
482
|
+
declaration: CatchAuthDeclaration
|
|
483
|
+
): AuthPolicy {
|
|
484
|
+
return Object.assign(policy, { [CATCH_AUTH_MARKER]: declaration });
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/** The catch declaration a policy carries, if it is a catch-capable policy. */
|
|
488
|
+
export function catchAuthDeclaration(
|
|
489
|
+
policy: AuthPolicy
|
|
490
|
+
): CatchAuthDeclaration | undefined {
|
|
491
|
+
const declaration = (policy as { [CATCH_AUTH_MARKER]?: unknown })[
|
|
492
|
+
CATCH_AUTH_MARKER
|
|
493
|
+
];
|
|
494
|
+
return typeof declaration === "object" && declaration !== null
|
|
495
|
+
? (declaration as CatchAuthDeclaration)
|
|
496
|
+
: undefined;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function presentedHeaderSecret(headerName: string, value: string): string {
|
|
500
|
+
if (
|
|
501
|
+
headerName === "authorization" &&
|
|
502
|
+
value.toLowerCase().startsWith("bearer ")
|
|
503
|
+
) {
|
|
504
|
+
return value.slice("bearer ".length).trim();
|
|
505
|
+
}
|
|
506
|
+
return value.trim();
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Admit requests whose `header` equals the secret named `secret` (an
|
|
511
|
+
* `Authorization: Bearer <secret>` header also matches). Locally the value
|
|
512
|
+
* is `process.env[secret]`; hosted, the deployment secret of that name. On
|
|
513
|
+
* Cursor hosting this ALSO declares the channel's routes reachable through
|
|
514
|
+
* the control-plane catch API, which runs the same comparison before any
|
|
515
|
+
* compute exists. An unset secret admits nothing.
|
|
516
|
+
*/
|
|
517
|
+
export function sharedSecretAuth(options: {
|
|
518
|
+
header: string;
|
|
519
|
+
secret: string;
|
|
520
|
+
}): AuthPolicy {
|
|
521
|
+
const header = options.header.toLowerCase();
|
|
522
|
+
const policy: AuthPolicy = (request) => {
|
|
523
|
+
const expected = process.env[options.secret];
|
|
524
|
+
const presented = request.headers.get(header);
|
|
525
|
+
if (
|
|
526
|
+
expected === undefined ||
|
|
527
|
+
expected.length === 0 ||
|
|
528
|
+
presented === null ||
|
|
529
|
+
!constantTimeEquals(presentedHeaderSecret(header, presented), expected)
|
|
530
|
+
) {
|
|
531
|
+
return null;
|
|
532
|
+
}
|
|
533
|
+
return {
|
|
534
|
+
authenticator: "shared-secret",
|
|
535
|
+
principalId: "webhook",
|
|
536
|
+
principalType: "service",
|
|
537
|
+
};
|
|
538
|
+
};
|
|
539
|
+
return markCatchAuth(policy, {
|
|
540
|
+
kind: "header",
|
|
541
|
+
header,
|
|
542
|
+
secret: options.secret,
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Admit requests whose `header` carries the hex HMAC-SHA256 of the raw body
|
|
548
|
+
* under the secret named `secret` (optionally behind a literal `prefix`
|
|
549
|
+
* such as `sha256=`). Same local / hosted / catch semantics as
|
|
550
|
+
* {@link sharedSecretAuth}.
|
|
551
|
+
*/
|
|
552
|
+
export function hmacSignatureAuth(options: {
|
|
553
|
+
header: string;
|
|
554
|
+
secret: string;
|
|
555
|
+
prefix?: string;
|
|
556
|
+
}): AuthPolicy {
|
|
557
|
+
const header = options.header.toLowerCase();
|
|
558
|
+
const prefix = options.prefix ?? "";
|
|
559
|
+
const policy: AuthPolicy = async (request) => {
|
|
560
|
+
const secret = process.env[options.secret];
|
|
561
|
+
const presented = request.headers.get(header)?.trim();
|
|
562
|
+
if (
|
|
563
|
+
secret === undefined ||
|
|
564
|
+
secret.length === 0 ||
|
|
565
|
+
presented === undefined ||
|
|
566
|
+
!presented.startsWith(prefix)
|
|
567
|
+
) {
|
|
568
|
+
return null;
|
|
569
|
+
}
|
|
570
|
+
const body = Buffer.from(await request.clone().arrayBuffer());
|
|
571
|
+
const expected = createHmac("sha256", secret).update(body).digest("hex");
|
|
572
|
+
if (
|
|
573
|
+
!constantTimeEquals(
|
|
574
|
+
presented.slice(prefix.length).toLowerCase(),
|
|
575
|
+
expected
|
|
576
|
+
)
|
|
577
|
+
) {
|
|
578
|
+
return null;
|
|
579
|
+
}
|
|
580
|
+
return {
|
|
581
|
+
authenticator: "hmac-signature",
|
|
582
|
+
principalId: "webhook",
|
|
583
|
+
principalType: "service",
|
|
584
|
+
};
|
|
585
|
+
};
|
|
586
|
+
return markCatchAuth(policy, {
|
|
587
|
+
kind: "hmac-sha256",
|
|
588
|
+
header,
|
|
589
|
+
secret: options.secret,
|
|
590
|
+
prefix: options.prefix,
|
|
591
|
+
});
|
|
592
|
+
}
|
package/src/connections.ts
CHANGED
|
@@ -65,7 +65,8 @@
|
|
|
65
65
|
* executes **on the Cursor backend** with the account's stored OAuth
|
|
66
66
|
* credentials, so the agent gets Linear / Notion / Slack / … tools with
|
|
67
67
|
* zero token plumbing and no OAuth secrets on this host. Requires the host
|
|
68
|
-
* to be signed in (`agent-sdk login` or
|
|
68
|
+
* to be signed in (`agent-sdk login`, `CURSOR_API_KEY`, or
|
|
69
|
+
* `CURSOR_SERVICE_ACCOUNT_KEY`):
|
|
69
70
|
*
|
|
70
71
|
* ```ts
|
|
71
72
|
* // agent/mcp-connections/account.ts — every connected server (no allowlist)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-improvement as an extension. An agent's behaviour is source in a
|
|
3
|
+
* repository, so changing how it behaves is a change to that source: a
|
|
4
|
+
* one-off Cursor cloud agent does the coding on a branch and opens a pull
|
|
5
|
+
* request, the agent that mounted this writes the brief and reports the
|
|
6
|
+
* pull request, and the tree it runs from is never edited.
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* // agent/extensions/improve.ts
|
|
10
|
+
* import improve from "@cursor/july/extensions/improve";
|
|
11
|
+
*
|
|
12
|
+
* export default improve({
|
|
13
|
+
* repo: "https://github.com/acme/agents",
|
|
14
|
+
* path: "agents/concierge",
|
|
15
|
+
* });
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* Contributes the `<namespace>__yourself` skill; mount it as `improve` so
|
|
19
|
+
* the skill reads `improve__yourself`. The coding goes through the
|
|
20
|
+
* cursor-cloud-agents extension, so mount that too, with `repo` among its
|
|
21
|
+
* `cloud.repos`. An agent whose own turns run on a cloud VM with the
|
|
22
|
+
* repository checked out needs no second mount: the skill has it work in
|
|
23
|
+
* that checkout.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { z } from "zod";
|
|
27
|
+
import { defineExtension } from "../../extensions.js";
|
|
28
|
+
import type { Extension } from "../../types.js";
|
|
29
|
+
|
|
30
|
+
export interface ImproveOptions {
|
|
31
|
+
/** Repository that holds the agent's source. One of the cloud agents mount's `cloud.repos`. */
|
|
32
|
+
repo: string;
|
|
33
|
+
/** The agent's directory in the repository, as `agent-sdk deploy --path` names it. */
|
|
34
|
+
path: string;
|
|
35
|
+
/** Ref the deployment tracks: branches start there and pull requests target it. Defaults to `main`. */
|
|
36
|
+
ref?: string;
|
|
37
|
+
/** What a coding agent must know about this tree beyond the Agent SDK layout, one line each. */
|
|
38
|
+
notes?: string[];
|
|
39
|
+
/** Who deploys a merged change and when it is live. Defaults to "It takes effect at the next deploy." */
|
|
40
|
+
deploy?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** {@link ImproveOptions} with defaults applied. */
|
|
44
|
+
export interface ImproveConfig extends ImproveOptions {
|
|
45
|
+
ref: string;
|
|
46
|
+
notes: string[];
|
|
47
|
+
deploy: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const line = z.string().trim().min(1);
|
|
51
|
+
|
|
52
|
+
const improve: Extension<ImproveConfig, ImproveOptions> = defineExtension({
|
|
53
|
+
root: import.meta.dirname,
|
|
54
|
+
description:
|
|
55
|
+
"Change your own source through a one-off cloud agent and a pull request, never the running tree.",
|
|
56
|
+
config: z.object({
|
|
57
|
+
repo: z.string().url(),
|
|
58
|
+
path: z
|
|
59
|
+
.string()
|
|
60
|
+
.regex(
|
|
61
|
+
/^[^\s/]+(\/[^\s/]+)*$/,
|
|
62
|
+
"Expected a repository-relative path like agents/concierge"
|
|
63
|
+
),
|
|
64
|
+
ref: line.default("main"),
|
|
65
|
+
notes: z.array(line).default([]),
|
|
66
|
+
deploy: line.default("It takes effect at the next deploy."),
|
|
67
|
+
}),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
export default improve;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { defineSkill } from "../../../skills.js";
|
|
2
|
+
import type { SkillDefinition } from "../../../types.js";
|
|
3
|
+
import extension from "../extension.js";
|
|
4
|
+
|
|
5
|
+
const { repo, path, ref, notes, deploy } = extension.config;
|
|
6
|
+
|
|
7
|
+
const treeNotes =
|
|
8
|
+
notes.length === 0
|
|
9
|
+
? ""
|
|
10
|
+
: `\nAbout this tree in particular; copy these into the brief verbatim:\n\n${notes.map((note) => `- ${note}`).join("\n")}\n`;
|
|
11
|
+
|
|
12
|
+
const yourself: SkillDefinition = defineSkill({
|
|
13
|
+
description:
|
|
14
|
+
"Change your own source (instructions, skills, tools, channels, connections, model, deployment) through a one-off Cursor cloud agent and a pull request. Use whenever the ask is about how you yourself behave, including adding or editing a skill, so the change lands in the repository instead of the tree you happen to be running from.",
|
|
15
|
+
markdown: `# Improve yourself
|
|
16
|
+
|
|
17
|
+
You are the Agent SDK agent at \`${path}\` in ${repo}, deployed from \`${ref}\`. A change to how you behave is a change to that source, and it goes through the repository like any other change: a branch, a pull request, review, a deploy. Nothing about it is special except that people describe it in the second person.
|
|
18
|
+
|
|
19
|
+
## What changes where
|
|
20
|
+
|
|
21
|
+
Under \`${path}/agent/\`:
|
|
22
|
+
|
|
23
|
+
- \`instructions.md\` is the always-on system prompt: identity, tool-choice rules, the output contract.
|
|
24
|
+
- \`skills/<name>.md\` is a procedure loaded on demand; a \`<name>/SKILL.md\` directory carries sibling files. The \`description\` frontmatter is the routing rule the model sees.
|
|
25
|
+
- \`tools/<name>.ts\` is typed, executable behaviour: anything that must be right every time.
|
|
26
|
+
- \`channels/\`, \`mcp-connections/\`, and \`host-connections/\` are the surfaces and connectors.
|
|
27
|
+
- \`agent.ts\` is the model, its parameters, and the runtime.
|
|
28
|
+
- \`extensions/<ns>.ts\` mounts an extension. This skill arrives through one.
|
|
29
|
+
${treeNotes}
|
|
30
|
+
## The change is made on a clean checkout
|
|
31
|
+
|
|
32
|
+
Never edit the tree you run from: it is a deployed copy of \`${path}\`, and nothing changed there lasts. The change is made on a clean checkout of ${repo} at \`${ref}\`. When your own turn runs on a cloud VM with that repository checked out, that VM is the checkout: branch there. Otherwise hand the coding to a one-off Cursor cloud agent through the cloud agents extension, with \`launch\` under its mount namespace, \`repo\` set to ${repo}, and \`starting_ref\` to \`${ref}\`. One agent per change; steer it with \`reply\` rather than launching another.
|
|
33
|
+
|
|
34
|
+
## The brief
|
|
35
|
+
|
|
36
|
+
Whoever makes the change works from the same brief. For a cloud agent it is the whole handoff, because it cannot see this conversation:
|
|
37
|
+
|
|
38
|
+
- Who and why. A change to the source of the Agent SDK agent at \`${path}\`, asked for by the person in front of you: the change in their words, and what you know about why they want it.
|
|
39
|
+
- Where. Repository ${repo}, ref \`${ref}\`, directory \`${path}\`, the layout above, and the files you expect to change.
|
|
40
|
+
- How. Follow the repository's own conventions (its \`AGENTS.md\` files and rules along the paths touched). Keep the diff to the change. Run the checks the repository defines for \`${path}\`. Open a pull request against \`${ref}\`; never merge it.
|
|
41
|
+
- Report. The pull request URL, what changed, what was verified and how, and anything left unverified.
|
|
42
|
+
|
|
43
|
+
## Report
|
|
44
|
+
|
|
45
|
+
If you launched a cloud agent, give the person its URL with the full id, read its report with \`dump\` (\`get\` for status), and read the pull request before repeating its claims. Report the pull request, not that you delegated.
|
|
46
|
+
|
|
47
|
+
A merged pull request changes nothing by itself. ${deploy} Never say you have already changed yourself. Editing your own source is still an edit: do it when asked, and stop at the pull request. Deploying is a separate authorization.`,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
export default yourself;
|
|
@@ -30,7 +30,7 @@ function kindVariant(name: string, schema: z.ZodType | undefined) {
|
|
|
30
30
|
.min(1)
|
|
31
31
|
.optional()
|
|
32
32
|
.describe(
|
|
33
|
-
"Stable
|
|
33
|
+
"Stable key, unique across kinds. Reusing it replaces that record instead of adding a new one."
|
|
34
34
|
),
|
|
35
35
|
});
|
|
36
36
|
}
|
|
@@ -47,10 +47,9 @@ export function tagArtifactTool(config: ArtifactsConfig): ToolDefinition {
|
|
|
47
47
|
);
|
|
48
48
|
}
|
|
49
49
|
const description = [
|
|
50
|
-
"Tag a
|
|
50
|
+
"Tag a finished output as an artifact. Artifacts persist across sessions and are listed for the people supervising this agent.",
|
|
51
51
|
"Kinds:",
|
|
52
52
|
...kinds.map(([name, kind]) => `- ${name}: ${kind.description}`),
|
|
53
|
-
"Same `key` upserts: tagging it again updates that record — use a stable key when re-tagging the same underlying thing (e.g. the same PR URL).",
|
|
54
53
|
].join("\n");
|
|
55
54
|
return defineTool({
|
|
56
55
|
description,
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { ChannelHandlerArgs, ChannelRoute } from "../types.js";
|
|
2
|
+
import { jsonResponse } from "./http-channel.js";
|
|
3
|
+
import { validateToolInput } from "./json-schema.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Run one authored channel route: validate the declared Zod query / body
|
|
7
|
+
* schemas onto `handlerArgs`, then call the handler. Shared by the local
|
|
8
|
+
* router mount and hosted catch replay so a caught request meets exactly
|
|
9
|
+
* the same schema gate as a direct one.
|
|
10
|
+
*/
|
|
11
|
+
export async function invokeChannelRoute(input: {
|
|
12
|
+
route: ChannelRoute;
|
|
13
|
+
request: Request;
|
|
14
|
+
handlerArgs: ChannelHandlerArgs;
|
|
15
|
+
}): Promise<Response> {
|
|
16
|
+
const { route, handlerArgs } = input;
|
|
17
|
+
let request = input.request;
|
|
18
|
+
|
|
19
|
+
if (route.querySchema !== undefined) {
|
|
20
|
+
const rawQuery = Object.fromEntries(
|
|
21
|
+
new URL(request.url).searchParams.entries()
|
|
22
|
+
);
|
|
23
|
+
const queryValidation = validateToolInput(route.querySchema, rawQuery);
|
|
24
|
+
if (!queryValidation.ok) {
|
|
25
|
+
return jsonResponse({ error: queryValidation.message }, { status: 400 });
|
|
26
|
+
}
|
|
27
|
+
handlerArgs.query = queryValidation.value;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (route.bodySchema !== undefined) {
|
|
31
|
+
// Buffer once so handlers that need the raw body (e.g. GitHub HMAC) can
|
|
32
|
+
// still read `request.text()` / `request.json()`.
|
|
33
|
+
let bodyText: string;
|
|
34
|
+
try {
|
|
35
|
+
bodyText = await request.text();
|
|
36
|
+
} catch {
|
|
37
|
+
return jsonResponse(
|
|
38
|
+
{ error: "Failed to read request body" },
|
|
39
|
+
{ status: 400 }
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
let rawBody: unknown;
|
|
43
|
+
try {
|
|
44
|
+
rawBody = bodyText === "" ? {} : JSON.parse(bodyText);
|
|
45
|
+
} catch {
|
|
46
|
+
return jsonResponse(
|
|
47
|
+
{ error: "Request body must be valid JSON" },
|
|
48
|
+
{ status: 400 }
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
const bodyValidation = validateToolInput(route.bodySchema, rawBody);
|
|
52
|
+
if (!bodyValidation.ok) {
|
|
53
|
+
return jsonResponse({ error: bodyValidation.message }, { status: 400 });
|
|
54
|
+
}
|
|
55
|
+
handlerArgs.body = bodyValidation.value;
|
|
56
|
+
request = new Request(request.url, {
|
|
57
|
+
method: request.method,
|
|
58
|
+
headers: request.headers,
|
|
59
|
+
body: bodyText === "" ? undefined : bodyText,
|
|
60
|
+
// node: undici / fetch may require duplex for streamed bodies;
|
|
61
|
+
// a string body does not.
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return route.handler(request, handlerArgs);
|
|
66
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
2
|
+
import type { JsonValue } from "../types.js";
|
|
3
|
+
|
|
4
|
+
type JsonRecord = Record<string, JsonValue>;
|
|
5
|
+
|
|
6
|
+
function asRecord(value: JsonValue | undefined): JsonRecord | undefined {
|
|
7
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
return value as JsonRecord;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Shallow-merge a wake patch into durable channel state. Null / undefined
|
|
15
|
+
* patch values leave the existing key alone so sticky fields survive.
|
|
16
|
+
*/
|
|
17
|
+
export function mergeChannelStateSkipNull(
|
|
18
|
+
current: JsonValue | undefined,
|
|
19
|
+
patch: JsonValue | undefined
|
|
20
|
+
): JsonValue | undefined {
|
|
21
|
+
if (patch === undefined) {
|
|
22
|
+
return current;
|
|
23
|
+
}
|
|
24
|
+
const currentRecord = asRecord(current);
|
|
25
|
+
const patchRecord = asRecord(patch);
|
|
26
|
+
if (currentRecord === undefined || patchRecord === undefined) {
|
|
27
|
+
return structuredClone(patch);
|
|
28
|
+
}
|
|
29
|
+
const merged = { ...currentRecord };
|
|
30
|
+
for (const [key, value] of Object.entries(patchRecord)) {
|
|
31
|
+
if (value !== null && value !== undefined) {
|
|
32
|
+
merged[key] = value;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return merged;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Apply handler mutations made against a turn snapshot without reverting
|
|
40
|
+
* unchanged fields refreshed by a later delivery. At a changed path, objects
|
|
41
|
+
* merge recursively, arrays and scalars replace, and deletions win.
|
|
42
|
+
*/
|
|
43
|
+
export function mergeChannelStateMutation(args: {
|
|
44
|
+
current: JsonValue | undefined;
|
|
45
|
+
before: JsonValue;
|
|
46
|
+
after: JsonValue;
|
|
47
|
+
}): JsonValue | undefined {
|
|
48
|
+
const { current, before, after } = args;
|
|
49
|
+
if (isDeepStrictEqual(before, after)) {
|
|
50
|
+
return current;
|
|
51
|
+
}
|
|
52
|
+
const currentRecord = asRecord(current);
|
|
53
|
+
const beforeRecord = asRecord(before);
|
|
54
|
+
const afterRecord = asRecord(after);
|
|
55
|
+
if (
|
|
56
|
+
currentRecord === undefined ||
|
|
57
|
+
beforeRecord === undefined ||
|
|
58
|
+
afterRecord === undefined
|
|
59
|
+
) {
|
|
60
|
+
return structuredClone(after);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const merged = structuredClone(currentRecord);
|
|
64
|
+
const keys = new Set([
|
|
65
|
+
...Object.keys(beforeRecord),
|
|
66
|
+
...Object.keys(afterRecord),
|
|
67
|
+
]);
|
|
68
|
+
for (const key of keys) {
|
|
69
|
+
const hadBefore = Object.hasOwn(beforeRecord, key);
|
|
70
|
+
const hasAfter = Object.hasOwn(afterRecord, key);
|
|
71
|
+
if (
|
|
72
|
+
hadBefore &&
|
|
73
|
+
hasAfter &&
|
|
74
|
+
isDeepStrictEqual(beforeRecord[key], afterRecord[key])
|
|
75
|
+
) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (!hasAfter) {
|
|
79
|
+
delete merged[key];
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const currentValue = merged[key];
|
|
83
|
+
const afterValue = afterRecord[key]!;
|
|
84
|
+
merged[key] =
|
|
85
|
+
hadBefore ||
|
|
86
|
+
(asRecord(currentValue) !== undefined &&
|
|
87
|
+
asRecord(afterValue) !== undefined)
|
|
88
|
+
? (mergeChannelStateMutation({
|
|
89
|
+
current: currentValue,
|
|
90
|
+
before: hadBefore ? beforeRecord[key]! : {},
|
|
91
|
+
after: afterValue,
|
|
92
|
+
}) as JsonValue)
|
|
93
|
+
: structuredClone(afterValue);
|
|
94
|
+
}
|
|
95
|
+
return merged;
|
|
96
|
+
}
|
package/src/internal/cli-ax.ts
CHANGED
|
@@ -23,6 +23,7 @@ import { lookupSessionContinuation, runChat } from "./chat-client.js";
|
|
|
23
23
|
import { ensureCursorLogin } from "./cli-cursor.js";
|
|
24
24
|
import { resolveDeployAuth } from "./cli-deploy.js";
|
|
25
25
|
import { resolveApiKey } from "./cursor/credentials.js";
|
|
26
|
+
import { DeployApiError } from "./deploy-client.js";
|
|
26
27
|
import { CLI_COMMAND_NAME, PACKAGE_NAME } from "./distribution.js";
|
|
27
28
|
import {
|
|
28
29
|
discoverEvals,
|
|
@@ -53,6 +54,7 @@ import {
|
|
|
53
54
|
resolveTemplate,
|
|
54
55
|
} from "./init-project.js";
|
|
55
56
|
import { offerInstallCursorSkills } from "./install-cursor-skills.js";
|
|
57
|
+
import { rejectV2ForLegacyCommand } from "./legacy-command-guard.js";
|
|
56
58
|
import { runLogsTail, sanitizeCustomerError } from "./logs-client.js";
|
|
57
59
|
import { mapPool } from "./map-pool.js";
|
|
58
60
|
import { openBrowser } from "./open-browser.js";
|
|
@@ -189,6 +191,13 @@ export interface AxCliOptions {
|
|
|
189
191
|
resume?: boolean;
|
|
190
192
|
/** Abort signal (tests / programmatic stop of `playground` proxy). */
|
|
191
193
|
signal?: AbortSignal;
|
|
194
|
+
/**
|
|
195
|
+
* `run --prod` / `session --prod --message`: hosted v2 session start on
|
|
196
|
+
* the agentsdk control plane. `--dry-run` marks the turn non-mutating;
|
|
197
|
+
* `--as-of` freezes the session clock at a timezone-bearing instant.
|
|
198
|
+
*/
|
|
199
|
+
dryRun?: boolean;
|
|
200
|
+
asOf?: string;
|
|
192
201
|
}
|
|
193
202
|
|
|
194
203
|
export interface ResolvedAgentTarget {
|
|
@@ -594,6 +603,15 @@ export async function cmdChat(options: AxCliOptions): Promise<number> {
|
|
|
594
603
|
}
|
|
595
604
|
|
|
596
605
|
export async function cmdRun(options: AxCliOptions): Promise<number> {
|
|
606
|
+
// Only a v2-shaped request tries the control plane; every v1 `run --prod`
|
|
607
|
+
// form (multi-turn, continuation token, local session id, --url) keeps its
|
|
608
|
+
// engine-alias path untouched.
|
|
609
|
+
if (options.prod === true && hostedSessionRejection(options) === undefined) {
|
|
610
|
+
const started = await cmdHostedSession(options);
|
|
611
|
+
if (started !== "fallback") {
|
|
612
|
+
return started;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
597
615
|
const messages = options.messages ?? [];
|
|
598
616
|
if (messages.length === 0) {
|
|
599
617
|
die("Provide --message (repeatable) or --messages-file");
|
|
@@ -703,6 +721,74 @@ export async function cmdRun(options: AxCliOptions): Promise<number> {
|
|
|
703
721
|
}
|
|
704
722
|
}
|
|
705
723
|
|
|
724
|
+
const HOSTED_SESSION_ID_RE = /^ses_[a-f0-9]{24}$/;
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* Hosted v2 session start: POST the agentsdk control plane, which hands the
|
|
728
|
+
* wake to Temporal (no resident engine, no `/engine/*` alias). `--session
|
|
729
|
+
* ses_…` resumes that exact session; there is no continuation token on
|
|
730
|
+
* this path. A 404 means the slug is not a v2 control-plane deployment, so
|
|
731
|
+
* `run --prod` falls back to the v1 engine alias.
|
|
732
|
+
*/
|
|
733
|
+
/** Why these options cannot be a hosted v2 session start, if they cannot. */
|
|
734
|
+
function hostedSessionRejection(options: AxCliOptions): string | undefined {
|
|
735
|
+
if (options.url !== undefined) {
|
|
736
|
+
return "--prod session start talks to the agentsdk control plane; do not pass --url";
|
|
737
|
+
}
|
|
738
|
+
const messages = options.messages ?? [];
|
|
739
|
+
if (messages.length !== 1 || messages[0]?.trim() === "") {
|
|
740
|
+
return "Provide exactly one --message (the wake). Multi-turn runs are local-only.";
|
|
741
|
+
}
|
|
742
|
+
if (trimNonEmpty(options.continuationToken) !== undefined) {
|
|
743
|
+
return "--continuation-token is not a hosted v2 identity; pass --session ses_… to resume a session";
|
|
744
|
+
}
|
|
745
|
+
const sessionId = trimNonEmpty(options.sessionId);
|
|
746
|
+
if (sessionId !== undefined && !HOSTED_SESSION_ID_RE.test(sessionId)) {
|
|
747
|
+
return `--session must be a hosted session id (ses_…), got ${sessionId}`;
|
|
748
|
+
}
|
|
749
|
+
return undefined;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
async function cmdHostedSession(
|
|
753
|
+
options: AxCliOptions
|
|
754
|
+
): Promise<number | "fallback"> {
|
|
755
|
+
const rejection = hostedSessionRejection(options);
|
|
756
|
+
if (rejection !== undefined) {
|
|
757
|
+
die(rejection);
|
|
758
|
+
}
|
|
759
|
+
const messages = options.messages ?? [];
|
|
760
|
+
const sessionId = trimNonEmpty(options.sessionId);
|
|
761
|
+
const auth = await resolveDeployAuth({
|
|
762
|
+
team: options.team,
|
|
763
|
+
apiKey: options.apiKey,
|
|
764
|
+
fetchImpl: options.fetchImpl,
|
|
765
|
+
});
|
|
766
|
+
if (typeof auth === "number") {
|
|
767
|
+
return auth;
|
|
768
|
+
}
|
|
769
|
+
const slug = resolveProdSlug(options);
|
|
770
|
+
try {
|
|
771
|
+
const accepted = await auth.client.startSession({
|
|
772
|
+
teamId: auth.teamId,
|
|
773
|
+
slug,
|
|
774
|
+
message: messages[0]!.trim(),
|
|
775
|
+
sessionId,
|
|
776
|
+
dryRun: options.dryRun,
|
|
777
|
+
asOf: options.asOf,
|
|
778
|
+
});
|
|
779
|
+
console.log(JSON.stringify(accepted, null, 2));
|
|
780
|
+
return 0;
|
|
781
|
+
} catch (error) {
|
|
782
|
+
if (error instanceof DeployApiError && error.status === 404) {
|
|
783
|
+
return "fallback";
|
|
784
|
+
}
|
|
785
|
+
process.stderr.write(
|
|
786
|
+
`${error instanceof Error ? error.message : String(error)}\n`
|
|
787
|
+
);
|
|
788
|
+
return 1;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
|
|
706
792
|
/**
|
|
707
793
|
* `agent-sdk call <tool>` — deterministic tool invocation over
|
|
708
794
|
* `POST /v1/tools/:toolName` (ephemeral local server unless --url).
|
|
@@ -1727,6 +1813,11 @@ export async function cmdLogs(options: AxCliOptions): Promise<number> {
|
|
|
1727
1813
|
if (typeof auth === "number") {
|
|
1728
1814
|
return auth;
|
|
1729
1815
|
}
|
|
1816
|
+
await rejectV2ForLegacyCommand({
|
|
1817
|
+
client: auth.client,
|
|
1818
|
+
teamId: auth.teamId,
|
|
1819
|
+
slug,
|
|
1820
|
+
});
|
|
1730
1821
|
await runLogsTail({
|
|
1731
1822
|
prod: true,
|
|
1732
1823
|
dir: options.dir,
|
|
@@ -1750,6 +1841,7 @@ export async function cmdLogs(options: AxCliOptions): Promise<number> {
|
|
|
1750
1841
|
apiKey: options.apiKey,
|
|
1751
1842
|
fetchImpl: options.fetchImpl,
|
|
1752
1843
|
err: () => {},
|
|
1844
|
+
skipLegacyArchitectureCheck: true,
|
|
1753
1845
|
});
|
|
1754
1846
|
return { agentUrl: target.agentUrl, headers: target.headers };
|
|
1755
1847
|
},
|
|
@@ -1864,9 +1956,26 @@ export async function cmdSession(
|
|
|
1864
1956
|
sessionId: string | undefined,
|
|
1865
1957
|
options: AxCliOptions
|
|
1866
1958
|
): Promise<number> {
|
|
1959
|
+
if (options.prod === true && (options.messages?.length ?? 0) > 0) {
|
|
1960
|
+
// `session --prod [ses_…] --message` is the same hosted start as
|
|
1961
|
+
// `run --prod`, with the positional id as the session to resume. Unlike
|
|
1962
|
+
// `run`, a 404 is an error here: this form is explicitly v2.
|
|
1963
|
+
const started = await cmdHostedSession({
|
|
1964
|
+
...options,
|
|
1965
|
+
sessionId: trimNonEmpty(sessionId) ?? options.sessionId,
|
|
1966
|
+
});
|
|
1967
|
+
if (started === "fallback") {
|
|
1968
|
+
process.stderr.write(
|
|
1969
|
+
`Hosted session start returned 404 for slug ${resolveProdSlug(options)}: deploy a running AgentSDK v2 agent, or use \`${CLI_COMMAND_NAME} run --prod\` (falls back to the v1 engine).\n`
|
|
1970
|
+
);
|
|
1971
|
+
return 1;
|
|
1972
|
+
}
|
|
1973
|
+
return started;
|
|
1974
|
+
}
|
|
1867
1975
|
if (sessionId === undefined || sessionId.trim() === "") {
|
|
1868
1976
|
process.stderr.write(
|
|
1869
|
-
`Usage: ${CLI_COMMAND_NAME} session <sessionId> [--url | --prod] [--json | --text | --events] [--out <file.ndjson>]\n`
|
|
1977
|
+
`Usage: ${CLI_COMMAND_NAME} session <sessionId> [--url | --prod] [--json | --text | --events] [--out <file.ndjson>]\n` +
|
|
1978
|
+
` or: ${CLI_COMMAND_NAME} session --prod [<sessionId>] --slug <slug> --message "…" [--dry-run] [--as-of <instant>]\n`
|
|
1870
1979
|
);
|
|
1871
1980
|
return 2;
|
|
1872
1981
|
}
|