@cursor/july 0.1.7 → 0.1.9

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 (210) hide show
  1. package/dist/bin/agent-serve.js +31 -2
  2. package/dist/channels/slack/channel-watch.d.ts +6 -1
  3. package/dist/channels/slack/channel-watch.d.ts.map +1 -1
  4. package/dist/channels/slack/channel-watch.js +22 -1
  5. package/dist/channels/slack/cursor-account.d.ts +4 -0
  6. package/dist/channels/slack/cursor-account.d.ts.map +1 -1
  7. package/dist/channels/slack/cursor-account.js +12 -2
  8. package/dist/channels/slack/defaults.d.ts +10 -5
  9. package/dist/channels/slack/defaults.d.ts.map +1 -1
  10. package/dist/channels/slack/defaults.js +39 -17
  11. package/dist/channels/slack/dispatch.d.ts +18 -0
  12. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  13. package/dist/channels/slack/dispatch.js +141 -38
  14. package/dist/channels/slack/inbound.d.ts +13 -3
  15. package/dist/channels/slack/inbound.d.ts.map +1 -1
  16. package/dist/channels/slack/inbound.js +17 -9
  17. package/dist/channels/slack/index.d.ts +4 -0
  18. package/dist/channels/slack/index.d.ts.map +1 -1
  19. package/dist/channels/slack/index.js +4 -0
  20. package/dist/channels/slack/live-delivery.d.ts.map +1 -1
  21. package/dist/channels/slack/live-delivery.js +1 -0
  22. package/dist/channels/slack/manifest.d.ts.map +1 -1
  23. package/dist/channels/slack/manifest.js +3 -0
  24. package/dist/channels/slack/nudge-carrier.d.ts +19 -0
  25. package/dist/channels/slack/nudge-carrier.d.ts.map +1 -0
  26. package/dist/channels/slack/nudge-carrier.js +166 -0
  27. package/dist/channels/slack/nudge-store.d.ts +61 -0
  28. package/dist/channels/slack/nudge-store.d.ts.map +1 -0
  29. package/dist/channels/slack/nudge-store.js +266 -0
  30. package/dist/channels/slack/post-update-delivery.js +318 -0
  31. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  32. package/dist/channels/slack/slack-channel.js +74 -25
  33. package/dist/channels/slack/stop.d.ts +14 -0
  34. package/dist/channels/slack/stop.d.ts.map +1 -0
  35. package/dist/channels/slack/stop.js +22 -0
  36. package/dist/channels/slack/thread-context.d.ts +81 -0
  37. package/dist/channels/slack/thread-context.d.ts.map +1 -0
  38. package/dist/channels/slack/thread-context.js +470 -0
  39. package/dist/channels/slack/types.d.ts +27 -0
  40. package/dist/channels/slack/types.d.ts.map +1 -1
  41. package/dist/docs/404.html +2 -2
  42. package/dist/docs/ab.html +4 -4
  43. package/dist/docs/assets/{app.COTN7wgo.js → app.Ciosf24M.js} +1 -1
  44. package/dist/docs/assets/chunks/@localSearchIndexroot.D154Detj.js +1 -0
  45. package/dist/docs/assets/chunks/{VPLocalSearchBox.BW3TBdT0.js → VPLocalSearchBox.BcRDly7n.js} +1 -1
  46. package/dist/docs/assets/chunks/{theme.BEJW0vE7.js → theme.CMpbjdfx.js} +2 -2
  47. package/dist/docs/assets/{deployment.md.BtfEsc9S.js → deployment.md.DC5-ImcM.js} +1 -1
  48. package/dist/docs/assets/{example-agents_approval-buddy.md.8R5phXb5.js → example-agents_approval-buddy.md.DFGBYLcc.js} +2 -2
  49. package/dist/docs/assets/{example-agents_approval-buddy.md.8R5phXb5.lean.js → example-agents_approval-buddy.md.DFGBYLcc.lean.js} +1 -1
  50. package/dist/docs/assets/example-agents_index.md.BPXjUtRB.js +2 -0
  51. package/dist/docs/assets/example-agents_index.md.BPXjUtRB.lean.js +1 -0
  52. package/dist/docs/assets/example-agents_oncall.md.BG_sUMly.js +10 -0
  53. package/dist/docs/assets/example-agents_oncall.md.BG_sUMly.lean.js +1 -0
  54. package/dist/docs/assets/{example-agents_security-reviewer.md.D2rtwDTO.js → example-agents_security-reviewer.md.CE9v-aKm.js} +2 -2
  55. package/dist/docs/assets/{example-agents_security-reviewer.md.D2rtwDTO.lean.js → example-agents_security-reviewer.md.CE9v-aKm.lean.js} +1 -1
  56. package/dist/docs/assets/{guides_slack.md.CCwqHvSV.js → guides_slack.md.FWale6Ip.js} +4 -3
  57. package/dist/docs/assets/{guides_slack.md.CCwqHvSV.lean.js → guides_slack.md.FWale6Ip.lean.js} +1 -1
  58. package/dist/docs/assets/{reference_sessions.md.DdjWabnN.js → reference_sessions.md.DLd6mvbv.js} +2 -2
  59. package/dist/docs/assets/{reference_sessions.md.DdjWabnN.lean.js → reference_sessions.md.DLd6mvbv.lean.js} +1 -1
  60. package/dist/docs/assets/{storage.md.CVnInNiN.js → storage.md.DRTdnFvd.js} +9 -2
  61. package/dist/docs/assets/{storage.md.CVnInNiN.lean.js → storage.md.DRTdnFvd.lean.js} +1 -1
  62. package/dist/docs/building-with-agents.html +4 -4
  63. package/dist/docs/concepts.html +4 -4
  64. package/dist/docs/deployment.html +6 -6
  65. package/dist/docs/evals.html +4 -4
  66. package/dist/docs/example-agents/approval-buddy.html +5 -5
  67. package/dist/docs/example-agents/benny.html +5 -5
  68. package/dist/docs/example-agents/bugbot.html +5 -5
  69. package/dist/docs/example-agents/codebase-wiki.html +4 -4
  70. package/dist/docs/example-agents/codeowners-review.html +4 -4
  71. package/dist/docs/example-agents/concierge.html +4 -4
  72. package/dist/docs/example-agents/fsd.html +4 -4
  73. package/dist/docs/example-agents/index.html +6 -6
  74. package/dist/docs/example-agents/knowledge-base.html +4 -4
  75. package/dist/docs/example-agents/oncall.html +34 -0
  76. package/dist/docs/example-agents/security-reviewer.html +6 -6
  77. package/dist/docs/example-agents/slack-agent.html +4 -4
  78. package/dist/docs/example-agents/weather-agent.html +4 -4
  79. package/dist/docs/guides/agent-to-agent.html +4 -4
  80. package/dist/docs/guides/cloud-runtime.html +4 -4
  81. package/dist/docs/guides/github.html +4 -4
  82. package/dist/docs/guides/human-in-the-loop.html +4 -4
  83. package/dist/docs/guides/slack.html +7 -6
  84. package/dist/docs/guides/webhooks.html +4 -4
  85. package/dist/docs/hashmap.json +1 -1
  86. package/dist/docs/hillclimbing.html +4 -4
  87. package/dist/docs/index.html +4 -4
  88. package/dist/docs/quickstart.html +4 -4
  89. package/dist/docs/reference/agent-config.html +4 -4
  90. package/dist/docs/reference/channels.html +4 -4
  91. package/dist/docs/reference/cli.html +4 -4
  92. package/dist/docs/reference/connections.html +4 -4
  93. package/dist/docs/reference/hooks.html +4 -4
  94. package/dist/docs/reference/http-api.html +4 -4
  95. package/dist/docs/reference/instructions.html +4 -4
  96. package/dist/docs/reference/playground.html +4 -4
  97. package/dist/docs/reference/project-layout.html +4 -4
  98. package/dist/docs/reference/schedules.html +4 -4
  99. package/dist/docs/reference/sessions.html +5 -5
  100. package/dist/docs/reference/skills.html +4 -4
  101. package/dist/docs/reference/subagents.html +4 -4
  102. package/dist/docs/reference/tools.html +4 -4
  103. package/dist/docs/scaffolding-agents.html +4 -4
  104. package/dist/docs/storage.html +13 -6
  105. package/dist/docs/troubleshooting.html +4 -4
  106. package/dist/internal/cli-deploy.d.ts.map +1 -1
  107. package/dist/internal/cli-deploy.js +45 -18
  108. package/dist/internal/cli-mcp.d.ts +64 -0
  109. package/dist/internal/cli-mcp.d.ts.map +1 -0
  110. package/dist/internal/cli-mcp.js +570 -0
  111. package/dist/internal/deploy-client.d.ts +7 -1
  112. package/dist/internal/deploy-client.d.ts.map +1 -1
  113. package/dist/internal/deploy-client.js +3 -0
  114. package/dist/internal/deploy-source.d.ts +3 -0
  115. package/dist/internal/deploy-source.d.ts.map +1 -1
  116. package/dist/internal/deploy-source.js +25 -7
  117. package/dist/internal/discovery.js +1 -1
  118. package/dist/internal/distribution.d.ts.map +1 -1
  119. package/dist/internal/distribution.js +3 -0
  120. package/dist/internal/host-kv.d.ts +17 -0
  121. package/dist/internal/host-kv.d.ts.map +1 -0
  122. package/dist/internal/host-kv.js +77 -0
  123. package/dist/internal/hosting.d.ts +12 -2
  124. package/dist/internal/hosting.d.ts.map +1 -1
  125. package/dist/internal/hosting.js +21 -4
  126. package/dist/internal/json-dir-store.js +100 -0
  127. package/dist/internal/logs-client.d.ts +16 -2
  128. package/dist/internal/logs-client.d.ts.map +1 -1
  129. package/dist/internal/logs-client.js +51 -16
  130. package/dist/internal/server.d.ts.map +1 -1
  131. package/dist/internal/server.js +8 -0
  132. package/dist/internal/session-engine.d.ts +42 -7
  133. package/dist/internal/session-engine.d.ts.map +1 -1
  134. package/dist/internal/session-engine.js +276 -66
  135. package/dist/internal/storage-coordinator.d.ts +18 -1
  136. package/dist/internal/storage-coordinator.d.ts.map +1 -1
  137. package/dist/internal/storage-coordinator.js +43 -0
  138. package/dist/internal/workspace.d.ts +19 -0
  139. package/dist/internal/workspace.d.ts.map +1 -1
  140. package/dist/internal/workspace.js +47 -13
  141. package/dist/playground/assets/{index-72vCOBWO.js → index-C0-QEG4O.js} +42 -42
  142. package/dist/playground/assets/index-z3JY9O2x.css +1 -0
  143. package/dist/playground/index.html +2 -2
  144. package/dist/storage-backends/cursor-hosted.d.ts +65 -0
  145. package/dist/storage-backends/cursor-hosted.d.ts.map +1 -0
  146. package/dist/storage-backends/cursor-hosted.js +203 -0
  147. package/dist/storage-backends/file-kv.d.ts +42 -0
  148. package/dist/storage-backends/file-kv.d.ts.map +1 -0
  149. package/dist/storage-backends/file-kv.js +190 -0
  150. package/dist/storage-backends/postgres-kv.d.ts +82 -0
  151. package/dist/storage-backends/postgres-kv.d.ts.map +1 -0
  152. package/dist/storage-backends/postgres-kv.js +185 -0
  153. package/dist/storage.d.ts +10 -1
  154. package/dist/storage.d.ts.map +1 -1
  155. package/dist/storage.js +10 -1
  156. package/dist/types.d.ts +79 -0
  157. package/dist/types.d.ts.map +1 -1
  158. package/docs/.vitepress/cache/deps/package.json +3 -0
  159. package/docs/deployment.md +7 -3
  160. package/docs/example-agents/approval-buddy.md +1 -1
  161. package/docs/example-agents/index.md +15 -10
  162. package/docs/example-agents/oncall.md +216 -0
  163. package/docs/example-agents/security-reviewer.md +5 -5
  164. package/docs/guides/slack.md +11 -2
  165. package/docs/reference/sessions.md +13 -4
  166. package/docs/storage.md +41 -0
  167. package/package.json +22 -1
  168. package/skills/framework-map/SKILL.md +6 -4
  169. package/src/bin/agent-serve.ts +32 -2
  170. package/src/channels/slack/channel-watch.ts +28 -1
  171. package/src/channels/slack/cursor-account.ts +20 -0
  172. package/src/channels/slack/defaults.ts +51 -20
  173. package/src/channels/slack/dispatch.ts +215 -40
  174. package/src/channels/slack/inbound.ts +30 -10
  175. package/src/channels/slack/index.ts +4 -0
  176. package/src/channels/slack/live-delivery.ts +1 -0
  177. package/src/channels/slack/manifest.ts +3 -0
  178. package/src/channels/slack/nudge-carrier.ts +182 -0
  179. package/src/channels/slack/nudge-store.ts +308 -0
  180. package/src/channels/slack/slack-channel.ts +106 -31
  181. package/src/channels/slack/stop.ts +25 -0
  182. package/src/channels/slack/thread-context.ts +572 -0
  183. package/src/channels/slack/types.ts +27 -0
  184. package/src/internal/cli-deploy.ts +58 -20
  185. package/src/internal/cli-mcp.ts +708 -0
  186. package/src/internal/deploy-client.ts +11 -0
  187. package/src/internal/deploy-source.ts +24 -6
  188. package/src/internal/discovery.ts +1 -1
  189. package/src/internal/distribution.ts +3 -0
  190. package/src/internal/host-kv.ts +85 -0
  191. package/src/internal/hosting.ts +24 -4
  192. package/src/internal/logs-client.ts +67 -21
  193. package/src/internal/server.ts +31 -0
  194. package/src/internal/session-engine.ts +379 -61
  195. package/src/internal/storage-coordinator.ts +41 -0
  196. package/src/internal/workspace.ts +48 -11
  197. package/src/storage-backends/cursor-hosted.ts +246 -0
  198. package/src/storage-backends/file-kv.ts +221 -0
  199. package/src/storage-backends/postgres-kv.ts +253 -0
  200. package/src/storage.ts +11 -1
  201. package/src/types.ts +83 -0
  202. package/dist/docs/assets/chunks/@localSearchIndexroot.B7UcKvIn.js +0 -1
  203. package/dist/docs/assets/example-agents_index.md.CRqJlnIf.js +0 -2
  204. package/dist/docs/assets/example-agents_index.md.CRqJlnIf.lean.js +0 -1
  205. package/dist/internal/persistence-coordinator.d.ts +0 -127
  206. package/dist/internal/persistence-coordinator.d.ts.map +0 -1
  207. package/dist/persistence.d.ts +0 -184
  208. package/dist/persistence.d.ts.map +0 -1
  209. package/dist/playground/assets/index-BjnMwYoR.css +0 -1
  210. /package/dist/docs/assets/{deployment.md.BtfEsc9S.lean.js → deployment.md.DC5-ImcM.lean.js} +0 -0
