@naiba/pi-durable-background-tasks 0.1.1 → 0.2.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/README.md CHANGED
@@ -8,12 +8,17 @@ Supervised background shell commands for Pi Durable. No delegated agents, model
8
8
  bun add @naiba/pi-durable-background-tasks
9
9
  ```
10
10
 
11
+ Available on [npm](https://www.npmjs.com/package/@naiba/pi-durable-background-tasks), with GitHub Actions Trusted Publishing and provenance. Node.js 22+ is required; Bun loads the TypeScript source exports, while Node loads the compiled JavaScript.
12
+
13
+ This is an extension for **Pi Durable**, not an auto-discovered Pi coding-agent CLI plugin. The host must install the extension in its registry and own its lifecycle. It does not manage Claude Code or Codex background tasks.
14
+
11
15
  ## Usage
12
16
 
13
17
  ```ts
14
18
  import {BackgroundTasks} from "@naiba/pi-durable-background-tasks";
15
19
  import {backgroundTasksExtension} from "@naiba/pi-durable-background-tasks/pi";
16
20
 
21
+ // `registry` is your host's Pi Durable registry.
17
22
  const tasks = new BackgroundTasks("/persistent/session/background");
18
23
  registry.install(backgroundTasksExtension(tasks, "/workspace"));
19
24
  // Register your normal Pi beforeTool approval policy. This package does not bypass it.
@@ -23,7 +28,14 @@ await tasks.close();
23
28
  unsubscribe();
24
29
  ```
25
30
 
26
- Tools: `background_start`, `background_status`, `background_output`, `background_wait`, `background_stop`.
31
+ | Tool | Input | Purpose |
32
+ | --- | --- | --- |
33
+ | `background_start` | `command`, optional `name` | Start a session-owned shell command |
34
+ | `background_status` | `{}` | List commands and their recorded state |
35
+ | `background_output` | `id`, optional `offset`, `limit` | Read up to 64 KiB using byte offsets |
36
+ | `background_wait` | `id`, `timeout_ms` | Observe completion for a bounded interval |
37
+ | `background_stop` | `id` | Stop a command and await supervisor cleanup |
38
+
27
39
  Wait requires `timeout_ms` between 1 and 600000. Observation timeout/cancellation does not stop the command. Commands have no implicit execution deadline.
28
40
 
29
41
  The core API works without creating a model session. `start` requires a stable `requestId`; repeats return the original record and mismatching arguments are rejected. Recovered records never automatically relaunch a command. Use one service instance per directory and one writer process; concurrent writers are not supported.
@@ -38,6 +50,12 @@ Records and logs survive reopening; live process continuity across host restart
38
50
 
39
51
  Output reads are bounded to 64 KiB, with byte offsets. Logs are retained without automatic deletion; configure filesystem quotas at the host level. Commands run with inherited environment and host permissions. Do not expose this service directly to untrusted clients; isolate task directories by session and authorize every tool/HTTP action.
40
52
 
53
+ ## CloudCode integration
54
+
55
+ [CloudCode](https://github.com/naiba/cloudcode) consumes this npm package for Pi sessions. It provides a collapsed background-activity panel, on-demand logs, explicit stop controls, native Pi approval, and protection against releasing/deleting sessions with active commands. Closing a browser is not host shutdown and does not stop commands.
56
+
57
+ The package itself has no Web UI and does not automatically resume a model when a command completes. CloudCode's Claude Code/Codex ACP background activity adapters are not implemented by this package; their execution must remain native.
58
+
41
59
  ## Development
42
60
 
43
61
  ```sh
@@ -57,4 +75,4 @@ Releases use `.github/workflows/publish.yml` on pushed `v*` tags, with npm Trust
57
75
  - Workflow filename: `publish.yml`
58
76
  - Environment: leave empty (the workflow does not declare an environment)
59
77
 
60
- Update `package.json` to a new stable version, refresh `bun.lock` if needed, commit and push, then push the matching tag (for example, `v0.1.1` for version `0.1.1`). The workflow rejects mismatching or prerelease versions, runs type checks, build and process tests, then publishes publicly with provenance. Published versions are immutable: `0.1.0` already exists and must not be republished. Never move a published release tag. There is no manual-dispatch publish path.
78
+ Update `package.json` to a new stable version, refresh `bun.lock` if needed, commit and push, then push the matching tag (for example, `v0.1.2` for version `0.1.2`). The tag-triggered publish workflow first calls the reusable `test.yml` workflow on the same tag. Its publish job uses `needs: test`, so failed or cancelled tests prevent publication. Branch pushes and pull requests also run `test.yml`; tag pushes avoid a redundant standalone test run. After tests succeed, the publish job rejects mismatching or prerelease versions, rebuilds the package, then publishes publicly with provenance. Published versions are immutable: `0.1.0` and `0.1.1` already exist and must not be republished. Never move a published release tag. There is no manual-dispatch publish path.
package/dist/pi.js CHANGED
@@ -1,16 +1,17 @@
1
1
  import { defineExtension, defineTool } from "@earendil-works/pi-durable";
2
2
  import { Type } from "@earendil-works/pi-ai";
