@cursor/july 0.1.104 → 0.1.107
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 +1 -8
- package/dist/channels/bitbucket/types.d.ts +2 -4
- package/dist/channels/bitbucket/types.d.ts.map +1 -1
- package/dist/channels/deployments/deployments-channel.d.ts.map +1 -1
- package/dist/channels/deployments/deployments-channel.js +1 -0
- package/dist/channels/deployments/types.d.ts +7 -8
- package/dist/channels/deployments/types.d.ts.map +1 -1
- package/dist/channels/github/types.d.ts +2 -4
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/channels/gitlab/types.d.ts +2 -5
- package/dist/channels/gitlab/types.d.ts.map +1 -1
- package/dist/channels/origin/types.d.ts +2 -3
- package/dist/channels/origin/types.d.ts.map +1 -1
- package/dist/channels/slack/defaults.d.ts.map +1 -1
- package/dist/channels/slack/defaults.js +3 -0
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +1 -1
- package/dist/channels/slack/live-delivery.d.ts.map +1 -1
- package/dist/channels/slack/live-delivery.js +48 -11
- package/dist/channels/slack/types.d.ts +4 -3
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/docs/404.html +3 -3
- package/dist/docs/ab.html +6 -6
- package/dist/docs/assets/{app.DbMWUyGb.js → app.CtuckIiL.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DXXZxiMv.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.DcNeTh9o.js → VPLocalSearchBox.Bkkhnu8K.js} +2 -2
- package/dist/docs/assets/chunks/{theme.DMjhY3gl.js → theme.DhpIe0Pa.js} +2 -2
- package/dist/docs/assets/guides_grokbot-agents.md.DVqdhBKb.js +9 -0
- package/dist/docs/assets/guides_grokbot-agents.md.DVqdhBKb.lean.js +1 -0
- package/dist/docs/assets/{guides_opentelemetry.md.COh9jFVg.js → guides_opentelemetry.md.SCRMskno.js} +3 -3
- package/dist/docs/assets/{guides_opentelemetry.md.COh9jFVg.lean.js → guides_opentelemetry.md.SCRMskno.lean.js} +1 -1
- package/dist/docs/assets/index.md.BW_6tOgR.js +5 -0
- package/dist/docs/assets/index.md.BW_6tOgR.lean.js +1 -0
- package/dist/docs/assets/{reference_channels.md.DBGcaB7m.js → reference_channels.md.D-qTqwcq.js} +2 -2
- package/dist/docs/assets/{reference_channels.md.DBGcaB7m.lean.js → reference_channels.md.D-qTqwcq.lean.js} +1 -1
- package/dist/docs/assets/{reference_cli.md.CJNc_xYK.js → reference_cli.md.Dm67hd2D.js} +3 -2
- package/dist/docs/assets/{reference_extensions.md.DykHW3qL.js → reference_extensions.md.CGmMLblt.js} +1 -1
- package/dist/docs/assets/{skills_otel.md.CqhobH3B.js → skills_otel.md.B5Ddv1xH.js} +1 -1
- package/dist/docs/assets/{style.BRuM8477.css → style.DCD_zyg3.css} +1 -1
- package/dist/docs/assets/templates_grokbot-agents.md.c1s6Flp9.js +3 -0
- package/dist/docs/assets/templates_grokbot-agents.md.c1s6Flp9.lean.js +1 -0
- package/dist/docs/building-with-agents.html +6 -6
- package/dist/docs/concepts.html +5 -5
- package/dist/docs/deployment.html +6 -6
- package/dist/docs/evals.html +6 -6
- package/dist/docs/guides/agent-to-agent.html +6 -6
- package/dist/docs/guides/cloud-agents.html +6 -6
- package/dist/docs/guides/convert-automation.html +6 -6
- package/dist/docs/guides/github.html +6 -6
- package/dist/docs/guides/grokbot-agents.html +35 -0
- package/dist/docs/guides/grokbot-agents.md +88 -0
- package/dist/docs/guides/human-in-the-loop.html +6 -6
- package/dist/docs/guides/mcp-oauth.html +6 -6
- package/dist/docs/guides/opentelemetry.html +8 -8
- package/dist/docs/guides/opentelemetry.md +3 -4
- package/dist/docs/guides/slack.html +6 -6
- package/dist/docs/guides/webhooks.html +6 -6
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +6 -6
- package/dist/docs/index.html +8 -8
- package/dist/docs/index.md +6 -1
- package/dist/docs/llms-full.txt +178 -30
- package/dist/docs/llms.txt +2 -0
- package/dist/docs/quickstart.html +6 -6
- package/dist/docs/reference/agent-config.html +6 -6
- package/dist/docs/reference/artifacts.html +6 -6
- package/dist/docs/reference/channels.html +7 -7
- package/dist/docs/reference/channels.md +11 -19
- package/dist/docs/reference/cli.html +10 -9
- package/dist/docs/reference/cli.md +7 -3
- package/dist/docs/reference/connections.html +6 -6
- package/dist/docs/reference/extensions.html +7 -7
- package/dist/docs/reference/extensions.md +2 -0
- package/dist/docs/reference/hooks.html +6 -6
- package/dist/docs/reference/http-api.html +6 -6
- package/dist/docs/reference/instructions.html +6 -6
- package/dist/docs/reference/playground.html +5 -5
- package/dist/docs/reference/project-layout.html +6 -6
- package/dist/docs/reference/prompt.html +6 -6
- package/dist/docs/reference/result.html +6 -6
- package/dist/docs/reference/schedules.html +6 -6
- package/dist/docs/reference/sessions.html +5 -5
- package/dist/docs/reference/skills.html +6 -6
- package/dist/docs/reference/subagents.html +6 -6
- package/dist/docs/reference/tools.html +6 -6
- package/dist/docs/scaffolding-agents.html +5 -5
- package/dist/docs/skills/ab.html +6 -6
- package/dist/docs/skills/create-agent.html +6 -6
- package/dist/docs/skills/debug.html +5 -5
- package/dist/docs/skills/evals.html +6 -6
- package/dist/docs/skills/framework-map.html +5 -5
- package/dist/docs/skills/github.html +6 -6
- package/dist/docs/skills/hillclimb.html +6 -6
- package/dist/docs/skills/index.html +5 -5
- package/dist/docs/skills/mcp-auth.html +6 -6
- package/dist/docs/skills/otel.html +8 -8
- package/dist/docs/skills/otel.md +3 -3
- package/dist/docs/skills/setup-slack.html +6 -6
- package/dist/docs/storage.html +6 -6
- package/dist/docs/templates/agentic-owners.html +6 -6
- package/dist/docs/templates/agents-md.html +6 -6
- package/dist/docs/templates/code-wiki.html +6 -6
- package/dist/docs/templates/demo.html +6 -6
- package/dist/docs/templates/grokbot-agents.html +29 -0
- package/dist/docs/templates/grokbot-agents.md +48 -0
- package/dist/docs/templates/pr-autofixer.html +6 -6
- package/dist/docs/templates/security-help.html +6 -6
- package/dist/docs/templates/security-reviewer.html +6 -6
- package/dist/docs/templates/triage.html +6 -6
- package/dist/docs/troubleshooting.html +5 -5
- package/dist/files.d.ts +7 -2
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +7 -2
- package/dist/index.d.ts +4 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -4
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +23 -11
- package/dist/internal/cursor/backend-client.d.ts +25 -0
- package/dist/internal/cursor/backend-client.d.ts.map +1 -1
- package/dist/internal/cursor/backend-client.js +57 -2
- package/dist/internal/cursor-agent-template.d.ts +1 -1
- package/dist/internal/cursor-agent-template.d.ts.map +1 -1
- package/dist/internal/cursor-agent-template.js +1 -0
- package/dist/internal/deploy-client.d.ts +8 -0
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +14 -0
- package/dist/internal/deploy-manifest.d.ts +3 -0
- package/dist/internal/deploy-manifest.d.ts.map +1 -1
- package/dist/internal/deploy-manifest.js +15 -12
- package/dist/internal/discovery/agent.d.ts.map +1 -1
- package/dist/internal/discovery/agent.js +1 -0
- package/dist/internal/discovery/info.d.ts.map +1 -1
- package/dist/internal/discovery/info.js +7 -3
- package/dist/internal/discovery/modules.d.ts +1 -1
- package/dist/internal/discovery/modules.d.ts.map +1 -1
- package/dist/internal/discovery/modules.js +1 -0
- package/dist/internal/discovery/project.d.ts.map +1 -1
- package/dist/internal/discovery/project.js +3 -0
- package/dist/internal/discovery/reminders.d.ts +7 -0
- package/dist/internal/discovery/reminders.d.ts.map +1 -0
- package/dist/internal/discovery/reminders.js +65 -0
- package/dist/internal/discovery/subagent.js +1 -0
- package/dist/internal/distribution.d.ts +5 -0
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +17 -0
- package/dist/internal/framework-file-storage.d.ts +3 -1
- package/dist/internal/framework-file-storage.d.ts.map +1 -1
- package/dist/internal/framework-file-storage.js +3 -1
- package/dist/internal/framework-storage-selection.d.ts +14 -3
- package/dist/internal/framework-storage-selection.d.ts.map +1 -1
- package/dist/internal/framework-storage-selection.js +36 -6
- package/dist/internal/guest-network.d.ts.map +1 -1
- package/dist/internal/guest-network.js +2 -0
- package/dist/internal/hosted-delivery-protocol.d.ts +48 -0
- package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
- package/dist/internal/hosted-delivery-protocol.js +60 -0
- package/dist/internal/hosted-delivery.d.ts +17 -0
- package/dist/internal/hosted-delivery.d.ts.map +1 -1
- package/dist/internal/hosted-delivery.js +112 -19
- package/dist/internal/hosted-managed-automation.d.ts.map +1 -1
- package/dist/internal/hosted-managed-automation.js +27 -2
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +4 -1
- package/dist/internal/local-control-plane.d.ts +18 -2
- package/dist/internal/local-control-plane.d.ts.map +1 -1
- package/dist/internal/local-control-plane.js +199 -17
- package/dist/internal/reminder-control-plane-protocol.d.ts +187 -0
- package/dist/internal/reminder-control-plane-protocol.d.ts.map +1 -0
- package/dist/internal/reminder-control-plane-protocol.js +244 -0
- package/dist/internal/reminder-runner.d.ts +48 -6
- package/dist/internal/reminder-runner.d.ts.map +1 -1
- package/dist/internal/reminder-runner.js +336 -48
- package/dist/internal/reminder-schedule.d.ts +5 -2
- package/dist/internal/reminder-schedule.d.ts.map +1 -1
- package/dist/internal/reminder-schedule.js +8 -6
- package/dist/internal/reminder-store.d.ts +24 -7
- package/dist/internal/reminder-store.d.ts.map +1 -1
- package/dist/internal/reminder-store.js +6 -5
- package/dist/internal/request-error-status.d.ts +2 -0
- package/dist/internal/request-error-status.d.ts.map +1 -1
- package/dist/internal/request-error-status.js +22 -3
- package/dist/internal/scm/pr-url.d.ts +40 -0
- package/dist/internal/scm/pr-url.d.ts.map +1 -1
- package/dist/internal/scm/pr-url.js +93 -14
- package/dist/internal/sdk-runner.d.ts +12 -0
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +18 -0
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +78 -30
- package/dist/internal/session-engine.d.ts +12 -0
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +137 -52
- package/dist/internal/store-mirror.d.ts +2 -2
- package/dist/internal/store-mirror.js +2 -2
- package/dist/internal/tenant-slack/relay.d.ts +2 -0
- package/dist/internal/tenant-slack/relay.d.ts.map +1 -1
- package/dist/internal/tenant-slack/relay.js +18 -8
- package/dist/internal/workspace-sync.d.ts +31 -6
- package/dist/internal/workspace-sync.d.ts.map +1 -1
- package/dist/internal/workspace-sync.js +67 -12
- package/dist/internal/workspace.d.ts +9 -0
- package/dist/internal/workspace.d.ts.map +1 -1
- package/dist/internal/workspace.js +16 -0
- package/dist/playground/assets/index-Bq2HpEQB.js +67 -0
- package/dist/playground/assets/index-CZKKNlmb.css +1 -0
- package/dist/playground/index.html +2 -2
- package/dist/reminders.d.ts +24 -5
- package/dist/reminders.d.ts.map +1 -1
- package/dist/reminders.js +28 -5
- package/dist/types.d.ts +98 -28
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +6 -1
- package/docs/guides/grokbot-agents.md +93 -0
- package/docs/guides/opentelemetry.md +3 -4
- package/docs/reference/channels.md +11 -19
- package/docs/reference/cli.md +7 -3
- package/docs/reference/extensions.md +2 -0
- package/docs/templates/grokbot-agents.md +53 -0
- package/package.json +12 -3
- package/skills/otel/SKILL.md +3 -3
- package/src/bin/agent-serve.ts +1 -9
- package/src/channels/bitbucket/types.ts +2 -4
- package/src/channels/deployments/deployments-channel.ts +1 -0
- package/src/channels/deployments/types.ts +7 -8
- package/src/channels/github/types.ts +2 -4
- package/src/channels/gitlab/types.ts +2 -5
- package/src/channels/origin/types.ts +2 -3
- package/src/channels/slack/defaults.ts +3 -0
- package/src/channels/slack/dispatch.ts +1 -0
- package/src/channels/slack/live-delivery.ts +53 -12
- package/src/channels/slack/types.ts +4 -3
- package/src/files.ts +9 -2
- package/src/index.ts +6 -3
- package/src/internal/cli-deploy.ts +30 -12
- package/src/internal/cursor/backend-client.ts +90 -2
- package/src/internal/cursor-agent-template.ts +1 -0
- package/src/internal/deploy-client.ts +16 -0
- package/src/internal/deploy-manifest.ts +16 -11
- package/src/internal/discovery/agent.ts +1 -0
- package/src/internal/discovery/info.ts +6 -0
- package/src/internal/discovery/modules.ts +2 -4
- package/src/internal/discovery/project.ts +3 -0
- package/src/internal/discovery/reminders.ts +65 -0
- package/src/internal/discovery/subagent.ts +1 -0
- package/src/internal/distribution.ts +20 -0
- package/src/internal/framework-file-storage.ts +3 -1
- package/src/internal/framework-storage-selection.ts +43 -6
- package/src/internal/guest-network.ts +3 -0
- package/src/internal/hosted-delivery-protocol.ts +96 -0
- package/src/internal/hosted-delivery.ts +207 -28
- package/src/internal/hosted-managed-automation.ts +49 -5
- package/src/internal/init-project.ts +4 -1
- package/src/internal/local-control-plane.ts +281 -22
- package/src/internal/reminder-control-plane-protocol.ts +499 -0
- package/src/internal/reminder-runner.ts +421 -48
- package/src/internal/reminder-schedule.ts +11 -6
- package/src/internal/reminder-store.ts +27 -7
- package/src/internal/request-error-status.ts +27 -3
- package/src/internal/scm/pr-url.ts +167 -20
- package/src/internal/sdk-runner.ts +19 -0
- package/src/internal/server.ts +85 -16
- package/src/internal/session-engine.ts +173 -56
- package/src/internal/store-mirror.ts +2 -2
- package/src/internal/tenant-slack/relay.ts +28 -4
- package/src/internal/workspace-sync.ts +84 -12
- package/src/internal/workspace.ts +20 -0
- package/src/reminders.ts +35 -4
- package/src/types.ts +104 -28
- package/templates/grokbot-agents/README.md +26 -0
- package/templates/grokbot-agents/agent/agent.ts +6 -0
- package/templates/grokbot-agents/agent/extensions/grokbot.ts +5 -0
- package/templates/grokbot-agents/agent/instructions.md +7 -0
- package/templates/grokbot-agents/evals/evals.config.ts +5 -0
- package/templates/grokbot-agents/evals/grokbot-agents.eval.ts +18 -0
- package/templates/grokbot-agents/init.json +18 -0
- package/templates/grokbot-agents/package.json +17 -0
- package/templates/grokbot-agents/tsconfig.json +12 -0
- package/dist/docs/assets/chunks/@localSearchIndexroot.Bqsulb9K.js +0 -1
- package/dist/docs/assets/index.md.D75GWhPl.js +0 -5
- package/dist/docs/assets/index.md.D75GWhPl.lean.js +0 -1
- package/dist/playground/assets/index-8YiozU89.js +0 -67
- package/dist/playground/assets/index-MbMxXg-y.css +0 -1
- /package/dist/docs/assets/{reference_cli.md.CJNc_xYK.lean.js → reference_cli.md.Dm67hd2D.lean.js} +0 -0
- /package/dist/docs/assets/{reference_extensions.md.DykHW3qL.lean.js → reference_extensions.md.CGmMLblt.lean.js} +0 -0
- /package/dist/docs/assets/{skills_otel.md.CqhobH3B.lean.js → skills_otel.md.B5Ddv1xH.lean.js} +0 -0
|
@@ -202,13 +202,19 @@ import { resolveAgentV2Hosting } from "./v2-hosting.js";
|
|
|
202
202
|
import {
|
|
203
203
|
buildAgentsMdContent,
|
|
204
204
|
buildCloudMemorySection,
|
|
205
|
+
buildCloudSessionWorkspaceSection,
|
|
205
206
|
buildCloudWorkspaceSection,
|
|
206
207
|
ensureOwnGitRoot,
|
|
207
208
|
isNestedInGitRepo,
|
|
208
209
|
materializeWorkspace,
|
|
209
210
|
SERVER_TOOLS_MCP_SERVER_NAME,
|
|
210
211
|
} from "./workspace.js";
|
|
211
|
-
import {
|
|
212
|
+
import {
|
|
213
|
+
syncSessionWorkspaceFiles,
|
|
214
|
+
syncWorkspaceToStore,
|
|
215
|
+
WorkspaceFileRefusal,
|
|
216
|
+
wipeSessionWorkspace,
|
|
217
|
+
} from "./workspace-sync.js";
|
|
212
218
|
|
|
213
219
|
export const HTTP_CHANNEL_ID = "http";
|
|
214
220
|
export { SCHEDULE_CHANNEL_PREFIX } from "./prompt-context.js";
|
|
@@ -222,6 +228,7 @@ interface CloudStoreTarget {
|
|
|
222
228
|
mountPath: string;
|
|
223
229
|
skillsPrefix: string;
|
|
224
230
|
workspacePrefix: string;
|
|
231
|
+
sessionWorkspacePrefix: (sessionId: string) => string;
|
|
225
232
|
label: string;
|
|
226
233
|
}
|
|
227
234
|
|
|
@@ -2254,10 +2261,13 @@ export class SessionEngine {
|
|
|
2254
2261
|
await this.baselineDeliveredNudges(record);
|
|
2255
2262
|
const pendingFiles = record.pendingWorkspaceFiles;
|
|
2256
2263
|
const pendingImages = record.pendingImages;
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
)
|
|
2264
|
+
const runtime = resolveSessionRuntime({
|
|
2265
|
+
agentRuntime: agent.runtime,
|
|
2266
|
+
cloudOverride: record.cloudOverride,
|
|
2267
|
+
});
|
|
2268
|
+
const hasPendingFiles =
|
|
2269
|
+
pendingFiles !== undefined && Object.keys(pendingFiles).length > 0;
|
|
2270
|
+
if (hasPendingFiles && runtime !== "cloud") {
|
|
2261
2271
|
const root = await this.resolveFileSystem({
|
|
2262
2272
|
sessionId,
|
|
2263
2273
|
continuationKey: record.continuationKey ?? undefined,
|
|
@@ -2267,13 +2277,14 @@ export class SessionEngine {
|
|
|
2267
2277
|
await writeWorkspaceFilesToRoot(root, pendingFiles);
|
|
2268
2278
|
}
|
|
2269
2279
|
if (
|
|
2270
|
-
(
|
|
2271
|
-
Object.keys(pendingFiles).length > 0) ||
|
|
2280
|
+
(hasPendingFiles && runtime !== "cloud") ||
|
|
2272
2281
|
(pendingImages !== undefined && pendingImages.length > 0)
|
|
2273
2282
|
) {
|
|
2274
2283
|
await this.updateRecord(sessionId, (current) => ({
|
|
2275
2284
|
...current,
|
|
2276
2285
|
pendingWorkspaceFiles:
|
|
2286
|
+
hasPendingFiles &&
|
|
2287
|
+
runtime !== "cloud" &&
|
|
2277
2288
|
current.pendingWorkspaceFiles === pendingFiles
|
|
2278
2289
|
? undefined
|
|
2279
2290
|
: current.pendingWorkspaceFiles,
|
|
@@ -2441,6 +2452,7 @@ export class SessionEngine {
|
|
|
2441
2452
|
data: { message: outcome.errorMessage ?? "task failed" },
|
|
2442
2453
|
}
|
|
2443
2454
|
);
|
|
2455
|
+
await this.wipeCloudSessionWorkspace(sessionId);
|
|
2444
2456
|
} else {
|
|
2445
2457
|
await this.appendEvent(sessionId, {
|
|
2446
2458
|
type: "session.waiting",
|
|
@@ -2495,6 +2507,68 @@ export class SessionEngine {
|
|
|
2495
2507
|
);
|
|
2496
2508
|
}
|
|
2497
2509
|
|
|
2510
|
+
/**
|
|
2511
|
+
* Replace this session's store prefix with the pending `workspaceFiles`.
|
|
2512
|
+
* Undefined when the write did not land; the prompt must not name the
|
|
2513
|
+
* mount, matching ensureCloudWorkspaceSynced.
|
|
2514
|
+
*/
|
|
2515
|
+
private async syncCloudSessionWorkspaceFiles(
|
|
2516
|
+
store: CloudStoreTarget,
|
|
2517
|
+
sessionId: string,
|
|
2518
|
+
files: NonNullable<SessionRecord["pendingWorkspaceFiles"]>
|
|
2519
|
+
): Promise<{ dir: string; relPaths: string[] } | undefined> {
|
|
2520
|
+
const workspacePrefix = store.sessionWorkspacePrefix(sessionId);
|
|
2521
|
+
try {
|
|
2522
|
+
const relPaths = await syncSessionWorkspaceFiles({
|
|
2523
|
+
sink: store.sink,
|
|
2524
|
+
files,
|
|
2525
|
+
workspacePrefix,
|
|
2526
|
+
});
|
|
2527
|
+
return {
|
|
2528
|
+
dir: `${store.mountPath}/${workspacePrefix}`,
|
|
2529
|
+
relPaths,
|
|
2530
|
+
};
|
|
2531
|
+
} catch (error) {
|
|
2532
|
+
this.logger(
|
|
2533
|
+
`[workspace] ${store.label} session files sync failed: ${error instanceof Error ? error.message : String(error)}`
|
|
2534
|
+
);
|
|
2535
|
+
if (error instanceof WorkspaceFileRefusal) {
|
|
2536
|
+
await this.clearPendingWorkspaceFiles(sessionId, files);
|
|
2537
|
+
}
|
|
2538
|
+
return undefined;
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
|
|
2542
|
+
private async wipeCloudSessionWorkspace(sessionId: string): Promise<void> {
|
|
2543
|
+
const store = this.cloudStore();
|
|
2544
|
+
if (store === undefined) {
|
|
2545
|
+
return;
|
|
2546
|
+
}
|
|
2547
|
+
try {
|
|
2548
|
+
await wipeSessionWorkspace({
|
|
2549
|
+
sink: store.sink,
|
|
2550
|
+
workspacePrefix: store.sessionWorkspacePrefix(sessionId),
|
|
2551
|
+
});
|
|
2552
|
+
} catch (error) {
|
|
2553
|
+
this.logger(
|
|
2554
|
+
`[workspace] ${store.label} session files wipe failed: ${error instanceof Error ? error.message : String(error)}`
|
|
2555
|
+
);
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
|
|
2559
|
+
private async clearPendingWorkspaceFiles(
|
|
2560
|
+
sessionId: string,
|
|
2561
|
+
pending: NonNullable<SessionRecord["pendingWorkspaceFiles"]>
|
|
2562
|
+
): Promise<void> {
|
|
2563
|
+
await this.updateRecord(sessionId, (current) => ({
|
|
2564
|
+
...current,
|
|
2565
|
+
pendingWorkspaceFiles:
|
|
2566
|
+
current.pendingWorkspaceFiles === pending
|
|
2567
|
+
? undefined
|
|
2568
|
+
: current.pendingWorkspaceFiles,
|
|
2569
|
+
}));
|
|
2570
|
+
}
|
|
2571
|
+
|
|
2498
2572
|
/**
|
|
2499
2573
|
* With files authored, every first cloud turn re-copies the authored
|
|
2500
2574
|
* bytes and the memo only dedupes concurrent first turns. The mount is
|
|
@@ -2547,6 +2621,8 @@ export class SessionEngine {
|
|
|
2547
2621
|
mountPath: DEPLOYMENT_STORE_MOUNT_PATH,
|
|
2548
2622
|
skillsPrefix: agentStoreKeys.skillsPrefix(),
|
|
2549
2623
|
workspacePrefix: agentStoreKeys.workspacePrefix(),
|
|
2624
|
+
sessionWorkspacePrefix: (sessionId: string) =>
|
|
2625
|
+
agentStoreKeys.sessionWorkspacePrefix(sessionId),
|
|
2550
2626
|
label: "deployment store",
|
|
2551
2627
|
};
|
|
2552
2628
|
if (v2Hosting.kind === "cursor-managed") {
|
|
@@ -2569,6 +2645,11 @@ export class SessionEngine {
|
|
|
2569
2645
|
mountPath: USER_STORE_MOUNT_PATH,
|
|
2570
2646
|
skillsPrefix: agentStoreKeys.userSkillsPrefix(this.project.name),
|
|
2571
2647
|
workspacePrefix: agentStoreKeys.userWorkspacePrefix(this.project.name),
|
|
2648
|
+
sessionWorkspacePrefix: (sessionId: string) =>
|
|
2649
|
+
agentStoreKeys.userSessionWorkspacePrefix(
|
|
2650
|
+
this.project.name,
|
|
2651
|
+
sessionId
|
|
2652
|
+
),
|
|
2572
2653
|
label: "user store",
|
|
2573
2654
|
};
|
|
2574
2655
|
}
|
|
@@ -2690,34 +2771,39 @@ export class SessionEngine {
|
|
|
2690
2771
|
});
|
|
2691
2772
|
turnPrompt = `${contextBlock}\n\n${prompt}`;
|
|
2692
2773
|
}
|
|
2693
|
-
if (
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2774
|
+
if (runtime === "cloud") {
|
|
2775
|
+
// Cloud VMs have no materialized workspace: ship the full context,
|
|
2776
|
+
// including agent tool script bodies. Authored skills and workspace
|
|
2777
|
+
// seeds land on the store mount (skills for native discovery, seeds
|
|
2778
|
+
// for the model's file tools), per-turn workspaceFiles land under
|
|
2779
|
+
// the session prefix, and on hosted deployments the shared memory
|
|
2780
|
+
// journal is on that mount too — say where. When the turn carries
|
|
2781
|
+
// the session-bound server-tool MCP endpoint, the preamble catalogs
|
|
2782
|
+
// server tools under that MCP server name *before* the agent-tool
|
|
2783
|
+
// "write the script if missing" recipe, so a model told to "use
|
|
2784
|
+
// <server tool>" calls MCP instead of inventing a .sh.
|
|
2785
|
+
const hosted = isCursorHostedFilesAvailable();
|
|
2786
|
+
const store =
|
|
2787
|
+
hosted || isCursorUserStoreFilesAvailable()
|
|
2788
|
+
? this.cloudStore()
|
|
2789
|
+
: undefined;
|
|
2790
|
+
let preamble = "";
|
|
2791
|
+
const sections: string[] = [];
|
|
2792
|
+
const pending = record.pendingWorkspaceFiles;
|
|
2793
|
+
if (isFirstTurn) {
|
|
2794
|
+
preamble = buildAgentsMdContent(agent, {
|
|
2705
2795
|
includeScripts: true,
|
|
2706
2796
|
serverToolsMcpName:
|
|
2707
2797
|
this.cloudServerToolsConnection === undefined
|
|
2708
2798
|
? undefined
|
|
2709
2799
|
: SERVER_TOOLS_MCP_SERVER_NAME,
|
|
2710
2800
|
});
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
? this.cloudStore()
|
|
2718
|
-
: undefined;
|
|
2719
|
-
let sections = "";
|
|
2720
|
-
if (store !== undefined) {
|
|
2801
|
+
}
|
|
2802
|
+
if (store !== undefined) {
|
|
2803
|
+
if (isFirstTurn) {
|
|
2804
|
+
// Synced whenever a store is reachable, not just when skills or
|
|
2805
|
+
// seeds exist: removing the last one has to delete its store copies
|
|
2806
|
+
// too.
|
|
2721
2807
|
const [, seedsSynced] = await Promise.all([
|
|
2722
2808
|
this.ensureCloudSkillsSynced(store),
|
|
2723
2809
|
this.ensureCloudWorkspaceSynced(store),
|
|
@@ -2725,37 +2811,63 @@ export class SessionEngine {
|
|
|
2725
2811
|
// Only name the mount when the bytes landed; a failed sync must
|
|
2726
2812
|
// not send the model looking for files that are not there.
|
|
2727
2813
|
if (seedsSynced && agent.seedFiles.length > 0) {
|
|
2728
|
-
|
|
2729
|
-
|
|
2814
|
+
sections.push(
|
|
2815
|
+
buildCloudWorkspaceSection(
|
|
2816
|
+
`${store.mountPath}/${store.workspacePrefix}`
|
|
2817
|
+
)
|
|
2818
|
+
);
|
|
2730
2819
|
}
|
|
2731
2820
|
}
|
|
2732
|
-
if (
|
|
2733
|
-
|
|
2821
|
+
if (pending !== undefined && Object.keys(pending).length > 0) {
|
|
2822
|
+
const synced = await this.syncCloudSessionWorkspaceFiles(
|
|
2823
|
+
store,
|
|
2824
|
+
record.sessionId,
|
|
2825
|
+
pending
|
|
2826
|
+
);
|
|
2827
|
+
if (synced !== undefined) {
|
|
2828
|
+
sections.push(
|
|
2829
|
+
buildCloudSessionWorkspaceSection(synced.dir, synced.relPaths)
|
|
2830
|
+
);
|
|
2831
|
+
await this.clearPendingWorkspaceFiles(record.sessionId, pending);
|
|
2832
|
+
}
|
|
2734
2833
|
}
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
preamble === ""
|
|
2746
|
-
? `${workspaceInfo}\n\n${turnPrompt}`
|
|
2747
|
-
: `${workspaceInfo}\n\n${preamble}\n\n${turnPrompt}`;
|
|
2748
|
-
} else if (runtime === "local" && instructionsInvisibleToHarness) {
|
|
2749
|
-
// Local workspace whose AGENTS.md the harness will not load. With a
|
|
2750
|
-
// borrowed harness cwd, agent tool scripts live only in the durable
|
|
2751
|
-
// scaffold dir — embed bodies like cloud. Otherwise the catalog can
|
|
2752
|
-
// reference on-disk paths under the harness cwd. Grokbot turns are
|
|
2753
|
-
// excluded: their instructions ride the request's `instructions`
|
|
2754
|
-
// field into the Sand system prompt instead.
|
|
2755
|
-
const includeScripts = this.hasBorrowedWorkspace(record);
|
|
2756
|
-
const preamble = buildAgentsMdContent(agent, { includeScripts });
|
|
2757
|
-
turnPrompt = `${preamble}\n\n${turnPrompt}`;
|
|
2834
|
+
} else if (pending !== undefined && Object.keys(pending).length > 0) {
|
|
2835
|
+
await this.clearPendingWorkspaceFiles(record.sessionId, pending);
|
|
2836
|
+
}
|
|
2837
|
+
if (isFirstTurn && hosted) {
|
|
2838
|
+
sections.push(buildCloudMemorySection(this.project.name));
|
|
2839
|
+
}
|
|
2840
|
+
if (preamble !== "" || sections.length > 0) {
|
|
2841
|
+
turnPrompt = [preamble, ...sections, turnPrompt]
|
|
2842
|
+
.filter((part) => part !== "")
|
|
2843
|
+
.join("\n\n");
|
|
2758
2844
|
}
|
|
2845
|
+
} else if (isFirstTurn && runtime === "local" && authoredRoot) {
|
|
2846
|
+
// Virtual roots skip materialize, so AGENTS.md never lands on disk.
|
|
2847
|
+
// Ship the same preamble the borrowed-cwd local path uses.
|
|
2848
|
+
const preamble = buildAgentsMdContent(
|
|
2849
|
+
{ ...agent, tools: [...agent.tools, ...synthesized] },
|
|
2850
|
+
{ includeScripts: true }
|
|
2851
|
+
);
|
|
2852
|
+
const workspaceInfo = `<user_info>\nWorkspace Path: /\n</user_info>`;
|
|
2853
|
+
turnPrompt =
|
|
2854
|
+
preamble === ""
|
|
2855
|
+
? `${workspaceInfo}\n\n${turnPrompt}`
|
|
2856
|
+
: `${workspaceInfo}\n\n${preamble}\n\n${turnPrompt}`;
|
|
2857
|
+
} else if (
|
|
2858
|
+
isFirstTurn &&
|
|
2859
|
+
runtime === "local" &&
|
|
2860
|
+
instructionsInvisibleToHarness
|
|
2861
|
+
) {
|
|
2862
|
+
// Local workspace whose AGENTS.md the harness will not load. With a
|
|
2863
|
+
// borrowed harness cwd, agent tool scripts live only in the durable
|
|
2864
|
+
// scaffold dir — embed bodies like cloud. Otherwise the catalog can
|
|
2865
|
+
// reference on-disk paths under the harness cwd. Grokbot turns are
|
|
2866
|
+
// excluded: their instructions ride the request's `instructions`
|
|
2867
|
+
// field into the Sand system prompt instead.
|
|
2868
|
+
const includeScripts = this.hasBorrowedWorkspace(record);
|
|
2869
|
+
const preamble = buildAgentsMdContent(agent, { includeScripts });
|
|
2870
|
+
turnPrompt = `${preamble}\n\n${turnPrompt}`;
|
|
2759
2871
|
}
|
|
2760
2872
|
return {
|
|
2761
2873
|
sessionId: record.sessionId,
|
|
@@ -4632,6 +4744,10 @@ export class SessionEngine {
|
|
|
4632
4744
|
* conversation whose blobs persisted but whose record (which selects the
|
|
4633
4745
|
* conversation to hydrate) never reached the sink. No-op without a
|
|
4634
4746
|
* storage mirror.
|
|
4747
|
+
*
|
|
4748
|
+
* Under a debounced event policy the turn-end flush is a timer, not the
|
|
4749
|
+
* chain's tail, so flush explicitly before waiting on the queue; a no-op
|
|
4750
|
+
* when the chain already flushed.
|
|
4635
4751
|
*/
|
|
4636
4752
|
async drainSessionDurability(sessionId: string): Promise<void> {
|
|
4637
4753
|
if (this.storage === undefined) {
|
|
@@ -4641,6 +4757,7 @@ export class SessionEngine {
|
|
|
4641
4757
|
if (chain !== undefined) {
|
|
4642
4758
|
await chain.catch(() => {});
|
|
4643
4759
|
}
|
|
4760
|
+
this.storage.flushSession(sessionId);
|
|
4644
4761
|
await this.storage.whenIdle();
|
|
4645
4762
|
}
|
|
4646
4763
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Mirror an authored file tree onto an Agent Store prefix. Shared by the
|
|
3
|
-
* skills
|
|
4
|
-
* discipline and the same path refusals.
|
|
3
|
+
* skills, workspace seed, and session workspaceFile syncs so they keep the
|
|
4
|
+
* same write-then-sweep discipline and the same path refusals.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { posix } from "node:path";
|
|
@@ -109,13 +109,22 @@ export async function postRelaySlackMessage(args: {
|
|
|
109
109
|
resolve?: () => RelaySlackPostTarget | undefined;
|
|
110
110
|
fetchImpl?: typeof fetch;
|
|
111
111
|
timeoutMs?: number;
|
|
112
|
+
/** Diagnostic sink; defaults to `console.info`. */
|
|
113
|
+
log?: (line: string) => void;
|
|
112
114
|
}): Promise<RelaySlackPostResult> {
|
|
113
115
|
const channel = args.channel.trim();
|
|
116
|
+
const log = args.log ?? ((line: string) => console.info(line));
|
|
114
117
|
if (channel === "") {
|
|
118
|
+
log(
|
|
119
|
+
`[agent-sdk-slack-relay-client] refusing post automationUuid=${args.automationUuid}: empty channel`
|
|
120
|
+
);
|
|
115
121
|
return { ok: false, channel, status: 0, error: "empty channel" };
|
|
116
122
|
}
|
|
117
123
|
const target = (args.resolve ?? resolveRelaySlackTarget)();
|
|
118
124
|
if (target === undefined) {
|
|
125
|
+
log(
|
|
126
|
+
`[agent-sdk-slack-relay-client] refusing post automationUuid=${args.automationUuid} channel=${channel}: no Cursor credential resolved (CURSOR_API_KEY / pod credential absent)`
|
|
127
|
+
);
|
|
119
128
|
return {
|
|
120
129
|
ok: false,
|
|
121
130
|
channel,
|
|
@@ -128,6 +137,11 @@ export async function postRelaySlackMessage(args: {
|
|
|
128
137
|
baseUrl: target.relayBaseUrl,
|
|
129
138
|
automationUuid: args.automationUuid,
|
|
130
139
|
});
|
|
140
|
+
const messageChars = args.message.length;
|
|
141
|
+
log(
|
|
142
|
+
`[agent-sdk-slack-relay-client] POST ${url} channel=${channel} messageChars=${String(messageChars)} truncated=${String(messageChars > AGENT_SDK_SLACK_RELAY_MAX_MESSAGE_CHARS)} threaded=${String(args.threadTs !== undefined)} timeoutMs=${String(args.timeoutMs ?? DEFAULT_TIMEOUT_MS)}`
|
|
143
|
+
);
|
|
144
|
+
const startedAt = Date.now();
|
|
131
145
|
let response: Response;
|
|
132
146
|
try {
|
|
133
147
|
response = await fetchImpl(url, {
|
|
@@ -145,6 +159,9 @@ export async function postRelaySlackMessage(args: {
|
|
|
145
159
|
signal: AbortSignal.timeout(args.timeoutMs ?? DEFAULT_TIMEOUT_MS),
|
|
146
160
|
});
|
|
147
161
|
} catch (error) {
|
|
162
|
+
log(
|
|
163
|
+
`[agent-sdk-slack-relay-client] request failed before a response channel=${channel} automationUuid=${args.automationUuid} elapsedMs=${String(Date.now() - startedAt)}: ${describeError(error)}`
|
|
164
|
+
);
|
|
148
165
|
return { ok: false, channel, status: 0, error: describeError(error) };
|
|
149
166
|
}
|
|
150
167
|
let body: unknown;
|
|
@@ -158,16 +175,23 @@ export async function postRelaySlackMessage(args: {
|
|
|
158
175
|
? (body as Record<string, unknown>)
|
|
159
176
|
: {};
|
|
160
177
|
if (!response.ok || record.ok !== true) {
|
|
178
|
+
const error =
|
|
179
|
+
typeof record.error === "string" && record.error !== ""
|
|
180
|
+
? record.error
|
|
181
|
+
: `relay responded ${response.status}`;
|
|
182
|
+
log(
|
|
183
|
+
`[agent-sdk-slack-relay-client] relay denied channel=${channel} automationUuid=${args.automationUuid} status=${String(response.status)} bodyOk=${String(record.ok)} elapsedMs=${String(Date.now() - startedAt)}: ${error}`
|
|
184
|
+
);
|
|
161
185
|
return {
|
|
162
186
|
ok: false,
|
|
163
187
|
channel,
|
|
164
188
|
status: response.status,
|
|
165
|
-
error
|
|
166
|
-
typeof record.error === "string" && record.error !== ""
|
|
167
|
-
? record.error
|
|
168
|
-
: `relay responded ${response.status}`,
|
|
189
|
+
error,
|
|
169
190
|
};
|
|
170
191
|
}
|
|
192
|
+
log(
|
|
193
|
+
`[agent-sdk-slack-relay-client] relay posted channel=${typeof record.channel === "string" ? record.channel : channel} automationUuid=${args.automationUuid} status=${String(response.status)} ts=${typeof record.ts === "string" ? record.ts : "<none>"} elapsedMs=${String(Date.now() - startedAt)}`
|
|
194
|
+
);
|
|
171
195
|
return {
|
|
172
196
|
ok: true,
|
|
173
197
|
channel: typeof record.channel === "string" ? record.channel : channel,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Sync
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
2
|
+
* Sync workspace trees onto an Agent Store so cloud-runtime turns read
|
|
3
|
+
* them from the store mount. Authored `agent/sandbox/workspace/**` seeds
|
|
4
|
+
* land at store-root `workspace/<path>` (see `buildCloudWorkspaceSection`).
|
|
5
|
+
* Per-turn `workspaceFiles` land at `sessions/<sessionId>/workspace/<path>`
|
|
6
|
+
* (see `buildCloudSessionWorkspaceSection`). Personal-key local+cloud
|
|
7
|
+
* nests both under `agent-serve/<agent>/`.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { readFile } from "node:fs/promises";
|
|
@@ -13,6 +13,15 @@ import { MEMORY_DIR_NAME } from "../memory.js";
|
|
|
13
13
|
import type { SeedFile } from "../types.js";
|
|
14
14
|
import { mirrorToStore, storeRelPath } from "./store-mirror.js";
|
|
15
15
|
|
|
16
|
+
export type WorkspaceFileBody =
|
|
17
|
+
| string
|
|
18
|
+
| { encoding: "base64"; data: string; mimeType?: string };
|
|
19
|
+
|
|
20
|
+
/** Path or encoding that can never land. Distinct from a store outage. */
|
|
21
|
+
export class WorkspaceFileRefusal extends Error {
|
|
22
|
+
override readonly name = "WorkspaceFileRefusal";
|
|
23
|
+
}
|
|
24
|
+
|
|
16
25
|
/**
|
|
17
26
|
* Mirror the seed tree onto the store at its authored relative paths and
|
|
18
27
|
* delete keys no seed claims so redeploys drop removed files. Every seed
|
|
@@ -30,7 +39,7 @@ export async function syncWorkspaceToStore(args: {
|
|
|
30
39
|
for (const seed of args.seedFiles) {
|
|
31
40
|
desired.set(
|
|
32
41
|
agentStoreKeys.workspaceFile(
|
|
33
|
-
|
|
42
|
+
workspaceRelPath(seed.relativePath),
|
|
34
43
|
workspacePrefix
|
|
35
44
|
),
|
|
36
45
|
await readFile(seed.sourcePath)
|
|
@@ -39,17 +48,80 @@ export async function syncWorkspaceToStore(args: {
|
|
|
39
48
|
await mirrorToStore({ sink: args.sink, prefix: workspacePrefix, desired });
|
|
40
49
|
}
|
|
41
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Replace the session workspace prefix with this turn's `workspaceFiles`.
|
|
53
|
+
* Keys the map does not claim are deleted so a later send cannot leave a
|
|
54
|
+
* stale `pr.json`. Every path validates before the store is touched.
|
|
55
|
+
* Returns the relative paths that landed, in send order.
|
|
56
|
+
*/
|
|
57
|
+
export async function syncSessionWorkspaceFiles(args: {
|
|
58
|
+
sink: FileSink;
|
|
59
|
+
files: Record<string, WorkspaceFileBody>;
|
|
60
|
+
workspacePrefix: string;
|
|
61
|
+
}): Promise<string[]> {
|
|
62
|
+
const desired = new Map<string, Uint8Array>();
|
|
63
|
+
const relPaths: string[] = [];
|
|
64
|
+
for (const [relativePath, contents] of Object.entries(args.files)) {
|
|
65
|
+
const rel = workspaceRelPath(relativePath);
|
|
66
|
+
desired.set(
|
|
67
|
+
agentStoreKeys.workspaceFile(rel, args.workspacePrefix),
|
|
68
|
+
workspaceFileBytes(relativePath, contents)
|
|
69
|
+
);
|
|
70
|
+
relPaths.push(rel);
|
|
71
|
+
}
|
|
72
|
+
await mirrorToStore({
|
|
73
|
+
sink: args.sink,
|
|
74
|
+
prefix: args.workspacePrefix,
|
|
75
|
+
desired,
|
|
76
|
+
});
|
|
77
|
+
return relPaths;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Delete every key under the session workspace prefix. */
|
|
81
|
+
export async function wipeSessionWorkspace(args: {
|
|
82
|
+
sink: FileSink;
|
|
83
|
+
workspacePrefix: string;
|
|
84
|
+
}): Promise<void> {
|
|
85
|
+
await mirrorToStore({
|
|
86
|
+
sink: args.sink,
|
|
87
|
+
prefix: args.workspacePrefix,
|
|
88
|
+
desired: new Map(),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
42
92
|
/**
|
|
43
93
|
* Same reservation as local materialization's `resolveWorkspaceWritePath`:
|
|
44
|
-
* `memory/` is the shared cross-session journal, so a seed under it
|
|
45
|
-
* the sync exactly as it fails a local first turn.
|
|
94
|
+
* `memory/` is the shared cross-session journal, so a seed or send under it
|
|
95
|
+
* fails the sync exactly as it fails a local first turn.
|
|
46
96
|
*/
|
|
47
|
-
function
|
|
48
|
-
|
|
97
|
+
function workspaceRelPath(relPath: string): string {
|
|
98
|
+
let rel: string;
|
|
99
|
+
try {
|
|
100
|
+
rel = storeRelPath(relPath, "workspace file");
|
|
101
|
+
} catch (error) {
|
|
102
|
+
throw new WorkspaceFileRefusal(
|
|
103
|
+
error instanceof Error ? error.message : String(error)
|
|
104
|
+
);
|
|
105
|
+
}
|
|
49
106
|
if (rel.split("/", 1)[0] === MEMORY_DIR_NAME) {
|
|
50
|
-
throw new
|
|
107
|
+
throw new WorkspaceFileRefusal(
|
|
51
108
|
`"${MEMORY_DIR_NAME}" is reserved for shared session memory; refusing to sync ${relPath}`
|
|
52
109
|
);
|
|
53
110
|
}
|
|
54
111
|
return rel;
|
|
55
112
|
}
|
|
113
|
+
|
|
114
|
+
function workspaceFileBytes(
|
|
115
|
+
relativePath: string,
|
|
116
|
+
contents: WorkspaceFileBody
|
|
117
|
+
): Uint8Array {
|
|
118
|
+
if (typeof contents === "string") {
|
|
119
|
+
return new TextEncoder().encode(contents);
|
|
120
|
+
}
|
|
121
|
+
if (contents.encoding === "base64") {
|
|
122
|
+
return new Uint8Array(Buffer.from(contents.data, "base64"));
|
|
123
|
+
}
|
|
124
|
+
throw new WorkspaceFileRefusal(
|
|
125
|
+
`Unsupported workspace file encoding for ${relativePath}`
|
|
126
|
+
);
|
|
127
|
+
}
|
|
@@ -80,6 +80,26 @@ export function buildCloudWorkspaceSection(dir: string): string {
|
|
|
80
80
|
].join("\n");
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Per-turn preamble for cloud `workspaceFiles` after they land on the
|
|
85
|
+
* session store prefix (see `syncSessionWorkspaceFiles`). Omitted when
|
|
86
|
+
* the sync did not land.
|
|
87
|
+
*
|
|
88
|
+
* Same shared Agent Store mount as authored seeds — the session path is
|
|
89
|
+
* replace-per-send / task-end wipe lifetime, not a separate mount ACL.
|
|
90
|
+
*/
|
|
91
|
+
export function buildCloudSessionWorkspaceSection(
|
|
92
|
+
dir: string,
|
|
93
|
+
relPaths: readonly string[]
|
|
94
|
+
): string {
|
|
95
|
+
const listed = relPaths.map((path) => `\`${path}\``).join(", ");
|
|
96
|
+
return [
|
|
97
|
+
"## Session workspace files",
|
|
98
|
+
"",
|
|
99
|
+
`This turn's workspace files live at \`${dir}\` on this machine (${listed}). Read them with your normal file tools. They sit on the same shared Agent Store mount as authored workspace seeds; the session directory is replaced on the next send for this session, and task sessions remove it when they finish.`,
|
|
100
|
+
].join("\n");
|
|
101
|
+
}
|
|
102
|
+
|
|
83
103
|
/** Relative path of an agent-side tool script inside the session workspace. */
|
|
84
104
|
export function agentToolScriptPath(toolName: string): string {
|
|
85
105
|
return `.agent-serve/tools/${toolName}.sh`;
|
package/src/reminders.ts
CHANGED
|
@@ -1,30 +1,48 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Runtime reminders — per-session durable wakes
|
|
2
|
+
* Runtime reminders — per-session durable wakes — and named handlers
|
|
3
|
+
* authored at `agent/reminders/<name>.ts`.
|
|
3
4
|
*
|
|
4
5
|
* ```ts
|
|
6
|
+
* import { defineReminder } from "@cursor/july/reminders";
|
|
5
7
|
* import type { ReminderCreateInput } from "@cursor/july/reminders";
|
|
6
8
|
*
|
|
9
|
+
* // agent/reminders/verdict_recheck.ts
|
|
10
|
+
* export default defineReminder({
|
|
11
|
+
* async run({ args, followup }) {
|
|
12
|
+
* await followup({ message: `Recheck ${String(args.headSha)}` });
|
|
13
|
+
* return { action: "delivered" };
|
|
14
|
+
* },
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* import verdictRecheck from "./reminders/verdict_recheck.js";
|
|
18
|
+
*
|
|
7
19
|
* // Recurring ≈ setInterval; one-shot ≈ setTimeout.
|
|
8
20
|
* await host.reminders?.create({
|
|
9
21
|
* purpose: "ci_recheck",
|
|
10
22
|
* channelId: session.channelId,
|
|
11
23
|
* continuationToken: session.continuationKey!,
|
|
12
24
|
* delay: "2h",
|
|
13
|
-
*
|
|
25
|
+
* handler: verdictRecheck,
|
|
26
|
+
* args: { headSha: "abc" },
|
|
14
27
|
* });
|
|
15
28
|
* ```
|
|
16
29
|
*
|
|
17
30
|
* Distinct from `defineSchedule` (deploy-time global cron under
|
|
18
31
|
* `agent/schedules/`). Reminders are created at runtime and bound to a
|
|
19
|
-
* channel continuation. Prefer `
|
|
20
|
-
*
|
|
32
|
+
* channel continuation. Prefer named `handler` + serializable `args` for
|
|
33
|
+
* hosted v2 durability; anonymous `run` remains on local and hosted v1.
|
|
21
34
|
*/
|
|
22
35
|
|
|
36
|
+
import { brandDefinition } from "./internal/brand.js";
|
|
37
|
+
import type { ReminderDefinition, ReminderHandlerConfig } from "./types.js";
|
|
38
|
+
|
|
23
39
|
export type {
|
|
24
40
|
ReminderBusyPolicy,
|
|
25
41
|
ReminderCreateInput,
|
|
42
|
+
ReminderDefinition,
|
|
26
43
|
ReminderFireContext,
|
|
27
44
|
ReminderFireResult,
|
|
45
|
+
ReminderHandlerConfig,
|
|
28
46
|
ReminderHostApi,
|
|
29
47
|
ReminderInfo,
|
|
30
48
|
ReminderReschedule,
|
|
@@ -33,3 +51,16 @@ export type {
|
|
|
33
51
|
ReminderTarget,
|
|
34
52
|
ReminderUntilContext,
|
|
35
53
|
} from "./types.js";
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Brand a named reminder handler for `agent/reminders/<name>.ts`.
|
|
57
|
+
* The registry name is the module stem (not a field on the definition).
|
|
58
|
+
*/
|
|
59
|
+
export function defineReminder(
|
|
60
|
+
config: ReminderHandlerConfig
|
|
61
|
+
): ReminderDefinition {
|
|
62
|
+
if (typeof config.run !== "function") {
|
|
63
|
+
throw new Error("defineReminder requires a `run` function");
|
|
64
|
+
}
|
|
65
|
+
return brandDefinition("reminder", config);
|
|
66
|
+
}
|