@@ -30,6 +30,7 @@ import { mkdir, rm, writeFile } from "node:fs/promises";
30
30
  import { dirname, join } from "node:path";
31
31
  import { mergeABInstructions, resolveABMaxPlaygroundSessions, resolveABSplit, } from "../ab.js";
32
32
  import { slackThreadPermalink } from "../channels/slack/links.js";
33
+ import { SlackAskDedupeStore, SlackNudgeStore, } from "../channels/slack/nudge-store.js";
33
34
  import { DIRECT_TOOL_CALL_CHANNEL_ID, isSymbolicConnectionTransport, } from "../types.js";
34
35
  import { ABCollector } from "./ab-collector.js";
35
36
  import { foldABStream } from "./ab-fold.js";
@@ -40,16 +41,17 @@ import { mergeCloudOptions, resolveSessionRuntime } from "./cloud-merge.js";
40
41
  import { isDevMode } from "./dev-mode.js";
41
42
  import { EventLogStore } from "./event-log.js";
42
43
  import { TurnEventMapper } from "./event-mapper.js";
44
+ import { createHostKvApi } from "./host-kv.js";
43
45
  import { createHostPlatforms } from "./host-platforms.js";
44
46
  import { formatContinuationToken, newContinuationKey, newSessionId, newToolCallId, } from "./ids.js";
