@h-sandbox/deepagents 0.1.0-rc.1 → 0.1.0-rc.2

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 CHANGED
@@ -35,17 +35,28 @@ verifies the original tests and returns a diff after confirmed cleanup.
35
35
 
36
36
  **Developer preview on npm.** Install `@h-sandbox/deepagents@next` with the exact
37
37
  SDK and framework peers below. Native tools and an independently verified model
38
- repair have passed. Node 20+ is the consumer target; repository tooling uses
39
- Node 22+. This optional package has its own release cycle, separate from the SDK.
38
+ repair have passed. Use Node 22 or 24 LTS. Node 20 remains a legacy compatibility target, not a supported
39
+ Node.js production line. This optional package has its
40
+ own release cycle, separate from the SDK.
41
+
42
+ Minimal backend installation:
43
+
44
+ ```bash
45
+ npm install --save-exact @h-sandbox/deepagents@next @h-sandbox/sdk@0.5.0-rc.12 deepagents@1.14.0
46
+ ```
47
+
48
+ For the complete model and checkpoint examples, start with the full direct
49
+ dependency set together. Adding older framework pins after auto-installed peers
50
+ can leave duplicate LangGraph types in a pnpm lockfile.
40
51
 
41
52
  ```bash
42
53
  # In your server-side application:
43
- npm install --save-exact @h-sandbox/deepagents@next @h-sandbox/sdk@0.5.0-rc.11 \
54
+ npm install --save-exact @h-sandbox/deepagents@next @h-sandbox/sdk@0.5.0-rc.12 \
44
55
  deepagents@1.14.0 langchain@1.5.11 @langchain/core@1.2.12 \
45
56
  @langchain/langgraph@1.4.17 langsmith@0.9.0 zod@4.4.3
46
57
  ```
47
58
 
48
- The tested peers are **SDK 0.5.0-rc.11 and Deep Agents 1.14.0**, both exact because
59
+ The tested peers are **SDK 0.5.0-rc.12 and Deep Agents 1.14.0**, both exact because
49
60
  these preview contracts are evolving. `--save-exact` resolves the moving `next`
50
61
  channel to a concrete version; commit your application's lockfile. Other
51
62
  framework or SDK versions need a fresh compatibility run.
@@ -129,6 +140,13 @@ checkpointer, pause for approval and retrieve one command's result. It does not
129
140
  claim process-restart durability. Production applications supply a persistent
130
141
  checkpointer and their own authorized sandbox lookup.
131
142
 
143
+ The [persistent workflow guide](https://github.com/nabilblk/h-sandbox/blob/main/docs/integrations/reliable-framework-workflows.md)
144
+ adds a real `createDeepAgent`/PostgresSaver application with separate worker
145
+ processes, checkpoint-bound approval, acknowledged-command observation and
146
+ explicit retained-file recovery. Use adapter `0.1.0-rc.2` and SDK `0.5.0-rc.12`.
147
+ The application example is versioned source, not a database service in the package;
148
+ the guide shows how to run it against the published dependencies.
149
+
132
150
  The repair example requires **Node.js, Git,
133
151
  bash and GNU file tools** in the selected template. Its blocked egress policy
134
152
  requires enforcement support in your installation. It downloads no dependencies
@@ -176,10 +194,13 @@ distinguishes native qualification from package publication and deployment.
176
194
  exits are ordinary tool results. The adapter never retries a mutation.
177
195
  A framework or model can still request the same action again; approval and
178
196
  idempotent application operations are required where repeat effects matter.
179
- - Execution timeout, status-polling timeout and sandbox TTL are separate.
180
- Cancellation stops polling/between-file work, not a running remote command.
181
- Submission, file and log HTTP requests use the SDK transport; supply bounded
182
- `fetch` in client configuration if you need per-request transport deadlines.
197
+ - HTTP `requestTimeoutMs`, execution timeout,
198
+ observation timeout and sandbox TTL are separate. JSON HTTP requests default
199
+ to 120 seconds, including body reads. Per-call options and caller cancellation
200
+ also cover submissions, logs and transfers; the adapter's observation budget
201
+ includes final logs. None of these kill a remote command or authorize a retry.
202
+ Earlier rc.1 instead requires a bounded custom `fetch` for per-request
203
+ deadlines and its observation budget excludes final logs.
183
204
  - Output is stdout followed by stderr, not a chronological merge. The default
184
205
  combined UTF-8 log limit is 64 KiB; provider truncation flags remain visible.
185
206
  A fixed-size termination notice is outside that limit so timeouts/kills are
package/dist/backend.js CHANGED
@@ -21,7 +21,7 @@ export class HarakiriSandboxBackend extends BaseSandbox {
21
21
  constructor(sandbox, options = {}) {
22
22
  super();
23
23
  if (typeof sandbox.files.readBytes !== "function" || typeof sandbox.processes.connect !== "function") {
24
- throw new TypeError("This adapter requires Harakiri SDK 0.5.0-rc.11 or newer, not npm rc.10.");
24
+ throw new TypeError("This adapter requires its tested Harakiri SDK peer, 0.5.0-rc.12.");
25
25
  }
26
26
  this.#sandbox = sandbox;
27
27
  this.#execution = executionOptions(sandbox, options);
@@ -38,7 +38,7 @@ export class HarakiriSandboxBackend extends BaseSandbox {
38
38
  try {
39
39
  const process = await this.#sandbox.processes.start({
40
40
  command, cwd: this.#execution.cwd, timeoutMs: this.#execution.timeoutMs, detached: true
41
- });
41
+ }, { signal: this.#execution.signal, requestTimeoutMs: this.#execution.requestTimeoutMs });
42
42
  reference = process.reference;
43
43
  }
44
44
  catch (cause) {
@@ -76,7 +76,9 @@ export class HarakiriSandboxBackend extends BaseSandbox {
76
76
  results.push({ path, error: "invalid_path" });
77
77
  continue;
78
78
  }
79
- await this.#sandbox.files.write(this.#path(path), content, { createParents: true });
79
+ await this.#sandbox.files.write(this.#path(path), content, {
80
+ createParents: true, signal: this.#execution.signal, requestTimeoutMs: this.#execution.requestTimeoutMs
81
+ });
80
82
  results.push({ path, error: null });
81
83
  }
