@llblab/pi-kit 0.22.1 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +4 -4
- package/node_modules/@llblab/pi-grow-loop/AGENTS.md +10 -6
- package/node_modules/@llblab/pi-grow-loop/CHANGELOG.md +7 -0
- package/node_modules/@llblab/pi-grow-loop/README.md +2 -0
- package/node_modules/@llblab/pi-grow-loop/dist/index.d.ts +33 -0
- package/node_modules/@llblab/pi-grow-loop/dist/index.js +286 -0
- package/node_modules/@llblab/pi-grow-loop/dist/pi-grow-loop/index.d.ts +1 -0
- package/node_modules/@llblab/pi-grow-loop/dist/pi-grow-loop/index.js +1 -0
- package/node_modules/@llblab/pi-grow-loop/dist/skills/grow-loop/SKILL.md +117 -0
- package/node_modules/@llblab/pi-grow-loop/dist/skills/while-true/SKILL.md +233 -0
- package/node_modules/@llblab/pi-grow-loop/index.ts +67 -12
- package/node_modules/@llblab/pi-grow-loop/package.json +9 -8
- package/node_modules/@llblab/pi-state-flow/AGENTS.md +23 -17
- package/node_modules/@llblab/pi-state-flow/BACKLOG.md +5 -3
- package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +11 -0
- package/node_modules/@llblab/pi-state-flow/README.md +18 -6
- package/node_modules/@llblab/pi-state-flow/dist/index.d.ts +2 -1
- package/node_modules/@llblab/pi-state-flow/dist/index.js +1 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/artifact.d.ts +2 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/artifact.js +5 -5
- package/node_modules/@llblab/pi-state-flow/dist/lib/context.d.ts +3 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/context.js +7 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/continuation.d.ts +5 -5
- package/node_modules/@llblab/pi-state-flow/dist/lib/continuation.js +54 -40
- package/node_modules/@llblab/pi-state-flow/dist/lib/durable.js +20 -20
- package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +755 -325
- package/node_modules/@llblab/pi-state-flow/dist/lib/git.d.ts +2 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/git.js +14 -17
- package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.d.ts +4 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.js +65 -23
- package/node_modules/@llblab/pi-state-flow/dist/lib/recovery.d.ts +16 -5
- package/node_modules/@llblab/pi-state-flow/dist/lib/recovery.js +32 -15
- package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.d.ts +28 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.js +276 -26
- package/node_modules/@llblab/pi-state-flow/dist/lib/session.d.ts +5 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/session.js +36 -2
- package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.d.ts +3 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.js +3 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/status.d.ts +1 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/status.js +3 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/storage.d.ts +11 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/storage.js +150 -24
- package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.d.ts +14 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.js +52 -18
- package/node_modules/@llblab/pi-state-flow/dist/lib/transition.js +8 -8
- package/node_modules/@llblab/pi-state-flow/dist/package.json +1 -1
- package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-guide/SKILL.md +3 -1
- package/node_modules/@llblab/pi-state-flow/docs/architecture.md +53 -7
- package/node_modules/@llblab/pi-state-flow/docs/compatibility.md +84 -44
- package/node_modules/@llblab/pi-state-flow/docs/filesystem-recovery.md +12 -2
- package/node_modules/@llblab/pi-state-flow/docs/fork-contract.md +6 -4
- package/node_modules/@llblab/pi-state-flow/docs/performance.md +2 -2
- package/node_modules/@llblab/pi-state-flow/docs/temporal-acceptance.md +28 -8
- package/node_modules/@llblab/pi-state-flow/docs/usage.md +41 -14
- package/node_modules/@llblab/pi-state-flow/index.ts +3 -0
- package/node_modules/@llblab/pi-state-flow/lib/artifact.ts +5 -5
- package/node_modules/@llblab/pi-state-flow/lib/context.ts +8 -3
- package/node_modules/@llblab/pi-state-flow/lib/continuation.ts +57 -40
- package/node_modules/@llblab/pi-state-flow/lib/durable.ts +20 -20
- package/node_modules/@llblab/pi-state-flow/lib/extension.ts +719 -316
- package/node_modules/@llblab/pi-state-flow/lib/git.ts +16 -18
- package/node_modules/@llblab/pi-state-flow/lib/protocol.ts +60 -24
- package/node_modules/@llblab/pi-state-flow/lib/recovery.ts +34 -21
- package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +290 -25
- package/node_modules/@llblab/pi-state-flow/lib/session.ts +37 -2
- package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +3 -0
- package/node_modules/@llblab/pi-state-flow/lib/status.ts +4 -1
- package/node_modules/@llblab/pi-state-flow/lib/storage.ts +141 -22
- package/node_modules/@llblab/pi-state-flow/lib/telegram.ts +60 -19
- package/node_modules/@llblab/pi-state-flow/lib/transition.ts +8 -8
- package/node_modules/@llblab/pi-state-flow/package.json +1 -1
- package/node_modules/@llblab/pi-state-flow/skills/state-flow-guide/SKILL.md +3 -1
- package/node_modules/@llblab/pi-telegram/AGENTS.md +3 -2
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +10 -0
- package/node_modules/@llblab/pi-telegram/README.md +3 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.js +22 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/skills.d.ts +8 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/skills.js +36 -4
- package/node_modules/@llblab/pi-telegram/dist/package.json +3 -8
- package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +1 -1
- package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +27 -3
- package/node_modules/@llblab/pi-telegram/lib/skills.ts +49 -5
- package/node_modules/@llblab/pi-telegram/package.json +3 -8
- package/node_modules/@llblab/pi-telegram/scripts/build-dist.mjs +103 -32
- package/package.json +6 -6
|
@@ -18,17 +18,36 @@ This is a maintained property-to-test map for the current canonical-file contrac
|
|
|
18
18
|
12. **Next inference sees new current state:** The same real-Pi test inspects actual model-input projections after session, CWD, and global barriers. “real Pi reads prior scoped state lazily after a barrier and rejects path offset eight without a transition” adds model-tool access to the predecessor.
|
|
19
19
|
13. **No automatic old full-state duplication:** `tests/context.test.ts` — “projects only the latest seven compact accepted transitions” rejects full-state records in transition context. The real-Pi barrier test requires exactly one current runtime projection per inference. Explicitly requested history remains ordinary tool-result trajectory, not eager snapshot injection.
|
|
20
20
|
14. **Accepted response changes are transitions:** `tests/extension.test.ts` verifies that an ordinary accepted answer becomes runtime-owned `response` without a finalization patch or fallback inference, and “an empty accepted answer finalizes the run and stores an empty response” proves `""` is accepted after an earlier barrier. `tests/transition.test.ts` proves the empty value is an ordinary Session-owned semantic change when it replaces prior text.
|
|
21
|
-
15. **
|
|
21
|
+
15. **Correct repeats succeed without semantic changes:** “accepts canonical atomic scope patches and correct repeats without another checkpoint” preserves canonical bytes and native checkpoint count on repetition, while still rejecting empty supplied scopes and obsolete finalization-shaped calls. Runtime current-head witnesses preserve revisions, lineage and step across identical patches and compilations; a changed accepted response remains a runtime-owned transition.
|
|
22
22
|
16. **Configured hot-history bounds:** `tests/temporal.test.ts` verifies hot-range and unavailable pre-origin boundaries; the real-Pi history-reader test rejects offset eight at the default limit seven without a transition. `tests/config.test.ts` exercises materialized and scope patch-history paths at limits 0, 1, 7, and 12, including single-path/one-item batch reads above seven and distinct configured versus actually retained boundaries.
|
|
23
|
-
17. **
|
|
23
|
+
17. **Historical restoration is distinct from activation:** `tests/recovery.test.ts` rejects unavailable selections without older-pointer or disabled-marker fallback. Extension/native tests fence passive publication until explicit current-memory activation, with unchanged canonical bytes and all passive-policy combinations. Native “real Pi activates current … memory after expired tree selection and reload” cases cover passive, active and interrupted work plus the next provider's private-memory input. Runtime current-head tests preserve semantics, provenance, revisions, step and available history across old modes, distinguish absent from incomplete private authority, adopt validated foreign shared streams without private leakage or invented history, and reject a concurrent private writer. Native pre-runtime and inherited-fork-selection witnesses preserve current owned session memory instead of resetting it or recopying a parent. Repeated active Start leaves response reconciliation intact. Checkpoint-free ordinary conversation still bootstraps; unaccepted fork identity/CWD repair retries the exact source.
|
|
24
24
|
18. **Tree/resume select the correct lineage:** `tests/integration.test.ts` — “real Pi preserves branch-local state through compaction and rejects an expired sibling after fresh-origin navigation” and “real Pi old tree branch stop and resume preserve selected semantics without rewinding shared files”.
|
|
25
|
-
19. **Stop changes
|
|
26
|
-
20. **Passive observability omits active status without hiding owner revisions:** `tests/status.test.ts` proves an accepted passive patch advances semantic history while compact terminal status stays absent. `tests/telegram.test.ts` drives the real extension port after Stop, requires the passive main-menu identity to render `State Flow: off`, verifies an owner Rich-state heading uses its independent `#revision` and Effective uses `G#/C#/S#`, and separately proves Telegram can lazily observe existing shared canonical state when passive model tools are disabled without initializing or mutating storage.
|
|
25
|
+
19. **Stop changes policy, not semantic history:** `tests/runtime.test.ts` lifecycle-only witnesses use a separate process to advance global/CWD semantics or provenance, including wider foreign retention, then prove exact semantic/sidecar preservation, unchanged steps, idempotent Stop, and same-session/stale-evidence refusal. Native Stop/new-request witnesses verify the accepted shared view reaches handoff/inference without a lifecycle semantic write, while the current-head native witness accepts a shared write racing after inference and retains its actual accepted predecessor. Mid-tool Stop tests preserve ordinary/bootstrap trajectories through tree, reload, resume, and restart. The native “real Pi retains a native split-turn continuation through late tools” Stop/no-Stop controls actually remove the original user with native threshold compaction, then require summary, paired reads and foreign context in model input. The Stop case also checks frozen semantics/step, unchanged trace prefix, tree/reload/cold resume/bootstrap restart, no resurrection of discarded input, and persistent foreign context after the next active run. Pure passive-selector tests cover missing, colliding and nonfinite recorded active anchors and explicit preservation of unfinished compilation. The native “real Pi repeated Start/Stop preserves uncompiled conversation and its prior boundary” cases cover initial/restarted bootstrap, repeated idle toggles, reload, unchanged native trace, and release after accepted compilation; extension tests cover interrupted idle Stop with and without a captured anchor. Completed idle/legacy cutoffs remain bounded. The extension's failed-Stop matrix covers concurrent private writers, invalid lock ownership and malformed runtime files across all passive-policy combinations: local mode turns off, canonical bytes stay unchanged, cached memory and available context survive, reload is read-only, and writes remain fenced until accepted Start. Native “real Pi failed Stop stays passive through late tools, tree, reload and resume” cases verify provider inputs, rejected tool calls, untouched trace prefixes, private memory, current foreign writes, and recovery through explicit Start. A native fork witness retains disabled policy without inheriting the parent's fence. Session tests bind fence lifetime to owner/reset/accepted-checkpoint evidence; runtime current-head tests prove read-only loading changes no canonical bytes. `tests/storage.test.ts` fences lifecycle-only writes to config/runtime files; `tests/extension.test.ts` covers idle/legacy cutoffs and new/fork boundaries.
|
|
26
|
+
20. **Passive observability omits active status without hiding owner revisions:** `tests/status.test.ts` proves an accepted passive patch advances semantic history while compact terminal status stays absent. `tests/telegram.test.ts` drives the real extension port after Stop, requires the passive main-menu identity to render `State Flow: off`, verifies an owner Rich-state heading uses its independent `#revision` and Effective uses `G#/C#/S#`, and separately proves Telegram can lazily observe existing shared canonical state when passive model tools are disabled without initializing or mutating storage. A failed-Stop callback reports local disablement and keeps accepted cached memory inspectable without overwriting the concurrent writer.
|
|
27
27
|
21. **Independent revisions survive folding and foreign writers:** `tests/temporal.test.ts` proves each materially changed scope advances once for sparse and multi-scope cohorts, response-only transitions advance only Session, no-ops advance none, selected retained history restores the matching revision, and folding never resets it. `tests/durable.test.ts` covers revision serialization plus the pre-revision 0.17 retained-tail baseline. `tests/runtime.test.ts` uses an independent file-backed writer to advance Global, then refreshes the first runtime without file mutation and proves `G1/C0/S1` becomes `G1/C0/S2` after one private Session patch.
|
|
28
|
+
22. **Current-head atomic model publication:** `tests/runtime.test.ts` applies authored independent/overlapping assignments, deletion and arrays to current shared values, preserves another owner's private files, and derives the predecessor/revisions from that basis. Compilation witnesses replace complete cards with matching provenance, preserve untouched evidence, accept repeats and refuse orphaned evidence after semantic-pair disappearance. Partial/malformed shared evidence and concurrent/unselected private authority stay fenced. Fault injection rolls back a mixed publication without installing the candidate's shared adoption or private draft. Extension tests reject a first passive patch without publishing empty setup, and recheck acquired-source freshness after lock waiting. Native “real Pi awaits foreign publication” cases hold an independent process beyond the former wait budget, verify responsive waiting/cancellation with unchanged canonical bytes, and require the next provider to see accepted current Global/CWD plus its own Session, never the foreign private layer.
|
|
29
|
+
23. **Awaited response acceptance owns its lifecycle:** `tests/extension.test.ts` verifies waiting against current shared memory, one accepted checkpoint without a second publication, rollback without candidate/cache or lifecycle installation, cancellation at Stop/session/tree/shutdown boundaries, shutdown fencing and superseding-answer isolation. Native “real Pi awaits response publication” witnesses wait behind an independent writer for over two seconds, then prove later boundary handlers and continuation inference see the accepted response, current Global/CWD and only their own Session. Native Abort withdraws the wait without changing canonical bytes, the prior response or unfinished specification, and without requesting a repair inference.
|
|
30
|
+
24. **Awaited inspection is coherent and observational:** `tests/runtime.test.ts` pauses an independent writer between semantic-tail and metadata publication for over two seconds: shared inspection waits responsively, preserves its cached view, and then reads matching Global/CWD/artifact evidence with its own private layer. Cancellation, absent/malformed storage, unchanged reads and competing private authority preserve bytes and history. `tests/telegram.test.ts` covers lazy passive and active reads without publication/checkpoints, coupled data/revision receipts, revocation before presentation, Stop/tree/shutdown cancellation, early callback acknowledgement and escaped late errors without a second callback answer. Legacy ports and failed-Stop cached inspection remain supported.
|
|
31
|
+
25. **Coherent backup capture preserves semantic acceptance and Abort:** `tests/git.test.ts` pauses an independent publisher between scope files for over two seconds, then verifies one complete committed inventory including newly created namespaces. Cancellation preserves files/HEAD/index and releases only the owned backup mutex; local waiters serialize, replacement ownership survives, and Git branch/head drift after waiting is rejected. Existing slow-Git witnesses still observe no Git command under canonical exclusion. `tests/episode.test.ts` checks operation cancellation, shutdown draining, later retry and Stop write-fence preservation. Native tests require the commit before later settlement handlers, or explicit deferral when SDK 0.87 provides no abort signal. `tests/storage.test.ts` distinguishes nonwaiting busy admission from malformed ownership without theft. Full native cancellation of a settlement wait is unavailable on this SDK; the selected policy is deferral, not scheduled upstream work or a synthetic-signal guarantee.
|
|
32
|
+
26. **Advisory startup inspection awaits coherent evidence:** `tests/continuation.test.ts` pauses an independent publisher between private-tail and metadata replacement for over two seconds. Inspection and the default-launch recommendation wait; cancellation rejects without suggesting another session or changing bytes/lock ownership. After release, eligibility uses the complete accepted cohort. Absent/empty stores stay uninitialized; malformed, orphaned, symlinked and invalid-lock evidence stays ineligible. Async host callbacks preserve detached native headers and eligibility, cannot override native identity, and cannot return late decisions or inspect more candidates after cancellation. Existing native-session, shared-drift and private-lineage witnesses remain applicable; these are advisory APIs, not an installed native startup hook.
|
|
33
|
+
27. **Awaited runtime-only acceptance preserves semantic authority:** `tests/runtime.test.ts` applies both raw and awaited lifecycle publication beside independently advanced Global/CWD state or evidence at limits 0/7, preserving wider stored tails, orphaned provenance, unknown metadata and private values. An independent publisher pauses mid-cohort for over two seconds; waiting/cancellation changes no accepted bytes/cache, and the post-wait callback uses current lifecycle input. Authority, malformed/partial/absent storage, private races, newer selections, single-use/expired capabilities and rollback remain fenced; correct repeats add no history. Native “real Pi context provides a cancellable lifecycle boundary” probes prove acceptance before the provider and actual Abort cancellation without later inference/publication. Those embedding probes establish the SDK seam; production preparation is covered separately below. No-transition patch publication passes the same wider-history/evidence matrix for complete accepted cohorts; an absence witness distinguishes its normal atomic initialization from the explicit lifecycle-only API, which still refuses missing semantic storage.
|
|
34
|
+
28. **Pre-inference preparation accepts once or stops inference:** Extension witnesses prove capture/no-signal projection is read-only, current-head maintenance shares lifecycle acceptance, reappearing sources survive, duplicate contexts reuse preparation and pre-acceptance failure rolls back both maintenance and metadata. Abort, Stop, selection, shutdown and superseded runs preserve newer state/fences; post-acceptance failure cannot replay an old specification. Native “real Pi production preparation” holds an independent partial publisher for over two seconds, then proves matching `G2/C2/S2`, source/provenance cleanup and private isolation before the first provider, while the native missing-CWD witness preserves current-empty initialization without resurrection, or actual Abort with no publication/inference after release. Native fault injection proves public `ctx.abort()` fences Pi's otherwise fail-open context hook. Session/native tests retain uncheckpointed input through reload/Stop/cold resume and interrupted boundary-continuation tool evidence through idle Stop/reload, without inventing a run or reviving a completed specification.
|
|
35
|
+
29. **Awaited Stop preserves immediate policy and accepted authority:** Extension tests prove immediate passive projection, one runtime-only acceptance for repeated pending Stops, current shared adoption with unchanged semantic/provenance bytes, and lifecycle derivation after an intervening same-instance passive patch. Operation cancellation keeps mode off and fences writes; selection/shutdown/accepted Start withdraw obsolete work without later mutation, while rejected Start leaves Stop pending. Publication faults roll back config/runtime without installing shared adoption; native checkpoint faults after acceptance do not roll back or retry writes. Native “real Pi stop keeps local mode off while awaiting a partial foreign publication without private leakage” holds an independent writer for over two seconds, then requires current Global/CWD plus local Session in passive provider input. Telegram controls acknowledge early, await results, escape late errors and suppress revoked/navigated/disposed views; legacy synchronous controls remain compatible.
|
|
36
|
+
30. **Awaited Start activates current authority without stale rollback:** Runtime witnesses preserve semantics, provenance, revisions, step and aligned history across old mode/unfinished-work combinations; expired/single-use capabilities, partial private authority, unauthorized absent-root creation, noncooperating private-byte replacement after capture, rollback and no-op acceptance are checked. Extension witnesses prove coalescing, no early policy/cache/checkpoint changes, post-wait bootstrap/private-step derivation, Stop/session/tree/shutdown cancellation, physical-owner revalidation and no rollback/replayed checkpoint after acceptance. A completed Start failure loses Telegram presentation authority when selection changes during callback acknowledgement. Native “real Pi start keeps local mode off while awaiting a partial foreign publication without private leakage” waits over two seconds behind an independent partial writer, then verifies current Global/CWD and local Session in the next provider input. Native “real Pi Abort cancels an in-run Start without waiting for the canonical owner or another provider call” uses the actual command and active operation signal: cancellation returns while the store remains held, without changed files/checkpoints, another provider call or late activation. Idle/no-signal waits and the separate initial-attachment/fork recovery paths are not certified by that Abort witness.
|
|
37
|
+
31. **Awaited retained restoration selects and accepts one coherent cohort:** Runtime restore matrices cover limits 0/1/12, historical/head artifact provenance and contradictory lineage. An independent partial publisher holds exclusion for over two seconds: cancellation preserves cache/files, successful restoration retains historical local Session over current shared values, and all five foreign-private files remain unchanged. Pointer mutation after admission cannot retarget selection; lifecycle policy is checked after waiting. Further witnesses cover expiry during the wait, missing/malformed/foreign authority, private checkpoint/runtime CAS races, rollback/retry, expired/single-use capabilities and post-acceptance failure. No current-head/empty fallback, invented history or unaccepted cache installation is permitted. Native caller cutover evidence is item 35.
|
|
38
|
+
32. **Awaited exact-source fork preserves parent and child authority:** Runtime restore/fork matrices cover limits 0/1/12, provenance and contradictory lineage. An independent partial writer held over two seconds proves responsive cancellation, pinned source identity/checkpoint, current shared adoption and historical private copying without parent/foreign-private changes. Source expiry during waiting and competing child acceptances prove post-wait checks. Every occupied child file, unsupported legacy evidence, invalid/missing parent authority and parent/child byte races refuse publication. Fault injection proves rollback/retry; expired/single-use capabilities and post-acceptance checkpoint failure cannot replace accepted child memory. Child lifecycle resets step and unfinished specification while retaining selected mode/bootstrap; later child writes leave the parent unchanged. Shared unknown metadata and unrelated provenance survive. Native fork caller evidence is item 35.
|
|
39
|
+
33. **Awaited current-memory recovery stays read-only:** Awaited loader matrices at limits 0/7 preserve current same-session values, step, revisions and bootstrap beside independently advanced shared streams without inheriting foreign private memory or unfinished specifications. No composed history is invented and no canonical files change, even when the in-memory view folds wider tails. Recovered caches cannot authorize lifecycle/patch publication. An independent partial writer held over two seconds proves cancelable coherent capture; injected cancellation during capture prevents cache installation. Missing roots/private authority remain absent, malformed/partial/identity/provenance errors retain the prior cache and bytes. Native failed-Stop wiring evidence is item 35.
|
|
40
|
+
34. **Lifecycle fixtures await completion without trapping withdrawal:** Direct startup/tree fixture calls await their handlers before inspecting completed state. Preparation, response, Start, Stop and Telegram inspection cancellation families dispatch replacement selection separately, prove the obsolete operation withdraws while storage remains held, then release exclusion and join the replacement. Canonical-byte and stale trace/notice assertions remain; UI stability is measured after legitimate replacement completion. Item 35 records the resulting native cutover.
|
|
41
|
+
35. **Native branch restoration awaits one owned lifetime:** Extension witnesses hold exclusion across startup, tree, auto-start and failed-Stop reload: mode stays passive, private reads/patches report the pending selection, and files/checkpoints stay unchanged until acceptance. Joined Starts stay inert; bootstrap derived after waiting shares the single runtime write. Replacement selection/shutdown withdraw obsolete restoration while storage remains held. Stop changes the pending acceptance's policy without revoking its memory operation; mode separation is covered by items 37–38. Failed-Stop reload stays read-only and fenced; post-acceptance checkpoint failure warns without rollback or replay. Native tree/fork witnesses hold a publisher, then require selected private memory, unchanged parent-private files and matching next-provider input. The initial cutover probes, except their shutdown case, also failed against pre-cutover synchronous orchestration.
|
|
42
|
+
36. **Recovery joins, draining and cache installation retain separate owners:** Start cancellation returns while independently owned restoration still waits; physical identity stays pinned across that join, so a changed owner cannot be activated. Recovery-domain tests preserve other joiners and observe late owner failures even after pre-cancellation. Shutdown waits for both current and superseded restoration operations, including delayed reader completion. Passive reads remain detached when physical CWD changes, and late native cancellation prevents both passive and private recovery cache installation. A private patch accepted between passive capture and attachment completion retains its newer cache and canonical bytes. These extension/domain witnesses supplement the native cutover probes rather than claiming universal idle Abort support.
|
|
43
|
+
37. **Fork memory creation is independent of active/passive policy:** The extension native-fork/Start-retry × warm/cold × retained/expired matrix holds exclusion while Stop selects passive policy. The copy must remain pending rather than be cancelled, including when Stop withdraws its Start waiter. After release, retained source memory is accepted once with disabled policy, passive patching changes child-owned memory without enabling the mode, and native trace reopening through `SessionManager.open` preserves that child state. Expired source history refuses canonical publication and substitute checkpoints. These fixtures replace the former tests that codified Stop cancelling copying; the separate public SDK control/next-provider witness is item 40; installed UI reachability remains unverified.
|
|
44
|
+
38. **Stop cannot manufacture a restoration failure:** Held-store retained-boundary and auto-start fixtures require repeated Stop to return promptly without cancelling memory acceptance, adding an error fence or publishing early. Release accepts the selected/initial memory with passive policy, permits passive patches and retains them across reload. Stop→Start while waiting activates only after memory acceptance. A separate Start-owned attachment witness withdraws the Start waiter without cancelling restoration. The native SDK tree witness invokes `/state-flow-stop` during awaited `navigateTree`, then verifies selected private memory, passive tool availability and a successful next-provider patch; selected conversation remains visible and later-branch private memory stays absent. This public SDK route is not installed Telegram/TUI reachability certification.
|
|
45
|
+
39. **Native Skill compilation reconciles an independent writer without private leakage:** The Global/CWD × duplicate/competing/invalid matrix runs real Pi acquisition and compiler tool calls while a separate process publishes between the model's source read and its patch. Identical compiler output leaves shared checkpoint/tail/provenance bytes, scope revision and runtime step unchanged. Different output replaces the complete artifact and matching source-hash/compiler evidence, preserves independent peer fields, and advances the affected revision once. The next provider sees adopted shared state and local private memory, never the peer's private sentinel; all five peer-private files remain unchanged. An invalid compilation combined with a Session mutation rejects without changing any captured canonical file or accepting the accompanying Session field. The peer uses the runtime publication API, not a second provider; these ordered interleavings supplement the separate held-writer cancellation tests.
|
|
46
|
+
40. **A public SDK host can control a child while fork binding awaits memory:** The fixture observes the public `createAgentSession` result before awaiting `bindExtensions`, without private SDK hooks or direct calls to State Flow command handlers. During a held-store `runtime.fork`, it calls the child's public `prompt('/state-flow-stop')`; an optional subsequent Start waits for memory acceptance. Stop returns while the fork remains pending. After release, the child has a distinct identity, selected private memory and the requested passive/active policy. Its next provider sees selected rather than later-parent private state, successfully patches child memory and leaves all parent-private files unchanged. This certifies the embedding route on the tested SDK, not whether the installed CLI or Telegram exposes that child before its runtime replacement completes.
|
|
28
47
|
|
|
29
48
|
## Additional preservation boundaries
|
|
30
49
|
|
|
31
|
-
- `tests/continuation.test.ts` independently advances global/CWD at limits 0/7, then proves read-only eligibility agrees with actual restore and preserves the session layer, including shared-only transitions. `tests/temporal.test.ts` covers sparse/inherited session lineage, conflicting checkpoint/tail identities and parents, future patches, and immutable inputs. The runtime restore/fork contradiction witnesses preserve source/target files rather than accepting a new origin over mixed session files. The native “real Pi refuses contradictory session files without passive substitution and retries the repaired selection” witness retains shared access, fences
|
|
50
|
+
- `tests/continuation.test.ts` independently advances global/CWD at limits 0/7, then proves read-only eligibility agrees with actual restore and preserves the session layer, including shared-only transitions. `tests/temporal.test.ts` covers sparse/inherited session lineage, conflicting checkpoint/tail identities and parents, future patches, and immutable inputs. The runtime restore/fork contradiction witnesses preserve source/target files rather than accepting a new origin over mixed session files. The native “real Pi refuses contradictory session files without passive substitution and retries the repaired selection” witness retains shared access, fences private access and publication, permits local Stop without a substitute checkpoint, and succeeds after exact-source repair. These are synthetic integrity tests, not evidence of spontaneous cross-session leakage.
|
|
32
51
|
- `tests/extension.test.ts` distinguishes pre-runtime Stop from accepted-runtime Stop across passive configurations: global-only and shared storage remain unchanged, repeated Stop/reload retain the disabled marker, and subsequent Start/passive patch establishes a retained session boundary. Malformed/incomplete scope files remain unavailable; Stop does not repair CWD-only storage, while explicit Start may initialize its wholly absent global scope. The native “real Pi Stop preserves a global-only passive branch through reload, patch, and Start” witness checks the same lifecycle without Git. `tests/invariants.test.ts` rejects the retired Skill converter in both package and domain exports; existing Skill/transition witnesses preserve real source hashing, hashing failure, and retired-field rejection.
|
|
33
52
|
- `tests/durable.test.ts` and `tests/storage.test.ts` cover canonical-format rejection, owned regular files, raw-byte rollback, prepared-output receipts, stale/omitted-scope CAS, literal paths, malformed cohorts, and shared-writer exclusion.
|
|
34
53
|
- `tests/git.test.ts` proves settled backup preserves unrelated staged/index-only/worktree data, already-staged owned deletions, Git ignore/filter policy, literal paths, and opaque bytes. Controlled Git pauses before/after snapshot capture allow an independent canonical writer to advance and create a new scope; the commit must match exactly one captured cohort, never mixed live bytes. Every observed Git command runs outside the backup's canonical lock. Namespace/type witnesses exclude unrelated traversal and refuse non-regular sources. No-op/unowned-only initial backups skip; Git/index failures preserve canonical acceptance and caller data. `tests/episode.test.ts` proves one diagnostic-only attempt at `agent_before_settle`, without answer changes or an automatic retry.
|
|
@@ -40,7 +59,7 @@ This is a maintained property-to-test map for the current canonical-file contrac
|
|
|
40
59
|
- `tests/integration.test.ts` — “real Pi boundary continuation keeps accepted State Flow memory” exercises actual companion `turn_end` and `agent_before_settle` draft entries and continuation. Each next provider input has exactly one current-memory projection before/after a continued patch, the accepted response and current tool declarations. There is only one `before_agent_start`, no restored specification in runtime checkpoints or model projection, exactly one requested continuation and correct final response/step. The pure “projects accepted memory without resurrecting a completed specification for boundary continuation” witness preserves source state, omits lazy bodies, and keeps available context when both specification and native capture are absent.
|
|
41
60
|
- `tests/integration.test.ts` — “real Pi context edits remain canonical through tools, tree selection and reload” proves native user-content replacement, assistant/custom-message omission and live tool-result replacement reach provider input without resurrecting raw history. Selecting an unedited branch restores its native view without overwriting accepted semantic files; selecting the edited branch and reloading preserves its edits and independent memory. Raw JSONL stays append-only.
|
|
42
61
|
- `tests/integration.test.ts` — “real Pi structured prompt composes with context hooks” covers active, passive, disabled and explicit foreign-force modes. Conversation hooks exclude systems; full-system hooks receive them; companion before-run and per-request sections plus actual tool declarations survive. User specifications remain outside system authority. Stop/Start on subsequent user requests removes/reinstates the owned section once.
|
|
43
|
-
- `tests/integration.test.ts` — “real Pi preserves native run identity across mode toggles” covers initially enabled, mid-tool Start and repeated Start/Stop with passive bootstrap on/off. Actual provider inputs retain original user/read evidence but not the preceding disabled request; persisted Stop markers name the observed native user, and fixture reload preserves trajectory, frozen state and raw trace prefix. `tests/compaction.test.ts` — “native session boundaries invalidate observed run capture without projection reacquisition” pairs session-start/tree resets with an admitted unchanged-run control.
|
|
62
|
+
- `tests/integration.test.ts` — “real Pi preserves native run identity across mode toggles” covers initially enabled, mid-tool Start and repeated Start/Stop with passive bootstrap on/off. Actual provider inputs retain original user/read evidence but not the preceding disabled request; persisted Stop markers name the observed native user, and fixture reload preserves trajectory, frozen state and raw trace prefix. `tests/compaction.test.ts` — “native session boundaries invalidate observed run capture without projection reacquisition” pairs session-start/tree resets with an admitted unchanged-run control. Run identity remains native rather than guessed; an independent optional Stop-marker flag preserves uncompiled bootstrap context without changing the captured anchor.
|
|
44
63
|
- `tests/integration.test.ts` — “real Pi refreshes protocol within the same run” covers mid-read Stop, passive Stop, mid-read Start and accepted-boundary Stop/continuation. The next provider request has current protocol/tools, foreign context and tool evidence, no elevated specification and only one user-run preparation; response reconciliation follows the actual mode and earlier native system frames remain intact. `tests/context.test.ts` — “refreshes only owned system protocol without mutating native frames” covers old/replaced/deleted sections, foreign content/tools, conversation identity/order, frozen inputs, absent-frame non-synthesis and unchanged-delta no-ops.
|
|
45
64
|
- `tests/integration.test.ts` — “real Pi recovery omits failed attempts without accepting them” checks retry/length/overflow through actual SDK dispatch: durable omission edits, raw trace preservation, unchanged accepted response/step until successful recovery, native split-turn summaries and fixture reload. “real Pi edited-context accounting discards stale provider usage” checks reduced native usage after replacement and one subsequent inference without phantom compaction.
|
|
46
65
|
- `tests/integration.test.ts` — “real Pi settled dispatch preserves deferred companion work” covers low pressure, real owned compaction and explicit compaction refusal. The native completion/error callback finishes before settled dispatch returns; all companion observers precede exactly one deferred request, which sees accepted memory and reconciles the expected response/step. The admitted case verifies an actual State Flow-owned compaction rather than relying only on a token estimate.
|
|
@@ -49,9 +68,10 @@ This is a maintained property-to-test map for the current canonical-file contrac
|
|
|
49
68
|
- `tests/context.test.ts` and `tests/extension.test.ts` prohibit process queries during ordinary context/history reads. `tests/transition.test.ts` rejects stale staging even when semantic values coincide across different causal boundaries.
|
|
50
69
|
- `tests/status.test.ts` distinguishes selected temporal history depth from retained per-scope tails and unavailable materialization from an empty state.
|
|
51
70
|
- `tests/artifact.test.ts` and extension/native integration witnesses prove exact registered-path inspection, `size + mtimeNs` evidence, non-destructive unavailable/symlink handling, owning-scope removal, runtime-only changed-source hints, and stable-read acceptance without directory discovery. The native newly-adopted-artifact witness proves run preparation refreshes live shared state before proven-missing maintenance and the first inference. Artifact/acquisition/rehydration tests and “real Pi ordinary artifact invalidations share the public fingerprint classifier” cover equal/changed/missing/malformed fingerprints, pre-epoch timestamps, compiler invalidation, detached read plans, legacy hash handling, and effective Skill masking while preserving Skill hashing.
|
|
52
|
-
- `tests/protocol.test.ts`
|
|
71
|
+
- `tests/protocol.test.ts` covers long/spaced/Windows/Unicode operands, exact scopes and basenames, trailing causes, prose apostrophes, nested/aggregate/cyclic causes, repeated compaction and malformed escaped quotes. Native “real Pi transports actionable long-path diagnostics and accepts an exact-target correction” cases cover all three scopes: rejection preserves canonical bytes, the next provider receives the same actionable error text despite empty `toolResult.details`, a corrected patch uses the original target, and opt-in logs retain full attempted input/path/call identity. The nested-publication native witness separately proves textual cause delivery. Extension lifecycle tests preserve filenames, `EEXIST`, one failed-Start notice and fenced-write recovery guidance under long spaced store paths; status and Telegram tests preserve causes/targets within their display budgets. `tests/episode.test.ts` retains the Git operation and missing-identity reason without undoing accepted memory. Tool failures keep heading separation.
|
|
72
|
+
- Runtime/Skill guidance names the reported artifact owner. Registered-Skill unit and native witnesses use Pi's public command source metadata to map user/project/temporary Skills to global/CWD/session, ignore unregistered `SKILL.md` reads, skip matching hashes, permit unrelated patches while compilation remains optional, retain strict attempted-output validation, and publish source-hash provenance to the exact reported owner.
|
|
53
73
|
- `tests/transition.test.ts` rejects authored provenance fields and field deletions in every scope through both staging entrypoints, without requiring a preceding read; legacy semantic edits and whole-artifact deletion remain valid. The native “real Pi rejects no-read provenance forgery atomically and accepts a corrected model patch” witness proves whole-cohort rejection and recovery, while existing artifact/Skill tests preserve runtime-owned compilation evidence and legacy decoding.
|
|
54
|
-
- `tests/storage.test.ts` covers exact file-cohort references, file CAS/rollback, and shared writer exclusion. `tests/runtime.test.ts` and native Pi lifecycle tests cover retained-boundary restart, immediate barriers, finalized response, config-only stop, and unavailable-reference provenance. Canonical files supply current state and bounded hot history; arbitrary cold revision recovery is unsupported.
|
|
74
|
+
- `tests/storage.test.ts` covers exact file-cohort references, file CAS/rollback, and shared writer exclusion. The asynchronous transaction witnesses pause an independent writer between tail and metadata renames for longer than the old wait budget: the waiting process remains responsive and captures the complete accepted receipt only after release. Local waiters, cancellation before acquisition/publication, interrupted/malformed/empty locks, root/lifetime binding, active recursive refusal, deferred work after context expiry, rollback and replaced-owner preservation are separately checked. The current-head and response witnesses above additionally prove awaited model patch/finalization integration; runtime startup/recovery and exact restoration/fork cutover, including Start's initial attachment and unaccepted-fork paths, are covered by items 35–40. Current-head Start and accepted-runtime Stop are covered above; optional settlement backup uses the selected no-signal deferral rather than an upstream release gate. `tests/runtime.test.ts` and native Pi lifecycle tests cover retained-boundary restart, immediate barriers, finalized response, config-only stop, and unavailable-reference provenance. Canonical files supply current state and bounded hot history; arbitrary cold revision recovery is unsupported.
|
|
55
75
|
- `tests/runtime.test.ts` and native integration cover retained-boundary restoration and fork after lowering `historyLimit` to 0/1: current shared values/provenance and selected private state survive folding, parent-private fork files remain unchanged, out-of-window selections fail closed, and later increases do not reconstruct discarded history. `tests/config.test.ts` covers optional agent configuration, path precedence/expansion, invalid input, load-time caching and read-only behavior. Session tests and native Pi distinguish configured new-session auto-start from branch-local resume/tree/stop. `tests/extension.test.ts` proves registered artifacts are reconciled only at enabled inference boundaries. The global default is manual; CWD materialization alone no longer grants automatic activation.
|
|
56
76
|
|
|
57
77
|
## Validation and limits
|
|
@@ -4,15 +4,34 @@ For the concept and installation, start with the [README](../README.md). This gu
|
|
|
4
4
|
|
|
5
5
|
## Session behavior
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
### Active, passive and configured off
|
|
8
|
+
|
|
9
|
+
State Flow has three distinct model-facing states. **Active versus passive changes the agent's workflow, not the existence of disk memory.**
|
|
10
|
+
|
|
11
|
+
- **Active:** Both `read_state` and `patch_state` are available, subject to host restrictions and valid memory authority. Prompts require the agent to consolidate future-relevant results into state before ending an iteration. The final meaningful semantic patch preserves decisions, outcomes and continuation needed after the completed conversation is removed from model projection. The next iteration starts from accepted state and its new input, not the previous iteration's completed reasoning. Native trace remains inspectable; a clean model context does not mean deleting Pi history.
|
|
12
|
+
- **Passive:** Both memory tools remain available with the default passive configuration. Existing state is projected into context, and the agent may read or patch it as useful. Ordinary conversation continuity remains: State Flow does not impose the active iteration-ending context reset or the same mandatory consolidation pressure. Accepted patches still reach the same canonical store with the same validation and ownership guarantees.
|
|
13
|
+
- **Configured off:** Neither memory tool is exposed to the model, and State Flow injects no bootstrap/state context. This is distinct from passive mode, and does not mean deleting stored memory. With the current configuration, this means an inactive branch plus `passiveTools: false` and `passiveBootstrap: false`. `autoStart: false` only disables automatic activation of new sessions; it does not disable passive memory or override an already active resumed branch.
|
|
14
|
+
|
|
15
|
+
The two passive settings are independent: turning off only tools or only projection produces a deliberately partial integration, not the fully off state. Host restrictions and genuine unavailable/corrupt-memory fences are separate from these mode definitions.
|
|
16
|
+
|
|
17
|
+
**Completion is semantic, not ceremonial.** In active mode, necessary final state changes must be accepted before relying on state-only continuation. This does not require an empty `patch_state` when memory is already current, an extra model turn, or a special finalization tool. Each `patch_state` remains an inference barrier, not automatically the end of an iteration; the runtime separately persists the accepted ordinary answer as Session `response` and completes the run.
|
|
18
|
+
|
|
19
|
+
Here, “bootstrap from state” means constructing model context from durable memory. The implementation's `meta.bootstrap` is narrower: the one transition run that retains an existing conversation while active mode is first enabled and that conversation is compiled into state. It is not a flag that must be re-entered on every active iteration. Native compaction is also separate from model-context projection; its safety checks and usage threshold still apply.
|
|
20
|
+
|
|
21
|
+
Start/Stop change active/passive behavior and respect the configured passive capabilities. They do not choose a different storage algorithm or cancel creation of a fork's private memory. A fork receives its own initial Session state from the selected parent boundary, then lives independently in either mode. See [fork support](#fork-support-and-limits).
|
|
22
|
+
|
|
23
|
+
### Lifecycle operations
|
|
24
|
+
|
|
25
|
+
`/state-flow-start` enables the current Pi branch over validated current same-session memory, initializing absent storage when safe. No remote is required. Starting mid-conversation retains Pi's active context for one complete bootstrap run, during which the agent must compile future-relevant information into state. Repeating Start while already active leaves the in-progress run unchanged. On an attached branch, current-memory activation waits asynchronously for a coherent canonical cohort; pending repeats share the wait. Until acceptance, existing passive context, tools and any Stop fence remain in effect. Stop or selection can withdraw obsolete activation; an error after acceptance does not undo enabled memory.
|
|
8
26
|
|
|
9
27
|
- **New session:** Passive durable memory is available by default without starting an episode. `autoStart` promotes genuinely new sessions into active State Flow; an active new session has its own empty session layer and inherits global/CWD state, never another session's private continuation.
|
|
10
28
|
- **Resume:** Restores the selected session's stored enablement, state, and lineage. Agent-level `autoStart` does not override a resumed branch.
|
|
11
29
|
- **Tree navigation:** Restores the selected retained private boundary over live shared scopes without checking out or resetting the shared store.
|
|
12
|
-
- **Abort inference:** Stops generation while already accepted patches remain durable for continued work and corrected direction in the same session.
|
|
30
|
+
- **Abort inference:** Stops generation or an outstanding response-publication wait while already accepted patches remain durable for continued work and corrected direction in the same session. Cancellation before response acceptance keeps the previous response and unfinished run; cancellation after acceptance never rolls it back. It does not require immediate remote replication.
|
|
13
31
|
- **Native boundary continuation:** A companion may continue through Pi's `turn_end` or `agent_before_settle` boundary without a new user prompt. State Flow keeps projecting current memory and accepted response across those requests and subsequent patches; it does not restore the completed specification or request another turn itself.
|
|
14
|
-
- **Stop:** Ends active episode semantics and returns to the configured passive bootstrap/tool combination. For a proven pre-runtime branch, it records only the existing disabled checkpoint in Pi, without creating canonical files or publishing the passive view; later Start or passive patch remains available. For an accepted runtime, it adopts unrelated shared-state changes without semantic/provenance writes or step changes
|
|
15
|
-
- **
|
|
32
|
+
- **Stop:** Ends active episode semantics and returns to the configured passive bootstrap/tool combination. For a proven pre-runtime branch, it records only the existing disabled checkpoint in Pi, without creating canonical files or publishing the passive view; later Start or passive patch remains available. For an accepted runtime, it adopts unrelated shared-state changes without semantic/provenance writes or step changes. Same-session conflicts refuse canonical publication but do not prevent local disablement. Its frozen handoff uses the accepted view. It does not change passive or automatic-start policy.
|
|
33
|
+
- **If Stop cannot persist:** Active mode still turns off. The last accepted cache remains readable and all available native conversation is retained. Pi's native session records the local Stop and a write fence, without a replacement semantic checkpoint. Tree/reload/resume stay passive and load validated current same-session memory read-only; they never replay an older selection over another writer. Unavailable or corrupt memory stays unavailable. Repeated Stop does not retry or clear the fence. Explicit Start revalidates current memory and clears the fence only after canonical acceptance. This fallback requires Pi's native trace to remain writable; it does not repair storage. The canonical config may still say enabled, but the native failed-Stop policy overrides it on this branch.
|
|
34
|
+
- **Continue after Stop:** The same physical session retains a frozen state handoff, any interrupted current request and tool trajectory (including late results), and post-stop conversation. Outside an unfinished bootstrap, a proven active boundary excludes completed earlier conversation; when native split-turn compaction removed the original request anchor, Stop instead preserves the available summary and tools without reconstructing discarded raw input. An unfinished bootstrap keeps all available native context, or the earlier passive boundary it received; repeated Start/Stop cannot move that boundary past uncompiled conversation. An interrupted run retains its captured anchor even after Pi becomes idle, falling back to all available context when that anchor is unknown. Other extensions' custom context survives. Only Stop after a completed idle run retains just later conversation plus foreign custom context. Reload/resume/tree preserve this projection; new/forked physical sessions do not inherit it. Mid-tool Start and repeated Start/Stop retain the first user event already observed while disabled, so subsequent Stop does not mistake that busy run for idle. Active restart uses the retained projection for one bootstrap run.
|
|
16
35
|
- **Completed-history compaction:** After an accepted run settles without queued input, State Flow asks Pi for a native compaction boundary only when public context usage reaches 24,000 tokens. No extra model summary is requested; Pi keeps the complete latest run—from its original request through steering, tools, foreign context and final answer—in active history and retains the complete append-only JSONL/tree. State Flow uses the native first-user anchor, independent of image-normalization hints or later steering; images and earlier tool results remain available to the model. Uncertain projection retains available context without changing that anchor. A missing or ambiguous native anchor skips compaction instead of choosing the last steering message. On resume, native `buildContextEntries()` and TUI rendering omit the older completed prefix. Unknown or smaller usage skips the request, and custom Pi retention settings may still decline it benignly. Foreign custom context in the removed prefix, bootstrap/abort/error, Stop and pending input prevent State Flow-owned shortening; ordinary manual/threshold/overflow compaction remains native and may preserve unfinished work not yet patched into memory.
|
|
17
36
|
|
|
18
37
|
State Flow does not undo tool effects. After interruption or returning to an older branch, check the relevant workspace or external system before repeating consequential operations. Restored memory is not restored reality.
|
|
@@ -25,7 +44,7 @@ The child starts at step zero and a new temporal origin. Its copied tail obeys t
|
|
|
25
44
|
|
|
26
45
|
Initial copying requires a native fork start event, a regular canonical direct-parent session file, matching CWD/identity, a readable temporal source and an unused child namespace. Missing/unsafe evidence or CAS conflicts leave the copy unavailable rather than importing unrelated or newer private state. Explicit Start can retry an unaccepted copy in the same loaded fork after the cause is corrected.
|
|
27
46
|
|
|
28
|
-
Selecting a copied parent checkpoint through the child's `/tree` does not make it child-owned:
|
|
47
|
+
Selecting a copied parent checkpoint through the child's `/tree` does not make it child-owned: historical restoration stays disabled without resetting existing child data. Select a child-owned checkpoint, resume the original session, or explicitly Start from the validated current child-owned memory; Start does not copy newer parent data. Cold recovery before the first child checkpoint, startup paths lacking the fork event, in-memory parent locators and cross-CWD imports remain outside this slice. File-only copying requires an exact still-available source cohort. See the [contract](fork-contract.md) and [SDK compatibility boundary](compatibility.md#public-host-seams); do not rewrite UUIDs or delete pointers to force recovery.
|
|
29
48
|
|
|
30
49
|
## Configuration
|
|
31
50
|
|
|
@@ -46,9 +65,9 @@ The canonical store is `state-flow/` beneath the agent directory. Keeping config
|
|
|
46
65
|
- `autoStart`: Defaults to `false`. When `true`, genuinely new sessions use the same initialization as explicit Start, including fresh CWDs.
|
|
47
66
|
- `passiveBootstrap`: Defaults to `true`. Projects existing effective durable memory into ordinary model context without creating scopes, publishing, or starting an episode.
|
|
48
67
|
- `passiveTools`: Defaults to `true`. Exposes `read_state` and `patch_state` outside active episodes. Reads remain side-effect free; the first explicit patch may initialize absent canonical storage but never converts predecessor formats or enables an episode or State Flow compaction.
|
|
49
|
-
- `logging`: Defaults to `false`. When enabled, records rejected patches and accepted-answer reconciliation failures locally at `tmp/state-flow/logs.jsonl` beneath the agent directory. Asynchronous Git push failures are recorded there even when this setting is off.
|
|
68
|
+
- `logging`: Defaults to `false`. When enabled, records rejected patches, preparation failures and accepted-answer reconciliation failures locally at `tmp/state-flow/logs.jsonl` beneath the agent directory. Asynchronous Git push failures are recorded there even when this setting is off.
|
|
50
69
|
- `showSuccessfulPatches`: Defaults to `true`. In interactive Pi, successful `patch_state` rows show only the applied pretty-printed JSON arguments, with blank lines between adjacent memory sections; set it to `false` to keep only the compact summary. Rejected calls still use ordinary error rendering; State Flow adds no private validation turn.
|
|
51
|
-
- `historyLimit`: Defaults to `7` and accepts integers from `0` through `100`. It bounds materialized-history and scope patch-history offsets. Lowering it on reload/restore/fork folds excess tails forward without losing current state; selected boundaries outside the new window are unavailable. Zero retains only current checkpoints. Raising the limit affects only future retention and cannot reconstruct discarded history.
|
|
70
|
+
- `historyLimit`: Defaults to `7` and accepts integers from `0` through `100`. It counts accepted semantic transitions, **not elapsed time or conversation length**. It bounds materialized-history and scope patch-history offsets. Lowering it on reload/restore/fork folds excess tails forward without losing current state; selected boundaries outside the new window are unavailable. Zero retains only current checkpoints. Raising the limit affects only future retention and cannot reconstruct discarded history.
|
|
52
71
|
|
|
53
72
|
Settings are read once at extension load. After editing, use `/reload` or restart Pi. A missing file uses defaults without creating a configuration file; malformed JSON, unknown keys, or invalid values fail loading rather than silently selecting another store.
|
|
54
73
|
|
|
@@ -58,7 +77,7 @@ Settings are read once at extension load. After editing, use `/reload` or restar
|
|
|
58
77
|
|
|
59
78
|
Rejected-call records may contain exact attempted arguments and useful draft text, plus the error category and tool/call identity. Asynchronous push failures retain the available redacted Git error there regardless of `logging`; interactive warnings stay short and appear once per failure streak, then reset on success. If logging the push failure is unavailable, one warning exposes the available detail instead. Successful patches are not logged; reasoning bodies are excluded. Logs are not semantic state, scope metadata, Pi checkpoints, or publication input. If the log path overlaps a custom state repository, capture fails closed instead of committing it. A logging failure changes no accepted state and produces at most one local warning.
|
|
60
79
|
|
|
61
|
-
Logs remain local unless you move them; rotation/deletion is operator-owned. Treat
|
|
80
|
+
Logs remain local unless you move them; rotation/deletion is operator-owned. State Flow-authored errors and warnings display as one compact line: long operands are elided with `…` before prose so the operation, offending scope, target basename/suffix and actionable reason remain visible. Nested causes travel as text because Pi tool results need not retain `Error.cause`. Enabled diagnostic records keep the full target and rejected input for investigation. An explicit Start retry reports its final failure once rather than repeating its startup warning. Tool failures still keep the required blank line beneath their heading. Treat logs and state files as private. Removing a secret from current state does not erase older offsets, Git history, native sessions, or remote copies.
|
|
62
81
|
|
|
63
82
|
## Status and controls
|
|
64
83
|
|
|
@@ -71,7 +90,7 @@ Logs remain local unless you move them; rotation/deletion is operator-owned. Tre
|
|
|
71
90
|
|
|
72
91
|
Tail counts are not history depth: inherited records may predate the active origin. Failed inspection reports unavailable evidence, not invented empty state. Status is observational: it does not read source files, calculate fingerprints, create invalidations, or mutate semantic state.
|
|
73
92
|
|
|
74
|
-
The terminal indicator is `state-flow G15/C8/S31` only in active mode. Global, CWD and Session own independent semantic revisions; one atomic transition advances each materially changed scope once, including session-only response reconciliation. Effective has no scalar counter and uses the `G#/C#/S#` revision vector. When `pi-telegram` is available, its main-menu section shows that vector only while active and `State Flow: off` while passive. Requested owner-scope Rich snapshots show `#revision`; Effective shows the vector. Telegram inspection
|
|
93
|
+
The terminal indicator is `state-flow G15/C8/S31` only in active mode. Global, CWD and Session own independent semantic revisions; one atomic transition advances each materially changed scope once, including session-only response reconciliation. Effective has no scalar counter and uses the `G#/C#/S#` revision vector. When `pi-telegram` is available, its main-menu section shows that vector only while active and `State Flow: off` while passive. Requested owner-scope Rich snapshots show `#revision`; Effective shows the vector. During a failed-Stop write fence, inspection uses the accepted cache without a potentially conflicting refresh. Otherwise, Telegram inspection waits cancelably to load or refresh one coherent shared view, even when passive model tools are disabled. It does not initialize, publish, or advance storage. Data and displayed revisions belong to the same observation; absent/invalid memory stays unavailable. Stop, session/tree changes and shutdown cancel obsolete observations. The button acknowledges immediately, and a late failure appears in the menu instead of an expired callback popup. Start requested during a run waits for settlement; Stop currently applies immediately. The adapter is optional and the Pi commands remain available without it. Open implementation work is tracked in [BACKLOG.md](../BACKLOG.md).
|
|
75
94
|
|
|
76
95
|
## Storage and recovery
|
|
77
96
|
|
|
@@ -86,11 +105,13 @@ Each scope materializes an anchored semantic-only `checkpoint.json` plus semanti
|
|
|
86
105
|
|
|
87
106
|
### Missing, partial, and malformed storage
|
|
88
107
|
|
|
89
|
-
A checkpoint and tail are one semantic pair. If both live files for
|
|
108
|
+
A checkpoint and tail are one semantic pair. If both live files for a global or CWD scope disappear, State Flow treats that complete absence as current empty shared reality. An authored `patch_state` applies to that empty basis under exclusion; normal file-cohort CAS creates the accepted pair without resurrecting cold values or orphaned compilation evidence. Raw precomputed replay still refuses a removed selected target rather than replaying stale normalized changes.
|
|
90
109
|
|
|
91
110
|
Exactly one surviving pair member is corruption and fails closed. Present malformed JSON, incomplete predecessor envelopes, semantic/metadata boundary mismatches, identity contradictions, and partial session runtime evidence also remain fail-closed and are not replaced. A missing or expired private retained boundary is unavailable; State Flow does not substitute Git history or newer private files.
|
|
92
111
|
|
|
93
|
-
After a selected-boundary failure, configured passive access may still expose current global/CWD memory, but it never grants access to the unavailable session layer or permission to publish an empty replacement.
|
|
112
|
+
After a selected-boundary failure, configured passive access may still expose current global/CWD memory, but it never grants access to the unavailable historical session layer or permission to publish an empty replacement. Historical session reads and every `patch_state` refuse without changing canonical files or appending substitute checkpoints. Stop remains available and records the native policy/write fence described above; a subsequent reload may expose validated current memory read-only, not the unavailable selected history. Status distinguishes a write fence from unavailable materialization.
|
|
113
|
+
|
|
114
|
+
**Explicit Start uses current memory, not unavailable history.** It independently validates the current same-session canonical cohort, preserving private memory, artifact provenance, revisions, step and available aligned history. Expired active/passive pointers and unfinished runtime work no longer block activation. A pre-runtime selection also keeps current accepted same-session memory rather than resetting it. Start bootstraps the conversation available on the selected Pi branch without resurrecting an old unfinished specification or claiming that expired historical private state was restored. Independently advanced shared scopes may require a new temporal origin; unavailable history is never invented. Exact-cohort CAS rejects a concurrent writer, and incomplete/corrupt storage remains untouched. An unaccepted native fork still retries its exact source rather than inventing child memory. When evidence is missing, repair it, select a retained boundary, or open a genuinely new Pi session.
|
|
94
115
|
|
|
95
116
|
Missing artifact provenance inside an otherwise complete scope `meta.json` means compilation evidence is unavailable while semantic state remains usable; removing the whole metadata file also removes temporal authority and fails closed. An unavailable registered source path does not prove that durable artifact routing was deleted, and external files are never created. State Flow has no durable push queue or publication-worker lease; failed replication is attempted again only after a later accepted turn. See the complete [filesystem recovery contract](filesystem-recovery.md).
|
|
96
117
|
|
|
@@ -98,7 +119,7 @@ Missing artifact provenance inside an otherwise complete scope `meta.json` means
|
|
|
98
119
|
|
|
99
120
|
Canonical scope/runtime files own current materialization and retained hot history regardless of Git availability. Pi checkpoints identify a retained semantic boundary, not a Git commit or arbitrary historical snapshot. Restart and branch restoration fail closed when the selected boundary has expired rather than substituting newer files as the selected past.
|
|
100
121
|
|
|
101
|
-
After an accepted turn has reconciled its response, Pi 0.87's final actionable `agent_before_settle` boundary may create one best-effort backup commit when Git is available. If the attached branch has an explicitly configured remote/ref, State Flow starts a non-interactive asynchronous push of the exact current commit without force. Settlement does not wait for the network. Within one Pi process, an in-flight push per repository skips overlapping attempts; a later accepted turn retries the latest backup without a durable queue. Session shutdown waits for that repository's in-flight push to close or time out, suppressing push-failure reporting after shutdown begins. Commit or push failure is diagnostic-only; repeated push failures warn once per failure streak and remain locally diagnosable. Git availability never changes semantic authority, step, or retained lineage.
|
|
122
|
+
After an accepted turn has reconciled its response, Pi 0.87's final actionable `agent_before_settle` boundary may create one best-effort backup commit when Git is available. The local attempt completes before settlement continues. Its capture waits cancelably when Pi supplies an operation signal; on Pi 0.87 that signal is absent at settlement, so an occupied backup/storage mutex explicitly defers backup until a later accepted turn instead of trapping Abort. Deferral neither changes memory nor starts a push. Shutdown cancels and drains pending local attempts. If the attached branch has an explicitly configured remote/ref, State Flow starts a non-interactive asynchronous push of the exact current commit without force. Settlement does not wait for the network. Within one Pi process, an in-flight push per repository skips overlapping attempts; a later accepted turn retries the latest backup without a durable queue. Session shutdown waits for that repository's in-flight push to close or time out, suppressing push-failure reporting after shutdown begins. Commit or push failure is diagnostic-only; repeated push failures warn once per failure streak and remain locally diagnosable. Git availability never changes semantic authority, step, or retained lineage.
|
|
102
123
|
|
|
103
124
|
### Moving a store and the 0.17 format boundary
|
|
104
125
|
|
|
@@ -108,11 +129,17 @@ Starting with 0.17, State Flow accepts only its canonical checkpoint/tail, tempo
|
|
|
108
129
|
|
|
109
130
|
### Conflicts and interrupted publication
|
|
110
131
|
|
|
111
|
-
|
|
132
|
+
`patch_state` asynchronously waits for a live cooperating writer, with native cancellation and no ordinary-contention deadline. It then applies authored Global/CWD operations to current canonical values: unmentioned fields survive, overlapping assignments follow successful acceptance order, and correct repeats return `State already current.` without another semantic revision. Session remains private; invalid/unavailable evidence and an independently changed private cohort still fail closed. Do not repeat external actions while memory publication waits.
|
|
133
|
+
|
|
134
|
+
Final-answer reconciliation also waits cancelably, then saves the response and completed-run lifecycle together over current shared memory. Stop, session/tree changes, shutdown and a superseding answer cancel obsolete waits without modifying newer work. A failed or canceled publication leaves the prior response and unfinished run intact; it never requests a repair inference.
|
|
135
|
+
|
|
136
|
+
Run preparation and missing-artifact maintenance wait cancelably before the first enabled inference, accepting current shared memory and lifecycle together. If preparation fails, State Flow aborts that native operation rather than sending a rejected or stale draft to the provider; previously accepted memory remains available. Cancellation can leave the new request without a specification checkpoint, so its native conversation is conservatively preserved through idle Stop/reload. Boundary continuation never replays a completed specification.
|
|
137
|
+
|
|
138
|
+
Telegram shared inspection also awaits exclusion read-only. Backup capture also uses the awaited API, with the no-signal settlement exception described above. Accepted-runtime Stop switches local policy and passive projection immediately, then awaits runtime-only persistence; pending repeats share that operation. Selection/shutdown or a successful Start cancel obsolete Stop work. An available host operation signal can cancel persistence without re-enabling mode; idle commands do not necessarily have that signal. Current-head Start similarly waits before enabling mode, with Stop/selection/shutdown cancellation and any available native operation signal. Startup, tree, auto-start, fork and failed-Stop reload restoration, including Start's initial attachment and fork retry, also await exclusion; until acceptance, mode stays passive and private memory reports the pending selection. Interrupted, malformed or unreadable locks are never stolen. Reconcile the owner rather than deleting locks or state directories merely because an operation is slow. File-cohort exclusion, exact prepared bytes and compare-and-swap checks are not kernel-atomic multi-file transactions against nonparticipating writers.
|
|
112
139
|
|
|
113
140
|
Fatal process termination can leave an incomplete canonical file cohort. Before repair, quiesce all store writers and preserve the complete store plus selected Pi retained-boundary references. Reconcile exact files against the last complete checkpoint/tail/metadata cohort; no automatic crash repair or power-loss durability is promised. Git backup has no semantic recovery authority.
|
|
114
141
|
|
|
115
|
-
|
|
142
|
+
Independently valid shared streams may require a fresh composed origin without inventing cross-writer history. Authored patches use that current basis; raw precomputed replay still refuses an advanced target instead of applying stale normalized changes. Rollback restores only bytes still matching that publisher's output and preserves detected external changes. See [performance evidence](performance.md) for measured contention and the [acceptance map](temporal-acceptance.md) for the tested boundaries; the [backlog](../BACKLOG.md) owns open implementation work.
|
|
116
143
|
|
|
117
144
|
## Memory and source acquisition
|
|
118
145
|
|
|
@@ -121,6 +121,7 @@ export {
|
|
|
121
121
|
type StateScope,
|
|
122
122
|
type TerminalTransition
|
|
123
123
|
} from "./lib/state.ts";
|
|
124
|
+
export { PublicationBusyError } from "./lib/storage.ts";
|
|
124
125
|
export {
|
|
125
126
|
buildStateFlowSectionView,
|
|
126
127
|
createStateFlowTelegramAdapter,
|
|
@@ -131,6 +132,8 @@ export {
|
|
|
131
132
|
type StateFlowTelegramButton,
|
|
132
133
|
type StateFlowTelegramCallbackContext,
|
|
133
134
|
type StateFlowTelegramControlResult,
|
|
135
|
+
type StateFlowTelegramInspection,
|
|
136
|
+
type StateFlowTelegramInspectionPort,
|
|
134
137
|
type StateFlowTelegramLoader,
|
|
135
138
|
type StateFlowTelegramModules,
|
|
136
139
|
type StateFlowTelegramPort,
|
|
@@ -204,11 +204,11 @@ export function isArtifactMetadata(value: unknown): value is ArtifactMetadata {
|
|
|
204
204
|
}
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
-
export function validateArtifactRegistry(value: unknown): asserts value is ArtifactRegistry {
|
|
207
|
+
export function validateArtifactRegistry(value: unknown, context = "artifacts"): asserts value is ArtifactRegistry {
|
|
208
208
|
if (!isObject(value)) throw new Error("Artifacts must be a path-keyed JSON object");
|
|
209
209
|
for (const [path, metadata] of Object.entries(value)) {
|
|
210
210
|
if (path.trim().length === 0) throw new Error("Artifact path keys must be non-empty");
|
|
211
|
-
validateArtifactMetadata(metadata, path);
|
|
211
|
+
validateArtifactMetadata(metadata, `${context}[${JSON.stringify(path)}]`);
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
|
|
@@ -368,7 +368,7 @@ export function compileArtifact(update: ArtifactCompilationUpdate): CompiledArti
|
|
|
368
368
|
// Timestamps are runtime evidence; the model-visible entry never retains them.
|
|
369
369
|
const semantic = structuredClone(update.output) as ArtifactMetadata;
|
|
370
370
|
delete semantic.compiled_at;
|
|
371
|
-
validateArtifactMetadata(semantic, update.source.path);
|
|
371
|
+
validateArtifactMetadata(semantic, `${update.source.scope ? `${update.source.scope}.` : ""}artifacts[${JSON.stringify(update.source.path)}]`);
|
|
372
372
|
return {
|
|
373
373
|
semantic,
|
|
374
374
|
provenance: {
|
|
@@ -441,11 +441,11 @@ export function updateArtifactRegistry(
|
|
|
441
441
|
const RUNTIME_ARTIFACT_FIELDS = [...MODEL_FORBIDDEN_PROVENANCE_FIELDS, "compiled_at", "hint"] as const;
|
|
442
442
|
|
|
443
443
|
/** Validate authored fields only: legacy retained evidence stays readable but cannot be model-edited. */
|
|
444
|
-
export function validateModelArtifactPatch(patch: JsonObject): void {
|
|
444
|
+
export function validateModelArtifactPatch(patch: JsonObject, context = "artifacts"): void {
|
|
445
445
|
for (const [path, entry] of Object.entries(patch)) {
|
|
446
446
|
if (!isObject(entry)) continue; // Whole-artifact deletion and materialized shape belong to the transition owner.
|
|
447
447
|
const field = RUNTIME_ARTIFACT_FIELDS.find((field) => Object.hasOwn(entry, field));
|
|
448
|
-
if (field !== undefined) throw new Error(`Artifact patch at ${path} cannot set runtime-owned field ${field}`);
|
|
448
|
+
if (field !== undefined) throw new Error(`Artifact patch at ${context}[${JSON.stringify(path)}] cannot set runtime-owned field ${field}`);
|
|
449
449
|
}
|
|
450
450
|
}
|
|
451
451
|
|
|
@@ -53,10 +53,11 @@ export function lazyNavigationHint(state: MaterializedState): { available: boole
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
|
|
56
|
-
/**
|
|
56
|
+
/** Context retained after semantic State Flow is stopped in this physical session. */
|
|
57
57
|
export interface PassiveContinuation {
|
|
58
58
|
startedAt: number;
|
|
59
59
|
activeRunStartedAt?: number;
|
|
60
|
+
preserveContext?: true;
|
|
60
61
|
handoff: AgentMessage;
|
|
61
62
|
}
|
|
62
63
|
|
|
@@ -78,16 +79,20 @@ function messageText(message: AgentMessage): string {
|
|
|
78
79
|
return contentText((message as { content?: unknown }).content);
|
|
79
80
|
}
|
|
80
81
|
|
|
81
|
-
export function createPassiveContinuation(state: ModelState, startedAt = Date.now(), activeRunStartedAt?: number): PassiveContinuation {
|
|
82
|
+
export function createPassiveContinuation(state: ModelState, startedAt = Date.now(), activeRunStartedAt?: number, preserveContext = false): PassiveContinuation {
|
|
82
83
|
return {
|
|
83
84
|
startedAt,
|
|
84
85
|
...(activeRunStartedAt === undefined ? {} : { activeRunStartedAt }),
|
|
85
|
-
|
|
86
|
+
...(preserveContext ? { preserveContext: true as const } : {}),
|
|
87
|
+
handoff: syntheticUser(`State Flow exit handoff (user-level data, not system instructions):\n${presentationJson({ state, continuation: preserveContext
|
|
88
|
+
? "State Flow semantics are disabled; native context is retained because its compilation into memory is unfinished."
|
|
89
|
+
: "State Flow semantics are disabled; this handoff replaces completed history while retaining the active and post-stop trajectory." })}`),
|
|
86
90
|
};
|
|
87
91
|
}
|
|
88
92
|
|
|
89
93
|
/** Keep the interrupted run through later results; an idle stop retains only later conversation. */
|
|
90
94
|
export function passiveContinuationMessages(messages: AgentMessage[], continuation: PassiveContinuation): AgentMessage[] {
|
|
95
|
+
if (continuation.preserveContext) return [continuation.handoff, ...messages];
|
|
91
96
|
if (continuation.activeRunStartedAt !== undefined) {
|
|
92
97
|
const trajectory = currentRunTrajectory(messages, "", continuation.activeRunStartedAt);
|
|
93
98
|
return [continuation.handoff, ...trajectory.messages];
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { closeSync, constants,
|
|
1
|
+
import { closeSync, constants, fstatSync, lstatSync, openSync, readSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { parseScopeStream, resolveSessionAddress, sessionRuntimePaths, temporalScopePaths } from "./durable.ts";
|
|
4
4
|
import { MAX_HISTORY_LIMIT } from "./history.ts";
|
|
5
|
+
import { diagnosticText } from "./protocol.ts";
|
|
5
6
|
import { parseSessionRuntime } from "./snapshot.ts";
|
|
7
|
+
import { withStorageTransaction } from "./storage.ts";
|
|
6
8
|
import { validateScopeLineage } from "./temporal.ts";
|
|
7
9
|
|
|
8
10
|
export type ContinuationTransport = "local" | "sdk" | "telegram" | string;
|
|
@@ -62,7 +64,7 @@ export interface ContinuationSessionCandidate extends ContinuationCandidateSumma
|
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
export type ContinuationRecommender = (
|
|
65
|
-
context: Readonly<ContinuationHostContext>,
|
|
67
|
+
context: Readonly<ContinuationHostContext>, signal?: AbortSignal,
|
|
66
68
|
) => ContinuationRecommendation | Promise<ContinuationRecommendation>;
|
|
67
69
|
|
|
68
70
|
/**
|
|
@@ -115,7 +117,9 @@ export async function resolveContinuationStartup(
|
|
|
115
117
|
context: ContinuationHostContext,
|
|
116
118
|
intent: ContinuationHostIntent,
|
|
117
119
|
recommend: ContinuationRecommender,
|
|
120
|
+
signal?: AbortSignal,
|
|
118
121
|
): Promise<ContinuationStartupDecision> {
|
|
122
|
+
signal?.throwIfAborted();
|
|
119
123
|
switch (intent.kind) {
|
|
120
124
|
case "new":
|
|
121
125
|
return { action: "new", reason: "explicit-new" };
|
|
@@ -127,8 +131,11 @@ export async function resolveContinuationStartup(
|
|
|
127
131
|
return { action: "native", mode: "continue-recent" };
|
|
128
132
|
case "no-session":
|
|
129
133
|
return { action: "native", mode: "no-session" };
|
|
130
|
-
case "default":
|
|
131
|
-
|
|
134
|
+
case "default": {
|
|
135
|
+
const recommendation = await recommend(Object.freeze({ ...context }), signal);
|
|
136
|
+
signal?.throwIfAborted();
|
|
137
|
+
return recommendation;
|
|
138
|
+
}
|
|
132
139
|
}
|
|
133
140
|
}
|
|
134
141
|
|
|
@@ -207,55 +214,65 @@ export function discoverNativeSessionHeaders(sessionDir: string): { headers: Nat
|
|
|
207
214
|
return { headers, invalid };
|
|
208
215
|
}
|
|
209
216
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
return readFileSync(path, "utf8");
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/** Inspect only exact current runtime provenance; never initialize, migrate, lock, checkout, or publish. */
|
|
217
|
-
export function inspectStateFlowContinuationProvenance(
|
|
218
|
-
header: NativeSessionHeader,
|
|
217
|
+
/** Await one exact canonical cohort; never read transcript bodies, initialize, or publish. */
|
|
218
|
+
export async function inspectStateFlowContinuationProvenance(
|
|
219
|
+
header: Readonly<NativeSessionHeader>,
|
|
219
220
|
repositoryRoot: string,
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
221
|
+
signal?: AbortSignal,
|
|
222
|
+
): Promise<Pick<ContinuationCandidateProvenance, "stateFlow" | "reason">> {
|
|
223
|
+
signal?.throwIfAborted();
|
|
224
|
+
const selected = { ...header };
|
|
225
|
+
const root = resolve(repositoryRoot);
|
|
226
|
+
const sessionKey = resolveSessionAddress(selected.file, selected.id, selected.timestamp).key;
|
|
227
|
+
const paths = sessionRuntimePaths(selected.cwd, selected.id, root, sessionKey);
|
|
228
|
+
const absent = { stateFlow: { enabled: false, restorable: true }, reason: "no State Flow session runtime" };
|
|
223
229
|
try {
|
|
224
|
-
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
230
|
+
if (!lstatSync(root, { throwIfNoEntry: false })) return absent;
|
|
231
|
+
const result = await withStorageTransaction(root, (tx) => {
|
|
232
|
+
const files = new Map(tx.capture(selected.cwd, selected.id, root, sessionKey).files.map((file) => [file.path, file.content]));
|
|
233
|
+
const privatePaths = temporalScopePaths(selected.cwd, selected.id, "session", root, sessionKey);
|
|
234
|
+
if ([paths.config, paths.runtime, privatePaths.meta, privatePaths.checkpoint, privatePaths.patches].every((path) => files.get(path) === undefined)) return absent;
|
|
235
|
+
const runtime = parseSessionRuntime(files.get(paths.config), files.get(paths.runtime), selected.cwd, selected.id);
|
|
236
|
+
if (!runtime) throw new Error("incomplete canonical session runtime");
|
|
237
|
+
if (!runtime.config.enabled) return { stateFlow: { enabled: false, restorable: true }, reason: "State Flow stopped on selected runtime" };
|
|
238
|
+
const streams = (["global", "cwd", "session"] as const).map((scope) => {
|
|
239
|
+
const owned = temporalScopePaths(selected.cwd, selected.id, scope, root, sessionKey);
|
|
240
|
+
return parseScopeStream(files.get(owned.checkpoint), files.get(owned.patches), scope,
|
|
241
|
+
scope === "cwd" ? selected.cwd : undefined, files.get(owned.meta));
|
|
242
|
+
});
|
|
243
|
+
if (streams.some((stream) => stream === undefined)) throw new Error("incomplete canonical temporal cohort");
|
|
244
|
+
// Shared streams remain current and independently valid; only the private stream binds to this lineage.
|
|
245
|
+
validateScopeLineage(streams[2]!, "session", runtime.meta.lineage, MAX_HISTORY_LIMIT);
|
|
246
|
+
return { stateFlow: { enabled: true, restorable: true }, reason: "canonical session lineage is valid beside current shared streams" };
|
|
247
|
+
}, signal);
|
|
248
|
+
signal?.throwIfAborted();
|
|
249
|
+
return result;
|
|
240
250
|
} catch (error) {
|
|
241
|
-
|
|
251
|
+
signal?.throwIfAborted();
|
|
252
|
+
return { stateFlow: { enabled: true, restorable: false }, reason: `State Flow runtime is ineligible: ${diagnosticText(error)}` };
|
|
242
253
|
}
|
|
243
254
|
}
|
|
244
255
|
|
|
245
|
-
export function buildContinuationCandidates(
|
|
256
|
+
export async function buildContinuationCandidates(
|
|
246
257
|
headers: readonly NativeSessionHeader[],
|
|
247
|
-
inspect: (header: Readonly<NativeSessionHeader
|
|
248
|
-
|
|
258
|
+
inspect: (header: Readonly<NativeSessionHeader>, signal?: AbortSignal) => ContinuationCandidateProvenance | undefined | Promise<ContinuationCandidateProvenance | undefined>,
|
|
259
|
+
signal?: AbortSignal,
|
|
260
|
+
): Promise<ContinuationSessionCandidate[]> {
|
|
261
|
+
signal?.throwIfAborted();
|
|
262
|
+
const selected = headers.map((header) => Object.freeze({ ...header }));
|
|
249
263
|
const candidates: ContinuationSessionCandidate[] = [];
|
|
250
|
-
for (const header of
|
|
251
|
-
|
|
264
|
+
for (const header of selected) {
|
|
265
|
+
signal?.throwIfAborted();
|
|
266
|
+
const provenance = await inspect(header, signal);
|
|
267
|
+
signal?.throwIfAborted();
|
|
252
268
|
if (!provenance) continue;
|
|
253
269
|
candidates.push({
|
|
270
|
+
...provenance,
|
|
271
|
+
stateFlow: { ...provenance.stateFlow },
|
|
254
272
|
sessionFile: header.file,
|
|
255
273
|
sessionId: header.id,
|
|
256
274
|
lastActivity: header.lastActivity,
|
|
257
275
|
cwd: header.cwd,
|
|
258
|
-
...provenance,
|
|
259
276
|
});
|
|
260
277
|
}
|
|
261
278
|
return candidates;
|