45
47
  import { validateToolInput } from "./json-schema.js";
46
48
  import { McpHost } from "./mcp-host.js";
47
- import { buildIdentitySection, buildSessionContextBlock, SCHEDULE_CHANNEL_PREFIX, } from "./prompt-context.js";
49
+ import { buildSessionContextBlock, SCHEDULE_CHANNEL_PREFIX, } from "./prompt-context.js";
48
50
  import { assertCloudCanHonorAccountServersFilters } from "./resolved-connections.js";
49
51
  import { SessionStore } from "./session-store.js";
50
52
  import { StorageCoordinator } from "./storage-coordinator.js";
51
53
  import { normalizeToolResult, toolCallErrorMessage } from "./tool-result.js";
52
- import { buildAgentToolsCatalog, materializeWorkspace, writeWorkspaceFiles, } from "./workspace.js";
54
+ import { buildAgentsMdContent, isNestedInGitRepo, materializeWorkspace, writeWorkspaceFiles, } from "./workspace.js";
53
55
  export const HTTP_CHANNEL_ID = "http";
54
56
  export { SCHEDULE_CHANNEL_PREFIX } from "./prompt-context.js";
55
57
  export class SessionBusyError extends Error {
@@ -120,6 +122,16 @@ export class SessionEngine {
120
122
  this.turnAborts = new Map();
121
123
  /** Serializes turn admission per session (latest follow-up wins). */
122
124
  this.admissionChains = new Map();
125
+ /**
126
+ * Per-session coalesce queue for `admission: "coalesce"` (Slack). Drained
127
+ * into one follow-up turn when the active turn finishes without interrupt.
128
+ */
129
+ this.coalesceQueues = new Map();
130
+ /**
131
+ * Sessions whose turn lifecycle is draining coalesced follow-ups. Counts as
132
+ * busy so inbound coalesce admits enqueue instead of racing `clearCoalesced`.
133
+ */
134
+ this.coalesceDrains = new Map();
123
135
  this.lastOutcomes = new Map();
124
136
  this.updateChains = new Map();
125
137
  this.dispatchChains = new Map();
@@ -132,6 +144,8 @@ export class SessionEngine {
132
144
  this.runner = options.runner;
133
145
  this.logger = (_a = options.logger) !== null && _a !== void 0 ? _a : (() => { });
134
146
  this.sessions = new SessionStore(join(options.stateRoot, "sessions"));
147
+ this.nudgeStore = new SlackNudgeStore(join(options.stateRoot, "slack-nudges"));
148
+ this.askDedupe = new SlackAskDedupeStore(join(options.stateRoot, "slack-asks"));
135
149
  this.logs = new EventLogStore((sessionId) => this.sessions.eventFilePath(sessionId), (sessionId, error) => this.logger(`[agentkit] failed to persist events for ${sessionId}: ${describeError(error)}`));
136
150
  this.storage =
137
151
  options.project.storage === undefined
@@ -142,6 +156,7 @@ export class SessionEngine {
142
156
  projectRoot: options.project.rootDir,
143
157
  logger: this.logger,
144
158
  });
159
+ this.kvApi = createHostKvApi(Object.assign({ agentName: options.project.name, stateRoot: options.stateRoot }, (this.storage === undefined ? {} : { storage: this.storage })));
145
160
  this.abCollector = new ABCollector(options.project.abs, this.logger, (sessionId) => __awaiter(this, void 0, void 0, function* () { return (yield this.logs.get(sessionId)).snapshot(0); }),
146
161
  // Samples flow to the defineStorage `abs` table (no-op without one).
147
162
  { persistSample: (sample) => { var _a; return (_a = this.storage) === null || _a === void 0 ? void 0 : _a.abSample(sample); } });
@@ -171,13 +186,26 @@ export class SessionEngine {
171
186
  });
172
187
  }
