@llblab/pi-kit 0.15.0 → 0.17.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.
Files changed (120) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +2 -2
  3. package/node_modules/@llblab/pi-state-flow/AGENTS.md +12 -12
  4. package/node_modules/@llblab/pi-state-flow/BACKLOG.md +2 -13
  5. package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +31 -0
  6. package/node_modules/@llblab/pi-state-flow/README.md +11 -7
  7. package/node_modules/@llblab/pi-state-flow/dist/lib/config.d.ts +5 -2
  8. package/node_modules/@llblab/pi-state-flow/dist/lib/config.js +17 -16
  9. package/node_modules/@llblab/pi-state-flow/dist/lib/context.d.ts +8 -0
  10. package/node_modules/@llblab/pi-state-flow/dist/lib/context.js +24 -0
  11. package/node_modules/@llblab/pi-state-flow/dist/lib/continuation.js +4 -3
  12. package/node_modules/@llblab/pi-state-flow/dist/lib/durable.d.ts +2 -1
  13. package/node_modules/@llblab/pi-state-flow/dist/lib/durable.js +15 -3
  14. package/node_modules/@llblab/pi-state-flow/dist/lib/episode.d.ts +2 -0
  15. package/node_modules/@llblab/pi-state-flow/dist/lib/episode.js +4 -0
  16. package/node_modules/@llblab/pi-state-flow/dist/lib/extension.d.ts +7 -4
  17. package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +124 -274
  18. package/node_modules/@llblab/pi-state-flow/dist/lib/git.js +20 -23
  19. package/node_modules/@llblab/pi-state-flow/dist/lib/history.js +8 -4
  20. package/node_modules/@llblab/pi-state-flow/dist/lib/json.js +29 -3
  21. package/node_modules/@llblab/pi-state-flow/dist/lib/logging.d.ts +18 -0
  22. package/node_modules/@llblab/pi-state-flow/dist/lib/logging.js +44 -1
  23. package/node_modules/@llblab/pi-state-flow/dist/lib/migration.d.ts +2 -2
  24. package/node_modules/@llblab/pi-state-flow/dist/lib/migration.js +55 -21
  25. package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.d.ts +7 -0
  26. package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.js +65 -17
  27. package/node_modules/@llblab/pi-state-flow/dist/lib/publication.d.ts +17 -0
  28. package/node_modules/@llblab/pi-state-flow/dist/lib/publication.js +102 -0
  29. package/node_modules/@llblab/pi-state-flow/dist/lib/query.d.ts +43 -1
  30. package/node_modules/@llblab/pi-state-flow/dist/lib/query.js +268 -10
  31. package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.d.ts +2 -0
  32. package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.js +34 -5
  33. package/node_modules/@llblab/pi-state-flow/dist/lib/session.d.ts +17 -0
  34. package/node_modules/@llblab/pi-state-flow/dist/lib/session.js +38 -0
  35. package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.d.ts +5 -5
  36. package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.js +20 -24
  37. package/node_modules/@llblab/pi-state-flow/dist/lib/state.d.ts +11 -3
  38. package/node_modules/@llblab/pi-state-flow/dist/lib/state.js +14 -4
  39. package/node_modules/@llblab/pi-state-flow/dist/lib/status.js +1 -1
  40. package/node_modules/@llblab/pi-state-flow/dist/lib/storage.d.ts +2 -0
  41. package/node_modules/@llblab/pi-state-flow/dist/lib/storage.js +55 -20
  42. package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.d.ts +9 -6
  43. package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.js +10 -3
  44. package/node_modules/@llblab/pi-state-flow/dist/lib/transition.js +8 -3
  45. package/node_modules/@llblab/pi-state-flow/dist/package.json +1 -1
  46. package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-guide/SKILL.md +79 -0
  47. package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-memory/SKILL.md +23 -121
  48. package/node_modules/@llblab/pi-state-flow/docs/README.md +1 -0
  49. package/node_modules/@llblab/pi-state-flow/docs/architecture.md +27 -20
  50. package/node_modules/@llblab/pi-state-flow/docs/compatibility.md +1 -1
  51. package/node_modules/@llblab/pi-state-flow/docs/filesystem-recovery.md +3 -2
  52. package/node_modules/@llblab/pi-state-flow/docs/lazy-state.md +513 -0
  53. package/node_modules/@llblab/pi-state-flow/docs/usage.md +13 -10
  54. package/node_modules/@llblab/pi-state-flow/lib/config.ts +18 -15
  55. package/node_modules/@llblab/pi-state-flow/lib/context.ts +24 -0
  56. package/node_modules/@llblab/pi-state-flow/lib/continuation.ts +4 -3
  57. package/node_modules/@llblab/pi-state-flow/lib/durable.ts +14 -5
  58. package/node_modules/@llblab/pi-state-flow/lib/episode.ts +5 -0
  59. package/node_modules/@llblab/pi-state-flow/lib/extension.ts +119 -262
  60. package/node_modules/@llblab/pi-state-flow/lib/git.ts +23 -22
  61. package/node_modules/@llblab/pi-state-flow/lib/history.ts +8 -4
  62. package/node_modules/@llblab/pi-state-flow/lib/json.ts +31 -3
  63. package/node_modules/@llblab/pi-state-flow/lib/logging.ts +53 -1
  64. package/node_modules/@llblab/pi-state-flow/lib/migration.ts +51 -20
  65. package/node_modules/@llblab/pi-state-flow/lib/protocol.ts +61 -17
  66. package/node_modules/@llblab/pi-state-flow/lib/publication.ts +96 -0
  67. package/node_modules/@llblab/pi-state-flow/lib/query.ts +261 -9
  68. package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +32 -4
  69. package/node_modules/@llblab/pi-state-flow/lib/session.ts +45 -0
  70. package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +22 -25
  71. package/node_modules/@llblab/pi-state-flow/lib/state.ts +22 -6
  72. package/node_modules/@llblab/pi-state-flow/lib/status.ts +1 -1
  73. package/node_modules/@llblab/pi-state-flow/lib/storage.ts +46 -18
  74. package/node_modules/@llblab/pi-state-flow/lib/telegram.ts +20 -6
  75. package/node_modules/@llblab/pi-state-flow/lib/transition.ts +8 -3
  76. package/node_modules/@llblab/pi-state-flow/package.json +1 -1
  77. package/node_modules/@llblab/pi-state-flow/skills/state-flow-guide/SKILL.md +79 -0
  78. package/node_modules/@llblab/pi-state-flow/skills/state-flow-memory/SKILL.md +23 -121
  79. package/node_modules/@llblab/pi-telegram/AGENTS.md +1 -1
  80. package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -0
  81. package/node_modules/@llblab/pi-telegram/CHANGELOG.md +7 -0
  82. package/node_modules/@llblab/pi-telegram/README.md +1 -0
  83. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +2 -1
  84. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +2 -1
  85. package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +134 -2
  86. package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +297 -16
  87. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.d.ts +2 -0
  88. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.js +11 -0
  89. package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +68 -8
  90. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.d.ts +4 -3
  91. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.js +17 -13
  92. package/node_modules/@llblab/pi-telegram/dist/lib/pi.d.ts +2 -1
  93. package/node_modules/@llblab/pi-telegram/dist/lib/pi.js +1 -0
  94. package/node_modules/@llblab/pi-telegram/dist/lib/routing.d.ts +1 -0
  95. package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +55 -5
  96. package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.d.ts +23 -0
  97. package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.js +20 -0
  98. package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +18 -0
  99. package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +152 -6
  100. package/node_modules/@llblab/pi-telegram/dist/lib/updates.d.ts +1 -0
  101. package/node_modules/@llblab/pi-telegram/dist/lib/updates.js +5 -3
  102. package/node_modules/@llblab/pi-telegram/dist/package.json +1 -1
  103. package/node_modules/@llblab/pi-telegram/docs/architecture.md +3 -3
  104. package/node_modules/@llblab/pi-telegram/docs/callback-namespaces.md +1 -1
  105. package/node_modules/@llblab/pi-telegram/docs/public-api.md +4 -3
  106. package/node_modules/@llblab/pi-telegram/docs/sections.md +2 -2
  107. package/node_modules/@llblab/pi-telegram/docs/ui-style.md +2 -1
  108. package/node_modules/@llblab/pi-telegram/docs/updates.md +1 -1
  109. package/node_modules/@llblab/pi-telegram/lib/bindings.ts +3 -0
  110. package/node_modules/@llblab/pi-telegram/lib/commands.ts +466 -21
  111. package/node_modules/@llblab/pi-telegram/lib/delivery.ts +15 -0
  112. package/node_modules/@llblab/pi-telegram/lib/extension.ts +77 -9
  113. package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +25 -10
  114. package/node_modules/@llblab/pi-telegram/lib/pi.ts +3 -0
  115. package/node_modules/@llblab/pi-telegram/lib/routing.ts +84 -17
  116. package/node_modules/@llblab/pi-telegram/lib/thread-display.ts +30 -0
  117. package/node_modules/@llblab/pi-telegram/lib/threads.ts +199 -6
  118. package/node_modules/@llblab/pi-telegram/lib/updates.ts +5 -2
  119. package/node_modules/@llblab/pi-telegram/package.json +1 -1
  120. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  All notable changes to `@llblab/pi-kit` are documented here.
4
4
 
5
+ ## 0.17.0 - 2026-09-18
6
+
7
+ - `Proportional State Flow Guidance`: Advances the exact State Flow pin to `0.16.0`, adding the focused operational Skill, compacting memory curation guidance, and returning bounded reactive diagnostics for missing paths with durable references.
8
+ - `Intentional Agency`: Includes State Flow 0.15.0 hot scoped intents for selected future commitments, explicit references to supporting Lazy memory, complete lifecycle and migration semantics, and read-only Telegram inspection without introducing scheduling or automatic execution.
9
+ - `Package Cohort`: Keeps every other bundled package at its current exact version; the package set, resource inventory, and explicit load order remain unchanged.
10
+
11
+ ## 0.16.0 - 2026-09-17
12
+
13
+ - `Canonical Knowledge Memory`: Advances the exact State Flow pin to `0.14.0`, making Global Lazy the sole canonical Knowledge store and removing the former Markdown Knowledge layer while preserving scoped semantic compilation and recovery.
14
+ - `Telegram Fresh Sessions`: Advances the exact Telegram pin to `0.49.0`, adding lifecycle-safe `/new` session replacement with durable update settlement, same-chat or same-Thread continuity, and terminal result notifications.
15
+ - `Package Cohort`: Confirms every other bundled package already matches its latest published npm version; the package set, resource inventory, and explicit load order remain unchanged.
16
+
5
17
  ## 0.15.0 - 2026-09-16
