@deepagents/context 0.18.0 → 0.20.0

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/index.js CHANGED
@@ -29,28 +29,6 @@ function fragment(name, ...children) {
29
29
  data: children
30
30
  };
31
31
  }
32
- function user(content) {
33
- const message2 = typeof content === "string" ? {
34
- id: generateId(),
35
- role: "user",
36
- parts: [{ type: "text", text: content }]
37
- } : content;
38
- return {
39
- id: message2.id,
40
- name: "user",
41
- data: "content",
42
- type: "message",
43
- persist: true,
44
- codec: {
45
- decode() {
46
- return message2;
47
- },
48
- encode() {
49
- return message2;
50
- }
51
- }
52
- };
53
- }
54
32
  function assistant(message2) {
55
33
  return {
56
34
  id: message2.id,
@@ -2277,6 +2255,145 @@ function policy(input) {
2277
2255
  };
2278
2256
  }
2279
2257
 
2258
+ // packages/context/src/lib/fragments/message/user.ts
2259
+ import { generateId as generateId3 } from "ai";
2260
+ var SYSTEM_REMINDER_OPEN_TAG = "<system-reminder>";
2261
+ var SYSTEM_REMINDER_CLOSE_TAG = "</system-reminder>";
2262
+ function getReminderRanges(metadata) {
2263
+ return metadata?.reminders ?? [];
2264
+ }
2265
+ function stripTextByRanges(text, ranges) {
2266
+ if (ranges.length === 0) {
2267
+ return text;
2268
+ }
2269
+ const normalized = ranges.map((range) => ({
2270
+ start: Math.max(0, Math.min(text.length, range.start)),
2271
+ end: Math.max(0, Math.min(text.length, range.end))
2272
+ })).filter((range) => range.end > range.start).sort((a, b) => a.start - b.start);
2273
+ if (normalized.length === 0) {
2274
+ return text;
2275
+ }
2276
+ let cursor = 0;
2277
+ let output = "";
2278
+ for (const range of normalized) {
2279
+ if (range.start < cursor) {
2280
+ if (range.end > cursor) {
2281
+ cursor = range.end;
2282
+ }
2283
+ continue;
2284
+ }
2285
+ output += text.slice(cursor, range.start);
2286
+ cursor = range.end;
2287
+ }
2288
+ output += text.slice(cursor);
2289
+ return output.trimEnd();
2290
+ }
2291
+ function isRecord(value) {
2292
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2293
+ }
2294
+ function assertReminderText(text) {
2295
+ if (text.trim().length === 0) {
2296
+ throw new Error("Reminder text must not be empty");
2297
+ }
2298
+ }
2299
+ function formatTaggedReminder(text) {
2300
+ return `${SYSTEM_REMINDER_OPEN_TAG}${text}${SYSTEM_REMINDER_CLOSE_TAG}`;
2301
+ }
2302
+ function findLastTextPartIndex(message2) {
2303
+ for (let i = message2.parts.length - 1; i >= 0; i--) {
2304
+ if (message2.parts[i].type === "text") {
2305
+ return i;
2306
+ }
2307
+ }
2308
+ return void 0;
2309
+ }
2310
+ function ensureTextPart(message2) {
2311
+ const existingIndex = findLastTextPartIndex(message2);
2312
+ if (existingIndex !== void 0) {
2313
+ return existingIndex;
2314
+ }
2315
+ const reminderPart = {
2316
+ type: "text",
2317
+ text: ""
2318
+ };
2319
+ message2.parts.push(reminderPart);
2320
+ return message2.parts.length - 1;
2321
+ }
2322
+ function applyInlineReminder(message2, value) {
2323
+ const partIndex = ensureTextPart(message2);
2324
+ const textPart = message2.parts[partIndex];
2325
+ if (textPart.type !== "text") {
2326
+ throw new Error("Failed to resolve text part for inline reminder");
2327
+ }
2328
+ const reminderText = formatTaggedReminder(value);
2329
+ const start = textPart.text.length;
2330
+ const updatedText = `${textPart.text}${reminderText}`;
2331
+ message2.parts[partIndex] = { ...textPart, text: updatedText };
2332
+ return {
2333
+ id: generateId3(),
2334
+ text: value,
2335
+ partIndex,
2336
+ start,
2337
+ end: start + reminderText.length,
2338
+ mode: "inline"
2339
+ };
2340
+ }
2341
+ function applyPartReminder(message2, value) {
2342
+ const part = { type: "text", text: value };
2343
+ message2.parts.push(part);
2344
+ const partIndex = message2.parts.length - 1;
2345
+ return {
2346
+ id: generateId3(),
2347
+ text: value,
2348
+ partIndex,
2349
+ start: 0,
2350
+ end: value.length,
2351
+ mode: "part"
2352
+ };
2353
+ }
2354
+ function reminder(text, options) {
2355
+ assertReminderText(text);
2356
+ return {
2357
+ text,
2358
+ asPart: options?.asPart ?? false
2359
+ };
2360
+ }
2361
+ function user(content, ...reminders) {
2362
+ const message2 = typeof content === "string" ? {
2363
+ id: generateId3(),
2364
+ role: "user",
2365
+ parts: [{ type: "text", text: content }]
2366
+ } : { ...content, role: "user", parts: [...content.parts] };
2367
+ if (reminders.length > 0) {
2368
+ const addedReminders = [];
2369
+ for (const item of reminders) {
2370
+ assertReminderText(item.text);
2371
+ addedReminders.push(
2372
+ item.asPart ? applyPartReminder(message2, item.text) : applyInlineReminder(message2, item.text)
2373
+ );
2374
+ }
2375
+ const metadata = isRecord(message2.metadata) ? { ...message2.metadata } : {};
2376
+ const existingReminders = Array.isArray(metadata.reminders) ? metadata.reminders : [];
2377
+ metadata.reminders = [...existingReminders, ...addedReminders];
2378
+ message2.metadata = metadata;
2379
+ }
2380
+ return {
2381
+ id: message2.id,
2382
+ name: "user",
2383
+ data: "content",
2384
+ type: "message",
2385
+ persist: true,
2386
+ codec: {
2387
+ decode() {
2388
+ return message2;
2389
+ },
2390
+ encode() {
2391
+ return message2;
2392
+ }
2393
+ }
2394
+ };
2395
+ }
2396
+
2280
2397
  // packages/context/src/lib/fragments/user.ts
2281
2398
  function identity(input) {
2282
2399
  return {
@@ -5509,6 +5626,97 @@ async function persistedWriter(options) {
5509
5626
  };
5510
5627
  }
5511
5628
 
5629
+ // packages/context/src/lib/stream/polling-policy.ts
5630
+ var DEFAULT_WATCH_POLLING = {
5631
+ minMs: 25,
5632
+ maxMs: 500,
5633
+ multiplier: 2,
5634
+ jitterRatio: 0.15,
5635
+ statusCheckEvery: 3,
5636
+ chunkPageSize: 128
5637
+ };
5638
+ var DEFAULT_CANCEL_POLLING = {
5639
+ minMs: 50,
5640
+ maxMs: 500,
5641
+ multiplier: 2,
5642
+ jitterRatio: 0.15
5643
+ };
5644
+ function normalizeWatchPolling(polling, fallback = DEFAULT_WATCH_POLLING) {
5645
+ const merged = {
5646
+ ...fallback,
5647
+ ...polling
5648
+ };
5649
+ const normalizedBase = normalizeAdaptivePolling(merged, fallback);
5650
+ return {
5651
+ ...normalizedBase,
5652
+ statusCheckEvery: clampInt(merged.statusCheckEvery, 1, 1e4),
5653
+ chunkPageSize: clampInt(merged.chunkPageSize, 1, 1e4)
5654
+ };
5655
+ }
5656
+ function normalizeCancelPolling(polling, fallback = DEFAULT_CANCEL_POLLING) {
5657
+ return normalizeAdaptivePolling(polling, fallback);
5658
+ }
5659
+ function createAdaptivePollingState(config) {
5660
+ return {
5661
+ config,
5662
+ currentMs: config.minMs
5663
+ };
5664
+ }
5665
+ function resetAdaptivePolling(state) {
5666
+ state.currentMs = state.config.minMs;
5667
+ }
5668
+ function nextAdaptivePollingDelay(state) {
5669
+ const current = clampInt(
5670
+ state.currentMs,
5671
+ state.config.minMs,
5672
+ state.config.maxMs
5673
+ );
5674
+ const delay = applyJitter(
5675
+ current,
5676
+ state.config.jitterRatio,
5677
+ state.config.minMs,
5678
+ state.config.maxMs
5679
+ );
5680
+ state.currentMs = clampInt(
5681
+ Math.ceil(current * state.config.multiplier),
5682
+ state.config.minMs,
5683
+ state.config.maxMs
5684
+ );
5685
+ return delay;
5686
+ }
5687
+ function normalizeAdaptivePolling(polling, fallback) {
5688
+ const merged = {
5689
+ ...fallback,
5690
+ ...polling
5691
+ };
5692
+ const minMs = clampInt(merged.minMs, 1, 6e4);
5693
+ const maxMs = clampInt(merged.maxMs, minMs, 6e4);
5694
+ return {
5695
+ minMs,
5696
+ maxMs,
5697
+ multiplier: clampFloat(merged.multiplier, 1, 10),
5698
+ jitterRatio: clampFloat(merged.jitterRatio, 0, 1)
5699
+ };
5700
+ }
5701
+ function applyJitter(value, jitterRatio, min, max) {
5702
+ if (jitterRatio <= 0) return value;
5703
+ const radius = value * jitterRatio;
5704
+ const lowerBound = Math.max(0, value - radius);
5705
+ const upperBound = value + radius;
5706
+ const jittered = Math.round(
5707
+ lowerBound + Math.random() * (upperBound - lowerBound)
5708
+ );
5709
+ return clampInt(jittered, min, max);
5710
+ }
5711
+ function clampInt(value, min, max) {
5712
+ if (!Number.isFinite(value)) return min;
5713
+ return Math.min(max, Math.max(min, Math.round(value)));
5714
+ }
5715
+ function clampFloat(value, min, max) {
5716
+ if (!Number.isFinite(value)) return min;
5717
+ return Math.min(max, Math.max(min, value));
5718
+ }
5719
+
5512
5720
  // packages/context/src/lib/stream/sqlite.stream-store.ts
5513
5721
  import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
5514
5722
 
@@ -5523,6 +5731,7 @@ var StreamStore = class {
5523
5731
  var SqliteStreamStore = class extends StreamStore {
5524
5732
  #db;
5525
5733
  #statements = /* @__PURE__ */ new Map();
5734
+ #closed = false;
5526
5735
  #stmt(sql) {
5527
5736
  let stmt = this.#statements.get(sql);
5528
5737
  if (!stmt) {
@@ -5531,6 +5740,12 @@ var SqliteStreamStore = class extends StreamStore {
5531
5740
  }
5532
5741
  return stmt;
5533
5742
  }
5743
+ close() {
5744
+ if (this.#closed) return;
5745
+ this.#closed = true;
5746
+ this.#statements.clear();
5747
+ this.#db.close();
5748
+ }
5534
5749
  constructor(pathOrDb) {
5535
5750
  super();
5536
5751
  this.#db = typeof pathOrDb === "string" ? new DatabaseSync2(pathOrDb) : pathOrDb;
@@ -5602,6 +5817,12 @@ var SqliteStreamStore = class extends StreamStore {
5602
5817
  error: row.error
5603
5818
  };
5604
5819
  }
5820
+ async getStreamStatus(streamId) {
5821
+ const row = this.#stmt("SELECT status FROM streams WHERE id = ?").get(
5822
+ streamId
5823
+ );
5824
+ return row?.status;
5825
+ }
5605
5826
  async updateStreamStatus(streamId, status, options) {
5606
5827
  const now = Date.now();
5607
5828
  switch (status) {
@@ -5727,8 +5948,20 @@ function isTerminal(status) {
5727
5948
  }
5728
5949
  var StreamManager = class {
5729
5950
  #store;
5951
+ #watchPollingDefaults;
5952
+ #cancelPollingDefaults;
5953
+ #onPollingEvent;
5730
5954
  constructor(options) {
5731
5955
  this.#store = options.store;
5956
+ this.#watchPollingDefaults = normalizeWatchPolling(
5957
+ options.watchPolling,
5958
+ DEFAULT_WATCH_POLLING
5959
+ );
5960
+ this.#cancelPollingDefaults = normalizeCancelPolling(
5961
+ options.cancelPolling,
5962
+ DEFAULT_CANCEL_POLLING
5963
+ );
5964
+ this.#onPollingEvent = options.onPollingEvent;
5732
5965
  }
5733
5966
  get store() {
5734
5967
  return this.#store;
@@ -5754,14 +5987,37 @@ var StreamManager = class {
5754
5987
  }
5755
5988
  await this.#store.updateStreamStatus(streamId, "running");
5756
5989
  const ac = new AbortController();
5757
- const checkInterval = options?.cancelCheckInterval ?? 500;
5990
+ const cancelPolling = normalizeCancelPolling(
5991
+ options?.cancelPolling,
5992
+ this.#cancelPollingDefaults
5993
+ );
5994
+ const pollState = createAdaptivePollingState(cancelPolling);
5758
5995
  const pollCancel = (async () => {
5759
5996
  while (!ac.signal.aborted) {
5760
- await setTimeout(checkInterval);
5761
- if (ac.signal.aborted) break;
5762
- const current = await this.#store.getStream(streamId);
5763
- if (current?.status === "cancelled") {
5997
+ const delayMs = nextAdaptivePollingDelay(pollState);
5998
+ const continued = await waitForDelay(delayMs, ac.signal);
5999
+ if (!continued || ac.signal.aborted) break;
6000
+ const status = await this.#store.getStreamStatus(streamId);
6001
+ this.#emitPolling({
6002
+ type: "persist:cancel-poll",
6003
+ streamId,
6004
+ delayMs,
6005
+ status: status ?? "missing"
6006
+ });
6007
+ if (status === void 0) {
5764
6008
  ac.abort();
6009
+ break;
6010
+ }
6011
+ if (status === "cancelled") {
6012
+ const current = await this.#store.getStream(streamId);
6013
+ const latencyMs = current?.cancelRequestedAt != null ? Math.max(0, Date.now() - current.cancelRequestedAt) : null;
6014
+ this.#emitPolling({
6015
+ type: "persist:cancel-detected",
6016
+ streamId,
6017
+ latencyMs
6018
+ });
6019
+ ac.abort();
6020
+ break;
5765
6021
  }
5766
6022
  }
5767
6023
  })();
@@ -5787,7 +6043,11 @@ var StreamManager = class {
5787
6043
  }
5788
6044
  } catch (err) {
5789
6045
  if (ac.signal.aborted) {
5790
- if (pw) await pw.flush();
6046
+ if (isAbortError(err)) {
6047
+ if (pw) await pw.flush();
6048
+ } else {
6049
+ throw err;
6050
+ }
5791
6051
  } else {
5792
6052
  const message2 = err instanceof Error ? err.message : String(err);
5793
6053
  if (pw) {
@@ -5807,8 +6067,18 @@ var StreamManager = class {
5807
6067
  }
5808
6068
  watch(streamId, options) {
5809
6069
  const store = this.#store;
5810
- const interval = options?.interval ?? 100;
5811
- let lastSeq = -1;
6070
+ const polling = normalizeWatchPolling(options, this.#watchPollingDefaults);
6071
+ const delayState = createAdaptivePollingState(polling);
6072
+ const ac = new AbortController();
6073
+ const lastSeqRef = { value: -1 };
6074
+ let chunkPollsSinceStatus = 0;
6075
+ const emitChunks = (controller, chunks) => {
6076
+ for (const chunk of chunks) {
6077
+ controller.enqueue(chunk.data);
6078
+ lastSeqRef.value = chunk.seq;
6079
+ }
6080
+ return chunks.length;
6081
+ };
5812
6082
  return new ReadableStream({
5813
6083
  async start() {
5814
6084
  const stream = await store.getStream(streamId);
@@ -5816,28 +6086,102 @@ var StreamManager = class {
5816
6086
  throw new Error(`Stream "${streamId}" not found`);
5817
6087
  }
5818
6088
  },
5819
- async pull(controller) {
5820
- while (true) {
5821
- const [chunks, current] = await Promise.all([
5822
- store.getChunks(streamId, lastSeq + 1),
5823
- store.getStream(streamId)
5824
- ]);
5825
- for (const chunk of chunks) {
5826
- controller.enqueue(chunk.data);
5827
- lastSeq = chunk.seq;
6089
+ pull: async (controller) => {
6090
+ while (!ac.signal.aborted) {
6091
+ const fromSeq = lastSeqRef.value + 1;
6092
+ const chunks = await store.getChunks(
6093
+ streamId,
6094
+ fromSeq,
6095
+ polling.chunkPageSize
6096
+ );
6097
+ let statusChecked = false;
6098
+ let currentStatus;
6099
+ if (chunks.length === 0) {
6100
+ chunkPollsSinceStatus = polling.statusCheckEvery;
6101
+ } else {
6102
+ chunkPollsSinceStatus += 1;
6103
+ }
6104
+ if (chunkPollsSinceStatus >= polling.statusCheckEvery) {
6105
+ statusChecked = true;
6106
+ chunkPollsSinceStatus = 0;
6107
+ currentStatus = await store.getStreamStatus(streamId);
5828
6108
  }
5829
- if (current && isTerminal(current.status)) {
5830
- const remaining = await store.getChunks(streamId, lastSeq + 1);
5831
- for (const chunk of remaining) {
5832
- controller.enqueue(chunk.data);
5833
- lastSeq = chunk.seq;
6109
+ this.#emitPolling({
6110
+ type: "watch:poll",
6111
+ streamId,
6112
+ fromSeq,
6113
+ chunkCount: chunks.length,
6114
+ statusChecked
6115
+ });
6116
+ if (chunks.length > 0) {
6117
+ const delivered = emitChunks(controller, chunks);
6118
+ this.#emitPolling({
6119
+ type: "watch:chunks",
6120
+ streamId,
6121
+ delivered,
6122
+ lastSeq: lastSeqRef.value
6123
+ });
6124
+ resetAdaptivePolling(delayState);
6125
+ if (chunks.length >= polling.chunkPageSize) {
6126
+ continue;
5834
6127
  }
5835
- controller.close();
5836
6128
  return;
5837
6129
  }
5838
- if (chunks.length > 0) return;
5839
- await setTimeout(interval);
6130
+ if (statusChecked) {
6131
+ if (currentStatus === void 0) {
6132
+ this.#emitPolling({
6133
+ type: "watch:closed",
6134
+ streamId,
6135
+ reason: "missing"
6136
+ });
6137
+ controller.close();
6138
+ ac.abort();
6139
+ return;
6140
+ }
6141
+ if (isTerminal(currentStatus)) {
6142
+ const drained = await drainRemainingChunks({
6143
+ controller,
6144
+ store,
6145
+ streamId,
6146
+ fromSeq: lastSeqRef.value + 1,
6147
+ chunkPageSize: polling.chunkPageSize,
6148
+ onChunk: (seq) => {
6149
+ lastSeqRef.value = seq;
6150
+ }
6151
+ });
6152
+ if (drained > 0) {
6153
+ this.#emitPolling({
6154
+ type: "watch:chunks",
6155
+ streamId,
6156
+ delivered: drained,
6157
+ lastSeq: lastSeqRef.value
6158
+ });
6159
+ }
6160
+ this.#emitPolling({
6161
+ type: "watch:closed",
6162
+ streamId,
6163
+ reason: "terminal"
6164
+ });
6165
+ controller.close();
6166
+ ac.abort();
6167
+ return;
6168
+ }
6169
+ }
6170
+ const delayMs = nextAdaptivePollingDelay(delayState);
6171
+ this.#emitPolling({
6172
+ type: "watch:empty",
6173
+ streamId,
6174
+ fromSeq: lastSeqRef.value + 1,
6175
+ delayMs
6176
+ });
6177
+ const continued = await waitForDelay(delayMs, ac.signal);
6178
+ if (!continued) {
6179
+ return;
6180
+ }
5840
6181
  }
6182
+ },
6183
+ cancel() {
6184
+ ac.abort();
5841
6185
  }
5842
6186
  });
