@tryinget/pi-agent-registry 0.3.0 → 0.3.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
@@ -1,11 +1,11 @@
1
1
  ---
2
- summary: "Overview and operator contract for pi-agent-registry manifest inspection, immutable fleet lint, and the Phase-2 exact-task read-only dispatch contract."
2
+ summary: "Overview and operator contract for pi-agent-registry manifest inspection, immutable fleet lint, and Phase-2 dispatch / Phase-3 visible admission contracts."
3
3
  read_when:
4
4
  - "Starting work in this package workspace."
5
- - "Using agent_registry, pi-agent-registry-lint, dispatch_agent, or the agent manifest convention."
5
+ - "Using agent_registry, pi-agent-registry-lint, dispatch_agent, standing_agent_spawn, or the agent manifest convention."
6
6
  system4d:
7
- container: "Pi extension and CLI for standing-agent manifest/fleet observation and the Phase-2 dispatch contract."
8
- compass: "Make fleet contract drift visible and bind one provable read-only dispatch without owning lifecycle or spawn machinery."
7
+ container: "Pi extension and CLI for standing-agent manifest/fleet observation and bounded dispatch/visible admission contracts."
8
+ compass: "Make fleet contract drift visible and bind one provable read-only dispatch without owning lifecycle or transport machinery."
9
9
  engine: "Discover every candidate -> capture committed bytes -> lint deterministically -> authorize one exact task -> dispatch read-only through ASC."
10
10
  fog: "A green tool run can be mistaken for a healthy or authorized fleet, and one settled dispatch can be mistaken for general standing-agent enablement."
11
11
  ---
@@ -14,16 +14,16 @@ system4d:
14
14
 
15
15
  Standing-agent registry for Pi: reads `ai-society.agent/1` manifests for
16
16
  inspection, emits a bounded, immutable-observation fleet lint report, and owns
17
- the Fleet Phase-2 dispatch contract. It does not own agent creation,
18
- lifecycle, role acceptance, or execution machinery — spawn/session/capacity
19
- stay ASC-owned.
17
+ the Fleet Phase-2 dispatch and Phase-3 visible admission contracts. It does not
18
+ own agent creation, lifecycle, role acceptance, or transport machinery. Phase-2
19
+ execution stays ASC-owned; Phase-3 Ghostty transport stays little-helpers-owned.
20
20
 
21
- Status: **Fleet Phase 2 (AK 5132)**. Manifest/template/profile contracts and
22
- fleet lint are converged (Phase 1), and `dispatch_agent` now executes exactly
23
- one read-only standing-agent dispatch per `(agent, exact AK task)` pair with an
24
- immutable receipt and one typed AK evidence row. Visible Ghostty standing
25
- agents, lifecycle-v2 permit binding, and orchestrator fleet integration
26
- remain later fleet phases.
21
+ Status: **Fleet Phase 3 (AK 5133), locally implemented and live-dogfood verified**.
22
+ Phase-2 (AK 5132) read-only ASC dispatch is unchanged. Phase 3 adds one clean
23
+ visible standing-agent TUI admission for an exact claimed task, not lifecycle
24
+ authority. The real TUI driver real child TUI proof and passing package/release
25
+ gates are recorded below. AK owns closeout. This is not a published-feature
26
+ claim; AK 5134 remains separate.
27
27
 
28
28
  ## Operator surfaces
29
29
 
@@ -40,6 +40,9 @@ remain later fleet phases.
40
40
  - `dispatch_agent` — the Fleet Phase-2 exact-task read-only contract (see
41
41
  below); fails closed with `confirmed_no_effects` before any ASC identity,
42
42
  capacity, session, or spawn effect exists.
43
+ - `standing_agent_spawn` — Fleet Phase-3 exact-task clean visible admission
44
+ through the little-helpers Ghostty transport; no automatic retry or AK
45
+ evidence recording (see below).
43
46
  - `/agents` — concise operator listing.
44
47
 
45
48
  ## Fleet layout
@@ -195,6 +198,110 @@ records AK evidence; an undetectable modify-and-restore interval is not
195
198
  claimed absent. The dispatch does not authenticate the calling session as the
196
199
  AK claimant — lifecycle-v2 permit binding (Fleet Phase 4) tightens that.
197
200
 
