@cursor/july 0.1.100 → 0.1.101
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/dist/bin/agent-serve.js +3 -2
- package/dist/channels/deployments/deployments-channel.d.ts +41 -5
- package/dist/channels/deployments/deployments-channel.d.ts.map +1 -1
- package/dist/channels/deployments/deployments-channel.js +88 -8
- package/dist/channels/deployments/types.d.ts +4 -3
- package/dist/channels/deployments/types.d.ts.map +1 -1
- package/dist/channels/origin/defaults.d.ts +43 -0
- package/dist/channels/origin/defaults.d.ts.map +1 -0
- package/dist/channels/origin/defaults.js +111 -0
- package/dist/channels/origin/index.d.ts +2 -0
- package/dist/channels/origin/index.d.ts.map +1 -1
- package/dist/channels/origin/index.js +2 -0
- package/dist/channels/origin/origin-channel.d.ts +2 -1
- package/dist/channels/origin/origin-channel.d.ts.map +1 -1
- package/dist/channels/origin/origin-channel.js +24 -3
- package/dist/channels/origin/state.d.ts +32 -0
- package/dist/channels/origin/state.d.ts.map +1 -0
- package/dist/channels/origin/state.js +23 -0
- package/dist/channels/origin/types.d.ts +23 -1
- package/dist/channels/origin/types.d.ts.map +1 -1
- package/dist/channels/slack/api.d.ts.map +1 -1
- package/dist/channels/slack/api.js +2 -1
- package/dist/channels/slack/code-fences.d.ts +17 -0
- package/dist/channels/slack/code-fences.d.ts.map +1 -0
- package/dist/channels/slack/code-fences.js +168 -0
- 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/live-delivery.d.ts.map +1 -1
- package/dist/channels/slack/live-delivery.js +11 -4
- package/dist/connections.d.ts +18 -2
- package/dist/connections.d.ts.map +1 -1
- package/dist/connections.js +18 -2
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.DZ1e0Ycq.js → app.BASO9Kih.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DbFXINq2.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.l8omwc6D.js → VPLocalSearchBox.DwkqxoE0.js} +1 -1
- package/dist/docs/assets/chunks/{theme.JVcD6gel.js → theme.CCil_13e.js} +2 -2
- package/dist/docs/assets/{reference_connections.md.BiGoBAk2.js → reference_connections.md.Di6jJAXF.js} +12 -2
- package/dist/docs/assets/{reference_connections.md.BiGoBAk2.lean.js → reference_connections.md.Di6jJAXF.lean.js} +1 -1
- package/dist/docs/assets/{reference_http-api.md.BEJx9XVj.js → reference_http-api.md.oySXBO8o.js} +1 -1
- package/dist/docs/building-with-agents.html +4 -4
- package/dist/docs/concepts.html +4 -4
- 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-runtime.html +4 -4
- package/dist/docs/guides/convert-automation.html +4 -4
- package/dist/docs/guides/github.html +4 -4
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/mcp-oauth.html +4 -4
- package/dist/docs/guides/opentelemetry.html +4 -4
- 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 +4 -4
- package/dist/docs/llms-full.txt +32 -1
- 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 +4 -4
- package/dist/docs/reference/cli.html +4 -4
- package/dist/docs/reference/connections.html +16 -6
- package/dist/docs/reference/connections.md +31 -0
- package/dist/docs/reference/hooks.html +4 -4
- package/dist/docs/reference/http-api.html +6 -6
- package/dist/docs/reference/http-api.md +1 -1
- package/dist/docs/reference/instructions.html +4 -4
- package/dist/docs/reference/playground.html +4 -4
- package/dist/docs/reference/project-layout.html +4 -4
- package/dist/docs/reference/prompt.html +4 -4
- package/dist/docs/reference/schedules.html +4 -4
- package/dist/docs/reference/sessions.html +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 +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 +4 -4
- 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 +55 -0
- package/dist/extensions/cursor-cloud-agents/extension.d.ts.map +1 -0
- package/dist/extensions/cursor-cloud-agents/extension.js +64 -0
- package/dist/extensions/cursor-cloud-agents/lib/cloud.d.ts +30 -0
- package/dist/extensions/cursor-cloud-agents/lib/cloud.d.ts.map +1 -0
- package/dist/extensions/cursor-cloud-agents/lib/cloud.js +45 -0
- package/dist/extensions/cursor-cloud-agents/lib/fleet.d.ts +25 -0
- package/dist/extensions/cursor-cloud-agents/lib/fleet.d.ts.map +1 -0
- package/dist/extensions/cursor-cloud-agents/lib/fleet.js +85 -0
- package/dist/extensions/cursor-cloud-agents/lib/repos.d.ts +15 -0
- package/dist/extensions/cursor-cloud-agents/lib/repos.d.ts.map +1 -0
- package/dist/extensions/cursor-cloud-agents/lib/repos.js +55 -0
- package/dist/extensions/cursor-cloud-agents/skills/handoff.md +27 -0
- package/dist/extensions/cursor-cloud-agents/tools/follow_up.d.ts +4 -0
- package/dist/extensions/cursor-cloud-agents/tools/follow_up.d.ts.map +1 -0
- package/dist/extensions/cursor-cloud-agents/tools/follow_up.js +37 -0
- package/dist/extensions/cursor-cloud-agents/tools/launch.d.ts +4 -0
- package/dist/extensions/cursor-cloud-agents/tools/launch.d.ts.map +1 -0
- package/dist/extensions/cursor-cloud-agents/tools/launch.js +83 -0
- package/dist/extensions/cursor-cloud-agents/tools/list.d.ts +4 -0
- package/dist/extensions/cursor-cloud-agents/tools/list.d.ts.map +1 -0
- package/dist/extensions/cursor-cloud-agents/tools/list.js +36 -0
- package/dist/extensions/cursor-cloud-agents/tools/status.d.ts +4 -0
- package/dist/extensions/cursor-cloud-agents/tools/status.d.ts.map +1 -0
- package/dist/extensions/cursor-cloud-agents/tools/status.js +41 -0
- package/dist/extensions.d.ts +49 -0
- package/dist/extensions.d.ts.map +1 -0
- package/dist/extensions.js +75 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/internal/advertise-tools.d.ts +4 -1
- package/dist/internal/advertise-tools.d.ts.map +1 -1
- package/dist/internal/advertise-tools.js +20 -1
- package/dist/internal/as-of.d.ts +18 -0
- package/dist/internal/as-of.d.ts.map +1 -1
- package/dist/internal/as-of.js +131 -0
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +64 -19
- package/dist/internal/continuation-identity.d.ts.map +1 -1
- package/dist/internal/continuation-identity.js +3 -1
- package/dist/internal/cursor/hosted-deployment-secrets.d.ts +16 -0
- package/dist/internal/cursor/hosted-deployment-secrets.d.ts.map +1 -0
- package/dist/internal/cursor/hosted-deployment-secrets.js +61 -0
- package/dist/internal/cursor-deployment-relay.d.ts +15 -1
- package/dist/internal/cursor-deployment-relay.d.ts.map +1 -1
- package/dist/internal/cursor-deployment-relay.js +46 -7
- package/dist/internal/deploy-client.d.ts +23 -0
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +54 -12
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +415 -120
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +5 -0
- package/dist/internal/framework-file-storage.d.ts +3 -3
- package/dist/internal/framework-file-storage.d.ts.map +1 -1
- package/dist/internal/framework-file-storage.js +34 -5
- package/dist/internal/hosted-delivery.d.ts +3 -1
- package/dist/internal/hosted-delivery.d.ts.map +1 -1
- package/dist/internal/hosted-delivery.js +19 -32
- package/dist/internal/hosted-execution-diag.d.ts +5 -0
- package/dist/internal/hosted-execution-diag.d.ts.map +1 -1
- package/dist/internal/hosted-execution-diag.js +5 -0
- package/dist/internal/hosted-execution-flush.d.ts +46 -0
- package/dist/internal/hosted-execution-flush.d.ts.map +1 -0
- package/dist/internal/hosted-execution-flush.js +81 -0
- package/dist/internal/mcp-endpoint.js +1 -0
- package/dist/internal/mcp-host.d.ts +2 -1
- package/dist/internal/mcp-host.d.ts.map +1 -1
- package/dist/internal/mcp-host.js +35 -15
- package/dist/internal/review-comments.d.ts +1 -1
- package/dist/internal/review-comments.d.ts.map +1 -1
- package/dist/internal/review-comments.js +4 -4
- package/dist/internal/server.d.ts +3 -0
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +79 -104
- package/dist/internal/session-engine.d.ts +9 -7
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +45 -34
- 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 +2 -1
- package/dist/internal/start-log-store.d.ts +60 -0
- package/dist/internal/start-log-store.d.ts.map +1 -0
- package/dist/internal/start-log-store.js +151 -0
- package/dist/internal/tool-policy.d.ts +8 -0
- package/dist/internal/tool-policy.d.ts.map +1 -1
- package/dist/internal/tool-policy.js +19 -7
- package/dist/playground/assets/{index-DVs98vPL.js → index-BnNN1-EK.js} +45 -45
- package/dist/playground/assets/index-DBQCwFp0.css +1 -0
- package/dist/playground/index.html +2 -2
- package/dist/storage-protocol.d.ts +24 -0
- package/dist/storage-protocol.d.ts.map +1 -1
- package/dist/storage-protocol.js +33 -0
- package/dist/storage.d.ts +1 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +1 -1
- package/dist/types.d.ts +118 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +29 -1
- package/docs/reference/connections.md +31 -0
- package/docs/reference/http-api.md +1 -1
- package/package.json +18 -4
- package/src/bin/agent-serve.ts +11 -2
- package/src/channels/deployments/deployments-channel.ts +117 -8
- package/src/channels/deployments/types.ts +4 -3
- package/src/channels/origin/defaults.ts +143 -0
- package/src/channels/origin/index.ts +2 -0
- package/src/channels/origin/origin-channel.ts +37 -1
- package/src/channels/origin/state.ts +54 -0
- package/src/channels/origin/types.ts +24 -0
- package/src/channels/slack/api.ts +2 -1
- package/src/channels/slack/code-fences.ts +183 -0
- package/src/channels/slack/index.ts +1 -0
- package/src/channels/slack/live-delivery.ts +16 -13
- package/src/connections.ts +18 -1
- package/src/extensions/cursor-cloud-agents/extension.ts +118 -0
- package/src/extensions/cursor-cloud-agents/lib/cloud.ts +79 -0
- package/src/extensions/cursor-cloud-agents/lib/fleet.ts +109 -0
- package/src/extensions/cursor-cloud-agents/lib/repos.ts +81 -0
- package/src/extensions/cursor-cloud-agents/skills/handoff.md +27 -0
- package/src/extensions/cursor-cloud-agents/tools/follow_up.ts +29 -0
- package/src/extensions/cursor-cloud-agents/tools/launch.ts +84 -0
- package/src/extensions/cursor-cloud-agents/tools/list.ts +31 -0
- package/src/extensions/cursor-cloud-agents/tools/status.ts +39 -0
- package/src/extensions.ts +98 -0
- package/src/index.ts +2 -0
- package/src/internal/advertise-tools.ts +37 -12
- package/src/internal/as-of.ts +163 -0
- package/src/internal/cli-deploy.ts +86 -23
- package/src/internal/continuation-identity.ts +3 -1
- package/src/internal/cursor/hosted-deployment-secrets.ts +72 -0
- package/src/internal/cursor-deployment-relay.ts +65 -8
- package/src/internal/deploy-client.ts +73 -18
- package/src/internal/discovery.ts +470 -113
- package/src/internal/distribution.ts +5 -0
- package/src/internal/framework-file-storage.ts +39 -5
- package/src/internal/hosted-delivery.ts +38 -63
- package/src/internal/hosted-execution-diag.ts +6 -0
- package/src/internal/hosted-execution-flush.ts +142 -0
- package/src/internal/mcp-endpoint.ts +1 -0
- package/src/internal/mcp-host.ts +40 -14
- package/src/internal/review-comments.ts +5 -2
- package/src/internal/server.ts +81 -100
- package/src/internal/session-engine.ts +56 -28
- package/src/internal/session-run-log.ts +2 -1
- package/src/internal/start-log-store.ts +177 -0
- package/src/internal/tool-policy.ts +39 -9
- package/src/storage-protocol.ts +37 -0
- package/src/storage.ts +1 -0
- package/src/types.ts +172 -14
- package/dist/docs/assets/chunks/@localSearchIndexroot.j70vvPL4.js +0 -1
- package/dist/playground/assets/index-BszoQDc6.css +0 -1
- /package/dist/docs/assets/{reference_http-api.md.BEJx9XVj.lean.js → reference_http-api.md.oySXBO8o.lean.js} +0 -0
package/src/internal/server.ts
CHANGED
|
@@ -72,6 +72,7 @@ import { CursorGithubCredentialProvider } from "./cursor/github-credentials.js";
|
|
|
72
72
|
import {
|
|
73
73
|
DeploymentEventRelay,
|
|
74
74
|
fanOutDeploymentEvent,
|
|
75
|
+
mergeResolvedSubscriptions,
|
|
75
76
|
type ResolvedSubscription,
|
|
76
77
|
resolveDeploymentSubscriptions,
|
|
77
78
|
} from "./cursor-deployment-relay.js";
|
|
@@ -111,6 +112,7 @@ import {
|
|
|
111
112
|
import { createHostPlatforms, warmupHostGitHubAuth } from "./host-platforms.js";
|
|
112
113
|
import { registerHostedDeliveryRoute } from "./hosted-delivery.js";
|
|
113
114
|
import { hostedErrorStackSnippet } from "./hosted-execution-diag.js";
|
|
115
|
+
import { flushHostedExecutionProcessDeath } from "./hosted-execution-flush.js";
|
|
114
116
|
import {
|
|
115
117
|
buildHttpChannelRoutes,
|
|
116
118
|
errorResponse,
|
|
@@ -194,6 +196,7 @@ import {
|
|
|
194
196
|
unregisterSessionRunFatalFlush,
|
|
195
197
|
} from "./session-run-log.js";
|
|
196
198
|
import { sessionUrlsForRequest } from "./session-urls.js";
|
|
199
|
+
import { flushLocalStartLog, selectStartLogStore } from "./start-log-store.js";
|
|
197
200
|
import { catalogDetailEntry, catalogListEntry } from "./tool-catalog.js";
|
|
198
201
|
|
|
199
202
|
function memoryHealthFields(engines: readonly SessionEngine[]) {
|
|
@@ -256,6 +259,8 @@ export interface StartServerInternalOptions {
|
|
|
256
259
|
mcpOauthSecrets?: McpOauthSecretsTarget;
|
|
257
260
|
/** Test seam: persist hosted-execution run logs without a live store. */
|
|
258
261
|
sessionRunLog?: ReturnType<typeof selectSessionRunLogStore>;
|
|
262
|
+
/** Test seam: persist the engine start.log tee without a live store. */
|
|
263
|
+
startLogStore?: ReturnType<typeof selectStartLogStore>;
|
|
259
264
|
}
|
|
260
265
|
|
|
261
266
|
/**
|
|
@@ -550,10 +555,35 @@ export async function startServer(
|
|
|
550
555
|
snapshotLogs: () => logRing.since(),
|
|
551
556
|
})
|
|
552
557
|
: undefined);
|
|
558
|
+
const startLogStore =
|
|
559
|
+
options.startLogStore ??
|
|
560
|
+
(mounts.length === 1 && mounts[0] !== undefined
|
|
561
|
+
? selectStartLogStore({
|
|
562
|
+
project: mounts[0].project,
|
|
563
|
+
logger,
|
|
564
|
+
})
|
|
565
|
+
: undefined);
|
|
566
|
+
// The running claim stamps this before the turn; process-death flush
|
|
567
|
+
// reuses it so the tee snapshot lands under the same ses_ key.
|
|
568
|
+
let lastStartLogId: string | undefined;
|
|
569
|
+
const flushStartLog = async (startLogId: string): Promise<void> => {
|
|
570
|
+
lastStartLogId = startLogId;
|
|
571
|
+
if (startLogStore === undefined) {
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
await flushLocalStartLog({ store: startLogStore, startLogId });
|
|
575
|
+
};
|
|
553
576
|
if (sessionRunLog !== undefined) {
|
|
554
|
-
registerSessionRunFatalFlush((error) =>
|
|
555
|
-
|
|
556
|
-
|
|
577
|
+
registerSessionRunFatalFlush(async (error) => {
|
|
578
|
+
await flushHostedExecutionProcessDeath({
|
|
579
|
+
flushProcessDeath: (reason, flushError) =>
|
|
580
|
+
sessionRunLog.flushProcessDeath(reason, flushError),
|
|
581
|
+
flushStartLog,
|
|
582
|
+
startLogId: lastStartLogId,
|
|
583
|
+
reason: "uncaught_exception",
|
|
584
|
+
error,
|
|
585
|
+
});
|
|
586
|
+
});
|
|
557
587
|
}
|
|
558
588
|
const sdkRunner =
|
|
559
589
|
options.runner ??
|
|
@@ -770,6 +800,7 @@ export async function startServer(
|
|
|
770
800
|
logRing,
|
|
771
801
|
diagStateRoot: baseStateRoot,
|
|
772
802
|
sessionRunLog,
|
|
803
|
+
flushStartLog,
|
|
773
804
|
...(options.authToken === undefined
|
|
774
805
|
? {}
|
|
775
806
|
: { authToken: options.authToken }),
|
|
@@ -1224,7 +1255,13 @@ export async function startServer(
|
|
|
1224
1255
|
},
|
|
1225
1256
|
close: async () => {
|
|
1226
1257
|
if (sessionRunLog !== undefined) {
|
|
1227
|
-
await
|
|
1258
|
+
await flushHostedExecutionProcessDeath({
|
|
1259
|
+
flushProcessDeath: (reason) =>
|
|
1260
|
+
sessionRunLog.flushProcessDeath(reason),
|
|
1261
|
+
flushStartLog,
|
|
1262
|
+
startLogId: lastStartLogId,
|
|
1263
|
+
reason: "process_exit",
|
|
1264
|
+
});
|
|
1228
1265
|
unregisterSessionRunFatalFlush();
|
|
1229
1266
|
}
|
|
1230
1267
|
playgroundDev?.stop();
|
|
@@ -1282,6 +1319,7 @@ function mountAgentRoutes(
|
|
|
1282
1319
|
logger: (line: string) => void;
|
|
1283
1320
|
logRing: LogRing;
|
|
1284
1321
|
sessionRunLog?: ReturnType<typeof selectSessionRunLogStore>;
|
|
1322
|
+
flushStartLog?: (startLogId: string) => Promise<void>;
|
|
1285
1323
|
authToken?: string;
|
|
1286
1324
|
allowAnonymous?: boolean;
|
|
1287
1325
|
/** Cursor account bridge + this mount's account MCP connections, when authored. */
|
|
@@ -1303,6 +1341,7 @@ function mountAgentRoutes(
|
|
|
1303
1341
|
logger,
|
|
1304
1342
|
logRing,
|
|
1305
1343
|
sessionRunLog,
|
|
1344
|
+
flushStartLog,
|
|
1306
1345
|
} = args;
|
|
1307
1346
|
// "" (single) -> "/v1/..."; "my-agent" (multi) -> "/my-agent/v1/...".
|
|
1308
1347
|
const base = slug === "" ? "" : `/${slug}`;
|
|
@@ -1366,7 +1405,12 @@ function mountAgentRoutes(
|
|
|
1366
1405
|
: { stateRoot: args.diagStateRoot }),
|
|
1367
1406
|
...(sessionRunLog === undefined
|
|
1368
1407
|
? {}
|
|
1369
|
-
: {
|
|
1408
|
+
: {
|
|
1409
|
+
persistSessionRun: async (document) => {
|
|
1410
|
+
await sessionRunLog.persist(document);
|
|
1411
|
+
},
|
|
1412
|
+
flushStartLog,
|
|
1413
|
+
}),
|
|
1370
1414
|
snapshotLogs: () => logRing.since(),
|
|
1371
1415
|
});
|
|
1372
1416
|
}
|
|
@@ -1474,51 +1518,6 @@ function mountAgentRoutes(
|
|
|
1474
1518
|
},
|
|
1475
1519
|
});
|
|
1476
1520
|
|
|
1477
|
-
// v2 only: one session-level hosted-execution log on the framework
|
|
1478
|
-
// store. Distinct from GET /v1/logs (process-wide ring). Keyed by the
|
|
1479
|
-
// factory session public id so an execute_throw that never minted a
|
|
1480
|
-
// July session is still readable after the pod parks.
|
|
1481
|
-
if (isV2Project(project) && sessionRunLog !== undefined) {
|
|
1482
|
-
router.add({
|
|
1483
|
-
method: "GET",
|
|
1484
|
-
pattern: `${base}/v1/session/:sessionId/runs`,
|
|
1485
|
-
auth: httpAuth,
|
|
1486
|
-
handler: async (_request, ctx) => {
|
|
1487
|
-
const sessionId = ctx.params.sessionId ?? "";
|
|
1488
|
-
if (sessionId.length === 0 || sessionId.includes("/")) {
|
|
1489
|
-
return errorResponse(
|
|
1490
|
-
400,
|
|
1491
|
-
"invalid_request",
|
|
1492
|
-
"Expected a session id."
|
|
1493
|
-
);
|
|
1494
|
-
}
|
|
1495
|
-
const record = await engine.getOrRestoreSession(sessionId);
|
|
1496
|
-
if (record === undefined) {
|
|
1497
|
-
return errorResponse(
|
|
1498
|
-
404,
|
|
1499
|
-
"session_not_found",
|
|
1500
|
-
`Unknown session ${sessionId}.`
|
|
1501
|
-
);
|
|
1502
|
-
}
|
|
1503
|
-
const ownership = requireSessionOwner(record, ctx.auth, {
|
|
1504
|
-
relax: relaxSessionReadAuth,
|
|
1505
|
-
beyondLoopback: relaxSessionReadAuthBeyondLoopback,
|
|
1506
|
-
requestIp: ctx.requestIp,
|
|
1507
|
-
});
|
|
1508
|
-
if (ownership !== undefined) {
|
|
1509
|
-
return ownership;
|
|
1510
|
-
}
|
|
1511
|
-
const document = await sessionRunLog.read(sessionId);
|
|
1512
|
-
return jsonResponse({
|
|
1513
|
-
sessionId,
|
|
1514
|
-
updatedAt: document?.updatedAt,
|
|
1515
|
-
lastFailure: document?.lastFailure ?? null,
|
|
1516
|
-
executions: document?.executions ?? [],
|
|
1517
|
-
});
|
|
1518
|
-
},
|
|
1519
|
-
});
|
|
1520
|
-
}
|
|
1521
|
-
|
|
1522
1521
|
router.add({
|
|
1523
1522
|
method: "GET",
|
|
1524
1523
|
pattern: `${base}/v1/sessions`,
|
|
@@ -2694,13 +2693,11 @@ interface CursorDeploymentTarget {
|
|
|
2694
2693
|
runtime: MountedRuntime;
|
|
2695
2694
|
channelId: string;
|
|
2696
2695
|
options: DeploymentsChannelOptions;
|
|
2697
|
-
subscriptions: ResolvedSubscription[];
|
|
2698
2696
|
}
|
|
2699
2697
|
|
|
2700
|
-
/** Every mounted `deploymentsChannel
|
|
2698
|
+
/** Every mounted `deploymentsChannel`. Subscriptions are re-resolved live. */
|
|
2701
2699
|
function collectCursorDeploymentTargets(
|
|
2702
|
-
runtimes: MountedRuntime[]
|
|
2703
|
-
skipped: string[]
|
|
2700
|
+
runtimes: MountedRuntime[]
|
|
2704
2701
|
): CursorDeploymentTarget[] {
|
|
2705
2702
|
const targets: CursorDeploymentTarget[] = [];
|
|
2706
2703
|
for (const runtime of runtimes) {
|
|
@@ -2709,19 +2706,24 @@ function collectCursorDeploymentTargets(
|
|
|
2709
2706
|
if (options === undefined) {
|
|
2710
2707
|
continue;
|
|
2711
2708
|
}
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
continue;
|
|
2718
|
-
}
|
|
2719
|
-
targets.push({ runtime, channelId: channel.id, options, subscriptions });
|
|
2709
|
+
targets.push({
|
|
2710
|
+
runtime,
|
|
2711
|
+
channelId: channel.id,
|
|
2712
|
+
options,
|
|
2713
|
+
});
|
|
2720
2714
|
}
|
|
2721
2715
|
}
|
|
2722
2716
|
return targets;
|
|
2723
2717
|
}
|
|
2724
2718
|
|
|
2719
|
+
function liveDeploymentSubscriptionUnion(
|
|
2720
|
+
targets: readonly CursorDeploymentTarget[]
|
|
2721
|
+
): ResolvedSubscription[] {
|
|
2722
|
+
return mergeResolvedSubscriptions(
|
|
2723
|
+
targets.map((target) => resolveDeploymentSubscriptions(target.options))
|
|
2724
|
+
);
|
|
2725
|
+
}
|
|
2726
|
+
|
|
2725
2727
|
/**
|
|
2726
2728
|
* Cursor deployment-events relay: automatic when a mounted agent authored
|
|
2727
2729
|
* `deploymentsChannel({ ... })`. One relay per process reads
|
|
@@ -2736,13 +2738,7 @@ function startCursorDeploymentRelay(input: {
|
|
|
2736
2738
|
logger: (line: string) => void;
|
|
2737
2739
|
}): DeploymentEventRelay | undefined {
|
|
2738
2740
|
const { options, runtimes, accountKey, logger } = input;
|
|
2739
|
-
const
|
|
2740
|
-
const targets = collectCursorDeploymentTargets(runtimes, skipped);
|
|
2741
|
-
if (skipped.length > 0) {
|
|
2742
|
-
logger(
|
|
2743
|
-
`[agent-sdk] deployment-events: no deploy sources declared on ${skipped.join(", ")}; those channels receive nothing`
|
|
2744
|
-
);
|
|
2745
|
-
}
|
|
2741
|
+
const targets = collectCursorDeploymentTargets(runtimes);
|
|
2746
2742
|
if (targets.length === 0) {
|
|
2747
2743
|
return undefined;
|
|
2748
2744
|
}
|
|
@@ -2757,34 +2753,19 @@ function startCursorDeploymentRelay(input: {
|
|
|
2757
2753
|
`This project has channels subscribed to deployment events (deploymentsChannel({ deploySourceUris })) but the host is not signed in to Cursor. ${SIGN_IN_HINT}`
|
|
2758
2754
|
);
|
|
2759
2755
|
}
|
|
2760
|
-
// Union
|
|
2761
|
-
//
|
|
2762
|
-
//
|
|
2763
|
-
const
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
continue;
|
|
2774
|
-
}
|
|
2775
|
-
for (const event of subscription.events) existing.events.add(event);
|
|
2776
|
-
// An empty environment list means "every environment", so it absorbs.
|
|
2777
|
-
existing.environments =
|
|
2778
|
-
existing.environments.length === 0 ||
|
|
2779
|
-
subscription.environments.length === 0
|
|
2780
|
-
? []
|
|
2781
|
-
: [
|
|
2782
|
-
...new Set([
|
|
2783
|
-
...existing.environments,
|
|
2784
|
-
...subscription.environments,
|
|
2785
|
-
]),
|
|
2786
|
-
];
|
|
2787
|
-
}
|
|
2756
|
+
// Union is re-resolved on each heartbeat and fan-out so a channel that
|
|
2757
|
+
// refreshes `deploySourceUris` after start (Change Monitor lists the
|
|
2758
|
+
// team's sources every 10 minutes) registers the new URIs.
|
|
2759
|
+
const initial = liveDeploymentSubscriptionUnion(targets);
|
|
2760
|
+
if (initial.length === 0) {
|
|
2761
|
+
logger(
|
|
2762
|
+
`[agent-sdk] deployment-events: no deploy sources yet on ${targets
|
|
2763
|
+
.map(
|
|
2764
|
+
(target) =>
|
|
2765
|
+
`${target.runtime.slug || target.runtime.project.name}/${target.channelId}`
|
|
2766
|
+
)
|
|
2767
|
+
.join(", ")}; the relay will register when a channel lists some`
|
|
2768
|
+
);
|
|
2788
2769
|
}
|
|
2789
2770
|
// Same host split as scm-events: `/v0/deployment-events/*` is on
|
|
2790
2771
|
// api.cursor.com, not api2.
|
|
@@ -2795,7 +2776,7 @@ function startCursorDeploymentRelay(input: {
|
|
|
2795
2776
|
baseUrl: cursorExternalApiUrl(),
|
|
2796
2777
|
apiKey: accountKey.apiKey,
|
|
2797
2778
|
stateDir: join(baseStateRoot, "deployment-events"),
|
|
2798
|
-
subscriptions:
|
|
2779
|
+
subscriptions: () => liveDeploymentSubscriptionUnion(targets),
|
|
2799
2780
|
logger,
|
|
2800
2781
|
dispatch: async (event) =>
|
|
2801
2782
|
fanOutDeploymentEvent({
|
|
@@ -2803,7 +2784,7 @@ function startCursorDeploymentRelay(input: {
|
|
|
2803
2784
|
logger,
|
|
2804
2785
|
targets: targets.map((target) => ({
|
|
2805
2786
|
name: `${target.runtime.slug || target.runtime.project.name}/${target.channelId}`,
|
|
2806
|
-
subscriptions: target.
|
|
2787
|
+
subscriptions: resolveDeploymentSubscriptions(target.options),
|
|
2807
2788
|
deliver: (delivered) =>
|
|
2808
2789
|
dispatchDeploymentEvent({
|
|
2809
2790
|
options: target.options,
|
|
@@ -2818,7 +2799,7 @@ function startCursorDeploymentRelay(input: {
|
|
|
2818
2799
|
});
|
|
2819
2800
|
relay.start();
|
|
2820
2801
|
logger(
|
|
2821
|
-
`[agent-sdk] deployment-events: relaying ${
|
|
2802
|
+
`[agent-sdk] deployment-events: relaying ${initial.length} deploy-source subscription(s) to ${targets.length} channel(s)`
|
|
2822
2803
|
);
|
|
2823
2804
|
return relay;
|
|
2824
2805
|
}
|
|
@@ -50,16 +50,17 @@ import {
|
|
|
50
50
|
type ConnectionTransport,
|
|
51
51
|
DEFAULT_MODEL,
|
|
52
52
|
DIRECT_TOOL_CALL_CHANNEL_ID,
|
|
53
|
+
type DiscoveredConnection,
|
|
53
54
|
type DiscoveredTool,
|
|
54
55
|
type HookHandler,
|
|
55
56
|
type HostContext,
|
|
56
57
|
type HostMcpRegistry,
|
|
58
|
+
type HostMcpToolInfo,
|
|
57
59
|
type HttpChannelDefinition,
|
|
58
60
|
isCursorAccountTransport,
|
|
59
61
|
isHostOauthConnectionTransport,
|
|
60
62
|
isSymbolicConnectionTransport,
|
|
61
63
|
type JsonValue,
|
|
62
|
-
type McpToolAnnotations,
|
|
63
64
|
type PendingApproval,
|
|
64
65
|
type PendingAuth,
|
|
65
66
|
type ReceiveInput,
|
|
@@ -89,7 +90,12 @@ import {
|
|
|
89
90
|
} from "./advertise-tools.js";
|
|
90
91
|
import { ApprovalGate } from "./approval-gate.js";
|
|
91
92
|
import { ArtifactsStore } from "./artifacts-store.js";
|
|
92
|
-
import {
|
|
93
|
+
import {
|
|
94
|
+
assertTimeArgs,
|
|
95
|
+
normalizeAsOf,
|
|
96
|
+
schemaTimeWindowKeys,
|
|
97
|
+
sessionClock,
|
|
98
|
+
} from "./as-of.js";
|
|
93
99
|
import { SessionAuthError, samePrincipal } from "./auth.js";
|
|
94
100
|
import { mergeCloudOptions, resolveSessionRuntime } from "./cloud-merge.js";
|
|
95
101
|
import {
|
|
@@ -600,18 +606,25 @@ export class SessionEngine {
|
|
|
600
606
|
setResolvedConnections(connections: EngineResolvedConnection[]): void {
|
|
601
607
|
this.resolvedConnections = connections;
|
|
602
608
|
for (const connection of connections) {
|
|
609
|
+
const authored = this.authoredConnection(connection.name);
|
|
603
610
|
this.mcpHost.addConnection({
|
|
611
|
+
...(authored ?? { name: connection.name }),
|
|
604
612
|
name: connection.name,
|
|
605
613
|
transport: {
|
|
606
614
|
url: connection.localUrl,
|
|
607
|
-
|
|
608
|
-
? {}
|
|
609
|
-
: { headers: connection.headers }),
|
|
615
|
+
headers: connection.headers,
|
|
610
616
|
},
|
|
611
617
|
});
|
|
612
618
|
}
|
|
613
619
|
}
|
|
614
620
|
|
|
621
|
+
private authoredConnection(name: string): DiscoveredConnection | undefined {
|
|
622
|
+
return (
|
|
623
|
+
this.project.agent.connections.find((item) => item.name === name) ??
|
|
624
|
+
this.project.agent.hostConnections?.find((item) => item.name === name)
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
|
|
615
628
|
setCloudServerToolsConnection(connection: ConnectionTransport): void {
|
|
616
629
|
this.cloudServerToolsConnection = connection;
|
|
617
630
|
}
|
|
@@ -2753,6 +2766,9 @@ export class SessionEngine {
|
|
|
2753
2766
|
input: validation.value,
|
|
2754
2767
|
asOf: args.record.asOf,
|
|
2755
2768
|
timeArgs: args.tool.definition.timeArgs,
|
|
2769
|
+
...(args.tool.advertised === true && args.record.asOf !== undefined
|
|
2770
|
+
? { timeWindow: schemaTimeWindowKeys(args.tool.inputSchema) }
|
|
2771
|
+
: {}),
|
|
2756
2772
|
});
|
|
2757
2773
|
if (decision.kind === "answer") {
|
|
2758
2774
|
this.stubbedCallIds.get(args.record.sessionId)?.add(toolCallId);
|
|
@@ -3083,13 +3099,6 @@ export class SessionEngine {
|
|
|
3083
3099
|
await this.oauthHub.persistFor(connection, authored.transport.url);
|
|
3084
3100
|
}
|
|
3085
3101
|
|
|
3086
|
-
private authoredConnection(name: string) {
|
|
3087
|
-
return (
|
|
3088
|
-
this.project.agent.connections.find((entry) => entry.name === name) ??
|
|
3089
|
-
this.project.agent.hostConnections?.find((entry) => entry.name === name)
|
|
3090
|
-
);
|
|
3091
|
-
}
|
|
3092
|
-
|
|
3093
3102
|
private async mintAuthUrl(
|
|
3094
3103
|
connection: string,
|
|
3095
3104
|
sessionId: string
|
|
@@ -3632,6 +3641,9 @@ export class SessionEngine {
|
|
|
3632
3641
|
input: validation.value,
|
|
3633
3642
|
asOf: record?.asOf,
|
|
3634
3643
|
timeArgs: tool.definition.timeArgs,
|
|
3644
|
+
...(tool.advertised === true && record?.asOf !== undefined
|
|
3645
|
+
? { timeWindow: schemaTimeWindowKeys(tool.inputSchema) }
|
|
3646
|
+
: {}),
|
|
3635
3647
|
});
|
|
3636
3648
|
|
|
3637
3649
|
const started = Date.now();
|
|
@@ -3770,28 +3782,42 @@ export class SessionEngine {
|
|
|
3770
3782
|
}
|
|
3771
3783
|
|
|
3772
3784
|
/**
|
|
3773
|
-
* Whether
|
|
3785
|
+
* Whether the host answers a bridged MCP call, for the cloud host bridge —
|
|
3774
3786
|
* the one tool path that does not reach {@link executeServerTool}, because
|
|
3775
3787
|
* it forwards to `host.mcp` instead of invoking a tool body.
|
|
3776
3788
|
*
|
|
3777
|
-
* A
|
|
3778
|
-
*
|
|
3779
|
-
*
|
|
3780
|
-
*
|
|
3789
|
+
* A frozen session refuses forward-looking time args here even when it is
|
|
3790
|
+
* not a dry run. A bridged tool carries no `defineTool`, so it declares no
|
|
3791
|
+
* effect, and the policy reads an undeclared tool as a write. In a dry run
|
|
3792
|
+
* that answers bridged reads too; resolving effects from a server's own
|
|
3793
|
+
* listing is the next unit's work.
|
|
3781
3794
|
*/
|
|
3782
3795
|
async bridgedCallAnswer(args: {
|
|
3783
3796
|
sessionId: string;
|
|
3784
3797
|
connectionName: string;
|
|
3785
3798
|
toolName: string;
|
|
3799
|
+
input?: unknown;
|
|
3786
3800
|
}): Promise<
|
|
3787
3801
|
{ answered: true; result: SDKCustomToolResult } | { answered: false }
|
|
3788
3802
|
> {
|
|
3789
3803
|
// Read off the in-flight turn's context rather than the session store:
|
|
3790
3804
|
// the bridge already refuses a call with no active turn, so the record is
|
|
3791
|
-
// in memory.
|
|
3792
|
-
// which is every session today — the listing below is only paid inside a
|
|
3793
|
-
// dry run.
|
|
3805
|
+
// in memory. The listing below is only paid inside a dry run.
|
|
3794
3806
|
const context = this.activeToolContexts.get(args.sessionId);
|
|
3807
|
+
if (context?.record.asOf !== undefined) {
|
|
3808
|
+
const decision = decideToolCall({
|
|
3809
|
+
dryRun: false,
|
|
3810
|
+
effect: undefined,
|
|
3811
|
+
input: args.input,
|
|
3812
|
+
asOf: context.record.asOf,
|
|
3813
|
+
});
|
|
3814
|
+
if (decision.kind === "unbounded") {
|
|
3815
|
+
this.logger(
|
|
3816
|
+
`[session] bridged tool ${args.toolName} refused, unbounded_time (${args.sessionId})`
|
|
3817
|
+
);
|
|
3818
|
+
return { answered: true, result: toolErrorResult(decision.message) };
|
|
3819
|
+
}
|
|
3820
|
+
}
|
|
3795
3821
|
if (context?.record.dryRun !== true) {
|
|
3796
3822
|
return { answered: false };
|
|
3797
3823
|
}
|
|
@@ -3799,14 +3825,16 @@ export class SessionEngine {
|
|
|
3799
3825
|
// server's own listing annotations; a tool the listing cannot classify
|
|
3800
3826
|
// refuses rather than being stubbed. A failed listing classifies nothing,
|
|
3801
3827
|
// which lands on the same refusal — never on a run.
|
|
3802
|
-
const
|
|
3803
|
-
args.connectionName,
|
|
3804
|
-
args.toolName
|
|
3805
|
-
);
|
|
3828
|
+
const info = await this.bridgedToolInfo(args.connectionName, args.toolName);
|
|
3806
3829
|
const decision = decideToolCall({
|
|
3807
3830
|
dryRun: true,
|
|
3808
3831
|
effect: undefined,
|
|
3809
|
-
mcpTool: { name: args.toolName, annotations },
|
|
3832
|
+
mcpTool: { name: args.toolName, annotations: info?.annotations },
|
|
3833
|
+
input: args.input,
|
|
3834
|
+
asOf: context.record.asOf,
|
|
3835
|
+
...(context.record.asOf !== undefined
|
|
3836
|
+
? { timeWindow: schemaTimeWindowKeys(info?.inputSchema) }
|
|
3837
|
+
: {}),
|
|
3810
3838
|
});
|
|
3811
3839
|
if (decision.kind === "run") {
|
|
3812
3840
|
return { answered: false };
|
|
@@ -3842,13 +3870,13 @@ export class SessionEngine {
|
|
|
3842
3870
|
* undefined when the tool is unlisted, unannotated, or the listing fails —
|
|
3843
3871
|
* all of which the policy reads as unclassified, the refusing state.
|
|
3844
3872
|
*/
|
|
3845
|
-
private async
|
|
3873
|
+
private async bridgedToolInfo(
|
|
3846
3874
|
connectionName: string,
|
|
3847
3875
|
toolName: string
|
|
3848
|
-
): Promise<
|
|
3876
|
+
): Promise<HostMcpToolInfo | undefined> {
|
|
3849
3877
|
try {
|
|
3850
3878
|
const listed = await this.host.mcp.listTools(connectionName);
|
|
3851
|
-
return listed.find((tool) => tool.name === toolName)
|
|
3879
|
+
return listed.find((tool) => tool.name === toolName);
|
|
3852
3880
|
} catch {
|
|
3853
3881
|
return undefined;
|
|
3854
3882
|
}
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
* session key with `processDeath: true` and the real throw. Soft-fail on
|
|
15
15
|
* persist so a store outage never changes the hosted-execution HTTP reply.
|
|
16
16
|
*
|
|
17
|
-
*
|
|
17
|
+
* Same flush moments as `hosted-execution.json` and the start.log tee —
|
|
18
|
+
* see `hosted-execution-flush.ts`. Keys: {@link storageKeys.sessionRun}.
|
|
18
19
|
*/
|
|
19
20
|
|
|
20
21
|
import type { FileSink } from "../files.js";
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable hosted `start.log` on the framework file store. **v2 only.**
|
|
3
|
+
*
|
|
4
|
+
* The engine tees stdout/stderr to a local file. That file is the v2
|
|
5
|
+
* customer log (errors, traces, debug). It is NOT written to S3 per line:
|
|
6
|
+
* callers snapshot the local buffer and PUT once. The Temporal host does
|
|
7
|
+
* a last catch-up at the end of the delivery so the object is complete
|
|
8
|
+
* after park/discard.
|
|
9
|
+
*
|
|
10
|
+
* Same flush moments as `hosted-execution.json` and session-runs —
|
|
11
|
+
* see `hosted-execution-flush.ts`. Keys: {@link storageKeys.log}.
|
|
12
|
+
* Values are raw UTF-8, not JSON.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { readFile } from "node:fs/promises";
|
|
16
|
+
import type { FileSink } from "../files.js";
|
|
17
|
+
import {
|
|
18
|
+
cursorFrameworkStoreSourceId,
|
|
19
|
+
cursorHostedFiles,
|
|
20
|
+
} from "../files-backends/cursor-hosted.js";
|
|
21
|
+
import { storageKeys } from "../storage-protocol.js";
|
|
22
|
+
import type { AgentProject } from "../types.js";
|
|
23
|
+
import { resolveApiKeySync } from "./cursor/credentials.js";
|
|
24
|
+
import { AGENT_SERVE_GENERATION_ENV_VAR } from "./cursor-relay-core.js";
|
|
25
|
+
import { describeError } from "./describe-error.js";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Last N bytes flushed to the store. Matches the Temporal catch-up cap
|
|
29
|
+
* so guest snapshots and host catch-up stay interchangeable. The store
|
|
30
|
+
* allows 64 MiB; this keeps the dashboard snapshot bounded.
|
|
31
|
+
*/
|
|
32
|
+
export const START_LOG_STORE_MAX_BYTES: number = 2 * 1024 * 1024;
|
|
33
|
+
|
|
34
|
+
/** Same path the hosted start command tees. Overridable in tests. */
|
|
35
|
+
export const DEFAULT_ENGINE_START_LOG_PATH =
|
|
36
|
+
"/workspace/.agent-serve/start.log";
|
|
37
|
+
|
|
38
|
+
export interface StartLogStore {
|
|
39
|
+
persist(startLogId: string, contents: string): Promise<void>;
|
|
40
|
+
read(startLogId: string): Promise<string | undefined>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function decodeSinkBody(body: Uint8Array | { body: Uint8Array }): Uint8Array {
|
|
44
|
+
return body instanceof Uint8Array ? body : body.body;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function tailStartLogText(raw: string, maxBytes: number): string {
|
|
48
|
+
const encoded = new TextEncoder().encode(raw);
|
|
49
|
+
if (encoded.byteLength <= maxBytes) {
|
|
50
|
+
return raw;
|
|
51
|
+
}
|
|
52
|
+
return new TextDecoder().decode(
|
|
53
|
+
encoded.subarray(encoded.byteLength - maxBytes)
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function resolveEngineStartLogPath(
|
|
58
|
+
env: NodeJS.ProcessEnv = process.env
|
|
59
|
+
): string {
|
|
60
|
+
const configured = env.AGENT_SERVE_ENGINE_START_LOG_PATH?.trim();
|
|
61
|
+
return configured !== undefined && configured.length > 0
|
|
62
|
+
? configured
|
|
63
|
+
: DEFAULT_ENGINE_START_LOG_PATH;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Read the local engine tee. Missing/empty is `undefined` (no store write).
|
|
68
|
+
* Soft-fail: a local read error must not change hosted-execution HTTP.
|
|
69
|
+
*/
|
|
70
|
+
export async function readLocalStartLogText(args?: {
|
|
71
|
+
path?: string;
|
|
72
|
+
maxBytes?: number;
|
|
73
|
+
}): Promise<string | undefined> {
|
|
74
|
+
const path = args?.path ?? resolveEngineStartLogPath();
|
|
75
|
+
const maxBytes = args?.maxBytes ?? START_LOG_STORE_MAX_BYTES;
|
|
76
|
+
try {
|
|
77
|
+
const raw = await readFile(path, "utf8");
|
|
78
|
+
if (raw.length === 0) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
return tailStartLogText(raw, maxBytes);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
if (
|
|
84
|
+
error !== null &&
|
|
85
|
+
typeof error === "object" &&
|
|
86
|
+
"code" in error &&
|
|
87
|
+
(error as { code?: string }).code === "ENOENT"
|
|
88
|
+
) {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function createStartLogStore(input: {
|
|
96
|
+
sink: FileSink;
|
|
97
|
+
agentName: string;
|
|
98
|
+
logger?: (line: string) => void;
|
|
99
|
+
}): StartLogStore {
|
|
100
|
+
const { sink, agentName } = input;
|
|
101
|
+
const logger = input.logger ?? (() => {});
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
async persist(startLogId, contents) {
|
|
105
|
+
const key = storageKeys.log(agentName, startLogId);
|
|
106
|
+
const body = tailStartLogText(contents, START_LOG_STORE_MAX_BYTES);
|
|
107
|
+
try {
|
|
108
|
+
await sink.put(key, new TextEncoder().encode(body));
|
|
109
|
+
} catch (error) {
|
|
110
|
+
logger(
|
|
111
|
+
`[agent-sdk] start-log: persist failed for ${startLogId} (${describeError(error)})`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
async read(startLogId) {
|
|
117
|
+
const key = storageKeys.log(agentName, startLogId);
|
|
118
|
+
try {
|
|
119
|
+
const body = await sink.get(key);
|
|
120
|
+
if (body === undefined) {
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
const text = new TextDecoder().decode(decodeSinkBody(body));
|
|
124
|
+
return text.length === 0 ? undefined : text;
|
|
125
|
+
} catch (error) {
|
|
126
|
+
logger(
|
|
127
|
+
`[agent-sdk] start-log: read failed for ${startLogId} (${describeError(error)})`
|
|
128
|
+
);
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Same selection as the session-run log: architecture-v2 hosted engines.
|
|
137
|
+
*/
|
|
138
|
+
export function selectStartLogStore(input: {
|
|
139
|
+
project: AgentProject;
|
|
140
|
+
logger?: (line: string) => void;
|
|
141
|
+
env?: Record<string, string | undefined>;
|
|
142
|
+
}): StartLogStore | undefined {
|
|
143
|
+
if (input.project.agent.architecture !== "v2") {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
const env = input.env ?? process.env;
|
|
147
|
+
if (env[AGENT_SERVE_GENERATION_ENV_VAR] === undefined) {
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
const envSnapshot = env as NodeJS.ProcessEnv;
|
|
151
|
+
return createStartLogStore({
|
|
152
|
+
sink: cursorHostedFiles({
|
|
153
|
+
resolveStoreSourceId: () => cursorFrameworkStoreSourceId(envSnapshot),
|
|
154
|
+
resolveApiKey: () => resolveApiKeySync()?.apiKey,
|
|
155
|
+
}),
|
|
156
|
+
agentName: input.project.name,
|
|
157
|
+
logger: input.logger,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Snapshot the local tee into the store. No-op when the file is missing
|
|
163
|
+
* or empty. Soft-fail inside {@link StartLogStore.persist}.
|
|
164
|
+
*/
|
|
165
|
+
export async function flushLocalStartLog(args: {
|
|
166
|
+
store: StartLogStore;
|
|
167
|
+
startLogId: string;
|
|
168
|
+
path?: string;
|
|
169
|
+
}): Promise<void> {
|
|
170
|
+
const contents = await readLocalStartLogText(
|
|
171
|
+
args.path === undefined ? undefined : { path: args.path }
|
|
172
|
+
);
|
|
173
|
+
if (contents === undefined) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
await args.store.persist(args.startLogId, contents);
|
|
177
|
+
}
|