5843
6187
  }
@@ -5848,7 +6192,45 @@ var StreamManager = class {
5848
6192
  async cleanup(streamId) {
5849
6193
  await this.#store.deleteStream(streamId);
5850
6194
  }
6195
+ #emitPolling(event) {
6196
+ if (!this.#onPollingEvent) return;
6197
+ try {
6198
+ this.#onPollingEvent(event);
6199
+ } catch {
6200
+ }
6201
+ }
5851
6202
  };
6203
+ async function drainRemainingChunks(options) {
6204
+ const { controller, store, streamId, chunkPageSize, onChunk } = options;
6205
+ let fromSeq = options.fromSeq;
6206
+ let drained = 0;
6207
+ while (true) {
6208
+ const chunks = await store.getChunks(streamId, fromSeq, chunkPageSize);
6209
+ if (chunks.length === 0) break;
6210
+ for (const chunk of chunks) {
6211
+ controller.enqueue(chunk.data);
6212
+ onChunk(chunk.seq);
6213
+ drained++;
6214
+ fromSeq = chunk.seq + 1;
6215
+ }
6216
+ if (chunks.length < chunkPageSize) {
6217
+ break;
6218
+ }
6219
+ }
6220
+ return drained;
6221
+ }
6222
+ async function waitForDelay(ms, signal) {
6223
+ try {
6224
+ await setTimeout(ms, void 0, signal ? { signal } : void 0);
6225
+ return true;
6226
+ } catch (error) {
6227
+ if (isAbortError(error)) return false;
6228
+ throw error;
6229
+ }
6230
+ }
6231
+ function isAbortError(error) {
6232
+ return error instanceof Error && (error.name === "AbortError" || /aborted/i.test(error.message));
6233
+ }
5852
6234
  async function drain(stream, signal) {
5853
6235
  const reader = stream.getReader();
5854
6236
  const onAbort = () => reader.cancel();
@@ -5929,6 +6311,8 @@ export {
5929
6311
  ContextEngine,
5930
6312
  ContextRenderer,
5931
6313
  ContextStore,
6314
+ DEFAULT_CANCEL_POLLING,
6315
+ DEFAULT_WATCH_POLLING,
5932
6316
  DockerNotAvailableError,
5933
6317
  DockerSandboxError,
5934
6318
  DockerSandboxStrategy,
@@ -5957,6 +6341,7 @@ export {
5957
6341
  assistantText,
5958
6342
  clarification,
5959
6343
  correction,
6344
+ createAdaptivePollingState,
5960
6345
  createBinaryBridges,
5961
6346
  createContainerTool,
5962
6347
  createDockerSandbox,
@@ -5969,6 +6354,7 @@ export {
5969
6354
  fail,
5970
6355
  fragment,
5971
6356
  getModelsRegistry,
6357
+ getReminderRanges,
5972
6358
  glossary,
5973
6359
  guardrail,
5974
6360
  hint,
@@ -5982,6 +6368,9 @@ export {
5982
6368
  lastAssistantMessage,
5983
6369
  loadSkillMetadata,
5984
6370
  message,
6371
+ nextAdaptivePollingDelay,
6372
+ normalizeCancelPolling,
6373
+ normalizeWatchPolling,
5985
6374
  parseFrontmatter,
5986
6375
  pass,
5987
6376
  persistedWriter,
@@ -5990,12 +6379,15 @@ export {
5990
6379
  preference,
5991
6380
  principle,
5992
6381
  quirk,
6382
+ reminder,
5993
6383
  render,
6384
+ resetAdaptivePolling,
5994
6385
  role,
5995
6386
  runGuardrailChain,
5996
6387
  skills,
5997
6388
  soul,
5998
6389
  stop,
6390
+ stripTextByRanges,
5999
6391
  structuredOutput,
6000
6392
  styleGuide,
6001
6393
  term,