@cursor/july 0.1.114 → 0.2.1
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/channels/slack/channel-watch.d.ts +19 -3
- package/dist/channels/slack/channel-watch.d.ts.map +1 -1
- package/dist/channels/slack/channel-watch.js +48 -9
- package/dist/channels/slack/inbound.d.ts +7 -0
- package/dist/channels/slack/inbound.d.ts.map +1 -1
- package/dist/channels/slack/inbound.js +23 -0
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +87 -8
- package/dist/channels/slack/types.d.ts +11 -12
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/assets/{app.BqkJwOZ-.js → app.D23Y-7Tp.js} +4 -4
- package/dist/docs/assets/chunks/@localSearchIndexroot.CzCCM7N8.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.BJAi2KiV.js → VPLocalSearchBox.CWBeTFRZ.js} +1 -1
- package/dist/docs/assets/chunks/{arc.BZpXTgvV.js → arc.DSF2O3pm.js} +1 -1
- package/dist/docs/assets/chunks/{architectureDiagram-Q4EWVU46.WYI-7F-Y.js → architectureDiagram-Q4EWVU46.J52Wzbkg.js} +1 -1
- package/dist/docs/assets/chunks/{baseUniq.CZaUPpg0.js → baseUniq.CQS3LPCt.js} +1 -1
- package/dist/docs/assets/chunks/{blockDiagram-DXYQGD6D.D6UES2pD.js → blockDiagram-DXYQGD6D.Dw339Gr5.js} +1 -1
- package/dist/docs/assets/chunks/{c4Diagram-AHTNJAMY.cwebIe4i.js → c4Diagram-AHTNJAMY.BUdtOaRZ.js} +1 -1
- package/dist/docs/assets/chunks/channel.Bfu4df88.js +1 -0
- package/dist/docs/assets/chunks/{chunk-4BX2VUAB.fVyFnjxg.js → chunk-4BX2VUAB.CuOrkEqk.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-4TB4RGXK.BanufG1c.js → chunk-4TB4RGXK.BJNBcY7U.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-55IACEB6.VaSMz5-2.js → chunk-55IACEB6.VJK5LAm_.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-EDXVE4YY.CN2diZOM.js → chunk-EDXVE4YY.BYYLihvj.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-FMBD7UC4.g4ivypu3.js → chunk-FMBD7UC4.CmoW8BXP.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-OYMX7WX6.GZXKn9JJ.js → chunk-OYMX7WX6.DTGY4C-M.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-QZHKN3VN.itXxJZCd.js → chunk-QZHKN3VN.Cg5n67vl.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-YZCP3GAM.-rw2GfvX.js → chunk-YZCP3GAM.C3GR_ia5.js} +1 -1
- package/dist/docs/assets/chunks/classDiagram-6PBFFD2Q.DdfgtaWs.js +1 -0
- package/dist/docs/assets/chunks/classDiagram-v2-HSJHXN6E.DdfgtaWs.js +1 -0
- package/dist/docs/assets/chunks/clone.rkmfti6d.js +1 -0
- package/dist/docs/assets/chunks/{cose-bilkent-S5V4N54A.CmaI5br0.js → cose-bilkent-S5V4N54A.BTRG8N3b.js} +1 -1
- package/dist/docs/assets/chunks/{dagre-KV5264BT.4wY9S4Kt.js → dagre-KV5264BT.Bob_bp_p.js} +1 -1
- package/dist/docs/assets/chunks/{diagram-5BDNPKRD.Pc3c0u9W.js → diagram-5BDNPKRD.ggPcs9uO.js} +1 -1
- package/dist/docs/assets/chunks/{diagram-G4DWMVQ6.CYrWz-nj.js → diagram-G4DWMVQ6.BP0qyJkp.js} +1 -1
- package/dist/docs/assets/chunks/{diagram-MMDJMWI5.Bgj5hukb.js → diagram-MMDJMWI5.B0X24UKr.js} +1 -1
- package/dist/docs/assets/chunks/{diagram-TYMM5635.DGMEXalS.js → diagram-TYMM5635.B4rXHFVt.js} +1 -1
- package/dist/docs/assets/chunks/{erDiagram-SMLLAGMA.GepTV9Im.js → erDiagram-SMLLAGMA._55Rt9oX.js} +1 -1
- package/dist/docs/assets/chunks/{flowDiagram-DWJPFMVM.DVKywg3j.js → flowDiagram-DWJPFMVM.DGP4XvR5.js} +1 -1
- package/dist/docs/assets/chunks/{ganttDiagram-T4ZO3ILL.C7qt9Mlo.js → ganttDiagram-T4ZO3ILL.BtXtkL4E.js} +1 -1
- package/dist/docs/assets/chunks/{gitGraphDiagram-UUTBAWPF.U30_r82P.js → gitGraphDiagram-UUTBAWPF.B9cPWblK.js} +1 -1
- package/dist/docs/assets/chunks/{graph.CyyMyAWv.js → graph.D8HzNexS.js} +1 -1
- package/dist/docs/assets/chunks/{infoDiagram-42DDH7IO.Dn9ACW3y.js → infoDiagram-42DDH7IO.Bw7CQUpi.js} +1 -1
- package/dist/docs/assets/chunks/{ishikawaDiagram-UXIWVN3A.DlIdIGOA.js → ishikawaDiagram-UXIWVN3A.MwkzF6nQ.js} +1 -1
- package/dist/docs/assets/chunks/{journeyDiagram-VCZTEJTY.DZj4vy4E.js → journeyDiagram-VCZTEJTY.DIGFF-3C.js} +1 -1
- package/dist/docs/assets/chunks/{kanban-definition-6JOO6SKY.Dl63eMUV.js → kanban-definition-6JOO6SKY.DhYef2BN.js} +1 -1
- package/dist/docs/assets/chunks/{layout.BLHZLWPH.js → layout.C0XUxuPi.js} +1 -1
- package/dist/docs/assets/chunks/{linear.aXKGKaNw.js → linear.BwNPpZex.js} +1 -1
- package/dist/docs/assets/chunks/{min.zWnFcpcc.js → min.CwAQdL7z.js} +1 -1
- package/dist/docs/assets/chunks/{mindmap-definition-QFDTVHPH.Qs4MQBea.js → mindmap-definition-QFDTVHPH.pWsSVLsP.js} +1 -1
- package/dist/docs/assets/chunks/{pieDiagram-DEJITSTG.BmPHgsk7.js → pieDiagram-DEJITSTG.BDJ3FbBy.js} +1 -1
- package/dist/docs/assets/chunks/{quadrantDiagram-34T5L4WZ.D5MQ3gwA.js → quadrantDiagram-34T5L4WZ.Co80izyB.js} +1 -1
- package/dist/docs/assets/chunks/{requirementDiagram-MS252O5E.CkdUFrO7.js → requirementDiagram-MS252O5E.JveKw4yx.js} +1 -1
- package/dist/docs/assets/chunks/{sankeyDiagram-XADWPNL6.KZrljrAV.js → sankeyDiagram-XADWPNL6.B0A7adPi.js} +1 -1
- package/dist/docs/assets/chunks/{sequenceDiagram-FGHM5R23.XMoEW-Lx.js → sequenceDiagram-FGHM5R23.d6JZ5Hre.js} +1 -1
- package/dist/docs/assets/chunks/{stateDiagram-FHFEXIEX.BmTzePLj.js → stateDiagram-FHFEXIEX.DWnL0NQl.js} +1 -1
- package/dist/docs/assets/chunks/stateDiagram-v2-QKLJ7IA2.ZEetPk0E.js +1 -0
- package/dist/docs/assets/chunks/{theme.BfQzpxsg.js → theme.MJTLx0hh.js} +2 -2
- package/dist/docs/assets/chunks/{timeline-definition-GMOUNBTQ.Dug0oamp.js → timeline-definition-GMOUNBTQ.CFS7Ai4c.js} +1 -1
- package/dist/docs/assets/chunks/{vennDiagram-DHZGUBPP.BOTHrEFu.js → vennDiagram-DHZGUBPP.CwSlnjCf.js} +1 -1
- package/dist/docs/assets/chunks/wardley-RL74JXVD.3gurI8YA.js +162 -0
- package/dist/docs/assets/chunks/{wardleyDiagram-NUSXRM2D.CoXKdfi6.js → wardleyDiagram-NUSXRM2D.B_8mvtjh.js} +1 -1
- package/dist/docs/assets/chunks/{xychartDiagram-5P7HB3ND.DXoSCjAW.js → xychartDiagram-5P7HB3ND.DtjU5H85.js} +1 -1
- package/dist/docs/assets/{guides_slack.md.Bo96y42E.js → guides_slack.md.Bjw2r2gL.js} +5 -5
- package/dist/docs/assets/{guides_slack.md.Bo96y42E.lean.js → guides_slack.md.Bjw2r2gL.lean.js} +1 -1
- package/dist/docs/assets/reference_cli.md.BvnQM8wd.js +97 -0
- package/dist/docs/assets/reference_cli.md.BvnQM8wd.lean.js +1 -0
- package/dist/docs/building-with-agents.html +35 -35
- package/dist/docs/deployment.html +35 -35
- package/dist/docs/evals.html +35 -35
- package/dist/docs/guides/agent-to-agent.html +35 -35
- package/dist/docs/guides/bitbucket.html +35 -35
- package/dist/docs/guides/cloud-agents.html +35 -35
- package/dist/docs/guides/convert-automation.html +35 -35
- package/dist/docs/guides/github.html +35 -35
- package/dist/docs/guides/gitlab.html +35 -35
- package/dist/docs/guides/grokbot-agents.html +35 -35
- package/dist/docs/guides/hooks.html +35 -35
- package/dist/docs/guides/improve.html +35 -35
- package/dist/docs/guides/jev.html +35 -35
- package/dist/docs/guides/mcp-oauth.html +35 -35
- package/dist/docs/guides/opentelemetry.html +35 -35
- package/dist/docs/guides/slack.html +39 -39
- package/dist/docs/guides/slack.md +16 -1
- package/dist/docs/guides/webhooks.html +35 -35
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +35 -35
- package/dist/docs/index.html +35 -35
- package/dist/docs/llms-full.txt +699 -752
- package/dist/docs/llms.txt +1 -1
- package/dist/docs/quickstart.html +35 -35
- package/dist/docs/reference/agent-config.html +35 -35
- package/dist/docs/reference/artifacts.html +35 -35
- package/dist/docs/reference/channels.html +35 -35
- package/dist/docs/reference/cli.html +127 -125
- package/dist/docs/reference/cli.md +685 -753
- package/dist/docs/reference/connections.html +35 -35
- package/dist/docs/reference/evals.html +35 -35
- package/dist/docs/reference/extensions.html +35 -35
- package/dist/docs/reference/hooks.html +35 -35
- package/dist/docs/reference/http-api.html +35 -35
- package/dist/docs/reference/instructions.html +35 -35
- package/dist/docs/reference/playground.html +35 -35
- package/dist/docs/reference/project-layout.html +35 -35
- package/dist/docs/reference/prompt.html +35 -35
- package/dist/docs/reference/schedules.html +35 -35
- package/dist/docs/reference/sessions.html +35 -35
- package/dist/docs/reference/skills.html +35 -35
- package/dist/docs/reference/subagents.html +35 -35
- package/dist/docs/reference/tools.html +35 -35
- package/dist/docs/templates/agentic-owners.html +35 -35
- package/dist/docs/templates/pr-autofixer.html +35 -35
- package/dist/docs/templates/security-reviewer.html +35 -35
- package/dist/docs/templates/thermo-quality-review.html +35 -35
- package/dist/docs/templates/thermo-review.html +35 -35
- package/dist/docs/templates/triage.html +35 -35
- package/dist/docs/troubleshooting.html +35 -35
- package/dist/files-backends/cursor-hosted.d.ts +11 -17
- package/dist/files-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/files-backends/cursor-hosted.js +13 -41
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/internal/artifacts-store.d.ts +11 -0
- package/dist/internal/artifacts-store.d.ts.map +1 -1
- package/dist/internal/artifacts-store.js +113 -18
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +9 -1
- package/dist/internal/cursor/cursor-api-transport.d.ts +37 -0
- package/dist/internal/cursor/cursor-api-transport.d.ts.map +1 -0
- package/dist/internal/cursor/cursor-api-transport.js +44 -0
- package/dist/internal/cursor/hosted-store-secrets.d.ts +21 -0
- package/dist/internal/cursor/hosted-store-secrets.d.ts.map +1 -1
- package/dist/internal/cursor/hosted-store-secrets.js +26 -0
- package/dist/internal/cursor/store-api-client.d.ts +82 -0
- package/dist/internal/cursor/store-api-client.d.ts.map +1 -0
- package/dist/internal/cursor/store-api-client.js +227 -0
- package/dist/internal/deploy-client.d.ts +6 -0
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +3 -0
- package/dist/internal/deploy-manifest.d.ts +8 -0
- package/dist/internal/deploy-manifest.d.ts.map +1 -1
- package/dist/internal/deploy-manifest.js +7 -1
- package/dist/internal/discovery/agent-config.d.ts +3 -1
- package/dist/internal/discovery/agent-config.d.ts.map +1 -1
- package/dist/internal/discovery/agent-config.js +7 -4
- package/dist/internal/framework-storage-selection.d.ts +1 -1
- package/dist/internal/framework-storage-selection.d.ts.map +1 -1
- package/dist/internal/platform-timers.d.ts.map +1 -1
- package/dist/internal/platform-timers.js +20 -2
- package/dist/internal/reminder-runner.d.ts +79 -1
- package/dist/internal/reminder-runner.d.ts.map +1 -1
- package/dist/internal/reminder-runner.js +287 -46
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +7 -0
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +13 -15
- package/dist/internal/store-api-protocol.d.ts +134 -0
- package/dist/internal/store-api-protocol.d.ts.map +1 -0
- package/dist/internal/store-api-protocol.js +126 -0
- package/dist/memory.d.ts +49 -10
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +193 -50
- package/dist/playground/assets/{index-CrMWlgUU.js → index-Cs0MKsv4.js} +30 -30
- package/dist/playground/assets/index-DLwnR9ys.css +1 -0
- package/dist/playground/index.html +2 -2
- package/dist/reminders.d.ts +1 -1
- package/dist/reminders.d.ts.map +1 -1
- package/dist/types.d.ts +44 -10
- package/dist/types.d.ts.map +1 -1
- package/docs/guides/slack.md +16 -1
- package/docs/reference/cli.md +686 -754
- package/package.json +1 -1
- package/skills/setup-slack/SKILL.md +1 -1
- package/src/channels/slack/channel-watch.ts +57 -8
- package/src/channels/slack/inbound.ts +24 -0
- package/src/channels/slack/slack-channel.ts +127 -4
- package/src/channels/slack/types.ts +11 -12
- package/src/files-backends/cursor-hosted.ts +31 -68
- package/src/index.ts +1 -0
- package/src/internal/artifacts-store.ts +131 -25
- package/src/internal/cli-deploy.ts +12 -1
- package/src/internal/cursor/cursor-api-transport.ts +73 -0
- package/src/internal/cursor/hosted-store-secrets.ts +35 -0
- package/src/internal/cursor/store-api-client.ts +360 -0
- package/src/internal/deploy-client.ts +9 -0
- package/src/internal/deploy-manifest.ts +15 -0
- package/src/internal/discovery/agent-config.ts +8 -6
- package/src/internal/framework-storage-selection.ts +1 -1
- package/src/internal/platform-timers.ts +24 -2
- package/src/internal/reminder-runner.ts +454 -66
- package/src/internal/server.ts +8 -0
- package/src/internal/session-engine.ts +17 -22
- package/src/internal/store-api-protocol.ts +222 -0
- package/src/memory.ts +240 -59
- package/src/reminders.ts +1 -0
- package/src/types.ts +47 -10
- package/dist/docs/assets/chunks/@localSearchIndexroot.BnSgidYE.js +0 -1
- package/dist/docs/assets/chunks/channel.DdM5EfNW.js +0 -1
- package/dist/docs/assets/chunks/classDiagram-6PBFFD2Q.CjfGHeg2.js +0 -1
- package/dist/docs/assets/chunks/classDiagram-v2-HSJHXN6E.CjfGHeg2.js +0 -1
- package/dist/docs/assets/chunks/clone.wSOICb_f.js +0 -1
- package/dist/docs/assets/chunks/stateDiagram-v2-QKLJ7IA2.Cu5X28zZ.js +0 -1
- package/dist/docs/assets/chunks/wardley-RL74JXVD.DXy2i1LS.js +0 -162
- package/dist/docs/assets/reference_cli.md.DLWDz9ij.js +0 -95
- package/dist/docs/assets/reference_cli.md.DLWDz9ij.lean.js +0 -1
- package/dist/playground/assets/index-C61EWMBK.css +0 -1
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The store API client: July's one gateway to the factory domain-call lane,
|
|
3
|
+
* `POST /internal/agentsdk/store/api/<call>`. This module owns the whole
|
|
4
|
+
* transport — base URL and deployment credential (the same resolution the
|
|
5
|
+
* files sink uses, via `cursor-api-transport.ts`), envelope encoding,
|
|
6
|
+
* response validation, typed error mapping, and the bounded backoff policy.
|
|
7
|
+
* Feature modules (`memory.ts`, later `session-run-log.ts`, …) call typed
|
|
8
|
+
* methods like {@link StoreApiClient.memoryAppend} and never hand-roll
|
|
9
|
+
* fetch; future domain calls (`sessionRun.report`, `startLog.flush`, …) are
|
|
10
|
+
* added here as new methods on this one client.
|
|
11
|
+
*
|
|
12
|
+
* The lane is selected per pod by the task-bound store protocol pin:
|
|
13
|
+
* {@link isStoreApiLaneEnabled} is true only under the exact
|
|
14
|
+
* `factory-api-v1` pin AND with a provisioned deployment store. The
|
|
15
|
+
* availability check is not redundant: the pin file is written by the hosted
|
|
16
|
+
* task bind, but a local-dev env var could name the pin without any store to
|
|
17
|
+
* serve reads — appending into the void. Both resolve lazily per call, so a
|
|
18
|
+
* pin bound after serve start takes effect without a restart.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
MEMORY_APPEND_MAX_RECORD_BYTES,
|
|
23
|
+
memoryAppendResponseSchema,
|
|
24
|
+
memoryReadResponseSchema,
|
|
25
|
+
STORE_API_MEMORY_APPEND_CALL,
|
|
26
|
+
STORE_API_MEMORY_READ_CALL,
|
|
27
|
+
STORE_API_PATH,
|
|
28
|
+
STORE_API_PROTOCOL_VERSION,
|
|
29
|
+
serializedRecordBytes,
|
|
30
|
+
} from "../store-api-protocol.js";
|
|
31
|
+
import {
|
|
32
|
+
type CursorApiTransportOptions,
|
|
33
|
+
resolveCursorApiBaseUrl,
|
|
34
|
+
resolveCursorApiFetch,
|
|
35
|
+
resolveCursorApiKey,
|
|
36
|
+
} from "./cursor-api-transport.js";
|
|
37
|
+
import {
|
|
38
|
+
resolveDeploymentStoreSourceIdSync,
|
|
39
|
+
resolveStoreProtocolSync,
|
|
40
|
+
storeProtocolEnablesStoreApi,
|
|
41
|
+
} from "./hosted-store-secrets.js";
|
|
42
|
+
|
|
43
|
+
export interface StoreApiClientOptions extends CursorApiTransportOptions {
|
|
44
|
+
/** Deployment store source id override (lane availability check). */
|
|
45
|
+
storeSourceId?: string;
|
|
46
|
+
/** Lazy store source id, preferred over {@link storeSourceId}. */
|
|
47
|
+
resolveStoreSourceId?: () => string | undefined;
|
|
48
|
+
/** Lazy store protocol pin (default: env, then the task-bound file). */
|
|
49
|
+
resolveStoreProtocol?: () => string | undefined;
|
|
50
|
+
/** Backoff sleep override (tests). */
|
|
51
|
+
sleep?: (ms: number) => Promise<void>;
|
|
52
|
+
/** Jitter source override (tests). Returns [0, 1). */
|
|
53
|
+
random?: () => number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* How one domain call failed, after the client's bounded retries:
|
|
58
|
+
*
|
|
59
|
+
* - `rejected` — the server (or the client's own pre-check) refused the
|
|
60
|
+
* payload with a typed 4xx. Deterministic; never retried.
|
|
61
|
+
* - `shed` — 429 from the per-deployment limiter or kill switch. Retried
|
|
62
|
+
* only within the small Retry-After bound below; retrying harder against
|
|
63
|
+
* a shed is exactly the stampede the domain lane exists to prevent.
|
|
64
|
+
* - `unavailable` — 5xx or a network failure, retried up to the attempt
|
|
65
|
+
* bound.
|
|
66
|
+
* - `bad_response` — a 2xx whose body failed the response schema; a
|
|
67
|
+
* contract break, not a transient, so never retried.
|
|
68
|
+
*/
|
|
69
|
+
export type StoreApiFailureKind =
|
|
70
|
+
| "rejected"
|
|
71
|
+
| "shed"
|
|
72
|
+
| "unavailable"
|
|
73
|
+
| "bad_response";
|
|
74
|
+
|
|
75
|
+
export class StoreApiCallError extends Error {
|
|
76
|
+
constructor(
|
|
77
|
+
readonly kind: StoreApiFailureKind,
|
|
78
|
+
message: string,
|
|
79
|
+
readonly status?: number
|
|
80
|
+
) {
|
|
81
|
+
super(message);
|
|
82
|
+
this.name = "StoreApiCallError";
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Bounded backoff policy. Domain calls run at turn boundaries (a memory
|
|
88
|
+
* append after `turn.completed`), so the budget is a few short retries, not
|
|
89
|
+
* persistence: 3 attempts total and never more than 2s asleep between them.
|
|
90
|
+
* A 429 whose Retry-After exceeds the cap is a deliberate shed (a kill
|
|
91
|
+
* switch, a hard budget) — give up immediately instead of camping on it.
|
|
92
|
+
*/
|
|
93
|
+
const STORE_API_MAX_ATTEMPTS = 3;
|
|
94
|
+
const STORE_API_MAX_BACKOFF_MS = 2_000;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Hard deadline per fetch (the envelope POSTs and the presigned journal
|
|
98
|
+
* GET). Without one, a stalled — not failed — connection rides undici's
|
|
99
|
+
* ~300 s inactivity timers, and hydration runs at the head of the session
|
|
100
|
+
* dispatch chain where a stall delays every queued handler. 15 s matches
|
|
101
|
+
* the server's own presigned-fetch deadline and is generous for a ≤5 MiB
|
|
102
|
+
* journal.
|
|
103
|
+
*/
|
|
104
|
+
const STORE_API_FETCH_TIMEOUT_MS = 15_000;
|
|
105
|
+
|
|
106
|
+
function backoffMs(attempt: number, random: () => number): number {
|
|
107
|
+
return Math.min(
|
|
108
|
+
200 * 2 ** attempt * (0.5 + random()),
|
|
109
|
+
STORE_API_MAX_BACKOFF_MS
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Is the domain-call lane on for this pod? Exact pin match plus a
|
|
115
|
+
* provisioned deployment store; anything else keeps the client-side
|
|
116
|
+
* patterns, fail-safe in both directions like the flush pin itself.
|
|
117
|
+
*/
|
|
118
|
+
export function isStoreApiLaneEnabled(
|
|
119
|
+
options: StoreApiClientOptions = {}
|
|
120
|
+
): boolean {
|
|
121
|
+
const protocol =
|
|
122
|
+
options.resolveStoreProtocol?.() ?? resolveStoreProtocolSync();
|
|
123
|
+
if (!storeProtocolEnablesStoreApi(protocol)) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
const sourceId = (
|
|
127
|
+
options.resolveStoreSourceId?.() ??
|
|
128
|
+
options.storeSourceId ??
|
|
129
|
+
resolveDeploymentStoreSourceIdSync()
|
|
130
|
+
)?.trim();
|
|
131
|
+
return sourceId !== undefined && sourceId !== "";
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export type StoreApiClient = {
|
|
135
|
+
/**
|
|
136
|
+
* One `memory.append` domain call: the server appends the record to a
|
|
137
|
+
* server-owned journal shard. Resolves on success; throws
|
|
138
|
+
* {@link StoreApiCallError} after the bounded retries. The caller owns the
|
|
139
|
+
* failure policy (memory drops the record — appending is advisory).
|
|
140
|
+
*/
|
|
141
|
+
memoryAppend(payload: {
|
|
142
|
+
agent: string;
|
|
143
|
+
record: Record<string, unknown>;
|
|
144
|
+
}): Promise<void>;
|
|
145
|
+
/**
|
|
146
|
+
* One `memory.read` domain call plus the presigned byte fetch: the server
|
|
147
|
+
* flushes the agent's pending append buffer (best-effort) and presigns
|
|
148
|
+
* the canonical journal; this method then downloads it. Undefined while
|
|
149
|
+
* the agent has no journal. Throws {@link StoreApiCallError} after the
|
|
150
|
+
* bounded retries — the caller owns the failure policy (hydration keeps
|
|
151
|
+
* the existing mirror).
|
|
152
|
+
*/
|
|
153
|
+
memoryReadContent(payload: {
|
|
154
|
+
agent: string;
|
|
155
|
+
}): Promise<Uint8Array | undefined>;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
export function createStoreApiClient(
|
|
159
|
+
options: StoreApiClientOptions = {}
|
|
160
|
+
): StoreApiClient {
|
|
161
|
+
const sleep =
|
|
162
|
+
options.sleep ??
|
|
163
|
+
((ms: number) => new Promise<void>(resolve => setTimeout(resolve, ms)));
|
|
164
|
+
const random = options.random ?? Math.random;
|
|
165
|
+
|
|
166
|
+
/** One POST of a versioned envelope; returns the parsed 2xx body. */
|
|
167
|
+
const postOnce = async (
|
|
168
|
+
call: string,
|
|
169
|
+
envelope: Record<string, unknown>
|
|
170
|
+
): Promise<Response> => {
|
|
171
|
+
const fetchImpl = resolveCursorApiFetch(options);
|
|
172
|
+
return await fetchImpl(
|
|
173
|
+
`${resolveCursorApiBaseUrl(options)}${STORE_API_PATH}/${call}`,
|
|
174
|
+
{
|
|
175
|
+
method: "POST",
|
|
176
|
+
headers: {
|
|
177
|
+
Authorization: `Bearer ${resolveCursorApiKey(options)}`,
|
|
178
|
+
"Content-Type": "application/json",
|
|
179
|
+
},
|
|
180
|
+
body: JSON.stringify(envelope),
|
|
181
|
+
signal: AbortSignal.timeout(STORE_API_FETCH_TIMEOUT_MS),
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
/** The response's `Retry-After` in ms, when present and numeric. */
|
|
187
|
+
const retryAfterDelayMs = (response: Response): number | undefined => {
|
|
188
|
+
const header = response.headers.get("retry-after")?.trim();
|
|
189
|
+
const seconds =
|
|
190
|
+
header === undefined || header === "" ? Number.NaN : Number(header);
|
|
191
|
+
return Number.isFinite(seconds) ? seconds * 1_000 : undefined;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const describeErrorBody = async (response: Response): Promise<string> => {
|
|
195
|
+
try {
|
|
196
|
+
const parsed = (await response.json()) as { error?: unknown };
|
|
197
|
+
if (typeof parsed?.error === "string") {
|
|
198
|
+
return parsed.error;
|
|
199
|
+
}
|
|
200
|
+
} catch {
|
|
201
|
+
// Non-JSON error body; the status is the message.
|
|
202
|
+
}
|
|
203
|
+
return `HTTP ${response.status}`;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const callApi = async (
|
|
207
|
+
call: string,
|
|
208
|
+
envelope: Record<string, unknown>
|
|
209
|
+
): Promise<unknown> => {
|
|
210
|
+
let lastFailure: StoreApiCallError | undefined;
|
|
211
|
+
// A shed's Retry-After (when within the cap) overrides the next jittered
|
|
212
|
+
// backoff; cleared before every attempt.
|
|
213
|
+
let nextDelayMs: number | undefined;
|
|
214
|
+
for (let attempt = 0; attempt < STORE_API_MAX_ATTEMPTS; attempt += 1) {
|
|
215
|
+
if (attempt > 0) {
|
|
216
|
+
await sleep(nextDelayMs ?? backoffMs(attempt, random));
|
|
217
|
+
}
|
|
218
|
+
nextDelayMs = undefined;
|
|
219
|
+
let response: Response;
|
|
220
|
+
try {
|
|
221
|
+
response = await postOnce(call, envelope);
|
|
222
|
+
} catch (error) {
|
|
223
|
+
lastFailure = new StoreApiCallError(
|
|
224
|
+
"unavailable",
|
|
225
|
+
`store api ${call} transport failure: ${
|
|
226
|
+
error instanceof Error ? error.message : String(error)
|
|
227
|
+
}`
|
|
228
|
+
);
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
if (response.ok) {
|
|
232
|
+
try {
|
|
233
|
+
return (await response.json()) as unknown;
|
|
234
|
+
} catch {
|
|
235
|
+
throw new StoreApiCallError(
|
|
236
|
+
"bad_response",
|
|
237
|
+
`store api ${call} answered non-JSON (${response.status})`,
|
|
238
|
+
response.status
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (response.status === 429) {
|
|
243
|
+
const retryDelayMs = retryAfterDelayMs(response);
|
|
244
|
+
const shed = new StoreApiCallError(
|
|
245
|
+
"shed",
|
|
246
|
+
`store api ${call} shed: ${await describeErrorBody(response)}`,
|
|
247
|
+
response.status
|
|
248
|
+
);
|
|
249
|
+
if (
|
|
250
|
+
retryDelayMs !== undefined &&
|
|
251
|
+
retryDelayMs > STORE_API_MAX_BACKOFF_MS
|
|
252
|
+
) {
|
|
253
|
+
// A long Retry-After is a kill switch or hard budget: obey it by
|
|
254
|
+
// giving up now, not by camping on the route.
|
|
255
|
+
throw shed;
|
|
256
|
+
}
|
|
257
|
+
nextDelayMs = retryDelayMs;
|
|
258
|
+
lastFailure = shed;
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
if (response.status >= 500) {
|
|
262
|
+
// A degraded 503 carries `Retry-After` too (the server's buffer-down
|
|
263
|
+
// answer). Honor it clamped to the backoff cap — unlike a shed, a
|
|
264
|
+
// 5xx is transient by definition, so a long hint never means "give
|
|
265
|
+
// up", just "wait the cap".
|
|
266
|
+
const retryDelayMs = retryAfterDelayMs(response);
|
|
267
|
+
if (retryDelayMs !== undefined) {
|
|
268
|
+
nextDelayMs = Math.min(retryDelayMs, STORE_API_MAX_BACKOFF_MS);
|
|
269
|
+
}
|
|
270
|
+
lastFailure = new StoreApiCallError(
|
|
271
|
+
"unavailable",
|
|
272
|
+
`store api ${call} failed: ${await describeErrorBody(response)}`,
|
|
273
|
+
response.status
|
|
274
|
+
);
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
// Any other 4xx is deterministic; retrying re-sends the same refusal.
|
|
278
|
+
throw new StoreApiCallError(
|
|
279
|
+
"rejected",
|
|
280
|
+
`store api ${call} rejected: ${await describeErrorBody(response)}`,
|
|
281
|
+
response.status
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
throw lastFailure ?? new StoreApiCallError("unavailable", "no attempt ran");
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
return {
|
|
288
|
+
async memoryAppend(payload) {
|
|
289
|
+
const recordBytes = serializedRecordBytes(payload.record);
|
|
290
|
+
if (recordBytes > MEMORY_APPEND_MAX_RECORD_BYTES) {
|
|
291
|
+
// The server-side controller enforces the same cap; checking here
|
|
292
|
+
// spares a doomed request and fails with the same typed shape.
|
|
293
|
+
throw new StoreApiCallError(
|
|
294
|
+
"rejected",
|
|
295
|
+
`memory.append record is ${recordBytes} serialized bytes; the cap is ${MEMORY_APPEND_MAX_RECORD_BYTES}`
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
const body = await callApi(STORE_API_MEMORY_APPEND_CALL, {
|
|
299
|
+
v: STORE_API_PROTOCOL_VERSION,
|
|
300
|
+
call: STORE_API_MEMORY_APPEND_CALL,
|
|
301
|
+
payload,
|
|
302
|
+
});
|
|
303
|
+
if (!memoryAppendResponseSchema.safeParse(body).success) {
|
|
304
|
+
throw new StoreApiCallError(
|
|
305
|
+
"bad_response",
|
|
306
|
+
"unexpected memory.append response shape"
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
async memoryReadContent(payload) {
|
|
312
|
+
const body = await callApi(STORE_API_MEMORY_READ_CALL, {
|
|
313
|
+
v: STORE_API_PROTOCOL_VERSION,
|
|
314
|
+
call: STORE_API_MEMORY_READ_CALL,
|
|
315
|
+
payload,
|
|
316
|
+
});
|
|
317
|
+
const parsed = memoryReadResponseSchema.safeParse(body);
|
|
318
|
+
if (!parsed.success) {
|
|
319
|
+
throw new StoreApiCallError(
|
|
320
|
+
"bad_response",
|
|
321
|
+
"unexpected memory.read response shape"
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
if (parsed.data.journal === null) {
|
|
325
|
+
return undefined;
|
|
326
|
+
}
|
|
327
|
+
// Bulk bytes ride the presigned URL, never the control plane — same
|
|
328
|
+
// split as the verb lane. Unretried: the whole call reruns at the
|
|
329
|
+
// next session start, and hydration keeps the existing mirror.
|
|
330
|
+
const fetchImpl = resolveCursorApiFetch(options);
|
|
331
|
+
let response: Response;
|
|
332
|
+
try {
|
|
333
|
+
response = await fetchImpl(parsed.data.journal.url, {
|
|
334
|
+
method: "GET",
|
|
335
|
+
redirect: "manual",
|
|
336
|
+
signal: AbortSignal.timeout(STORE_API_FETCH_TIMEOUT_MS),
|
|
337
|
+
});
|
|
338
|
+
} catch (error) {
|
|
339
|
+
throw new StoreApiCallError(
|
|
340
|
+
"unavailable",
|
|
341
|
+
`memory.read journal fetch transport failure: ${
|
|
342
|
+
error instanceof Error ? error.message : String(error)
|
|
343
|
+
}`
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
if (response.status === 404) {
|
|
347
|
+
// The presign raced a rotation or delete; absent is absent.
|
|
348
|
+
return undefined;
|
|
349
|
+
}
|
|
350
|
+
if (!response.ok) {
|
|
351
|
+
throw new StoreApiCallError(
|
|
352
|
+
"unavailable",
|
|
353
|
+
`memory.read journal fetch failed (HTTP ${response.status})`,
|
|
354
|
+
response.status
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
358
|
+
},
|
|
359
|
+
};
|
|
360
|
+
}
|
|
@@ -295,6 +295,12 @@ export interface AgentkitStopAccepted {
|
|
|
295
295
|
applicationId: string;
|
|
296
296
|
releaseId: string;
|
|
297
297
|
status: string;
|
|
298
|
+
/**
|
|
299
|
+
* `"global"` when the backend escalated the stop to the application-level
|
|
300
|
+
* suspension — the default for a multi-tenant app stopped by its
|
|
301
|
+
* publisher. Resume needs `start <slug> --global`.
|
|
302
|
+
*/
|
|
303
|
+
scope?: "global";
|
|
298
304
|
cancellation?: AgentkitCancelRunsResult;
|
|
299
305
|
}
|
|
300
306
|
|
|
@@ -471,6 +477,9 @@ const agentkitStopAcceptedSchema = z
|
|
|
471
477
|
applicationId: z.string().min(1),
|
|
472
478
|
releaseId: z.string().min(1),
|
|
473
479
|
status: z.string(),
|
|
480
|
+
// Tolerate scope values a newer backend may add: parse only "global",
|
|
481
|
+
// drop anything else rather than failing the stop response.
|
|
482
|
+
scope: z.literal("global").optional().catch(undefined),
|
|
474
483
|
cancellation: agentkitCancelRunsResultSchema.optional(),
|
|
475
484
|
})
|
|
476
485
|
.passthrough();
|
|
@@ -128,6 +128,10 @@ const agentDeployManifestV1Schema = z.object({
|
|
|
128
128
|
continuation: continuationIdentitySchema,
|
|
129
129
|
catch: catchDeclarationSchema.optional(),
|
|
130
130
|
slackReply: slackReplyDeclarationSchema.optional(),
|
|
131
|
+
slackChannelPosts: z
|
|
132
|
+
.array(z.string().min(1).max(80))
|
|
133
|
+
.max(64)
|
|
134
|
+
.optional(),
|
|
131
135
|
})
|
|
132
136
|
)
|
|
133
137
|
.max(64),
|
|
@@ -166,6 +170,14 @@ export interface AgentDeployManifestDeclarations {
|
|
|
166
170
|
* hosted runtime starts. Present only for `slack` channels in agentic mode.
|
|
167
171
|
*/
|
|
168
172
|
slackReply?: AgentDeployManifestSlackReplyDeclaration;
|
|
173
|
+
/**
|
|
174
|
+
* The channel-watch allowlist (`engagement.channelPosts.allow`).
|
|
175
|
+
* Present only for `slack` channels that watch channel posts. The
|
|
176
|
+
* control plane materializes channel-scoped subscription edges from
|
|
177
|
+
* the channel-id entries so posts outside the allowlist drop at
|
|
178
|
+
* ingress.
|
|
179
|
+
*/
|
|
180
|
+
slackChannelPosts?: string[];
|
|
169
181
|
/**
|
|
170
182
|
* Control-plane catch routes: present only for an authored channel
|
|
171
183
|
* whose auth chain carries a `sharedSecretAuth` / `hmacSignatureAuth`
|
|
@@ -242,6 +254,9 @@ export function buildAgentDeployManifest(
|
|
|
242
254
|
...(kind === "slack" && meta?.slackReply !== undefined
|
|
243
255
|
? { slackReply: meta.slackReply }
|
|
244
256
|
: {}),
|
|
257
|
+
...(kind === "slack" && meta?.slackChannelPosts !== undefined
|
|
258
|
+
? { slackChannelPosts: [...meta.slackChannelPosts] }
|
|
259
|
+
: {}),
|
|
245
260
|
};
|
|
246
261
|
});
|
|
247
262
|
|
|
@@ -33,16 +33,18 @@ export function runtimeArchitectureCompatibilityError(agent: {
|
|
|
33
33
|
: undefined;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/** Author-facing default when {@link AgentConfig.architecture} is omitted. */
|
|
37
|
+
export const DEFAULT_AGENT_ARCHITECTURE: AgentArchitecture = "v2";
|
|
38
|
+
|
|
36
39
|
export function resolveArchitecture(
|
|
37
40
|
ctx: DiscoveryContext,
|
|
38
41
|
agentDir: string,
|
|
39
42
|
architecture: AgentArchitecture | undefined
|
|
40
43
|
): AgentArchitecture | undefined {
|
|
41
|
-
if (
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
) {
|
|
44
|
+
if (architecture === undefined) {
|
|
45
|
+
return DEFAULT_AGENT_ARCHITECTURE;
|
|
46
|
+
}
|
|
47
|
+
if (architecture === "v1" || architecture === "v2") {
|
|
46
48
|
return architecture;
|
|
47
49
|
}
|
|
48
50
|
ctx.diagnostics.push({
|
|
@@ -55,7 +57,7 @@ export function resolveArchitecture(
|
|
|
55
57
|
|
|
56
58
|
/**
|
|
57
59
|
* `persistent: true` selects the long-lived-pod hosting mode of v2. It has
|
|
58
|
-
* no meaning on v1 (resident engine)
|
|
60
|
+
* no meaning on v1 (resident engine).
|
|
59
61
|
*/
|
|
60
62
|
export function resolvePersistent(
|
|
61
63
|
ctx: DiscoveryContext,
|
|
@@ -66,7 +66,7 @@ import { resolveAgentV2Hosting } from "./v2-hosting.js";
|
|
|
66
66
|
export interface FrameworkStorageSelectionInput {
|
|
67
67
|
/** `agent/storage.ts`, when the author wrote one (v1 only: discovery rejects it on v2). */
|
|
68
68
|
readonly authored: StorageDefinition | undefined;
|
|
69
|
-
/** {@link AgentConfig.architecture};
|
|
69
|
+
/** {@link AgentConfig.architecture}; discovery fills `"v2"` when omitted. */
|
|
70
70
|
readonly architecture?: AgentArchitecture | undefined;
|
|
71
71
|
/** Environment override for tests; defaults to `process.env`. */
|
|
72
72
|
readonly env?: Record<string, string | undefined>;
|
|
@@ -236,6 +236,28 @@ async function timedWorkJsonFetch(
|
|
|
236
236
|
});
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
const CP_ERROR_BODY_LIMIT = 512;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Read failures carry status and a bounded body: the guest logs them once
|
|
243
|
+
* per process, and a 405 body names the route the caller should be on.
|
|
244
|
+
*/
|
|
245
|
+
async function reminderCpReadError(
|
|
246
|
+
what: string,
|
|
247
|
+
response: Response
|
|
248
|
+
): Promise<Error> {
|
|
249
|
+
let body = "";
|
|
250
|
+
try {
|
|
251
|
+
body = (await response.text()).trim();
|
|
252
|
+
} catch {
|
|
253
|
+
// Status-only detail.
|
|
254
|
+
}
|
|
255
|
+
const detail = body === "" ? "" : ` ${body.slice(0, CP_ERROR_BODY_LIMIT)}`;
|
|
256
|
+
return new Error(
|
|
257
|
+
`reminder CP ${what} failed: HTTP ${response.status}${detail}`
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
239
261
|
/**
|
|
240
262
|
* Cursor-hosted HTTP ReminderControlPlane for architecture v2.
|
|
241
263
|
* Speaks factory `/internal/agentsdk/reminders` (ELB → factory ECS).
|
|
@@ -275,7 +297,7 @@ export function createCursorHostedReminderControlPlane(
|
|
|
275
297
|
return undefined;
|
|
276
298
|
}
|
|
277
299
|
if (!response.ok) {
|
|
278
|
-
throw
|
|
300
|
+
throw await reminderCpReadError("get", response);
|
|
279
301
|
}
|
|
280
302
|
const parsed: unknown = await response.json();
|
|
281
303
|
return isReminderRecord(parsed) ? parsed : undefined;
|
|
@@ -287,7 +309,7 @@ export function createCursorHostedReminderControlPlane(
|
|
|
287
309
|
`${AGENTSDK_REMINDERS_PREFIX}/reminders`
|
|
288
310
|
);
|
|
289
311
|
if (!response.ok) {
|
|
290
|
-
throw
|
|
312
|
+
throw await reminderCpReadError("list", response);
|
|
291
313
|
}
|
|
292
314
|
const parsed: unknown = await response.json();
|
|
293
315
|
if (
|