@cursor/july 0.1.15 → 0.1.17
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 +9 -4
- package/dist/bin/agent-serve.js +60 -2
- package/dist/channels/slack/api.d.ts.map +1 -1
- package/dist/channels/slack/api.js +13 -3
- package/dist/channels/slack/auth.d.ts +7 -1
- package/dist/channels/slack/auth.d.ts.map +1 -1
- package/dist/channels/slack/auth.js +14 -0
- package/dist/channels/slack/blocks.d.ts +61 -0
- package/dist/channels/slack/blocks.d.ts.map +1 -0
- package/dist/channels/slack/blocks.js +90 -0
- package/dist/channels/slack/defaults.d.ts +2 -0
- package/dist/channels/slack/defaults.d.ts.map +1 -1
- package/dist/channels/slack/defaults.js +29 -3
- package/dist/channels/slack/dispatch.d.ts +3 -17
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/index.d.ts +1 -0
- package/dist/channels/slack/index.d.ts.map +1 -1
- package/dist/channels/slack/index.js +1 -0
- package/dist/channels/slack/interactive.d.ts +39 -3
- package/dist/channels/slack/interactive.d.ts.map +1 -1
- package/dist/channels/slack/interactive.js +152 -7
- package/dist/channels/slack/manifest.d.ts +3 -0
- package/dist/channels/slack/manifest.d.ts.map +1 -1
- package/dist/channels/slack/manifest.js +3 -0
- package/dist/channels/slack/setup.d.ts +4 -1
- package/dist/channels/slack/setup.d.ts.map +1 -1
- package/dist/channels/slack/setup.js +16 -2
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +24 -3
- package/dist/channels/slack/types.d.ts +77 -0
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/connections.d.ts +4 -1
- package/dist/connections.d.ts.map +1 -1
- package/dist/connections.js +4 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +3 -3
- package/dist/docs/assets/{app.DpdGZTLe.js → app.BbEXr2KO.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DCBsrfwP.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.IB-0QqIT.js → VPLocalSearchBox.DBEi4HDp.js} +1 -1
- package/dist/docs/assets/chunks/{theme.-fYBoNtR.js → theme.DGfptcHw.js} +2 -2
- package/dist/docs/assets/{example-agents_index.md.BPXjUtRB.js → example-agents_index.md.D2PEVSXl.js} +2 -2
- package/dist/docs/assets/{example-agents_index.md.BPXjUtRB.lean.js → example-agents_index.md.D2PEVSXl.lean.js} +1 -1
- package/dist/docs/assets/example-agents_security-reviewer.md.BZSNycMf.js +19 -0
- package/dist/docs/assets/{example-agents_security-reviewer.md.CE9v-aKm.lean.js → example-agents_security-reviewer.md.BZSNycMf.lean.js} +1 -1
- package/dist/docs/assets/{guides_mcp-oauth.md.DqmEOQY1.js → guides_mcp-oauth.md.Dd8EgSem.js} +3 -2
- package/dist/docs/assets/{guides_mcp-oauth.md.DqmEOQY1.lean.js → guides_mcp-oauth.md.Dd8EgSem.lean.js} +1 -1
- package/dist/docs/assets/{guides_slack.md.FWale6Ip.js → guides_slack.md.D_xFKlnF.js} +1 -1
- package/dist/docs/assets/{reference_connections.md.CsSr3rDz.js → reference_connections.md.B9Q3TOve.js} +12 -5
- package/dist/docs/assets/{reference_connections.md.CsSr3rDz.lean.js → reference_connections.md.B9Q3TOve.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +3 -3
- package/dist/docs/concepts.html +3 -3
- package/dist/docs/deployment.html +3 -3
- package/dist/docs/evals.html +3 -3
- package/dist/docs/example-agents/approval-buddy.html +3 -3
- package/dist/docs/example-agents/benny.html +3 -3
- package/dist/docs/example-agents/bugbot.html +3 -3
- package/dist/docs/example-agents/codebase-wiki.html +3 -3
- package/dist/docs/example-agents/codeowners-review.html +3 -3
- package/dist/docs/example-agents/concierge.html +3 -3
- package/dist/docs/example-agents/fsd.html +3 -3
- package/dist/docs/example-agents/index.html +5 -5
- package/dist/docs/example-agents/knowledge-base.html +3 -3
- package/dist/docs/example-agents/oncall.html +3 -3
- package/dist/docs/example-agents/security-reviewer.html +14 -14
- package/dist/docs/example-agents/slack-agent.html +3 -3
- package/dist/docs/example-agents/weather-agent.html +3 -3
- package/dist/docs/guides/agent-to-agent.html +3 -3
- package/dist/docs/guides/cloud-runtime.html +3 -3
- package/dist/docs/guides/github.html +3 -3
- package/dist/docs/guides/human-in-the-loop.html +3 -3
- package/dist/docs/guides/mcp-oauth.html +6 -5
- package/dist/docs/guides/slack.html +5 -5
- package/dist/docs/guides/webhooks.html +3 -3
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +3 -3
- package/dist/docs/index.html +3 -3
- package/dist/docs/quickstart.html +3 -3
- package/dist/docs/reference/agent-config.html +3 -3
- package/dist/docs/reference/channels.html +3 -3
- package/dist/docs/reference/cli.html +3 -3
- package/dist/docs/reference/connections.html +14 -7
- package/dist/docs/reference/hooks.html +3 -3
- package/dist/docs/reference/http-api.html +3 -3
- package/dist/docs/reference/instructions.html +3 -3
- package/dist/docs/reference/playground.html +3 -3
- package/dist/docs/reference/project-layout.html +3 -3
- package/dist/docs/reference/prompt.html +3 -3
- package/dist/docs/reference/schedules.html +3 -3
- package/dist/docs/reference/sessions.html +3 -3
- package/dist/docs/reference/skills.html +3 -3
- package/dist/docs/reference/subagents.html +3 -3
- package/dist/docs/reference/tools.html +3 -3
- package/dist/docs/scaffolding-agents.html +3 -3
- package/dist/docs/storage.html +3 -3
- package/dist/docs/troubleshooting.html +3 -3
- package/dist/evals/assertions.d.ts +190 -0
- package/dist/evals/assertions.d.ts.map +1 -0
- package/dist/evals/assertions.js +461 -0
- package/dist/evals/expect.d.ts +95 -0
- package/dist/evals/expect.d.ts.map +1 -0
- package/dist/evals/expect.js +194 -0
- package/dist/evals/judge.d.ts +133 -0
- package/dist/evals/judge.d.ts.map +1 -0
- package/dist/evals/judge.js +228 -0
- package/dist/evals/loaders.d.ts +51 -0
- package/dist/evals/loaders.d.ts.map +1 -0
- package/dist/evals/loaders.js +124 -0
- package/dist/evals/matchers.d.ts +95 -0
- package/dist/evals/matchers.d.ts.map +1 -0
- package/dist/evals/matchers.js +145 -0
- package/dist/evals/reporters.d.ts +70 -0
- package/dist/evals/reporters.d.ts.map +1 -0
- package/dist/evals/reporters.js +221 -0
- package/dist/evals/results.d.ts +103 -0
- package/dist/evals/results.d.ts.map +1 -0
- package/dist/evals/results.js +28 -0
- package/dist/evals/run-facts.d.ts +77 -0
- package/dist/evals/run-facts.d.ts.map +1 -0
- package/dist/evals/run-facts.js +187 -0
- package/dist/evals.d.ts +136 -31
- package/dist/evals.d.ts.map +1 -1
- package/dist/evals.js +76 -12
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/internal/cli-ax.d.ts +27 -0
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +90 -12
- package/dist/internal/cli-mcp.d.ts +8 -0
- package/dist/internal/cli-mcp.d.ts.map +1 -1
- package/dist/internal/cli-mcp.js +98 -0
- package/dist/internal/cli-slack.d.ts +29 -1
- package/dist/internal/cli-slack.d.ts.map +1 -1
- package/dist/internal/cli-slack.js +777 -12
- package/dist/internal/deploy-client.d.ts +7 -0
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +9 -0
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +29 -9
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +2 -0
- package/dist/internal/eval-judge-model.d.ts +59 -0
- package/dist/internal/eval-judge-model.d.ts.map +1 -0
- package/dist/internal/eval-judge-model.js +131 -0
- package/dist/internal/eval-run-store.d.ts.map +1 -1
- package/dist/internal/eval-run-store.js +12 -20
- package/dist/internal/eval-runner.d.ts +31 -25
- package/dist/internal/eval-runner.d.ts.map +1 -1
- package/dist/internal/eval-runner.js +332 -128
- package/dist/internal/evals-client.d.ts.map +1 -1
- package/dist/internal/evals-client.js +7 -3
- package/dist/internal/local-env.d.ts +8 -0
- package/dist/internal/local-env.d.ts.map +1 -0
- package/dist/internal/local-env.js +47 -0
- package/dist/internal/playground/static.d.ts +3 -0
- package/dist/internal/playground/static.d.ts.map +1 -1
- package/dist/internal/playground/static.js +3 -0
- package/dist/internal/run-client.d.ts +15 -0
- package/dist/internal/run-client.d.ts.map +1 -1
- package/dist/internal/run-client.js +3 -2
- package/dist/internal/sdk-runner.d.ts +36 -1
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +65 -25
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +10 -1
- package/dist/internal/session-engine.d.ts +18 -5
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +72 -19
- package/dist/internal/slack-provision-client.d.ts +130 -0
- package/dist/internal/slack-provision-client.d.ts.map +1 -0
- package/dist/internal/slack-provision-client.js +284 -0
- package/dist/playground/assets/cursor-icons-16-f_W_ogc-.woff2 +0 -0
- package/dist/playground/assets/index-CidizGZv.css +1 -0
- package/dist/playground/assets/index-DNqirsrK.js +85 -0
- package/dist/playground/index.html +2 -2
- package/dist/types.d.ts +70 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +26 -0
- package/docs/example-agents/index.md +1 -1
- package/docs/example-agents/security-reviewer.md +23 -21
- package/docs/guides/mcp-oauth.md +10 -5
- package/docs/guides/slack.md +3 -2
- package/docs/reference/connections.md +28 -18
- package/package.json +20 -2
- package/skills/create-agent/SKILL.md +15 -7
- package/skills/debug/SKILL.md +3 -1
- package/skills/mcp-auth/SKILL.md +1 -1
- package/skills/setup-slack/SKILL.md +173 -11
- package/src/bin/agent-serve.ts +63 -2
- package/src/channels/slack/api.ts +16 -3
- package/src/channels/slack/auth.ts +16 -1
- package/src/channels/slack/blocks.ts +139 -0
- package/src/channels/slack/defaults.ts +32 -4
- package/src/channels/slack/dispatch.ts +3 -18
- package/src/channels/slack/index.ts +1 -0
- package/src/channels/slack/interactive.ts +183 -19
- package/src/channels/slack/manifest.ts +3 -0
- package/src/channels/slack/setup.ts +16 -2
- package/src/channels/slack/slack-channel.ts +37 -6
- package/src/channels/slack/types.ts +89 -0
- package/src/connections.ts +4 -1
- package/src/evals/assertions.ts +704 -0
- package/src/evals/expect.ts +272 -0
- package/src/evals/judge.ts +346 -0
- package/src/evals/loaders.ts +113 -0
- package/src/evals/matchers.ts +246 -0
- package/src/evals/reporters.ts +274 -0
- package/src/evals/results.ts +132 -0
- package/src/evals/run-facts.ts +274 -0
- package/src/evals.ts +249 -42
- package/src/index.ts +2 -0
- package/src/internal/cli-ax.ts +134 -15
- package/src/internal/cli-mcp.ts +118 -5
- package/src/internal/cli-slack.ts +993 -15
- package/src/internal/deploy-client.ts +10 -0
- package/src/internal/discovery.ts +31 -5
- package/src/internal/distribution.ts +2 -0
- package/src/internal/eval-judge-model.ts +148 -0
- package/src/internal/eval-run-store.ts +10 -14
- package/src/internal/eval-runner.ts +466 -215
- package/src/internal/evals-client.ts +14 -2
- package/src/internal/local-env.ts +53 -0
- package/src/internal/playground/static.ts +3 -0
- package/src/internal/run-client.ts +17 -1
- package/src/internal/sdk-runner.ts +103 -22
- package/src/internal/server.ts +10 -0
- package/src/internal/session-engine.ts +89 -21
- package/src/internal/slack-provision-client.ts +438 -0
- package/src/types.ts +92 -4
- package/dist/channels/slack/post-update-delivery.d.ts +0 -85
- package/dist/channels/slack/post-update-delivery.d.ts.map +0 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.D2d8CfFS.js +0 -1
- package/dist/docs/assets/example-agents_security-reviewer.md.CE9v-aKm.js +0 -19
- package/dist/internal/json-dir-store.d.ts +0 -32
- package/dist/internal/json-dir-store.d.ts.map +0 -1
- package/dist/internal/persistence-coordinator.d.ts +0 -127
- package/dist/internal/persistence-coordinator.d.ts.map +0 -1
- package/dist/persistence.d.ts +0 -184
- package/dist/persistence.d.ts.map +0 -1
- package/dist/playground/assets/cursor-icons-16-CQ50JpfO.woff2 +0 -0
- package/dist/playground/assets/index-BVMOoiek.js +0 -86
- package/dist/playground/assets/index-DYmP4b--.css +0 -1
- /package/dist/docs/assets/{guides_slack.md.FWale6Ip.lean.js → guides_slack.md.D_xFKlnF.lean.js} +0 -0
|
@@ -8,7 +8,11 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { EvalRunSnapshot } from "../evals.js";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
expandSummarySuiteSelection,
|
|
13
|
+
isEvalSummaryCase,
|
|
14
|
+
matchesEvalIdFilter,
|
|
15
|
+
} from "../evals.js";
|
|
12
16
|
import type { EvalRunResult } from "./eval-runner.js";
|
|
13
17
|
import { mergeRequestHeaders, type RequestAuth } from "./request-headers.js";
|
|
14
18
|
|
|
@@ -170,7 +174,10 @@ export async function listRemoteEvals(args: {
|
|
|
170
174
|
(e.tags ?? []).some((t) => args.tags!.includes(t))
|
|
171
175
|
);
|
|
172
176
|
}
|
|
173
|
-
|
|
177
|
+
// Match server-side filterDiscoveredEvals: summary → whole file.
|
|
178
|
+
return expandSummarySuiteSelection(listed, selected, (e) =>
|
|
179
|
+
isEvalSummaryCase(e.tags)
|
|
180
|
+
);
|
|
174
181
|
}
|
|
175
182
|
|
|
176
183
|
export async function startRemoteEvalRun(args: {
|
|
@@ -481,6 +488,11 @@ export function snapshotToEvalRunResults(
|
|
|
481
488
|
path: c.fileId,
|
|
482
489
|
...(c.description === undefined ? {} : { description: c.description }),
|
|
483
490
|
ok: c.ok === true,
|
|
491
|
+
// Older serve processes send no verdict; infer it from `ok` so a remote
|
|
492
|
+
// batch still prints and reports consistently.
|
|
493
|
+
verdict: c.verdict ?? (c.ok === true ? "passed" : "failed"),
|
|
494
|
+
...(c.skipReason === undefined ? {} : { skipReason: c.skipReason }),
|
|
495
|
+
...(c.metadata === undefined ? {} : { metadata: c.metadata }),
|
|
484
496
|
assertions: c.assertions ?? [],
|
|
485
497
|
...(c.sessionId === undefined ? {} : { sessionId: c.sessionId }),
|
|
486
498
|
inputs: c.inputs ?? [],
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loads an agent directory's `.env.local` (and each immediate child's,
|
|
3
|
+
* for multi-agent hosts) into process.env. Real env always wins —
|
|
4
|
+
* `process.loadEnvFile` never overrides. Values a previous call loaded are
|
|
5
|
+
* refreshed, so a reload picks up edits made since.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
|
|
11
|
+
// Not real env, so a reload has to clear them first: loadEnvFile skips
|
|
12
|
+
// anything already in process.env, which would pin the first-boot values.
|
|
13
|
+
let fileLoadedKeys = new Set<string>();
|
|
14
|
+
|
|
15
|
+
export function loadAgentLocalEnv(dir: string): string[] {
|
|
16
|
+
for (const key of fileLoadedKeys) {
|
|
17
|
+
delete process.env[key];
|
|
18
|
+
}
|
|
19
|
+
const realKeys = new Set(Object.keys(process.env));
|
|
20
|
+
const loaded: string[] = [];
|
|
21
|
+
const tryLoad = (path: string): void => {
|
|
22
|
+
if (!existsSync(path)) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
process.loadEnvFile(path);
|
|
26
|
+
loaded.push(path);
|
|
27
|
+
};
|
|
28
|
+
const trackLoadedKeys = (): void => {
|
|
29
|
+
fileLoadedKeys = new Set(
|
|
30
|
+
Object.keys(process.env).filter((key) => !realKeys.has(key))
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
tryLoad(join(dir, ".env.local"));
|
|
35
|
+
|
|
36
|
+
let children: string[];
|
|
37
|
+
try {
|
|
38
|
+
children = readdirSync(dir, { withFileTypes: true })
|
|
39
|
+
.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."))
|
|
40
|
+
.map((entry) => entry.name);
|
|
41
|
+
} catch {
|
|
42
|
+
trackLoadedKeys();
|
|
43
|
+
return loaded;
|
|
44
|
+
}
|
|
45
|
+
for (const child of children) {
|
|
46
|
+
if (child === "node_modules") {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
tryLoad(join(dir, child, ".env.local"));
|
|
50
|
+
}
|
|
51
|
+
trackLoadedKeys();
|
|
52
|
+
return loaded;
|
|
53
|
+
}
|
|
@@ -52,6 +52,9 @@ export function playgroundBundleExists(): boolean {
|
|
|
52
52
|
* The SPA `index.html` with a `<base href>` injected so its relative asset
|
|
53
53
|
* URLs resolve under the mount point (`/playground/` or `/<slug>/playground/`).
|
|
54
54
|
* Falls back to a helpful placeholder page when the bundle is missing.
|
|
55
|
+
* The hosted alias performs the same injection for its central copy
|
|
56
|
+
* (`backend/server/src/agentServe/aliasPlaygroundShell.ts`) — keep them
|
|
57
|
+
* behaviorally aligned.
|
|
55
58
|
*/
|
|
56
59
|
export function renderPlaygroundIndexHtml(baseHref: string): string {
|
|
57
60
|
if (!playgroundBundleExists()) {
|
|
@@ -46,6 +46,11 @@ export interface RunSessionResult {
|
|
|
46
46
|
events: SessionEvent[];
|
|
47
47
|
trajectory: Trajectory;
|
|
48
48
|
ok: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Index to pass as `startIndex` on the next call for this session. Undefined
|
|
51
|
+
* when the turn streamed nothing, in which case reuse the previous value.
|
|
52
|
+
*/
|
|
53
|
+
nextEventIndex?: number;
|
|
49
54
|
}
|
|
50
55
|
|
|
51
56
|
const TERMINAL_TYPES = new Set([
|
|
@@ -63,6 +68,16 @@ export async function runSession(
|
|
|
63
68
|
messages: string[];
|
|
64
69
|
sessionId?: string;
|
|
65
70
|
continuationToken?: string;
|
|
71
|
+
/**
|
|
72
|
+
* First event index to read when resuming an existing session.
|
|
73
|
+
*
|
|
74
|
+
* The stream replays from `startIndex` and this client stops at the first
|
|
75
|
+
* terminal event, so a caller that sends several turns across separate
|
|
76
|
+
* `runSession` calls **must** pass the index after its last consumed event
|
|
77
|
+
* (see {@link RunSessionResult.nextEventIndex}). Omitting it replays the
|
|
78
|
+
* earlier turn and returns those stale events instead of the new turn's.
|
|
79
|
+
*/
|
|
80
|
+
startIndex?: number;
|
|
66
81
|
/** Applied only when creating a new session (first message). */
|
|
67
82
|
workspaceDir?: string;
|
|
68
83
|
/** Applied only when creating a new session (first message). */
|
|
@@ -78,7 +93,7 @@ export async function runSession(
|
|
|
78
93
|
let sessionId = options.sessionId;
|
|
79
94
|
let continuationToken = options.continuationToken;
|
|
80
95
|
const allEvents: SessionEvent[] = [];
|
|
81
|
-
let nextEventIndex = 0;
|
|
96
|
+
let nextEventIndex = options.startIndex ?? 0;
|
|
82
97
|
|
|
83
98
|
const auth: RequestAuth = {
|
|
84
99
|
bearerToken: options.bearerToken,
|
|
@@ -126,6 +141,7 @@ export async function runSession(
|
|
|
126
141
|
events: allEvents,
|
|
127
142
|
trajectory,
|
|
128
143
|
ok: trajectory.ok,
|
|
144
|
+
nextEventIndex: allEvents.length === 0 ? undefined : nextEventIndex,
|
|
129
145
|
};
|
|
130
146
|
}
|
|
131
147
|
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
type AgentDefinition,
|
|
13
13
|
type AgentOptions,
|
|
14
14
|
type CloudAgentOptions,
|
|
15
|
+
createAgentPlatform,
|
|
15
16
|
JsonlLocalAgentStore,
|
|
16
17
|
type LocalAgentStore,
|
|
17
18
|
type ModelSelection,
|
|
@@ -30,9 +31,48 @@ import {
|
|
|
30
31
|
/** Internal seam between the session engine and the Cursor SDK harness. */
|
|
31
32
|
export interface AgentRunner {
|
|
32
33
|
runTurn(request: RunnerTurnRequest): Promise<TurnOutcome>;
|
|
34
|
+
/**
|
|
35
|
+
* Resolve a local workspace before any session needs it, so the first turn
|
|
36
|
+
* is not the one that pays for it. Best effort, hence the reported outcome:
|
|
37
|
+
* a runner that cannot warm must say so rather than let a caller log
|
|
38
|
+
* success over a workspace that was never touched.
|
|
39
|
+
*/
|
|
40
|
+
prewarm?(request: RunnerPrewarmRequest): Promise<PrewarmOutcome>;
|
|
33
41
|
dispose?(): Promise<void>;
|
|
34
42
|
}
|
|
35
43
|
|
|
44
|
+
/**
|
|
45
|
+
* The parts of a turn that decide which local executor it runs on. Must stay
|
|
46
|
+
* a subset of {@link RunnerTurnRequest} — anything that disagrees warms an
|
|
47
|
+
* executor no turn will ask for.
|
|
48
|
+
*/
|
|
49
|
+
export type RunnerPrewarmRequest = Pick<
|
|
50
|
+
RunnerTurnRequest,
|
|
51
|
+
| "workspaceDir"
|
|
52
|
+
| "runnerStateDir"
|
|
53
|
+
| "sandbox"
|
|
54
|
+
| "mcpServers"
|
|
55
|
+
| "subagents"
|
|
56
|
+
| "model"
|
|
57
|
+
>;
|
|
58
|
+
|
|
59
|
+
/** Whether a workspace was actually resolved, and why not when it wasn't. */
|
|
60
|
+
export type PrewarmOutcome =
|
|
61
|
+
| { readonly warmed: true }
|
|
62
|
+
| { readonly warmed: false; readonly reason: string };
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* `CursorAgentPlatform.prewarmLocalWorkspace` as of `@cursor/sdk` 1.0.26.
|
|
66
|
+
* Declared structurally so this keeps building — and reports that it skipped
|
|
67
|
+
* warming — against the pinned older SDK. Delete once the pin passes that
|
|
68
|
+
* version.
|
|
69
|
+
*/
|
|
70
|
+
type MaybePrewarmingPlatform = {
|
|
71
|
+
prewarmLocalWorkspace?: (
|
|
72
|
+
options: AgentOptions
|
|
73
|
+
) => Promise<() => Promise<void>>;
|
|
74
|
+
};
|
|
75
|
+
|
|
36
76
|
export interface CursorSdkRunnerOptions {
|
|
37
77
|
/** Cursor API key. Defaults to the `CURSOR_API_KEY` environment variable. */
|
|
38
78
|
apiKey?: string;
|
|
@@ -49,6 +89,7 @@ export interface CursorSdkRunnerOptions {
|
|
|
49
89
|
export class CursorSdkRunner implements AgentRunner {
|
|
50
90
|
private readonly agents = new Map<string, Promise<SDKAgent>>();
|
|
51
91
|
private readonly stores = new Map<string, Promise<LocalAgentStore>>();
|
|
92
|
+
private readonly prewarmReleases: Array<() => Promise<void>> = [];
|
|
52
93
|
|
|
53
94
|
constructor(private readonly options: CursorSdkRunnerOptions = {}) {}
|
|
54
95
|
|
|
@@ -112,7 +153,31 @@ export class CursorSdkRunner implements AgentRunner {
|
|
|
112
153
|
}
|
|
113
154
|
}
|
|
114
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Build the local executor for a workspace before any session asks for it.
|
|
158
|
+
* The warmed executor is reference counted, so the lease is held until
|
|
159
|
+
* {@link dispose}; dropping it early would tear the workspace back down.
|
|
160
|
+
*/
|
|
161
|
+
async prewarm(request: RunnerPrewarmRequest): Promise<PrewarmOutcome> {
|
|
162
|
+
const platform = (await createAgentPlatform()) as MaybePrewarmingPlatform;
|
|
163
|
+
if (typeof platform.prewarmLocalWorkspace !== "function") {
|
|
164
|
+
// Older @cursor/sdk. Turns still work; they just pay on the first one.
|
|
165
|
+
return {
|
|
166
|
+
warmed: false,
|
|
167
|
+
reason:
|
|
168
|
+
"the installed @cursor/sdk has no prewarmLocalWorkspace; upgrade it to warm the workspace before the first turn",
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
const release = await platform.prewarmLocalWorkspace(
|
|
172
|
+
await this.buildLocalOptions(request)
|
|
173
|
+
);
|
|
174
|
+
this.prewarmReleases.push(release);
|
|
175
|
+
return { warmed: true };
|
|
176
|
+
}
|
|
177
|
+
|
|
115
178
|
async dispose(): Promise<void> {
|
|
179
|
+
const releases = this.prewarmReleases.splice(0);
|
|
180
|
+
await Promise.allSettled(releases.map((release) => release()));
|
|
116
181
|
const agents = await Promise.allSettled([...this.agents.values()]);
|
|
117
182
|
this.agents.clear();
|
|
118
183
|
for (const settled of agents) {
|
|
@@ -134,47 +199,61 @@ export class CursorSdkRunner implements AgentRunner {
|
|
|
134
199
|
return agentPromise;
|
|
135
200
|
}
|
|
136
201
|
|
|
137
|
-
private
|
|
202
|
+
private requireApiKey(): string {
|
|
138
203
|
const apiKey = this.options.apiKey ?? process.env.CURSOR_API_KEY;
|
|
139
204
|
if (apiKey === undefined || apiKey.trim() === "") {
|
|
140
205
|
throw new Error(
|
|
141
206
|
"The Cursor SDK runner requires a Cursor credential: run `agent-serve login`, set CURSOR_API_KEY, or pass serve({ apiKey })."
|
|
142
207
|
);
|
|
143
208
|
}
|
|
144
|
-
|
|
209
|
+
return apiKey;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
private buildSharedOptions(
|
|
213
|
+
request: RunnerPrewarmRequest
|
|
214
|
+
): Pick<AgentOptions, "model" | "apiKey" | "mcpServers" | "agents"> {
|
|
145
215
|
const subagents = toAgentDefinitions(request.subagents);
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
> = {
|
|
150
|
-
model: toModelSelection(model),
|
|
151
|
-
apiKey,
|
|
216
|
+
return {
|
|
217
|
+
model: toModelSelection(request.model ?? DEFAULT_MODEL),
|
|
218
|
+
apiKey: this.requireApiKey(),
|
|
152
219
|
...(Object.keys(request.mcpServers).length > 0
|
|
153
220
|
? { mcpServers: request.mcpServers as AgentOptions["mcpServers"] }
|
|
154
221
|
: {}),
|
|
155
222
|
...(subagents === undefined ? {} : { agents: subagents }),
|
|
156
223
|
};
|
|
157
|
-
|
|
158
|
-
if (request.runtime === "cloud") {
|
|
159
|
-
return this.openCloudAgent(request, shared);
|
|
160
|
-
}
|
|
161
|
-
return this.openLocalAgent(request, shared);
|
|
162
224
|
}
|
|
163
225
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
226
|
+
/**
|
|
227
|
+
* The options that decide which local executor a turn runs on. Prewarming
|
|
228
|
+
* goes through here too, so a warmed workspace is the one turns land on.
|
|
229
|
+
*/
|
|
230
|
+
private async buildLocalOptions(
|
|
231
|
+
request: RunnerPrewarmRequest
|
|
232
|
+
): Promise<AgentOptions> {
|
|
168
233
|
const store =
|
|
169
234
|
this.options.store ?? (await this.getStore(request.runnerStateDir));
|
|
170
|
-
|
|
171
|
-
...
|
|
235
|
+
return {
|
|
236
|
+
...this.buildSharedOptions(request),
|
|
172
237
|
local: {
|
|
173
238
|
cwd: request.workspaceDir,
|
|
174
239
|
settingSources: ["project"],
|
|
175
240
|
store,
|
|
241
|
+
...(request.sandbox === true
|
|
242
|
+
? { sandboxOptions: { enabled: true } }
|
|
243
|
+
: {}),
|
|
176
244
|
},
|
|
177
245
|
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private async openAgent(request: RunnerTurnRequest): Promise<SDKAgent> {
|
|
249
|
+
if (request.runtime === "cloud") {
|
|
250
|
+
return this.openCloudAgent(request, this.buildSharedOptions(request));
|
|
251
|
+
}
|
|
252
|
+
return this.openLocalAgent(request);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
private async openLocalAgent(request: RunnerTurnRequest): Promise<SDKAgent> {
|
|
256
|
+
const options = await this.buildLocalOptions(request);
|
|
178
257
|
|
|
179
258
|
if (request.isFirstTurn || request.sdkAgentId === undefined) {
|
|
180
259
|
return Agent.create({
|
|
@@ -341,11 +420,13 @@ function toTurnOutcome(result: RunResult): TurnOutcome {
|
|
|
341
420
|
return {
|
|
342
421
|
status: result.status,
|
|
343
422
|
...(result.result === undefined ? {} : { result: result.result }),
|
|
344
|
-
// The published RunResult carries no structured error details; failures
|
|
345
|
-
// thrown by send()/wait() surface through the engine's catch instead.
|
|
346
423
|
...(result.status === "error"
|
|
347
|
-
? { errorMessage:
|
|
424
|
+
? { errorMessage: runErrorMessage(result) }
|
|
348
425
|
: {}),
|
|
349
426
|
...(usage === undefined ? {} : { usage }),
|
|
350
427
|
};
|
|
351
428
|
}
|
|
429
|
+
|
|
430
|
+
export function runErrorMessage(result: Pick<RunResult, "error">): string {
|
|
431
|
+
return result.error?.message?.trim() || "The Cursor agent run failed";
|
|
432
|
+
}
|
package/src/internal/server.ts
CHANGED
|
@@ -385,6 +385,12 @@ export async function startServer(
|
|
|
385
385
|
// channel or route can address them (continuation tokens must resolve).
|
|
386
386
|
await engine.restoreStoredSessions();
|
|
387
387
|
|
|
388
|
+
// Resolve a shared harness workspace in the background: on a large repo
|
|
389
|
+
// that takes seconds, and a deployment coming online should absorb it
|
|
390
|
+
// rather than whoever sends the first message. No-op unless the agent
|
|
391
|
+
// pins `local.workspaceDir`.
|
|
392
|
+
void engine.prewarmSharedWorkspace();
|
|
393
|
+
|
|
388
394
|
if (!githubAuthWarmed) {
|
|
389
395
|
// Soft ambient GitHub auth check before channel onStart (which may call
|
|
390
396
|
// host.github). Missing auth is a warning only — serve still starts.
|
|
@@ -911,6 +917,10 @@ function mountAgentRoutes(
|
|
|
911
917
|
name: project.name,
|
|
912
918
|
dev,
|
|
913
919
|
basePath: base,
|
|
920
|
+
// Framework version, for SPA/engine skew observability now that the
|
|
921
|
+
// hosted playground shell is served centrally (backend deploys)
|
|
922
|
+
// while engines stay on their install-time version.
|
|
923
|
+
version: packageVersion(),
|
|
914
924
|
...(multi ? { indexPath: "/" } : {}),
|
|
915
925
|
}),
|
|
916
926
|
});
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
type PendingApproval,
|
|
40
40
|
type ReceiveInput,
|
|
41
41
|
type ResolvedAgent,
|
|
42
|
+
type RunnerSubagent,
|
|
42
43
|
type RunnerTurnRequest,
|
|
43
44
|
type SendMessageFn,
|
|
44
45
|
type SendMessageOptions,
|
|
@@ -957,13 +958,18 @@ export class SessionEngine {
|
|
|
957
958
|
}
|
|
958
959
|
|
|
959
960
|
/**
|
|
960
|
-
* Local harness cwd for a session: per-send override, else
|
|
961
|
-
* `<agent.local.cwd>/<sessionId>`, else
|
|
961
|
+
* Local harness cwd for a session: per-send override, else the shared
|
|
962
|
+
* `agent.local.workspaceDir`, else `<agent.local.cwd>/<sessionId>`, else
|
|
963
|
+
* the durable session workspace.
|
|
962
964
|
*/
|
|
963
965
|
harnessCwd(record: SessionRecord): string {
|
|
964
966
|
if (record.workspaceOverride !== undefined) {
|
|
965
967
|
return record.workspaceOverride;
|
|
966
968
|
}
|
|
969
|
+
const shared = this.project.agent.local?.workspaceDir;
|
|
970
|
+
if (shared !== undefined) {
|
|
971
|
+
return shared;
|
|
972
|
+
}
|
|
967
973
|
const agentCwd = this.project.agent.local?.cwd;
|
|
968
974
|
if (agentCwd !== undefined) {
|
|
969
975
|
return join(agentCwd, record.sessionId);
|
|
@@ -972,12 +978,64 @@ export class SessionEngine {
|
|
|
972
978
|
}
|
|
973
979
|
|
|
974
980
|
/**
|
|
975
|
-
*
|
|
976
|
-
*
|
|
977
|
-
*
|
|
981
|
+
* True when the harness cwd is a directory the session does not own — a
|
|
982
|
+
* per-send override (e.g. a PR worktree) or a workspace shared by every
|
|
983
|
+
* session. Agent scaffold has to stay out of those, so it lands in the
|
|
984
|
+
* durable session workspace and the instructions travel inline instead.
|
|
985
|
+
*/
|
|
986
|
+
private hasBorrowedWorkspace(record: SessionRecord): boolean {
|
|
987
|
+
return (
|
|
988
|
+
record.workspaceOverride !== undefined ||
|
|
989
|
+
this.project.agent.local?.workspaceDir !== undefined
|
|
990
|
+
);
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
/**
|
|
994
|
+
* Resolve the shared harness workspace during startup, so the first session
|
|
995
|
+
* is not the one that waits for it. Only possible with
|
|
996
|
+
* `local.workspaceDir`: every other layout gives a session its own cwd,
|
|
997
|
+
* which is unknowable until that session exists.
|
|
998
|
+
*
|
|
999
|
+
* Best effort — warming is an optimization, and a turn rebuilds on its own.
|
|
978
1000
|
*/
|
|
1001
|
+
async prewarmSharedWorkspace(): Promise<void> {
|
|
1002
|
+
const agent = this.project.agent;
|
|
1003
|
+
const workspaceDir = agent.local?.workspaceDir;
|
|
1004
|
+
if (
|
|
1005
|
+
workspaceDir === undefined ||
|
|
1006
|
+
agent.runtime !== "local" ||
|
|
1007
|
+
this.runner.prewarm === undefined
|
|
1008
|
+
) {
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
const startedAt = Date.now();
|
|
1012
|
+
try {
|
|
1013
|
+
const outcome = await this.runner.prewarm({
|
|
1014
|
+
workspaceDir,
|
|
1015
|
+
runnerStateDir: join(this.stateRoot, "runner"),
|
|
1016
|
+
sandbox: agent.local?.sandbox === true,
|
|
1017
|
+
mcpServers: this.buildMcpServers("local"),
|
|
1018
|
+
subagents: buildRunnerSubagents(agent),
|
|
1019
|
+
...(agent.model === undefined ? {} : { model: agent.model }),
|
|
1020
|
+
});
|
|
1021
|
+
// Report what happened rather than that the call returned: a runner
|
|
1022
|
+
// that cannot warm resolves cleanly, and logging success there would
|
|
1023
|
+
// tell an operator the workspace is hot when nothing touched it.
|
|
1024
|
+
this.logger(
|
|
1025
|
+
outcome.warmed
|
|
1026
|
+
? `[agentkit] warmed ${workspaceDir} in ${Date.now() - startedAt}ms`
|
|
1027
|
+
: `[agentkit] did not warm ${workspaceDir}: ${outcome.reason}`
|
|
1028
|
+
);
|
|
1029
|
+
} catch (error) {
|
|
1030
|
+
this.logger(
|
|
1031
|
+
`[agentkit] could not warm ${workspaceDir}: ${describeError(error)}`
|
|
1032
|
+
);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
/** Directory that receives AGENTS.md / skills / agent tools. */
|
|
979
1037
|
private scaffoldDir(record: SessionRecord): string {
|
|
980
|
-
if (record
|
|
1038
|
+
if (this.hasBorrowedWorkspace(record)) {
|
|
981
1039
|
return this.sessions.workspaceDir(record.sessionId);
|
|
982
1040
|
}
|
|
983
1041
|
return this.harnessCwd(record);
|
|
@@ -1053,13 +1111,13 @@ export class SessionEngine {
|
|
|
1053
1111
|
});
|
|
1054
1112
|
}
|
|
1055
1113
|
// The harness only loads the scaffolded AGENTS.md when the session
|
|
1056
|
-
// workspace is its own project root.
|
|
1057
|
-
// (PR worktree) the scaffold lands elsewhere, and a
|
|
1058
|
-
// inside a git repo re-roots harness rules at that
|
|
1059
|
-
// cases deliver instructions inline on the first turn
|
|
1114
|
+
// workspace is its own project root. When the harness cwd is borrowed
|
|
1115
|
+
// (PR worktree, shared checkout) the scaffold lands elsewhere, and a
|
|
1116
|
+
// workspace nested inside a git repo re-roots harness rules at that
|
|
1117
|
+
// repo — in both cases deliver instructions inline on the first turn.
|
|
1060
1118
|
const instructionsInvisibleToHarness =
|
|
1061
1119
|
isFirstTurn &&
|
|
1062
|
-
(record
|
|
1120
|
+
(this.hasBorrowedWorkspace(record) ||
|
|
1063
1121
|
(await isNestedInGitRepo(harnessCwd)));
|
|
1064
1122
|
const request = this.buildTurnRequest(
|
|
1065
1123
|
record,
|
|
@@ -1201,10 +1259,10 @@ export class SessionEngine {
|
|
|
1201
1259
|
turnPrompt = `${preamble}\n\n${turnPrompt}`;
|
|
1202
1260
|
} else if (instructionsInvisibleToHarness) {
|
|
1203
1261
|
// Local workspace whose AGENTS.md the harness will not load. With a
|
|
1204
|
-
//
|
|
1205
|
-
//
|
|
1206
|
-
//
|
|
1207
|
-
const includeScripts = record
|
|
1262
|
+
// borrowed harness cwd, agent tool scripts live only in the durable
|
|
1263
|
+
// scaffold dir — embed bodies like cloud. Otherwise the catalog can
|
|
1264
|
+
// reference on-disk paths under the harness cwd.
|
|
1265
|
+
const includeScripts = this.hasBorrowedWorkspace(record);
|
|
1208
1266
|
const preamble = buildAgentsMdContent(agent, { includeScripts });
|
|
1209
1267
|
turnPrompt = `${preamble}\n\n${turnPrompt}`;
|
|
1210
1268
|
}
|
|
@@ -1215,6 +1273,7 @@ export class SessionEngine {
|
|
|
1215
1273
|
? {}
|
|
1216
1274
|
: { sdkAgentId: record.sdkAgentId }),
|
|
1217
1275
|
workspaceDir: this.harnessCwd(record),
|
|
1276
|
+
sandbox: this.project.agent.local?.sandbox === true,
|
|
1218
1277
|
runnerStateDir: join(this.stateRoot, "runner"),
|
|
1219
1278
|
prompt: turnPrompt,
|
|
1220
1279
|
isFirstTurn,
|
|
@@ -1224,12 +1283,7 @@ export class SessionEngine {
|
|
|
1224
1283
|
customTools:
|
|
1225
1284
|
runtime === "local" ? this.buildCustomTools(record, turnId, emit) : {},
|
|
1226
1285
|
mcpServers: this.buildMcpServers(runtime),
|
|
1227
|
-
subagents: agent
|
|
1228
|
-
name: subagent.name,
|
|
1229
|
-
description: subagent.description ?? subagent.name,
|
|
1230
|
-
prompt: subagentPrompt(subagent),
|
|
1231
|
-
...(subagent.model === undefined ? {} : { model: subagent.model }),
|
|
1232
|
-
})),
|
|
1286
|
+
subagents: buildRunnerSubagents(agent),
|
|
1233
1287
|
onUpdate,
|
|
1234
1288
|
signal,
|
|
1235
1289
|
// Local follow-ups expire any lingering active run so a preempted turn
|
|
@@ -2509,6 +2563,20 @@ function subagentPrompt(subagent: ResolvedAgent): string {
|
|
|
2509
2563
|
return subagent.description ?? subagent.name;
|
|
2510
2564
|
}
|
|
2511
2565
|
|
|
2566
|
+
/**
|
|
2567
|
+
* Subagents as the runner takes them. Shared by turns and by workspace
|
|
2568
|
+
* warming, which has to describe the same agent a turn would to reach the
|
|
2569
|
+
* executor that turn will run on.
|
|
2570
|
+
*/
|
|
2571
|
+
function buildRunnerSubagents(agent: ResolvedAgent): RunnerSubagent[] {
|
|
2572
|
+
return agent.subagents.map((subagent) => ({
|
|
2573
|
+
name: subagent.name,
|
|
2574
|
+
description: subagent.description ?? subagent.name,
|
|
2575
|
+
prompt: subagentPrompt(subagent),
|
|
2576
|
+
...(subagent.model === undefined ? {} : { model: subagent.model }),
|
|
2577
|
+
}));
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2512
2580
|
function describeError(error: unknown): string {
|
|
2513
2581
|
return error instanceof Error ? error.message : String(error);
|
|
2514
2582
|
}
|