82
84
  catch (cause) {
@@ -101,13 +103,14 @@ export class HarakiriSandboxBackend extends BaseSandbox {
101
103
  continue;
102
104
  }
103
105
  const absolute = this.#path(path);
104
- const { file } = await this.#sandbox.files.stat(absolute);
106
+ const request = { signal: this.#execution.signal, requestTimeoutMs: this.#execution.requestTimeoutMs };
107
+ const { file } = await this.#sandbox.files.stat(absolute, request);
105
108
  if (file.type === "directory" || file.type === "dir") {
106
109
  results.push({ path, content: null, error: "is_directory" });
107
110
  continue;
108
111
  }
109
112
  this.#checkBytes(bytes + file.size);
110
- const content = await this.#sandbox.files.readBytes(absolute);
113
+ const content = await this.#sandbox.files.readBytes(absolute, request);
111
114
  bytes += content.byteLength;
112
115
  this.#checkBytes(bytes);
113
116
  results.push({ path, content, error: null });
@@ -1,16 +1,16 @@
1
- import type { HarakiriSandbox, SandboxCommandLogsResponse, SandboxCommandSummary } from "@h-sandbox/sdk";
1
+ import type { HarakiriSandbox, RequestOptions, SandboxCommandLogsResponse, SandboxCommandSummary } from "@h-sandbox/sdk";
2
2
  import type { ExecuteResponse } from "deepagents";
3
3
  import { type CommandReference } from "./errors.js";
4
- export type ExecutionOptions = {
4
+ export type ExecutionOptions = RequestOptions & {
5
5
  /** Remote execution budget. Defaults to the runtime's advertised command timeout. */
6
6
  timeoutMs?: number;
7
- /** Command-status polling budget, excluding log download; never extends TTL. */
7
+ /** Total observation budget, including final log download; never extends TTL. */
8
8
  observationTimeoutMs?: number;
9
9
  /** Combined UTF-8 log limit, excluding a fixed termination notice. Default 64 KiB; at most 1 MiB. */
10
10
  maxOutputBytes?: number;
11
11
  /** Must be absolute. This is a working directory, not a security boundary. */
12
12
  cwd?: string;
13
- /** Stops observation. It does not kill the remote command. */
13
+ /** Cancels local requests/observation. It does not kill the remote command. */
14
14
  signal?: AbortSignal;
15
15
  /** Persist the acknowledged reference before waiting. Never receives credentials. */
16
16
  onCommandStarted?: (reference: CommandReference) => void | Promise<void>;
@@ -25,8 +25,8 @@ export declare function executionOptions(sandbox: HarakiriSandbox, options: Exec
25
25
  timeoutMs: number;
26
26
  observationTimeoutMs: number;
27
27
  maxOutputBytes: number;
28
- /** Stops observation. It does not kill the remote command. */
29
28
  signal?: AbortSignal;
29
+ requestTimeoutMs?: number;
30
30
  /** Persist the acknowledged reference before waiting. Never receives credentials. */
31
31
  onCommandStarted?: (reference: CommandReference) => void | Promise<void>;
32
32
  };
package/dist/execution.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { HarakiriWaitTimeoutError } from "@h-sandbox/sdk";
1
2
  import { HarakiriExecutionError } from "./errors.js";
2
3
  export function positiveInteger(name, value, maximum = Number.MAX_SAFE_INTEGER) {
3
4
  if (!Number.isSafeInteger(value) || value < 1 || value > maximum) {
@@ -10,9 +11,11 @@ export function executionOptions(sandbox, options) {
10
11
  if (!cwd.startsWith("/") || cwd.includes("\0"))
11
12
  throw new TypeError("cwd must be an absolute path without NUL characters.");
12
13
  const timeoutMs = positiveInteger("timeoutMs", options.timeoutMs ?? sandbox.runtimeMetadata.limits.commandTimeoutMs);
14
+ if (options.requestTimeoutMs !== undefined)
15
+ positiveInteger("requestTimeoutMs", options.requestTimeoutMs, 2_147_483_647);
13
16
  return {
14
17
  ...options, cwd, timeoutMs,
15
- observationTimeoutMs: positiveInteger("observationTimeoutMs", options.observationTimeoutMs ?? timeoutMs + 10_000),
18
+ observationTimeoutMs: positiveInteger("observationTimeoutMs", options.observationTimeoutMs ?? timeoutMs + 10_000, 2_147_483_647),
16
19
  maxOutputBytes: positiveInteger("maxOutputBytes", options.maxOutputBytes ?? 65_536, 1_048_576)
17
20
  };
18
21
  }
@@ -54,13 +57,33 @@ export async function observeCommand(sandbox, reference, options) {
54
57
  }
55
58
  try {
56
59
  options.signal?.throwIfAborted();
60
+ const deadline = Date.now() + options.observationTimeoutMs;
57
61
  const { command } = await sandbox.processes.wait(reference.commandId, {
58
62
  statuses: ["succeeded", "failed", "killed"],
59
63
  timeoutMs: options.observationTimeoutMs,
64
+ requestTimeoutMs: options.requestTimeoutMs,
60
65
  signal: options.signal
61
66
  });
62
67
  options.signal?.throwIfAborted();
63
- const logs = await sandbox.processes.logs(reference.commandId);
68
+ const remainingMs = deadline - Date.now();
69
+ const timeout = () => new HarakiriWaitTimeoutError("Observation deadline elapsed during log retrieval. Reconnect using the command reference.", "command", reference.commandId, command.status);
70
+ if (remainingMs <= 0)
71
+ throw timeout();
72
+ const controller = new AbortController();
73
+ const signal = options.signal ? AbortSignal.any([options.signal, controller.signal]) : controller.signal;
74
+ const timer = setTimeout(() => controller.abort(timeout()), remainingMs);
75
+ let logs;
76
+ try {
77
+ logs = await sandbox.processes.logs(reference.commandId, {
78
+ signal, requestTimeoutMs: options.requestTimeoutMs
79
+ });
80
+ signal.throwIfAborted();
81
+ if (Date.now() >= deadline)
82
+ throw timeout();
83
+ }
84
+ finally {
85
+ clearTimeout(timer);
86
+ }
64
87
  options.signal?.throwIfAborted();
65
88
  const result = commandOutput(logs, options.maxOutputBytes);
66
89
  const notice = terminationNotice(command);
package/dist/lifecycle.js CHANGED
@@ -11,11 +11,13 @@ export async function withHarakiriSandbox(client, input, task, options = {}) {
11
11
  const cleanupTimeoutMs = positiveInteger("cleanupTimeoutMs", options.cleanupTimeoutMs ?? 90_000);
12
12
  options.backend?.signal?.throwIfAborted();
13
13
  // Retain the accepted handle before readiness so failures still enter cleanup.
14
- const sandbox = await client.sandboxes.create({ ...input, wait: false });
14
+ const sandbox = await client.sandboxes.create({ ...input, wait: false }, {
15
+ signal: options.backend?.signal, requestTimeoutMs: options.backend?.requestTimeoutMs
16
+ });
15
17
  let result;
16
18
  const failures = [];
17
19
  try {
18
- await sandbox.wait({ timeoutMs: readinessTimeoutMs, signal: options.backend?.signal });
20
+ await sandbox.wait({ timeoutMs: readinessTimeoutMs, signal: options.backend?.signal, requestTimeoutMs: options.backend?.requestTimeoutMs });
19
21
  const backend = new HarakiriSandboxBackend(sandbox, options.backend);
20
22
  result = await task({ sandbox, backend });
21
23
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@h-sandbox/deepagents",
3
- "version": "0.1.0-rc.1",
3
+ "version": "0.1.0-rc.2",
4
4
  "description": "Deep Agents sandbox backend for the Harakiri control plane.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -30,18 +30,21 @@
30
30
  "LICENSE"
31
31
  ],
32
32
  "peerDependencies": {
33
- "@h-sandbox/sdk": "0.5.0-rc.11",
33
+ "@h-sandbox/sdk": "0.5.0-rc.12",
34
34
  "deepagents": "1.14.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "deepagents": "1.14.0",
38
38
  "@langchain/core": "1.2.12",
39
39
  "@langchain/langgraph": "1.4.17",
40
+ "@langchain/langgraph-checkpoint-postgres": "1.0.5",
41
+ "@types/pg": "8.20.0",
40
42
  "langchain": "1.5.11",
41
43
  "langsmith": "0.9.0",
44
+ "pg": "8.21.0",
42
45
  "zod": "4.4.3",
43
46
  "tsx": "^4.23.13",
44
- "@h-sandbox/sdk": "0.5.0-rc.11"
47
+ "@h-sandbox/sdk": "0.5.0-rc.12"
45
48
  },
46
49
  "scripts": {
47
50
  "clean": "rm -rf dist",