173
188
  }
174
- /** Shared host services (MCP / GitHub / Slack / reminders). */
189
+ /** Shared host services (MCP / GitHub / Slack / reminders / kv / Slack nudges). */
175
190
  get host() {
176
191
  return {
177
192
  mcp: this.mcpHost,
178
193
  github: this.platforms.github,
179
194
  slack: this.platforms.slack,
180
195
  reminders: this.reminderApi,
196
+ kv: this.kvApi,
197
+ slackNudges: this.slackNudgeHostApi(),
198
+ };
199
+ }
200
+ slackNudgeHostApi() {
201
+ return {
202
+ append: (input) => this.nudgeStore.append(input),
203
+ drainFormatted: (continuationKey) => this.nudgeStore.drainFormatted(continuationKey),
204
+ clear: (continuationKey) => this.nudgeStore.clear(continuationKey),
205
+ hasUndelivered: (continuationKey) => this.nudgeStore.hasUndelivered(continuationKey),
206
+ isDelivered: (continuationKey, sourceTs) => this.nudgeStore.isDelivered(continuationKey, sourceTs),
207
+ tryClaimAsk: (channelId, messageTs) => this.askDedupe.tryClaim(channelId, messageTs),
208
+ releaseAsk: (channelId, messageTs) => this.askDedupe.release(channelId, messageTs),
181
209
  };
182
210
  }
183
211
  /** Attach the serve-host reminder API (called from startServer). */
@@ -195,7 +223,7 @@ export class SessionEngine {
195
223
  send(channelId_1, message_1) {
196
224
  return __awaiter(this, arguments, void 0, function* (channelId, message, options = {}) {
197
225
  var _a;
198
- var _b, _c, _d;
226
+ var _b, _c, _d, _e, _f;
199
227
  const auth = (_b = options.auth) !== null && _b !== void 0 ? _b : null;
200
228
  let existing = options.continuationToken === undefined
201
229
  ? undefined
@@ -219,8 +247,10 @@ export class SessionEngine {
219
247
  else if (options.workspaceFiles !== undefined) {
220
248
  record = yield this.updateRecord(existing.sessionId, (current) => (Object.assign(Object.assign({}, current), { pendingWorkspaceFiles: options.workspaceFiles })));
221
249
  }
222
- yield this.startTurn(record.sessionId, message);
223
- return this.channelSession(record.sessionId, record, false);
250
+ const coalesced = yield this.startTurn(record.sessionId, message, Object.assign({ admission: (_c = options.admission) !== null && _c !== void 0 ? _c : "preempt" }, (options.coalesceSourceTs === undefined
251
+ ? {}
252
+ : { sourceTs: options.coalesceSourceTs })));
253
+ return this.channelSession(record.sessionId, record, false, coalesced);
224
254
  }
225
255
  const now = new Date().toISOString();
226
256
  const channelState = this.initialChannelState(channelId, options.state);
@@ -228,9 +258,9 @@ export class SessionEngine {
228
258
  let record = {
229
259
  sessionId: newSessionId(),
230
260
  channelId,
231
- mode: (_c = options.mode) !== null && _c !== void 0 ? _c : "chat",
261
+ mode: (_d = options.mode) !== null && _d !== void 0 ? _d : "chat",
232
262
  purpose,
233
- continuationKey: (_d = options.continuationToken) !== null && _d !== void 0 ? _d : newContinuationKey(),
263
+ continuationKey: (_e = options.continuationToken) !== null && _e !== void 0 ? _e : newContinuationKey(),
234
264
  auth,
235
265
  title: options.title,
236
266
  channelState,
@@ -249,8 +279,20 @@ export class SessionEngine {
249
279
  data: { channelId },
250
280
  });
251
281
  record = yield this.enrollABs(record);
252
- yield this.startTurn(record.sessionId, message);
253
- return this.channelSession(record.sessionId, record, true);
282
+ yield this.startTurn(record.sessionId, message, Object.assign({ admission: (_f = options.admission) !== null && _f !== void 0 ? _f : "preempt" }, (options.coalesceSourceTs === undefined
283
+ ? {}
284
+ : { sourceTs: options.coalesceSourceTs })));
285
+ return this.channelSession(record.sessionId, record, true, false);
286
+ });
287
+ }
288
+ /** Whether a continuation token already maps to a session on this channel. */
289
+ hasContinuationSession(channelId, continuationToken) {
290
+ return __awaiter(this, void 0, void 0, function* () {
291
+ let existing = yield this.sessions.findByContinuation(channelId, continuationToken);
292
+ if (existing === undefined) {
293
+ existing = yield this.restoreSessionByContinuation(channelId, continuationToken);
294
+ }
295
+ return existing !== undefined;
254
296
  });
255
297
  }
256
298
  /**
@@ -377,27 +419,95 @@ export class SessionEngine {
377
419
  // Turn execution
378
420
  // ==========================================================================
379
421
  /**
380
- * Admit a turn for `sessionId`. A follow-up that arrives while a turn is in
381
- * flight preempts it: the running turn is cancelled and its SDK run marked
382
- * terminal before the new turn starts. Admissions are serialized per session
383
- * so a burst of follow-ups can't race two runs onto the same SDK agent — the
384
- * last message wins. Resolves once the new turn has started (not finished).
422
+ * Admit a turn for `sessionId`.
423
+ *
424
+ * Default (`preempt`): a follow-up cancels the running turn and starts
425
+ * immediately (HTTP / MCP). With `admission: "coalesce"` (Slack), a busy
426
+ * session enqueues the message and returns without interrupting; the queue
427
+ * drains into one follow-up when the active turn finishes.
428
+ *
429
+ * Resolves to `true` when the message was coalesced (enqueued), `false`
430
+ * when a turn was started.
385
431
  */
386
- startTurn(sessionId, message) {
432
+ startTurn(sessionId, message, options = {}) {
387
433
  var _a;
388
434
  const prior = (_a = this.admissionChains.get(sessionId)) !== null && _a !== void 0 ? _a : Promise.resolve();
389
435
  const admitted = prior
390
436
  .catch(() => { })
391
- .then(() => this.admitTurn(sessionId, message));
437
+ .then(() => this.admitTurn(sessionId, message, options));
392
438
  this.admissionChains.set(sessionId, admitted.catch(() => { }));
393
439
  return admitted;
394
440
  }
395
- admitTurn(sessionId, message) {
441
+ enqueueCoalesced(sessionId, item) {
442
+ var _a;
443
+ const queue = (_a = this.coalesceQueues.get(sessionId)) !== null && _a !== void 0 ? _a : [];
444
+ if (item.sourceTs !== undefined &&
445
+ queue.some((entry) => entry.sourceTs === item.sourceTs)) {
446
+ return;
447
+ }
448
+ queue.push(item);
449
+ this.coalesceQueues.set(sessionId, queue);
450
+ }
451
+ clearCoalesced(sessionId) {
452
+ this.coalesceQueues.delete(sessionId);
453
+ }
454
+ formatCoalescedFollowups(items) {
455
+ if (items.length === 1) {
456
+ return items[0].message;
457
+ }
458
+ const parts = [
459
+ "<slack_followups>",
460
+ `ANSWER-EACH: ${items.length} asks arrived while you were busy. Address each in order; keep replies compact.`,
461
+ "",
462
+ ];
463
+ for (let i = 0; i < items.length; i++) {
464
+ parts.push(`### Ask ${i + 1}`);
465
+ parts.push(items[i].message);
466
+ parts.push("");
467
+ }
468
+ parts.push("</slack_followups>");
469
+ return parts.join("\n").trimEnd();
470
+ }
471
+ drainCoalescedFollowups(sessionId) {
396
472
  return __awaiter(this, void 0, void 0, function* () {
473
+ var _a;
474
+ const queued = (_a = this.coalesceQueues.get(sessionId)) !== null && _a !== void 0 ? _a : [];
475
+ if (queued.length === 0) {
476
+ return;
477
+ }
478
+ this.coalesceQueues.delete(sessionId);
479
+ const message = this.formatCoalescedFollowups(queued);
480
+ this.logger(`[session] coalesce drain starting follow-up (${queued.length}) ${sessionId}`);
481
+ yield this.startTurn(sessionId, message, {
482
+ admission: "preempt",
483
+ retainCoalesced: true,
484
+ });
485
+ });
486
+ }
487
+ admitTurn(sessionId_1, message_1) {
488
+ return __awaiter(this, arguments, void 0, function* (sessionId, message, options = {}) {
489
+ var _a;
490
+ const admission = (_a = options.admission) !== null && _a !== void 0 ? _a : "preempt";
491
+ if (admission === "coalesce" && this.isTurnRunning(sessionId)) {
492
+ this.enqueueCoalesced(sessionId, Object.assign({ message }, (options.sourceTs === undefined
493
+ ? {}
494
+ : { sourceTs: options.sourceTs })));
495
+ this.logger(`[session] coalesce enqueue ${sessionId}`);
496
+ return true;
497
+ }
397
498
  // Interrupt any turn in flight and wait for it to settle so the SDK run is
398
499
  // terminal before this turn's send (the follow-up also force-expires as a
399
- // belt-and-suspenders — see buildTurnRequest).
400
- yield this.interruptActiveTurn(sessionId);
500
+ // belt-and-suspenders — see buildTurnRequest). Drain-resume skips interrupt:
501
+ // the parent lifecycle already cleared activeTurns, and awaiting the in-
502
+ // flight coalesce drain here would deadlock on itself.
503
+ if (options.retainCoalesced !== true) {
504
+ yield this.interruptActiveTurn(sessionId);
505
+ }
506
+ // Preempt owns the conversation — drop stale coalesced asks. Coalesce
507
+ // admits and drain-resume keep the queue (retainCoalesced / coalesce path).
508
+ if (admission === "preempt" && options.retainCoalesced !== true) {
509
+ this.clearCoalesced(sessionId);
510
+ }
401
511
  this.pendingTurns.add(sessionId);
402
512
  try {
403
513
  let record = yield this.updateRecord(sessionId, (current) => (Object.assign(Object.assign({}, current), { turnCount: current.turnCount + 1 })));
@@ -417,26 +527,51 @@ export class SessionEngine {
417
527
  });
418
528
  const abort = new AbortController();
419
529
  this.turnAborts.set(sessionId, abort);
420
- const turnPromise = this.executeTurn(record, turnId, message, isFirstTurn, abort.signal);
421
- this.activeTurns.set(sessionId, turnPromise);
422
- void turnPromise
423
- .then((outcome) => {
530
+ // Lifecycle promise includes post-turn coalesce drain so waiters and
531
+ // interrupt settle after follow-ups are admitted (not only executeTurn).
532
+ // Assigned before the async body runs so self-identity checks typecheck.
533
+ let lifecycle;
534
+ lifecycle = (() => __awaiter(this, void 0, void 0, function* () {
535
+ let outcome;
536
+ try {
537
+ outcome = yield this.executeTurn(record, turnId, message, isFirstTurn, abort.signal);
538
+ }
539
+ catch (error) {
540
+ outcome = {
541
+ status: "error",
542
+ errorMessage: describeError(error),
543
+ };
544
+ }
424
545
  this.lastOutcomes.set(sessionId, outcome);
425
- })
426
- .catch(() => { })
427
- .finally(() => {
428
- // Guard against clobbering a newer turn that already replaced us.
429
- if (this.activeTurns.get(sessionId) === turnPromise) {
546
+ const wasCurrent = this.activeTurns.get(sessionId) === lifecycle;
547
+ if (wasCurrent) {
430
548
  this.activeTurns.delete(sessionId);
431
549
  }
432
550
  if (this.turnAborts.get(sessionId) === abort) {
433
551
  this.turnAborts.delete(sessionId);
434
552
  }
435
- });
553
+ if (wasCurrent && !abort.signal.aborted) {
554
+ const drain = this.drainCoalescedFollowups(sessionId).catch((error) => {
555
+ this.logger(`[session] coalesce drain failed for ${sessionId}: ${describeError(error)}`);
556
+ });
557
+ this.coalesceDrains.set(sessionId, drain);
558
+ try {
559
+ yield drain;
560
+ }
561
+ finally {
562
+ if (this.coalesceDrains.get(sessionId) === drain) {
563
+ this.coalesceDrains.delete(sessionId);
564
+ }
565
+ }
566
+ }
567
+ return outcome;
568
+ }))();
569
+ this.activeTurns.set(sessionId, lifecycle);
436
570
  }
437
571
  finally {
438
572
  this.pendingTurns.delete(sessionId);
439
573
  }
574
+ return false;
440
575
  });
441
576
  }
442
577
  /**
@@ -452,19 +587,80 @@ export class SessionEngine {
452
587
  if (active !== undefined) {
453
588
  yield active.catch(() => { });
454
589
  }
590
+ const drain = this.coalesceDrains.get(sessionId);
591
+ if (drain !== undefined) {
592
+ yield drain.catch(() => { });
593
+ }
455
594
  });
456
595
  }
457
596
  /**
458
597
  * Abort the in-flight turn without starting a follow-up. Returns whether
459
- * a turn was active (or pending abort) when called.
598
+ * a turn was active (or pending abort) when called. Also clears any
599
+ * coalesced follow-ups queued behind the session.
460
600
  */
461
601
  interrupt(sessionId) {
462
602
  return __awaiter(this, void 0, void 0, function* () {
463
- const hadWork = this.turnAborts.has(sessionId) || this.activeTurns.has(sessionId);
603
+ var _a;
604
+ var _b;
605
+ const hadWork = this.turnAborts.has(sessionId) ||
606
+ this.activeTurns.has(sessionId) ||
607
+ this.coalesceDrains.has(sessionId) ||
608
+ ((_b = (_a = this.coalesceQueues.get(sessionId)) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0;
609
+ this.clearCoalesced(sessionId);
610
+ const record = yield this.sessions.get(sessionId);
611
+ const continuationKey = record === null || record === void 0 ? void 0 : record.continuationKey;
612
+ if (continuationKey !== undefined && continuationKey !== null) {
613
+ yield this.nudgeStore.clear(continuationKey);
614
+ }
464
615
  yield this.interruptActiveTurn(sessionId);
465
616
  return hadWork;
466
617
  });
467
618
  }
619
+ isContinuationBusy(channelId, continuationToken) {
620
+ return __awaiter(this, void 0, void 0, function* () {
621
+ let existing = yield this.sessions.findByContinuation(channelId, continuationToken);
622
+ if (existing === undefined) {
623
+ existing = yield this.restoreSessionByContinuation(channelId, continuationToken);
624
+ }
625
+ if (existing === undefined) {
626
+ return false;
627
+ }
628
+ return this.isTurnRunning(existing.sessionId);
629
+ });
630
+ }
631
+ /**
632
+ * Last agent-posted Slack message ts for warm `<slack_thread_delta>`, from
633
+ * the session's channel state when present.
634
+ */
635
+ getContinuationLastBotMessageTs(channelId, continuationToken) {
636
+ return __awaiter(this, void 0, void 0, function* () {
637
+ let existing = yield this.sessions.findByContinuation(channelId, continuationToken);
638
+ if (existing === undefined) {
639
+ existing = yield this.restoreSessionByContinuation(channelId, continuationToken);
640
+ }
641
+ if (existing === undefined) {
642
+ return undefined;
643
+ }
644
+ const state = existing.channelState;
645
+ if (state === null || state === undefined || typeof state !== "object") {
646
+ return undefined;
647
+ }
648
+ const ts = state.lastBotMessageTs;
649
+ return typeof ts === "string" && ts.trim() !== "" ? ts.trim() : undefined;
650
+ });
651
+ }
652
+ interruptContinuation(channelId, continuationToken) {
653
+ return __awaiter(this, void 0, void 0, function* () {
654
+ let existing = yield this.sessions.findByContinuation(channelId, continuationToken);
655
+ if (existing === undefined) {
656
+ existing = yield this.restoreSessionByContinuation(channelId, continuationToken);
657
+ }
658
+ if (existing === undefined) {
659
+ return false;
660
+ }
661
+ return this.interrupt(existing.sessionId);
662
+ });
663
+ }
468
664
  /**
469
665
  * Local harness cwd for a session: per-send override, else
470
666
  * `<agent.local.cwd>/<sessionId>`, else the durable session workspace.
@@ -530,7 +726,15 @@ export class SessionEngine {
530
726
  return rest;
531
727
  });
532
728
  }
533
- const request = this.buildTurnRequest(record, agent, turnId, message, isFirstTurn, emit, (update) => __awaiter(this, void 0, void 0, function* () {
729
+ // The harness only loads the scaffolded AGENTS.md when the session
730
+ // workspace is its own project root. With a per-session cwd override
731
+ // (PR worktree) the scaffold lands elsewhere, and a workspace nested
732
+ // inside a git repo re-roots harness rules at that repo — in both
733
+ // cases deliver instructions inline on the first turn instead.
734
+ const instructionsInvisibleToHarness = isFirstTurn &&
735
+ (record.workspaceOverride !== undefined ||
736
+ (yield isNestedInGitRepo(harnessCwd)));
737
+ const request = this.buildTurnRequest(record, agent, turnId, message, isFirstTurn, instructionsInvisibleToHarness, emit, (update) => __awaiter(this, void 0, void 0, function* () {
534
738
  mapper.handleUpdate(update);
535
739
  // Yield so the HTTP NDJSON writer can flush tool start/result
536
740
  // events while sibling parallel tools are still executing.
@@ -601,9 +805,8 @@ export class SessionEngine {
601
805
  return Object.assign(Object.assign(Object.assign({}, outcome), (finalResult === undefined ? {} : { result: finalResult })), (finalUsage === undefined ? {} : { usage: finalUsage }));
602
806
  });
603
807
  }
604
- buildTurnRequest(record, agent, turnId, prompt, isFirstTurn, emit, onUpdate, signal) {
605
- var _a, _b, _c;
606
- var _d;
808
+ buildTurnRequest(record, agent, turnId, prompt, isFirstTurn, instructionsInvisibleToHarness, emit, onUpdate, signal) {
809
+ var _a, _b;
607
810
  // Per-session cloud attach (channel/tool handoff) forces cloud runtime
608
811
  // even when the agent defaults to local for chat server tools.
609
812
  const runtime = resolveSessionRuntime({
@@ -626,15 +829,22 @@ export class SessionEngine {
626
829
  });
627
830
  turnPrompt = `${contextBlock}\n\n${prompt}`;
628
831
  }
629
- if (runtime === "cloud" && isFirstTurn) {
630
- const preamble = [
631
- (_d = (_c = agent.instructions) === null || _c === void 0 ? void 0 : _c.trim()) !== null && _d !== void 0 ? _d : "",
632
- buildIdentitySection(agent),
633
- buildAgentToolsCatalog(agent.tools, { includeScripts: true }),
634
- ]
635
- .filter((part) => part !== "")
636
- .join("\n\n");
637
- turnPrompt = `${preamble}\n\n${turnPrompt}`;
832
+ if (isFirstTurn) {
833
+ if (runtime === "cloud") {
834
+ // Cloud VMs have no materialized workspace: ship the full context,
835
+ // including agent tool script bodies.
836
+ const preamble = buildAgentsMdContent(agent, { includeScripts: true });
837
+ turnPrompt = `${preamble}\n\n${turnPrompt}`;
838
+ }
839
+ else if (instructionsInvisibleToHarness) {
840
+ // Local workspace whose AGENTS.md the harness will not load. With a
841
+ // per-session harness cwd override, agent tool scripts live only in
842
+ // the durable scaffold dir — embed bodies like cloud. Otherwise the
843
+ // catalog can reference on-disk paths under the harness cwd.
844
+ const includeScripts = record.workspaceOverride !== undefined;
845
+ const preamble = buildAgentsMdContent(agent, { includeScripts });
846
+ turnPrompt = `${preamble}\n\n${turnPrompt}`;
847
+ }
638
848
  }
639
849
  return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ sessionId: record.sessionId }, (record.sdkAgentId === undefined
640
850
  ? {}
@@ -1128,15 +1338,20 @@ export class SessionEngine {
1128
1338
  var _a;
1129
1339
  // Drain admission first so pendingTurns → activeTurns is visible before we
1130
1340
  // decide the session is idle (reminders use this for queueBehindTurn).
1131
- const chain = this.admissionChains.get(sessionId);
1132
- if (chain !== undefined) {
1133
- yield chain.catch(() => { });
1134
- }
1135
- const active = this.activeTurns.get(sessionId);
1136
- if (active !== undefined) {
1137
- return active;
1341
+ // The active lifecycle promise also awaits coalesce drain + the drained
1342
+ // follow-up's admission, so Slack coalesced waiters see the resume turn.
1343
+ for (;;) {
1344
+ const chain = this.admissionChains.get(sessionId);
1345
+ if (chain !== undefined) {
1346
+ yield chain.catch(() => { });
1347
+ }
1348
+ const active = this.activeTurns.get(sessionId);
1349
+ if (active !== undefined) {
1350
+ yield active;
1351
+ continue;
1352
+ }
1353
+ return (_a = this.lastOutcomes.get(sessionId)) !== null && _a !== void 0 ? _a : { status: "finished" };
1138
1354
  }
1139
- return (_a = this.lastOutcomes.get(sessionId)) !== null && _a !== void 0 ? _a : { status: "finished" };
1140
1355
  });
1141
1356
  }
1142
1357
  /**
@@ -1275,25 +1490,20 @@ export class SessionEngine {
1275
1490
  });
1276
1491
  }
1277
1492
  isTurnRunning(sessionId) {
1278
- // Include the admit window (pendingTurns) so busy policies do not treat a
1279
- // turn that has been accepted but not yet marked active as idle.
1280
- return this.activeTurns.has(sessionId) || this.pendingTurns.has(sessionId);
1493
+ // Include the admit window (pendingTurns) and coalesce drain so busy
1494
+ // policies do not treat a turn that has finished executeTurn but not yet
1495
+ // resumed queued follow-ups as idle (avoids clearCoalesced races).
1496
+ return (this.activeTurns.has(sessionId) ||
1497
+ this.pendingTurns.has(sessionId) ||
1498
+ this.coalesceDrains.has(sessionId));
1281
1499
  }
1282
1500
  sessionInfo(record) {
1283
1501
  return Object.assign(Object.assign(Object.assign({ id: record.sessionId, channelId: record.channelId, mode: record.mode, purpose: record.purpose === "eval" ? "eval" : "live", auth: record.auth, continuationKey: record.continuationKey }, (record.abs === undefined ? {} : { abs: record.abs })), (record.title === undefined ? {} : { title: record.title })), (record.sdkAgentId === undefined
1284
1502
  ? {}
1285
1503
  : { sdkAgentId: record.sdkAgentId }));
1286
1504
  }
1287
- channelSession(sessionId, record, isNew) {
1288
- return {
1289
- id: sessionId,
1290
- continuationToken: record.continuationKey,
1291
- isNew,
1292
- info: this.sessionInfo(record),
1293
- getEventStream: (options) => { var _a; return this.eventStream(sessionId, (_a = options === null || options === void 0 ? void 0 : options.startIndex) !== null && _a !== void 0 ? _a : 0); },
1294
- waitForCompletion: () => this.waitForCompletion(sessionId),
1295
- setContinuationToken: (token) => this.setContinuationToken(sessionId, token),
1296
- };
1505
+ channelSession(sessionId, record, isNew, coalesced = false) {
1506
+ return Object.assign(Object.assign({ id: sessionId, continuationToken: record.continuationKey, isNew }, (coalesced ? { coalesced: true } : {})), { info: this.sessionInfo(record), getEventStream: (options) => { var _a; return this.eventStream(sessionId, (_a = options === null || options === void 0 ? void 0 : options.startIndex) !== null && _a !== void 0 ? _a : 0); }, waitForCompletion: () => this.waitForCompletion(sessionId), setContinuationToken: (token) => this.setContinuationToken(sessionId, token) });
1297
1507
  }
1298
1508
  resolveChannelId(channel) {
1299
1509
  if (typeof channel === "string") {
@@ -16,7 +16,7 @@
16
16
  */
17
17
  import type { ABMetricSample } from "../ab.js";
18
18
  import { type ResolvedStoragePolicy, type StorageContext, type StorageDefinition } from "../storage.js";
19
- import type { SessionEvent, SessionRecord } from "../types.js";
19
+ import type { JsonValue, SessionEvent, SessionRecord } from "../types.js";
20
20
  import type { ABSnapshot } from "./ab-snapshot.js";
21
21
  import type { EvalRunStorage } from "./eval-run-store.js";
22
22
  import { type ReminderRecord } from "./reminder-store.js";
@@ -106,6 +106,23 @@ export declare class StorageCoordinator {
106
106
  * disk). Errors read as empty — same posture as session bulk restore.
107
107
  */
108
108
  listReminders(): Promise<ReminderRecord[]>;
109
+ /**
110
+ * Whether the sink can serve awaited author reads/writes (`get` is
111
+ * required — `put` is always present on {@link StorageConfig}).
112
+ */
113
+ get supportsAuthorKv(): boolean;
114
+ /**
115
+ * Awaited author KV read. Unlike framework session mirrors, this awaits
116
+ * the sink and propagates errors — handlers need read-after-write.
117
+ */
118
+ kvGet(authorKey: string): Promise<JsonValue | undefined>;
119
+ /**
120
+ * Awaited author KV write. Goes straight to the sink (not the
121
+ * at-most-once delivery queue) so callers observe durable success/failure.
122
+ */
123
+ kvPut(authorKey: string, value: JsonValue): Promise<void>;
124
+ /** Awaited author KV delete. No-op when the sink omits `delete`. */
125
+ kvDelete(authorKey: string): Promise<void>;
109
126
  /**
110
127
  * Adapter over the sink's dedicated `evals` table for the playground
111
128
  * eval-run store. Undefined when the table is not configured — eval
@@ -1 +1 @@
1
- {"version":3,"file":"storage-coordinator.d.ts","sourceRoot":"","sources":["../../src/internal/storage-coordinator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"storage-coordinator.d.ts","sourceRoot":"","sources":["../../src/internal/storage-coordinator.ts"],"names":[],"mappings":"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"}
@@ -292,6 +292,49 @@ export class StorageCoordinator {
292
292
  });
293
293
  }
294
294
  // ==========================================================================
295
+ // Author KV ({@link HostContext.kv})
296
+ // ==========================================================================
297
+ /**
298
+ * Whether the sink can serve awaited author reads/writes (`get` is
299
+ * required — `put` is always present on {@link StorageConfig}).
300
+ */
301
+ get supportsAuthorKv() {
302
+ return this.definition.get !== undefined;
303
+ }
304
+ /**
305
+ * Awaited author KV read. Unlike framework session mirrors, this awaits
306
+ * the sink and propagates errors — handlers need read-after-write.
307
+ */
308
+ kvGet(authorKey) {
309
+ return __awaiter(this, void 0, void 0, function* () {
310
+ const get = this.definition.get;
311
+ if (get === undefined) {
312
+ throw new Error("storage get is not configured");
313
+ }
314
+ const key = storageKeys.kv(this.agentName, authorKey);
315
+ // Preserve stored JSON `null` (distinct from a missing key).
316
+ return yield get(key, this.context("restore"));
317
+ });
318
+ }
319
+ /**
320
+ * Awaited author KV write. Goes straight to the sink (not the
321
+ * at-most-once delivery queue) so callers observe durable success/failure.
322
+ */
323
+ kvPut(authorKey, value) {
324
+ return __awaiter(this, void 0, void 0, function* () {
325
+ const key = storageKeys.kv(this.agentName, authorKey);
326
+ yield this.definition.put(key, value, this.context("policy"));
327
+ });
328
+ }
329
+ /** Awaited author KV delete. No-op when the sink omits `delete`. */
330
+ kvDelete(authorKey) {
331
+ return __awaiter(this, void 0, void 0, function* () {
332
+ var _a, _b;
333
+ const key = storageKeys.kv(this.agentName, authorKey);
334
+ yield (_b = (_a = this.definition).delete) === null || _b === void 0 ? void 0 : _b.call(_a, key, this.context("policy"));
335
+ });
336
+ }
337
+ // ==========================================================================
295
338
  // Evals (dedicated `evals` table)
296
339
  // ==========================================================================
297
340
  /**
@@ -38,6 +38,25 @@ export declare function withIdentityPreamble(name: string, instructions: string
38
38
  export declare function buildAgentToolsCatalog(tools: DiscoveredTool[], options?: {
39
39
  includeScripts: boolean;
40
40
  }): string;
41
+ /**
42
+ * The full AGENTS.md-equivalent context for an agent: instructions, identity
43
+ * section, and the agent-side tool catalog. Written to the session workspace
44
+ * as `AGENTS.md`, and inlined into the first turn's prompt when the harness
45
+ * cannot discover that file (cloud runtime, or a local workspace the harness
46
+ * will not treat as the project root — see {@link isNestedInGitRepo}).
47
+ */
48
+ export declare function buildAgentsMdContent(agent: ResolvedAgent, options: {
49
+ includeScripts: boolean;
50
+ }): string;
51
+ /**
52
+ * True when `dir` sits strictly inside a git repository (a `.git` exists in
53
+ * an ancestor directory but not in `dir` itself). The Cursor harness resolves
54
+ * its project root by walking up to the nearest git root, so a nested session
55
+ * workspace loses its own `AGENTS.md` / `.cursor` scaffold: the enclosing
56
+ * repo's rules load instead, and the (necessarily gitignored) workspace files
57
+ * are skipped by nested rule discovery.
58
+ */
59
+ export declare function isNestedInGitRepo(dir: string): Promise<boolean>;
41
60
  export declare function materializeWorkspace(options: MaterializeWorkspaceOptions): Promise<void>;
42
61
  /**
43
62
  * Generate the SKILL.md the harness reads: `name` + `description`
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/internal/workspace.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACd,aAAa,EACd,MAAM,aAAa,CAAC;AAIrB,MAAM,WAAW,2BAA2B;IAC1C,KAAK,EAAE,aAAa,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,IAAI,EAAE,OAAO,CAAC;CACf;AAED,+EAA+E;AAC/E,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM1D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,GAAG,SAAS,GAC/B,MAAM,GAAG,SAAS,CAapB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,cAAc,EAAE,EACvB,OAAO,GAAE;IAAE,cAAc,EAAE,OAAO,CAAA;CAA8B,GAC/D,MAAM,CA6CR;AAED,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,IAAI,CAAC,CAqCf;AAmDD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAO3D;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5B,OAAO,CAAC,IAAI,CAAC,CAMf"}
1
+ {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/internal/workspace.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACd,aAAa,EACd,MAAM,aAAa,CAAC;AAIrB,MAAM,WAAW,2BAA2B;IAC1C,KAAK,EAAE,aAAa,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,IAAI,EAAE,OAAO,CAAC;CACf;AAED,+EAA+E;AAC/E,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM1D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,GAAG,SAAS,GAC/B,MAAM,GAAG,SAAS,CAapB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,cAAc,EAAE,EACvB,OAAO,GAAE;IAAE,cAAc,EAAE,OAAO,CAAA;CAA8B,GAC/D,MAAM,CA6CR;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE;IAAE,cAAc,EAAE,OAAO,CAAA;CAAE,GACnC,MAAM,CAUR;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAcrE;AAED,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,IAAI,CAAC,CA4Bf;AAmDD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAO3D;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5B,OAAO,CAAC,IAAI,CAAC,CAMf"}