@cursor/july 0.1.43 → 0.1.46
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/AGENTS.md +5 -3
- package/README.md +3 -1
- package/dist/channels/github/api.d.ts +25 -0
- package/dist/channels/github/api.d.ts.map +1 -1
- package/dist/channels/github/api.js +48 -0
- package/dist/channels/github/cursor-account.d.ts +7 -0
- package/dist/channels/github/cursor-account.d.ts.map +1 -1
- package/dist/channels/github/cursor-account.js +18 -0
- package/dist/channels/github/defaults.d.ts +1 -1
- package/dist/channels/github/defaults.js +1 -1
- package/dist/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +15 -7
- package/dist/channels/github/progress.d.ts +6 -5
- package/dist/channels/github/progress.d.ts.map +1 -1
- package/dist/channels/github/progress.js +58 -16
- package/dist/channels/github/state.d.ts +11 -0
- package/dist/channels/github/state.d.ts.map +1 -1
- package/dist/channels/github/state.js +1 -1
- package/dist/channels/github/types.d.ts +11 -3
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/channels.d.ts +17 -0
- package/dist/channels.d.ts.map +1 -1
- package/dist/channels.js +31 -0
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +3 -3
- package/dist/docs/assets/{app.DsTLo2j1.js → app.C2NFTb-h.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.B-FxvuoI.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.DoVfutcV.js → VPLocalSearchBox.o1_FJGip.js} +1 -1
- package/dist/docs/assets/chunks/{theme.DtuVtKkc.js → theme.BcGduE7g.js} +2 -2
- package/dist/docs/assets/{guides_github.md.DSg0j9qj.js → guides_github.md.B8QKtIpw.js} +7 -6
- package/dist/docs/assets/{guides_github.md.DSg0j9qj.lean.js → guides_github.md.B8QKtIpw.lean.js} +1 -1
- package/dist/docs/assets/guides_opentelemetry.md.Csn7ZI25.js +27 -0
- package/dist/docs/assets/guides_opentelemetry.md.Csn7ZI25.lean.js +1 -0
- package/dist/docs/assets/index.md.COeoSkcF.js +5 -0
- package/dist/docs/assets/index.md.COeoSkcF.lean.js +1 -0
- package/dist/docs/assets/{quickstart.md.rIbfahJM.js → quickstart.md.DZxBu44y.js} +18 -49
- package/dist/docs/assets/quickstart.md.DZxBu44y.lean.js +1 -0
- package/dist/docs/assets/{reference_agent-config.md.CRmkoxd6.js → reference_agent-config.md.DHkkvcEZ.js} +9 -4
- package/dist/docs/assets/{reference_agent-config.md.CRmkoxd6.lean.js → reference_agent-config.md.DHkkvcEZ.lean.js} +1 -1
- package/dist/docs/assets/{reference_cli.md.DqEqQpGn.js → reference_cli.md.BDmjPPAm.js} +2 -2
- package/dist/docs/assets/{reference_hooks.md.4cbRRIIu.js → reference_hooks.md.DJE5DXcT.js} +1 -1
- package/dist/docs/assets/{reference_tools.md.lSrsTxYJ.js → reference_tools.md.kJfJAP2m.js} +1 -1
- package/dist/docs/assets/{storage.md.DcGSnIxX.js → storage.md.CJYd5YmN.js} +2 -2
- package/dist/docs/assets/{storage.md.DcGSnIxX.lean.js → storage.md.CJYd5YmN.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +3 -3
- package/dist/docs/concepts.html +3 -3
- package/dist/docs/deployment.html +3 -3
- package/dist/docs/evals.html +3 -3
- package/dist/docs/example-agents/approval-buddy.html +3 -3
- package/dist/docs/example-agents/benny.html +3 -3
- package/dist/docs/example-agents/bugbot.html +3 -3
- package/dist/docs/example-agents/codebase-wiki.html +3 -3
- package/dist/docs/example-agents/codeowners-review.html +3 -3
- package/dist/docs/example-agents/concierge.html +3 -3
- package/dist/docs/example-agents/fsd.html +3 -3
- package/dist/docs/example-agents/index.html +3 -3
- package/dist/docs/example-agents/knowledge-base.html +3 -3
- package/dist/docs/example-agents/oncall.html +3 -3
- package/dist/docs/example-agents/security-reviewer.html +3 -3
- package/dist/docs/example-agents/slack-agent.html +3 -3
- package/dist/docs/example-agents/weather-agent.html +3 -3
- package/dist/docs/guides/agent-to-agent.html +3 -3
- package/dist/docs/guides/cloud-runtime.html +3 -3
- package/dist/docs/guides/github.html +10 -9
- package/dist/docs/guides/human-in-the-loop.html +3 -3
- package/dist/docs/guides/mcp-oauth.html +3 -3
- package/dist/docs/guides/opentelemetry.html +12 -5
- package/dist/docs/guides/slack.html +3 -3
- package/dist/docs/guides/webhooks.html +3 -3
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +3 -3
- package/dist/docs/index.html +8 -28
- package/dist/docs/quickstart.html +24 -55
- package/dist/docs/reference/agent-config.html +11 -6
- package/dist/docs/reference/artifacts.html +3 -3
- package/dist/docs/reference/channels.html +3 -3
- package/dist/docs/reference/cli.html +6 -6
- package/dist/docs/reference/connections.html +3 -3
- package/dist/docs/reference/hooks.html +5 -5
- package/dist/docs/reference/http-api.html +3 -3
- package/dist/docs/reference/instructions.html +3 -3
- package/dist/docs/reference/playground.html +3 -3
- package/dist/docs/reference/project-layout.html +3 -3
- package/dist/docs/reference/prompt.html +3 -3
- package/dist/docs/reference/schedules.html +3 -3
- package/dist/docs/reference/sessions.html +3 -3
- package/dist/docs/reference/skills.html +3 -3
- package/dist/docs/reference/subagents.html +3 -3
- package/dist/docs/reference/tools.html +5 -5
- package/dist/docs/scaffolding-agents.html +3 -3
- package/dist/docs/storage.html +5 -5
- package/dist/docs/templates/triage.html +3 -3
- package/dist/docs/troubleshooting.html +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/internal/auth.d.ts +4 -0
- package/dist/internal/auth.d.ts.map +1 -1
- package/dist/internal/auth.js +7 -0
- package/dist/internal/cli-ax.js +2 -2
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +38 -7
- package/dist/internal/deploy-client.d.ts +8 -0
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +5 -2
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +46 -9
- package/dist/internal/durable-session-store.d.ts +35 -23
- package/dist/internal/durable-session-store.d.ts.map +1 -1
- package/dist/internal/durable-session-store.js +71 -26
- package/dist/internal/host-otel.d.ts +25 -0
- package/dist/internal/host-otel.d.ts.map +1 -0
- package/dist/internal/host-otel.js +162 -0
- package/dist/internal/init-project.js +10 -10
- package/dist/internal/ledger-serving.d.ts +135 -67
- package/dist/internal/ledger-serving.d.ts.map +1 -1
- package/dist/internal/ledger-serving.js +326 -107
- package/dist/internal/otel/collector.d.ts +5 -0
- package/dist/internal/otel/collector.d.ts.map +1 -1
- package/dist/internal/otel/collector.js +68 -27
- package/dist/internal/sdk-runner.d.ts +1 -1
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +1 -1
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +3 -2
- package/dist/internal/session-engine.d.ts +23 -18
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +83 -32
- package/dist/kv/memory-kv.d.ts +8 -0
- package/dist/kv/memory-kv.d.ts.map +1 -1
- package/dist/kv/memory-kv.js +8 -0
- package/dist/ledger/janitor.d.ts +60 -0
- package/dist/ledger/janitor.d.ts.map +1 -0
- package/dist/ledger/janitor.js +137 -0
- package/dist/ledger/session-ledger.d.ts +132 -43
- package/dist/ledger/session-ledger.d.ts.map +1 -1
- package/dist/ledger/session-ledger.js +282 -50
- package/dist/ledger.d.ts +9 -8
- package/dist/ledger.d.ts.map +1 -1
- package/dist/ledger.js +7 -7
- package/dist/playground/assets/{index-D-DIYQzw.js → index-6wW6SAc4.js} +19 -19
- package/dist/playground/assets/index-CSRYDAFw.css +1 -0
- package/dist/playground/index.html +2 -2
- package/dist/storage-backends/cursor-hosted.d.ts +5 -0
- package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/storage-backends/cursor-hosted.js +6 -0
- package/dist/types.d.ts +100 -10
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +33 -106
- package/docs/guides/github.md +23 -18
- package/docs/guides/opentelemetry.md +30 -0
- package/docs/quickstart.md +89 -227
- package/docs/reference/agent-config.md +47 -0
- package/docs/reference/cli.md +2 -2
- package/docs/reference/hooks.md +1 -1
- package/docs/reference/tools.md +5 -4
- package/docs/storage.md +26 -9
- package/package.json +2 -2
- package/skills/otel/SKILL.md +20 -2
- package/src/channels/github/api.ts +122 -0
- package/src/channels/github/cursor-account.ts +23 -0
- package/src/channels/github/defaults.ts +1 -1
- package/src/channels/github/github-channel.ts +14 -1
- package/src/channels/github/progress.ts +84 -19
- package/src/channels/github/state.ts +14 -0
- package/src/channels/github/types.ts +7 -3
- package/src/channels.ts +38 -0
- package/src/index.ts +2 -0
- package/src/internal/auth.ts +10 -0
- package/src/internal/cli-ax.ts +2 -2
- package/src/internal/cli-deploy.ts +58 -3
- package/src/internal/deploy-client.ts +11 -0
- package/src/internal/discovery.ts +49 -0
- package/src/internal/durable-session-store.ts +66 -27
- package/src/internal/host-otel.ts +252 -0
- package/src/internal/init-project.ts +10 -10
- package/src/internal/ledger-serving.ts +439 -148
- package/src/internal/otel/collector.ts +135 -50
- package/src/internal/sdk-runner.ts +6 -0
- package/src/internal/server.ts +7 -2
- package/src/internal/session-engine.ts +127 -39
- package/src/kv/memory-kv.ts +8 -0
- package/src/ledger/janitor.ts +193 -0
- package/src/ledger/session-ledger.ts +372 -68
- package/src/ledger.ts +21 -6
- package/src/storage-backends/cursor-hosted.ts +17 -2
- package/src/types.ts +108 -10
- package/dist/docs/assets/chunks/@localSearchIndexroot.BZx4pmZL.js +0 -1
- package/dist/docs/assets/guides_opentelemetry.md.DmyUwC5R.js +0 -20
- package/dist/docs/assets/guides_opentelemetry.md.DmyUwC5R.lean.js +0 -1
- package/dist/docs/assets/index.md.CMc5FP7E.js +0 -25
- package/dist/docs/assets/index.md.CMc5FP7E.lean.js +0 -1
- package/dist/docs/assets/quickstart.md.rIbfahJM.lean.js +0 -1
- package/dist/playground/assets/index-DQTH622f.css +0 -1
- /package/dist/docs/assets/{reference_cli.md.DqEqQpGn.lean.js → reference_cli.md.BDmjPPAm.lean.js} +0 -0
- /package/dist/docs/assets/{reference_hooks.md.4cbRRIIu.lean.js → reference_hooks.md.DJE5DXcT.lean.js} +0 -0
- /package/dist/docs/assets/{reference_tools.md.lSrsTxYJ.lean.js → reference_tools.md.kJfJAP2m.lean.js} +0 -0
|
@@ -30,7 +30,7 @@ import { foldABStream } from "./ab-fold.js";
|
|
|
30
30
|
import { buildABSnapshot } from "./ab-snapshot.js";
|
|
31
31
|
import { ApprovalGate } from "./approval-gate.js";
|
|
32
32
|
import { ArtifactsStore } from "./artifacts-store.js";
|
|
33
|
-
import { samePrincipal } from "./auth.js";
|
|
33
|
+
import { SessionAuthError, samePrincipal } from "./auth.js";
|
|
34
34
|
import { mergeCloudOptions, resolveSessionRuntime } from "./cloud-merge.js";
|
|
35
35
|
import { applyCloudUsageToEvents, cloudSdkAgentIdFromEvents, isUnpricedCompletedTurn, mapSdkTokenUsage, } from "./cloud-turn-cost.js";
|
|
36
36
|
import { isDevMode } from "./dev-mode.js";
|
|
@@ -40,6 +40,7 @@ import { TurnEventMapper } from "./event-mapper.js";
|
|
|
40
40
|
import { selectFrameworkStorageDefinition } from "./framework-storage-selection.js";
|
|
41
41
|
import { createHostFilesApi } from "./host-files.js";
|
|
42
42
|
import { createHostKvApi } from "./host-kv.js";
|
|
43
|
+
import { createHostOtelApi } from "./host-otel.js";
|
|
43
44
|
import { createHostPlatforms, createHostSlackClient, } from "./host-platforms.js";
|
|
44
45
|
import { formatContinuationToken, newContinuationKey, newSessionId, newToolCallId, } from "./ids.js";
|
|
45
46
|
import { validateToolInput } from "./json-schema.js";
|
|
@@ -61,12 +62,9 @@ export class SessionBusyError extends Error {
|
|
|
61
62
|
this.name = "SessionBusyError";
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
this.name = "SessionAuthError";
|
|
68
|
-
}
|
|
69
|
-
}
|
|
65
|
+
// Defined in auth.js (the ledger pump needs it without importing this
|
|
66
|
+
// module); re-exported here because this is its historical public home.
|
|
67
|
+
export { SessionAuthError } from "./auth.js";
|
|
70
68
|
export class UnknownChannelError extends Error {
|
|
71
69
|
constructor(channelId) {
|
|
72
70
|
super(`Unknown channel "${channelId}"`);
|
|
@@ -112,7 +110,8 @@ export class ApprovalInterruptedError extends Error {
|
|
|
112
110
|
}
|
|
113
111
|
export class SessionEngine {
|
|
114
112
|
constructor(options) {
|
|
115
|
-
var _a
|
|
113
|
+
var _a;
|
|
114
|
+
var _b, _c, _d, _e;
|
|
116
115
|
/** In-flight lazy restores, deduped per channel + continuation key. */
|
|
117
116
|
this.restoreInFlight = new Map();
|
|
118
117
|
this.channelsById = new Map();
|
|
@@ -149,10 +148,15 @@ export class SessionEngine {
|
|
|
149
148
|
this.project = options.project;
|
|
150
149
|
this.stateRoot = options.stateRoot;
|
|
151
150
|
this.runner = options.runner;
|
|
152
|
-
this.logger = (
|
|
153
|
-
this.getCloudUsage = (
|
|
154
|
-
this.estimateTurnCost = (
|
|
151
|
+
this.logger = (_b = options.logger) !== null && _b !== void 0 ? _b : (() => { });
|
|
152
|
+
this.getCloudUsage = (_c = options.getCloudUsage) !== null && _c !== void 0 ? _c : defaultGetCloudUsage;
|
|
153
|
+
this.estimateTurnCost = (_d = options.estimateTurnCost) !== null && _d !== void 0 ? _d : estimateTurnCostFromApi;
|
|
155
154
|
this.otel = options.otel;
|
|
155
|
+
this.otelHost = createHostOtelApi({
|
|
156
|
+
agentName: options.project.name,
|
|
157
|
+
meter: options.otelMeter,
|
|
158
|
+
collector: options.otel,
|
|
159
|
+
});
|
|
156
160
|
this.turnGovernor = new TurnGovernor(options.project.agent.concurrency);
|
|
157
161
|
this.sessions = new SessionStore(join(options.stateRoot, "sessions"));
|
|
158
162
|
this.nudgeStore = new SlackNudgeStore(join(options.stateRoot, "slack-nudges"));
|
|
@@ -190,7 +194,7 @@ export class SessionEngine {
|
|
|
190
194
|
// resolves them; the host-side MCP registry starts with the concrete
|
|
191
195
|
// connections and gains the rest via setResolvedConnections.
|
|
192
196
|
this.mcpHost = new McpHost(options.project.agent.connections.filter((connection) => !isSymbolicConnectionTransport(connection.transport)));
|
|
193
|
-
const basePlatforms = (
|
|
197
|
+
const basePlatforms = (_e = options.platforms) !== null && _e !== void 0 ? _e : createHostPlatforms();
|
|
194
198
|
const slackBinding = resolveDedicatedSlackHostBinding(options.project.channels);
|
|
195
199
|
this.platforms =
|
|
196
200
|
slackBinding === undefined
|
|
@@ -206,20 +210,37 @@ export class SessionEngine {
|
|
|
206
210
|
this.channelsById.set(channel.id, channel.definition);
|
|
207
211
|
}
|
|
208
212
|
this.ledgerServing = this.buildLedgerServing(options);
|
|
213
|
+
(_a = this.ledgerServing) === null || _a === void 0 ? void 0 : _a.start();
|
|
209
214
|
}
|
|
210
215
|
/**
|
|
211
216
|
* Ledger-mode wiring (`serving: "ledger"`): resolve a CAS-capable
|
|
212
|
-
* durable-session store and build the
|
|
217
|
+
* durable-session store and build the event pump. Resident agents
|
|
213
218
|
* (the default) return undefined and never touch ledger keys.
|
|
214
219
|
*/
|
|
215
220
|
buildLedgerServing(options) {
|
|
216
221
|
if (options.project.agent.serving !== "ledger") {
|
|
217
222
|
return undefined;
|
|
218
223
|
}
|
|
224
|
+
// The handle is already agent-scoped (resolveDurableSessionStore):
|
|
225
|
+
// two ledger-mode agents sharing one backend never resolve to each
|
|
226
|
+
// other's sessions or janitor-claim each other's parked envelopes.
|
|
219
227
|
const ledgerKv = this.resolveDurableSessionStore(options);
|
|
220
228
|
return new LedgerServing({
|
|
221
229
|
ledger: new SessionLedger({ kv: ledgerKv, podId: newLedgerPodId() }),
|
|
222
|
-
deliverTurn: (
|
|
230
|
+
deliverTurn: (envelope) => this.send(envelope.channelId, envelope.message, Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ continuationToken: envelope.continuationToken, auth: envelope.auth }, (envelope.title === undefined ? {} : { title: envelope.title })), (envelope.state === undefined ? {} : { state: envelope.state })), (envelope.mode === undefined ? {} : { mode: envelope.mode })), (envelope.purpose === undefined
|
|
231
|
+
? {}
|
|
232
|
+
: { purpose: envelope.purpose })), (envelope.workspaceDir === undefined
|
|
233
|
+
? {}
|
|
234
|
+
: { workspaceDir: envelope.workspaceDir })), (envelope.cloud === undefined ? {} : { cloud: envelope.cloud })), { fromLedgerPump: true })),
|
|
235
|
+
findSession: (channelId, continuationToken) => __awaiter(this, void 0, void 0, function* () {
|
|
236
|
+
let record = yield this.sessions.findByContinuation(channelId, continuationToken);
|
|
237
|
+
if (record === undefined) {
|
|
238
|
+
record = yield this.restoreSessionByContinuation(channelId, continuationToken);
|
|
239
|
+
}
|
|
240
|
+
return record === undefined
|
|
241
|
+
? undefined
|
|
242
|
+
: this.channelSession(record.sessionId, record, false);
|
|
243
|
+
}),
|
|
223
244
|
logger: this.logger,
|
|
224
245
|
});
|
|
225
246
|
}
|
|
@@ -236,20 +257,21 @@ export class SessionEngine {
|
|
|
236
257
|
*
|
|
237
258
|
* An authored backend without `cas` fails fast: silently falling back
|
|
238
259
|
* to local files would fork coordination away from the deployment's
|
|
239
|
-
* durable store. The check demands
|
|
240
|
-
*
|
|
260
|
+
* durable store. The check demands the full surface the inbox uses —
|
|
261
|
+
* the CAS trio plus `get`/`put`/`delete` for mailbox payload blobs and
|
|
262
|
+
* `listKeys` for the janitor scan and the orphan sweep.
|
|
241
263
|
*
|
|
242
264
|
* Whatever store wins, the returned handle is scoped here — once —
|
|
243
265
|
* under `agentkit/v1/<agent>/durable-session/`. The ledger speaks bare
|
|
244
|
-
* `session/…` / `ledger/…` keys; namespacing is this
|
|
245
|
-
* exactly like `host.kv`'s agent scoping.
|
|
266
|
+
* `session/…` / `ledger/…` / `mailbox/…` keys; namespacing is this
|
|
267
|
+
* boundary's job, exactly like `host.kv`'s agent scoping.
|
|
246
268
|
*/
|
|
247
269
|
resolveDurableSessionStore(options) {
|
|
248
270
|
const namespace = ledgerKeyPrefix(options.project.name);
|
|
249
271
|
const explicit = options.durableSessionStore;
|
|
250
272
|
if (explicit !== undefined) {
|
|
251
273
|
if (!supportsSessionLedgerKv(explicit)) {
|
|
252
|
-
throw new Error(`Agent "${options.project.name}" declares serving: "ledger", but the configured durableSessionStore is not CAS-capable (getWithVersion / putIfAbsent / putIfVersion). Provide a store implementing
|
|
274
|
+
throw new Error(`Agent "${options.project.name}" declares serving: "ledger", but the configured durableSessionStore is not CAS-capable (getWithVersion / putIfAbsent / putIfVersion / listKeys). Provide a store implementing HostKvCasApi (see @cursor/july/kv).`);
|
|
253
275
|
}
|
|
254
276
|
return namespacedKv(explicit, namespace);
|
|
255
277
|
}
|
|
@@ -287,7 +309,7 @@ export class SessionEngine {
|
|
|
287
309
|
setCloudServerToolsConnection(connection) {
|
|
288
310
|
this.cloudServerToolsConnection = connection;
|
|
289
311
|
}
|
|
290
|
-
/** Shared host services (MCP / GitHub / Slack / reminders / evals / kv / Slack nudges). */
|
|
312
|
+
/** Shared host services (MCP / GitHub / Slack / reminders / evals / kv / files / otel / Slack nudges). */
|
|
291
313
|
get host() {
|
|
292
314
|
return {
|
|
293
315
|
mcp: this.mcpHost,
|
|
@@ -297,6 +319,7 @@ export class SessionEngine {
|
|
|
297
319
|
evals: this.evalsApi,
|
|
298
320
|
kv: this.kvApi,
|
|
299
321
|
files: this.filesApi,
|
|
322
|
+
otel: this.otelHost,
|
|
300
323
|
slackNudges: this.slackNudgeHostApi(),
|
|
301
324
|
};
|
|
302
325
|
}
|
|
@@ -310,7 +333,7 @@ export class SessionEngine {
|
|
|
310
333
|
const host = this.host;
|
|
311
334
|
return sessionId === undefined
|
|
312
335
|
? host
|
|
313
|
-
: Object.assign(Object.assign({}, host), { files: this.filesApi.forSession(sessionId) });
|
|
336
|
+
: Object.assign(Object.assign({}, host), { files: this.filesApi.forSession(sessionId), otel: this.otelHost.forSession(sessionId) });
|
|
314
337
|
}
|
|
315
338
|
/** Unbound `ctx.artifacts` facade (channel handlers, scratch tool calls). */
|
|
316
339
|
get artifacts() {
|
|
@@ -404,10 +427,10 @@ export class SessionEngine {
|
|
|
404
427
|
return __awaiter(this, arguments, void 0, function* (channelId, message, options = {}) {
|
|
405
428
|
var _a;
|
|
406
429
|
var _b, _c, _d, _e, _f;
|
|
407
|
-
// Ledger-mode agents
|
|
408
|
-
//
|
|
409
|
-
// resident path.
|
|
410
|
-
if (this.ledgerServing !== undefined && options.
|
|
430
|
+
// Ledger-mode agents park the event durably and coordinate through
|
|
431
|
+
// the session lease before any turn runs; only the pump's own
|
|
432
|
+
// lease-holding deliveries fall through to the resident path.
|
|
433
|
+
if (this.ledgerServing !== undefined && options.fromLedgerPump !== true) {
|
|
411
434
|
return this.ledgerServing.deliver(channelId, message, options);
|
|
412
435
|
}
|
|
413
436
|
const auth = (_b = options.auth) !== null && _b !== void 0 ? _b : null;
|
|
@@ -448,10 +471,11 @@ export class SessionEngine {
|
|
|
448
471
|
if (options.refreshState !== undefined) {
|
|
449
472
|
record = yield this.updateRecord(record.sessionId, (current) => (Object.assign(Object.assign({}, current), { channelState: mergeChannelStateSkipNull(current.channelState, options.refreshState) })));
|
|
450
473
|
}
|
|
474
|
+
const since = (yield this.logs.get(record.sessionId)).count;
|
|
451
475
|
const coalesced = yield this.startTurn(record.sessionId, message, Object.assign({ admission: (_c = options.admission) !== null && _c !== void 0 ? _c : "preempt" }, (options.coalesceSourceTs === undefined
|
|
452
476
|
? {}
|
|
453
477
|
: { sourceTs: options.coalesceSourceTs })));
|
|
454
|
-
return this.channelSession(record.sessionId, record, false, coalesced);
|
|
478
|
+
return this.channelSession(record.sessionId, record, false, coalesced, since);
|
|
455
479
|
}
|
|
456
480
|
const now = new Date().toISOString();
|
|
457
481
|
const channelState = this.initialChannelState(channelId, options.state);
|
|
@@ -476,15 +500,23 @@ export class SessionEngine {
|
|
|
476
500
|
};
|
|
477
501
|
yield this.sessions.save(record);
|
|
478
502
|
(_a = this.storage) === null || _a === void 0 ? void 0 : _a.sessionRecord(record);
|
|
503
|
+
// Ledger serving: another pod can follow up mid-turn, so the session
|
|
504
|
+
// identity (record + continuation index) must hit the shared store
|
|
505
|
+
// before we return a session id. Default turnEnd flush is too late.
|
|
506
|
+
if (this.ledgerServing !== undefined && this.storage !== undefined) {
|
|
507
|
+
this.storage.flushSession(record.sessionId);
|
|
508
|
+
yield this.storage.whenIdle();
|
|
509
|
+
}
|
|
479
510
|
yield this.appendEvent(record.sessionId, {
|
|
480
511
|
type: "session.started",
|
|
481
512
|
data: { channelId },
|
|
482
513
|
});
|
|
483
514
|
record = yield this.enrollABs(record);
|
|
515
|
+
const since = (yield this.logs.get(record.sessionId)).count;
|
|
484
516
|
yield this.startTurn(record.sessionId, message, Object.assign({ admission: (_f = options.admission) !== null && _f !== void 0 ? _f : "preempt" }, (options.coalesceSourceTs === undefined
|
|
485
517
|
? {}
|
|
486
518
|
: { sourceTs: options.coalesceSourceTs })));
|
|
487
|
-
return this.channelSession(record.sessionId, record, true, false);
|
|
519
|
+
return this.channelSession(record.sessionId, record, true, false, since);
|
|
488
520
|
});
|
|
489
521
|
}
|
|
490
522
|
/** Whether a continuation token already maps to a session on this channel. */
|
|
@@ -914,7 +946,9 @@ export class SessionEngine {
|
|
|
914
946
|
}
|
|
915
947
|
const startedAt = Date.now();
|
|
916
948
|
try {
|
|
917
|
-
const outcome = yield this.runner.prewarm(Object.assign({ workspaceDir, runnerStateDir: join(this.stateRoot, "runner"), sandbox: ((_b = agent.local) === null || _b === void 0 ? void 0 : _b.sandbox) === true
|
|
949
|
+
const outcome = yield this.runner.prewarm(Object.assign(Object.assign(Object.assign({ workspaceDir, runnerStateDir: join(this.stateRoot, "runner"), sandbox: ((_b = agent.local) === null || _b === void 0 ? void 0 : _b.sandbox) === true }, (agent.allowedTools === undefined
|
|
950
|
+
? {}
|
|
951
|
+
: { tools: agent.allowedTools })), { mcpServers: this.buildMcpServers("local"), subagents: buildRunnerSubagents(agent) }), (agent.model === undefined ? {} : { model: agent.model })));
|
|
918
952
|
// Report what happened rather than that the call returned: a runner
|
|
919
953
|
// that cannot warm resolves cleanly, and logging success there would
|
|
920
954
|
// tell an operator the workspace is hot when nothing touched it.
|
|
@@ -1107,6 +1141,13 @@ export class SessionEngine {
|
|
|
1107
1141
|
agentRuntime: agent.runtime,
|
|
1108
1142
|
cloudOverride: record.cloudOverride,
|
|
1109
1143
|
});
|
|
1144
|
+
// Fail closed: the SDK cannot enforce the built-in tool allowlist on
|
|
1145
|
+
// cloud agents yet, and a restriction that silently does not apply is
|
|
1146
|
+
// worse than a refused turn. Cloud-runtime agents already fail
|
|
1147
|
+
// discovery; this guards hybrid agents' per-send cloud sessions.
|
|
1148
|
+
if (runtime === "cloud" && agent.allowedTools !== undefined) {
|
|
1149
|
+
throw new Error(`Agent "${agent.name}" declares a built-in tool allowlist (tools), which cannot be enforced on cloud turns yet. Refusing the cloud session rather than running it unrestricted.`);
|
|
1150
|
+
}
|
|
1110
1151
|
const mergedCloud = runtime === "cloud"
|
|
1111
1152
|
? mergeCloudOptions({
|
|
1112
1153
|
base: agent.cloud,
|
|
@@ -1153,9 +1194,11 @@ export class SessionEngine {
|
|
|
1153
1194
|
turnPrompt = `${preamble}\n\n${turnPrompt}`;
|
|
1154
1195
|
}
|
|
1155
1196
|
}
|
|
1156
|
-
return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ sessionId: record.sessionId }, (record.sdkAgentId === undefined
|
|
1197
|
+
return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ sessionId: record.sessionId }, (record.sdkAgentId === undefined
|
|
1157
1198
|
? {}
|
|
1158
|
-
: { sdkAgentId: record.sdkAgentId })), { workspaceDir: this.harnessCwd(record), sandbox: ((_c = this.project.agent.local) === null || _c === void 0 ? void 0 : _c.sandbox) === true
|
|
1199
|
+
: { sdkAgentId: record.sdkAgentId })), { workspaceDir: this.harnessCwd(record), sandbox: ((_c = this.project.agent.local) === null || _c === void 0 ? void 0 : _c.sandbox) === true }), (runtime === "local" && agent.allowedTools !== undefined
|
|
1200
|
+
? { tools: agent.allowedTools }
|
|
1201
|
+
: {})), { runnerStateDir: join(this.stateRoot, "runner"), prompt: turnPrompt, isFirstTurn }), (images !== undefined && images.length > 0 ? { images } : {})), (agent.model === undefined ? {} : { model: agent.model })), { runtime }), (mergedCloud === undefined ? {} : { cloud: mergedCloud })), { customTools: runtime === "local" ? this.buildCustomTools(record, turnId, emit) : {}, mcpServers: this.buildMcpServers(runtime, record.sessionId), subagents: buildRunnerSubagents(agent), onUpdate,
|
|
1159
1202
|
signal,
|
|
1160
1203
|
// Local follow-ups expire any lingering active run so a preempted turn
|
|
1161
1204
|
// (or one orphaned by a crashed process) never blocks this send.
|
|
@@ -1398,6 +1441,7 @@ export class SessionEngine {
|
|
|
1398
1441
|
}),
|
|
1399
1442
|
send: (channelId, message, sendOptions = {}) => { var _a; return this.send(channelId, message, Object.assign(Object.assign({}, sendOptions), { auth: (_a = sendOptions.auth) !== null && _a !== void 0 ? _a : args.record.auth })); },
|
|
1400
1443
|
getSession: (channelId, sessionId) => this.getChannelSession(channelId, sessionId),
|
|
1444
|
+
emit: args.emit,
|
|
1401
1445
|
};
|
|
1402
1446
|
try {
|
|
1403
1447
|
const needsGate = yield evaluateNeedsApproval(args.tool.definition.needsApproval, validation.value);
|
|
@@ -1622,6 +1666,12 @@ export class SessionEngine {
|
|
|
1622
1666
|
this.logger(`[session] tool ${toolName} start (direct ${callId})`);
|
|
1623
1667
|
let result;
|
|
1624
1668
|
let isError;
|
|
1669
|
+
const live = record;
|
|
1670
|
+
const emit = live === undefined
|
|
1671
|
+
? undefined
|
|
1672
|
+
: (payload) => {
|
|
1673
|
+
void this.appendEvent(live.sessionId, Object.assign(Object.assign({}, payload), { turnId: callId })).catch(() => { });
|
|
1674
|
+
};
|
|
1625
1675
|
try {
|
|
1626
1676
|
result = normalizeToolResult(yield execute(validation.value, {
|
|
1627
1677
|
toolCallId: callId,
|
|
@@ -1640,6 +1690,7 @@ export class SessionEngine {
|
|
|
1640
1690
|
? sendOptions.auth
|
|
1641
1691
|
: session.auth })),
|
|
1642
1692
|
getSession: (channelId, sessionId) => this.getChannelSession(channelId, sessionId),
|
|
1693
|
+
emit,
|
|
1643
1694
|
}));
|
|
1644
1695
|
isError = isErrorEnvelope(result);
|
|
1645
1696
|
}
|
|
@@ -2216,8 +2267,8 @@ export class SessionEngine {
|
|
|
2216
2267
|
? {}
|
|
2217
2268
|
: { sdkAgentId: record.sdkAgentId }));
|
|
2218
2269
|
}
|
|
2219
|
-
channelSession(sessionId, record, isNew, coalesced = false) {
|
|
2220
|
-
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 :
|
|
2270
|
+
channelSession(sessionId, record, isNew, coalesced = false, since = 0) {
|
|
2271
|
+
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 : since); }, waitForCompletion: () => this.waitForCompletion(sessionId), setContinuationToken: (token) => this.setContinuationToken(sessionId, token) });
|
|
2221
2272
|
}
|
|
2222
2273
|
resolveChannelId(channel) {
|
|
2223
2274
|
if (typeof channel === "string") {
|
package/dist/kv/memory-kv.d.ts
CHANGED
|
@@ -18,6 +18,14 @@ export declare function createMemoryKv(): HostKvCasApi;
|
|
|
18
18
|
* fixtures, an off-hosting no-op sink). Non-durable and process-local by
|
|
19
19
|
* construction; a real deployment backend should implement CAS against
|
|
20
20
|
* its own store instead.
|
|
21
|
+
*
|
|
22
|
+
* **Never pair this with `serving: "ledger"` on a real backend.** The
|
|
23
|
+
* ledger derives its coordination store from the whole storage config,
|
|
24
|
+
* so a durable plain backend with a memory `cas` group silently voids
|
|
25
|
+
* ack-after-durable: leases, heads, and pending indexes live only in
|
|
26
|
+
* this process and vanish on restart while payload blobs go through the
|
|
27
|
+
* plain methods. Ledger state must live in the same durable store as
|
|
28
|
+
* the blobs.
|
|
21
29
|
*/
|
|
22
30
|
export declare function memoryCasTable(): StorageCasTable;
|
|
23
31
|
//# sourceMappingURL=memory-kv.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-kv.d.ts","sourceRoot":"","sources":["../../src/kv/memory-kv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EACV,YAAY,EAIb,MAAM,aAAa,CAAC;AAErB,gDAAgD;AAChD,wBAAgB,cAAc,IAAI,YAAY,CA4D7C;AAED
|
|
1
|
+
{"version":3,"file":"memory-kv.d.ts","sourceRoot":"","sources":["../../src/kv/memory-kv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EACV,YAAY,EAIb,MAAM,aAAa,CAAC;AAErB,gDAAgD;AAChD,wBAAgB,cAAc,IAAI,YAAY,CA4D7C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,IAAI,eAAe,CAShD"}
|
package/dist/kv/memory-kv.js
CHANGED
|
@@ -91,6 +91,14 @@ export function createMemoryKv() {
|
|
|
91
91
|
* fixtures, an off-hosting no-op sink). Non-durable and process-local by
|
|
92
92
|
* construction; a real deployment backend should implement CAS against
|
|
93
93
|
* its own store instead.
|
|
94
|
+
*
|
|
95
|
+
* **Never pair this with `serving: "ledger"` on a real backend.** The
|
|
96
|
+
* ledger derives its coordination store from the whole storage config,
|
|
97
|
+
* so a durable plain backend with a memory `cas` group silently voids
|
|
98
|
+
* ack-after-durable: leases, heads, and pending indexes live only in
|
|
99
|
+
* this process and vanish on restart while payload blobs go through the
|
|
100
|
+
* plain methods. Ledger state must live in the same durable store as
|
|
101
|
+
* the blobs.
|
|
94
102
|
*/
|
|
95
103
|
export function memoryCasTable() {
|
|
96
104
|
const kv = createMemoryKv();
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ledger janitor: lazy crash recovery has a hole — a session with
|
|
3
|
+
* pending events and no future events would wait forever — so serving runs
|
|
4
|
+
* this loop. It scans `ledger/` by prefix, finds pending-with-expired-lease
|
|
5
|
+
* sessions, claims them, and hands each claim to the execution callback.
|
|
6
|
+
* Self-host runs the same loop; it is part of the library, not the control
|
|
7
|
+
* plane.
|
|
8
|
+
*
|
|
9
|
+
* Nothing starts on construction: call `start()` for the interval loop or
|
|
10
|
+
* `runOnce()` for an explicit pass (what unit tests use).
|
|
11
|
+
*/
|
|
12
|
+
import type { LedgerLease, SessionLedger } from "./session-ledger.js";
|
|
13
|
+
export declare const DEFAULT_JANITOR_INTERVAL_MS = 15000;
|
|
14
|
+
export interface JanitorClaimedSession {
|
|
15
|
+
sessionId: string;
|
|
16
|
+
lease: LedgerLease;
|
|
17
|
+
head: number;
|
|
18
|
+
pending: string[];
|
|
19
|
+
}
|
|
20
|
+
export interface JanitorRunResult {
|
|
21
|
+
/** Ledgers scanned this pass. */
|
|
22
|
+
scanned: number;
|
|
23
|
+
/** Sessions this pass claimed and handed to the callback. */
|
|
24
|
+
claimed: JanitorClaimedSession[];
|
|
25
|
+
/** Orphan blob ids deleted (only when the sweep is enabled). */
|
|
26
|
+
orphansDeleted: string[];
|
|
27
|
+
}
|
|
28
|
+
export interface LedgerJanitorOptions {
|
|
29
|
+
ledger: SessionLedger;
|
|
30
|
+
/**
|
|
31
|
+
* Executes a recovered session: drain, run the turn, commit. Awaited so
|
|
32
|
+
* one pass never claims more sessions than it can serve.
|
|
33
|
+
*/
|
|
34
|
+
onSession: (session: JanitorClaimedSession) => Promise<void> | void;
|
|
35
|
+
/** Scan cadence for `start()` (default {@link DEFAULT_JANITOR_INTERVAL_MS}). */
|
|
36
|
+
intervalMs?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Also sweep unreferenced mailbox blobs each pass. Off by default —
|
|
39
|
+
* enable with `{ olderThanMs }` (see `SessionLedger.sweepOrphans`).
|
|
40
|
+
*/
|
|
41
|
+
orphanSweep?: {
|
|
42
|
+
olderThanMs?: number;
|
|
43
|
+
};
|
|
44
|
+
/** Called with per-session or sweep failures; the pass continues. */
|
|
45
|
+
onError?: (error: unknown, sessionId?: string) => void;
|
|
46
|
+
/** Time source for lease-expiry checks (default `Date.now`). */
|
|
47
|
+
clock?: () => number;
|
|
48
|
+
}
|
|
49
|
+
export interface LedgerJanitor {
|
|
50
|
+
start(): void;
|
|
51
|
+
/**
|
|
52
|
+
* Clear the interval AND await any in-flight pass: a running pass can
|
|
53
|
+
* claim a session and spawn a turn, so shutdown must not proceed while
|
|
54
|
+
* one is mid-scan.
|
|
55
|
+
*/
|
|
56
|
+
stop(): Promise<void>;
|
|
57
|
+
runOnce(): Promise<JanitorRunResult>;
|
|
58
|
+
}
|
|
59
|
+
export declare function createLedgerJanitor(options: LedgerJanitorOptions): LedgerJanitor;
|
|
60
|
+
//# sourceMappingURL=janitor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"janitor.d.ts","sourceRoot":"","sources":["../../src/ledger/janitor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAEV,WAAW,EACX,aAAa,EACd,MAAM,qBAAqB,CAAC;AAE7B,eAAO,MAAM,2BAA2B,QAAS,CAAC;AAElD,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,WAAW,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,iCAAiC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACjC,gEAAgE;IAChE,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,aAAa,CAAC;IACtB;;;OAGG;IACH,SAAS,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACpE,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,qEAAqE;IACrE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,IAAI,IAAI,CAAC;IACd;;;;OAIG;IACH,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,OAAO,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACtC;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,oBAAoB,GAC5B,aAAa,CA2Hf"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ledger janitor: lazy crash recovery has a hole — a session with
|
|
3
|
+
* pending events and no future events would wait forever — so serving runs
|
|
4
|
+
* this loop. It scans `ledger/` by prefix, finds pending-with-expired-lease
|
|
5
|
+
* sessions, claims them, and hands each claim to the execution callback.
|
|
6
|
+
* Self-host runs the same loop; it is part of the library, not the control
|
|
7
|
+
* plane.
|
|
8
|
+
*
|
|
9
|
+
* Nothing starts on construction: call `start()` for the interval loop or
|
|
10
|
+
* `runOnce()` for an explicit pass (what unit tests use).
|
|
11
|
+
*/
|
|
12
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
13
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
14
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
15
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
16
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
17
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
18
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
export const DEFAULT_JANITOR_INTERVAL_MS = 15000;
|
|
22
|
+
export function createLedgerJanitor(options) {
|
|
23
|
+
var _a, _b, _c;
|
|
24
|
+
const intervalMs = (_a = options.intervalMs) !== null && _a !== void 0 ? _a : DEFAULT_JANITOR_INTERVAL_MS;
|
|
25
|
+
const clock = (_b = options.clock) !== null && _b !== void 0 ? _b : Date.now;
|
|
26
|
+
const reportError = (_c = options.onError) !== null && _c !== void 0 ? _c : (() => {
|
|
27
|
+
// Recovery is best-effort per pass; an unobserved failure is retried
|
|
28
|
+
// on the next scan.
|
|
29
|
+
});
|
|
30
|
+
let timer;
|
|
31
|
+
let running = false;
|
|
32
|
+
/** The settled-shape promise of the pass currently scanning, if any. */
|
|
33
|
+
let inFlight;
|
|
34
|
+
const runPass = () => __awaiter(this, void 0, void 0, function* () {
|
|
35
|
+
// An unparseable ledger record is reported and skipped: it degrades
|
|
36
|
+
// that one session's recovery, never the whole agent's pass.
|
|
37
|
+
const ledgers = yield options.ledger.listLedgers((key, error) => {
|
|
38
|
+
reportError(new Error(`janitor scan: skipping unparseable ledger record at ${key}: ${String(error instanceof Error ? error.message : error)}`));
|
|
39
|
+
});
|
|
40
|
+
const claimed = [];
|
|
41
|
+
for (const snapshot of ledgers) {
|
|
42
|
+
if (snapshot.pending.length === 0) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (snapshot.lease !== null && snapshot.lease.expiresAt > clock()) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
let claim;
|
|
49
|
+
try {
|
|
50
|
+
claim = yield options.ledger.claim(snapshot.sessionId);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
reportError(error, snapshot.sessionId);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (!claim.claimed) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const session = {
|
|
60
|
+
sessionId: snapshot.sessionId,
|
|
61
|
+
lease: claim.lease,
|
|
62
|
+
head: claim.head,
|
|
63
|
+
pending: claim.pending,
|
|
64
|
+
};
|
|
65
|
+
claimed.push(session);
|
|
66
|
+
try {
|
|
67
|
+
yield options.onSession(session);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
reportError(error, snapshot.sessionId);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
let orphansDeleted = [];
|
|
74
|
+
if (options.orphanSweep !== undefined) {
|
|
75
|
+
try {
|
|
76
|
+
const swept = yield options.ledger.sweepOrphans({
|
|
77
|
+
olderThanMs: options.orphanSweep.olderThanMs,
|
|
78
|
+
});
|
|
79
|
+
orphansDeleted = swept.deleted;
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
reportError(error);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return { scanned: ledgers.length, claimed, orphansDeleted };
|
|
86
|
+
});
|
|
87
|
+
const runOnce = () => {
|
|
88
|
+
const pass = runPass();
|
|
89
|
+
const settled = pass.then(() => undefined, () => undefined);
|
|
90
|
+
inFlight = settled;
|
|
91
|
+
void settled.finally(() => {
|
|
92
|
+
if (inFlight === settled) {
|
|
93
|
+
inFlight = undefined;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
return pass;
|
|
97
|
+
};
|
|
98
|
+
const tick = () => __awaiter(this, void 0, void 0, function* () {
|
|
99
|
+
// Skip a tick rather than overlap passes when a scan outlasts the
|
|
100
|
+
// cadence — overlapping claims would just fence each other.
|
|
101
|
+
if (running) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
running = true;
|
|
105
|
+
try {
|
|
106
|
+
yield runOnce();
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
reportError(error);
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
running = false;
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
return {
|
|
116
|
+
start() {
|
|
117
|
+
var _a;
|
|
118
|
+
if (timer !== undefined) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
timer = setInterval(() => {
|
|
122
|
+
void tick();
|
|
123
|
+
}, intervalMs);
|
|
124
|
+
(_a = timer.unref) === null || _a === void 0 ? void 0 : _a.call(timer);
|
|
125
|
+
},
|
|
126
|
+
stop() {
|
|
127
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
128
|
+
if (timer !== undefined) {
|
|
129
|
+
clearInterval(timer);
|
|
130
|
+
timer = undefined;
|
|
131
|
+
}
|
|
132
|
+
yield inFlight;
|
|
133
|
+
});
|
|
134
|
+
},
|
|
135
|
+
runOnce,
|
|
136
|
+
};
|
|
137
|
+
}
|