@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
|
@@ -23,8 +23,14 @@ export interface ResolvedSchedule {
|
|
|
23
23
|
nextFireAtMs: number;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
/**
|
|
27
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Resolve create-input schedule fields into a concrete next-fire plan.
|
|
28
|
+
* Pass `nowMs` for deterministic CP / test resolution; CP owns this call.
|
|
29
|
+
*/
|
|
30
|
+
export function resolveSchedule(
|
|
31
|
+
input: Pick<ReminderCreateInput, "every" | "cron" | "delay" | "at">,
|
|
32
|
+
nowMs: number = Date.now()
|
|
33
|
+
): ResolvedSchedule {
|
|
28
34
|
const shapes = [
|
|
29
35
|
input.every !== undefined,
|
|
30
36
|
input.cron !== undefined,
|
|
@@ -37,7 +43,6 @@ export function resolveSchedule(input: ReminderCreateInput): ResolvedSchedule {
|
|
|
37
43
|
);
|
|
38
44
|
}
|
|
39
45
|
|
|
40
|
-
const now = Date.now();
|
|
41
46
|
if (input.every !== undefined) {
|
|
42
47
|
const intervalMs = parseDuration(input.every);
|
|
43
48
|
if (intervalMs <= 0) {
|
|
@@ -49,7 +54,7 @@ export function resolveSchedule(input: ReminderCreateInput): ResolvedSchedule {
|
|
|
49
54
|
kind: "every",
|
|
50
55
|
value: String(input.every),
|
|
51
56
|
intervalMs,
|
|
52
|
-
nextFireAtMs:
|
|
57
|
+
nextFireAtMs: nowMs + intervalMs,
|
|
53
58
|
};
|
|
54
59
|
}
|
|
55
60
|
if (input.cron !== undefined) {
|
|
@@ -57,7 +62,7 @@ export function resolveSchedule(input: ReminderCreateInput): ResolvedSchedule {
|
|
|
57
62
|
return {
|
|
58
63
|
kind: "cron",
|
|
59
64
|
value: input.cron,
|
|
60
|
-
nextFireAtMs: nextCronFireMs(input.cron,
|
|
65
|
+
nextFireAtMs: nextCronFireMs(input.cron, nowMs),
|
|
61
66
|
};
|
|
62
67
|
}
|
|
63
68
|
if (input.delay !== undefined) {
|
|
@@ -65,7 +70,7 @@ export function resolveSchedule(input: ReminderCreateInput): ResolvedSchedule {
|
|
|
65
70
|
return {
|
|
66
71
|
kind: "delay",
|
|
67
72
|
value: String(input.delay),
|
|
68
|
-
nextFireAtMs:
|
|
73
|
+
nextFireAtMs: nowMs + delayMs,
|
|
69
74
|
};
|
|
70
75
|
}
|
|
71
76
|
const atMs =
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* Reminder document store used by the local control plane
|
|
3
3
|
* (`<stateRoot>/control-plane/reminders/`).
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* in the in-memory {@link ReminderRunner} registry.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Anonymous `run` / function `until` handlers are not serializable — they live
|
|
6
|
+
* only in the in-memory {@link ReminderRunner} registry. Named `handler` +
|
|
7
|
+
* `args` and prompt-form reminders survive restart (handlers resolve from
|
|
8
|
+
* `agent/reminders/` on every fire).
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import {
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
} from "node:fs/promises";
|
|
19
19
|
import { join } from "node:path";
|
|
20
20
|
import type {
|
|
21
|
+
JsonObject,
|
|
21
22
|
ReminderBusyPolicy,
|
|
22
23
|
ReminderScheduleKind,
|
|
23
24
|
ReminderStatus,
|
|
@@ -31,6 +32,12 @@ export type {
|
|
|
31
32
|
ReminderStatus,
|
|
32
33
|
} from "../types.js";
|
|
33
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Internal record status. `firing` is CP claim state and must not appear on
|
|
37
|
+
* public {@link ReminderStatus} / {@link ReminderInfo}.
|
|
38
|
+
*/
|
|
39
|
+
export type ReminderRecordStatus = ReminderStatus | "firing";
|
|
40
|
+
|
|
34
41
|
/** Serializable reminder record (no function bodies). */
|
|
35
42
|
export interface ReminderRecord {
|
|
36
43
|
version: typeof REMINDER_SNAPSHOT_VERSION;
|
|
@@ -46,8 +53,15 @@ export interface ReminderRecord {
|
|
|
46
53
|
/** Raw schedule value: every/delay string, cron expression, or ISO `at`. */
|
|
47
54
|
scheduleValue: string;
|
|
48
55
|
intervalMs?: number;
|
|
49
|
-
/** Prompt form — mutually exclusive with in-memory `run`. */
|
|
56
|
+
/** Prompt form — mutually exclusive with `handler` and in-memory `run`. */
|
|
50
57
|
prompt?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Named handler stem (`agent/reminders/<name>.ts`). Mutually exclusive with
|
|
60
|
+
* `prompt` and in-memory `run`.
|
|
61
|
+
*/
|
|
62
|
+
handler?: string;
|
|
63
|
+
/** Serializable args for {@link handler}. */
|
|
64
|
+
args?: JsonObject;
|
|
51
65
|
/**
|
|
52
66
|
* True when create() registered a `run` handler. Handlers are not
|
|
53
67
|
* serializable; after restart, armed records with this flag and no live
|
|
@@ -59,9 +73,14 @@ export interface ReminderRecord {
|
|
|
59
73
|
/** Natural-language until (agent-facing); function until is in-memory only. */
|
|
60
74
|
untilPrompt?: string;
|
|
61
75
|
busyPolicy: ReminderBusyPolicy;
|
|
62
|
-
status:
|
|
76
|
+
status: ReminderRecordStatus;
|
|
63
77
|
disarmReason?: string;
|
|
64
78
|
fireCount: number;
|
|
79
|
+
/**
|
|
80
|
+
* CAS generation. Cancel/rekey and successful outcome apply bump this so
|
|
81
|
+
* in-flight outcomes cannot resurrect a superseded reminder.
|
|
82
|
+
*/
|
|
83
|
+
generation?: number;
|
|
65
84
|
nextFireAtMs: number;
|
|
66
85
|
createdAt: string;
|
|
67
86
|
updatedAt: string;
|
|
@@ -120,7 +139,8 @@ export class ReminderStore {
|
|
|
120
139
|
|
|
121
140
|
async listArmed(): Promise<ReminderRecord[]> {
|
|
122
141
|
const all = await this.list();
|
|
123
|
-
|
|
142
|
+
// Treat in-flight claims as still armed for restart / listing purposes.
|
|
143
|
+
return all.filter((r) => r.status === "armed" || r.status === "firing");
|
|
124
144
|
}
|
|
125
145
|
|
|
126
146
|
async get(id: string): Promise<ReminderRecord | undefined> {
|
|
@@ -7,12 +7,18 @@ import {
|
|
|
7
7
|
hasGuestNetworkErrno,
|
|
8
8
|
isQuietGuestConnectStall,
|
|
9
9
|
} from "./guest-network.js";
|
|
10
|
+
import {
|
|
11
|
+
HOSTED_GUEST_RETRY_TERMINAL,
|
|
12
|
+
HOSTED_GUEST_RETRY_TRANSIENT,
|
|
13
|
+
type HostedGuestRetryDisposition,
|
|
14
|
+
} from "./hosted-delivery-protocol.js";
|
|
10
15
|
import { SessionAuthError, SessionBusyError } from "./session-engine.js";
|
|
11
16
|
|
|
12
17
|
export interface MappedRequestError {
|
|
13
18
|
status: 400 | 403 | 409 | 503;
|
|
14
19
|
code: string;
|
|
15
20
|
message: string;
|
|
21
|
+
retry: HostedGuestRetryDisposition;
|
|
16
22
|
}
|
|
17
23
|
|
|
18
24
|
/**
|
|
@@ -23,23 +29,39 @@ export function mapThrownRequestError(
|
|
|
23
29
|
error: unknown
|
|
24
30
|
): MappedRequestError | undefined {
|
|
25
31
|
if (error instanceof SessionBusyError) {
|
|
26
|
-
return {
|
|
32
|
+
return {
|
|
33
|
+
status: 409,
|
|
34
|
+
code: "session_busy",
|
|
35
|
+
message: error.message,
|
|
36
|
+
retry: HOSTED_GUEST_RETRY_TRANSIENT,
|
|
37
|
+
};
|
|
27
38
|
}
|
|
28
39
|
if (error instanceof V2EngineError) {
|
|
29
40
|
return {
|
|
30
41
|
status: 409,
|
|
31
42
|
code: "v2_identity_conflict",
|
|
32
43
|
message: "Control-plane session identity conflicts with existing state.",
|
|
44
|
+
retry: HOSTED_GUEST_RETRY_TERMINAL,
|
|
33
45
|
};
|
|
34
46
|
}
|
|
35
47
|
if (error instanceof SessionAuthError) {
|
|
36
|
-
return {
|
|
48
|
+
return {
|
|
49
|
+
status: 403,
|
|
50
|
+
code: "forbidden",
|
|
51
|
+
message: error.message,
|
|
52
|
+
retry: HOSTED_GUEST_RETRY_TERMINAL,
|
|
53
|
+
};
|
|
37
54
|
}
|
|
38
55
|
if (
|
|
39
56
|
error instanceof InvalidAsOfError ||
|
|
40
57
|
error instanceof VacuousTimeArgsError
|
|
41
58
|
) {
|
|
42
|
-
return {
|
|
59
|
+
return {
|
|
60
|
+
status: 400,
|
|
61
|
+
code: "invalid_request",
|
|
62
|
+
message: error.message,
|
|
63
|
+
retry: HOSTED_GUEST_RETRY_TERMINAL,
|
|
64
|
+
};
|
|
43
65
|
}
|
|
44
66
|
if (
|
|
45
67
|
error instanceof GuestNetworkUnavailableError ||
|
|
@@ -52,6 +74,7 @@ export function mapThrownRequestError(
|
|
|
52
74
|
error instanceof Error
|
|
53
75
|
? error.message
|
|
54
76
|
: "Guest network is not ready for outbound requests.",
|
|
77
|
+
retry: HOSTED_GUEST_RETRY_TRANSIENT,
|
|
55
78
|
};
|
|
56
79
|
}
|
|
57
80
|
if (isQuietGuestConnectStall(error)) {
|
|
@@ -62,6 +85,7 @@ export function mapThrownRequestError(
|
|
|
62
85
|
error instanceof Error
|
|
63
86
|
? error.message
|
|
64
87
|
: "Guest Connect stalled without a network errno.",
|
|
88
|
+
retry: HOSTED_GUEST_RETRY_TRANSIENT,
|
|
65
89
|
};
|
|
66
90
|
}
|
|
67
91
|
return undefined;
|
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Provider-neutral PR URL parse. GitHub.com plus the Origin page shapes
|
|
3
3
|
* humans paste: cursor.com/codebase, review.cursor.com/pr, origin.cursor.com.
|
|
4
|
+
*
|
|
5
|
+
* {@link parseScmPrUrl} is the wider parse the review seams use: it also
|
|
6
|
+
* recognizes GitLab merge requests (`/-/merge_requests/<iid>` on any host),
|
|
7
|
+
* Bitbucket Cloud / Data Center pull requests, and GitHub Enterprise Server
|
|
8
|
+
* (`/<owner>/<repo>/pull/<n>` on a host that is none of the above). The
|
|
9
|
+
* narrow {@link parsePrUrl} keeps its github.com + Origin contract for the
|
|
10
|
+
* free-text callers (Slack `pr-ref`) where "any host with /pull/N" would be
|
|
11
|
+
* too eager.
|
|
4
12
|
*/
|
|
5
13
|
|
|
6
14
|
export type PrProvider = "github" | "origin";
|
|
7
15
|
|
|
16
|
+
/** Every SCM the review seams can address. */
|
|
17
|
+
export type ScmPrProvider = PrProvider | "gitlab" | "bitbucket";
|
|
18
|
+
|
|
8
19
|
export type ParsedPrUrl = {
|
|
9
20
|
provider: PrProvider;
|
|
10
21
|
owner: string;
|
|
@@ -13,6 +24,27 @@ export type ParsedPrUrl = {
|
|
|
13
24
|
url: string;
|
|
14
25
|
};
|
|
15
26
|
|
|
27
|
+
export type ParsedScmPrUrl = {
|
|
28
|
+
provider: ScmPrProvider;
|
|
29
|
+
/**
|
|
30
|
+
* Namespace half of the repo: GitHub owner, GitLab group path (nested
|
|
31
|
+
* groups joined with `/`), Bitbucket workspace / project key (`~user`
|
|
32
|
+
* for Data Center personal repos).
|
|
33
|
+
*/
|
|
34
|
+
owner: string;
|
|
35
|
+
repo: string;
|
|
36
|
+
number: number;
|
|
37
|
+
/** Canonical PR page URL. */
|
|
38
|
+
url: string;
|
|
39
|
+
/**
|
|
40
|
+
* Hostname the PR lives on (lowercase). `github.com` for dotcom, the
|
|
41
|
+
* GHES hostname for GitHub Enterprise, `gitlab.com` or the self-managed
|
|
42
|
+
* host for GitLab, `bitbucket.org` or the Data Center host for Bitbucket.
|
|
43
|
+
* Origin PRs report the Origin page host.
|
|
44
|
+
*/
|
|
45
|
+
host: string;
|
|
46
|
+
};
|
|
47
|
+
|
|
16
48
|
const ORIGIN_PAGE = /^origin(-[a-z0-9]+)?\.cursor\.com$/i;
|
|
17
49
|
|
|
18
50
|
function positiveInt(raw: string | undefined): number | undefined {
|
|
@@ -27,19 +59,28 @@ function positiveInt(raw: string | undefined): number | undefined {
|
|
|
27
59
|
}
|
|
28
60
|
|
|
29
61
|
function parsed(
|
|
30
|
-
provider:
|
|
62
|
+
provider: ScmPrProvider,
|
|
63
|
+
host: string,
|
|
31
64
|
owner: string | undefined,
|
|
32
65
|
repo: string | undefined,
|
|
33
|
-
number: number | undefined
|
|
34
|
-
|
|
35
|
-
|
|
66
|
+
number: number | undefined,
|
|
67
|
+
url?: string
|
|
68
|
+
): ParsedScmPrUrl | undefined {
|
|
69
|
+
if (
|
|
70
|
+
owner === undefined ||
|
|
71
|
+
owner === "" ||
|
|
72
|
+
repo === undefined ||
|
|
73
|
+
repo === "" ||
|
|
74
|
+
number === undefined
|
|
75
|
+
) {
|
|
36
76
|
return undefined;
|
|
37
77
|
}
|
|
38
|
-
const
|
|
39
|
-
|
|
78
|
+
const canonical =
|
|
79
|
+
url ??
|
|
80
|
+
(provider === "origin"
|
|
40
81
|
? `https://cursor.com/codebase/${owner}/${repo}/pull/${number}`
|
|
41
|
-
: `https
|
|
42
|
-
return { provider, owner, repo, number, url };
|
|
82
|
+
: `https://${host}/${owner}/${repo}/pull/${number}`);
|
|
83
|
+
return { provider, owner, repo, number, url: canonical, host };
|
|
43
84
|
}
|
|
44
85
|
|
|
45
86
|
/** Strip Slack mrkdwn wrappers and trailing punctuation before `URL`. */
|
|
@@ -50,27 +91,49 @@ function normalizeRaw(raw: string): string {
|
|
|
50
91
|
.replace(/[>\].,;]+$/, "");
|
|
51
92
|
}
|
|
52
93
|
|
|
94
|
+
function isGithubDotCom(host: string): boolean {
|
|
95
|
+
return host === "github.com" || host === "www.github.com";
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** github.com and Origin only — the contract free-text PR references use. */
|
|
53
99
|
export function parsePrUrl(raw: string): ParsedPrUrl | undefined {
|
|
100
|
+
const wide = parseScmPrUrl(raw);
|
|
101
|
+
if (wide === undefined) {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
if (wide.provider === "origin") {
|
|
105
|
+
return { ...wide, provider: "origin" };
|
|
106
|
+
}
|
|
107
|
+
if (wide.provider === "github" && isGithubDotCom(wide.host)) {
|
|
108
|
+
return { ...wide, provider: "github" };
|
|
109
|
+
}
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Every provider shape, including GitHub Enterprise Server. The GitLab and
|
|
115
|
+
* Bitbucket path shapes are unambiguous on any host; a plain
|
|
116
|
+
* `/<owner>/<repo>/pull/<n>` on a host that is not github.com, Origin,
|
|
117
|
+
* GitLab, or Bitbucket is taken as GHES (the only remaining forge with that
|
|
118
|
+
* route). Callers that must not accept arbitrary hosts use
|
|
119
|
+
* {@link parsePrUrl}.
|
|
120
|
+
*/
|
|
121
|
+
export function parseScmPrUrl(raw: string): ParsedScmPrUrl | undefined {
|
|
54
122
|
let url: URL;
|
|
55
123
|
try {
|
|
56
124
|
url = new URL(normalizeRaw(raw));
|
|
57
125
|
} catch {
|
|
58
126
|
return undefined;
|
|
59
127
|
}
|
|
128
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
60
131
|
|
|
61
132
|
const parts = url.pathname.split("/").filter(Boolean);
|
|
62
133
|
const host = url.hostname.toLowerCase();
|
|
63
134
|
|
|
64
|
-
if (
|
|
65
|
-
(host === "github.com" || host === "www.github.com") &&
|
|
66
|
-
parts.length >= 4 &&
|
|
67
|
-
parts[2] === "pull"
|
|
68
|
-
) {
|
|
69
|
-
return parsed("github", parts[0], parts[1], positiveInt(parts[3]));
|
|
70
|
-
}
|
|
71
|
-
|
|
72
135
|
if (ORIGIN_PAGE.test(host) && parts.length >= 4 && parts[2] === "github") {
|
|
73
|
-
return parsed("origin", parts[0], parts[1], positiveInt(parts[3]));
|
|
136
|
+
return parsed("origin", host, parts[0], parts[1], positiveInt(parts[3]));
|
|
74
137
|
}
|
|
75
138
|
|
|
76
139
|
if (
|
|
@@ -79,17 +142,101 @@ export function parsePrUrl(raw: string): ParsedPrUrl | undefined {
|
|
|
79
142
|
parts.length >= 5 &&
|
|
80
143
|
parts[3] === "pull"
|
|
81
144
|
) {
|
|
82
|
-
return parsed("origin", parts[1], parts[2], positiveInt(parts[4]));
|
|
145
|
+
return parsed("origin", host, parts[1], parts[2], positiveInt(parts[4]));
|
|
83
146
|
}
|
|
84
147
|
|
|
85
148
|
if (host === "review.cursor.com" && parts[0] === "pr") {
|
|
86
149
|
if (parts.length >= 5 && parts[3] === "github") {
|
|
87
|
-
return parsed("origin", parts[1], parts[2], positiveInt(parts[4]));
|
|
150
|
+
return parsed("origin", host, parts[1], parts[2], positiveInt(parts[4]));
|
|
88
151
|
}
|
|
89
152
|
if (parts.length >= 4) {
|
|
90
|
-
return parsed("origin", parts[1], parts[2], positiveInt(parts[3]));
|
|
153
|
+
return parsed("origin", host, parts[1], parts[2], positiveInt(parts[3]));
|
|
91
154
|
}
|
|
92
155
|
}
|
|
93
156
|
|
|
157
|
+
// GitLab MR page: /<group>/[<subgroup>/…]<project>/-/merge_requests/<iid>
|
|
158
|
+
const separator = parts.indexOf("-");
|
|
159
|
+
if (
|
|
160
|
+
separator >= 2 &&
|
|
161
|
+
parts[separator + 1] === "merge_requests" &&
|
|
162
|
+
parts.length >= separator + 3
|
|
163
|
+
) {
|
|
164
|
+
const owner = parts.slice(0, separator - 1).join("/");
|
|
165
|
+
const repo = parts[separator - 1];
|
|
166
|
+
const number = positiveInt(parts[separator + 2]);
|
|
167
|
+
return parsed(
|
|
168
|
+
"gitlab",
|
|
169
|
+
host,
|
|
170
|
+
owner,
|
|
171
|
+
repo,
|
|
172
|
+
number,
|
|
173
|
+
number === undefined
|
|
174
|
+
? undefined
|
|
175
|
+
: `https://${host}/${owner}/${repo}/-/merge_requests/${number}`
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Bitbucket Cloud: bitbucket.org/<workspace>/<slug>/pull-requests/<id>
|
|
180
|
+
if (
|
|
181
|
+
(host === "bitbucket.org" || host === "www.bitbucket.org") &&
|
|
182
|
+
parts.length >= 4 &&
|
|
183
|
+
parts[2] === "pull-requests"
|
|
184
|
+
) {
|
|
185
|
+
const number = positiveInt(parts[3]);
|
|
186
|
+
return parsed(
|
|
187
|
+
"bitbucket",
|
|
188
|
+
"bitbucket.org",
|
|
189
|
+
parts[0],
|
|
190
|
+
parts[1],
|
|
191
|
+
number,
|
|
192
|
+
number === undefined
|
|
193
|
+
? undefined
|
|
194
|
+
: `https://bitbucket.org/${parts[0]}/${parts[1]}/pull-requests/${number}`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Bitbucket Data Center: [/<context>]/projects/<KEY>/repos/<slug>/pull-requests/<id>
|
|
199
|
+
// (personal repos: /users/<name>/repos/<slug>/…, full name `~name/slug`).
|
|
200
|
+
const dc = parts.findIndex(
|
|
201
|
+
(part, index) =>
|
|
202
|
+
(part === "projects" || part === "users") &&
|
|
203
|
+
parts[index + 2] === "repos" &&
|
|
204
|
+
parts[index + 4] === "pull-requests"
|
|
205
|
+
);
|
|
206
|
+
if (dc !== -1 && parts.length >= dc + 6) {
|
|
207
|
+
const number = positiveInt(parts[dc + 5]);
|
|
208
|
+
const owner = parts[dc] === "users" ? `~${parts[dc + 1]}` : parts[dc + 1];
|
|
209
|
+
const slug = parts[dc + 3];
|
|
210
|
+
const context = parts.slice(0, dc).join("/");
|
|
211
|
+
return parsed(
|
|
212
|
+
"bitbucket",
|
|
213
|
+
host,
|
|
214
|
+
owner,
|
|
215
|
+
slug,
|
|
216
|
+
number,
|
|
217
|
+
number === undefined
|
|
218
|
+
? undefined
|
|
219
|
+
: `${url.origin}/${context === "" ? "" : `${context}/`}${parts.slice(dc, dc + 4).join("/")}/pull-requests/${number}`
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// GitHub (dotcom or Enterprise Server): /<owner>/<repo>/pull/<n>. Checked
|
|
224
|
+
// last so the forge-specific shapes above win on shared hosts.
|
|
225
|
+
if (
|
|
226
|
+
parts.length >= 4 &&
|
|
227
|
+
parts[2] === "pull" &&
|
|
228
|
+
!ORIGIN_PAGE.test(host) &&
|
|
229
|
+
!/(^|\.)cursor\.com$/.test(host)
|
|
230
|
+
) {
|
|
231
|
+
const githubHost = isGithubDotCom(host) ? "github.com" : host;
|
|
232
|
+
return parsed(
|
|
233
|
+
"github",
|
|
234
|
+
githubHost,
|
|
235
|
+
parts[0],
|
|
236
|
+
parts[1],
|
|
237
|
+
positiveInt(parts[3])
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
94
241
|
return undefined;
|
|
95
242
|
}
|
|
@@ -432,6 +432,25 @@ export class CursorSdkRunner implements AgentRunner {
|
|
|
432
432
|
return { warmed: true };
|
|
433
433
|
}
|
|
434
434
|
|
|
435
|
+
/**
|
|
436
|
+
* Drop the cached SDK agent for this session, if one is open.
|
|
437
|
+
*
|
|
438
|
+
* Hosted v2 parks and hibernates this process as soon as the execution
|
|
439
|
+
* reply lands on non-interactive channels. A leftover handle still holds
|
|
440
|
+
* the pre-freeze Connect/HTTP2 session; the next wake then reuses it and
|
|
441
|
+
* `AgentService/Run` never starts. Call after `waitForCompletion` so the
|
|
442
|
+
* hibernate snapshot has an empty cache. Slack's interactive hot-window
|
|
443
|
+
* still reuses handles because that path does not go through hosted
|
|
444
|
+
* delivery.
|
|
445
|
+
*/
|
|
446
|
+
evictSession(sessionId: string): void {
|
|
447
|
+
const cached = this.agents.get(sessionId);
|
|
448
|
+
if (cached === undefined) {
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
this.evictAgent(sessionId, cached.agent);
|
|
452
|
+
}
|
|
453
|
+
|
|
435
454
|
async dispose(): Promise<void> {
|
|
436
455
|
const releases = this.prewarmReleases.splice(0);
|
|
437
456
|
await Promise.allSettled(releases.map((release) => release()));
|
package/src/internal/server.ts
CHANGED
|
@@ -129,6 +129,11 @@ import {
|
|
|
129
129
|
} from "./handleAgentServeTrigger.js";
|
|
130
130
|
import { createHostPlatforms, warmupHostGitHubAuth } from "./host-platforms.js";
|
|
131
131
|
import { registerHostedDeliveryRoute } from "./hosted-delivery.js";
|
|
132
|
+
import {
|
|
133
|
+
HOSTED_DELIVERY_HEADER,
|
|
134
|
+
HOSTED_GUEST_RETRY_TERMINAL,
|
|
135
|
+
hostedGuestErrorResponse,
|
|
136
|
+
} from "./hosted-delivery-protocol.js";
|
|
132
137
|
import { hostedErrorStackSnippet } from "./hosted-execution-diag.js";
|
|
133
138
|
import { flushHostedExecutionProcessDeath } from "./hosted-execution-flush.js";
|
|
134
139
|
import {
|
|
@@ -191,6 +196,7 @@ import {
|
|
|
191
196
|
import {
|
|
192
197
|
ReminderRunner,
|
|
193
198
|
ReminderValidationError,
|
|
199
|
+
rejectAnonymousReminderHandlers,
|
|
194
200
|
UnknownReminderError,
|
|
195
201
|
} from "./reminder-runner.js";
|
|
196
202
|
import { mapThrownRequestError } from "./request-error-status.js";
|
|
@@ -319,12 +325,9 @@ export async function startServer(
|
|
|
319
325
|
noControlPlane?: boolean;
|
|
320
326
|
/** `--cursor-scm-proxy`: every cursorAccount SCM pack routes via the backend forwarder. */
|
|
321
327
|
cursorScmProxy?: boolean;
|
|
322
|
-
/** Deprecated alias (`--cursor-github-proxy`); same effect as cursorScmProxy. */
|
|
323
|
-
cursorGithubProxy?: boolean;
|
|
324
328
|
};
|
|
325
329
|
const noControlPlane = hosted.noControlPlane === true;
|
|
326
|
-
const scmProxyRequested =
|
|
327
|
-
hosted.cursorScmProxy === true || hosted.cursorGithubProxy === true;
|
|
330
|
+
const scmProxyRequested = hosted.cursorScmProxy === true;
|
|
328
331
|
const runSchedules = options.schedules ?? !dev;
|
|
329
332
|
const runReminders = options.reminders ?? !dev;
|
|
330
333
|
|
|
@@ -846,12 +849,23 @@ export async function startServer(
|
|
|
846
849
|
}
|
|
847
850
|
const scheduleRunner = new ScheduleRunner(engine, project, logger);
|
|
848
851
|
let reminderRunner!: ReminderRunner;
|
|
852
|
+
const architecture = project.agent.architecture;
|
|
853
|
+
const useReminderCpProtocol = !noControlPlane && architecture === "v2";
|
|
849
854
|
const localControlPlane = noControlPlane
|
|
850
855
|
? undefined
|
|
851
856
|
: await createLocalControlPlane({
|
|
852
857
|
fireSchedule: (name) => scheduleRunner.dispatch(name),
|
|
853
|
-
|
|
854
|
-
|
|
858
|
+
...(useReminderCpProtocol
|
|
859
|
+
? {
|
|
860
|
+
executeReminder: (descriptor) =>
|
|
861
|
+
reminderRunner.executeFire(descriptor),
|
|
862
|
+
}
|
|
863
|
+
: {
|
|
864
|
+
fireReminder: (id, fireCount) =>
|
|
865
|
+
reminderRunner.dispatch(id, {
|
|
866
|
+
expectedFireCount: fireCount,
|
|
867
|
+
}),
|
|
868
|
+
}),
|
|
855
869
|
stateDir: join(stateRoot, "control-plane"),
|
|
856
870
|
armReminderClocks: runReminders,
|
|
857
871
|
logger,
|
|
@@ -859,7 +873,16 @@ export async function startServer(
|
|
|
859
873
|
const timerClient = noControlPlane
|
|
860
874
|
? createCursorHostedPlatformTimers()
|
|
861
875
|
: localControlPlane;
|
|
862
|
-
reminderRunner = new ReminderRunner(engine, logger, timerClient
|
|
876
|
+
reminderRunner = new ReminderRunner(engine, logger, timerClient, {
|
|
877
|
+
namedHandlers: project.reminders ?? [],
|
|
878
|
+
// Architecture v2 rejects anonymous run/until everywhere (local + hosted).
|
|
879
|
+
rejectAnonymousHandlers: rejectAnonymousReminderHandlers({
|
|
880
|
+
noControlPlane,
|
|
881
|
+
architecture,
|
|
882
|
+
}),
|
|
883
|
+
architecture,
|
|
884
|
+
controlPlane: useReminderCpProtocol ? localControlPlane : undefined,
|
|
885
|
+
});
|
|
863
886
|
const evalRuns = new EvalRunStore(
|
|
864
887
|
project.rootDir,
|
|
865
888
|
logger,
|
|
@@ -918,6 +941,13 @@ export async function startServer(
|
|
|
918
941
|
...(accountRefs !== undefined && accountBridge !== undefined
|
|
919
942
|
? { accountBridge, accountRefs }
|
|
920
943
|
: {}),
|
|
944
|
+
...(sdkRunner instanceof CursorSdkRunner
|
|
945
|
+
? {
|
|
946
|
+
evictSdkSession: (sessionId: string) => {
|
|
947
|
+
sdkRunner.evictSession(sessionId);
|
|
948
|
+
},
|
|
949
|
+
}
|
|
950
|
+
: {}),
|
|
921
951
|
});
|
|
922
952
|
for (const channel of project.channels) {
|
|
923
953
|
const definition = channel.definition;
|
|
@@ -928,6 +958,7 @@ export async function startServer(
|
|
|
928
958
|
const startArgs: ChannelStartArgs = {
|
|
929
959
|
send: (message, sendOptions) =>
|
|
930
960
|
engine.send(channel.id, message, sendOptions),
|
|
961
|
+
architecture: project.agent.architecture,
|
|
931
962
|
getSession: (sessionId) =>
|
|
932
963
|
engine.getChannelSession(channel.id, sessionId),
|
|
933
964
|
receive: (target, input) => engine.receive(target, input),
|
|
@@ -1440,6 +1471,12 @@ function mountAgentRoutes(
|
|
|
1440
1471
|
* here so the Temporal host can read a stable path after the POST.
|
|
1441
1472
|
*/
|
|
1442
1473
|
diagStateRoot?: string;
|
|
1474
|
+
/**
|
|
1475
|
+
* Drop the cached SDK agent handle after a hosted turn so hibernate
|
|
1476
|
+
* cannot snapshot a frozen Connect/HTTP2 session. Injected test
|
|
1477
|
+
* runners may omit this.
|
|
1478
|
+
*/
|
|
1479
|
+
evictSdkSession?: (sessionId: string) => void;
|
|
1443
1480
|
}
|
|
1444
1481
|
): void {
|
|
1445
1482
|
const { slug, project, engine, scheduleRunner, reminderRunner } = runtime;
|
|
@@ -1508,6 +1545,8 @@ function mountAgentRoutes(
|
|
|
1508
1545
|
channelId,
|
|
1509
1546
|
hostedSessionId,
|
|
1510
1547
|
}),
|
|
1548
|
+
fireReminder: (reminderId, fireCount) =>
|
|
1549
|
+
reminderRunner.dispatch(reminderId, { expectedFireCount: fireCount }),
|
|
1511
1550
|
drainSessionDurability: (sessionId) =>
|
|
1512
1551
|
engine.drainSessionDurability(sessionId),
|
|
1513
1552
|
flushOtel: args.flushOtel,
|
|
@@ -1523,6 +1562,9 @@ function mountAgentRoutes(
|
|
|
1523
1562
|
flushStartLog,
|
|
1524
1563
|
}),
|
|
1525
1564
|
snapshotLogs: () => logRing.since(),
|
|
1565
|
+
...(args.evictSdkSession === undefined
|
|
1566
|
+
? {}
|
|
1567
|
+
: { evictSdkSession: args.evictSdkSession }),
|
|
1526
1568
|
});
|
|
1527
1569
|
}
|
|
1528
1570
|
// Playground lists/opens Slack (and other channel) sessions across
|
|
@@ -2969,19 +3011,34 @@ async function handleRequest(
|
|
|
2969
3011
|
logger: (line: string) => void
|
|
2970
3012
|
): Promise<void> {
|
|
2971
3013
|
let response: Response;
|
|
3014
|
+
let hostedDeliveryRequest = false;
|
|
2972
3015
|
const method = req.method ?? "GET";
|
|
2973
3016
|
try {
|
|
2974
3017
|
const request = await toFetchRequest(req);
|
|
3018
|
+
hostedDeliveryRequest = request.headers.get(HOSTED_DELIVERY_HEADER) === "1";
|
|
3019
|
+
const requestErrorResponse = (
|
|
3020
|
+
status: number,
|
|
3021
|
+
error: string,
|
|
3022
|
+
message: string
|
|
3023
|
+
) =>
|
|
3024
|
+
hostedDeliveryRequest
|
|
3025
|
+
? hostedGuestErrorResponse({
|
|
3026
|
+
status,
|
|
3027
|
+
error,
|
|
3028
|
+
retry: HOSTED_GUEST_RETRY_TERMINAL,
|
|
3029
|
+
message,
|
|
3030
|
+
})
|
|
3031
|
+
: errorResponse(status, error, message);
|
|
2975
3032
|
const url = new URL(request.url);
|
|
2976
3033
|
const match = router.match(request.method, url.pathname);
|
|
2977
3034
|
if (match === undefined) {
|
|
2978
3035
|
response = router.hasPath(url.pathname)
|
|
2979
|
-
?
|
|
3036
|
+
? requestErrorResponse(
|
|
2980
3037
|
405,
|
|
2981
3038
|
"method_not_allowed",
|
|
2982
3039
|
`${request.method} is not supported on ${url.pathname}.`
|
|
2983
3040
|
)
|
|
2984
|
-
:
|
|
3041
|
+
: requestErrorResponse(
|
|
2985
3042
|
404,
|
|
2986
3043
|
"not_found",
|
|
2987
3044
|
`No route for ${request.method} ${url.pathname}.`
|
|
@@ -2995,7 +3052,7 @@ async function handleRequest(
|
|
|
2995
3052
|
unauthorized = auth === null;
|
|
2996
3053
|
}
|
|
2997
3054
|
response = unauthorized
|
|
2998
|
-
?
|
|
3055
|
+
? requestErrorResponse(
|
|
2999
3056
|
401,
|
|
3000
3057
|
"unauthorized",
|
|
3001
3058
|
"No auth policy admitted this request. In production, configure channel auth (e.g. bearerAuth) — the default localDevStrict() policy only admits loopback callers with a loopback Host."
|
|
@@ -3009,7 +3066,14 @@ async function handleRequest(
|
|
|
3009
3066
|
} catch (error) {
|
|
3010
3067
|
const mapped = mapThrownRequestError(error);
|
|
3011
3068
|
if (mapped !== undefined) {
|
|
3012
|
-
response =
|
|
3069
|
+
response = hostedDeliveryRequest
|
|
3070
|
+
? hostedGuestErrorResponse({
|
|
3071
|
+
status: mapped.status,
|
|
3072
|
+
error: mapped.code,
|
|
3073
|
+
retry: mapped.retry,
|
|
3074
|
+
message: mapped.message,
|
|
3075
|
+
})
|
|
3076
|
+
: errorResponse(mapped.status, mapped.code, mapped.message);
|
|
3013
3077
|
} else {
|
|
3014
3078
|
logger(
|
|
3015
3079
|
`[agent-sdk] request failed: ${
|
|
@@ -3019,13 +3083,18 @@ async function handleRequest(
|
|
|
3019
3083
|
}`
|
|
3020
3084
|
);
|
|
3021
3085
|
const snippet = hostedErrorStackSnippet(error);
|
|
3022
|
-
|
|
3023
|
-
500,
|
|
3024
|
-
"internal_error",
|
|
3086
|
+
const message =
|
|
3025
3087
|
snippet.length > 0
|
|
3026
3088
|
? `Internal server error: ${snippet}`
|
|
3027
|
-
: "Internal server error."
|
|
3028
|
-
|
|
3089
|
+
: "Internal server error.";
|
|
3090
|
+
response = hostedDeliveryRequest
|
|
3091
|
+
? hostedGuestErrorResponse({
|
|
3092
|
+
status: 500,
|
|
3093
|
+
error: "internal_error",
|
|
3094
|
+
retry: HOSTED_GUEST_RETRY_TERMINAL,
|
|
3095
|
+
message,
|
|
3096
|
+
})
|
|
3097
|
+
: errorResponse(500, "internal_error", message);
|
|
3029
3098
|
}
|
|
3030
3099
|
}
|
|
3031
3100
|
|