Skip to content

feat(server): enforce worker launch policies on upstream 0.81.1 base - #1

Merged
max-miller1204 merged 35 commits into
mainfrom
server-compat
Jul 23, 2026
Merged

feat(server): enforce worker launch policies on upstream 0.81.1 base#1
max-miller1204 merged 35 commits into
mainfrom
server-compat

Conversation

@max-miller1204

Copy link
Copy Markdown
Owner

Intent

Synchronize the maintained Pi fork with upstream Pi 0.81.1 and establish the server-compat branch after upstream renamed packages/orchestrator to packages/server. Port worker launch-policy version 1 capability negotiation, strict role-specific tool allowlist validation, disabled extension/skill/prompt-template/context-file discovery, and exact applied-policy attestation into packages/server. Preserve ESM import-condition resolution of @earendil-works/pi-coding-agent/rpc-entry, keep the existing server CLI and server.sock behavior, document the security boundary, and add focused compatibility tests. This branch is consumed by pi-build-conductor through PI_SERVER_DIR and server.sock.

What Changed

  • Synchronized the fork with upstream Pi 0.81.1, picking up the packages/orchestrator -> packages/server rename, the new @earendil-works/pi-storage-sqlite-node SQLite session storage package, retry-policy support for compaction and branch summarization, release source archives, and regenerated model catalogs.
  • Ported worker launch-policy version 1 into packages/server: capability negotiation over server.sock, strict validation of spawn launchPolicy against role-specific tool allowlists, workers spawned with extension/skill/prompt-template/context-file discovery disabled, and exact appliedPolicy attestation echoed back and persisted, with ESM import-condition resolution of @earendil-works/pi-coding-agent/rpc-entry preserved and covered by new compatibility tests in packages/server/test/compatibility.test.ts.
  • Added the missing vitest devDependency to pi-server so its new test script runs under CI (auto-fixed by the pipeline, which verified the policy enforcement end-to-end over a live server.sock), and documented the fork-only launch-policy changelog decision and security boundary in the server package docs.

Risk Assessment

✅ Low: The feature is well-bounded, fails closed with strict exact-match validation, is documented and tested, and fully satisfies every required intent criterion; the only finding is a behavior-identical defense-in-depth hardening, not a current defect.

Testing

I ran the package's focused compatibility test (3/3 green) and then drove the real product surface end-to-end: a live server serve with PI_SERVER_DIR, talked to server.sock the way pi-build-conductor does, and captured a full CLI/socket transcript showing capability negotiation returning version 1, seven tampered launch policies each refused with "Invalid worker launch policy", review/implementation/repair spawns attesting the exact applied policy in the spawn response plus server list and persisted instances.json, policy-free spawns still working unchanged, and the socket cleaned up on shutdown. I recorded each worker process's own argv from inside the child, which shows the exact role allowlist and all four discovery-disabling flags actually delivered to the worker, and confirmed the runtime effect behaviorally: in a fixture project with a .pi skill and prompt template, the policy-free worker discovers them via get_commands while both policy workers discover nothing. I also confirmed the ESM import-condition resolution is both necessary and preserved (require.resolve fails with ERR_PACKAGE_PATH_NOT_EXPORTED where import.meta.resolve succeeds), and that dist is byte-identical after a rebuild so the E2E exercised the current source. This change has no UI, HTML, or rendered surface - it is a Unix-socket protocol plus CLI, so the reviewer-visible evidence is CLI/socket transcripts and captured worker command lines rather than screenshots. One limitation: without model API credentials I could not make a worker attempt a denied tool call, so the tool allowlist is evidenced as the exact flags delivered to each launched worker rather than by observing a blocked tool invocation; discovery disablement was verified behaviorally. Everything passed and the worktree is clean, with transient sandbox state removed.

Evidence: End-to-end server.sock + CLI transcript (capability negotiation, policy rejections, attestation, discovery effect, shutdown)

======================================================================
  1. existing server CLI still works
======================================================================
$ server --version
0.81.1

$ server --help
server v0.81.1

Usage:
  server serve
  server list
  server spawn [--cwd <path>] [--label <label>]
  server status <instance-id>
  server stop <instance-id>
  server rpc <instance-id> <json-command>
  server rpc-stream <instance-id>
  server --help
  server --version

RPC stream stdin expects JSONL RpcCommand or extension_ui_response messages.


======================================================================
  2. server.sock comes up under PI_SERVER_DIR (how pi-build-conductor finds it)
======================================================================
$ PI_SERVER_DIR=/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/server server serve &
radius integration disabled: login radius in ~/.pi/agent/auth.json or set RADIUS_API_KEY
server listening on /tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/server/server.sock
$ test -S $PI_SERVER_DIR/server.sock && echo 'socket present'
socket present


======================================================================
  3. capability negotiation
======================================================================
--> client asks what launch-policy versions the server speaks
$ printf '%s\n' '{"type":"capabilities"}' | socat - UNIX-CONNECT:$PI_SERVER_DIR/server.sock
{
  "type": "capabilities_result",
  "ok": true,
  "capabilities": {
    "workerLaunchPolicyVersions": [
      1
    ]
  }
}


======================================================================
  4. strict role allowlist validation - every tampered policy is refused
======================================================================
--> review role tries to add bash
$ printf '%s\n' '{"type":"spawn","cwd":"/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/work","launchPolicy":{"version":1,"role":"review","tools":["read","grep","find","ls","bash"],"resourceDiscovery":"disabled"}}' | socat - UNIX-CONNECT:$PI_SERVER_DIR/server.sock
{
  "type": "error",
  "ok": false,
  "error": "Invalid worker launch policy"
}