201
+ ## Phase-3 clean visible admission (AK 5133)
202
+
203
+ `standing_agent_spawn { agent, task, objective, parentPeerTarget, reportBack?, cwd? }`
204
+ composes one clean Pi TUI session in a Ghostty tab/window. `task` is an exact
205
+ positive AK task id; `objective` is nonblank, bounded to 32 KiB UTF-8, and
206
+ read-only — never an unbound standby brief. `reportBack` defaults to
207
+ `intercom` and requires an exact `session-<UUID>` controller target, not
208
+ `parent`/`active` aliases. `manual` and `none` may omit `parentPeerTarget`.
209
+ `cwd` defaults to the origin repository root and must remain within that
210
+ same Git repository.
211
+
212
+ Admission gates and composition:
213
+
214
+ 1. Reject malformed requests, recursive standing-agent children, unavailable
215
+ version-1 transport, unknown agents, mutation tools, and any manifest
216
+ extensions. Declared tools must be a nonempty subset of `read,bash`;
217
+ `intercom` is an explicit added communication instrument, recorded
218
+ separately from declared tools.
219
+ 2. Observe the origin Git repository and authorize `ak task show`: the exact
220
+ task must be origin-repo-bound, claimed, and carry a live lease. This does
221
+ not authenticate the caller as the AK claimant.
222
+ 3. Require a clean agent repository and agreement between cached, freshly
223
+ parsed, committed, and current manifest/persona inputs. Compose the persona
224
+ plus advisory scope, selected skills, model, and manifest thinking request;
225
+ recheck these inputs and origin/agent stability around transport admission.
226
+ 4. Use `--offline --no-extensions --no-skills --no-prompt-templates`, then
227
+ explicit `--extension` paths for approved locally installed
228
+ `@tryinget/pi-peer-messaging` intercom and `@tryinget/pi-little-helpers`
229
+ session-presence entrypoints, and explicit `--skill` selections. No ambient
230
+ extension/skill discovery or controller conversation is inherited. Normal
231
+ cwd-bound AGENTS context is retained; this is not an empty-policy session.
232
+ `--offline` is a Pi startup flag, not network isolation: model/intercom
233
+ communication still occurs. Installed Pi supplies builtin `zai` (including
234
+ the dogfood model `zai/glm-5.3`); no ambient provider extension is needed.
235
+ Unsupported provider/bootstrap shapes fail closed.
236
+ 5. Persist an exclusive per-`(agent, task)` reservation before transport,
237
+ then reread the exact task/lease and recheck inputs, bootstrap entry bytes,
238
+ and origin/agent observations immediately before launch. Reservations are
239
+ scoped to the resolved visible-receipts directory, not fleet-global.
240
+ 6. Delegate to `@tryinget/pi-little-helpers/sidequest-launch` only when
241
+ `STANDING_AGENT_TRANSPORT_VERSION === 1` and `launchPiQuestSession` exists.
242
+ Registry owns composition/admission/receipts, not Ghostty or session machinery.
243
+ Publish a write-once `pi-agent-registry.visible-launch-receipt/1` receipt
244
+ (`0o400`, canonical self-digest, hard-link publication) after transport
245
+ observation; post-launch drift remains indeterminate rather than clean proof.
246
+
247
+ **Never retry automatically**, including `confirmed_no_effects`, reservation
248
+ failure, cancellation, crash, receipt-publication failure, or indeterminate
249
+ transport. Existing, corrupt, or partial reservations block admission and are
250
+ never automatically released. Supervise the returned run id; any further
251
+ attempt requires explicit owner disposition. Phase-2's three-attempt policy
252
+ is not a Phase-3 retry policy.
253
+
254
+ For intercom mode, the child must send one correlated `PEER_ACK` before any
255
+ file/context work, stop visibly with `ACK_FAILED` if sending fails, and send
256
+ one correlated `PEER_FINAL` when the bounded objective ends, then stop.
257
+ Admission is **not** session-start, ACK, read-only lifetime, task consumption,
258
+ or completion proof. Receipt fields for startup/ACK/completion remain
259
+ `unproven` even when separate live evidence exists. The launcher writes no
260
+ AK evidence; the parent owns reality-test evidence and closeout.
261
+
262
+ Limits: `bash` and scope/read-only instructions are advisory, not a sandbox.
263
+ Launch-window Git observations exclude ignored files, .git internals, external
264
+ surfaces, and modify-and-restore intervals. Bootstrap hashes cover approved
265
+ package manifests and entry files only, not settings/auth/models or transitive
266
+ import integrity. Skill selection/materialization is not whole-runtime
267
+ immutability. Manifest thinking is the **request**, not a guarantee of the
268
+ runtime level: Pi clamps to model-supported levels; live `medium` requested
269
+ became `high` on `zai/glm-5.3`.
270
+
271
+ ### Local evidence and release boundary (2026-09-07)
272
+
273
+ Real TUI driver → real child TUI proof, with immutable launch receipt,
274
+ independent runtime assertion output and bounded session observation:
275
+ [Phase-3 dogfood](docs/project/2026-09-07-fleet-phase3-dogfood.md).
276
+ AK evidence/task state remains authoritative.
277
+
278
+ - Run: `standingagent-mtqmbq5w-aeaf6023`; agent revision:
279
+ `dc354723482f0470ad287d1de3e067a72cd99a85`.
280
+ - Receipt: `visible-agent-adoption-steward.20260907T022519Z.8fb48fd0.launch-receipt.json`;
281
+ SHA-256: `6a37b218e3ea7dff14ec0a13e8230920585ddcbf83598d5d949d62828adffd15`.
282
+ - Child session: `01a079af-2ef4-762c-8bfc-228d0b411d4a`; observed PID
283
+ `189076`, `terminalBound` on `/dev/pts/11`, Ghostty surface
284
+ `0x516815b20d7e09a3`.
285
+ - Ghostty build: `origin-main492300cad`, actual version `1.3.2-main`;
286
+ this does **not** prove a literal 1.4 release.
287
+ - Correlated ACK `cce92533-53a7-49d0-bc42-0b808408aafe` and FINAL
288
+ `44cee2a7-9695-42f8-a76e-d4a08b000752` received; no duplicate protocol.
289
+ Child inspected four files read-only; no file or AK writes observed.
290
+ - Registry check: **134/134**; little-helpers check: **430/430**; both full
291
+ release checks pass, including installed-tarball smoke. The stale real-fleet
292
+ baseline was explicitly refreshed after independent HEAD-only reproduction:
293
+ engineering-core docs-only revision `51fc387` → `9225dd6`, unchanged profile
294
+ bytes, fleet revisions, diagnostics and counts. Assertions were not weakened.
295
+ - The new standing-agent reality assertion passes. The broader all-controller
296
+ observer check still detects an unrelated live process naming retired Ghostty
297
+ `9d8fbd15`; this task did not restart other sessions to hide that drift.
298
+
299
+ The version-1 export exists in local little-helpers **0.9.0 source**, not a
300
+ proven published release. Its next release must ship `./sidequest-launch`
301
+ and the version marker before registry publication can claim Phase 3. A packed
302
+ registry resolving older npm helpers fails closed `visible_transport_unavailable`.
303
+ See [dependency posture](docs/engineering.local.md#package-local-deltas-on-the-pi-ts-lane).
304
+
198
305
  ## Environment
199
306
 
200
307
  | Variable | Meaning |
@@ -203,6 +310,7 @@ AK claimant — lifecycle-v2 permit binding (Fleet Phase 4) tightens that.
203
310
  | `PI_AGENT_REGISTRY_EC_PROFILES` | engineering-core `skills/profiles.json` |
204
311
  | `PI_AGENT_REGISTRY_USER_SKILLS` | mutable user fallback for runtime extras; fleet lint does not call it immutable |
205
312
  | `PI_AGENT_REGISTRY_DISPATCH_RECEIPTS_DIR` | explicit dispatch-receipts directory (default `<pi-agent-dir>/dispatch-receipts`) |
313
+ | `PI_AGENT_REGISTRY_VISIBLE_LAUNCH_RECEIPTS_DIR` | Phase-3 receipts and persistent pair reservations (default `<pi-agent-dir>/visible-launch-receipts`) |
206
314
 
207
315
  ## Validation
208
316
 
@@ -219,7 +327,9 @@ smoke, and the Phase-2 dispatch contract: AK authorization matrix,
219
327
  write-once/tamper-evident receipts, settled-pair re-dispatch rejection, the
220
328
  three-attempt bound, ledger rename-integrity, read-only violation observation,
221
329
  recursion guard, ASC effect-receipt-first disposition derivation, and ASC
222
- request composition.
330
+ request composition. Phase-3 coverage adds explicit bootstrap/argv composition,
331
+ exact-task rechecks, drift/cancellation gates, durable pair reservation,
332
+ transport capability gating, and immutable admission receipts.
223
333
 
224
334
  Live dogfood (2026-08-31, AK 5132): `agent-adoption-steward` dispatched for
225
335
  task 5132 through a fresh one-shot Pi session. Attempt 1 failed closed at child
@@ -77,13 +77,22 @@ approval, Pi runtime install/reload proof, or monorepo owner authority.
77
77
 
78
78
  - Runtime baseline: Node 22+ running TypeScript sources directly; the
79
79
  package ships TS sources and has no dist build of its own.
80
- - Dependency posture: ASC is a semver-exact runtime dependency (`0.5.2`)
81
- resolved from the npm registry. It is deliberately not bundled: npm 12
82
- refuses to pack a package whose `overrides` (the `fast-xml-parser`
83
- security floor) affect a bundled subtree (EBUNDLEOVERRIDE), and ASC
84
- `0.5.2` itself has zero runtime dependencies, so consumers resolve it
85
- from the registry while the repo's own lock pins the integrity-checked
86
- tarball. The packed manifest contains no `file:` runtime dependency.
80
+ - Dependency posture: ASC and little-helpers are local runtime links in the
81
+ workspace (`file:../pi-autonomous-session-control` and
82
+ `file:../pi-little-helpers`), not the former npm-only ASC `0.5.2` pin.
83
+ The shared `pi-interaction/scripts/prepare-publish-manifest.mjs` prepack /
84
+ postpack hooks temporarily rewrite local runtime dependencies to the linked
85
+ packages' exact versions for publication and restore the workspace manifest.
86
+ Runtime `typebox` is pinned separately; the packed manifest must contain no
87
+ `file:` runtime dependency. The dependencies are not bundled; the existing
88
+ `fast-xml-parser` security override is retained.
89
+ - Publish capability is separate from version rewriting: Phase 2 requires
90
+ ASC's execution exports; Phase 3 requires little-helpers'
91
+ `./sidequest-launch` export with `STANDING_AGENT_TRANSPORT_VERSION === 1`.
92
+ That export is in current local little-helpers `0.9.0` source; the next
93
+ helpers release must ship it. A packed registry with older npm helpers
94
+ fails closed `visible_transport_unavailable`. Do not describe local tests
95
+ or successful packing as a published Phase-3 feature.
87
96
  - Tests intentionally read live workspace fixtures (the real steward agent
88
97
  repo and the real engineering-core `profiles.json`) to keep the
89
98
  convention honest against the fleet, not just against synthetic data.
@@ -1,29 +1,32 @@
1
1
  ---
2
- summary: "Design record for pi-agent-registry runtime inspection, Fleet Phase-1 immutable lint, and the Phase-2 exact-task read-only dispatch contract."
2
+ summary: "Design record for pi-agent-registry runtime inspection, Fleet Phase-1 immutable lint, and Phase-2 dispatch / Phase-3 visible admission contracts."
3
3
  read_when:
4
- - "Changing manifest loading, fleet discovery/lint, immutable observations, or dispatch posture."
4
+ - "Changing manifest loading, fleet discovery/lint, immutable observations, or dispatch/visible admission posture."
5
5
  - "Onboarding to the standing-agent fleet contract implementation."
6
6
  system4d:
7
7
  container: "Pi-side read-only registry and fleet observation package."
8
8
  compass: "Converge owner interfaces without absorbing lifecycle or execution authority."
9
- engine: "Capture committed bytes -> lint every candidate -> report stable diagnostics -> keep dispatch disabled."
9
+ engine: "Capture committed bytes -> lint every candidate -> report stable diagnostics -> authorize bounded dispatch/admission."
10
10
  fog: "Mutable paths and a green process exit can be mistaken for immutable fleet health."
11
11
  ---
12
12
 
13
- # pi-agent-registry — Phase-1 design record
13
+ # pi-agent-registry — fleet contract design record
14
14
 
15
15
  The monorepo manifest convention is the consumer contract. L0
16
16
  `tpl-agent-repo` owns birth/propagation shape, `softwareco-agents` owns
17
17
  fleet/role/lifecycle conventions, engineering-core owns profile keys/members,
18
- AK owns task/evidence/decision truth, and ASC owns any future execution.
18
+ AK owns task/evidence/decision truth, ASC owns Phase-2 execution, and
19
+ little-helpers owns Phase-3 visible Ghostty transport.
19
20
 
20
- This package owns two read-only Pi-side surfaces and one bounded execution
21
- contract:
21
+ This package owns two read-only Pi-side surfaces and two bounded composition
22
+ contracts:
22
23
 
23
24
  1. mutable manifest inspection for already loadable agents;
24
25
  2. aggregate immutable-observation fleet lint;
25
26
  3. the Fleet Phase-2 exact-task read-only dispatch contract (authorization,
26
- receipt, evidence) with all spawn/session/capacity machinery ASC-owned.
27
+ receipt, evidence) with all Phase-2 spawn/session/capacity machinery ASC-owned;
28
+ 4. the Fleet Phase-3 exact-task clean visible admission contract, with
29
+ little-helpers-owned transport and no automatic AK evidence.
27
30
 
