@cursor/july 0.1.38 → 0.1.40

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.
Files changed (93) hide show
  1. package/dist/bin/agent-serve.js +0 -0
  2. package/dist/channels/slack/api.d.ts.map +1 -1
  3. package/dist/channels/slack/api.js +18 -8
  4. package/dist/channels/slack/defaults.d.ts.map +1 -1
  5. package/dist/channels/slack/defaults.js +26 -17
  6. package/dist/channels/slack/live-delivery.d.ts.map +1 -1
  7. package/dist/channels/slack/live-delivery.js +16 -4
  8. package/dist/channels/slack/post-update-delivery.d.ts +85 -0
  9. package/dist/channels/slack/post-update-delivery.d.ts.map +1 -0
  10. package/dist/docs/404.html +1 -1
  11. package/dist/docs/ab.html +2 -2
  12. package/dist/docs/assets/{app.B4nJHNwO.js → app.DpGLSiym.js} +1 -1
  13. package/dist/docs/assets/chunks/@localSearchIndexroot.D82_RVtF.js +1 -0
  14. package/dist/docs/assets/chunks/{VPLocalSearchBox.DHrr3pLB.js → VPLocalSearchBox.B11XEMST.js} +1 -1
  15. package/dist/docs/assets/chunks/{theme.DQNgSMha.js → theme.iTidHsAx.js} +2 -2
  16. package/dist/docs/building-with-agents.html +2 -2
  17. package/dist/docs/concepts.html +2 -2
  18. package/dist/docs/deployment.html +2 -2
  19. package/dist/docs/evals.html +2 -2
  20. package/dist/docs/example-agents/approval-buddy.html +2 -2
  21. package/dist/docs/example-agents/benny.html +2 -2
  22. package/dist/docs/example-agents/bugbot.html +2 -2
  23. package/dist/docs/example-agents/codebase-wiki.html +2 -2
  24. package/dist/docs/example-agents/codeowners-review.html +2 -2
  25. package/dist/docs/example-agents/concierge.html +2 -2
  26. package/dist/docs/example-agents/fsd.html +2 -2
  27. package/dist/docs/example-agents/index.html +2 -2
  28. package/dist/docs/example-agents/knowledge-base.html +2 -2
  29. package/dist/docs/example-agents/oncall.html +2 -2
  30. package/dist/docs/example-agents/security-reviewer.html +2 -2
  31. package/dist/docs/example-agents/slack-agent.html +2 -2
  32. package/dist/docs/example-agents/weather-agent.html +2 -2
  33. package/dist/docs/guides/agent-to-agent.html +2 -2
  34. package/dist/docs/guides/cloud-runtime.html +2 -2
  35. package/dist/docs/guides/github.html +2 -2
  36. package/dist/docs/guides/human-in-the-loop.html +2 -2
  37. package/dist/docs/guides/mcp-oauth.html +2 -2
  38. package/dist/docs/guides/slack.html +2 -2
  39. package/dist/docs/guides/webhooks.html +2 -2
  40. package/dist/docs/hillclimbing.html +2 -2
  41. package/dist/docs/index.html +2 -2
  42. package/dist/docs/quickstart.html +2 -2
  43. package/dist/docs/reference/agent-config.html +2 -2
  44. package/dist/docs/reference/artifacts.html +2 -2
  45. package/dist/docs/reference/channels.html +2 -2
  46. package/dist/docs/reference/cli.html +2 -2
  47. package/dist/docs/reference/connections.html +2 -2
  48. package/dist/docs/reference/hooks.html +2 -2
  49. package/dist/docs/reference/http-api.html +2 -2
  50. package/dist/docs/reference/instructions.html +2 -2
  51. package/dist/docs/reference/playground.html +2 -2
  52. package/dist/docs/reference/project-layout.html +2 -2
  53. package/dist/docs/reference/prompt.html +2 -2
  54. package/dist/docs/reference/schedules.html +2 -2
  55. package/dist/docs/reference/sessions.html +2 -2
  56. package/dist/docs/reference/skills.html +2 -2
  57. package/dist/docs/reference/subagents.html +2 -2
  58. package/dist/docs/reference/tools.html +2 -2
  59. package/dist/docs/scaffolding-agents.html +2 -2
  60. package/dist/docs/storage.html +2 -2
  61. package/dist/docs/troubleshooting.html +2 -2
  62. package/dist/internal/cli-mcp-oauth.d.ts.map +1 -1
  63. package/dist/internal/cli-mcp-oauth.js +3 -1
  64. package/dist/internal/cursor-event-relay.d.ts +45 -0
  65. package/dist/internal/cursor-event-relay.d.ts.map +1 -1
  66. package/dist/internal/cursor-event-relay.js +96 -2
  67. package/dist/internal/json-dir-store.d.ts +32 -0
  68. package/dist/internal/json-dir-store.d.ts.map +1 -0
  69. package/dist/internal/mcp-oauth.d.ts +12 -1
  70. package/dist/internal/mcp-oauth.d.ts.map +1 -1
  71. package/dist/internal/mcp-oauth.js +25 -3
  72. package/dist/internal/persistence-coordinator.d.ts +127 -0
  73. package/dist/internal/persistence-coordinator.d.ts.map +1 -0
  74. package/dist/internal/server.d.ts.map +1 -1
  75. package/dist/internal/server.js +6 -2
  76. package/dist/multi-tenant.d.ts +80 -0
  77. package/dist/multi-tenant.d.ts.map +1 -0
  78. package/dist/multi-tenant.js +69 -0
  79. package/dist/persistence.d.ts +184 -0
  80. package/dist/persistence.d.ts.map +1 -0
  81. package/dist/playground/assets/index-BRhBbnd4.css +1 -0
  82. package/dist/playground/assets/{index-DLtPsWuV.js → index-Bc1oFgFG.js} +50 -50
  83. package/dist/playground/index.html +2 -2
  84. package/package.json +1 -1
  85. package/src/channels/slack/api.ts +19 -14
  86. package/src/channels/slack/defaults.ts +34 -18
  87. package/src/channels/slack/live-delivery.ts +17 -3
  88. package/src/internal/cli-mcp-oauth.ts +3 -1
  89. package/src/internal/cursor-event-relay.ts +168 -0
  90. package/src/internal/mcp-oauth.ts +29 -4
  91. package/src/internal/server.ts +8 -1
  92. package/dist/docs/assets/chunks/@localSearchIndexroot.B6oHmJzD.js +0 -1
  93. package/dist/playground/assets/index-Bs6TgymR.css +0 -1