--> review role drops a tool from its fixed allowlist
$ printf '%s\n' '{"type":"spawn","cwd":"/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/work","launchPolicy":{"version":1,"role":"review","tools":["read","grep"],"resourceDiscovery":"disabled"}}' | socat - UNIX-CONNECT:$PI_SERVER_DIR/server.sock
{
  "type": "error",
  "ok": false,
  "error": "Invalid worker launch policy"
}

--> review role reorders its fixed allowlist
$ printf '%s\n' '{"type":"spawn","cwd":"/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/work","launchPolicy":{"version":1,"role":"review","tools":["grep","read","find","ls"],"resourceDiscovery":"disabled"}}' | socat - UNIX-CONNECT:$PI_SERVER_DIR/server.sock
{
  "type": "error",
  "ok": false,
  "error": "Invalid worker launch policy"
}

--> implementation allowlist smuggled onto the review role
$ printf '%s\n' '{"type":"spawn","cwd":"/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/work","launchPolicy":{"version":1,"role":"review","tools":["read","grep","find","ls","bash","edit","write"],"resourceDiscovery":"disabled"}}' | socat - UNIX-CONNECT:$PI_SERVER_DIR/server.sock
{
  "type": "error",
  "ok": false,
  "error": "Invalid worker launch policy"
}

--> unknown role
$ printf '%s\n' '{"type":"spawn","cwd":"/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/work","launchPolicy":{"version":1,"role":"root","tools":["read","grep","find","ls"],"resourceDiscovery":"disabled"}}' | socat - UNIX-CONNECT:$PI_SERVER_DIR/server.sock
{
  "type": "error",
  "ok": false,
  "error": "Invalid worker launch policy"
}

--> resource discovery re-enabled
$ printf '%s\n' '{"type":"spawn","cwd":"/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/work","launchPolicy":{"version":1,"role":"review","tools":["read","grep","find","ls"],"resourceDiscovery":"enabled"}}' | socat - UNIX-CONNECT:$PI_SERVER_DIR/server.sock
{
  "type": "error",
  "ok": false,
  "error": "Invalid worker launch policy"
}

--> unsupported policy version
$ printf '%s\n' '{"type":"spawn","cwd":"/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/work","launchPolicy":{"version":2,"role":"review","tools":["read","grep","find","ls"],"resourceDiscovery":"disabled"}}' | socat - UNIX-CONNECT:$PI_SERVER_DIR/server.sock
{
  "type": "error",
  "ok": false,
  "error": "Invalid worker launch policy"
}


======================================================================
  5. accepted policies - workers launch and the applied policy is attested back exactly
======================================================================
--> spawn review worker
$ printf '%s\n' '{"type":"spawn","cwd":"/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/work","label":"review-worker","launchPolicy":{"version":1,"role":"review","tools":["read","grep","find","ls"],"resourceDiscovery":"disabled"}}' | socat - UNIX-CONNECT:$PI_SERVER_DIR/server.sock
{
  "type": "spawn_result",
  "ok": true,
  "instance": {
    "id": "81a00068-1b3d-4fb3-8f12-8af2dce04ca2",
    "status": "online",
    "cwd": "/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/work",
    "label": "review-worker",
    "sessionId": "019f908b-95fe-7369-96d1-97c6a9ade087",
    "sessionFile": "/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/agent/sessions/--tmp-no-mistakes-evidence-01KY84F7F5YD7K82D2YY8F6BR3-sandbox-work--/2026-07-23T19-54-54-846Z_019f908b-95fe-7369-96d1-97c6a9ade087.jsonl",
    "appliedPolicy": {
      "version": 1,
      "role": "review",
      "tools": [
        "read",
        "grep",
        "find",
        "ls"
      ],
      "resourceDiscovery": "disabled"
    }
  }
}

--> spawn implementation worker
$ printf '%s\n' '{"type":"spawn","cwd":"/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/work","label":"impl-worker","launchPolicy":{"version":1,"role":"implementation","tools":["read","grep","find","ls","bash","edit","write"],"resourceDiscovery":"disabled"}}' | socat - UNIX-CONNECT:$PI_SERVER_DIR/server.sock
{
  "type": "spawn_result",
  "ok": true,
  "instance": {
    "id": "c4b2293b-1840-43db-9b89-8b413f86bb5b",
    "status": "online",
    "cwd": "/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/work",
    "label": "impl-worker",
    "sessionId": "019f908b-9805-76fe-8abf-bd010d2bd346",
    "sessionFile": "/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/agent/sessions/--tmp-no-mistakes-evidence-01KY84F7F5YD7K82D2YY8F6BR3-sandbox-work--/2026-07-23T19-54-55-365Z_019f908b-9805-76fe-8abf-bd010d2bd346.jsonl",
    "appliedPolicy": {
      "version": 1,
      "role": "implementation",
      "tools": [
        "read",
        "grep",
        "find",
        "ls",
        "bash",
        "edit",
        "write"
      ],
      "resourceDiscovery": "disabled"
    }
  }
}

