@deepagents/context 3.1.0 → 4.0.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.
Files changed (43) hide show
  1. package/README.md +46 -13
  2. package/dist/browser.js +30 -56
  3. package/dist/browser.js.map +3 -3
  4. package/dist/index.js +2359 -2384
  5. package/dist/index.js.map +4 -4
  6. package/dist/lib/chain-summary.d.ts.map +1 -1
  7. package/dist/lib/engine.d.ts.map +1 -1
  8. package/dist/lib/fragments/message/user.d.ts +42 -85
  9. package/dist/lib/fragments/message/user.d.ts.map +1 -1
  10. package/dist/lib/sandbox/agent-os-sandbox.d.ts.map +1 -1
  11. package/dist/lib/sandbox/bash-tool.d.ts +9 -28
  12. package/dist/lib/sandbox/bash-tool.d.ts.map +1 -1
  13. package/dist/lib/sandbox/daytona-sandbox.d.ts.map +1 -1
  14. package/dist/lib/sandbox/docker-sandbox.d.ts +29 -0
  15. package/dist/lib/sandbox/docker-sandbox.d.ts.map +1 -1
  16. package/dist/lib/sandbox/index.d.ts +2 -1
  17. package/dist/lib/sandbox/index.d.ts.map +1 -1
  18. package/dist/lib/sandbox/installers/bin.d.ts.map +1 -1
  19. package/dist/lib/sandbox/installers/index.d.ts +7 -7
  20. package/dist/lib/sandbox/installers/index.d.ts.map +1 -1
  21. package/dist/lib/sandbox/installers/installer.d.ts +0 -5
  22. package/dist/lib/sandbox/installers/installer.d.ts.map +1 -1
  23. package/dist/lib/sandbox/installers/url-binary.d.ts.map +1 -1
  24. package/dist/lib/sandbox/shell-quote.d.ts +10 -0
  25. package/dist/lib/sandbox/shell-quote.d.ts.map +1 -0
  26. package/dist/lib/sandbox/strace/file-change.d.ts +21 -0
  27. package/dist/lib/sandbox/strace/file-change.d.ts.map +1 -0
  28. package/dist/lib/sandbox/strace/file-changes.d.ts +66 -0
  29. package/dist/lib/sandbox/strace/file-changes.d.ts.map +1 -0
  30. package/dist/lib/sandbox/strace/index.d.ts +89 -0
  31. package/dist/lib/sandbox/strace/index.d.ts.map +1 -0
  32. package/dist/lib/sandbox/strace/index.js +285 -0
  33. package/dist/lib/sandbox/strace/index.js.map +7 -0
  34. package/dist/lib/sandbox/types.d.ts +6 -1
  35. package/dist/lib/sandbox/types.d.ts.map +1 -1
  36. package/dist/lib/sandbox/virtual-sandbox.d.ts.map +1 -1
  37. package/dist/lib/save/save-pipeline.d.ts +1 -13
  38. package/dist/lib/save/save-pipeline.d.ts.map +1 -1
  39. package/dist/lib/skills/skill-reminder.d.ts +2 -2
  40. package/dist/lib/skills/skill-reminder.d.ts.map +1 -1
  41. package/package.json +7 -2
  42. package/dist/lib/sandbox/file-changes.d.ts +0 -83
  43. package/dist/lib/sandbox/file-changes.d.ts.map +0 -1
package/README.md CHANGED
@@ -49,26 +49,59 @@ deletes the underlying Daytona sandbox.
49
49
 
50
50
  ### File-change tracking
51
51
 
52
- `createBashTool` reports what each tool call mutated as a `FileChange[]`
52
+ `withStraceFileChanges()` decorates a real sandbox backend so each command,
53
+ spawn, or file write reports the mutations it caused as a `FileChange[]`
53
54
  (`{ op: 'write' | 'delete' | 'rename'; path; from?; timestamp }`).
54
- Consume it via the `onFileChanges` callback (fires per command the reactive
55
- post-tool effect) or `tool-result.output.meta.fileChanges` (per call, hidden
56
- from the model).
55
+ Consume changes via the `onFileChanges` callback (fires per command, spawn, or
56
+ `writeFiles` call). Bash tool calls also receive
57
+ `tool-result.output.meta.fileChanges` as hidden host-only metadata.
58
+
59
+ Scope which paths are reported with `include` glob patterns (required — a path
60
+ must match at least one); drop noise such as uploaded skills with optional
61
+ `exclude` patterns. Both match absolute paths via Node's `path.matchesGlob`.
57
62
 