@@ -36,22 +36,44 @@ export function formatCursorScmEventLog(event) {
36
36
  const delivery = (_a = event.deliveryId) !== null && _a !== void 0 ? _a : "no delivery id";
37
37
  return `${event.eventName}${action} on ${pr} received=${received}${occurred} (${delivery})`;
38
38
  }
39
+ /** Relay log suffix for one fan-out target, including parse errors on 4xx. */
40
+ export function formatCursorScmDispatchOutcome(target) {
41
+ const slug = target.slug === "" ? "default" : target.slug;
42
+ if (target.status < 400) {
43
+ return `${slug}:${target.status}`;
44
+ }
45
+ const error = readDispatchError(target.body);
46
+ return error === undefined
47
+ ? `${slug}:${target.status}`
48
+ : `${slug}:${target.status} ${error}`;
49
+ }
50
+ function readDispatchError(body) {
51
+ if (typeof body !== "object" || body === null)
52
+ return undefined;
53
+ if (!("error" in body))
54
+ return undefined;
55
+ const error = body.error;
56
+ return typeof error === "string" && error !== "" ? error : undefined;
57
+ }
39
58
  /**
40
59
  * Minimal GitHub webhook body from an SCM-events metadata envelope.
41
60
  * When `pullRequest` has title + head/base refs+shas, emits a Zod-valid
42
61
  * `pull_request` object for `onPullRequest`; otherwise identity-only stubs.
43
62
  * When `comment` is present, emits a Zod-valid `comment` object so relayed
44
63
  * `issue_comment` / `pull_request_review_comment` deliveries dispatch.
64
+ * When a CI snapshot is present, emits `check_run` / `check_suite` /
65
+ * `workflow_run` so those typed hooks parse on the `--cursor-events` path.
45
66
  */