--> spawn repair worker
$ printf '%s\n' '{"type":"spawn","cwd":"/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/work","label":"repair-worker","launchPolicy":{"version":1,"role":"repair","tools":["read","grep","find","ls","bash","edit","write"],"resourceDiscovery":"disabled"}}' | socat - UNIX-CONNECT:$PI_SERVER_DIR/server.sock
{
  "type": "spawn_result",
  "ok": true,
  "instance": {
    "id": "314eaedd-ae02-46ce-87f0-d3d64f592aae",
    "status": "online",
    "cwd": "/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/work",
    "label": "repair-worker",
    "sessionId": "019f908b-9a0c-7868-89f0-3ea6479cfb68",
    "sessionFile": "/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/agent/sessions/--tmp-no-mistakes-evidence-01KY84F7F5YD7K82D2YY8F6BR3-sandbox-work--/2026-07-23T19-54-55-884Z_019f908b-9a0c-7868-89f0-3ea6479cfb68.jsonl",
    "appliedPolicy": {
      "version": 1,
      "role": "repair",
      "tools": [
        "read",
        "grep",
        "find",
        "ls",
        "bash",
        "edit",
        "write"
      ],
      "resourceDiscovery": "disabled"
    }
  }
}

--> spawn with no launch policy (unchanged leg

... [8232 bytes truncated] ...

"online",
      "cwd": "/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/work",
      "label": "review-worker",
      "sessionId": "019f908b-95fe-7369-96d1-97c6a9ade087",
      "sessionFile": "/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/agent/sessions/--tmp-no-mistakes-evidence-01KY84F7F5YD7K82D2YY8F6BR3-sandbox-work--/2026-07-23T19-54-54-846Z_019f908b-95fe-7369-96d1-97c6a9ade087.jsonl",
      "appliedPolicy": {
        "version": 1,
        "role": "review",
        "tools": [
          "read",
          "grep",
          "find",
          "ls"
        ],
        "resourceDiscovery": "disabled"
      }
    },
    {
      "id": "c4b2293b-1840-43db-9b89-8b413f86bb5b",
      "status": "online",
      "cwd": "/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/work",
      "label": "impl-worker",
      "sessionId": "019f908b-9805-76fe-8abf-bd010d2bd346",
      "sessionFile": "/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/agent/sessions/--tmp-no-mistakes-evidence-01KY84F7F5YD7K82D2YY8F6BR3-sandbox-work--/2026-07-23T19-54-55-365Z_019f908b-9805-76fe-8abf-bd010d2bd346.jsonl",
      "appliedPolicy": {
        "version": 1,
        "role": "implementation",
        "tools": [
          "read",
          "grep",
          "find",
          "ls",
          "bash",
          "edit",
          "write"
        ],
        "resourceDiscovery": "disabled"
      }
    },
    {
      "id": "314eaedd-ae02-46ce-87f0-d3d64f592aae",
      "status": "online",
      "cwd": "/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/work",
      "label": "repair-worker",
      "sessionId": "019f908b-9a0c-7868-89f0-3ea6479cfb68",
      "sessionFile": "/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/agent/sessions/--tmp-no-mistakes-evidence-01KY84F7F5YD7K82D2YY8F6BR3-sandbox-work--/2026-07-23T19-54-55-884Z_019f908b-9a0c-7868-89f0-3ea6479cfb68.jsonl",
      "appliedPolicy": {
        "version": 1,
        "role": "repair",
        "tools": [
          "read",
          "grep",
          "find",
          "ls",
          "bash",
          "edit",
          "write"
        ],
        "resourceDiscovery": "disabled"
      }
    },
    {
      "id": "9b92139d-1e03-40a3-98b1-713a5b68d450",
      "status": "online",
      "cwd": "/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/work",
      "label": "legacy-worker",
      "sessionId": "019f908b-9c0a-7e53-8ade-cbdea756a2ce",
      "sessionFile": "/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/agent/sessions/--tmp-no-mistakes-evidence-01KY84F7F5YD7K82D2YY8F6BR3-sandbox-work--/2026-07-23T19-54-56-394Z_019f908b-9c0a-7e53-8ade-cbdea756a2ce.jsonl"
    }
  ]
}

