@orkestrel/agent 0.0.6 → 0.0.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/README.md +1 -1
- package/dist/src/core/index.cjs +163 -21
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +176 -21
- package/dist/src/core/index.d.ts +176 -21
- package/dist/src/core/index.js +159 -22
- package/dist/src/core/index.js.map +1 -1
- package/package.json +11 -10
package/README.md
CHANGED
package/dist/src/core/index.cjs
CHANGED
|
@@ -74,6 +74,24 @@ var THINK_CLOSE = "</think>";
|
|
|
74
74
|
*/
|
|
75
75
|
var WORKSPACE_SECTION_HEADER = "## Workspace";
|
|
76
76
|
/**
|
|
77
|
+
* The estimated per-message role/framing overhead {@link import('./helpers.js').estimateMessages}
|
|
78
|
+
* adds on top of a message's content estimate — accounts for the fixed wire framing every
|
|
79
|
+
* conversation turn carries (its role tag, delimiters) that {@link import('./helpers.js').estimateTokens}'s
|
|
80
|
+
* content-only heuristic does not otherwise capture.
|
|
81
|
+
*/
|
|
82
|
+
var MESSAGE_TOKEN_OVERHEAD = 4;
|
|
83
|
+
/**
|
|
84
|
+
* The coarse, deliberately-approximate per-image token cost {@link import('./helpers.js').estimateMessages}
|
|
85
|
+
* charges for each attached image.
|
|
86
|
+
*
|
|
87
|
+
* @remarks
|
|
88
|
+
* A base64 image payload's LENGTH is NOT a reliable token proxy (a vision model's actual image
|
|
89
|
+
* token cost depends on resolution / tiling, not byte size), so this is a fixed, coarse
|
|
90
|
+
* per-image estimate rather than a derivation from `image.length` — a planning heuristic, not an
|
|
91
|
+
* exact count.
|
|
92
|
+
*/
|
|
93
|
+
var IMAGE_TOKEN_ESTIMATE = 512;
|
|
94
|
+
/**
|
|
77
95
|
* The extension→language table {@link import('./helpers.js').inferLanguage} reads to
|
|
78
96
|
* map a file path's extension to a fenced-code language tag. Covers the common source /
|
|
79
97
|
* markup / data extensions; an unlisted extension falls back to `'text'`.
|
|
@@ -202,18 +220,21 @@ function isAgentJobError(value) {
|
|
|
202
220
|
}
|
|
203
221
|
/**
|
|
204
222
|
* Thrown by a {@link ConversationInterface}'s `compact()` when the conversation has no
|
|
205
|
-
* {@link ConversationSummarizer} to fold its messages with
|
|
206
|
-
* `code`.
|
|
223
|
+
* {@link ConversationSummarizer} to fold its messages with, or when its `sections` cap is
|
|
224
|
+
* structurally invalid — carries a machine-readable `code`.
|
|
207
225
|
*
|
|
208
226
|
* @remarks
|
|
209
227
|
* Compaction REQUIRES a summarizer (it digests the folded slice into a section summary and
|
|
210
228
|
* regenerates the rollup); a conversation created without one can still store + `view()` its
|
|
211
|
-
* live tail, but a `compact()` is a programmer error (§12) and throws this
|
|
212
|
-
* `
|
|
229
|
+
* live tail, but a `compact()` is a programmer error (§12) and throws this with `'SUMMARIZER'`.
|
|
230
|
+
* A `sections` cap (§F2, on {@link import('./types.js').ConversationOptions} /
|
|
231
|
+
* {@link import('./types.js').ConversationManagerOptions} /
|
|
232
|
+
* {@link import('./types.js').CompactOptions}) must be `>= 1` — a sub-1 cap is a programmer
|
|
233
|
+
* error and throws this with `'SECTIONS'`. Narrow a caught value with
|
|
213
234
|
* {@link isConversationError} and branch on `error.code`.
|
|
214
235
|
*/
|
|
215
236
|
var ConversationError = class extends Error {
|
|
216
|
-
/** The machine-readable condition — `'SUMMARIZER'`: a `compact()` with no summarizer. */
|
|
237
|
+
/** The machine-readable condition — `'SUMMARIZER'`: a `compact()` with no summarizer; `'SECTIONS'`: a sub-1 `sections` cap. */
|
|
217
238
|
code;
|
|
218
239
|
constructor(code, message) {
|
|
219
240
|
super(message);
|
|
@@ -278,6 +299,45 @@ var WorkspaceError = class extends Error {
|
|
|
278
299
|
function isWorkspaceError(value) {
|
|
279
300
|
return value instanceof WorkspaceError;
|
|
280
301
|
}
|
|
302
|
+
/**
|
|
303
|
+
* Thrown synchronously by an {@link AgentInterface}'s `stream()` when a concurrent run would
|
|
304
|
+
* corrupt SHARED per-agent accounting — carries a machine-readable `code`.
|
|
305
|
+
*
|
|
306
|
+
* @remarks
|
|
307
|
+
* A run already in flight on the same agent, PLUS a construction-level `window` (a shared
|
|
308
|
+
* context budget) or a construction-level `budget` with no per-run override (a shared cost
|
|
309
|
+
* budget), means a second concurrent `stream()` would race its charges against the same
|
|
310
|
+
* shared instance — corrupting the accounting. `code` is `'CONCURRENCY'` (the only condition
|
|
311
|
+
* so far). Use separate agents, or per-run `budget` overrides with no `window`, for genuinely
|
|
312
|
+
* concurrent runs. Narrow a caught value with {@link isAgentError} and branch on `error.code`.
|
|
313
|
+
*/
|
|
314
|
+
var AgentError = class extends Error {
|
|
315
|
+
/** The machine-readable condition — `'CONCURRENCY'`: a concurrent run on a shared accounting agent. */
|
|
316
|
+
code;
|
|
317
|
+
constructor(code, message) {
|
|
318
|
+
super(message);
|
|
319
|
+
this.name = "AgentError";
|
|
320
|
+
this.code = code;
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
/**
|
|
324
|
+
* Narrow an unknown caught value to an {@link AgentError}.
|
|
325
|
+
*
|
|
326
|
+
* @param value - The value to test (typically a `catch` binding)
|
|
327
|
+
* @returns `true` when `value` is an {@link AgentError}
|
|
328
|
+
*
|
|
329
|
+
* @example
|
|
330
|
+
* ```ts
|
|
331
|
+
* try {
|
|
332
|
+
* agent.stream()
|
|
333
|
+
* } catch (error) {
|
|
334
|
+
* if (isAgentError(error) && error.code === 'CONCURRENCY') useSeparateAgents()
|
|
335
|
+
* }
|
|
336
|
+
* ```
|
|
337
|
+
*/
|
|
338
|
+
function isAgentError(value) {
|
|
339
|
+
return value instanceof AgentError;
|
|
340
|
+
}
|
|
281
341
|
//#endregion
|
|
282
342
|
//#region src/core/conversations/Conversation.ts
|
|
283
343
|
/**
|
|
@@ -331,6 +391,7 @@ var Conversation = class {
|
|
|
331
391
|
#emitter;
|
|
332
392
|
#summarize;
|
|
333
393
|
#keep;
|
|
394
|
+
#cap;
|
|
334
395
|
#sections = [];
|
|
335
396
|
#summary;
|
|
336
397
|
#messages = /* @__PURE__ */ new Map();
|
|
@@ -342,6 +403,8 @@ var Conversation = class {
|
|
|
342
403
|
});
|
|
343
404
|
this.#summarize = options?.summarize;
|
|
344
405
|
this.#keep = options?.keep ?? 0;
|
|
406
|
+
if (options?.sections !== void 0 && options.sections < 1) throw new ConversationError("SECTIONS", "a sections cap must be >= 1");
|
|
407
|
+
this.#cap = options?.sections;
|
|
345
408
|
if (seed) {
|
|
346
409
|
this.#summary = seed.summary;
|
|
347
410
|
for (const section of seed.sections) this.#sections.push(section);
|
|
@@ -393,6 +456,8 @@ var Conversation = class {
|
|
|
393
456
|
async compact(options) {
|
|
394
457
|
const summarize = this.#summarize;
|
|
395
458
|
if (summarize === void 0) throw new ConversationError("SUMMARIZER", "cannot compact a conversation without a summarizer");
|
|
459
|
+
const cap = options?.sections ?? this.#cap;
|
|
460
|
+
if (cap !== void 0 && cap < 1) throw new ConversationError("SECTIONS", "a sections cap must be >= 1");
|
|
396
461
|
const keep = options?.keep ?? this.#keep;
|
|
397
462
|
const live = [...this.#messages.values()];
|
|
398
463
|
const fold = keep <= 0 ? live.length : live.length - keep;
|
|
@@ -406,6 +471,23 @@ var Conversation = class {
|
|
|
406
471
|
};
|
|
407
472
|
for (const message of slice) this.#messages.delete(message.id);
|
|
408
473
|
this.#sections.push(section);
|
|
474
|
+
if (cap !== void 0 && this.#sections.length > cap) {
|
|
475
|
+
const overflow = this.#sections.length - cap + 1;
|
|
476
|
+
const folded = this.#sections.slice(0, overflow);
|
|
477
|
+
try {
|
|
478
|
+
const merged = {
|
|
479
|
+
id: crypto.randomUUID(),
|
|
480
|
+
summary: await summarize(folded.map((one) => this.#summaryMessage(one))),
|
|
481
|
+
messages: folded.flatMap((one) => one.messages)
|
|
482
|
+
};
|
|
483
|
+
this.#sections.splice(0, overflow, merged);
|
|
484
|
+
this.#emitter.emit("collapse", merged);
|
|
485
|
+
} catch (error) {
|
|
486
|
+
this.#summary = await summarize(this.#sections.map((one) => this.#summaryMessage(one)));
|
|
487
|
+
this.#emitter.emit("summary", this.#summary);
|
|
488
|
+
throw error;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
409
491
|
this.#summary = await summarize(this.#sections.map((one) => this.#summaryMessage(one)));
|
|
410
492
|
this.#emitter.emit("summary", this.#summary);
|
|
411
493
|
this.#emitter.emit("compact", section);
|
|
@@ -505,10 +587,12 @@ var ConversationManager = class {
|
|
|
505
587
|
#active;
|
|
506
588
|
#summarize;
|
|
507
589
|
#keep;
|
|
590
|
+
#sections;
|
|
508
591
|
#store;
|
|
509
592
|
constructor(options) {
|
|
510
593
|
this.#summarize = options?.summarize;
|
|
511
594
|
this.#keep = options?.keep ?? 0;
|
|
595
|
+
this.#sections = options?.sections;
|
|
512
596
|
this.#store = options?.store;
|
|
513
597
|
}
|
|
514
598
|
get count() {
|
|
@@ -524,11 +608,13 @@ var ConversationManager = class {
|
|
|
524
608
|
return [...this.#conversations.values()];
|
|
525
609
|
}
|
|
526
610
|
add(input) {
|
|
611
|
+
const sections = input?.sections ?? this.#sections;
|
|
527
612
|
const conversation = new Conversation({
|
|
528
613
|
...input?.id === void 0 ? {} : { id: input.id },
|
|
529
614
|
...input?.on === void 0 ? {} : { on: input.on },
|
|
530
615
|
summarize: input?.summarize ?? this.#summarize,
|
|
531
|
-
keep: input?.keep ?? this.#keep
|
|
616
|
+
keep: input?.keep ?? this.#keep,
|
|
617
|
+
...sections === void 0 ? {} : { sections }
|
|
532
618
|
}, input?.snapshot);
|
|
533
619
|
this.#conversations.set(conversation.id, conversation);
|
|
534
620
|
if (this.#active === void 0) this.#active = conversation.id;
|
|
@@ -669,23 +755,40 @@ function estimateTokens(text) {
|
|
|
669
755
|
* (the {@link import('./types.js').AgentOptions} `window`).
|
|
670
756
|
*
|
|
671
757
|
* @remarks
|
|
672
|
-
* Sums {@link estimateTokens} over
|
|
673
|
-
* heuristic)
|
|
674
|
-
*
|
|
675
|
-
*
|
|
676
|
-
*
|
|
758
|
+
* Sums, per message, {@link estimateTokens} over its `content` (the `ceil(length / 4)` char
|
|
759
|
+
* heuristic) PLUS {@link import('./constants.js').MESSAGE_TOKEN_OVERHEAD} (a fixed per-message
|
|
760
|
+
* role/framing overhead) PLUS, when present, {@link estimateTokens} over its JSON-stringified
|
|
761
|
+
* `calls` PLUS `images.length * `{@link import('./constants.js').IMAGE_TOKEN_ESTIMATE} (a coarse,
|
|
762
|
+
* deliberately-approximate per-image cost — a base64 length is NOT a token proxy). Deterministic
|
|
763
|
+
* and provider-free — the same messages always yield the same estimate, with an empty batch `0`.
|
|
764
|
+
* It is the fully-swappable default an agent's auto-compaction context budget charges each
|
|
765
|
+
* turn's new messages through; a caller wanting a sharper count supplies its own `consume` to
|
|
766
|
+
* `createBudget` instead. Total — never throws: a `calls` `JSON.stringify` that throws (a
|
|
767
|
+
* circular `ToolCall.arguments`) is caught and replaced with a conservative fixed contribution of
|
|
768
|
+
* {@link import('./constants.js').MESSAGE_TOKEN_OVERHEAD} (the same per-message overhead scale)
|
|
769
|
+
* instead of estimating the (unreachable) serialized length.
|
|
677
770
|
*
|
|
678
771
|
* @param messages - The messages to estimate (a turn's appended assistant + tool messages)
|
|
679
|
-
* @returns The summed estimated token count (
|
|
772
|
+
* @returns The summed estimated token count (`0` when empty)
|
|
680
773
|
*
|
|
681
774
|
* @example
|
|
682
775
|
* ```ts
|
|
683
776
|
* estimateMessages([]) // 0
|
|
684
|
-
* estimateMessages([{ id: '1', role: 'user', content: 'hello' }]) // 2
|
|
777
|
+
* estimateMessages([{ id: '1', role: 'user', content: 'hello' }]) // 6 (2 content + 4 overhead)
|
|
685
778
|
* ```
|
|
686
779
|
*/
|
|
687
780
|
function estimateMessages(messages) {
|
|
688
|
-
return messages.reduce((sum, message) =>
|
|
781
|
+
return messages.reduce((sum, message) => {
|
|
782
|
+
const content = estimateTokens(message.content) + 4;
|
|
783
|
+
let calls = 0;
|
|
784
|
+
if (message.calls?.length) try {
|
|
785
|
+
calls = estimateTokens(JSON.stringify(message.calls));
|
|
786
|
+
} catch {
|
|
787
|
+
calls = 4;
|
|
788
|
+
}
|
|
789
|
+
const images = (message.images?.length ?? 0) * 512;
|
|
790
|
+
return sum + content + calls + images;
|
|
791
|
+
}, 0);
|
|
689
792
|
}
|
|
690
793
|
/**
|
|
691
794
|
* Run one rehydrated agent and apply the partial-as-configurable-failure policy — the
|
|
@@ -1311,6 +1414,32 @@ function fencedFile(path, language, content) {
|
|
|
1311
1414
|
function escapeRegExp(value) {
|
|
1312
1415
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1313
1416
|
}
|
|
1417
|
+
/**
|
|
1418
|
+
* Sanitize a {@link TokenUsage} into safe, non-negative integers — the guard an agent's
|
|
1419
|
+
* abort-usage path applies to a provider's partial usage before it is charged against a
|
|
1420
|
+
* budget or folded into the run total.
|
|
1421
|
+
*
|
|
1422
|
+
* @remarks
|
|
1423
|
+
* Per field (`prompt` / `completion` / `total`): a non-finite value (`NaN`, `+Infinity`,
|
|
1424
|
+
* `-Infinity`) or a negative value floors to `0`; a fractional value floors to its
|
|
1425
|
+
* non-negative integer part. No upper cap is applied. Total — never throws.
|
|
1426
|
+
*
|
|
1427
|
+
* @param usage - The token usage to sanitize (e.g. a provider's abort-partial usage)
|
|
1428
|
+
* @returns A new {@link TokenUsage} with every field a safe non-negative integer
|
|
1429
|
+
*
|
|
1430
|
+
* @example
|
|
1431
|
+
* ```ts
|
|
1432
|
+
* sanitizeUsage({ prompt: -5, completion: NaN, total: 12.7 }) // { prompt: 0, completion: 0, total: 12 }
|
|
1433
|
+
* ```
|
|
1434
|
+
*/
|
|
1435
|
+
function sanitizeUsage(usage) {
|
|
1436
|
+
const clean = (value) => Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
|
|
1437
|
+
return {
|
|
1438
|
+
prompt: clean(usage.prompt),
|
|
1439
|
+
completion: clean(usage.completion),
|
|
1440
|
+
total: clean(usage.total)
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1314
1443
|
//#endregion
|
|
1315
1444
|
//#region src/core/instructions/Instruction.ts
|
|
1316
1445
|
/**
|
|
@@ -2236,6 +2365,7 @@ var Agent = class {
|
|
|
2236
2365
|
#signal;
|
|
2237
2366
|
#authority;
|
|
2238
2367
|
#window;
|
|
2368
|
+
#strict;
|
|
2239
2369
|
#emitter;
|
|
2240
2370
|
id = crypto.randomUUID();
|
|
2241
2371
|
#status = "idle";
|
|
@@ -2245,6 +2375,9 @@ var Agent = class {
|
|
|
2245
2375
|
this.#context = new AgentContext({
|
|
2246
2376
|
system: options?.system,
|
|
2247
2377
|
tools: options?.tools,
|
|
2378
|
+
instructions: options?.instructions,
|
|
2379
|
+
workspaces: options?.workspaces,
|
|
2380
|
+
scope: options?.scope,
|
|
2248
2381
|
conversations: options?.conversations
|
|
2249
2382
|
});
|
|
2250
2383
|
this.#limit = options?.limit ?? 10;
|
|
@@ -2254,6 +2387,7 @@ var Agent = class {
|
|
|
2254
2387
|
this.#signal = options?.signal;
|
|
2255
2388
|
this.#authority = options?.authority;
|
|
2256
2389
|
this.#window = options?.window;
|
|
2390
|
+
this.#strict = options?.strict ?? false;
|
|
2257
2391
|
this.#emitter = new _orkestrel_emitter.Emitter({
|
|
2258
2392
|
on: options?.on,
|
|
2259
2393
|
error: options?.error
|
|
@@ -2272,6 +2406,7 @@ var Agent = class {
|
|
|
2272
2406
|
return this.stream(options).result;
|
|
2273
2407
|
}
|
|
2274
2408
|
stream(options) {
|
|
2409
|
+
if (this.#runs.size > 0 && (this.#window !== void 0 || this.#budget !== void 0 && options?.budget === void 0)) throw new AgentError("CONCURRENCY", "concurrent runs on one agent with a shared construction window/budget corrupt accounting; use separate agents or per-run budgets");
|
|
2275
2410
|
const timeoutMs = options?.timeout ?? this.#timeoutMs;
|
|
2276
2411
|
const timeout = timeoutMs === void 0 ? void 0 : (0, _orkestrel_timeout.createTimeout)({ ms: timeoutMs });
|
|
2277
2412
|
timeout?.start();
|
|
@@ -2391,7 +2526,7 @@ var Agent = class {
|
|
|
2391
2526
|
if (isProviderAbortError(error)) {
|
|
2392
2527
|
if (error.partial.thinking !== void 0) thinking = this.#thought(thinking, error.partial.thinking);
|
|
2393
2528
|
if (error.partial.usage !== void 0) {
|
|
2394
|
-
const abortUsage = error.partial.usage;
|
|
2529
|
+
const abortUsage = sanitizeUsage(error.partial.usage);
|
|
2395
2530
|
budget?.consume({
|
|
2396
2531
|
prompt: abortUsage.prompt,
|
|
2397
2532
|
completion: Math.max(0, abortUsage.completion - charged),
|
|
@@ -2408,16 +2543,17 @@ var Agent = class {
|
|
|
2408
2543
|
}
|
|
2409
2544
|
if (result.thinking !== void 0 && result.thinking.length > 0) thinking = this.#thought(thinking, result.thinking);
|
|
2410
2545
|
if (result.usage !== void 0) {
|
|
2546
|
+
const resultUsage = sanitizeUsage(result.usage);
|
|
2411
2547
|
budget?.consume({
|
|
2412
|
-
prompt:
|
|
2413
|
-
completion: Math.max(0,
|
|
2414
|
-
total: Math.max(0,
|
|
2548
|
+
prompt: resultUsage.prompt,
|
|
2549
|
+
completion: Math.max(0, resultUsage.completion - charged),
|
|
2550
|
+
total: Math.max(0, resultUsage.total - charged)
|
|
2415
2551
|
});
|
|
2416
|
-
usage = this.#sum(usage,
|
|
2417
|
-
this.#emitter.emit("usage",
|
|
2552
|
+
usage = this.#sum(usage, resultUsage);
|
|
2553
|
+
this.#emitter.emit("usage", resultUsage);
|
|
2418
2554
|
yield {
|
|
2419
2555
|
type: "usage",
|
|
2420
|
-
usage:
|
|
2556
|
+
usage: resultUsage
|
|
2421
2557
|
};
|
|
2422
2558
|
}
|
|
2423
2559
|
if (result.tools !== void 0 && result.tools.length > 0) {
|
|
@@ -2476,6 +2612,7 @@ var Agent = class {
|
|
|
2476
2612
|
section = await conversation.compact();
|
|
2477
2613
|
} catch (error) {
|
|
2478
2614
|
this.#emitter.emit("compactError", error);
|
|
2615
|
+
if (this.#strict) throw error;
|
|
2479
2616
|
return;
|
|
2480
2617
|
}
|
|
2481
2618
|
if (section === void 0) {
|
|
@@ -4064,6 +4201,7 @@ function createWorkspaceManager(options) {
|
|
|
4064
4201
|
//#endregion
|
|
4065
4202
|
exports.Agent = Agent;
|
|
4066
4203
|
exports.AgentContext = AgentContext;
|
|
4204
|
+
exports.AgentError = AgentError;
|
|
4067
4205
|
exports.AgentJobError = AgentJobError;
|
|
4068
4206
|
exports.AgentRegistry = AgentRegistry;
|
|
4069
4207
|
exports.Authority = Authority;
|
|
@@ -4078,8 +4216,10 @@ exports.DEFAULT_CONVERSATION_KEEP = DEFAULT_CONVERSATION_KEEP;
|
|
|
4078
4216
|
exports.DatabaseConversationStore = DatabaseConversationStore;
|
|
4079
4217
|
exports.DatabaseWorkspaceStore = DatabaseWorkspaceStore;
|
|
4080
4218
|
exports.EXTENSION_TO_LANGUAGE = EXTENSION_TO_LANGUAGE;
|
|
4219
|
+
exports.IMAGE_TOKEN_ESTIMATE = IMAGE_TOKEN_ESTIMATE;
|
|
4081
4220
|
exports.Instruction = Instruction;
|
|
4082
4221
|
exports.InstructionManager = InstructionManager;
|
|
4222
|
+
exports.MESSAGE_TOKEN_OVERHEAD = MESSAGE_TOKEN_OVERHEAD;
|
|
4083
4223
|
exports.MemoryConversationStore = MemoryConversationStore;
|
|
4084
4224
|
exports.MemoryWorkspaceStore = MemoryWorkspaceStore;
|
|
4085
4225
|
exports.ProviderAbortError = ProviderAbortError;
|
|
@@ -4130,6 +4270,7 @@ exports.estimateTokens = estimateTokens;
|
|
|
4130
4270
|
exports.fencedFile = fencedFile;
|
|
4131
4271
|
exports.filterAllowList = filterAllowList;
|
|
4132
4272
|
exports.inferLanguage = inferLanguage;
|
|
4273
|
+
exports.isAgentError = isAgentError;
|
|
4133
4274
|
exports.isAgentJobError = isAgentJobError;
|
|
4134
4275
|
exports.isBinary = isBinary;
|
|
4135
4276
|
exports.isConversationError = isConversationError;
|
|
@@ -4146,6 +4287,7 @@ exports.isWorkspaceError = isWorkspaceError;
|
|
|
4146
4287
|
exports.isWorkspaceSnapshot = isWorkspaceSnapshot;
|
|
4147
4288
|
exports.offsetAt = offsetAt;
|
|
4148
4289
|
exports.rangeOf = rangeOf;
|
|
4290
|
+
exports.sanitizeUsage = sanitizeUsage;
|
|
4149
4291
|
exports.settleAgentJob = settleAgentJob;
|
|
4150
4292
|
exports.sliceRange = sliceRange;
|
|
4151
4293
|
exports.spliceRange = spliceRange;
|