46
67
  export function scmMetadataToGitHubPayload(event) {
47
68
  var _a, _b, _c;
69
+ var _d, _e, _f, _g, _h;
48
70
  const slash = event.repo.indexOf("/");
49
71
  const owner = slash === -1 ? "" : event.repo.slice(0, slash);
50
72
  const name = slash === -1 ? event.repo : event.repo.slice(slash + 1);
51
73
  const repoUrl = `https://github.com/${event.repo}`;
52
74
  const prMeta = event.pullRequest;
53
- const prNumber = (_a = prMeta === null || prMeta === void 0 ? void 0 : prMeta.number) !== null && _a !== void 0 ? _a : event.prNumber;
54
- const prUrl = (_c = (_b = prMeta === null || prMeta === void 0 ? void 0 : prMeta.url) !== null && _b !== void 0 ? _b : event.prUrl) !== null && _c !== void 0 ? _c : (prNumber !== undefined ? `${repoUrl}/pull/${prNumber}` : undefined);
75
+ const prNumber = (_d = prMeta === null || prMeta === void 0 ? void 0 : prMeta.number) !== null && _d !== void 0 ? _d : event.prNumber;
76
+ const prUrl = (_f = (_e = prMeta === null || prMeta === void 0 ? void 0 : prMeta.url) !== null && _e !== void 0 ? _e : event.prUrl) !== null && _f !== void 0 ? _f : (prNumber !== undefined ? `${repoUrl}/pull/${prNumber}` : undefined);
55
77
  const payload = {
56
78
  repository: {
57
79
  full_name: event.repo,
@@ -96,6 +118,20 @@ export function scmMetadataToGitHubPayload(event) {
96
118
  }
97
119
  payload.comment = comment;
98
120
  }
121
+ const checkPullRequests = stubGitHubCiPullRequests({
122
+ snapshot: (_h = (_g = (_a = event.checkRun) === null || _a === void 0 ? void 0 : _a.pullRequests) !== null && _g !== void 0 ? _g : (_b = event.checkSuite) === null || _b === void 0 ? void 0 : _b.pullRequests) !== null && _h !== void 0 ? _h : (_c = event.workflowRun) === null || _c === void 0 ? void 0 : _c.pullRequests,
123
+ prNumber,
124
+ prUrl,
125
+ });
126
+ if (event.checkRun !== undefined) {
127
+ payload.check_run = stubGitHubCheckRun(event.checkRun, checkPullRequests);
128
+ }
129
+ if (event.checkSuite !== undefined) {
130
+ payload.check_suite = stubGitHubCheckSuite(event.checkSuite, checkPullRequests);
131
+ }
132
+ if (event.workflowRun !== undefined) {
133
+ payload.workflow_run = stubGitHubWorkflowRun(event.workflowRun, checkPullRequests);
134
+ }
99
135
  const title = prMeta === null || prMeta === void 0 ? void 0 : prMeta.title;
100
136
  const headRef = prMeta === null || prMeta === void 0 ? void 0 : prMeta.headRef;
101
137
  const headSha = prMeta === null || prMeta === void 0 ? void 0 : prMeta.headSha;
@@ -143,6 +179,64 @@ export function scmMetadataToGitHubPayload(event) {
143
179
  }
144
180
  return payload;
145
181
  }
182
+ function stubGitHubCiPullRequests(input) {
183
+ if (input.snapshot !== undefined && input.snapshot.length > 0) {
184
+ return input.snapshot.map((pr) => (Object.assign({ number: pr.number }, (pr.url === undefined ? {} : { html_url: pr.url }))));
185
+ }
186
+ if (input.prNumber === undefined)
187
+ return [];
188
+ return [
189
+ Object.assign({ number: input.prNumber }, (input.prUrl === undefined ? {} : { html_url: input.prUrl })),
190
+ ];
191
+ }
192
+ function stubGitHubCiApp(app) {
193
+ if (app === undefined)
194
+ return undefined;
195
+ const out = {};
196
+ if (app.slug !== undefined)
197
+ out.slug = app.slug;
198
+ if (app.name !== undefined)
199
+ out.name = app.name;
200
+ return out;
201
+ }
202
+ function stubGitHubCiBase(meta, pullRequests) {
203
+ const object = {
204
+ id: meta.id,
205
+ head_sha: meta.headSha,
206
+ };
207
+ if (meta.status !== undefined)
208
+ object.status = meta.status;
209
+ if (meta.conclusion !== undefined)
210
+ object.conclusion = meta.conclusion;
211
+ if (meta.htmlUrl !== undefined)
212
+ object.html_url = meta.htmlUrl;
213
+ const app = stubGitHubCiApp(meta.app);
214
+ if (app !== undefined)
215
+ object.app = app;
216
+ if (pullRequests.length > 0)
217
+ object.pull_requests = pullRequests;
218
+ return object;
219
+ }
220
+ function stubGitHubCheckRun(meta, pullRequests) {
221
+ const object = stubGitHubCiBase(meta, pullRequests);
222
+ if (meta.name !== undefined)
223
+ object.name = meta.name;
224
+ if (meta.checkSuiteId !== undefined) {
225
+ object.check_suite = { id: meta.checkSuiteId };
226
+ }
227
+ return object;
228
+ }
229
+ function stubGitHubCheckSuite(meta, pullRequests) {
230
+ return stubGitHubCiBase(meta, pullRequests);
231
+ }
232
+ function stubGitHubWorkflowRun(meta, pullRequests) {
233
+ const object = stubGitHubCiBase(meta, pullRequests);
234
+ if (meta.name !== undefined)
235
+ object.name = meta.name;
236
+ if (meta.workflowId !== undefined)
237
+ object.workflow_id = meta.workflowId;
238
+ return object;
239
+ }
146
240
  /**
147
241
  * GitHub SCM-events relay: a thin {@link CursorRelayCore} adapter. Consumers
148
242
  * keep the same `new CursorEventRelay(options).start()/.stop()` surface.
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Small filesystem helpers for one-JSON-file-per-id persistence under a
3
+ * project-relative directory (eval run snapshots, A/B aggregate snapshots).
4
+ */
5
+ export declare function assertNonEmptyDir(dir: string, fnName: string): string;
6
+ /** Resolve `dir` against `projectRoot` when relative. */
7
+ export declare function resolveProjectRelativeDir(args: {
8
+ dir: string;
9
+ projectRoot: string;
10
+ }): string;
11
+ /** List `*.json` basenames in `root` (any order). Missing dir → []. */
12
+ export declare function listJsonBasenames(root: string): Promise<string[]>;
13
+ /**
14
+ * Read and JSON.parse every `*.json` file under `root`. Missing dir → [].
15
+ * Unreadable or corrupt files are skipped so one bad snapshot cannot wipe
16
+ * the rest of the history.
17
+ */
18
+ export declare function readJsonDir<T>(root: string, options?: {
19
+ sortBasenames?: "asc" | "desc";
20
+ }): Promise<T[]>;
21
+ /** Atomically write JSON under `root`/`fileName` (temp file + rename). */
22
+ export declare function writeJsonFile(args: {
23
+ root: string;
24
+ fileName: string;
25
+ value: unknown;
26
+ }): Promise<void>;
27
+ /** Best-effort delete; ignores missing files. */
28
+ export declare function deleteFileInDir(args: {
29
+ root: string;
30
+ fileName: string;
31
+ }): Promise<void>;
32
+ //# sourceMappingURL=json-dir-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-dir-store.d.ts","sourceRoot":"","sources":["../../src/internal/json-dir-store.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAKrE;AAED,yDAAyD;AACzD,wBAAgB,yBAAyB,CAAC,IAAI,EAAE;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,MAAM,CAET;AAWD,uEAAuE;AACvE,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAWvE;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,CAAC,EACjC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;CAAE,GAC3C,OAAO,CAAC,CAAC,EAAE,CAAC,CAiBd;AAED,0EAA0E;AAC1E,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,IAAI,CAAC,CAUhB;AAED,iDAAiD;AACjD,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC,IAAI,CAAC,CAMhB"}
@@ -78,7 +78,18 @@ export type OAuthCallbackResult = {
78
78
  code: string;
79
79
  state?: string;
80
80
  };
81
- export declare function waitForMcpOAuthCallback(args?: {
81
+ /**
82
+ * Deliberately not `@anysphere/mcp-core/oauth/loopback`, which is the canonical
83
+ * implementation every other Cursor surface shares. `@cursor/july` publishes to
84
+ * public npm with no private workspace dependencies, and its OAuth state is a
85
+ * bare UUID rather than the payload that module's validator expects.
86
+ *
87
+ * Nothing here reads Statsig, so this listener is outside
88
+ * `unified_mcp_loopback`: turning that gate off restores the other surfaces'
89
+ * original servers but leaves this one, and its state check, in place.
90
+ */
91
+ export declare function waitForMcpOAuthCallback(args: {
92
+ authorizationUrl: string;
82
93
  port?: number;
83
94
  path?: string;
84
95
  timeoutMs?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-oauth.d.ts","sourceRoot":"","sources":["../../src/internal/mcp-oauth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mcp-oauth.d.ts","sourceRoot":"","sources":["../../src/internal/mcp-oauth.ts"],"names":[],"mappings":"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"}
@@ -307,12 +307,23 @@ export function createHostMcpOAuthProvider(args) {
307
307
  });
308
308
  });
309
309
  }
310
+ /**
311
+ * Deliberately not `@anysphere/mcp-core/oauth/loopback`, which is the canonical
312
+ * implementation every other Cursor surface shares. `@cursor/july` publishes to
313
+ * public npm with no private workspace dependencies, and its OAuth state is a
314
+ * bare UUID rather than the payload that module's validator expects.
315
+ *
316
+ * Nothing here reads Statsig, so this listener is outside
317
+ * `unified_mcp_loopback`: turning that gate off restores the other surfaces'
318
+ * original servers but leaves this one, and its state check, in place.
319
+ */
310
320
  export function waitForMcpOAuthCallback(args) {
311
321
  return __awaiter(this, void 0, void 0, function* () {
312
322
  var _a, _b, _c;
313
- const port = (_a = args === null || args === void 0 ? void 0 : args.port) !== null && _a !== void 0 ? _a : MCP_OAUTH_CALLBACK_PORT;
314
- const path = (_b = args === null || args === void 0 ? void 0 : args.path) !== null && _b !== void 0 ? _b : MCP_OAUTH_CALLBACK_PATH;
315
- const timeoutMs = (_c = args === null || args === void 0 ? void 0 : args.timeoutMs) !== null && _c !== void 0 ? _c : 5 * 60 * 1000;
323
+ const port = (_a = args.port) !== null && _a !== void 0 ? _a : MCP_OAUTH_CALLBACK_PORT;
324
+ const path = (_b = args.path) !== null && _b !== void 0 ? _b : MCP_OAUTH_CALLBACK_PATH;
325
+ const timeoutMs = (_c = args.timeoutMs) !== null && _c !== void 0 ? _c : 5 * 60 * 1000;
326
+ const expectedState = new URL(args.authorizationUrl).searchParams.get("state");
316
327
  return new Promise((resolve, reject) => {
317
328
  let done = false;
318
329
  const finish = (result, error) => {
@@ -344,6 +355,17 @@ export function waitForMcpOAuthCallback(args) {
344
355
  const code = url.searchParams.get("code");
345
356
  const oauthError = url.searchParams.get("error");
346
357
  const state = (_b = url.searchParams.get("state")) !== null && _b !== void 0 ? _b : undefined;
358
+ // Without this, any page that can reach the loopback port during a login
359
+ // can inject its own authorization code into the exchange. Ignore what is
360
+ // not ours rather than failing the login: treating a stray request as
361
+ // fatal would let any page cancel a login, and free the port for someone
362
+ // else to bind before the real callback arrives.
363
+ if (state !== (expectedState !== null && expectedState !== void 0 ? expectedState : undefined)) {
364
+ res
365
+ .writeHead(404, { "content-type": "text/html; charset=utf-8" })
366
+ .end("<!doctype html><html><body><h1>Not found</h1></body></html>");
367
+ return;
368
+ }
347
369
  if (code) {
348
370
  res
349
371
  .writeHead(200, { "content-type": "text/html; charset=utf-8" })
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Runtime for `definePersistence` (`agent/persistence.ts`).
3
+ *
4
+ * The coordinator is the single funnel between the engine's hot paths and
5
+ * the author's key-value sink. The sink is four functions (`put` / `get` /
6
+ * `delete` / `list`); the coordinator owns everything else: it mints every
7
+ * key from the versioned scheme ({@link persistenceKeys}), coalesces
8
+ * session records and batches event chunks per turn (or debounce window),
9
+ * serializes delivery on one bounded queue per agent, and isolates
10
+ * failures — a throwing sink is logged and its write dropped; persistence
11
+ * must never stall or fail a turn.
12
+ *
13
+ * Scaling shape: sessions, evals, and A/Bs all reduce to keyed puts on the
14
+ * same queue, so one author-owned sink covers every domain, and new
15
+ * domains are new key prefixes — not new config surface.
16
+ */
17
+ import type { ABSamplePersistence } from "../ab.js";
18
+ import type { EvalRunPersistence } from "../evals.js";
19
+ import { type PersistenceContext, type PersistenceDefinition, type ResolvedPersistencePolicy } from "../persistence.js";
20
+ import type { SessionEvent, SessionRecord } from "../types.js";
21
+ import type { ABSnapshot } from "./ab-snapshot.js";
22
+ export interface PersistenceCoordinatorOptions {
23
+ definition: PersistenceDefinition;
24
+ agentName: string;
25
+ projectRoot: string;
26
+ logger: (line: string) => void;
27
+ }
28
+ export declare class PersistenceCoordinator {
29
+ readonly policy: ResolvedPersistencePolicy;
30
+ private readonly definition;
31
+ private readonly agentName;
32
+ private readonly projectRoot;
33
+ private readonly logger;
34
+ /** Per-session pending state (events + coalesced record). */
35
+ private readonly buffers;
36
+ /**
37
+ * Last continuation key written to the sink per session, so a token
38
+ * change emits a delete for the stale index entry alongside the new put.
39
+ */
40
+ private readonly continuationIndex;
41
+ /** Single delivery chain: writes reach the sink serialized, in order. */
42
+ private queue;
43
+ /** Ops on the queue not yet delivered (bounded by MAX_PENDING_OPS). */
44
+ private pending;
45
+ /** Ops dropped because the queue was full. */
46
+ private dropped;
47
+ private lastAbSnapshotAt;
48
+ private closed;
49
+ constructor(options: PersistenceCoordinatorOptions);
50
+ /** Durable session-record update (engine calls after every store save). */
51
+ sessionRecord(record: SessionRecord): void;
52
+ /** One appended session event (engine calls from its dispatch funnel). */
53
+ event(event: SessionEvent): void;
54
+ /**
55
+ * A boundary event's dispatch settled: channel/hook handlers ran and
56
+ * their record updates (channel state, continuation tokens) are
57
+ * buffered. This — not the append — is the turn-end flush point, so the
58
+ * persisted session record includes the boundary event's own handler
59
+ * mutations. Debounced batching intentionally ignores boundaries.
60
+ */
61
+ eventDispatched(event: SessionEvent): void;
62
+ /** Flush everything buffered for one session (ordered: events, record). */
63
+ flushSession(sessionId: string, reason?: PersistenceContext["reason"]): void;
64
+ /**
65
+ * Index maintenance for `continuation/{channelId}/{token}` → sessionId:
66
+ * put the current token, delete the previous one when it changed.
67
+ */
68
+ private continuationOps;
69
+ /** Whether the sink can serve the startup bulk restore (`list`). */
70
+ get canBulkRestore(): boolean;
71
+ /**
72
+ * Saved session records for this agent (startup restore). Errors are
73
+ * logged and read as "nothing saved" — a broken store must never block
74
+ * serve start.
75
+ */
76
+ listSessions(): Promise<SessionRecord[]>;
77
+ /**
78
+ * Saved event stream for one session: chunk values concatenated in key
79
+ * order (chunks are keyed by their first event's index, zero-padded, so
80
+ * ascending key order is append order). Errors read as an empty stream.
81
+ */
82
+ listSessionEvents(sessionId: string): Promise<SessionEvent[]>;
83
+ /**
84
+ * One saved session by channel continuation key (lazy restore). Unlike
85
+ * the startup reads, sink errors **propagate**: a failing store must
86
+ * fail the follow-up (which the caller can retry) rather than read as
87
+ * "unknown token" — that would mint a new session under the same
88
+ * continuation key and permanently shadow the real one on this host.
89
+ * A definitive miss (no index entry, or a write-only sink without
90
+ * `get`) resolves undefined.
91
+ */
92
+ getSessionByContinuation(channelId: string, continuationKey: string): Promise<SessionRecord | undefined>;
93
+ /**
94
+ * Adapter for {@link EvalRunPersistence} so the playground eval store
95
+ * can fall back to this sink when `evals.config.ts` sets no
96
+ * `persistRuns`.
97
+ */
98
+ asEvalRunPersistence(): EvalRunPersistence;
99
+ /**
100
+ * Adapter for {@link ABSamplePersistence} so the AB collector can fall
101
+ * back to this sink when `ab.config.ts` sets no `persistSamples`.
102
+ */
103
+ asABSamplePersistence(): ABSamplePersistence;
104
+ /**
105
+ * Refresh the persisted aggregate A/B snapshot, throttled to once per
106
+ * {@link AB_SNAPSHOT_MIN_INTERVAL_MS} (the playground recomputes the
107
+ * fold on every `GET /v1/abs` poll).
108
+ */
109
+ abSnapshot(snapshot: ABSnapshot): void;
110
+ /** Latest persisted aggregate A/B snapshot; errors read as absent. */
111
+ getLatestAbSnapshot(): Promise<ABSnapshot | undefined>;
112
+ /**
113
+ * Flush all buffers and drain the queue, giving the sink at most
114
+ * {@link CLOSE_DRAIN_TIMEOUT_MS} — a hung sink must not stall shutdown
115
+ * past the pod grace period. Called from engine close.
116
+ */
117
+ close(): Promise<void>;
118
+ /** Pending deliveries (exposed for tests and drain instrumentation). */
119
+ whenIdle(): Promise<void>;
120
+ private buffer;
121
+ private armDebounce;
122
+ /** `list` wrapper for the startup reads: missing hook or throw ⇒ empty. */
123
+ private tryList;
124
+ private enqueue;
125
+ private context;
126
+ }
127
+ //# sourceMappingURL=persistence-coordinator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persistence-coordinator.d.ts","sourceRoot":"","sources":["../../src/internal/persistence-coordinator.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/internal/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA4BH,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,EAOhB,YAAY,EACb,MAAM,aAAa,CAAC;AAmGrB,OAAO,EAAE,KAAK,WAAW,EAAmB,MAAM,iBAAiB,CAAC;AAiBpE,8EAA8E;AAC9E,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;CACvB;AAaD,6EAA6E;AAC7E,MAAM,WAAW,0BAA0B;IACzC,oEAAoE;IACpE,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,UAAU,EAAE,EACpB,OAAO,GAAE,YAAY,GAAG,0BAA+B,GACtD,OAAO,CAAC,gBAAgB,CAAC,CAmuB3B"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/internal/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA4BH,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,EAOhB,YAAY,EACb,MAAM,aAAa,CAAC;AAoGrB,OAAO,EAAE,KAAK,WAAW,EAAmB,MAAM,iBAAiB,CAAC;AAiBpE,8EAA8E;AAC9E,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;CACvB;AAaD,6EAA6E;AAC7E,MAAM,WAAW,0BAA0B;IACzC,oEAAoE;IACpE,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,UAAU,EAAE,EACpB,OAAO,GAAE,YAAY,GAAG,0BAA+B,GACtD,OAAO,CAAC,gBAAgB,CAAC,CAmuB3B"}
@@ -44,7 +44,7 @@ import { CursorAccountMcpBridge, collectCursorAccountConnections, cursorAccountM
44
44
  import { CursorBackendClient } from "./cursor/backend-client.js";
45
45
  import { cursorExternalApiUrl, resolveApiKey, SIGN_IN_HINT, } from "./cursor/credentials.js";
46
46
  import { CursorGithubCredentialProvider } from "./cursor/github-credentials.js";
47
- import { CursorEventRelay, formatCursorScmEventLog, scmMetadataToGitHubPayload, } from "./cursor-event-relay.js";
47
+ import { CursorEventRelay, formatCursorScmDispatchOutcome, formatCursorScmEventLog, scmMetadataToGitHubPayload, } from "./cursor-event-relay.js";
48
48
  import { CursorSlackRelay, createCursorSlackApiCaller, formatCursorSlackEventLog, } from "./cursor-slack-relay.js";
49
49
  import { formatDiagnostics, hasErrorDiagnostics, projectInfo, } from "./discovery.js";
50
50
  import { packageVersion } from "./distribution.js";
@@ -1401,8 +1401,12 @@ function startCursorEventRelay(input) {
1401
1401
  targets,
1402
1402
  });
1403
1403
  const outcomes = result.targets
1404
- .map((target) => `${target.slug || "default"}:${target.status}`)
1404
+ .map((target) => formatCursorScmDispatchOutcome(target))
1405
1405
  .join(", ");
1406
+ const rejected = result.targets.filter((target) => target.status >= 400);
1407
+ if (rejected.length > 0) {
1408
+ logger(`[agent-sdk] cursor-events: dropped ${formatCursorScmEventLog(event)} before hook: ${rejected.map(formatCursorScmDispatchOutcome).join(", ")}`);
1409
+ }
1406
1410
  logger(`[agent-sdk] cursor-events: dispatched ${formatCursorScmEventLog(event)}${outcomes === "" ? "" : ` → ${outcomes}`}`);
1407
1411
  }),
1408
1412
  });
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Multi-customer managed-agent bindings for agentkit.
3
+ *
4
+ * Control plane checks the Statsig gate {@link MULTI_TENANT_ENABLED_GATE}
5
+ * before attaching bindings to a wake. Engines never call Statsig; they
6
+ * only react when a {@link TenantWakeContext} / binding list is present.
7
+ * Gate off ⇒ callers pass `multiTenant: false` and keep legacy allowlists.
8
+ */
9
+ /** Statsig gate id — must match `FLAGS.multi_tenant_enabled`. */
10
+ export declare const MULTI_TENANT_ENABLED_GATE: "multi_tenant_enabled";
11
+ /**
12
+ * Per-customer binding for a catalog (managed) agentkit agent.
13
+ * Prompts / tools / MCP are overlays on the fixed agent image.
14
+ */
15
+ export interface ManagedAgentBinding {
16
+ bindingId: string;
17
+ /** Catalog agent id (e.g. `security-reviewer`). */
18
+ agentId: string;
19
+ teamId: number;
20
+ /** `owner/name` repos this customer enabled. */
21
+ repos: readonly string[];
22
+ prompts?: {
23
+ /** Appended to base system instructions; does not replace them. */
24
+ overlay?: string;
25
+ /** Injected as the wake / task brief. */
26
+ task?: string;
27
+ };
28
+ tools?: {
29
+ /** When set, only these tool names are admitted for the wake. */
30
+ enable?: readonly string[];
31
+ };
32
+ mcp?: readonly {
33
+ id: string;
34
+ url: string;
35
+ secretRef?: string;
36
+ }[];
37
+ policy?: {
38
+ model?: string;
39
+ effort?: string;
40
+ [key: string]: unknown;
41
+ };
42
+ }
43
+ /** Request-scoped tenant bag attached to a managed-agent wake. */
44
+ export interface TenantWakeContext {
45
+ binding: ManagedAgentBinding;
46
+ }
47
+ export declare function normalizeRepoFullName(repo: string): string;
48
+ export declare function bindingAllowsRepo(binding: ManagedAgentBinding, repoFullName: string): boolean;
49
+ export declare function findBindingForRepo(bindings: readonly ManagedAgentBinding[], agentId: string, repoFullName: string): ManagedAgentBinding | undefined;
50
+ export type RepositoryAdmission = {
51
+ admitted: true;
52
+ binding?: ManagedAgentBinding;
53
+ } | {
54
+ admitted: false;
55
+ };
56
+ /**
57
+ * Admit a repository for a managed agent.
58
+ * - `multiTenant: true` → matching binding required (fail closed).
59
+ * - `multiTenant: false` → legacy static allowlist only.
60
+ */
61
+ export declare function admitRepository(args: {
62
+ multiTenant: boolean;
63
+ agentId: string;
64
+ repoFullName: string;
65
+ bindings: readonly ManagedAgentBinding[];
66
+ legacyAllowlist: readonly string[];
67
+ }): RepositoryAdmission;
68
+ /** Tools with no `enable` list stay admitted (compat with unconfigured bindings). */
69
+ export declare function isToolAdmitted(binding: ManagedAgentBinding | undefined, toolName: string): boolean;
70
+ /**
71
+ * Author-KV key scoped to a customer team under the existing `kv/` scheme:
72
+ * `agentkit/v1/{agent}/kv/t/{teamId}/{key}`.
73
+ */
74
+ export declare function tenantKvKey(agent: string, teamId: number, key: string): string;
75
+ /**
76
+ * Prefix for listing author-KV keys for one customer team.
77
+ * Matches the URI-encoded form produced by {@link tenantKvKey}.
78
+ */
79
+ export declare function tenantKvPrefix(agent: string, teamId: number): string;
80
+ //# sourceMappingURL=multi-tenant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi-tenant.d.ts","sourceRoot":"","sources":["../src/multi-tenant.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,iEAAiE;AACjE,eAAO,MAAM,yBAAyB,EAAG,sBAA+B,CAAC;AAEzE;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE;QACR,mEAAmE;QACnE,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,yCAAyC;QACzC,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,KAAK,CAAC,EAAE;QACN,iEAAiE;QACjE,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;KAC5B,CAAC;IACF,GAAG,CAAC,EAAE,SAAS;QACb,EAAE,EAAE,MAAM,CAAC;QACX,GAAG,EAAE,MAAM,CAAC;QACZ,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,EAAE,CAAC;IACJ,MAAM,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;CACH;AAED,kEAAkE;AAClE,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,mBAAmB,CAAC;CAC9B;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,mBAAmB,EAC5B,YAAY,EAAE,MAAM,GACnB,OAAO,CAGT;AAED,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,SAAS,mBAAmB,EAAE,EACxC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,GACnB,mBAAmB,GAAG,SAAS,CAIjC;AAED,MAAM,MAAM,mBAAmB,GAC3B;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,OAAO,CAAC,EAAE,mBAAmB,CAAA;CAAE,GACjD;IAAE,QAAQ,EAAE,KAAK,CAAA;CAAE,CAAC;AAExB;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACzC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC,GAAG,mBAAmB,CAiBtB;AAED,qFAAqF;AACrF,wBAAgB,cAAc,CAC5B,OAAO,EAAE,mBAAmB,GAAG,SAAS,EACxC,QAAQ,EAAE,MAAM,GACf,OAAO,CAMT;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,GACV,MAAM,CAKR;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAKpE"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Multi-customer managed-agent bindings for agentkit.
3
+ *
4
+ * Control plane checks the Statsig gate {@link MULTI_TENANT_ENABLED_GATE}
5
+ * before attaching bindings to a wake. Engines never call Statsig; they
6
+ * only react when a {@link TenantWakeContext} / binding list is present.
7
+ * Gate off ⇒ callers pass `multiTenant: false` and keep legacy allowlists.
8
+ */
9
+ import { STORAGE_KEY_ROOT, storageKeys } from "./storage.js";
10
+ /** Statsig gate id — must match `FLAGS.multi_tenant_enabled`. */
11
+ export const MULTI_TENANT_ENABLED_GATE = "multi_tenant_enabled";
12
+ export function normalizeRepoFullName(repo) {
13
+ return repo.trim().toLowerCase();
14
+ }
15
+ export function bindingAllowsRepo(binding, repoFullName) {
16
+ const needle = normalizeRepoFullName(repoFullName);
17
+ return binding.repos.some((r) => normalizeRepoFullName(r) === needle);
18
+ }
19
+ export function findBindingForRepo(bindings, agentId, repoFullName) {
20
+ return bindings.find((b) => b.agentId === agentId && bindingAllowsRepo(b, repoFullName));
21
+ }
22
+ /**
23
+ * Admit a repository for a managed agent.
24
+ * - `multiTenant: true` → matching binding required (fail closed).
25
+ * - `multiTenant: false` → legacy static allowlist only.
26
+ */
27
+ export function admitRepository(args) {
28
+ if (args.multiTenant) {
29
+ const binding = findBindingForRepo(args.bindings, args.agentId, args.repoFullName);
30
+ if (binding === undefined) {
31
+ return { admitted: false };
32
+ }
33
+ return { admitted: true, binding };
34
+ }
35
+ const needle = normalizeRepoFullName(args.repoFullName);
36
+ if (!args.legacyAllowlist.some((r) => normalizeRepoFullName(r) === needle)) {
37
+ return { admitted: false };
38
+ }
39
+ return { admitted: true };
40
+ }
41
+ /** Tools with no `enable` list stay admitted (compat with unconfigured bindings). */
42
+ export function isToolAdmitted(binding, toolName) {
43
+ var _a;
44
+ const enable = (_a = binding === null || binding === void 0 ? void 0 : binding.tools) === null || _a === void 0 ? void 0 : _a.enable;
45
+ if (enable === undefined) {
46
+ return true;
47
+ }
48
+ return enable.includes(toolName);
49
+ }
50
+ /**
51
+ * Author-KV key scoped to a customer team under the existing `kv/` scheme:
52
+ * `agentkit/v1/{agent}/kv/t/{teamId}/{key}`.
53
+ */
54
+ export function tenantKvKey(agent, teamId, key) {
55
+ if (!Number.isInteger(teamId) || teamId < 0) {
56
+ throw new Error(`invalid teamId for tenant storage: ${String(teamId)}`);
57
+ }
58
+ return storageKeys.kv(agent, `t/${teamId}/${key}`);
59
+ }
60
+ /**
61
+ * Prefix for listing author-KV keys for one customer team.
62
+ * Matches the URI-encoded form produced by {@link tenantKvKey}.
63
+ */
64
+ export function tenantKvPrefix(agent, teamId) {
65
+ if (!Number.isInteger(teamId) || teamId < 0) {
66
+ throw new Error(`invalid teamId for tenant storage: ${String(teamId)}`);
67
+ }
68
+ return `${STORAGE_KEY_ROOT}/${agent}/kv/${encodeURIComponent(`t/${teamId}/`)}`;
69
+ }