$ cat $PI_SERVER_DIR/instances.json
[
  {
    "id": "81a00068-1b3d-4fb3-8f12-8af2dce04ca2",
    "status": "online",
    "cwd": "/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/work",
    "createdAt": "2026-07-23T19:54:54.388Z",
    "lastSeenAt": "2026-07-23T19:54:54.912Z",
    "label": "review-worker",
    "appliedPolicy": {
      "version": 1,
      "role": "review",
      "tools": [
        "read",
        "grep",
        "find",
        "ls"
      ],
      "resourceDiscovery": "disabled"
    },
    "sessionId": "019f908b-95fe-7369-96d1-97c6a9ade087",
    "sessionFile": "/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/agent/sessions/--tmp-no-mistakes-evidence-01KY84F7F5YD7K82D2YY8F6BR3-sandbox-work--/2026-07-23T19-54-54-846Z_019f908b-95fe-7369-96d1-97c6a9ade087.jsonl"
  },
  {
    "id": "c4b2293b-1840-43db-9b89-8b413f86bb5b",
    "status": "online",
    "cwd": "/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/work",
    "createdAt": "2026-07-23T19:54:54.913Z",
    "lastSeenAt": "2026-07-23T19:54:55.428Z",
    "label": "impl-worker",
    "appliedPolicy": {
      "version": 1,
      "role": "implementation",
      "tools": [
        "read",
        "grep",
        "find",
        "ls",
        "bash",
        "edit",
        "write"
      ],
      "resourceDiscovery": "disabled"
    },
    "sessionId": "019f908b-9805-76fe-8abf-bd010d2bd346",
    "sessionFile": "/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/agent/sessions/--tmp-no-mistakes-evidence-01KY84F7F5YD7K82D2YY8F6BR3-sandbox-work--/2026-07-23T19-54-55-365Z_019f908b-9805-76fe-8abf-bd010d2bd346.jsonl"
  },
  {
    "id": "314eaedd-ae02-46ce-87f0-d3d64f592aae",
    "status": "online",
    "cwd": "/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/work",
    "createdAt": "2026-07-23T19:54:55.429Z",
    "lastSeenAt": "2026-07-23T19:54:55.948Z",
    "label": "repair-worker",
    "appliedPolicy": {
      "version": 1,
      "role": "repair",
      "tools": [
        "read",
        "grep",
        "find",
        "ls",
        "bash",
        "edit",
        "write"
      ],
      "resourceDiscovery": "disabled"
    },
    "sessionId": "019f908b-9a0c-7868-89f0-3ea6479cfb68",
    "sessionFile": "/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/agent/sessions/--tmp-no-mistakes-evidence-01KY84F7F5YD7K82D2YY8F6BR3-sandbox-work--/2026-07-23T19-54-55-884Z_019f908b-9a0c-7868-89f0-3ea6479cfb68.jsonl"
  },
  {
    "id": "9b92139d-1e03-40a3-98b1-713a5b68d450",
    "status": "online",
    "cwd": "/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/work",
    "createdAt": "2026-07-23T19:54:55.950Z",
    "lastSeenAt": "2026-07-23T19:54:56.465Z",
    "label": "legacy-worker",
    "sessionId": "019f908b-9c0a-7e53-8ade-cbdea756a2ce",
    "sessionFile": "/tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/sandbox/agent/sessions/--tmp-no-mistakes-evidence-01KY84F7F5YD7K82D2YY8F6BR3-sandbox-work--/2026-07-23T19-54-56-394Z_019f908b-9c0a-7e53-8ade-cbdea756a2ce.jsonl"
  }
]


======================================================================
  9. stop the workers
======================================================================
$ server stop 81a00068-1b3d-4fb3-8f12-8af2dce04ca2
{
  "type": "stop_result",
  "ok": true,
  "instanceId": "81a00068-1b3d-4fb3-8f12-8af2dce04ca2"
}

$ server stop c4b2293b-1840-43db-9b89-8b413f86bb5b
{
  "type": "stop_result",
  "ok": true,
  "instanceId": "c4b2293b-1840-43db-9b89-8b413f86bb5b"
}

$ server stop 314eaedd-ae02-46ce-87f0-d3d64f592aae
{
  "type": "stop_result",
  "ok": true,
  "instanceId": "314eaedd-ae02-46ce-87f0-d3d64f592aae"
}

$ server stop 9b92139d-1e03-40a3-98b1-713a5b68d450
{
  "type": "stop_result",
  "ok": true,
  "instanceId": "9b92139d-1e03-40a3-98b1-713a5b68d450"
}


======================================================================
  VERDICT
======================================================================
PASS  capabilities advertises worker launch policy version 1
PASS  refused: review role tries to add bash -> "Invalid worker launch policy"
PASS  refused: review role drops a tool from its fixed allowlist -> "Invalid worker launch policy"
PASS  refused: review role reorders its fixed allowlist -> "Invalid worker launch policy"
PASS  refused: implementation allowlist smuggled onto the review role -> "Invalid worker launch policy"
PASS  refused: unknown role -> "Invalid worker launch policy"
PASS  refused: resource discovery re-enabled -> "Invalid worker launch policy"
PASS  refused: unsupported policy version -> "Invalid worker launch policy"
PASS  review spawn attests the exact applied policy
PASS  implementation spawn attests the exact applied policy
PASS  repair spawn attests the exact applied policy
PASS  spawn without a policy still works and attests no policy
PASS  a worker was launched with the review allowlist + all four discovery flags
PASS  a worker was launched with the implementation/repair allowlist + all four discovery flags
PASS  the policy-free worker was launched with no policy flags
PASS  workers resolved the ESM-only rpc-entry subpath
PASS  legacy worker discovers the project skill and prompt template
PASS  review-worker (policy) discovers no skills or prompt templates
PASS  impl-worker (policy) discovers no skills or prompt templates
PASS  server list surfaces appliedPolicy
PASS  persisted server state records appliedPolicy
PASS  socket cleaned up on shutdown

ALL CHECKS PASSED
Evidence: Exact command lines the worker processes were launched with (recorded inside each worker)

<repo>/packages/coding-agent/dist/rpc-entry.js --tools read,grep,find,ls --no-extensions --no-skills --no-prompt-templates --no-context-files <repo>/packages/coding-agent/dist/rpc-entry.js --tools read,grep,find,ls,bash,edit,write --no-extensions --no-skills --no-prompt-templates --no-context-files <repo>/packages/coding-agent/dist/rpc-entry.js --tools read,grep,find,ls,bash,edit,write --no-extensions --no-skills --no-prompt-templates --no-context-files <repo>/packages/coding-agent/dist/rpc-entry.js

<repo>/packages/coding-agent/dist/rpc-entry.js --tools read,grep,find,ls --no-extensions --no-skills --no-prompt-templates --no-context-files
<repo>/packages/coding-agent/dist/rpc-entry.js --tools read,grep,find,ls,bash,edit,write --no-extensions --no-skills --no-prompt-templates --no-context-files
<repo>/packages/coding-agent/dist/rpc-entry.js --tools read,grep,find,ls,bash,edit,write --no-extensions --no-skills --no-prompt-templates --no-context-files
<repo>/packages/coding-agent/dist/rpc-entry.js
Evidence: ESM import-condition resolution of @earendil-works/pi-coding-agent/rpc-entry