6
18
 
7
19
  - `Business Credit Usage`: Advances the exact Codex Usage pin to `0.10.0`, adding Business-account credit usage with normalized display, precise reset countdown updates, and cache reuse.
package/README.md CHANGED
@@ -14,8 +14,8 @@ Package links lead to the owning repositories for usage, documentation, issues,
14
14
  | [`@llblab/pi-clean-room`](https://github.com/llblab/pi-clean-room) | `0.1.1` | Isolated nested Pi TUI with explicitly selected extensions |
15
15
  | [`@llblab/pi-codex-usage`](https://github.com/llblab/pi-codex-usage) | `0.10.0` | Compact Codex/Spark subscription-limit and Business credit-usage status |
16
16
  | [`@llblab/pi-grow-loop`](https://github.com/llblab/pi-grow-loop) | `0.8.1` | Visible continuation scheduling and bounded worker Skills |
17
- | [`@llblab/pi-state-flow`](https://github.com/llblab/pi-state-flow) | `0.13.3` | Incremental scoped state, context, and memory with semantic storage, compiled runtime delivery, safe compaction, exact publication, and reliable Telegram section discovery |
18
- | [`@llblab/pi-telegram`](https://github.com/llblab/pi-telegram) | `0.48.3` | Telegram companion with built runtime delivery, State Flow recovery deduplication, adaptive Thread display, exact queues, files, voice, controls, and Generative Apps guidance |
17
+ | [`@llblab/pi-state-flow`](https://github.com/llblab/pi-state-flow) | `0.16.0` | Incremental scoped state/context/memory compiler with proportional operational guidance, intentional agency, reactive dangling-reference diagnostics, compiled runtime delivery, safe compaction, and exact publication |
18
+ | [`@llblab/pi-telegram`](https://github.com/llblab/pi-telegram) | `0.49.0` | Telegram companion with native fresh-session replacement, adaptive Thread continuity, exact queues, files, voice, controls, and Generative Apps guidance |
19
19
  | [`@llblab/skills`](https://github.com/llblab/skills) | `1.15.0` | Portable workflows for engineering, review, design, context maintenance, and other focused tasks |
20
20
 
21
21
  Versions are exact by design. An upstream release does not change an installed kit until this repository explicitly advances the dependency and publishes a new kit version. Runtime defects and package-specific feature requests belong in the linked repository; package selection and kit installation issues belong here.
@@ -1,25 +1,25 @@
1
1
  # Agent Instructions
2
2
 
3
- - Keep independent domain modules under `lib/`, mirror every domain with a same-named file under `tests/`, place cross-domain architecture checks in `tests/invariants.test.ts`, and keep `index.ts` as a minimal composition/public-export boundary.
3
+ - Keep independent domain modules under `lib/`, mirror every domain with a same-named file under `tests/`, place cross-domain architecture checks in `tests/invariants.test.ts`, and keep `index.ts` as a minimal public-export boundary. Keep `lib/extension.ts` as the Pi lifecycle composition root: it may wire configuration, domain capabilities, handlers, and event subscriptions, but low-level parsing, traversal, formatting, persistence, queue/worker mechanics, and diagnostic I/O belong to their existing owning domains.
4
4
  - Keep the extension opt-in and preserve clear attribution to SKILL.state wherever the inherited explicit-state approach is described.
5
5
  - Preserve Pi's native tool loop and complete inspectable session trace; project completed-run history only at user-run boundaries. The runtime-context builder owns model projection of the raw scope overlay; callers must not pre-project it. Retain persistent and current-run context-bearing custom messages from other extensions, plus the complete current-run trajectory, except State Flow's own validation feedback when it is represented separately.
6
- - Expose one canonical materialized state shape across global, CWD, and session scopes with exactly `artifacts`, `contract`, `working`, and `response`; the first three are flexible semantic objects and the fourth is the latest complete user-facing answer string where applicable.
6
+ - Expose one canonical materialized state shape across global, CWD, and session scopes with exactly `artifacts`, `contract`, `working`, `intents`, and `response`, plus optional `lazy`; the first four are flexible semantic objects and `response` is the latest complete user-facing answer string where applicable. `intents` contains only active commitments to future action, remains hot by default, and must not become a planner, scheduler, task manager, or execution loop.
7
7
  - Key artifacts directly by their source path. Model-visible artifact entries require only a non-empty `description` plus optional forward-compatible metadata; runtime-owned freshness evidence (`sourceHash`, `compilerRevision`, `compiledAt`) belongs in the scope `meta.json` provenance registry, never in projected semantic state. Retired embedded `hash`/`compiler`/`compiled_at` fields remain readable compatibility evidence and are stripped from model projection. Validate provenance restrictions on every authored artifact entry, not on retained legacy state: individual runtime/legacy provenance fields cannot be set or deleted by model patches in any scope.
8
8
  - After local activation and before the next enabled inference, discover regular lowercase `*.md` files recursively beneath the configured Knowledge repository root; use canonical absolute paths, hash opaque source bytes and retain their byte counts without decoding or retaining bodies, skip symlinks, and never traverse outside that canonical root. Treat the current candidate set plus explicit owner-confirmed removals as generic global artifact input without invoking Knowledge validators. Partial candidate sets, unavailable roots, and symlink/external/non-Markdown paths never authorize deletion. Re-derive removals from the retained global registry so status inspection and restart cannot consume uncommitted observations; unavailable roots mean unknown freshness. Materialize removals deterministically as runtime-owned global transitions without rereading missing bodies or requesting model compiler output. Never reserve Knowledge document names, provide built-in Knowledge templates, or implement `save_knowledge`.
9
9
  - Derive artifact freshness per available evidence: a new source always requires compilation, a matching `sourceHash` or `compilerRevision` detects its own change, malformed present evidence fails closed for the capability that depends on it, and missing evidence degrades to an unknown-but-usable artifact rather than corrupt state or a migration. Never treat `compiled_at` as a correctness signal. Plan acquisition from path/hash identity without source bodies, project stale global candidates as runtime-owned `artifact_invalidations` containing only `{path, reason}`, and publish accepted compilation semantics plus matching provenance in one durable cohort.
10
10
  - Correlate successful exact-path reads with the current ordinary global invalidation plan. Require each acquired stale source to have a compact compiler output at the same path in a global transition, reject model-authored provenance fields, and record the runtime-observed source hash plus current ordinary compiler revision in the global provenance registry. Compile routing descriptions by default, never raw Markdown or a full-file summary; preserve uncertainty and reserve richer compilations for reusable operational semantics.
11
11
  - Apply one materialized-first acquisition policy to every source: read only for a concrete relevant gap not covered by a sufficient compilation, an exact-source operation including edits, evidenced invalidation, contradiction/failure reconciliation, explicit request, or selection by bounded maintenance. New sessions, routine recall or activation, reassurance, and an index or description alone are not reasons to read. Changed hashes require rereading; prefer the smallest sufficient read.
12
12
  - Keep artifact maintenance opt-in, low-frequency, and side-effect free until a selected source is acquired. Select only otherwise-fresh artifacts old enough for maintenance, rank missing/unparseable `compiled_at` first then oldest timestamp and path, and enforce strict per-cycle read-count and source-byte ceilings. Treat source bytes as the conservative source-token upper bound. Never let maintenance displace correctness invalidation; use explicit invalidation refresh for full rebuilds and never mutate source files.
13
- - Load optional agent-level `state-flow.json` once per extension load/reload. Its `directory` selects the state store, `autoStart` defaults false for genuinely new sessions, `showSuccessfulPatches` defaults true for interactive successful-call JSON rendering, and `remotePublication` selects `off`, `turn-end`, or compatibility `transition`. State Flow always owns durable memory while enabled and global semantic memory is always available; these are invariants, not configuration switches. Keep runtime configuration and provenance outside model-patchable semantic state.
14
- - Keep runtime configuration and provenance separate from model-patchable semantic state. Persist session `config.json` for enablement/projection settings and `meta.json` for lineage, counters, branch identity, durable base, publication, migration metadata, and session-scope artifact provenance; global and CWD scopes own their artifact provenance in scope `meta.json`. Neither participates in scope overlay. New Pi checkpoints contain only a durable revision pointer, or `{disabled:true}` when no durable runtime exists on that branch. Preserve exact Git hashes for Git-backed revisions and give file-only current-cohort references an unambiguous distinct identity; file references use `file:<64 lowercase hex>`, bind the store root and complete scoped/runtime cohort, and must not pretend to provide arbitrary cold or branch history. Operationally unavailable references (including expired file cohorts, missing Git, and publication exclusion) retain selection rather than falling through to older disabled markers; malformed immutable targets remain a distinct recovery case; they are not a parallel authoritative semantic or config/meta store. Emit a disabled marker only from proven pre-runtime branch provenance; missing/invalid revisions and failed restoration do not establish that permission. Predecessor checkpoint/tail envelopes are read-only migration input, recognized only by their complete exact structure and converted explicitly through the normal lock/CAS publication path. Validate immutable pointer targets before acquiring the live publication basis; transient publication failures preserve the selected retry reference rather than choosing older semantic state. In Git mode, runtime `revision: "self"` resolves to the commit owning the config/meta pair, not an unrelated later repository commit. File publication uses explicit `publication: "files"` provenance and resolves the exact complete live cohort instead of claiming a Git owner. Use a distinct `meta.temporalRevision` when a runtime-only write selects older semantic scopes; restore their artifact provenance from that same revision and preserve live shared checkpoints, tails, and provenance during session config/meta writes. Persist publication intent as unconfirmed before pushing; reconcile that exact existing commit after restart rather than embedding an impossible self-hash or inventing semantic history.
13
+ - Load optional repository-global `config.json` from the canonical `<agentDir>/state-flow/` root once per extension load/reload. `autoStart` defaults false for genuinely new sessions, `passiveBootstrap` and `passiveTools` default true and remain independent of episode lifecycle, `showSuccessfulPatches` defaults true for interactive successful-call JSON rendering, and `remotePublication` selects `off`, `turn-end`, or compatibility `transition`; SDK embeddings may explicitly override the repository. State Flow always owns durable memory while enabled and global semantic memory is always available; these are invariants, not configuration switches. Keep global operator configuration, runtime configuration, and provenance outside model-patchable semantic state.
14
+ - Keep runtime configuration and provenance separate from model-patchable semantic state. Persist session `config.json` for enablement/projection settings, session `runtime.json` for lineage, counters, branch identity, durable base, publication, and migration metadata, and every scope's adjacent `meta.json` for temporal boundaries, ownership where applicable, and artifact provenance. None participates in scope overlay. Read the predecessor combined session `meta.json` only as migration input; the next normal CAS publication must write `runtime.json` and clean known runtime fields from semantic metadata. New Pi checkpoints contain only a durable revision pointer, or `{disabled:true}` when no durable runtime exists on that branch. Preserve exact Git hashes for Git-backed revisions and give file-only current-cohort references an unambiguous distinct identity; file references use `file:<64 lowercase hex>`, bind the store root and complete scoped/runtime cohort, and must not pretend to provide arbitrary cold or branch history. Operationally unavailable references (including expired file cohorts, missing Git, and publication exclusion) retain selection rather than falling through to older disabled markers; malformed immutable targets remain a distinct recovery case; they are not a parallel authoritative semantic or config/meta store. Emit a disabled marker only from proven pre-runtime branch provenance; missing/invalid revisions and failed restoration do not establish that permission. Predecessor checkpoint/tail envelopes are read-only migration input, recognized only by their complete exact structure and converted explicitly through the normal lock/CAS publication path. Validate immutable pointer targets before acquiring the live publication basis; transient publication failures preserve the selected retry reference rather than choosing older semantic state. In Git mode, runtime `revision: "self"` resolves to the commit owning the config/meta pair, not an unrelated later repository commit. File publication uses explicit `publication: "files"` provenance and resolves the exact complete live cohort instead of claiming a Git owner. Use a distinct `meta.temporalRevision` when a runtime-only write selects older semantic scopes; restore their artifact provenance from that same revision and preserve live shared checkpoints, tails, and provenance during session config/meta writes. Persist publication intent as unconfirmed before pushing; reconcile that exact existing commit after restart rather than embedding an impossible self-hash or inventing semantic history.
15
15
  - Overlay materialized state recursively in `global → cwd → session` order. Scope-local deletion removes only that scope's key so a lower-scope value becomes visible again; scope never changes instruction authority. Route branch/run-local continuation to session, project-local reusable state and Skill artifacts to CWD, and cross-project reusable state to global.
16
16
  - Reserve `state` for the runtime materialized semantic view. `checkpoint.json` contains only the canonical materialized semantic state, each nonblank `patches.jsonl` line contains only one semantic patch, and adjacent `meta.json` owns their checkpoint/tail boundaries plus CWD identity. For each scope, current state is exactly `materialize(checkpoint.json, patches.jsonl, meta.json)`: an older anchored checkpoint plus its ordered tail of at most seven materially effective semantic patches. On overflow, apply the oldest patch into the checkpoint, advance its `through` boundary, remove it, and append the new patch. Never truncate unapplied replay records or replay patches over an already-current snapshot.
17
17
  - Give every accepted semantic transition one opaque identity shared by all affected scope records, with explicit active causal lineage. A branch-local position can order identities but never acts as a global counter, substitutes for identity, or merges forks. Unchanged scopes contribute no record and remain unchanged at that boundary. Adopt revision-proven inherited streams at a new session origin without rewriting their checkpoints/tails; pre-origin coordinates are not one shared clock. A restored branch adopts current proven live shared streams it does not modify at a fresh origin while preserving its selected session layer; a shared scope the accepted transition actually changes must still match its selected basis or fail closed naming that scope. Reconciliation adoption is not a fabricated semantic transition, parent link, or retry loop. True semantic no-ops and origin adoption do not advance semantic history.
18
- - Define `state[n]` and `state.global[n]`, `state.cwd[n]`, `state.session[n]` at the same nth previous accepted transition boundary in the active lineage, never the nth local patch of each scope. Guarantee offsets zero through seven once the lineage has seven proven transitions; report earlier-than-origin history as unavailable for new/migrated lineages and reject offsets beyond seven on the hot interface. Reconstruct scopes at one target before overlaying them.
19
- - Keep historical reads lazy through the smallest runtime/model read interface. Normal inference gets only current effective state and useful bounded compact transition context, never eight full snapshots. `patch_state` is the sole semantic mutation tool. `read_state` resolves current-as-index-zero aliases for effective/scoped materializations and scope-local retained accepted patches; materialization indices share the composed causal lineage while `patches[n]` walks the selected scope's retained patch tail. The prior offset/scope form remains a compatibility input and cannot combine with `path`. Reads return the exact boundary and never publish, append a checkpoint, or advance history. Both tools follow branch enablement and host tool restrictions; the patch barrier blocks reader siblings too. Live/cached checkpoint plus tails own the hot path; use Git for branch restoration and explicit cold inspection, not to rebuild current state on every inference.
20
- - Store runtime state in its own directory with optional Git durability, defaulting to `state-flow/` beneath Pi's configured agent directory (`~/.pi/agent/state-flow/` normally), independently from the Knowledge Markdown source root. Use exactly these owned paths: `checkpoint.json`, `patches.jsonl`, and `meta.json` for global; `<cwd-key>/checkpoint.json`, `<cwd-key>/patches.jsonl`, and `<cwd-key>/meta.json` for CWD; `<cwd-key>/<session-key>/checkpoint.json`, `patches.jsonl`, `config.json`, and `meta.json` for session. Do not create `.state-flow`, `scopes`, or another storage/history namespace. Mirror Pi's native CWD session-directory encoding and JSONL-basename session key (deriving `<header timestamp>_<UUID>` for in-memory sessions), while retaining separately verifiable canonical identity provenance in owned state. Keep the UUID authoritative, reject unsafe segments and mismatches rather than selecting another scope, and use CWD `meta.json` ownership to fail closed on Pi-name collisions.
18
+ - Define `effective[n]`, `global[n]`, `cwd[n]`, and `session[n]` at the same nth previous accepted transition boundary in the active lineage, never the nth local patch of each scope. Guarantee offsets zero through seven once the lineage has seven proven transitions; report earlier-than-origin history as unavailable for new/migrated lineages and reject offsets beyond seven on the hot interface. Reconstruct scopes at one target before overlaying them.
19
+ - Keep historical reads lazy through the smallest runtime/model read interface. Normal inference gets only current effective state and useful bounded compact transition context, never eight full snapshots. `patch_state` is the sole semantic mutation tool. `read_state` treats unscoped semantic paths as current effective aliases, accepts explicit effective/scoped materializations and scope-local retained accepted patches, and rejects the retired top-level `state` segment; materialization indices share the composed causal lineage while `patches[n]` walks the selected scope's retained patch tail. The public reader accepts only `path` or `paths`; temporal offsets and scope selection are expressed in path syntax. Reads return the exact boundary and never publish, append a checkpoint, or advance history. Both tools follow branch enablement and host tool restrictions; the patch barrier blocks reader siblings too. Live/cached checkpoint plus tails own the hot path; use Git for branch restoration and explicit cold inspection, not to rebuild current state on every inference.
20
+ - Store runtime state in its own directory with optional Git durability, defaulting to `state-flow/` beneath Pi's configured agent directory (`~/.pi/agent/state-flow/` normally), independently from the Knowledge Markdown source root. Use exactly these owned paths: `checkpoint.json`, `patches.jsonl`, and `meta.json` for global; `<cwd-key>/checkpoint.json`, `<cwd-key>/patches.jsonl`, and `<cwd-key>/meta.json` for CWD; `<cwd-key>/<session-key>/checkpoint.json`, `patches.jsonl`, `meta.json`, `config.json`, and `runtime.json` for session. Do not create `.state-flow`, `scopes`, or another storage/history namespace. Mirror Pi's native CWD session-directory encoding and JSONL-basename session key (deriving `<header timestamp>_<UUID>` for in-memory sessions), while retaining separately verifiable canonical identity provenance in owned state. Keep the UUID authoritative, reject unsafe segments and mismatches rather than selecting another scope, and use CWD `meta.json` ownership to fail closed on Pi-name collisions.
21
21
  - Classify every filesystem cohort before recovery: complete valid evidence is usable; total absence is recoverable only from an explicit semantic default or exact surviving authority; partial, malformed, contradictory, or authority-losing evidence fails closed at the smallest dependent capability. A wholly absent untouched global/CWD checkpoint-tail pair is current empty shared reality and must not resurrect selected cold values; a patch targeting that disappeared scope is stale and must fail for reinference. Missing provenance means unavailable freshness, not semantic corruption. Perform every repair through normal locked/CAS publication, never ad-hoc writes.
22
- - Support exactly one storage migration: complete predecessor checkpoint/tail envelopes across every owner-proven session in the configured store convert into semantic-only checkpoint/tail files plus temporal `meta.json` through one normal locked/CAS publication. Replace a Git-backed store's history with one new root commit for the complete migrated tree; apply the same cohort without Git in file-only mode. `state.json`, pre-0.4 hashed layouts, and semantic Pi checkpoint envelopes are unsupported and never become recovery authority. Exercise migrations in temporary repositories rather than modifying the user's active data during development.
22
+ - Support the release-owned storage migration chain through the migration domain: complete predecessor checkpoint/tail envelopes convert into semantic-only checkpoint/tail files plus temporal `meta.json`, the 0.13 → 0.14 step splits every owner-proven session's combined `meta.json` into scope-only `meta.json` plus branch/run `runtime.json`; and the 0.14 → 0.15 step adds empty `intents` to retained semantic checkpoints without inferring commitments. Execute the applicable steps through one normal locked/CAS publication. Replace a Git-backed semantic-envelope store's history with one new root commit for the complete migrated tree; apply the same cohort without Git in file-only mode. `state.json`, pre-0.4 hashed layouts, and semantic Pi checkpoint envelopes are unsupported and never become recovery authority. Exercise migrations in temporary repositories rather than modifying the user's active data during development.
23
23
  - Read and write only regular non-symlink State Flow-owned files at those exact paths, publish each file by same-directory atomic rename, preserve arbitrary repository contents, and classify ownership exactly. Git acceptance follows the complete-delta isolated-index contract below. Retain opaque source bytes for file identity and rollback, not decoded-text reconstructions. Use validated structural JSON equality for in-process semantic comparisons; reserve cryptographic hashes for compact identities that cross inference, persistence, process, or source-freshness boundaries. Serialize cooperating Git publications through a common-Git-directory publication lock; low-level file helpers require caller exclusion. Recheck prepared bases before each replacement/deletion and restore only bytes still matching the publisher's own output. Preserve detected concurrent changes and report unresolved rollback conflicts; do not claim kernel-atomic multi-file CAS against nonparticipating writers. Markdown discovery uses its independently configured source root, defaulting to `knowledge/` beneath Pi's agent directory; a storage-root override must not redirect source discovery.
24
24
  - At instance initialization resolve the repository, CWD key, and session key; load each scope's anchored checkpoint and tail, materialize at the selected temporal boundary, then overlay `global → cwd → session`. Install cached view and publication basis atomically only after successful restoration/initialization; unavailable publication is an error, not a semantic no-op. Branch recovery may reuse one owner-bound, single-use inspection of the exact selected immutable Git revision, with a detached inspection snapshot and a freshly acquired live publication basis at installation. Revalidate mutable file cohorts, legacy snapshot fallbacks, and redirected runtime owners; never turn inspection into a long-lived revision or publication-basis cache. Preserve the selected revision across transient restore failure so an explicit start can retry it. A genuinely new session gets an empty session layer and inherits only global/CWD values; it must never reuse another same-CWD session layer. Explicit native fork adoption instead follows the [session-copy contract](docs/fork-contract.md): copy the proven source session checkpoint/tail and provenance into a distinct owner over current shared streams, without pruning shared provenance or modifying the parent. Use a fresh origin, preserve copied replay records, reject occupied live/HEAD targets, and publish/checkpoint the child only after CAS acceptance. Inherited parent pointers never authorize an empty reset; fence parent passive Stop projection across child reload.
25
25
  - Bind every loaded scope and active Pi branch/checkpoint to the corresponding State Flow Git revision. Resume and tree restoration must recover branch-correct runtime config and semantic layers from that revision without blindly importing Git `HEAD`; reading an older revision must use object-level Git reads and never reset or check out the shared repository worktree.
@@ -29,7 +29,7 @@
29
29
  - Treat every semantic `patch_state` as a strict inference barrier. Give it sequential execution mode; find the matching synchronized assistant through public parent traversal from the selected leaf without constructing a full branch during tool preflight; require exactly one `patch_state` call in that response and block every sibling tool call before execution. Every enabled iteration begins terminal-ineligible; only a successful call containing `final:true` latches eligibility for the next accepted `turn_end`, without stopping later reasoning, tools, or patches. If a terminal draft ends before eligibility, preserve it as the runtime-owned response at that `turn_end` and start at most two same-run fallback turns whose only purpose is the `final:true` patch; an eligible draft whose final validation fails after a later acquisition follows the same path. Fallback turns never enter `response`, and their only instruction is to apply `patch_state` with any durable changes and `final:true`, or `{final:true}` alone. A successful fallback closes resolution with the preserved answer intact; two failed fallbacks close the iteration with the preserved answer and current state plus one bounded warning and a finalization diagnostic. Failed patch calls do not consume fallback turns. A subsequent legal patch remains possible, and only an accepted ordinary answer is reconciled.
30
30
  - Reconcile `response` only from the actually accepted ordinary assistant answer at `turn_end`; it remains runtime-owned. State Flow has no terminal HTML-comment mutation protocol and does not parse generic service comments. The first terminal draft that cannot be reconciled is preserved as the iteration response rather than discarded; fallback turns during pending resolution never reach `response`. Other extensions retain ownership of their own comments and output handling.
31
31
  - Treat terminal state as a decision-relevant handoff, not narration: retain source-addressed reusable operational knowledge in `artifacts`; compile stable requirements, confirmed decisions, rejected approaches, and interface commitments into `contract`; retain observations, validation, failures, current domain state, unresolved work, interaction consequences, and exact continuation in `working`. Preserve relevant completed prerequisites and verified outcomes while removing obsolete progress narration; reconcile only information affected by the run and relevant existing commitments, not every scope or repository surface.
32
- - Preserve active constraints, unresolved questions, consequential negative results, and the next discriminating check before compression. Distinguish observations, user requirements, assistant decisions, and hypotheses; do not promote assistant conclusions to user requirements. Retain useful source locators and validity conditions for consequential facts without mandatory per-value metadata. Keep rejection reasons and reconsideration conditions. Reconcile contradictions through evidence or user clarification instead of silently overwriting established constraints or observations; retain unresolved conflicts and decision-relevant hypotheses as uncertain. These are protocol obligations, not deterministic semantic validation gates.
32
+ - Preserve active constraints, unresolved questions, consequential negative results, and the next discriminating check before compression. Distinguish observations, user requirements, assistant decisions, and hypotheses; do not promote assistant conclusions to user requirements. Treat resource, document, memory, Skill, and agent locators as semantic references wherever context expresses them. Inside ordinary strings and prose, encode a semantic-state path as `$` immediately followed by one valid `read_state` path, for example `$effective.lazy.memory[7]`; the prefix distinguishes a reference from incidental path-like text and leaves room for future parsing. `{"$ref":"cwd.lazy.plan"}` remains the optional structured state-reference form; neither form is a runtime link type. External resource locators retain their native path, URI, Skill, or agent syntax. Resolve references explicitly through the appropriate read/tool when needed and infer no authority, existence, dependency, hydration, execution, or completion merely from their presence. Never scan, audit, or proactively resolve references merely to find broken ones. Only after one requested `read_state` value path is missing may the query domain perform one bounded reverse lookup over current model-patchable semantic planes for exact structured `$ref` and `$path` matches. When durable sources match, return a successful top-level `{value:null, hint:[...]}` diagnostic sentinel: every hint has `type:"dangling-reference"`, an action message, and at most three runtime-verified current owning paths. The null sentinel is never presented without `hint` for this case; keys, patch, and multi-path reads retain ordinary all-or-error semantics. Never scan runtime-owned `response`, and retain the ordinary missing-path error when no durable source matches. A match proves durable semantic provenance, not staleness; no match does not prove invention. The agent may then inspect ownership and patch a proven stale source while preserving surrounding meaning. Effective-state absence does not identify ownership, and unavailable history, inaccessible external resources, or transient read failure do not prove a dangling reference. Retain useful source locators and validity conditions for consequential facts without mandatory per-value metadata. Keep rejection reasons and reconsideration conditions. Reconcile contradictions through evidence or user clarification instead of silently overwriting established constraints or observations; retain unresolved conflicts and decision-relevant hypotheses as uncertain. These are protocol obligations, not deterministic semantic validation gates.
33
33
  - Treat `working` as last observations, not a live workspace. Revalidate volatile facts before consequential actions; after interruption or branch navigation inspect relevant external effects before repeating operations. Failed state commits and restored memory do not undo tool effects. Missing evidence proves neither success nor absence of effects: retain uncertainty and the next check. Keep revalidation targeted, without action ledgers or runtime freshness/rollback guarantees.
34
34
  - Treat each successful `SKILL.md` read as CWD artifact acquisition using the finalized tool-execution arguments after mutable interception: require a non-empty compiler output in the next `patch_state` call under `cwd.artifacts[exactReadPath]` with `description`, `kind: "skill"`, and a flexible non-empty `compilation` object; hash the executed source bytes and record runtime-owned `sourceHash` plus `skill-artifact-v1` `compilerRevision` in the CWD provenance registry; reject missing, unhashable, malformed, or forged freshness data; replace the complete prior Skill artifact and its provenance entry on refresh so obsolete evidence cannot survive. `contract.compiled_skills` is retired and rejected; migrate useful legacy entries into artifacts while preserving behavior and marking fallback hashes unverified when the source is unavailable.
35
35
  - Make state stewardship an ordinary model responsibility without a background loop or gratuitous full-state rewrite. Every handoff curates touched and obviously stale or mis-scoped visible branches: place new knowledge at the narrowest valid scope, merge fragmented facts, compress history into conclusions, and delete stale, completed, redundant, or low-value keys while preserving active commitments and evidence. At a completed feature/release/campaign, project switch, or active-version change, require one bounded scoped reconciliation before terminal completion; use targeted `read_state` when ownership is not visible, then write and verify the destination before deleting and verifying the source. Global is only established cross-project/user/environment knowledge, CWD is reusable project truth, and session is branch/run continuation.
@@ -37,17 +37,17 @@
37
37
  - Agent-level opt-in `logging` appends local JSONL diagnostics for rejected `patch_state` attempts and preserved drafts plus fallback turns while turn resolution remains pending. Every rejected call retains its exact attempted arguments plus the precise error and, when available, tool identity, call id, resolution attempt, and terminal-eligibility state; successful patches are never logged. Preserve exact text blocks only where useful, reduce other blocks to structural identity, never duplicate reasoning bodies, and keep logging outside semantic state, scope metadata, checkpoints, and publication. Logging failure emits at most one warning and never changes resolution, enablement, or accepted state.
38
38
  - Recursively materialize patches immediately; empty objects preserve, nested object-key `null` deletes, and `null` anywhere in semantic state including arrays is invalid. This prohibition does not apply to runtime envelopes such as an origin's null parent. Persist runtime-normalized replay patches that exactly reproduce accepted state, including complete artifact replacement; runtime-owned provenance is stored in scope `meta.json` and is not part of semantic replay.
39
39
  - Do not impose project schemas, state or patch byte caps, dynamic growth pressure, observation envelopes, action authorization, action ledgers, or state-size limits.
40
- - Rotate the turn-stable specification on every user-initiated run, while runtime-triggered turn-resolution continuation remains inside that same run. Keep user-controlled specification text at user authority: never interpolate it into the system prompt; repeat it only in synthetic user runtime context. Treat materialized state in that message as fallible assistant-produced data whose transport role does not elevate it into user instructions.
40
+ - Rotate the turn-stable specification on every user-initiated run, while runtime-triggered turn-resolution continuation remains inside that same run. Persist the full specification only while that run is unfinished, then remove it atomically with accepted terminal response reconciliation. Keep user-controlled specification text at user authority: never interpolate it into the system prompt; repeat it only in synthetic user runtime context. Treat materialized state in that message as fallible assistant-produced data whose transport role does not elevate it into user instructions.
41
41
  - When enabled inside an existing session, retain Pi's active context for exactly one complete bootstrap run and require its `patch_state` resolution to migrate all future-relevant context.
42
42
  - Restore extension state from the active Pi session branch's checkpoint and recorded State Flow revision, not the full session entry list or current Git `HEAD`, on both startup and successful in-session tree navigation.
43
43
  - Render compact status as accent `state-flow` plus dim `#<step>`. `/state-flow-status` must distinguish session config/meta from semantic temporal materialization; show the CWD and session keys, step, active temporal head and State Flow revision, available hot-history depth, per-scope artifact/tail counts, discovered global Markdown stale reasons, and pending publication; and render only the effective global → CWD → session materialization as semantic JSON, without reading or dumping source bodies beyond path/hash discovery needed for freshness diagnostics. Distinguish selected retained tail counts from active history depth; inherited tails may predate the origin. When temporal materialization is unavailable, report unknown counts/freshness and unavailable state rather than inventing empty projections.
44
44
  - Explicit `/state-flow-start` creates the State Flow directory when missing and returns after locally usable runtime acceptance for normal `turn-end`/`off` policy. If Git is installed, initialize an exact-root Git repository when needed, including a populated file-only store, preserving all existing bytes and unrelated files; an ancestor repository is not a valid substitute. Skip full predecessor-format migration planning only when all exact legacy snapshot names are absent, and defer Markdown freshness discovery until before the next enabled inference. If Git is absent, use file persistence. Manual-mode startup/status/restore do not initialize Git; configured automatic start of a genuinely new session uses the same initialization as explicit start. Never create external accounts, remote repositories, credentials, or remote configuration; those remain operator-owned. Never auto-import, delete, or reset files/history in a previous Knowledge-backed store; old branch revisions require their original Git history to remain available in the selected store. `/state-flow-start` must initialize missing global, CWD, and current-session checkpoint/tail pairs plus session config/meta through compare-and-swap publication when required, enable only the current session branch, and bootstrap prior conversation when needed. Explicit start on a proven pre-runtime branch (no checkpoint or an ordinary-disabled marker) establishes an empty session origin rather than importing a later same-session layer; validate existing runtime identity, retain shared streams unchanged, and preserve later branch data in cold Git history. Ordinary new sessions remain manual unless agent-level `autoStart` is true; CWD materialization alone grants no automatic activation. Configured new sessions may initialize missing CWD state and receive distinct empty session layers while inheriting global/CWD values. Resumed and tree-selected branches restore their own config and temporal lineage, regardless of the global flag.
45
- - `/state-flow-stop` must persist only the current session/branch's `config.enabled = false` and necessary runtime provenance, preserving all semantic checkpoints/tails and creating no semantic transition. Retain a frozen handoff plus the active user-run trajectory (including later tool results), post-stop conversation, and foreign context-bearing custom messages across same-physical-session reload/resume/tree. Exclude completed pre-run conversation and private State Flow validation feedback; idle Stop does not retain the completed run. Active restart replaces passive mode but uses that bounded boundary for its one migration run; new and forked physical sessions inherit neither projection. It does not rewrite agent-level `autoStart` or change its policy for future new sessions.
45
+ - `/state-flow-stop` returns to the configured passive bootstrap/tool combination and must persist only the current session/branch's `config.enabled = false` and necessary runtime provenance, preserving all semantic checkpoints/tails and creating no semantic transition. Retain a frozen handoff plus the active user-run trajectory (including later tool results), post-stop conversation, and foreign context-bearing custom messages across same-physical-session reload/resume/tree. Exclude completed pre-run conversation and private State Flow validation feedback; idle Stop does not retain the completed run. Active restart replaces passive mode but uses that bounded boundary for its one migration run; new and forked physical sessions inherit neither projection. It does not rewrite agent-level `autoStart` or change its policy for future new sessions.
46
46
  - After an accepted non-bootstrap run settles with no pending input, State Flow may request native manual compaction under a generation-private marker only when public `getContextUsage()` reports at least 24,000 tokens. Keep the complete latest accepted user iteration, store only revision/step identity in details, and never duplicate state in the summary. Unknown or smaller usage skips compaction; a benign native refusal releases the attempt for later work. Skip prefixes containing foreign custom context. Never customize user manual or native threshold/overflow compaction, discard unfinished pre-patch work, create a State Flow origin, or rewrite Pi's append-only JSONL/tree.
47
47
  - Keep the injected runtime protocol compact and normative; put rationale and extended explanation in README rather than the model prompt. Never parse or strip State Flow HTML comments; they are ordinary historical text, while foreign comment handling remains owned by other extensions.
48
48
  - Do not claim strict boundedness for state, the current run trajectory, the turn specification, or the external full trace.
49
49
  - Remain extension-agnostic in core semantics, storage, and inference: core modules never import, name, special-case, or encode policy for another extension or transport. One optional leaf presentation adapter (`lib/telegram.ts`) may import public `pi-telegram` membranes to mirror the live status on exactly one main-menu section button and expose the same start/stop affordances already owned by `state-flow-start` and `state-flow-stop`; it must fail open when the transport is absent or its registry is unready and must never alter core behavior.
50
- - Activate State Flow model tools only while enabled on the selected branch; preserve every unrelated active tool when toggling them. Keep mutation confined to `patch_state` and historical observation read-only.
50
+ - Activate State Flow model tools while an episode is enabled or passive tools are configured; preserve every unrelated active tool when toggling them. Passive reads never initialize storage, while an explicit passive patch may initialize or migrate storage without enabling episode barriers, continuation, or compaction. Keep mutation confined to `patch_state` and historical observation read-only.
51
51
  - Keep `.github/workflows/release.yml` as the sole version-tag release owner: it validates immutable tag identity, publishes through npm Trusted Publisher with provenance, verifies the public package, and only then creates the GitHub Release. Keep package, lockfile, tag, and changelog versions aligned; never add a long-lived npm token fallback.
52
52
  - Keep opt-in performance executables and workers in top-level `benchmarks/`; their correctness regressions stay in `tests/`. Benchmark workloads may reuse synthetic test fixtures, remain excluded from runtime packaging, and retain source-bound measurement identities across relocations.
53
53
  - Run `npm run validate` after retained code changes, then run the canonical ABCd context validator after context edits.
@@ -1,16 +1,5 @@
1
1
  # BACKLOG
2
2
 
3
- Completed release work belongs in [CHANGELOG.md](CHANGELOG.md).
3
+ Completed implementation belongs in [CHANGELOG.md](CHANGELOG.md); durable semantic contracts belong in [AGENTS.md](AGENTS.md) and [docs/architecture.md](docs/architecture.md).
4
4
 
5
- ## Candidate evolution
6
-
7
- - [ ] `Default passive memory access`: Separate memory availability from the active State Flow episode lifecycle. A normal installation defaults to both passive state bootstrap and `read_state`/`patch_state` tools, while automatic terminal compaction, iteration continuation, and active episode semantics remain gated behind `/state-flow-start`.
8
- - Configuration: control passive bootstrap and passive tools independently, yielding all four supported combinations: both off, bootstrap only, tools only, or both on (default). Fully off must add no state projection or tools to model context.
9
- - Passive bootstrap: project existing effective durable state without creating scopes, migrating storage, publishing changes, starting an episode, or promising automatic continuation. Name this separately from active episode/bootstrap semantics.
10
- - Passive tools: `read_state` remains read-only; an explicit `patch_state` may initialize or migrate the required durable runtime and publish only the requested semantic change, but must not activate terminal compaction or future automatic iterations.
11
- - Lifecycle: `/state-flow-start` promotes passive access into the existing active episode behavior. `/state-flow-stop` returns to the configured passive combination rather than overriding it globally.
12
- - Acceptance: cover all four configuration combinations, fresh and existing stores, explicit passive patch publication, unsupported-storage failure, start/stop transitions, tool/context visibility, and proof that passive turns never run terminal compaction.
13
- - Status: accepted direction, not implemented. Requires a dedicated release contract.
14
- - [ ] `Pi Telegram submenu state analyzer`: Extend the State Flow Telegram section with a read-only analyzer view over the same diagnostics `/state-flow-status` already reports (branch mode, temporal head and hot depth, scope keys, retained tails, artifact freshness, publication). The operator deliberately deferred this beyond the 0.9.0 control surface.
15
- - Boundary: presentation only. Reuse existing status diagnostics; no new semantic mode, and never mutate state from the analyzer view.
16
- - Status: candidate, not scheduled. Do not start without a dedicated release contract.
5
+ No open work.
@@ -4,6 +4,37 @@
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.16.0: Proportional State Flow guidance
8
+
9
+ - `Operational Skill`: Adds the packaged `state-flow-guide` as an on-demand reference for concrete read, patch, inheritance, acquisition, finalization, and recovery problems without triggering memory audits or unsolicited cleanup.
10
+ - `Memory Skill`: Compresses `state-flow-memory` into one bounded curation procedure while preserving authority separation, intent cleanup, evidence boundaries, ownership checks, two-phase scope transfer, external acceptance verification, and active/passive finalization behavior.
11
+ - `Contract alignment`: Updates executable Skill discovery and release-package inventory checks for both Skills, removes obsolete documentation for retired read syntax, and defines state references as structured `$ref` values or `$`-prefixed `read_state` paths in prose without proactive link scanning.
12
+ - `Reference diagnostics`: When one requested value path is missing and exact durable sources exist, returns `{value:null, hint:[...]}` with a typed reconciliation message and at most three runtime-verified current owning paths. Keys, patch, batch, and unmatched reads remain all-or-error; no match never implies that the agent invented the path.
13
+ - `Composition root`: Reduces `lib/extension.ts` to higher-level Pi lifecycle wiring by moving patch presentation into `protocol`, branch traversal into `session`, diagnostic persistence into `logging`, and durable queue/worker lifecycle into `publication`, without adding domains or changing public behavior.
14
+
15
+ ## 0.15.0: Intentional agency
16
+
17
+ - `Intent semantic plane`: Adds hot, object-valued `intents` to global, CWD, session, and effective state. An intent is a selected commitment to future action, distinct from requirements in `contract`, observations or possibilities in `working`, and inactive supporting memory in `lazy`.
18
+ - `Intent lifecycle`: Supports ordinary atomic creation, update, scope overlay, supersession, and removal. Active intents survive intermediate handoffs; fulfilled, abandoned, superseded, or impossible intents disappear while consequential results and referenced state remain independently retained.
19
+ - `Explicit semantic references`: Intents may carry conventional `{"$ref":"cwd.lazy.plan"}` pointers. Reads return references exactly and follow their targets only through a separate explicit `read_state`; State Flow adds no dependency graph, automatic hydration, scheduling, execution, or completion behavior.
20
+ - `Durability and migration`: Preserves intents across hot history, Git and file-only persistence, passive and active operation, forks, compaction, and CAS conflicts. The explicit 0.14 → 0.15 migration adds empty intents to retained scopes without inferring commitments from working state, plans, requirements, lazy memory, or response prose.
21
+ - `Inspection and guidance`: Extends current, scoped, historical, key, patch, and batch reads; adds read-only Telegram intent inspection; and updates the model protocol and bundled memory Skill to reconcile active commitments without introducing a task manager or second agent loop.
22
+
23
+ ## 0.14.0: Passive and progressive memory
24
+
25
+ - `Default passive memory`: Repository-root configuration now adds independently configurable passive bootstrap and `read_state`/`patch_state` access, both enabled by default. Passive reads project durable memory without mutation; explicit patches may materialize storage while episode barriers, continuation, response reconciliation, and compaction remain inactive. Start promotes to active semantics and Stop returns to passive mode.
26
+ - `Bounded array ranges`: Added strict half-open `[start:end]` selectors to `read_state` value and patch paths, with `[start..end]` accepted as a forgiving fallback spelling. Ranges are zero-based, must fit fully within the selected array, preserve ordered batch semantics, and never truncate silently.
27
+ - `Direct read paths`: Removed the redundant top-level `state` segment and legacy public `offset`/`scope` inputs. `read_state` now accepts only `path` or `paths`; unscoped semantic paths read the current effective overlay, while `effective`, `global`, `cwd`, and `session` select overlay or ownership. The injected model protocol is 25% shorter while retaining its normative contract.
28
+ - `Lazy semantic plane`: Added scope-local ordinary-JSON `lazy` state under the existing atomic temporal lineage. Ordinary projections omit lazy bodies but expose a bounded root hint; explicit scoped, effective, historical, value, and keys reads resolve lazy data through `read_state`. Telegram inspection now exposes `lazy` and represents the state step with native rich-text code rather than visible Markdown delimiters.
29
+ - `Lazy durability`: Preserved lazy values through predecessor-envelope migration, exact Git and file-only restoration, and native session forks. Invalid lazy state and stale-basis hot-plus-lazy publication now fail before changing retained hot or lazy semantics. Local incompressible-payload measurements found roughly linear current-layout growth and sub-0.5-second publication/restoration through 1 MiB, so this release keeps lazy data co-located with canonical scope semantics rather than adding speculative sharding.
30
+ - `Indexed array patches`: Extended recursive `patch_state` semantics so canonical `"[N]"` selectors update existing array elements, including nested object and array elements, while invalid indices or indexed deletion reject the complete atomic cohort.
31
+ - `Progressive historical reads`: Extended `read_state` with arbitrary path-intersected semantic `patch` projection, including ordered all-or-error batches and effective-state change projection without exposing temporal metadata.
32
+ - `Session storage ownership`: Split branch/run recovery into session `runtime.json`, leaving every scope's `meta.json` symmetric around temporal boundaries and artifact provenance. The migration domain now explicitly detects and atomically upgrades every owner-proven 0.13 session to the 0.14 contract through normal CAS publication; full specifications are removed after accepted terminal reconciliation.
33
+
34
+ ## 0.13.4: Concurrent publication hotfix
35
+
36
+ - `Publication contention`: A publisher now waits briefly for a cooperating live State Flow process to release the storage or shared Git lock, preventing transient concurrent `patch_state` calls from failing while preserving explicit errors for interrupted, malformed, reentrant, or prolonged lock ownership.
37
+
7
38
  ## 0.13.3: Telegram section discovery hotfix
8
39
 
9
40
  - `Telegram section discovery`: The compiled extension now resolves the sibling pi-telegram public sections membrane from its compiled layout as well as the package export, restoring the State Flow control in the Telegram main menu without making Telegram a core dependency.
@@ -2,9 +2,9 @@
2
2
 
3
3
  ![pi-state-flow banner](https://raw.githubusercontent.com/llblab/pi-state-flow/main/banner.jpg)
4
4
 
5
- **Working memory for Pi, carried with the session.**
5
+ **Incremental scoped context/memory compiler for Pi.**
6
6
 
7
- Keep decisions, constraints, verified findings, and next steps without sending every completed tool exchange back to the model. State Flow lets the agent maintain explicit state; Pi still owns the conversation, the native tool loop, and the complete inspectable trace.
7
+ Keep decisions, constraints, verified findings, and next steps without sending every completed tool exchange back to the model. State Flow compiles curated context and memory into explicit state; Pi still owns the conversation, the native tool loop, and the complete inspectable trace.
8
8
 
9
9
  > Inspired by [SKILL.state](https://arxiv.org/html/2608.26263v2).
10
10
 
@@ -51,22 +51,26 @@ Continue working normally. The agent receives the state protocol and uses `patch
51
51
  - `/state-flow-status`: Inspect the selected state, history, artifact freshness, and publication status.
52
52
  - `/state-flow-stop`: Disable updates on this branch without deleting retained state.
53
53
 
54
- State Flow is **opt-in**. Starting in an existing conversation keeps its active context for one migration run. To enable genuinely new sessions automatically, set `"autoStart": true` in the optional [configuration](docs/usage.md#configuration).
54
+ Active State Flow episodes remain **opt-in**, while passive durable memory bootstrap and tools are available by default. Passive turns never require `final:true`, continue automatically, or trigger State Flow compaction. Starting an active episode in an existing conversation keeps its context for one migration run; set `"autoStart": true` to promote genuinely new sessions automatically. See [configuration](docs/usage.md#configuration).
55
55
 
56
56
  ## What carries forward
57
57
 
58
- The same four fields exist at every scope:
58
+ The same semantic planes exist at every scope:
59
59
 
60
60
  - `contract`: Requirements, decisions, constraints, and interface commitments.
61
- - `working`: Observations, results, unresolved questions, and what to do next.
61
+ - `working`: Observations, results, unresolved questions, and possible next steps.
62
+ - `intents`: Courses of action the agent has actually committed to pursue.
62
63
  - `artifacts`: Source-addressed descriptions and reusable compiled knowledge.
63
64
  - `response`: The latest complete answer, captured by the runtime.
65
+ - `lazy`: Durable, versioned memory omitted from ordinary context until explicitly read.
64
66
 
65
- Memory overlays **global → project CWD → session**. Put reusable cross-project knowledge in global, project knowledge in CWD, and private task continuation in session.
67
+ Memory overlays **global → project CWD → session**. Put reusable cross-project knowledge in global, project knowledge in CWD, and private task continuation in session. Hot planes carry what must matter now; `lazy` retains what may matter later without hydrating its body into every prompt. An intent stays hot only while its course remains chosen and disappears when fulfilled, abandoned, superseded, or impossible. It may refer to supporting detail through a structured `{"$ref":"cwd.lazy.plan"}` value or a `$`-prefixed state path inside ordinary prose, such as `$effective.lazy.memory[7]`. Both remain semantic content interpreted by the agent: State Flow never parses, validates, hydrates, executes, or completes them automatically. The agent never scans references for breakage. Only when current work already follows one and a single value path is missing does State Flow perform a bounded exact reverse lookup. Matching durable sources produce a top-level `{value:null, hint:[...]}` sentinel whose typed hint names runtime-verified current owning paths and asks the agent to reconcile them. Keys, patch, batch, and unmatched reads retain ordinary all-or-error behavior; no match does not prove that the agent invented the path. The agent may then repair or remove a proven stale locator in its owning value.
66
68
 
67
69
  **Resuming an existing Pi session restores its selected State Flow state and enablement.** A genuinely new session starts with an empty session layer and inherits only shared global/CWD memory; it does not resume another session's private work. Tree navigation follows the selected branch, not whichever state happens to be at Git `HEAD`.
68
70
 
69
- The agent can inspect `state`/`state[0]` through `state[7]`: now and up to seven prior accepted transitions. Scoped paths such as `state.cwd[1]` use the same causal boundary; `state.global.patches[0]` reads the latest retained accepted patch for that scope. Git-backed stores retain older committed history separately.
71
+ The agent uses `read_state` for exact current or historical paths. Unscoped paths read the effective overlay; `global`, `cwd`, and `session` address ownership directly; `[n]` selects one of up to seven prior accepted transition boundaries. Bounded array ranges and `value`, `keys`, or `patch` projections support progressive reads without hydrating whole lazy collections. Git-backed stores retain older committed history separately.
72
+
73
+ Two packaged Skills keep guidance proportional: `state-flow-guide` resolves concrete operational questions about reading, patching, inheritance, acquisition, finalization, and recovery; `state-flow-memory` performs one bounded explicit or phase-boundary curation. Neither runs background maintenance.
70
74
 
71
75
  ## Boundaries worth knowing
72
76
 
@@ -1,11 +1,14 @@
1
1
  export interface StateFlowConfig {
2
+ /** Canonical State Flow repository. SDK callers may still override it explicitly. */
2
3
  directory: string;
3
4
  autoStart: boolean;
5
+ passiveBootstrap: boolean;
6
+ passiveTools: boolean;
4
7
  /** Opt-in local capture of rejected patch attempts and unresolved terminal drafts. */
5
8
  logging: boolean;
6
9
  /** Show successful patch_state arguments in the interactive tool row. */
7
10
  showSuccessfulPatches: boolean;
8
11
  remotePublication?: "off" | "turn-end" | "transition";
9
12
  }
10
- /** Read once at extension load/reload. Missing config uses defaults; invalid config never falls back. */
11
- export declare function loadStateFlowConfig(agentDir?: string): StateFlowConfig;
13
+ /** Read the repository-global config once at extension load/reload. Missing config uses defaults; invalid config never falls back. */
14
+ export declare function loadStateFlowConfig(agentDir?: string, repositoryRoot?: string): StateFlowConfig;
@@ -1,16 +1,18 @@
1
- // Domain: agent-level State Flow configuration, independent of session runtime and semantic state.
1
+ // Domain: repository-global State Flow configuration, independent of session runtime and semantic state.
2
2
  import { lstatSync, readFileSync } from "node:fs";
3
- import { homedir } from "node:os";
4
- import { dirname, resolve } from "node:path";
3
+ import { join } from "node:path";
5
4
  import { getAgentDir } from "@earendil-works/pi-coding-agent";
6
5
  import { getDurableRepositoryRoot } from "./durable.js";
7
6
  import { isObject } from "./json.js";
8
- /** Read once at extension load/reload. Missing config uses defaults; invalid config never falls back. */
9
- export function loadStateFlowConfig(agentDir = getAgentDir()) {
10
- const path = resolve(agentDir, "state-flow.json");
7
+ /** Read the repository-global config once at extension load/reload. Missing config uses defaults; invalid config never falls back. */
8
+ export function loadStateFlowConfig(agentDir = getAgentDir(), repositoryRoot = getDurableRepositoryRoot(agentDir)) {
9
+ const directory = repositoryRoot;
10
+ const path = join(directory, "config.json");
11
11
  const defaults = {
12
- directory: getDurableRepositoryRoot(agentDir),
12
+ directory,
13
13
  autoStart: false,
14
+ passiveBootstrap: true,
15
+ passiveTools: true,
14
16
  logging: false,
15
17
  showSuccessfulPatches: true,
16
18
  };
@@ -23,12 +25,16 @@ export function loadStateFlowConfig(agentDir = getAgentDir()) {
23
25
  catch (error) {
24
26
  throw new Error(`Cannot read State Flow configuration: ${path}`, { cause: error });
25
27
  }
26
- const allowed = new Set(["directory", "autoStart", "logging", "showSuccessfulPatches", "remotePublication"]);
28
+ const allowed = new Set(["autoStart", "passiveBootstrap", "passiveTools", "logging", "showSuccessfulPatches", "remotePublication"]);
27
29
  if (!isObject(value) || Object.keys(value).some((key) => !allowed.has(key))) {
28
30
  throw new Error(`State Flow configuration contains unknown settings: ${path}`);
29
31
  }
30
32
  if (Object.hasOwn(value, "autoStart") && typeof value.autoStart !== "boolean")
31
33
  throw new Error(`State Flow autoStart must be a boolean: ${path}`);
34
+ if (Object.hasOwn(value, "passiveBootstrap") && typeof value.passiveBootstrap !== "boolean")
35
+ throw new Error(`State Flow passiveBootstrap must be a boolean: ${path}`);
36
+ if (Object.hasOwn(value, "passiveTools") && typeof value.passiveTools !== "boolean")
37
+ throw new Error(`State Flow passiveTools must be a boolean: ${path}`);
32
38
  if (Object.hasOwn(value, "logging") && typeof value.logging !== "boolean")
33
39
  throw new Error(`State Flow logging must be a boolean: ${path}`);
34
40
  if (Object.hasOwn(value, "showSuccessfulPatches") && typeof value.showSuccessfulPatches !== "boolean")
@@ -36,16 +42,11 @@ export function loadStateFlowConfig(agentDir = getAgentDir()) {
36
42
  if (Object.hasOwn(value, "remotePublication") && value.remotePublication !== "off" && value.remotePublication !== "turn-end" && value.remotePublication !== "transition") {
37
43
  throw new Error(`State Flow remotePublication must be off, turn-end, or transition: ${path}`);
38
44
  }
39
- if (Object.hasOwn(value, "directory") && (typeof value.directory !== "string" || !value.directory.trim() || value.directory.includes("\0"))) {
40
- throw new Error(`State Flow directory must be a non-empty path: ${path}`);
41
- }
42
- const directory = value.directory;
43
- if (directory?.startsWith("~") && directory !== "~" && !directory.startsWith("~/"))
44
- throw new Error(`State Flow directory supports ~ or ~/ paths, not named-user expansion: ${path}`);
45
- const expanded = directory === "~" ? homedir() : directory?.startsWith("~/") ? resolve(homedir(), directory.slice(2)) : directory;
46
45
  return {
47
- directory: expanded === undefined ? defaults.directory : resolve(dirname(path), expanded),
46
+ directory,
48
47
  autoStart: value.autoStart === true,
48
+ passiveBootstrap: value.passiveBootstrap !== false,
49
+ passiveTools: value.passiveTools !== false,
49
50
  logging: value.logging === true,
50
51
  showSuccessfulPatches: value.showSuccessfulPatches !== false,
51
52
  ...(value.remotePublication === undefined ? {} : { remotePublication: value.remotePublication }),
@@ -5,6 +5,13 @@ import type { Snapshot } from "./snapshot.ts";
5
5
  import type { RehydrationPhase } from "./rehydration.ts";
6
6
  import { type MaterializedState } from "./state.ts";
7
7
  export declare const VALIDATION_MESSAGE_TYPE = "state-flow-validation";
8
+ type LazyValueKind = "array" | "boolean" | "null" | "number" | "object" | "string";
9
+ /** Fixed-budget navigation only: never place lazy bodies or partial key catalogs in baseline context. */
10
+ export declare function lazyNavigationHint(state: MaterializedState): {
11
+ available: boolean;
12
+ path: string;
13
+ keys?: Record<string, LazyValueKind>;
14
+ };
8
15
  /** Bounded context retained after semantic State Flow is stopped in this physical session. */
9
16
  export interface PassiveContinuation {
10
17
  startedAt: number;
@@ -21,3 +28,4 @@ export declare function currentRunTrajectory(messages: AgentMessage[], specifica
21
28
  messages: AgentMessage[];
22
29
  anchorTimestamp?: number;
23
30
  };
31
+ export {};
@@ -2,6 +2,29 @@ import { projectArtifactForModel } from "./artifact.js";
2
2
  import { canonicalJson } from "./json.js";
3
3
  import { projectStateForModel } from "./state.js";
4
4
  export const VALIDATION_MESSAGE_TYPE = "state-flow-validation";
5
+ const LAZY_HINT_PATH = "effective.lazy";
6
+ const LAZY_HINT_MAX_KEYS = 32;
7
+ const LAZY_HINT_MAX_JSON_CHARS = 1024;
8
+ function lazyValueKind(value) {
9
+ if (value === null)
10
+ return "null";
11
+ if (Array.isArray(value))
12
+ return "array";
13
+ if (typeof value === "object")
14
+ return "object";
15
+ return typeof value;
16
+ }
17
+ /** Fixed-budget navigation only: never place lazy bodies or partial key catalogs in baseline context. */
18
+ export function lazyNavigationHint(state) {
19
+ const base = { available: Object.hasOwn(state, "lazy"), path: LAZY_HINT_PATH };
20
+ if (!base.available || typeof state.lazy !== "object" || state.lazy === null || Array.isArray(state.lazy))
21
+ return base;
22
+ const entries = Object.entries(state.lazy);
23
+ if (entries.length > LAZY_HINT_MAX_KEYS)
24
+ return base;
25
+ const keys = Object.fromEntries(entries.map(([key, value]) => [key, lazyValueKind(value)]));
26
+ return JSON.stringify(keys).length <= LAZY_HINT_MAX_JSON_CHARS ? { ...base, keys } : base;
27
+ }
5
28
  export function syntheticUser(text) {
6
29
  return { role: "user", content: [{ type: "text", text }], timestamp: Date.now() };
7
30
  }
@@ -63,6 +86,7 @@ export function runtimeContextMessage(snapshot, state, recentTransitions = [], a
63
86
  const context = {
64
87
  specification: snapshot.meta.specification,
65
88
  state: projectStateForModel(state),
89
+ lazy_navigation: lazyNavigationHint(state),
66
90
  ...(rehydrationPhase === undefined ? {} : { knowledge_rehydration: { phase: rehydrationPhase } }),
67
91
  ...(artifactInvalidations.length === 0 ? {} : { artifact_invalidations: artifactInvalidations.map(({ path, reason }) => ({ path, reason })) }),
68
92
  ...(recentTransitions.length === 0 ? {} : { recent_transitions: projectRecentForModel(recentTransitions) }),
@@ -138,11 +138,12 @@ export function inspectStateFlowContinuationProvenance(header, repositoryRoot) {
138
138
  const paths = sessionRuntimePaths(header.cwd, header.id, repositoryRoot, sessionKey);
139
139
  try {
140
140
  const config = readRegular(paths.config);
141
+ const runtimeSource = readRegular(paths.runtime);
141
142
  const meta = readRegular(paths.meta);
142
- if (config === undefined && meta === undefined) {
143
+ if (config === undefined && runtimeSource === undefined && meta === undefined) {
143
144
  return { stateFlow: { enabled: false, restorable: true }, reason: "no State Flow session runtime" };
144
145
  }
145
- const runtime = parseSessionRuntime(config, meta, header.cwd, header.id);
146
+ const runtime = parseSessionRuntime(config, runtimeSource, header.cwd, header.id, meta);
146
147
  if (!runtime)
147
148
  return { stateFlow: { enabled: false, restorable: true }, reason: "no State Flow session runtime" };
148
149
  if (!runtime.config.enabled)
@@ -156,7 +157,7 @@ export function inspectStateFlowContinuationProvenance(header, repositoryRoot) {
156
157
  validateTemporalState({ lineage: runtime.meta.lineage, scopes: { global, cwd, session } });
157
158
  return { stateFlow: { enabled: true, restorable: true }, reason: "exact file-only runtime cohort is restorable" };
158
159
  }
159
- const runtimePath = relative(resolve(repositoryRoot), paths.meta);
160
+ const runtimePath = relative(resolve(repositoryRoot), runtimeSource === undefined ? paths.meta : paths.runtime);
160
161
  if (runtimePath.startsWith(".."))
161
162
  throw new Error("runtime provenance escapes repository");
162
163
  const owner = execFileSync("git", ["-C", repositoryRoot, "log", "-1", "--format=%H", "--", runtimePath], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
@@ -1,6 +1,6 @@
1
1
  import { type ArtifactProvenanceRegistry } from "./artifact.ts";
2
2
  import { type ScopeStream, type TemporalState } from "./temporal.ts";
3
- import type { StateScope } from "./state.ts";
3
+ import { type StateScope } from "./state.ts";
4
4
  /** Canonical semantic sources plus runtime-owned temporal metadata. */
5
5
  export interface ScopeStreamSources {
6
6
  checkpoint: string;
@@ -37,6 +37,7 @@ export interface TemporalScopePaths {
37
37
  export declare function temporalScopePaths(cwd: string, sessionId: string, scope: StateScope, repositoryRoot: string, sessionKey?: string): TemporalScopePaths;
38
38
  export declare function sessionRuntimePaths(cwd: string, sessionId: string, repositoryRoot: string, sessionKey?: string): {
39
39
  config: string;
40
+ runtime: string;
40
41
  meta: string;
41
42
  };
42
43
  /** Unsupported state.json presence never becomes an anchored checkpoint. */
@@ -5,11 +5,13 @@ import { getAgentDir } from "@earendil-works/pi-coding-agent";
5
5
  import { parseArtifactProvenanceRegistry, serializeArtifactProvenanceRegistry, } from "./artifact.js";
6
6
  import { canonicalJson, isJsonValue, isObject } from "./json.js";
7
7
  import { validateScopeStream, validateTemporalState } from "./temporal.js";
8
+ import { migratePreIntentState } from "./state.js";
8
9
  const SESSION_KEY_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/;
9
10
  const STATE_FILE = "state.json";
10
11
  const CHECKPOINT_FILE = "checkpoint.json";
11
12
  const PATCHES_FILE = "patches.jsonl";
12
13
  const META_FILE = "meta.json";
14
+ const RUNTIME_FILE = "runtime.json";
13
15
  /** Semantic files contain no runtime envelope; temporal boundaries and CWD ownership live in meta.json. */
14
16
  export function serializeScopeStream(stream, scope, cwdIdentity) {
15
17
  validateScopeStream(stream, scope);
@@ -64,6 +66,7 @@ export function classifyScopeStream(checkpointSource, patchesSource, scope, expe
64
66
  }
65
67
  const temporal = meta?.temporal;
66
68
  if (isObject(temporal) && Object.hasOwn(temporal, "checkpoint") && Array.isArray(temporal.patches)) {
69
+ checkpoint = migratePreIntentState(checkpoint) ?? checkpoint;
67
70
  const owner = meta?.owner;
68
71
  if (scope === "cwd" && expectedCwd !== undefined
69
72
  && (!isObject(owner) || Object.keys(owner).join(",") !== "cwd" || owner.cwd !== resolve(expectedCwd))) {
@@ -93,6 +96,11 @@ export function classifyScopeStream(checkpointSource, patchesSource, scope, expe
93
96
  else if (scope === "cwd" && expectedCwd !== undefined) {
94
97
  throw new Error("State Flow CWD scope identity is missing");
95
98
  }
99
+ if (isObject(legacyCheckpoint) && isObject(legacyCheckpoint.state)) {
100
+ const migrated = migratePreIntentState(legacyCheckpoint.state);
101
+ if (migrated)
102
+ legacyCheckpoint = { ...legacyCheckpoint, state: migrated };
103
+ }
96
104
  const stream = { checkpoint: legacyCheckpoint, patches };
97
105
  validateScopeStream(stream, scope);
98
106
  return { kind: "present", stream };
@@ -113,7 +121,7 @@ export function temporalScopePaths(cwd, sessionId, scope, repositoryRoot, sessio
113
121
  }
114
122
  export function sessionRuntimePaths(cwd, sessionId, repositoryRoot, sessionKey = sessionId) {
115
123
  const directory = sessionScopePaths(cwd, sessionId, repositoryRoot, sessionKey).directory;
116
- return { config: join(directory, "config.json"), meta: join(directory, META_FILE) };
124
+ return { config: join(directory, "config.json"), runtime: join(directory, RUNTIME_FILE), meta: join(directory, META_FILE) };
117
125
  }
118
126
  /** Unsupported state.json presence never becomes an anchored checkpoint. */
119
127
  export function loadScopeStream(cwd, sessionId, scope, repositoryRoot, sessionKey = sessionId) {
@@ -128,7 +136,7 @@ export function captureTemporalFileBases(cwd, sessionId, repositoryRoot, session
128
136
  const paths = ["global", "cwd", "session"].flatMap((scope) => {
129
137
  const pair = temporalScopePaths(cwd, sessionId, scope, repositoryRoot, sessionKey);
130
138
  const runtime = scope === "session" ? sessionRuntimePaths(cwd, sessionId, repositoryRoot, sessionKey) : undefined;
131
- return [pair.checkpoint, pair.patches, join(pair.directory, STATE_FILE), ...(runtime === undefined ? [pair.meta] : [runtime.config, runtime.meta])];
139
+ return [pair.checkpoint, pair.patches, join(pair.directory, STATE_FILE), ...(runtime === undefined ? [pair.meta] : [runtime.config, runtime.runtime, runtime.meta])];
132
140
  });
133
141
  return captureOwnedFileBases(paths, repositoryRoot);
134
142
  }
@@ -151,6 +159,10 @@ export function temporalStateFileUpdates(cwd, sessionId, view, scopes, repositor
151
159
  /** Merge authoritative owned leaves while preserving forward-compatible metadata siblings. */
152
160
  export function serializeScopeMetadata(registry, stream, scope, cwdIdentity, existingSource) {
153
161
  const existing = parseMetadataDocument(existingSource, `State Flow metadata`);
162
+ if (scope === "session") {
163
+ for (const key of ["identity", "lineage", "step", "specification", "validation", "bootstrap", "remotePublication", "revision", "temporalRevision", "publication"])
164
+ delete existing[key];
165
+ }
154
166
  const sources = serializeScopeStream(stream, scope, cwdIdentity);
155
167
  const value = {
156
168
  ...existing,
@@ -276,7 +288,7 @@ export function isStateFlowOwnedPath(candidate, repositoryRoot = getDurableRepos
276
288
  return cwdKey(segments[0])
277
289
  && sessionKey(segments[1])
278
290
  && (segments[2] === STATE_FILE || segments[2] === CHECKPOINT_FILE || segments[2] === PATCHES_FILE
279
- || segments[2] === "config.json" || segments[2] === META_FILE);
291
+ || segments[2] === "config.json" || segments[2] === RUNTIME_FILE || segments[2] === META_FILE);
280
292
  }
281
293
  return false;
282
294
  }
@@ -6,3 +6,5 @@ export declare function resumeEpisode(snapshot: Snapshot, bootstrap: boolean): S
6
6
  export declare function stopEpisode(snapshot: Snapshot): Snapshot;
7
7
  /** Apply one user-run boundary while preserving checkpoint-owned runtime state. */
8
8
  export declare function prepareRun(snapshot: Snapshot, prompt: string): boolean;
9
+ /** Retain the full prompt only while its run remains recoverable and unfinished. */
10
+ export declare function completeRun(snapshot: Snapshot): void;
@@ -25,3 +25,7 @@ export function prepareRun(snapshot, prompt) {
25
25
  snapshot.meta.validation = undefined;
26
26
  return true;
27
27
  }
28
+ /** Retain the full prompt only while its run remains recoverable and unfinished. */
29
+ export function completeRun(snapshot) {
30
+ delete snapshot.meta.specification;
31
+ }
@@ -1,4 +1,5 @@
1
1
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { formatPatchStateArguments, normalizePatchStateArguments } from "./protocol.ts";
2
3
  import { type StateFlowTelegramLoader } from "./telegram.ts";
3
4
  import { type MaterializedState, type StateScope } from "./state.ts";
4
5
  export interface StateFlowExtensionOptions {
@@ -11,12 +12,14 @@ export interface StateFlowExtensionOptions {
11
12
  telegram?: {
12
13
  load?: StateFlowTelegramLoader;
13
14
  };
15
+ /** Test/SDK capability override; repository config remains the Pi default. */
16
+ passive?: {
17
+ bootstrap?: boolean;
18
+ tools?: boolean;
19
+ };
14
20
  }
21
+ export { formatPatchStateArguments, normalizePatchStateArguments };
15
22
  export declare const PATCH_STATE_TOOL_NAME = "patch_state";
16
23
  export declare const READ_STATE_TOOL_NAME = "read_state";
17
24
  export declare const MAX_FALLBACK_ATTEMPTS: number;
18
- /** Keep successful patch JSON valid while separating adjacent scopes and memory sections visually. */
19
- export declare function formatPatchStateArguments(args: unknown): string;
20
- /** Normalize a bounded compatibility superset without advertising aliases in the model-facing contract. */
21
- export declare function normalizePatchStateArguments(args: unknown): any;
22
25
  export default function stateFlowExtension(pi: ExtensionAPI, options?: StateFlowExtensionOptions): void;