@deepagents/context 3.1.0 → 4.1.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 (59) hide show
  1. package/README.md +57 -19
  2. package/dist/browser.js +48 -64
  3. package/dist/browser.js.map +3 -3
  4. package/dist/index.js +2004 -1479
  5. package/dist/index.js.map +4 -4
  6. package/dist/lib/agent.d.ts.map +1 -1
  7. package/dist/lib/chain-summary.d.ts.map +1 -1
  8. package/dist/lib/engine.d.ts +3 -3
  9. package/dist/lib/engine.d.ts.map +1 -1
  10. package/dist/lib/fragments/message/user.d.ts +51 -92
  11. package/dist/lib/fragments/message/user.d.ts.map +1 -1
  12. package/dist/lib/sandbox/agent-os-sandbox.d.ts.map +1 -1
  13. package/dist/lib/sandbox/apple-container-sandbox-errors.d.ts +44 -0
  14. package/dist/lib/sandbox/apple-container-sandbox-errors.d.ts.map +1 -0
  15. package/dist/lib/sandbox/apple-container-sandbox.d.ts +122 -0
  16. package/dist/lib/sandbox/apple-container-sandbox.d.ts.map +1 -0
  17. package/dist/lib/sandbox/bash-tool.d.ts +9 -28
  18. package/dist/lib/sandbox/bash-tool.d.ts.map +1 -1
  19. package/dist/lib/sandbox/cli-process.d.ts +24 -0
  20. package/dist/lib/sandbox/cli-process.d.ts.map +1 -0
  21. package/dist/lib/sandbox/container-engine.d.ts +149 -0
  22. package/dist/lib/sandbox/container-engine.d.ts.map +1 -0
  23. package/dist/lib/sandbox/container-sandbox-errors.d.ts +10 -0
  24. package/dist/lib/sandbox/container-sandbox-errors.d.ts.map +1 -0
  25. package/dist/lib/sandbox/container-sandbox.d.ts +90 -0
  26. package/dist/lib/sandbox/container-sandbox.d.ts.map +1 -0
  27. package/dist/lib/sandbox/daytona-sandbox.d.ts.map +1 -1
  28. package/dist/lib/sandbox/docker-sandbox-errors.d.ts +2 -2
  29. package/dist/lib/sandbox/docker-sandbox-errors.d.ts.map +1 -1
  30. package/dist/lib/sandbox/docker-sandbox.d.ts +122 -189
  31. package/dist/lib/sandbox/docker-sandbox.d.ts.map +1 -1
  32. package/dist/lib/sandbox/index.d.ts +6 -1
  33. package/dist/lib/sandbox/index.d.ts.map +1 -1
  34. package/dist/lib/sandbox/installers/bin.d.ts.map +1 -1
  35. package/dist/lib/sandbox/installers/index.d.ts +7 -7
  36. package/dist/lib/sandbox/installers/index.d.ts.map +1 -1
  37. package/dist/lib/sandbox/installers/installer.d.ts +0 -5
  38. package/dist/lib/sandbox/installers/installer.d.ts.map +1 -1
  39. package/dist/lib/sandbox/installers/url-binary.d.ts.map +1 -1
  40. package/dist/lib/sandbox/shell-quote.d.ts +10 -0
  41. package/dist/lib/sandbox/shell-quote.d.ts.map +1 -0
  42. package/dist/lib/sandbox/strace/file-change.d.ts +21 -0
  43. package/dist/lib/sandbox/strace/file-change.d.ts.map +1 -0
  44. package/dist/lib/sandbox/strace/file-changes.d.ts +66 -0
  45. package/dist/lib/sandbox/strace/file-changes.d.ts.map +1 -0
  46. package/dist/lib/sandbox/strace/index.d.ts +74 -0
  47. package/dist/lib/sandbox/strace/index.d.ts.map +1 -0
  48. package/dist/lib/sandbox/strace/index.js +296 -0
  49. package/dist/lib/sandbox/strace/index.js.map +7 -0
  50. package/dist/lib/sandbox/types.d.ts +9 -4
  51. package/dist/lib/sandbox/types.d.ts.map +1 -1
  52. package/dist/lib/sandbox/virtual-sandbox.d.ts.map +1 -1
  53. package/dist/lib/save/save-pipeline.d.ts +1 -13
  54. package/dist/lib/save/save-pipeline.d.ts.map +1 -1
  55. package/dist/lib/skills/skill-reminder.d.ts +2 -2
  56. package/dist/lib/skills/skill-reminder.d.ts.map +1 -1
  57. package/package.json +9 -3
  58. package/dist/lib/sandbox/file-changes.d.ts +0 -83
  59. package/dist/lib/sandbox/file-changes.d.ts.map +0 -1