58
63
  ```ts
59
- const sandbox = await createBashTool({
60
- sandbox: await createDockerSandbox({ image: 'my-image-with-strace' }),
64
+ const backend = await createDockerSandbox({ image: 'my-image-with-strace' });
65
+ const tracked = await withStraceFileChanges(backend, {
66
+ include: ['/workspace/**', '/workspace'],
61
67
  onFileChanges: (changes) => {
62
68
  for (const c of changes) console.log(`${c.op} ${c.path}`);
63
69
  },
64
70
  });
71
+ const sandbox = await createBashTool({
72
+ sandbox: tracked,
73
+ destination: '/workspace',
74
+ });
65
75
  ```
66
76
 
67
- Tracking is **always on** via `strace` (per `executeCommand` and per `spawn`)
68
- there is no opt-in flag. A one-time self-test runs at `createBashTool` time and
69
- throws `StraceUnavailableError` (`reason: 'strace-missing' | 'ptrace-blocked' |
70
- 'trace-unparseable'`) there is no silent fallback. The backend must therefore
71
- satisfy, on any non-virtual backend (Docker, Daytona, e2b, …):
77
+ Tracking uses `strace` (per `executeCommand` and per `spawn`) when you compose
78
+ the `withStraceFileChanges()` decorator. The decorator itself does **no**
79
+ self-test it trusts that strace tracing works in the sandbox, because "strace
80
+ works here" is an invariant of the (image + host kernel + seccomp/caps) that is
81
+ constant for the container's lifetime. Re-proving it per composition would re-pay
82
+ several host→container round-trips on every tool call for no new information.
83
+
84
+ Verifying the invariant is the consumer's **once-per-container** responsibility.
85
+ Run the probe once at startup (e.g. a daemon boot gate) via the lean leaf entry:
86
+
87
+ ```ts
88
+ import { selfTestStrace } from '@deepagents/context/sandbox/strace';
89
+
90
+ // Throws StraceUnavailableError (reason: 'strace-missing' | 'ptrace-blocked' |
91
+ // 'trace-unparseable') with no silent fallback. A DisposableSandbox satisfies
92
+ // the StraceHost shape structurally, so pass a real backend unchanged; an
93
+ // in-process caller implements just { executeCommand, readFile }.
94
+ await selfTestStrace(backend);
95
+ ```
96
+
97
+ The `@deepagents/context/sandbox/strace` subpath is a node-builtins-only bundle
98
+ (probe + parser + `StraceUnavailableError`) with no agent/context-framework
99
+ imports, so a minimal daemon can import it without pulling the whole framework.
100
+ Import `StraceUnavailableError` from this same subpath when catching the probe's
101
+ error (each entry point is bundled independently, so `instanceof` requires the
102
+ class from the same entry).
103
+
104
+ The backend must satisfy, on any non-virtual backend (Docker, Daytona, e2b, ...):
72
105
 
73
106
  1. `strace` installed in the image — `apk add strace` (Alpine) /
74
107
  `apt-get install -y strace` (Debian), or `installers: [pkg(['strace'])]` for
@@ -78,8 +111,8 @@ satisfy, on any non-virtual backend (Docker, Daytona, e2b, …):
78
111
  garbles the trace, so build the image for the host arch.
79
112
 
80
113
  The **in-process virtual sandbox cannot host strace** (no real processes/ptrace),
81
- so it is unsupported by `createBashTool` — the self-test hard-fails. Use a
82
- container/VM backend.
114
+ so it is unsupported by `withStraceFileChanges()` — `selfTestStrace` hard-fails
115
+ against it. Use a container/VM backend.
83
116
 
84
117
  Ops are intentionally coarse: strace cannot distinguish a new file from an
85
118
  overwrite within one command (both are `O_CREAT|O_TRUNC`), so both report
package/dist/browser.js CHANGED
@@ -1815,6 +1815,11 @@ function getReminderRanges(metadata) {
1815
1815
  end: record.end
1816
1816
  }));
1817
1817
  }