3
- const result = (value) => ({ content: [{ type: "text", text: JSON.stringify(value) }] });
3
+ // Pi 1.2 consumes output; content silently hides task IDs and status from the model.
4
+ const result = (value) => ({ output: [{ type: "text", text: JSON.stringify(value) }] });
4
5
  export function backgroundTasksExtension(service, cwd) {
5
6
  return defineExtension({ name: "background-tasks", tools: [
6
- defineTool({ name: "background_start", description: "Start a supervised background shell command. Returns immediately; no model delegation. Commands do not survive host loss. Approval belongs to the host tool policy.", replay: "unsafe",
7
+ defineTool({ name: "background_start", description: "Start a supervised background shell command. Returns immediately; continue independent work before checking results. Do not immediately wait when other work is available; no model delegation. Commands do not survive host loss. Approval belongs to the host tool policy.", replay: "unsafe",
7
8
  parameters: Type.Object({ command: Type.String(), name: Type.Optional(Type.String()) }),
8
9
  execute: async (args, api) => result(service.start({ ...args, cwd, requestId: String(api.taskId) })) }),
9
10
  defineTool({ name: "background_status", description: "List background commands owned by this session.", replay: "safe", parameters: Type.Object({}), execute: async () => result(service.list()) }),
10
11
  defineTool({ name: "background_output", description: "Read bounded background output using byte offsets.", replay: "safe",
11
12
  parameters: Type.Object({ id: Type.String(), offset: Type.Optional(Type.Integer({ minimum: 0 })), limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 65536 })) }),
12
13
  execute: async (args) => result(await service.output(args.id, args.offset, args.limit)) }),
13
- defineTool({ name: "background_wait", description: "Wait for a background command. Explicit timeout ends observation only, not execution.", replay: "safe",
14
+ defineTool({ name: "background_wait", description: "Blocking observation of a background command. Use only when the next step requires its result and no independent work remains; otherwise continue working and check status later. Explicit timeout ends observation only, not execution.", replay: "safe",
14
15
  parameters: Type.Object({ id: Type.String(), timeout_ms: Type.Integer({ minimum: 1, maximum: 600000 }) }),
15
16
  execute: async (args, api, ctx) => result(await service.wait(args.id, args.timeout_ms, ctx.abortSignal)) }),
16
17
  defineTool({ name: "background_stop", description: "Stop a session-owned command and reap its descendants. Returns after supervisor cleanup.", replay: "unsafe",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naiba/pi-durable-background-tasks",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Background shell tools for Pi Durable; no model delegation",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -36,14 +36,14 @@
36
36
  "check": "tsc --noEmit"
37
37
  },
38
38
  "peerDependencies": {
39
- "@earendil-works/pi-durable": "^1.0.4",
40
- "@earendil-works/pi-ai": "^1.0.4"
39
+ "@earendil-works/pi-durable": "^1.2.0",
40
+ "@earendil-works/pi-ai": "^1.2.0"
41
41
  },
42
42
  "devDependencies": {
43
- "@types/node": "^24.0.0",
44
- "@types/bun": "latest",
45
- "typescript": "~5.9.3",
46
- "@earendil-works/pi-durable": "1.0.4",
47
- "@earendil-works/pi-ai": "1.0.4"
43
+ "@types/node": "^26.6.5",
44
+ "@types/bun": "^1.4.3",
45
+ "typescript": "~7.0.2",
46
+ "@earendil-works/pi-durable": "1.2.0",
47
+ "@earendil-works/pi-ai": "1.2.0"
48
48
  }
49
49
  }
package/src/pi.ts CHANGED
@@ -1,17 +1,18 @@
1
1
  import {defineExtension,defineTool} from "@earendil-works/pi-durable";
2
2
  import {Type} from "@earendil-works/pi-ai";
3
3
  import {BackgroundTasks} from "./index.js";
4
- const result=(value:unknown)=>({content:[{type:"text" as const,text:JSON.stringify(value)}]});
4
+ // Pi 1.2 consumes output; content silently hides task IDs and status from the model.
5
+ const result=(value:unknown)=>({output:[{type:"text" as const,text:JSON.stringify(value)}]});
5
6
  export function backgroundTasksExtension(service:BackgroundTasks,cwd:string){
6
7
  return defineExtension({name:"background-tasks",tools:[
7
- defineTool({name:"background_start",description:"Start a supervised background shell command. Returns immediately; no model delegation. Commands do not survive host loss. Approval belongs to the host tool policy.",replay:"unsafe",
8
+ defineTool({name:"background_start",description:"Start a supervised background shell command. Returns immediately; continue independent work before checking results. Do not immediately wait when other work is available; no model delegation. Commands do not survive host loss. Approval belongs to the host tool policy.",replay:"unsafe",
8
9
  parameters:Type.Object({command:Type.String(),name:Type.Optional(Type.String())}),
9
10
  execute:async(args,api)=>result(service.start({...args,cwd,requestId:String(api.taskId)}))}),
10
11
  defineTool({name:"background_status",description:"List background commands owned by this session.",replay:"safe",parameters:Type.Object({}),execute:async()=>result(service.list())}),
11
12
  defineTool({name:"background_output",description:"Read bounded background output using byte offsets.",replay:"safe",
12
13
  parameters:Type.Object({id:Type.String(),offset:Type.Optional(Type.Integer({minimum:0})),limit:Type.Optional(Type.Integer({minimum:1,maximum:65536}))}),
13
14
  execute:async(args)=>result(await service.output(args.id,args.offset,args.limit))}),
14
- defineTool({name:"background_wait",description:"Wait for a background command. Explicit timeout ends observation only, not execution.",replay:"safe",
15
+ defineTool({name:"background_wait",description:"Blocking observation of a background command. Use only when the next step requires its result and no independent work remains; otherwise continue working and check status later. Explicit timeout ends observation only, not execution.",replay:"safe",
15
16
  parameters:Type.Object({id:Type.String(),timeout_ms:Type.Integer({minimum:1,maximum:600000})}),
16
17
  execute:async(args,api,ctx)=>result(await service.wait(args.id,args.timeout_ms,ctx.abortSignal))}),
17
18
  defineTool({name:"background_stop",description:"Stop a session-owned command and reap its descendants. Returns after supervisor cleanup.",replay:"unsafe",