import.meta.resolve -> <repo>/packages/coding-agent/dist/rpc-entry.js require.resolve -> FAILED: ERR_PACKAGE_PATH_NOT_EXPORTED Package subpath './rpc-entry' is not defined by "exports" in node_modules/@earendil-works/pi-coding-agent/package.json

Why packages/server/src/rpc-process.ts must use import.meta.resolve, not require.resolve
(@earendil-works/pi-coding-agent exposes ./rpc-entry under an "import"-only export condition)

import.meta.resolve  -> /home/max/.no-mistakes/worktrees/e2ab91bba366/01KY84F7F5YD7K82D2YY8F6BR3/packages/coding-agent/dist/rpc-entry.js
require.resolve      -> FAILED: ERR_PACKAGE_PATH_NOT_EXPORTED
                       Package subpath './rpc-entry' is not defined by "exports" in /home/max/.no-mistakes/worktrees/e2ab91bba366/01KY84F7F5YD7K82D2YY8F6BR3/node_modules/@earendil-works/pi-coding-agent/package.json
Evidence: Capability negotiation and policy rejection over server.sock (excerpt)
$ printf '%s\n' '{"type":"capabilities"}' | socat - UNIX-CONNECT:$PI_SERVER_DIR/server.sock
{
"type": "capabilities_result",
"ok": true,
"capabilities": { "workerLaunchPolicyVersions": [ 1 ] }
}

--> review role tries to add bash
$ printf '%s\n' '{"type":"spawn","cwd":"...","launchPolicy":{"version":1,"role":"review","tools":["read","grep","find","ls","bash"],"resourceDiscovery":"disabled"}}' | socat - UNIX-CONNECT:$PI_SERVER_DIR/server.sock
{
"type": "error",
"ok": false,
"error": "Invalid worker launch policy"
}

--> spawn review worker
{
"type": "spawn_result",
"ok": true,
"instance": {
"id": "81a00068-1b3d-4fb3-8f12-8af2dce04ca2",
"status": "online",
"label": "review-worker",
"appliedPolicy": {
"version": 1,
"role": "review",
"tools": [ "read", "grep", "find", "ls" ],
"resourceDiscovery": "disabled"
}
}
}
Evidence: E2E driver script (reproducible)
// End-to-end driver for the pi-server worker launch policy.
// Simulates how pi-build-conductor talks to the server: PI_SERVER_DIR + server.sock.
import { spawn } from "node:child_process";
import { existsSync, readFileSync, writeFileSync } from "node:fs";
import { createConnection } from "node:net";
import { join } from "node:path";

const REPO = process.env.REPO;
const SERVER_DIR = process.env.PI_SERVER_DIR;
const WORK_DIR = process.env.WORK_DIR;
const ARGV_LOG = process.env.ARGV_LOG;
const SOCK = join(SERVER_DIR, "server.sock");
const CLI = join(REPO, "packages/server/dist/cli.js");

const out = [];
function say(line = "") {
	out.push(line);
	console.log(line);
}
function section(title) {
	say("");
	say("======================================================================");
	say(`  ${title}`);
	say("======================================================================");
}

function sendIpc(request) {
	return new Promise((resolve, reject) => {
		const socket = createConnection(SOCK);
		let buffer = "";
		socket.on("connect", () => socket.write(`${JSON.stringify(request)}\n`));
		socket.on("data", (chunk) => {
			buffer += chunk.toString();
		});
		socket.on("end", () => {
			const line = buffer.split("\n").find((l) => l.trim());
			resolve(line ? JSON.parse(line) : undefined);
		});
		socket.on("error", reject);
	});
}

async function ipcCall(label, request) {
	say(`--> ${label}`);
	say(`$ printf '%s\\n' '${JSON.stringify(request)}' | socat - UNIX-CONNECT:$PI_SERVER_DIR/server.sock`);
	const response = await sendIpc(request);
	say(JSON.stringify(response, null, 2));
	say("");
	return response;
}

function runCli(args) {
	return new Promise((resolve) => {
		const child = spawn(process.execPath, [CLI, ...args], { env: process.env, stdio: ["ignore", "pipe", "pipe"] });
		let stdout = "";
		let stderr = "";
		child.stdout.on("data", (c) => {
			stdout += c;
		});
		child.stderr.on("data", (c) => {
			stderr += c;
		});
		child.on("exit", (code) => resolve({ code, stdout, stderr }));
	});
}

async function cli(args) {
	say(`$ server ${args.map((a) => (a.includes(" ") || a.includes("{") ? `'${a}'` : a)).join(" ")}`);
	const result = await runCli(args);
	say(result.stdout.trimEnd());
	if (result.stderr.trim()) say(`[stderr] ${result.stderr.trimEnd()}`);
	say("");
	return result;
}

function recordedWorkerArgvs() {
	if (!existsSync(ARGV_LOG)) return [];
	return readFileSync(ARGV_LOG, "utf8")
		.split("\n")
		.filter(Boolean)
		.map((l) => JSON.parse(l))
		.filter((argv) => argv.some((a) => a.endsWith("rpc-entry.js")));
}

const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

const REVIEW = { version: 1, role: "review", tools: ["read", "grep", "find", "ls"], resourceDiscovery: "disabled" };
const IMPLEMENTATION = {
	version: 1,
	role: "implementation",
	tools: ["read", "grep", "find", "ls", "bash", "edit", "write"],
	resourceDiscovery: "disabled",
};
const REPAIR = {
	version: 1,
	role: "repair",
	tools: ["read", "grep", "find", "ls", "bash", "edit", "write"],
	resourceDiscovery: "disabled",
};

