@phyzess/pappra 0.1.0-beta.13 → 0.1.0-beta.18
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 +134 -15
- package/assets/integrations/skills/pappra/SKILL.md +28 -7
- package/assets/integrations/skills/pappra/references/collaboration.md +633 -0
- package/assets/integrations/skills/pappra/references/environment.md +360 -0
- package/assets/integrations/skills/pappra/references/hosts.md +20 -0
- package/assets/integrations/skills/pappra/references/operations.md +36 -5
- package/dist/adapters/pi/recovery-extension.d.ts +65 -0
- package/dist/adapters/pi/recovery-extension.js +238 -0
- package/dist/adapters/pi/session-transition-extension.d.ts +30 -0
- package/dist/adapters/pi/session-transition-extension.js +265 -0
- package/dist/app/automatic-handoff-events.d.ts +26 -0
- package/dist/app/automatic-handoff-events.js +134 -0
- package/dist/app/bounded-file.d.ts +2 -0
- package/dist/app/bounded-file.js +2 -0
- package/dist/app/capability-evidence/claude.d.ts +37 -0
- package/dist/app/capability-evidence/claude.js +206 -0
- package/dist/app/capability-evidence/codex.d.ts +35 -0
- package/dist/app/capability-evidence/codex.js +193 -0
- package/dist/app/capability-evidence/index.d.ts +33 -0
- package/dist/app/capability-evidence/index.js +117 -0
- package/dist/app/capability-evidence/pi.d.ts +73 -0
- package/dist/app/capability-evidence/pi.js +669 -0
- package/dist/app/capability-evidence/shared.d.ts +69 -0
- package/dist/app/capability-evidence/shared.js +195 -0
- package/dist/app/collab-auto-handoff.d.ts +55 -0
- package/dist/app/collab-auto-handoff.js +384 -0
- package/dist/app/collab-bindings.d.ts +57 -0
- package/dist/app/collab-bindings.js +1006 -0
- package/dist/app/collab-bootstrap-transport.d.ts +163 -0
- package/dist/app/collab-bootstrap-transport.js +769 -0
- package/dist/app/collab-bootstrap.d.ts +137 -0
- package/dist/app/collab-bootstrap.js +3455 -0
- package/dist/app/collab-handoff.d.ts +12 -0
- package/dist/app/collab-handoff.js +511 -0
- package/dist/app/collab-messages.d.ts +2 -0
- package/dist/app/collab-messages.js +2 -0
- package/dist/app/collab-notify.d.ts +156 -0
- package/dist/app/collab-notify.js +1208 -0
- package/dist/app/collab-preflight.d.ts +34 -0
- package/dist/app/collab-preflight.js +96 -0
- package/dist/app/collab-recovery-executor.d.ts +161 -0
- package/dist/app/collab-recovery-executor.js +1153 -0
- package/dist/app/collab-recovery-projection.d.ts +103 -0
- package/dist/app/collab-recovery-projection.js +659 -0
- package/dist/app/collab-recovery.d.ts +25 -0
- package/dist/app/collab-recovery.js +429 -0
- package/dist/app/collab-resources.d.ts +16 -0
- package/dist/app/collab-resources.js +278 -0
- package/dist/app/collab-role-events.d.ts +22 -0
- package/dist/app/collab-role-events.js +329 -0
- package/dist/app/collab-role-orchestration.d.ts +53 -0
- package/dist/app/collab-role-orchestration.js +601 -0
- package/dist/app/collab-role-plan.d.ts +81 -0
- package/dist/app/collab-role-plan.js +504 -0
- package/dist/app/collab-role-repair.d.ts +109 -0
- package/dist/app/collab-role-repair.js +821 -0
- package/dist/app/collab-rollover.d.ts +263 -0
- package/dist/app/collab-rollover.js +1112 -0
- package/dist/app/collab-route.d.ts +64 -0
- package/dist/app/collab-route.js +240 -0
- package/dist/app/collab-session-transition.d.ts +221 -0
- package/dist/app/collab-session-transition.js +3334 -0
- package/dist/app/collab-state.d.ts +7 -0
- package/dist/app/collab-state.js +162 -0
- package/dist/app/collab-task-store.d.ts +22 -0
- package/dist/app/collab-task-store.js +204 -0
- package/dist/app/collab-work-execution.d.ts +127 -0
- package/dist/app/collab-work-execution.js +1027 -0
- package/dist/app/collab-work-plan.d.ts +87 -0
- package/dist/app/collab-work-plan.js +437 -0
- package/dist/app/collaboration.d.ts +8 -2
- package/dist/app/collaboration.js +101 -12
- package/dist/app/doctor.js +108 -5
- package/dist/app/host-mcp.js +34 -8
- package/dist/app/host-probe.js +4 -4
- package/dist/app/init.d.ts +58 -4
- package/dist/app/init.js +768 -155
- package/dist/app/managed-pi-bootstrap-admission.d.ts +14 -0
- package/dist/app/managed-pi-bootstrap-admission.js +104 -0
- package/dist/app/managed-pi-bootstrap-refresh.d.ts +10 -0
- package/dist/app/managed-pi-bootstrap-refresh.js +78 -0
- package/dist/app/managed-pi-delivery.d.ts +8 -0
- package/dist/app/managed-pi-delivery.js +110 -0
- package/dist/app/managed-pi-handoff-receiver.d.ts +15 -0
- package/dist/app/managed-pi-handoff-receiver.js +263 -0
- package/dist/app/managed-pi-handoff-reference.d.ts +3 -0
- package/dist/app/managed-pi-handoff-reference.js +60 -0
- package/dist/app/managed-pi-herdr.d.ts +21 -0
- package/dist/app/managed-pi-herdr.js +185 -0
- package/dist/app/managed-pi-initial-admission.d.ts +28 -0
- package/dist/app/managed-pi-initial-admission.js +416 -0
- package/dist/app/managed-pi-initial-launch.d.ts +5 -0
- package/dist/app/managed-pi-initial-launch.js +61 -0
- package/dist/app/managed-pi-native-observation.d.ts +36 -0
- package/dist/app/managed-pi-native-observation.js +191 -0
- package/dist/app/managed-pi-readiness.d.ts +29 -0
- package/dist/app/managed-pi-readiness.js +288 -0
- package/dist/app/managed-pi-repair-stop.d.ts +21 -0
- package/dist/app/managed-pi-repair-stop.js +371 -0
- package/dist/app/managed-pi-resume-admission.d.ts +32 -0
- package/dist/app/managed-pi-resume-admission.js +618 -0
- package/dist/app/managed-pi-retirement.d.ts +28 -0
- package/dist/app/managed-pi-retirement.js +112 -0
- package/dist/app/managed-pi-sdk-control.d.ts +7 -0
- package/dist/app/managed-pi-sdk-control.js +169 -0
- package/dist/app/managed-pi-sdk-runtime.d.ts +3 -0
- package/dist/app/managed-pi-sdk-runtime.js +483 -0
- package/dist/app/managed-pi-source-admission.d.ts +14 -0
- package/dist/app/managed-pi-source-admission.js +323 -0
- package/dist/app/managed-pi-transition-host.d.ts +36 -0
- package/dist/app/managed-pi-transition-host.js +292 -0
- package/dist/app/managed-pi.d.ts +54 -0
- package/dist/app/managed-pi.js +248 -0
- package/dist/app/shell-readiness.d.ts +66 -0
- package/dist/app/shell-readiness.js +368 -0
- package/dist/app/task-pin.d.ts +2 -23
- package/dist/app/task-pin.js +2 -113
- package/dist/cli/commands/collaboration-binding.d.ts +13 -0
- package/dist/cli/commands/collaboration-binding.js +118 -0
- package/dist/cli/commands/collaboration-bootstrap.d.ts +10 -0
- package/dist/cli/commands/collaboration-bootstrap.js +84 -0
- package/dist/cli/commands/collaboration-capabilities.d.ts +13 -0
- package/dist/cli/commands/collaboration-capabilities.js +127 -0
- package/dist/cli/commands/collaboration-handoff.d.ts +13 -0
- package/dist/cli/commands/collaboration-handoff.js +102 -0
- package/dist/cli/commands/collaboration-message.d.ts +17 -0
- package/dist/cli/commands/collaboration-message.js +177 -0
- package/dist/cli/commands/collaboration-plan.d.ts +8 -0
- package/dist/cli/commands/collaboration-plan.js +108 -0
- package/dist/cli/commands/collaboration-preflight.d.ts +10 -0
- package/dist/cli/commands/collaboration-preflight.js +167 -0
- package/dist/cli/commands/collaboration-recovery-run.d.ts +46 -0
- package/dist/cli/commands/collaboration-recovery-run.js +224 -0
- package/dist/cli/commands/collaboration-recovery.d.ts +11 -0
- package/dist/cli/commands/collaboration-recovery.js +104 -0
- package/dist/cli/commands/collaboration-resources.d.ts +11 -0
- package/dist/cli/commands/collaboration-resources.js +96 -0
- package/dist/cli/commands/collaboration-role-plan.d.ts +9 -0
- package/dist/cli/commands/collaboration-role-plan.js +69 -0
- package/dist/cli/commands/collaboration-role-run.d.ts +11 -0
- package/dist/cli/commands/collaboration-role-run.js +128 -0
- package/dist/cli/commands/collaboration-rollover.d.ts +10 -0
- package/dist/cli/commands/collaboration-rollover.js +103 -0
- package/dist/cli/commands/collaboration-state.d.ts +10 -0
- package/dist/cli/commands/collaboration-state.js +96 -0
- package/dist/cli/commands/collaboration-transition.d.ts +10 -0
- package/dist/cli/commands/collaboration-transition.js +75 -0
- package/dist/cli/commands/collaboration-work-run.d.ts +34 -0
- package/dist/cli/commands/collaboration-work-run.js +178 -0
- package/dist/cli/commands/collaboration.d.ts +4 -0
- package/dist/cli/commands/collaboration.js +39 -4
- package/dist/cli/commands/continuity.js +2 -6
- package/dist/cli/commands/docs.d.ts +2 -0
- package/dist/cli/commands/docs.js +89 -15
- package/dist/cli/commands/host.d.ts +7 -1
- package/dist/cli/commands/host.js +122 -7
- package/dist/cli/commands/init.d.ts +20 -3
- package/dist/cli/commands/init.js +897 -232
- package/dist/cli/commands/mcp.d.ts +1 -0
- package/dist/cli/commands/mcp.js +11 -5
- package/dist/cli/commands/repair.d.ts +12 -0
- package/dist/cli/commands/repair.js +41 -0
- package/dist/cli/commands/runtime.d.ts +1 -1
- package/dist/cli/commands/runtime.js +139 -38
- package/dist/cli/commands/session.d.ts +7 -0
- package/dist/cli/commands/session.js +82 -0
- package/dist/cli/commands/task.js +19 -5
- package/dist/cli/run.js +866 -24
- package/dist/code/identity.d.ts +7 -0
- package/dist/code/identity.js +7 -3
- package/dist/code/registration.js +7 -0
- package/dist/code/service.d.ts +9 -0
- package/dist/code/service.js +16 -3
- package/dist/code/text.js +19 -7
- package/dist/collab/admission.d.ts +91 -66
- package/dist/collab/admission.js +21 -6
- package/dist/collab/canonical.d.ts +1 -1
- package/dist/collab/canonical.js +44 -3
- package/dist/collab/envelope.d.ts +2 -2
- package/dist/collab/envelope.js +12 -12
- package/dist/collab/first-work.d.ts +3 -0
- package/dist/collab/first-work.js +15 -0
- package/dist/collab/handoff.d.ts +16 -0
- package/dist/collab/handoff.js +39 -5
- package/dist/collab/index.d.ts +97 -1
- package/dist/collab/index.js +9 -1
- package/dist/collab/messages.js +126 -3
- package/dist/collab/persisted-messages.d.ts +123 -0
- package/dist/collab/persisted-messages.js +496 -0
- package/dist/collab/preflight.d.ts +1 -1
- package/dist/collab/resources.d.ts +13 -3
- package/dist/collab/resources.js +37 -6
- package/dist/collab/rollover.d.ts +311 -0
- package/dist/collab/rollover.js +1024 -0
- package/dist/collab/store.d.ts +1 -0
- package/dist/collab/store.js +1 -4
- package/dist/collab/types.d.ts +2 -354
- package/dist/collab/validation.d.ts +2 -10
- package/dist/collab/validation.js +2 -303
- package/dist/collab/work-plan.d.ts +105 -0
- package/dist/collab/work-plan.js +843 -0
- package/dist/continuity/context.d.ts +11 -2
- package/dist/continuity/context.js +100 -13
- package/dist/continuity/handoff-bundle.d.ts +296 -0
- package/dist/continuity/handoff-bundle.js +477 -0
- package/dist/continuity/index.d.ts +2 -0
- package/dist/continuity/index.js +1 -0
- package/dist/continuity/paths.js +10 -2
- package/dist/continuity/service.js +91 -15
- package/dist/continuity/task-pin.d.ts +24 -0
- package/dist/continuity/task-pin.js +590 -0
- package/dist/doctor.d.ts +1 -0
- package/dist/doctor.js +6 -0
- package/dist/experimental/index.d.ts +13 -0
- package/dist/experimental/index.js +25 -0
- package/dist/experimental/pi.d.ts +4 -0
- package/dist/experimental/pi.js +12 -0
- package/dist/hosts/claude/native.js +1 -1
- package/dist/hosts/codex/native.js +1 -1
- package/dist/hosts/codex/protocol.js +1 -1
- package/dist/hosts/integration/bootstrap-config.d.ts +58 -0
- package/dist/hosts/integration/bootstrap-config.js +254 -0
- package/dist/hosts/integration/bundle-export.d.ts +44 -0
- package/dist/hosts/integration/bundle-export.js +366 -3
- package/dist/hosts/integration/bundle.js +3 -0
- package/dist/hosts/integration/hook.js +36 -9
- package/dist/hosts/integration/pappra-bootstrap-extension.mjs +80 -0
- package/dist/hosts/integration/pappra-extension-runtime.mjs +59 -24631
- package/dist/hosts/integration/pappra-hook.mjs +61 -24749
- package/dist/hosts/integration/pappra-recovery-extension.mjs +259 -0
- package/dist/hosts/integration/pappra-session-transition-extension.mjs +276 -0
- package/dist/hosts/integration/pi-bootstrap-extension.d.ts +36 -0
- package/dist/hosts/integration/pi-bootstrap-extension.js +101 -0
- package/dist/hosts/integration/pi-bootstrap-guard.d.ts +125 -0
- package/dist/hosts/integration/pi-bootstrap-guard.js +438 -0
- package/dist/hosts/integration/pi-bootstrap-runtime.d.ts +63 -0
- package/dist/hosts/integration/pi-bootstrap-runtime.js +223 -0
- package/dist/hosts/integration/pi-native-events.d.ts +112 -0
- package/dist/hosts/integration/pi-native-events.js +348 -0
- package/dist/hosts/integration/pi-rollover-extension.d.ts +166 -0
- package/dist/hosts/integration/pi-rollover-extension.js +431 -0
- package/dist/hosts/integration/recovery-config.d.ts +48 -0
- package/dist/hosts/integration/recovery-config.js +117 -0
- package/dist/hosts/integration/service.d.ts +6 -1
- package/dist/hosts/integration/service.js +485 -35
- package/dist/hosts/integration/support.d.ts +7 -0
- package/dist/hosts/integration/support.js +9 -1
- package/dist/hosts/integration/templates.d.ts +3 -0
- package/dist/hosts/integration/templates.js +26 -3
- package/dist/hosts/pi/managed-host-identity.d.ts +2 -0
- package/dist/hosts/pi/managed-host-identity.js +40 -0
- package/dist/hosts/pi/managed-provider-diagnostics.d.ts +6 -0
- package/dist/hosts/pi/managed-provider-diagnostics.js +184 -0
- package/dist/hosts/pi/managed-sdk-session.d.ts +335 -0
- package/dist/hosts/pi/managed-sdk-session.js +1639 -0
- package/dist/hosts/pi/managed-session-extension.d.ts +20 -0
- package/dist/hosts/pi/managed-session-extension.js +84 -0
- package/dist/hosts/pi/managed-session.d.ts +177 -0
- package/dist/hosts/pi/managed-session.js +1557 -0
- package/dist/hosts/pi/protocol.js +1 -1
- package/dist/hosts/pi/query-result.js +1 -1
- package/dist/hosts/pi/resume-capability.d.ts +41 -0
- package/dist/hosts/pi/resume-capability.js +33 -0
- package/dist/hosts/pi/rpc.d.ts +182 -134
- package/dist/hosts/pi/rpc.js +230 -37
- package/dist/hosts/pilot.d.ts +97 -74
- package/dist/hosts/pilot.js +5 -1
- package/dist/hosts/version.d.ts +3 -0
- package/dist/hosts/version.js +18 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/infra/bounded-file.d.ts +19 -0
- package/dist/infra/bounded-file.js +66 -0
- package/dist/infra/fs.js +10 -2
- package/dist/infra/git.d.ts +29 -0
- package/dist/infra/git.js +45 -9
- package/dist/infra/json-property.d.ts +2 -0
- package/dist/infra/json-property.js +69 -0
- package/dist/infra/json.d.ts +4 -0
- package/dist/infra/json.js +24 -0
- package/dist/infra/owned-process-group.js +1 -1
- package/dist/infra/private-lock.d.ts +16 -0
- package/dist/infra/private-lock.js +148 -0
- package/dist/infra/private-state.d.ts +7 -0
- package/dist/infra/private-state.js +146 -0
- package/dist/infra/process-identity.d.ts +12 -0
- package/dist/infra/process-identity.js +142 -0
- package/dist/infra/validation.d.ts +5 -0
- package/dist/infra/validation.js +15 -0
- package/dist/init.d.ts +1 -0
- package/dist/init.js +6 -0
- package/dist/install/apply.d.ts +9 -1
- package/dist/install/apply.js +176 -12
- package/dist/install/dependencies.d.ts +38 -0
- package/dist/install/dependencies.js +282 -0
- package/dist/install/ownership.js +30 -1
- package/dist/install/references.d.ts +31 -0
- package/dist/install/references.js +208 -0
- package/dist/install/repair.d.ts +37 -0
- package/dist/install/repair.js +798 -0
- package/dist/install/reset-local.d.ts +16 -0
- package/dist/install/reset-local.js +304 -0
- package/dist/install/rollback.d.ts +3 -0
- package/dist/install/rollback.js +9 -3
- package/dist/install/runtime.d.ts +10 -6
- package/dist/install/runtime.js +299 -59
- package/dist/install/source-fingerprint.d.ts +2 -0
- package/dist/install/source-fingerprint.js +103 -0
- package/dist/knowledge/catalog.d.ts +1 -0
- package/dist/knowledge/catalog.js +143 -10
- package/dist/knowledge/journal.js +17 -7
- package/dist/knowledge/paths.js +2 -1
- package/dist/knowledge/refs.js +4 -0
- package/dist/knowledge/register-batch.d.ts +4 -0
- package/dist/knowledge/register-batch.js +225 -0
- package/dist/knowledge/relocate.d.ts +5 -0
- package/dist/knowledge/relocate.js +92 -0
- package/dist/knowledge/scan.js +103 -47
- package/dist/knowledge/service.d.ts +3 -0
- package/dist/knowledge/service.js +33 -1
- package/dist/knowledge/write.d.ts +4 -1
- package/dist/knowledge/write.js +1 -1
- package/dist/mcp/server.d.ts +4 -2
- package/dist/mcp/server.js +87 -136
- package/dist/schemas/collaboration-state-validation.d.ts +6 -0
- package/dist/schemas/collaboration-state-validation.js +342 -0
- package/dist/schemas/collaboration-state.d.ts +436 -0
- package/dist/schemas/collaboration-state.js +1 -0
- package/dist/schemas/collaboration.d.ts +335 -0
- package/dist/schemas/collaboration.js +272 -0
- package/dist/schemas/continuity.d.ts +8 -0
- package/dist/schemas/continuity.js +13 -3
- package/dist/schemas/doctor.d.ts +1 -0
- package/dist/schemas/doctor.js +1 -0
- package/dist/schemas/document.d.ts +61 -0
- package/dist/schemas/document.js +9 -0
- package/dist/schemas/errors.d.ts +1 -0
- package/dist/schemas/errors.js +1 -0
- package/dist/schemas/host.d.ts +24 -0
- package/dist/schemas/host.js +19 -0
- package/dist/schemas/install.d.ts +8 -2
- package/dist/schemas/install.js +11 -1
- package/dist/schemas/managed-pi-execution.d.ts +16 -0
- package/dist/schemas/managed-pi-execution.js +2 -0
- package/dist/schemas/outcome.d.ts +9 -5
- package/dist/workspace/config-file.js +1 -3
- package/dist/workspace/documents.js +12 -2
- package/dist/workspace/repositories.d.ts +7 -0
- package/dist/workspace/repositories.js +14 -5
- package/package.json +35 -35
- package/skills/pappra/SKILL.md +28 -7
- package/skills/pappra/references/collaboration.md +633 -0
- package/skills/pappra/references/environment.md +360 -0
- package/skills/pappra/references/hosts.md +20 -0
- package/skills/pappra/references/operations.md +36 -5
- package/dist/app/collab-from.d.ts +0 -35
- package/dist/app/collab-from.js +0 -84
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concrete local task-record reader + opt-in bootstrap-guard registration (one rollover prerequisite).
|
|
3
|
+
*
|
|
4
|
+
* This supplies the accepted successor bootstrap guard (`pi-bootstrap-guard.ts`) with a real, bounded,
|
|
5
|
+
* read-only task-record dependency and wires the actual `tool_call` gate. It is NOT old-writer stop, a
|
|
6
|
+
* transfer executor, automatic rollover, or an installation; it changes no accepted guard/core/host code.
|
|
7
|
+
*
|
|
8
|
+
* Reader contract (fail-closed): it reads ONLY `<domainRoot>/tasks/<taskId>/task.json` for an explicit,
|
|
9
|
+
* trusted, CANONICAL absolute domain root (rejected at registration if a symlink prefix means
|
|
10
|
+
* `realpathSync(root) !== root`) and a short safe task id. The three ancestor directories (domain root,
|
|
11
|
+
* `tasks`, task root) are pinned by device/inode once and re-checked by no-follow `lstat` both BEFORE and
|
|
12
|
+
* AFTER the bounded read, so a replaced/redirected/symlinked ancestor during the read interval denies the
|
|
13
|
+
* call instead of silently rebinding; it never creates directories (it does NOT use
|
|
14
|
+
* `resolveDomainRoot`/`taskRootFor`, which `mkdir`) and never resolves from cwd or a cache. The record is
|
|
15
|
+
* read via `src/infra/bounded-file.ts` (`readBoundedFile`): a bounded (8 MiB), regular-file,
|
|
16
|
+
* no-follow, non-blocking descriptor read refusing symlink/FIFO/device/directory targets and oversized
|
|
17
|
+
* content. The record file's observable identity and change metadata (inode, size, mtime, ctime) are
|
|
18
|
+
* captured before and after the read; a replacement or in-place change during the read is resolved with a
|
|
19
|
+
* strictly bounded coherent retry (the record inode is never pinned forever, so normal atomic store updates
|
|
20
|
+
* between calls are supported). Only a stable, coherent read is parsed, schema-validated (`isTaskRecord`)
|
|
21
|
+
* and identity-checked: `task_id`, `domain_id` (= `domainIdOf(domainRoot)`), `domain_root` and `task_root`
|
|
22
|
+
* must all equal the selected canonical root. An in-memory revision/digest floor rejects a lower
|
|
23
|
+
* `state_revision` (rollback) or different bytes at the same revision, and is NOT reset by a failed read
|
|
24
|
+
* (same revision + same bytes is repeatable). Any violation/read-failure/malformed result throws, which the
|
|
25
|
+
* guard turns into an explicit block; a fresh parsed object is returned and the floor/pinned identities are
|
|
26
|
+
* primitives, immune to outward mutation.
|
|
27
|
+
*
|
|
28
|
+
* Boundaries (explicit): rollback detection is for THIS runtime only — not durable restart fencing, and not
|
|
29
|
+
* authentication against arbitrary user-owned filesystem changes (explicit local-root trust boundary). The
|
|
30
|
+
* reader detects a change DURING its own read; there is still an UNAVOIDABLE check/use interval AFTER
|
|
31
|
+
* `read()` returns, before the tool actually runs. There is no filesystem lock, atomic global snapshot, OS
|
|
32
|
+
* write fence, native-stop proof, old-writer stop, transfer executor, automatic rollover, or installation
|
|
33
|
+
* here. No writes, locks, sidecars, watchers, timers, process/model/network/transcript calls, new authority
|
|
34
|
+
* store, or automatic recovery. The imported guard/store/validation APIs are read-only dependencies.
|
|
35
|
+
*/
|
|
36
|
+
import fs from 'node:fs';
|
|
37
|
+
import path from 'node:path';
|
|
38
|
+
import { readBoundedFile } from '../../infra/bounded-file.js';
|
|
39
|
+
import { RECORD_FILE, domainIdOf } from '../../collab/store.js';
|
|
40
|
+
import { isTaskRecord } from '../../collab/validation.js';
|
|
41
|
+
import { registerPiBootstrapGuard, } from './pi-bootstrap-guard.js';
|
|
42
|
+
const TASKS_DIRNAME = 'tasks';
|
|
43
|
+
const MAX_RECORD_BYTES = 8 * 1024 * 1024;
|
|
44
|
+
const MAX_READ_ATTEMPTS = 8;
|
|
45
|
+
const SAFE_TASK_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
46
|
+
function pinDirectory(dir) {
|
|
47
|
+
let stat;
|
|
48
|
+
try {
|
|
49
|
+
stat = fs.lstatSync(dir);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
throw new Error(`pi-bootstrap-runtime: required directory is missing: ${dir}`);
|
|
53
|
+
}
|
|
54
|
+
if (stat.isSymbolicLink() || !stat.isDirectory())
|
|
55
|
+
throw new Error(`pi-bootstrap-runtime: path is not a real directory: ${dir}`);
|
|
56
|
+
return { dev: stat.dev, ino: stat.ino };
|
|
57
|
+
}
|
|
58
|
+
function recheckDirectory(dir, pinned) {
|
|
59
|
+
let stat;
|
|
60
|
+
try {
|
|
61
|
+
stat = fs.lstatSync(dir);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
throw new Error(`pi-bootstrap-runtime: directory disappeared: ${dir}`);
|
|
65
|
+
}
|
|
66
|
+
if (stat.isSymbolicLink() ||
|
|
67
|
+
!stat.isDirectory() ||
|
|
68
|
+
stat.dev !== pinned.dev ||
|
|
69
|
+
stat.ino !== pinned.ino)
|
|
70
|
+
throw new Error(`pi-bootstrap-runtime: directory identity changed (redirect): ${dir}`);
|
|
71
|
+
}
|
|
72
|
+
function sameObservation(a, b) {
|
|
73
|
+
return (a.ino === b.ino &&
|
|
74
|
+
a.size === b.size &&
|
|
75
|
+
a.mtimeMs === b.mtimeMs &&
|
|
76
|
+
a.ctimeMs === b.ctimeMs);
|
|
77
|
+
}
|
|
78
|
+
class BoundedLocalReader {
|
|
79
|
+
#domainRoot;
|
|
80
|
+
#taskId;
|
|
81
|
+
#tasksDir;
|
|
82
|
+
#taskRoot;
|
|
83
|
+
#recordPath;
|
|
84
|
+
#expectedDomainId;
|
|
85
|
+
#domainIdentity;
|
|
86
|
+
#tasksIdentity;
|
|
87
|
+
#taskIdentity;
|
|
88
|
+
#floorRevision = null;
|
|
89
|
+
#floorDigest = null;
|
|
90
|
+
constructor(domainRoot, taskId) {
|
|
91
|
+
if (typeof domainRoot !== 'string' || !path.isAbsolute(domainRoot))
|
|
92
|
+
throw new RangeError('pi-bootstrap-runtime: domain root must be an absolute path');
|
|
93
|
+
if (typeof taskId !== 'string' || !SAFE_TASK_ID.test(taskId))
|
|
94
|
+
throw new RangeError('pi-bootstrap-runtime: task id must be a short safe identifier');
|
|
95
|
+
// Require a canonical root: a symlink anywhere in the prefix makes realpath differ from the input.
|
|
96
|
+
let canonical;
|
|
97
|
+
try {
|
|
98
|
+
canonical = fs.realpathSync(domainRoot);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
throw new Error(`pi-bootstrap-runtime: domain root does not exist: ${domainRoot}`);
|
|
102
|
+
}
|
|
103
|
+
if (canonical !== domainRoot)
|
|
104
|
+
throw new RangeError('pi-bootstrap-runtime: domain root must be canonical (no symlink prefix or alias)');
|
|
105
|
+
this.#domainRoot = domainRoot;
|
|
106
|
+
this.#taskId = taskId;
|
|
107
|
+
this.#tasksDir = path.join(domainRoot, TASKS_DIRNAME);
|
|
108
|
+
this.#taskRoot = path.join(this.#tasksDir, taskId);
|
|
109
|
+
this.#recordPath = path.join(this.#taskRoot, RECORD_FILE);
|
|
110
|
+
this.#expectedDomainId = domainIdOf(domainRoot);
|
|
111
|
+
// Require existing directories and pin their identities once; never create them.
|
|
112
|
+
this.#domainIdentity = pinDirectory(this.#domainRoot);
|
|
113
|
+
this.#tasksIdentity = pinDirectory(this.#tasksDir);
|
|
114
|
+
this.#taskIdentity = pinDirectory(this.#taskRoot);
|
|
115
|
+
}
|
|
116
|
+
#recheckAncestors() {
|
|
117
|
+
recheckDirectory(this.#domainRoot, this.#domainIdentity);
|
|
118
|
+
recheckDirectory(this.#tasksDir, this.#tasksIdentity);
|
|
119
|
+
recheckDirectory(this.#taskRoot, this.#taskIdentity);
|
|
120
|
+
}
|
|
121
|
+
#observeRecordFile() {
|
|
122
|
+
try {
|
|
123
|
+
const stat = fs.lstatSync(this.#recordPath);
|
|
124
|
+
if (stat.isSymbolicLink() || !stat.isFile())
|
|
125
|
+
return null;
|
|
126
|
+
return {
|
|
127
|
+
ino: stat.ino,
|
|
128
|
+
size: stat.size,
|
|
129
|
+
mtimeMs: stat.mtimeMs,
|
|
130
|
+
ctimeMs: stat.ctimeMs,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
read() {
|
|
138
|
+
let lastReason = 'record unstable during read';
|
|
139
|
+
for (let attempt = 0; attempt < MAX_READ_ATTEMPTS; attempt += 1) {
|
|
140
|
+
// Pinned ancestors must still be the same real directories before the read.
|
|
141
|
+
this.#recheckAncestors();
|
|
142
|
+
const before = this.#observeRecordFile();
|
|
143
|
+
const result = readBoundedFile(this.#recordPath, MAX_RECORD_BYTES);
|
|
144
|
+
if (!result.ok)
|
|
145
|
+
throw new Error(`pi-bootstrap-runtime: record read failed (${result.code})`);
|
|
146
|
+
const after = this.#observeRecordFile();
|
|
147
|
+
// A pinned ancestor replaced DURING the read interval denies this call (namespace redirect).
|
|
148
|
+
this.#recheckAncestors();
|
|
149
|
+
// The record file replaced or changed in place during the read interval: bounded coherent retry.
|
|
150
|
+
if (before === null ||
|
|
151
|
+
after === null ||
|
|
152
|
+
!sameObservation(before, after)) {
|
|
153
|
+
lastReason = 'record changed during read';
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
let parsed;
|
|
157
|
+
try {
|
|
158
|
+
parsed = JSON.parse(result.text);
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
throw new Error('pi-bootstrap-runtime: record is not valid JSON');
|
|
162
|
+
}
|
|
163
|
+
if (!isTaskRecord(parsed))
|
|
164
|
+
throw new Error('pi-bootstrap-runtime: record failed schema validation');
|
|
165
|
+
const record = parsed;
|
|
166
|
+
// Complete task-domain identity against the selected canonical root (not just derived domain_id).
|
|
167
|
+
if (record.task_id !== this.#taskId)
|
|
168
|
+
throw new Error('pi-bootstrap-runtime: record task id mismatch');
|
|
169
|
+
if (record.domain_id !== this.#expectedDomainId)
|
|
170
|
+
throw new Error('pi-bootstrap-runtime: record domain id does not derive from the selected root');
|
|
171
|
+
if (record.domain_root !== this.#domainRoot)
|
|
172
|
+
throw new Error('pi-bootstrap-runtime: record domain_root does not match the selected root');
|
|
173
|
+
if (record.task_root !== this.#taskRoot)
|
|
174
|
+
throw new Error('pi-bootstrap-runtime: record task_root does not match the selected task');
|
|
175
|
+
const revision = record.state_revision;
|
|
176
|
+
if (typeof revision !== 'number' ||
|
|
177
|
+
!Number.isSafeInteger(revision) ||
|
|
178
|
+
revision < 0)
|
|
179
|
+
throw new Error('pi-bootstrap-runtime: record has an invalid state_revision');
|
|
180
|
+
const digest = result.digest;
|
|
181
|
+
if (this.#floorRevision !== null) {
|
|
182
|
+
if (revision < this.#floorRevision)
|
|
183
|
+
throw new Error('pi-bootstrap-runtime: record state_revision rolled back');
|
|
184
|
+
if (revision === this.#floorRevision && digest !== this.#floorDigest)
|
|
185
|
+
throw new Error('pi-bootstrap-runtime: record bytes changed at the same state_revision');
|
|
186
|
+
}
|
|
187
|
+
// Advance the floor only after a stable, coherent, validated read; a failure never resets it.
|
|
188
|
+
if (this.#floorRevision === null || revision > this.#floorRevision) {
|
|
189
|
+
this.#floorRevision = revision;
|
|
190
|
+
this.#floorDigest = digest;
|
|
191
|
+
}
|
|
192
|
+
// Fresh parsed data; the floor and pinned identities are primitives, immune to outward mutation.
|
|
193
|
+
return record;
|
|
194
|
+
}
|
|
195
|
+
throw new Error(`pi-bootstrap-runtime: ${lastReason} after ${MAX_READ_ATTEMPTS} attempts`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Build the bounded local reader for an explicit canonical domain root + task id. Requires existing
|
|
200
|
+
* directories; never creates any. Exposed for focused testing and reuse.
|
|
201
|
+
*/
|
|
202
|
+
export function createLocalTaskRecordReader(domainRoot, taskId) {
|
|
203
|
+
return new BoundedLocalReader(domainRoot, taskId);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Register the opt-in successor bootstrap gate wired to the concrete local reader. Configuration is
|
|
207
|
+
* trusted host integration (never model input); the reader is built here (no caller-reader override). The
|
|
208
|
+
* `tool_call` gate is registered synchronously before any model work. Returns the guard's conservative
|
|
209
|
+
* status handle only — no allow/release surface.
|
|
210
|
+
*/
|
|
211
|
+
export function registerPiBootstrapRuntime(pi, config) {
|
|
212
|
+
if (typeof config !== 'object' || config === null)
|
|
213
|
+
throw new RangeError('pi-bootstrap-runtime: configuration is required');
|
|
214
|
+
if (config.taskId !== config.guard.taskId)
|
|
215
|
+
throw new RangeError('pi-bootstrap-runtime: runtime task id must equal the guard task id');
|
|
216
|
+
const reader = createLocalTaskRecordReader(config.domainRoot, config.taskId);
|
|
217
|
+
// registerPiBootstrapGuard copies/validates the pinned config; the reader is supplied here, not by a
|
|
218
|
+
// caller, and a reader throw/malformed result becomes the guard's explicit block.
|
|
219
|
+
return registerPiBootstrapGuard(pi, {
|
|
220
|
+
...config.guard,
|
|
221
|
+
readRecord: () => reader.read(),
|
|
222
|
+
});
|
|
223
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-process native Pi event-fact capture (rollover observation source).
|
|
3
|
+
*
|
|
4
|
+
* A small, bounded, opt-in producer that subscribes to a fixed set of supported installed Pi 1.3.0
|
|
5
|
+
* extension callbacks and derives conservative in-process facts plus invalidation notifications. It is
|
|
6
|
+
* NOT authority, external control, writer fencing, or a native-stop/quiescence/transfer proof. It never
|
|
7
|
+
* calls the accepted freeze controller's `observe()`, never synthesizes queue/approval numbers, and
|
|
8
|
+
* never infers a stop from balanced tool events. Facts come only from live callback contexts, never from
|
|
9
|
+
* caller-supplied fact JSON.
|
|
10
|
+
*
|
|
11
|
+
* It holds no resources: no timer, watcher, filesystem, process, transcript, network, controller, or
|
|
12
|
+
* rollover import. In-memory only: a restart or reload loses every fact. There is no thaw and no
|
|
13
|
+
* transfer behavior.
|
|
14
|
+
*
|
|
15
|
+
* Conservative identity handling: an unknown, throwing, or malformed session read invalidates the prior
|
|
16
|
+
* session-derived facts (settled confidence and tool-tracking health) and never revalidates them on the
|
|
17
|
+
* next matching identity read; only a later trustworthy supported boundary can re-establish settled, and
|
|
18
|
+
* only a native session change starts fresh tool tracking. Payload reads are guarded so a throwing or
|
|
19
|
+
* proxy accessor is treated as malformed and still notifies subscribers.
|
|
20
|
+
*
|
|
21
|
+
* Notifications are delivered in chronological order under bounded reentrancy. If the bounded buffer or
|
|
22
|
+
* per-drain budget is exceeded, delivery is lost; this is a sticky delivery gap. Rather than ending any
|
|
23
|
+
* subscriber on an older healthy snapshot, the drain discards stale buffered facts and delivers one
|
|
24
|
+
* terminal, conservative gap invalidation to every still-subscribed listener. The gap is sticky: while it
|
|
25
|
+
* is unresolved every snapshot reports `notificationBacklogOverflow`, `settled: 'unknown'` and
|
|
26
|
+
* `toolTrackingHealthy: false`, and no ordinary later event restores healthy confidence.
|
|
27
|
+
*
|
|
28
|
+
* Installed Pi 1.3.0 API pinned from the installed docs/examples (the package ships no .d.ts):
|
|
29
|
+
* docs/extensions.md (sha256:a2b5ff0f95e7e470ed0846d7c4fbfc5781fe7e694b8e7f51ea08859f3ad7325d):
|
|
30
|
+
* lifecycle events `session_start`, `before_agent_start` (a run becomes busy), `agent_settled`
|
|
31
|
+
* (final, "Pi will not continue automatically"), and nested `tool_execution_start` /
|
|
32
|
+
* `tool_execution_end` carrying a string `toolCallId` (`<parent id>/<n>` for nested calls); the
|
|
33
|
+
* running session is read from `ctx.sessionManager.getSessionId()` and idleness from `ctx.isIdle()`
|
|
34
|
+
* (examples/extensions/send-user-message.ts
|
|
35
|
+
* sha256:9261e433c782f354530105e1c9723a19798868f72fd1f5d68af3a4624ad7e446). The narrow structural
|
|
36
|
+
* host interface below matches that shape; structural compatibility is not a shipped-declaration or
|
|
37
|
+
* live-load proof.
|
|
38
|
+
*/
|
|
39
|
+
export declare const PI_NATIVE_EVENTS_SCHEMA = "pappra/pi-native-events/1";
|
|
40
|
+
/** The only installed callbacks this producer subscribes. Nothing else is observed. */
|
|
41
|
+
export declare const SUPPORTED_CALLBACKS: readonly ["session_start", "before_agent_start", "agent_settled", "tool_execution_start", "tool_execution_end"];
|
|
42
|
+
export type SupportedCallback = (typeof SUPPORTED_CALLBACKS)[number];
|
|
43
|
+
/** Explicit, non-inferable facts: there is no supported extension API for either. */
|
|
44
|
+
export type UnknownFact = 'unknown';
|
|
45
|
+
/**
|
|
46
|
+
* Honest boundaries of these facts. Shared frozen; a snapshot exposes the same immutable array.
|
|
47
|
+
*/
|
|
48
|
+
export declare const NATIVE_EVENT_LIMITATIONS: readonly string[];
|
|
49
|
+
/** A single subscribed callback handler, shaped like the installed `pi.on(event, handler)`. */
|
|
50
|
+
export type PiNativeCallbackHandler = (event: unknown, context: unknown) => void;
|
|
51
|
+
/** Narrow structural host: only `on` for the five supported callbacks is required. */
|
|
52
|
+
export interface PiNativeEventHost {
|
|
53
|
+
on(event: SupportedCallback, handler: PiNativeCallbackHandler): void;
|
|
54
|
+
}
|
|
55
|
+
export interface NativeEventProducerOptions {
|
|
56
|
+
/** The native session expected at registration; every callback re-derives identity from context. */
|
|
57
|
+
readonly sessionId: string;
|
|
58
|
+
/** Bound on tracked open tool ids; exceeding it marks tracking unhealthy (default 4096). */
|
|
59
|
+
readonly maxTrackedTools?: number;
|
|
60
|
+
/** Sequence saturation bound; reaching it stops advancement to avoid a replayable wrap. */
|
|
61
|
+
readonly maxSequence?: number;
|
|
62
|
+
/** Bound on buffered/drained reentrant notifications; exceeding it is a delivery gap (default 1024). */
|
|
63
|
+
readonly maxNotifyQueue?: number;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* An immutable snapshot of derived facts. `settled`/`idle` are tri-state: a boolean only when cleanly
|
|
67
|
+
* observed, else `'unknown'`. `observedOpenToolCount` is a local, non-authoritative observation; true
|
|
68
|
+
* in-flight completeness is never established, so it is reported as `inFlightCompleteness: 'unknown'`.
|
|
69
|
+
* Once `notificationBacklogOverflow` is set (a delivery gap), `settled` is forced to `'unknown'` and
|
|
70
|
+
* `toolTrackingHealthy` to `false` and stays so for every later snapshot.
|
|
71
|
+
*/
|
|
72
|
+
export interface NativeFactsSnapshot {
|
|
73
|
+
readonly schema: typeof PI_NATIVE_EVENTS_SCHEMA;
|
|
74
|
+
/** Last identity read from a callback context; trust it only when `sessionKnown` is true. */
|
|
75
|
+
readonly sessionId: string | null;
|
|
76
|
+
readonly sessionKnown: boolean;
|
|
77
|
+
readonly settled: boolean | UnknownFact;
|
|
78
|
+
readonly idle: boolean | UnknownFact;
|
|
79
|
+
/** Always `'unknown'`: no supported invariant establishes complete in-flight knowledge. */
|
|
80
|
+
readonly inFlightCompleteness: UnknownFact;
|
|
81
|
+
/** Count of tool ids observed open since the last clean session start; NOT a complete count. */
|
|
82
|
+
readonly observedOpenToolCount: number;
|
|
83
|
+
/**
|
|
84
|
+
* False after any duplicate start, unmatched end, malformed/oversized id, tracking overflow, an
|
|
85
|
+
* unknown-session interval, or a notification delivery gap. Only a native session change can reset the
|
|
86
|
+
* tool-tracking causes; a delivery gap keeps it false.
|
|
87
|
+
*/
|
|
88
|
+
readonly toolTrackingHealthy: boolean;
|
|
89
|
+
readonly queue: UnknownFact;
|
|
90
|
+
readonly approvals: UnknownFact;
|
|
91
|
+
/** Never inferred from events; always false. */
|
|
92
|
+
readonly nativeStop: false;
|
|
93
|
+
/** Strictly increasing across every subscribed callback and session change until saturation. */
|
|
94
|
+
readonly sequence: number;
|
|
95
|
+
/** True once the sequence bound is reached; the sequence then stays fixed (no replayable wrap). */
|
|
96
|
+
readonly sequenceSaturated: boolean;
|
|
97
|
+
/** Sticky: true once notification delivery was lost; confidence is then permanently suspect. */
|
|
98
|
+
readonly notificationBacklogOverflow: boolean;
|
|
99
|
+
readonly limitations: readonly string[];
|
|
100
|
+
}
|
|
101
|
+
export interface NativeEventProducer {
|
|
102
|
+
/** Current derived facts as an immutable snapshot. Never fed blindly into a controller. */
|
|
103
|
+
snapshot(): NativeFactsSnapshot;
|
|
104
|
+
/** Subscribe to invalidation; fires in chronological order after every callback. Returns unsubscribe. */
|
|
105
|
+
onInvalidate(listener: (facts: NativeFactsSnapshot) => void): () => void;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Subscribe a fresh producer to the five supported callbacks on `pi`. It only touches the supplied
|
|
109
|
+
* host (`pi.on`); it starts, installs, exports, and wires nothing else. The returned producer yields
|
|
110
|
+
* observations and invalidation notifications only — never authority, control, or a stop proof.
|
|
111
|
+
*/
|
|
112
|
+
export declare function registerPiNativeEvents(pi: PiNativeEventHost, options: NativeEventProducerOptions): NativeEventProducer;
|