package/README.md CHANGED
@@ -27,14 +27,15 @@ like store implementations, sandbox tooling, and filesystem-based skill loading.
27
27
 
28
28
  The server-side package also ships the sandbox primitives used by
29
29
  `@deepagents/text2sql` and other tool-driven agents. Use `createBashTool()`
30
- with `createVirtualSandbox()`, `createDockerSandbox()`, or
31
- `createDaytonaSandbox(client, options)`, or `createAgentOsSandbox()` depending
32
- on whether commands should run in memory, Docker, managed Daytona sandboxes, or
33
- Agent OS.
30
+ with `createVirtualSandbox()`, `createDockerSandbox()`,
31
+ `createAppleContainerSandbox()`, `createDaytonaSandbox(client, options)`, or
32
+ `createAgentOsSandbox()` depending on whether commands should run in memory,
33
+ Docker, Apple Container lightweight VMs, managed Daytona sandboxes, or Agent OS.
34
34
 
35
35
  See the docs for the full API surface:
36
36
 
37
37
  - [Sandbox](https://januarylabs.github.io/deepagents/docs/context/sandbox)
38
+ - [Apple Container Sandbox](https://januarylabs.github.io/deepagents/docs/context/apple-container-sandbox)
38
39
  - [Subcommand Builders](https://januarylabs.github.io/deepagents/docs/context/subcommand)
39
40
  - [GCS Cloud Storage Volumes](https://januarylabs.github.io/deepagents/docs/context/recipes/gcs-cloud-storage)
40
41
 
@@ -49,37 +50,74 @@ deletes the underlying Daytona sandbox.
49
50
 
50
51
  ### File-change tracking
51
52
 
52
- `createBashTool` reports what each tool call mutated as a `FileChange[]`
53
+ `withStraceFileChanges()` decorates a real sandbox backend so each command,
54
+ spawn, or file write reports the mutations it caused as a `FileChange[]`
53
55
  (`{ 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).
56
+ Consume changes via the `onFileChanges` callback (fires per command, spawn, or
57
+ `writeFiles` call). Bash tool calls also receive
58
+ `tool-result.output.meta.fileChanges` as hidden host-only metadata.
59
+
60
+ Scope which paths are reported with `include` glob patterns (required — a path
61
+ must match at least one); drop noise such as uploaded skills with optional
62
+ `exclude` patterns. Both match absolute paths via Node's `path.matchesGlob`.
57
63
 
58
64
  ```ts
59
- const sandbox = await createBashTool({
60
- sandbox: await createDockerSandbox({ image: 'my-image-with-strace' }),
65
+ const backend = await createDockerSandbox({ image: 'my-image-with-strace' });
66
+ const tracked = await withStraceFileChanges(backend, {
67
+ include: ['/workspace/**', '/workspace'],
61
68
  onFileChanges: (changes) => {
62
69
  for (const c of changes) console.log(`${c.op} ${c.path}`);
63
70
  },
64
71
  });
72
+ const sandbox = await createBashTool({
73
+ sandbox: tracked,
74
+ destination: '/workspace',
75
+ });
65
76
  ```
66
77
 
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, …):
78
+ Tracking uses `strace` (per `executeCommand` and per `spawn`) when you compose
79
+ the `withStraceFileChanges()` decorator. The decorator itself does **no**
80
+ self-test it trusts that strace tracing works in the sandbox, because "strace
81
+ works here" is an invariant of the (image + host kernel + seccomp/caps) that is
82
+ constant for the container's lifetime. Re-proving it per composition would re-pay
83
+ several host→container round-trips on every tool call for no new information.
84
+
85
+ Verifying the invariant is the consumer's **once-per-container** responsibility.
86
+ Run the probe once inside the sandbox at startup (e.g. a daemon boot gate) via
87
+ the lean leaf entry:
88
+
89
+ ```ts
90
+ import { selfTestStrace } from '@deepagents/context/sandbox/strace';
91
+
92
+ // Throws StraceUnavailableError (reason: 'strace-missing' | 'ptrace-blocked' |
93
+ // 'trace-unparseable') with no silent fallback.
94
+ await selfTestStrace();
95
+ ```
96
+
97
+ The `@deepagents/context/sandbox/strace` subpath is a small Node-runtime leaf
98
+ (probe + parser + `StraceUnavailableError`) with no agent/context-framework
99
+ imports, so a minimal daemon or bundled probe can import it without pulling the
100
+ whole framework. If the host process owns only a remote `DisposableSandbox`,
101
+ bundle this leaf entry with a tiny driver, write it into the sandbox, and run it
102
+ there with `node`.
103
+ Import `StraceUnavailableError` from this same subpath when catching the probe's
104
+ error (each entry point is bundled independently, so `instanceof` requires the
105
+ class from the same entry).
106
+
107
+ The backend must satisfy, on any non-virtual backend (Docker, Apple Container,
108
+ Daytona, e2b, ...):
72
109
 
73
110
  1. `strace` installed in the image — `apk add strace` (Alpine) /
74
111
  `apt-get install -y strace` (Debian), or `installers: [pkg(['strace'])]` for
75
112
  `createDockerSandbox`;
76
- 2. `ptrace` permitted by the runtime — the default on Docker and Daytona;
77
- 3. a **native-architecture** sandbox — amd64-under-Rosetta on Apple Silicon
113
+ 2. `ptrace` permitted by the runtime — the default on Docker, Apple Container,
114
+ and Daytona;
115
+ 3. a **native-architecture** sandbox — amd64-under-Rosetta on Apple silicon
78
116
  garbles the trace, so build the image for the host arch.
79
117
 
80
118
  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.
119
+ so it is unsupported by `withStraceFileChanges()` — `selfTestStrace` hard-fails
120
+ against it. Use a container/VM backend.
83
121
 
84
122
  Ops are intentionally coarse: strace cannot distinguish a new file from an
85
123
  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
  }
@@ -1856,7 +1840,7 @@ function isOutputAvailableToolPart(part) {
1856
1840
  return isStaticToolUIPart(part) && part.state === "output-available";
1857
1841
  }
1858
1842
  function isToolOutputReminderEnvelope(value) {
1859
- return isRecord(value) && "result" in value && typeof value.systemReminder === "string" && value.systemReminder.startsWith(SYSTEM_REMINDER_OPEN_TAG);
1843
+ return isRecord(value) && isRecord(value.meta) && value.meta.reminder === true && "result" in value && typeof value.systemReminder === "string";
1860
1844
  }
1861
1845
  function stripTextByRanges(text, ranges) {
1862
1846
  if (ranges.length === 0) {
@@ -1979,7 +1963,8 @@ function applyInlineReminder(message2, value) {
1979
1963
  };
1980
1964
  }
1981
1965
  function applyPartReminder(message2, value) {
1982
- const part = { type: "text", text: value };
1966
+ const reminderText = formatTaggedReminder(value);
1967
+ const part = { type: "text", text: reminderText };
1983
1968
  message2.parts.push(part);
1984
1969
  const partIndex = message2.parts.length - 1;
1985
1970
  return {
@@ -1988,13 +1973,10 @@ function applyPartReminder(message2, value) {
1988
1973
  target: "user",
1989
1974
  partIndex,
1990
1975
  start: 0,
1991
- end: value.length,
1976
+ end: reminderText.length,
1992
1977
  mode: "part"
1993
1978
  };
1994
1979
  }
1995
- function resolveReminderText(item, ctx) {
1996
- return resolveReminder(item, ctx)?.text ?? "";
1997
- }
1998
1980
  function normalizeReminderResolution(value) {
1999
1981
  if (typeof value === "string") {
2000
1982
  return value.trim().length === 0 ? null : { text: value };
@@ -2044,7 +2026,19 @@ function applyRemindersToToolOutput(output, texts) {
2044
2026
  if (texts.length === 0) return output;
2045
2027
  return {
2046
2028
  result: output === void 0 ? null : output,
2047
- systemReminder: formatTaggedReminder(texts.join("\n"))
2029
+ systemReminder: formatTaggedReminder(texts.join("\n")),
2030
+ meta: { reminder: true }
2031
+ };
2032
+ }
2033
+ function toToolReminderModelOutput(output, projectResult) {
2034
+ if (!isToolOutputReminderEnvelope(output)) return null;
2035
+ const projected = projectResult(output.result);
2036
+ return {
2037
+ type: "json",
2038
+ value: {
2039
+ result: isRecord(projected) ? projected.value : projected,
2040
+ systemReminder: output.systemReminder
2041
+ }
2048
2042
  };
2049
2043
  }
2050
2044
  function mergeReminderMetadata(message2, addedReminders) {
@@ -2057,54 +2051,32 @@ function mergeReminderMetadata(message2, addedReminders) {
2057
2051
  function reminder(textOrFragment, options) {
2058
2052
  const target = normalizeReminderTarget(options?.target);
2059
2053
  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") {
2054
+ if (options?.when === void 0 && target !== "user") {
2079
2055
  throw new Error(`Reminder target "${target}" requires a when predicate`);
2080
2056
  }
2081
- const text = normalizeImmediateReminderText(textOrFragment);
2057
+ const text = normalizeConditionalReminderText(textOrFragment);
2082
2058
  if (typeof text === "string") {
2083
2059
  assertReminderText(text);
2084
2060
  }
2085
2061
  return {
2086
- text,
2087
- asPart,
2088
- target
2062
+ name: "reminder",
2063
+ data: null,
2064
+ metadata: {
2065
+ reminder: {
2066
+ text,
2067
+ when: options?.when ?? (() => true),
2068
+ asPart,
2069
+ target
2070
+ }
2071
+ }
2089
2072
  };
2090
2073
  }
2091
- function user(content, ...reminders) {
2074
+ function user(content) {
2092
2075
  const message2 = typeof content === "string" ? {
2093
2076
  id: generateId2(),
2094
2077
  role: "user",
2095
2078
  parts: [{ type: "text", text: content }]
2096
2079
  } : { ...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
2080
  return {
2109
2081
  id: message2.id,
2110
2082
  name: "user",
@@ -2120,6 +2092,16 @@ function user(content, ...reminders) {
2120
2092
  }
2121
2093
  };
2122
2094
  }
2095
+ function applyUserRemindersToMessage(message2, reminders) {
2096
+ if (reminders.length === 0) return;
2097
+ const plainText = extractPlainText(message2);
2098
+ const added = [];
2099
+ for (const item of reminders) {
2100
+ const meta = applyReminderToMessage(message2, item, { content: plainText });
2101
+ if (meta) added.push(meta);
2102
+ }
2103
+ mergeReminderMetadata(message2, added);
2104
+ }
2123
2105
  function synthesizeSteerUserMessage(text, firedAt, onceIds = []) {
2124
2106
  const texts = Array.isArray(text) ? text : [text];
2125
2107
  for (const value of texts) assertReminderText(value);
@@ -2388,6 +2370,7 @@ export {
2388
2370
  applyPartReminder,
2389
2371
  applyReminderToMessage,
2390
2372
  applyRemindersToToolOutput,
2373
+ applyUserRemindersToMessage,
2391
2374
  assistant,
2392
2375
  assistantText,
2393
2376
  clarification,
@@ -2402,6 +2385,7 @@ export {
2402
2385
  fromFragment,
2403
2386
  getFragmentData,
2404
2387
  getModelsRegistry,
2388
+ getReminderOnceIds,
2405
2389
  getReminderRanges,
2406
2390
  glossary,
2407
2391
  guardrail,
@@ -2427,7 +2411,6 @@ export {
2427
2411
  render,
2428
2412
  resolveReminder,
2429
2413
  resolveReminderAsync,
2430
- resolveReminderText,
2431
2414
  role,
2432
2415
  runGuardrailChain,
2433
2416
  soul,
@@ -2439,6 +2422,7 @@ export {
2439
2422
  term,
2440
2423
  toFragment,
2441
2424
  toMessageFragment,
2425
+ toToolReminderModelOutput,
2442
2426
  user,
2443
2427
  visualizeGraph,
2444
2428
  workflow