let serveProc;

async function main() {
	section("1. existing server CLI still works");
	await cli(["--version"]);
	await cli(["--help"]);

	section("2. server.sock comes up under PI_SERVER_DIR (how pi-build-conductor finds it)");
	say(`$ PI_SERVER_DIR=${SERVER_DIR} server serve &`);
	serveProc = spawn(process.execPath, [CLI, "serve"], { env: process.env, stdio: ["ignore", "pipe", "pipe"] });
	let serveLog = "";
	serveProc.stdout.on("data", (c) => {
		serveLog += c;
	});
	serveProc.stderr.on("data", (c) => {
		serveLog += c;
	});
	for (let i = 0; i < 100 && !existsSync(SOCK); i++) await sleep(100);
	await sleep(300);
	say(serveLog.trimEnd());
	say("$ test -S $PI_SERVER_DIR/server.sock && echo 'socket present'");
	say(existsSync(SOCK) ? "socket present" : "SOCKET MISSING");
	say("");

	section("3. capability negotiation");
	const caps = await ipcCall("client asks what launch-policy versions the server speaks", { type: "capabilities" });

	section("4. strict role allowlist validation - every tampered policy is refused");
	const rejects = [
		["review role tries to add bash", { ...REVIEW, tools: [...REVIEW.tools, "bash"] }],
		["review role drops a tool from its fixed allowlist", { ...REVIEW, tools: ["read", "grep"] }],
		["review role reorders its fixed allowlist", { ...REVIEW, tools: ["grep", "read", "find", "ls"] }],
		["implementation allowlist smuggled onto the review role", { ...REVIEW, tools: IMPLEMENTATION.tools }],
		["unknown role", { ...REVIEW, role: "root" }],
		["resource discovery re-enabled", { ...REVIEW, resourceDiscovery: "enabled" }],
		["unsupported policy version", { ...REVIEW, version: 2 }],
	];
	const rejectResults = [];
	for (const [label, launchPolicy] of rejects) {
		const response = await ipcCall(label, { type: "spawn", cwd: WORK_DIR, launchPolicy });
		rejectResults.push({ label, response });
	}

	section("5. accepted policies - workers launch and the applied policy is attested back exactly");
	const reviewSpawn = await ipcCall("spawn review worker", {
		type: "spawn",
		cwd: WORK_DIR,
		label: "review-worker",
		launchPolicy: REVIEW,
	});
	const implSpawn = await ipcCall("spawn implementation worker", {
		type: "spawn",
		cwd: WORK_DIR,
		label: "impl-worker",
		launchPolicy: IMPLEMENTATION,
	});
	const repairSpawn = await ipcCall("spawn repair worker", {
		type: "spawn",
		cwd: WORK_DIR,
		label: "repair-worker",
		launchPolicy: REPAIR,
	});
	const legacySpawn = await ipcCall("spawn with no launch policy (unchanged legacy behavior)", {
		type: "spawn",
		cwd: WORK_DIR,
		label: "legacy-worker",
	});

	await sleep(500);
	section("6. how the worker processes were actually launched (argv recorded inside each worker)");
	const workerArgvs = recordedWorkerArgvs();
	for (const argv of workerArgvs) {
		say(`  ${argv.map((a) => a.replace(REPO, "<repo>")).join(" ")}`);
	}
	say("");

	section("7. runtime effect: project skills / prompt templates the worker can see");
	say(`fixture project at ${WORK_DIR} contains:`);
	say("  .pi/skills/danger/SKILL.md      (skill: danger)");
	say("  .pi/prompts/leak.md             (prompt template: leak)");
	say("  AGENTS.md                       (context file)");
	say("");
	const commandRuns = {};
	for (const [name, spawnResponse] of [
		["legacy-worker (no policy)", legacySpawn],
		["review-worker (policy)", reviewSpawn],
		["impl-worker (policy)", implSpawn],
	]) {
		say(`--- ${name} ---`);
		const result = await cli(["rpc", spawnResponse.instance.id, '{"type":"get_commands"}']);
		commandRuns[name] = result.stdout;
	}

	section("8. attestation visible through the server CLI and persisted server state");
	const listResult = await cli(["list"]);
	say("$ cat $PI_SERVER_DIR/instances.json");
	const instancesJson = readFileSync(join(SERVER_DIR, "instances.json"), "utf8");
	say(instancesJson.trimEnd());
	say("");

	section("9. stop the workers");
	for (const spawnResponse of [reviewSpawn, implSpawn, repairSpawn, legacySpawn]) {
		if (spawnResponse?.instance?.id) await cli(["stop", spawnResponse.instance.id]);
	}

	return {
		caps,
		rejectResults,
		reviewSpawn,
		implSpawn,
		repairSpawn,
		legacySpawn,
		workerArgvs,
		commandRuns,
		listResult,
		instancesJson,
	};
}

let result;
try {
	result = await main();
} finally {
	if (serveProc) {
		serveProc.kill("SIGTERM");
		await sleep(800);
		serveProc.kill("SIGKILL");
	}
}

const failures = [];
const check = (name, condition, detail = "") => {
	say(`${condition ? "PASS" : "FAIL"}  ${name}${detail ? ` ${detail}` : ""}`);
	if (!condition) failures.push(name);
};