28
31
  None of these surfaces grants standing-agent lifecycle authority.
29
32
 
@@ -80,6 +83,21 @@ src/dispatch-request.ts
80
83
 
81
84
  src/dispatch.ts + src/sessions-dir.ts
82
85
  Phase-2 pipeline with fail-closed gates; sessions delegated to ASC
86
+
87
+ extensions/standing-agent-spawn.ts + src/visible-launch.ts
88
+ Phase-3 tool registration and exact-task admission/recheck pipeline
89
+
90
+ src/visible-launch-contract.ts + visible-launch-compose.ts
91
+ clean child argv, manifest persona/model/thinking request, ACK/FINAL brief
92
+
93
+ src/visible-launch-inputs.ts + visible-launch-bootstrap.ts
94
+ clean agent byte agreement and approved installed bootstrap entry checks
95
+
96
+ src/visible-launch-admission.ts + visible-launch-receipt.ts
97
+ persistent exclusive agent/task reservation and write-once launch observation
98
+
99
+ src/visible-launch-transport.ts
100
+ version-1 little-helpers sidequest-launch capability gate, not transport code
83
101
  ```
84
102
 
85
103
  ## Manifest compatibility
@@ -218,7 +236,7 @@ failure still fails closed with `confirmed_no_effects`:
218
236
  request and creates the runtime through ASC's exported
219
237
  `createAscExecutionRuntime` + `resolveSubagentSessionsDir` + model
220
238
  selection; the registry supplies only the `extraSkillProfileResolver` seam
221
- (`skillProfile = agent name`). No spawn/session/capacity code lives here.
239
+ (`skillProfile = agent name`). No Phase-2 spawn/session/capacity code lives here.
222
240
  `src/sessions-dir.ts` now delegates to ASC instead of the Phase-0
223
241
  quarantine.
224
242
  - **One level deep**: dispatched children carry
@@ -256,11 +274,65 @@ so dash-led prompts (persona front matter) require the registry's dispatch
256
274
  header envelope — a pi/ASC transport hardening candidate for a later slice
257
275
  (first observed live 2026-08-31 as child exit `Unknown option: ---`).
258
276
 
277
+ ## Phase-3 clean visible admission (AK 5133)
278
+
279
+ **Locally implemented and live-dogfood verified; not published.**
280
+ `standing_agent_spawn { agent, task, objective, parentPeerTarget, reportBack?, cwd? }`
281
+ adds visible TUI admission without changing Phase-2 ASC dispatch. Full operator
282
+ contract and compact evidence identifiers live in the
283
+ [README](../../README.md#phase-3-clean-visible-admission-ak-5133) and the
284
+ [2026-09-07 dogfood evidence](2026-09-07-fleet-phase3-dogfood.md), including
285
+ receipt and real-runtime assertion output. AK owns evidence and closeout.
286
+
287
+ The exact task must be claimed with a live lease in the observed origin repo;
288
+ child cwd cannot cross that repo boundary. Clean committed manifest/persona
289
+ inputs must agree with cached, fresh, and resolved inputs. After exclusive
290
+ persistent pair reservation, task/lease facts and origin/agent/bootstrap inputs
291
+ are rechecked immediately before transport and inputs are observed afterward.
292
+ An unresolved, corrupt, partial, or admitted reservation blocks another
293
+ admission in that receipts directory. Never retry automatically — including
294
+ no-effects, reservation failure, cancellation, crash, or publication failure;
295
+ explicit owner disposition is required. Phase-2 attempt limits do not apply.
296
+
297
+ Composition uses `--offline --no-extensions --no-skills --no-prompt-templates`
298
+ plus approved installed intercom/presence entrypoints and explicitly selected
299
+ skills. Normal cwd-bound AGENTS context remains; controller conversation and
300
+ ambient extension/skill discovery do not. Builtin `zai` is supported without
301
+ an ambient provider extension. Manifest thinking is requested via argv, then
302
+ Pi applies its supported-level clamp (live `medium` → `high` on
303
+ `zai/glm-5.3`). Settings/auth/models, transitive imports, and full skill/runtime
304
+ integrity are not proven by the approved-entry hashes. Offline startup is not
305
+ network isolation; `bash`/scope are advisory, not a sandbox.
306
+
307
+ Registry composes; little-helpers' exported `sidequest-launch` owns visible
308
+ transport and must advertise `STANDING_AGENT_TRANSPORT_VERSION === 1`.
309
+ The export is present in local helpers 0.9.0 source but must ship in its next
310
+ release; older npm helpers make packed registry admission fail closed. No
311
+ claim of published availability follows from local tests.
312
+
313
+ The immutable launch receipt records transport admission and bounded
314
+ launch-window observations, never session-start/ACK/completion proof; those
315
+ fields remain `unproven`. Intercom mode requires an exact controller
316
+ `session-<UUID>`, one first-action `PEER_ACK`, then one closing `PEER_FINAL`
317
+ and stop; manual/none modes may omit the target. No automatic AK evidence is
318
+ written. Verified live proof used a real TUI driver and real child TUI,
319
+ correlated ACK/FINAL without duplicates, and four read-only file inspections
320
+ with no file/AK writes observed. Ghostty was `1.3.2-main`
321
+ (`origin-main492300cad`), not a literal 1.4 release.
322
+
323
+ Final package checks: registry **134/134**, helpers **430/430**, and both full
324
+ release checks pass. The real-fleet pin was explicitly refreshed after independent
325
+ HEAD-only reproduction of engineering-core's docs-only `51fc387` → `9225dd6`
326
+ drift; profile bytes, fleet revisions, diagnostics and assertions are unchanged.
327
+ The new standing-agent reality assertion passes; the broader observer test still
328
+ reports an unrelated process on retired Ghostty `9d8fbd15`. AK5134 remains separate.
329
+
259
330
  ## Real fleet baseline
260
331
 
261
332
  The real `~/ai-society/agents/agent-*` walk is revision-bound in
262
- `tests/fixtures/real-fleet-lint-baseline.json`. The known unhealthy result is a
263
- successful implementation proof:
333
+ `tests/fixtures/real-fleet-lint-baseline.json`. The original Phase-1 unhealthy
334
+ result recorded these historical observations (not a claim about current
335
+ external HEADs):
264
336
 
265
337
  - four canonical repositories observed;
266
338
  - three missing manifests;
@@ -283,5 +355,7 @@ slice rather than silently turning green.
283
355
  - real fleet test: exact commits, profile bytes, diagnostics, and report digest;
284
356
  - extension test: `action=lint` returns observation-only unhealthy baseline;
285
357
  - CLI test: JSON and exit taxonomy;
286
- - packed smoke: shipped CLI/module/action plus Phase-0 dispatch gate;
358
+ - packed smoke: shipped CLI/module/action and fail-closed dependency capability gates;
359
+ - Phase-3 tests: argv/bootstrap composition, exact-task and input rechecks,
360
+ persistent reservation, cancellation/drift, and immutable launch receipts;
287
361
  - package check, fresh Pi dogfood, root loop gates, and independent review.
@@ -10,6 +10,7 @@ import { Type } from "typebox";
10
10
  import { dispatchAgent } from "../src/dispatch.ts";
11
11
  import { lintAgentFleet } from "../src/fleet-lint.ts";
12
12
  import { type AgentRegistry, AgentRegistryError, createAgentRegistry } from "../src/registry.ts";
13
+ import { registerStandingAgentSpawnTool } from "./standing-agent-spawn.ts";
13
14
 
14
15
  interface RegistryHandle {
15
16
  registry: AgentRegistry;
@@ -375,4 +376,6 @@ Phase-2 boundaries: standing-agent dispatch is one level deep (children carry PI
375
376
  };
376
377
  },
377
378
  });
379
+
380
+ registerStandingAgentSpawnTool({ pi, getRegistry });
378
381
  }
@@ -0,0 +1,85 @@
1
+ // summary: register exact-task visible admission; no transport/session machinery here.
2
+ import { StringEnum } from "@earendil-works/pi-ai";
3
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
4
+ import { Type } from "typebox";
5
+ import type { AgentRegistry } from "../src/registry.ts";
6
+ import { spawnStandingAgentVisible } from "../src/visible-launch.ts";
7
+
8
+ export function registerStandingAgentSpawnTool({
9
+ pi,
10
+ getRegistry,
11
+ }: {
12
+ pi: ExtensionAPI;
13
+ getRegistry: () => Promise<AgentRegistry>;
14
+ }): void {
15
+ pi.registerTool({
16
+ name: "standing_agent_spawn",
17
+ label: "Standing Agent Visible Admission (Phase 3)",
18
+ description:
19
+ "Request ONE clean visible Pi TUI session in a Ghostty tab/window (Fleet Phase 3), composed as a read-only standing agent for an exact claimed AK task in this origin repository. Requires a nonblank bounded objective, clean committed agent inputs and trusted explicit ACK/presence/provider bootstrap. Atomically reserves the agent/task pair before transport: unresolved or admitted launches cannot be retried automatically, even after cancellation or crashes. Explicit owner disposition is required. Transport admission does NOT prove session startup, ACK, read-only lifetime behavior, task consumption or completion; no AK evidence is written. Read/bash posture is advisory, not a sandbox.",
20
+ promptSnippet:
21
+ "Request one exact-task read-only visible standing-agent admission, not task completion.",
22
+ promptGuidelines: [
23
+ "standing_agent_spawn requires agent discovery, an exact claimed AK task, a bounded read-only objective and an exact controller session id for intercom report-back.",
24
+ "Never retry standing_agent_spawn automatically for a reserved/admitted or indeterminate agent/task pair; supervise the returned runId.",
25
+ ],
26
+ parameters: Type.Object({
27
+ agent: Type.String({ pattern: "^[a-z][a-z0-9-]{0,63}$" }),
28
+ task: Type.Integer({
29
+ minimum: 1,
30
+ description: "Exact claimed AK task in the origin repository.",
31
+ }),
32
+ objective: Type.String({
33
+ minLength: 1,
34
+ maxLength: 32_768,
35
+ description:
36
+ "Bounded read-only inspection/report objective, maximum 32 KiB UTF-8. No standby or mutation.",
37
+ }),
38
+ reportBack: Type.Optional(
39
+ StringEnum(["intercom", "manual", "none"] as const, { description: "Default intercom." }),
40
+ ),
41
+ parentPeerTarget: Type.Optional(
42
+ Type.String({ description: "Exact controller session id; required for intercom." }),
43
+ ),
44
+ cwd: Type.Optional(
45
+ Type.String({
46
+ description:
47
+ "Existing directory within the exact task's origin repository; defaults to that root.",
48
+ }),
49
+ ),
50
+ }),
51
+ async execute(_toolCallId, params, signal, _onUpdate, ctx) {
52
+ const registry = await getRegistry();
53
+ const outcome = await spawnStandingAgentVisible(
54
+ params,
55
+ { registry, pi },
56
+ {
57
+ cwd: ctx.cwd,
58
+ ...(ctx.model ? { model: ctx.model as { provider?: string; id?: string } } : {}),
59
+ },
60
+ signal,
61
+ );
62
+ const text = outcome.ok
63
+ ? `Transport admitted (${outcome.launchMode}), run ${outcome.runId}. Session start, ACK and task completion remain unproven.\nReceipt: ${outcome.receiptPath}`
64
+ : `${outcome.message}${outcome.runId ? `\nSupervision run: ${outcome.runId}` : ""}`;
65
+ return {
66
+ content: [{ type: "text" as const, text }],
67
+ details: {
68
+ ok: outcome.ok,
69
+ phase: outcome.phase,
70
+ ...(outcome.runId ? { runId: outcome.runId } : {}),
71
+ ...(outcome.ok
72
+ ? { admission: outcome.admission }
73
+ : {
74
+ reason: outcome.reason,
75
+ effectDisposition: outcome.effectDisposition,
76
+ spawnAttempted: outcome.spawnAttempted,
77
+ }),
78
+ ...(outcome.receiptPath ? { receiptPath: outcome.receiptPath } : {}),
79
+ ...(outcome.receipt ? { receiptSha256: outcome.receipt.receiptSha256 } : {}),
80
+ },
81
+ ...(!outcome.ok ? { isError: true } : {}),
82
+ };
83
+ },
84
+ });
85
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tryinget/pi-agent-registry",
3
- "version": "0.3.0",
4
- "description": "Pi standing-agent manifest inspection, immutable fleet lint, and the Fleet Phase-2 exact-task read-only dispatch contract",
3
+ "version": "0.3.2",
4
+ "description": "Pi standing-agent manifest inspection, immutable fleet lint, the Fleet Phase-2 exact-task read-only dispatch contract, and the Fleet Phase-3 clean visible standing-agent launch",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",
7
7
  "repository": {
@@ -57,6 +57,7 @@
57
57
  },
58
58
  "files": [
59
59
  "extensions/pi-agent-registry.ts",
60
+ "extensions/standing-agent-spawn.ts",
60
61
  "src",
61
62
  "scripts/fleet-lint.mjs",
62
63
  "prompts",
@@ -84,9 +85,9 @@
84
85
  },
85
86
  "devDependencies": {
86
87
  "@biomejs/biome": "2.3.14",
87
- "@earendil-works/pi-agent-core": "0.84.3",
88
- "@earendil-works/pi-ai": "0.84.3",
89
- "@earendil-works/pi-coding-agent": "0.84.3",
88
+ "@earendil-works/pi-agent-core": "0.84.4",
89
+ "@earendil-works/pi-ai": "0.84.4",
90
+ "@earendil-works/pi-coding-agent": "0.84.4",
90
91
  "@types/node": "^25.3.3",
91
92
  "@typescript/native-preview": "7.0.0-dev.20260417.1"
92
93
  },
@@ -100,6 +101,7 @@
100
101
  },
101
102
  "dependencies": {
102
103
  "@tryinget/pi-autonomous-session-control": "0.7.0",
103
- "typebox": "1.3.7"
104
+ "typebox": "1.3.7",
105
+ "@tryinget/pi-little-helpers": "0.10.0"
104
106
  }
105
107
  }
@@ -0,0 +1,69 @@
1
+ // summary: durable exclusive Phase-3 pair reservation; no automatic release or retry, including crashes.
2
+ import { constants } from "node:fs";
3
+ import { mkdir, open, readFile, realpath } from "node:fs/promises";
4
+ import { join } from "node:path";
5
+ import { canonicalJsonString, sha256Hex } from "./dispatch-receipt.ts";
6
+ import {
7
+ resolveVisibleLaunchReceiptsDir,
8
+ validateVisibleLaunchIdentity,
9
+ } from "./visible-launch-receipt.ts";
10
+
11
+ export async function reserveVisibleLaunchPair(
12
+ input: {
13
+ agent: string;
14
+ task: number;
15
+ runId: string;
16
+ requestSha256: string;
17
+ },
18
+ dir?: string,
19
+ ): Promise<{ reserved: true; path: string } | { reserved: false; runId?: string }> {
20
+ validateVisibleLaunchIdentity(input.agent, input.runId);
21
+ if (
22
+ !Number.isSafeInteger(input.task) ||
23
+ input.task <= 0 ||
24
+ !/^[a-f0-9]{64}$/u.test(input.requestSha256)
25
+ ) {
26
+ throw new Error("Invalid launch reservation identity");
27
+ }
28
+ const home = resolveVisibleLaunchReceiptsDir(dir);
29
+ await mkdir(home, { recursive: true, mode: 0o700 });
30
+ const canonicalHome = await realpath(home);
31
+ const path = join(canonicalHome, `pair-${input.agent}.ak-${input.task}.reservation.json`);
32
+ let handle: Awaited<ReturnType<typeof open>>;
33
+ try {
34
+ handle = await open(path, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY, 0o600);
35
+ } catch (error) {
36
+ if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
37
+ // Corrupt/incomplete reservations also block. Never infer an expired process or retry permission.
38
+ try {
39
+ const previous = JSON.parse(await readFile(path, "utf8"));
40
+ validateVisibleLaunchIdentity(previous.agent, previous.runId);
41
+ if (previous.agent === input.agent && previous.task === input.task)
42
+ return { reserved: false, runId: previous.runId };
43
+ } catch {
44
+ /* fail closed */
45
+ }
46
+ return { reserved: false };
47
+ }
48
+ try {
49
+ const facts = {
50
+ schema: "pi-agent-registry.visible-launch-reservation/1",
51
+ ...input,
52
+ recordedAt: new Date().toISOString(),
53
+ };
54
+ await handle.writeFile(
55
+ `${canonicalJsonString({ ...facts, sha256: sha256Hex(canonicalJsonString(facts)) })}\n`,
56
+ );
57
+ await handle.sync();
58
+ await handle.chmod(0o400);
59
+ } finally {
60
+ await handle.close();
61
+ }
62
+ const directory = await open(canonicalHome, constants.O_RDONLY);
63
+ try {
64
+ await directory.sync();
65
+ } finally {
66
+ await directory.close();
67
+ }
68
+ return { reserved: true, path };
69
+ }