1818
+ function getReminderOnceIds(message2) {
1819
+ const meta = message2.metadata;
1820
+ if (!isRecord(meta) || !Array.isArray(meta.onceIds)) return [];
1821
+ return meta.onceIds.filter((id) => typeof id === "string");
1822
+ }
1818
1823
  function getReminderMetadataRecords(metadata) {
1819
1824
  const reminders = metadata?.reminders;
1820
1825
  if (!Array.isArray(reminders)) return [];
@@ -1828,27 +1833,6 @@ function normalizeReminderTarget(target) {
1828
1833
  if (target === "steer") return "steer";
1829
1834
  throw new Error(`Unsupported reminder target: ${String(target)}`);
1830
1835
  }
1831
- function isConditionalReminderOptions(options) {
1832
- return options !== void 0 && "when" in options;
1833
- }
1834
- function isPromiseLike(value) {
1835
- return (typeof value === "object" || typeof value === "function") && value !== null && "then" in value && typeof value.then === "function";
1836
- }
1837
- function normalizeImmediateReminderText(textOrFragment) {
1838
- if (isFragment(textOrFragment)) {
1839
- return new XmlRenderer().render([textOrFragment]);
1840
- }
1841
- if (typeof textOrFragment === "string") {
1842
- return textOrFragment;
1843
- }
1844
- return (ctx) => {
1845
- const resolved = textOrFragment(ctx);
1846
- if (isPromiseLike(resolved)) {
1847
- throw new Error("Async reminder text requires a when predicate");
1848
- }
1849
- return resolved;
1850
- };
1851
- }
1852
1836
  function normalizeConditionalReminderText(textOrFragment) {
1853
1837
  return isFragment(textOrFragment) ? new XmlRenderer().render([textOrFragment]) : textOrFragment;
1854
1838
  }
@@ -2057,54 +2041,32 @@ function mergeReminderMetadata(message2, addedReminders) {
2057
2041
  function reminder(textOrFragment, options) {
2058
2042
  const target = normalizeReminderTarget(options?.target);
2059
2043
  const asPart = target === "user" ? options?.asPart ?? false : false;
2060
- if (isConditionalReminderOptions(options)) {
2061
- const text2 = normalizeConditionalReminderText(textOrFragment);
2062
- if (typeof text2 === "string") {
2063
- assertReminderText(text2);
2064
- }
2065
- return {
2066
- name: "reminder",
2067
- data: null,
2068
- metadata: {
2069
- reminder: {
2070
- text: text2,
2071
- when: options.when,
2072
- asPart,
2073
- target
2074
- }
2075
- }
2076
- };
2077
- }
2078
- if (target !== "user") {
2044
+ if (options?.when === void 0 && target !== "user") {
2079
2045
  throw new Error(`Reminder target "${target}" requires a when predicate`);
2080
2046
  }
2081
- const text = normalizeImmediateReminderText(textOrFragment);
2047
+ const text = normalizeConditionalReminderText(textOrFragment);
2082
2048
  if (typeof text === "string") {
2083
2049
  assertReminderText(text);
2084
2050
  }
2085
2051
  return {
2086
- text,
2087
- asPart,
2088
- target
2052
+ name: "reminder",
2053
+ data: null,
2054
+ metadata: {
2055
+ reminder: {
2056
+ text,
2057
+ when: options?.when ?? (() => true),
2058
+ asPart,
2059
+ target
2060
+ }
2061
+ }
2089
2062
  };
2090
2063
  }
2091
- function user(content, ...reminders) {
2064
+ function user(content) {
2092
2065
  const message2 = typeof content === "string" ? {
2093
2066
  id: generateId2(),
2094
2067
  role: "user",
2095
2068
  parts: [{ type: "text", text: content }]
2096
2069
  } : { ...content, role: "user", parts: [...content.parts] };
2097
- if (reminders.length > 0) {
2098
- const plainText = extractPlainText(message2);
2099
- const added = [];
2100
- for (const item of reminders) {
2101
- const meta = applyReminderToMessage(message2, item, {
2102
- content: plainText
2103
- });
2104
- if (meta) added.push(meta);
2105
- }
2106
- mergeReminderMetadata(message2, added);
2107
- }
2108
2070
  return {
2109
2071
  id: message2.id,
2110
2072
  name: "user",
@@ -2120,6 +2082,16 @@ function user(content, ...reminders) {
2120
2082
  }
2121
2083
  };
2122
2084
  }
2085
+ function applyUserRemindersToMessage(message2, reminders) {
2086
+ if (reminders.length === 0) return;
2087
+ const plainText = extractPlainText(message2);
2088
+ const added = [];
2089
+ for (const item of reminders) {
2090
+ const meta = applyReminderToMessage(message2, item, { content: plainText });
2091
+ if (meta) added.push(meta);
2092
+ }
2093
+ mergeReminderMetadata(message2, added);
2094
+ }
2123
2095
  function synthesizeSteerUserMessage(text, firedAt, onceIds = []) {
2124
2096
  const texts = Array.isArray(text) ? text : [text];
2125
2097
  for (const value of texts) assertReminderText(value);
@@ -2388,6 +2360,7 @@ export {
2388
2360
  applyPartReminder,
2389
2361
  applyReminderToMessage,
2390
2362
  applyRemindersToToolOutput,
2363
+ applyUserRemindersToMessage,
2391
2364
  assistant,
2392
2365
  assistantText,
2393
2366
  clarification,
@@ -2402,6 +2375,7 @@ export {
2402
2375
  fromFragment,
2403
2376
  getFragmentData,
2404
2377
  getModelsRegistry,
2378
+ getReminderOnceIds,
2405
2379
  getReminderRanges,
2406
2380
  glossary,
2407
2381
  guardrail,