section("VERDICT");
check(
	"capabilities advertises worker launch policy version 1",
	JSON.stringify(result.caps) ===
		JSON.stringify({ type: "capabilities_result", ok: true, capabilities: { workerLaunchPolicyVersions: [1] } }),
);
for (const { label, response } of result.rejectResults) {
	check(
		`refused: ${label}`,
		response?.ok === false && response?.error === "Invalid worker launch policy",
		`-> ${JSON.stringify(response?.error)}`,
	);
}
for (const [role, policy, spawnResponse] of [
	["review", REVIEW, result.reviewSpawn],
	["implementation", IMPLEMENTATION, result.implSpawn],
	["repair", REPAIR, result.repairSpawn],
]) {
	check(
		`${role} spawn attests the exact applied policy`,
		JSON.stringify(spawnResponse?.instance?.appliedPolicy) === JSON.stringify(policy),
	);
}
check(
	"spawn without a policy still works and attests no policy",
	result.legacySpawn?.ok === true && result.legacySpawn?.instance?.appliedPolicy === undefined,
);

const joined = result.workerArgvs.map((a) => a.join(" "));
const DISCOVERY_FLAGS = ["--no-extensions", "--no-skills", "--no-prompt-templates", "--no-context-files"];
check(
	"a worker was launched with the review allowlist + all four discovery flags",
	joined.some((a) => a.includes("--tools read,grep,find,ls ") && DISCOVERY_FLAGS.every((f) => a.includes(f))),
);
check(
	"a worker was launched with the implementation/repair allowlist + all four discovery flags",
	joined.filter((a) => a.includes("--tools read,grep,find,ls,bash,edit,write")).length === 2 &&
		joined
			.filter((a) => a.includes("--tools read,grep,find,ls,bash,edit,write"))
			.every((a) => DISCOVERY_FLAGS.every((f) => a.includes(f))),
);
check(
	"the policy-free worker was launched with no policy flags",
	joined.some((a) => a.endsWith("rpc-entry.js")),
);
check(
	"workers resolved the ESM-only rpc-entry subpath",
	joined.length === 4 && joined.every((a) => a.includes("rpc-entry.js")),
);
check(
	"legacy worker discovers the project skill and prompt template",
	result.commandRuns["legacy-worker (no policy)"].includes("skill:danger") &&
		result.commandRuns["legacy-worker (no policy)"].includes('"leak"'),
);
for (const name of ["review-worker (policy)", "impl-worker (policy)"]) {
	check(
		`${name} discovers no skills or prompt templates`,
		!result.commandRuns[name].includes("skill:danger") && !result.commandRuns[name].includes('"leak"'),
	);
}
check("server list surfaces appliedPolicy", result.listResult.stdout.includes("appliedPolicy"));
check("persisted server state records appliedPolicy", result.instancesJson.includes("appliedPolicy"));
check("socket cleaned up on shutdown", !existsSync(SOCK));

say("");
say(failures.length === 0 ? "ALL CHECKS PASSED" : `FAILURES:\n - ${failures.join("\n - ")}`);

