@cursor/july 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/agent-serve.js +31 -2
- package/dist/channels/slack/channel-watch.d.ts +6 -1
- package/dist/channels/slack/channel-watch.d.ts.map +1 -1
- package/dist/channels/slack/channel-watch.js +22 -1
- package/dist/channels/slack/cursor-account.d.ts +4 -0
- package/dist/channels/slack/cursor-account.d.ts.map +1 -1
- package/dist/channels/slack/cursor-account.js +12 -2
- package/dist/channels/slack/defaults.d.ts +10 -5
- package/dist/channels/slack/defaults.d.ts.map +1 -1
- package/dist/channels/slack/defaults.js +39 -17
- package/dist/channels/slack/dispatch.d.ts +18 -0
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +141 -38
- package/dist/channels/slack/inbound.d.ts +13 -3
- package/dist/channels/slack/inbound.d.ts.map +1 -1
- package/dist/channels/slack/inbound.js +17 -9
- package/dist/channels/slack/index.d.ts +4 -0
- package/dist/channels/slack/index.d.ts.map +1 -1
- package/dist/channels/slack/index.js +4 -0
- package/dist/channels/slack/live-delivery.d.ts.map +1 -1
- package/dist/channels/slack/live-delivery.js +1 -0
- package/dist/channels/slack/manifest.d.ts.map +1 -1
- package/dist/channels/slack/manifest.js +3 -0
- package/dist/channels/slack/nudge-carrier.d.ts +19 -0
- package/dist/channels/slack/nudge-carrier.d.ts.map +1 -0
- package/dist/channels/slack/nudge-carrier.js +166 -0
- package/dist/channels/slack/nudge-store.d.ts +61 -0
- package/dist/channels/slack/nudge-store.d.ts.map +1 -0
- package/dist/channels/slack/nudge-store.js +266 -0
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +74 -25
- package/dist/channels/slack/stop.d.ts +14 -0
- package/dist/channels/slack/stop.d.ts.map +1 -0
- package/dist/channels/slack/stop.js +22 -0
- package/dist/channels/slack/thread-context.d.ts +81 -0
- package/dist/channels/slack/thread-context.d.ts.map +1 -0
- package/dist/channels/slack/thread-context.js +470 -0
- package/dist/channels/slack/types.d.ts +27 -0
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.COTN7wgo.js → app.BP8mAVMW.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DevOabKJ.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.BW3TBdT0.js → VPLocalSearchBox.1FXIZbz8.js} +1 -1
- package/dist/docs/assets/chunks/{theme.BEJW0vE7.js → theme.Dm1R02v9.js} +2 -2
- package/dist/docs/assets/example-agents_index.md.CwtWu5ix.js +2 -0
- package/dist/docs/assets/{example-agents_index.md.CRqJlnIf.lean.js → example-agents_index.md.CwtWu5ix.lean.js} +1 -1
- package/dist/docs/assets/example-agents_oncall.md.BG_sUMly.js +10 -0
- package/dist/docs/assets/example-agents_oncall.md.BG_sUMly.lean.js +1 -0
- package/dist/docs/assets/{guides_slack.md.CCwqHvSV.js → guides_slack.md.FWale6Ip.js} +4 -3
- package/dist/docs/assets/{guides_slack.md.CCwqHvSV.lean.js → guides_slack.md.FWale6Ip.lean.js} +1 -1
- package/dist/docs/assets/{reference_sessions.md.DdjWabnN.js → reference_sessions.md.DLd6mvbv.js} +2 -2
- package/dist/docs/assets/{reference_sessions.md.DdjWabnN.lean.js → reference_sessions.md.DLd6mvbv.lean.js} +1 -1
- package/dist/docs/assets/{storage.md.CVnInNiN.js → storage.md.Cu3MwM2a.js} +3 -2
- package/dist/docs/assets/{storage.md.CVnInNiN.lean.js → storage.md.Cu3MwM2a.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +4 -4
- package/dist/docs/concepts.html +4 -4
- package/dist/docs/deployment.html +4 -4
- package/dist/docs/evals.html +4 -4
- package/dist/docs/example-agents/approval-buddy.html +4 -4
- package/dist/docs/example-agents/benny.html +5 -5
- package/dist/docs/example-agents/bugbot.html +5 -5
- package/dist/docs/example-agents/codebase-wiki.html +4 -4
- package/dist/docs/example-agents/codeowners-review.html +4 -4
- package/dist/docs/example-agents/concierge.html +4 -4
- package/dist/docs/example-agents/fsd.html +4 -4
- package/dist/docs/example-agents/index.html +6 -6
- package/dist/docs/example-agents/knowledge-base.html +4 -4
- package/dist/docs/example-agents/oncall.html +34 -0
- package/dist/docs/example-agents/security-reviewer.html +4 -4
- package/dist/docs/example-agents/slack-agent.html +4 -4
- package/dist/docs/example-agents/weather-agent.html +4 -4
- package/dist/docs/guides/agent-to-agent.html +4 -4
- package/dist/docs/guides/cloud-runtime.html +4 -4
- package/dist/docs/guides/github.html +4 -4
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/slack.html +7 -6
- package/dist/docs/guides/webhooks.html +4 -4
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +4 -4
- package/dist/docs/index.html +4 -4
- package/dist/docs/quickstart.html +4 -4
- package/dist/docs/reference/agent-config.html +4 -4
- package/dist/docs/reference/channels.html +4 -4
- package/dist/docs/reference/cli.html +4 -4
- package/dist/docs/reference/connections.html +4 -4
- package/dist/docs/reference/hooks.html +4 -4
- package/dist/docs/reference/http-api.html +4 -4
- package/dist/docs/reference/instructions.html +4 -4
- package/dist/docs/reference/playground.html +4 -4
- package/dist/docs/reference/project-layout.html +4 -4
- package/dist/docs/reference/schedules.html +4 -4
- package/dist/docs/reference/sessions.html +5 -5
- package/dist/docs/reference/skills.html +4 -4
- package/dist/docs/reference/subagents.html +4 -4
- package/dist/docs/reference/tools.html +4 -4
- package/dist/docs/scaffolding-agents.html +4 -4
- package/dist/docs/storage.html +7 -6
- package/dist/docs/troubleshooting.html +4 -4
- package/dist/internal/cli-mcp.d.ts +64 -0
- package/dist/internal/cli-mcp.d.ts.map +1 -0
- package/dist/internal/cli-mcp.js +570 -0
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +2 -0
- package/dist/internal/host-kv.d.ts +17 -0
- package/dist/internal/host-kv.d.ts.map +1 -0
- package/dist/internal/host-kv.js +77 -0
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +8 -0
- package/dist/internal/session-engine.d.ts +42 -7
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +247 -51
- package/dist/internal/storage-coordinator.d.ts +18 -1
- package/dist/internal/storage-coordinator.d.ts.map +1 -1
- package/dist/internal/storage-coordinator.js +43 -0
- package/dist/playground/assets/index-7GJIUFQj.css +1 -0
- package/dist/playground/assets/index-Bpr4Th4M.js +319 -0
- package/dist/playground/index.html +2 -2
- package/dist/storage-backends/file-kv.d.ts +42 -0
- package/dist/storage-backends/file-kv.d.ts.map +1 -0
- package/dist/storage-backends/file-kv.js +190 -0
- package/dist/storage-backends/postgres-kv.d.ts +77 -0
- package/dist/storage-backends/postgres-kv.d.ts.map +1 -0
- package/dist/storage-backends/postgres-kv.js +180 -0
- package/dist/storage.d.ts +10 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +10 -1
- package/dist/types.d.ts +79 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/example-agents/index.md +14 -9
- package/docs/example-agents/oncall.md +216 -0
- package/docs/guides/slack.md +11 -2
- package/docs/reference/sessions.md +13 -4
- package/docs/storage.md +27 -0
- package/package.json +15 -1
- package/skills/framework-map/SKILL.md +6 -4
- package/src/bin/agent-serve.ts +32 -2
- package/src/channels/slack/channel-watch.ts +28 -1
- package/src/channels/slack/cursor-account.ts +20 -0
- package/src/channels/slack/defaults.ts +51 -20
- package/src/channels/slack/dispatch.ts +215 -40
- package/src/channels/slack/inbound.ts +30 -10
- package/src/channels/slack/index.ts +4 -0
- package/src/channels/slack/live-delivery.ts +1 -0
- package/src/channels/slack/manifest.ts +3 -0
- package/src/channels/slack/nudge-carrier.ts +182 -0
- package/src/channels/slack/nudge-store.ts +308 -0
- package/src/channels/slack/slack-channel.ts +106 -31
- package/src/channels/slack/stop.ts +25 -0
- package/src/channels/slack/thread-context.ts +572 -0
- package/src/channels/slack/types.ts +27 -0
- package/src/internal/cli-mcp.ts +708 -0
- package/src/internal/distribution.ts +2 -0
- package/src/internal/host-kv.ts +85 -0
- package/src/internal/server.ts +31 -0
- package/src/internal/session-engine.ts +351 -50
- package/src/internal/storage-coordinator.ts +41 -0
- package/src/storage-backends/file-kv.ts +221 -0
- package/src/storage-backends/postgres-kv.ts +248 -0
- package/src/storage.ts +11 -1
- package/src/types.ts +83 -0
- package/dist/channels/slack/post-update-delivery.d.ts +0 -85
- package/dist/channels/slack/post-update-delivery.d.ts.map +0 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.B7UcKvIn.js +0 -1
- package/dist/docs/assets/example-agents_index.md.CRqJlnIf.js +0 -2
- package/dist/internal/json-dir-store.d.ts +0 -32
- package/dist/internal/json-dir-store.d.ts.map +0 -1
- package/dist/internal/persistence-coordinator.d.ts +0 -127
- package/dist/internal/persistence-coordinator.d.ts.map +0 -1
- package/dist/persistence.d.ts +0 -184
- package/dist/persistence.d.ts.map +0 -1
- package/dist/playground/assets/index-72vCOBWO.js +0 -86
- package/dist/playground/assets/index-BjnMwYoR.css +0 -1
|
@@ -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,6 +41,7 @@ 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";
|
|
@@ -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
|
-
|
|
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: (
|
|
261
|
+
mode: (_d = options.mode) !== null && _d !== void 0 ? _d : "chat",
|
|
232
262
|
purpose,
|
|
233
|
-
continuationKey: (
|
|
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
|
-
|
|
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`.
|
|
381
|
-
*
|
|
382
|
-
*
|
|
383
|
-
*
|
|
384
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
@@ -1128,15 +1324,20 @@ export class SessionEngine {
|
|
|
1128
1324
|
var _a;
|
|
1129
1325
|
// Drain admission first so pendingTurns → activeTurns is visible before we
|
|
1130
1326
|
// decide the session is idle (reminders use this for queueBehindTurn).
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1327
|
+
// The active lifecycle promise also awaits coalesce drain + the drained
|
|
1328
|
+
// follow-up's admission, so Slack coalesced waiters see the resume turn.
|
|
1329
|
+
for (;;) {
|
|
1330
|
+
const chain = this.admissionChains.get(sessionId);
|
|
1331
|
+
if (chain !== undefined) {
|
|
1332
|
+
yield chain.catch(() => { });
|
|
1333
|
+
}
|
|
1334
|
+
const active = this.activeTurns.get(sessionId);
|
|
1335
|
+
if (active !== undefined) {
|
|
1336
|
+
yield active;
|
|
1337
|
+
continue;
|
|
1338
|
+
}
|
|
1339
|
+
return (_a = this.lastOutcomes.get(sessionId)) !== null && _a !== void 0 ? _a : { status: "finished" };
|
|
1138
1340
|
}
|
|
1139
|
-
return (_a = this.lastOutcomes.get(sessionId)) !== null && _a !== void 0 ? _a : { status: "finished" };
|
|
1140
1341
|
});
|
|
1141
1342
|
}
|
|
1142
1343
|
/**
|
|
@@ -1275,25 +1476,20 @@ export class SessionEngine {
|
|
|
1275
1476
|
});
|
|
1276
1477
|
}
|
|
1277
1478
|
isTurnRunning(sessionId) {
|
|
1278
|
-
// Include the admit window (pendingTurns)
|
|
1279
|
-
// turn that has
|
|
1280
|
-
|
|
1479
|
+
// Include the admit window (pendingTurns) and coalesce drain so busy
|
|
1480
|
+
// policies do not treat a turn that has finished executeTurn but not yet
|
|
1481
|
+
// resumed queued follow-ups as idle (avoids clearCoalesced races).
|
|
1482
|
+
return (this.activeTurns.has(sessionId) ||
|
|
1483
|
+
this.pendingTurns.has(sessionId) ||
|
|
1484
|
+
this.coalesceDrains.has(sessionId));
|
|
1281
1485
|
}
|
|
1282
1486
|
sessionInfo(record) {
|
|
1283
1487
|
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
1488
|
? {}
|
|
1285
1489
|
: { sdkAgentId: record.sdkAgentId }));
|
|
1286
1490
|
}
|
|
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
|
-
};
|
|
1491
|
+
channelSession(sessionId, record, isNew, coalesced = false) {
|
|
1492
|
+
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
1493
|
}
|
|
1298
1494
|
resolveChannelId(channel) {
|
|
1299
1495
|
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":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C,OAAO,EACL,KAAK,qBAAqB,EAE1B,KAAK,cAAc,EACnB,KAAK,iBAAiB,EAEvB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,
|
|
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
|
/**
|