writeFileSync(process.env.TRANSCRIPT, `${out.join("\n")}\n`);
process.exit(failures.length === 0 ? 0 : 1);
- Outcome: 🔧 1 issue found → auto-fixed ✅ across 2 runs (30m5s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

⚠️ **Rebase** - 1 warning

Push main to origin, or rebase your branch onto origin/main, before gating.

⚠️ **Review** - 1 info
  • ℹ️ packages/server/src/rpc-process.ts:31 - getRpcSpawnCommand builds the enforced --tools sandbox allowlist from the client-supplied launchPolicy.tools rather than from the server-trusted ROLE_TOOLS[role] constant. It is behavior-identical today because assertWorkerLaunchPolicy (handler.ts:64) requires launchPolicy.tools to exactly equal ROLE_TOOLS[policy.role], so no drift is currently possible. As defense-in-depth for this sandbox boundary, prefer deriving the --tools value from ROLE_TOOLS[policy.role] and treating the client array purely as an assertion to validate; that way enforcement cannot drift from attestation if the validation is ever weakened (e.g. changed to a subset or order-insensitive check).
🔧 **Test** - 1 issue found → auto-fixed ✅
  • 🚨 packages/server/package.json:47 - The pi-server package added a test script (vitest --run) and test/compatibility.test.ts, but did not declare vitest as a devDependency (base packages/orchestrator had no test script). vitest is not hoisted to root, so npm run test --workspace @earendil-works/pi-server and root npm test (which CI runs) fail with exit 127 (vitest: not found) - the intent's required focused compatibility tests cannot run and CI's test job breaks. Auto-fixed by adding vitest: 4.1.9 (matching sibling packages and the repo exact-pin rule) to devDependencies and regenerating package-lock.json; npm hoists the now-shared vitest to root and dedups the three nested copies (that dedup, plus in-range bumps of vitest-only transitive deps picomatch/es-module-lexer/std-env, is the large lockfile diff). After the fix, npm ci --ignore-scripts succeeds and the compatibility test passes 3/3.
  • npm run test --workspace @earendil-works/pi-server --if-present - initially failed exit 127 (vitest not found); passes 3/3 after adding the vitest devDependency
  • packages/server/test/compatibility.test.ts via vitest 4.1.9 - 3/3 pass (capability v1, allowlist-expansion rejection, ESM rpc-entry + policy flags)
  • npm ci --ignore-scripts - exact CI install; succeeds after regenerating package-lock.json (confirms package.json/lock consistency)
  • Live server.sock round-trip: spawned real node dist/cli.js serve under a throwaway PI_SERVER_DIR (Radius disabled) and drove capabilities + two policy-violating spawn requests + list over the actual unix socket (socket-e2e.mjs)
  • Real IPC entry points via node --experimental-strip-types policy-driver.mjs: handleIpcRequest (capabilities/spawn-rejection/status attestation) + getRpcSpawnCommand per role (implementation/review/repair)
  • node dist/cli.js --version and --help from the built server binary (CLI behavior preserved)
  • Verified import.meta.resolve(&#39;@earendil-works/pi-coding-agent/rpc-entry&#39;) resolves to dist/rpc-entry.js under plain Node (production import condition)

🔧 Fix: add missing vitest devDependency to pi-server
✅ Re-checked - no issues remain.

  • npm run test --workspace @earendil-works/pi-server (vitest --run, packages/server/test/compatibility.test.ts, 3/3 pass)
  • npm run build --workspace @earendil-works/pi-server then md5sum diff of packages/server/dist -> identical, confirming the E2E ran against the current src
  • Manual E2E driver node /tmp/no-mistakes-evidence/01KY84F7F5YD7K82D2YY8F6BR3/e2e-launch-policy.mjs with PI_SERVER_DIR pointed at a sandbox: started server serve, verified server.sock creation and removal on shutdown
  • Raw UNIX-socket request {&#34;type&#34;:&#34;capabilities&#34;} -> {&#34;type&#34;:&#34;capabilities_result&#34;,&#34;ok&#34;:true,&#34;capabilities&#34;:{&#34;workerLaunchPolicyVersions&#34;:[1]}}
  • Seven tampered {&#34;type&#34;:&#34;spawn&#34;,...,&#34;launchPolicy&#34;:...} requests over server.sock (added bash, dropped tool, reordered allowlist, cross-role allowlist, unknown role, resourceDiscovery=enabled, version=2) -> all {&#34;ok&#34;:false,&#34;error&#34;:&#34;Invalid worker launch policy&#34;}
  • Accepted spawns for review / implementation / repair roles -> exact appliedPolicy echoed in spawn_result
  • Recorded each worker process's own process.argv via a NODE_OPTIONS preload -> rpc-entry.js --tools read,grep,find,ls --no-extensions --no-skills --no-prompt-templates --no-context-files (and the implementation/repair allowlist), policy-free worker launched with no policy flags
  • server rpc &lt;instance-id&gt; &#39;{&#34;type&#34;:&#34;get_commands&#34;}&#39; against a fixture project containing .pi/skills/danger, .pi/prompts/leak.md and AGENTS.md -> policy-free worker discovers the project skill and prompt template, policy workers discover neither
  • server list and cat $PI_SERVER_DIR/instances.json -> appliedPolicy persisted and surfaced
  • server --version, server --help, server stop &lt;instance-id&gt; -> unchanged CLI behavior
  • import.meta.resolve vs require.resolve for @earendil-works/pi-coding-agent/rpc-entry -> ESM resolves, CJS fails with ERR_PACKAGE_PATH_NOT_EXPORTED
🔧 **Document** - 1 issue found → auto-fixed ✅
  • ⚠️ packages/server/CHANGELOG.md:1 - The worker launch policy feature (capabilities negotiation, spawn launchPolicy, appliedPolicy attestation) has no CHANGELOG entry. AGENTS.md requires all new entries under a '## [Unreleased]' section, and no such section exists on this branch (the v0.81.1 release commit consumed it and no 'Add [Unreleased] section' commit followed). I did not add one: global instructions forbid manually modifying CHANGELOG.md files. Needs the maintainer to create the '## [Unreleased]' / '### Added' section, or an explicit decision that fork-only server-compat work stays out of the upstream changelog.

🔧 Fix: document fork-only launch policy changelog decision
✅ Re-checked - no issues remain.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

github-actions Bot and others added 30 commits July 20, 2026 20:00
…ce-expansion

update brace-expansion version
This PR:

- Adds retainedTail to compaction entries in the new agent harness so we don't have to walk up the tree for the 2000 tokens before compaction,
- Changes getPathToRoot to getPathToRootOrCompaction to only load until last compaction, as unnecessary to access all nodes where it is called,
- Adds a SQLite storage backend, in a separate packages/session-backend-sqlite, with a migration system and schemas as per on-site discussions: sessions to match session header messages (except for metadata, which I couldn't understand what it's used for or where it gets written, so I omitted it), session_entries for shared entry types as columns plus payload as a json for what remains, session_sequences to represent the append-only, serialized nature of the jsonl files, branch_entries to attribute nodes to branches (relationship one-to-many), and session_materialized with the session info (see /session in TUI) to act as a "cache" or quick-access for costs, message count, token info, labels, session name, and model-thinking-level config (e.g. for fast resume).
- This is compatible with the new agent harness Session abstraction.
fixes earendil-works#6647

compaction (auto & manual) and branch summarization retry on transient failures.
use the same retry policy from settings.
emit events for the tui to show indication of retries
…b-actions-versions

update deprecated github actions
…6647-retry-summary-requests-2

compaction & branch summarization follow retry policy
Keep streamFn required for typed callers while preserving the legacy runtime fallback for extensions that omit it.\n\nfixes earendil-works#6915
@max-miller1204
max-miller1204 merged commit c08917e into main Jul 23, 2026
3 of 4 checks passed
@max-miller1204
max-miller1204 deleted the server-compat branch July 23, 2026 23:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants