@llblab/pi-kit 0.26.0 → 0.27.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/BACKLOG.md +2 -2
- package/CHANGELOG.md +5 -0
- package/README.md +4 -4
- package/node_modules/@llblab/pi-state-flow/AGENTS.md +1 -1
- package/node_modules/@llblab/pi-state-flow/BACKLOG.md +12 -5
- package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +9 -0
- package/node_modules/@llblab/pi-state-flow/README.md +116 -34
- package/node_modules/@llblab/pi-state-flow/dist/lib/acquisition.d.ts +24 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/acquisition.js +80 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/compaction.d.ts +17 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/compaction.js +40 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +149 -298
- package/node_modules/@llblab/pi-state-flow/dist/lib/git.d.ts +29 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/git.js +58 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/operation.d.ts +37 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/operation.js +59 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/ownership.d.ts +31 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/ownership.js +117 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.js +7 -7
- package/node_modules/@llblab/pi-state-flow/dist/lib/query.js +1 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/status.d.ts +2 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/status.js +28 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/transition.js +6 -1
- 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 +14 -6
- package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-memory/SKILL.md +2 -2
- package/node_modules/@llblab/pi-state-flow/docs/README.md +19 -9
- package/node_modules/@llblab/pi-state-flow/docs/agent-contract-relocation.md +4 -4
- package/node_modules/@llblab/pi-state-flow/docs/architecture.md +644 -91
- package/node_modules/@llblab/pi-state-flow/docs/compatibility.md +116 -37
- package/node_modules/@llblab/pi-state-flow/docs/filesystem-recovery.md +118 -21
- package/node_modules/@llblab/pi-state-flow/docs/fork-contract.md +68 -8
- package/node_modules/@llblab/pi-state-flow/docs/lazy-state.md +88 -14
- package/node_modules/@llblab/pi-state-flow/docs/performance.md +83 -66
- package/node_modules/@llblab/pi-state-flow/docs/temporal-acceptance.md +391 -62
- package/node_modules/@llblab/pi-state-flow/docs/usage.md +317 -62
- package/node_modules/@llblab/pi-state-flow/lib/acquisition.ts +85 -1
- package/node_modules/@llblab/pi-state-flow/lib/compaction.ts +47 -0
- package/node_modules/@llblab/pi-state-flow/lib/extension.ts +161 -295
- package/node_modules/@llblab/pi-state-flow/lib/git.ts +63 -0
- package/node_modules/@llblab/pi-state-flow/lib/operation.ts +75 -0
- package/node_modules/@llblab/pi-state-flow/lib/ownership.ts +120 -0
- package/node_modules/@llblab/pi-state-flow/lib/protocol.ts +7 -8
- package/node_modules/@llblab/pi-state-flow/lib/query.ts +1 -1
- package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +1 -1
- package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +1 -2
- package/node_modules/@llblab/pi-state-flow/lib/status.ts +29 -0
- package/node_modules/@llblab/pi-state-flow/lib/transition.ts +5 -1
- package/node_modules/@llblab/pi-state-flow/package.json +1 -1
- package/node_modules/@llblab/pi-state-flow/skills/state-flow-guide/SKILL.md +14 -6
- package/node_modules/@llblab/pi-state-flow/skills/state-flow-memory/SKILL.md +2 -2
- package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -0
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +10 -0
- package/node_modules/@llblab/pi-telegram/README.md +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +4 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +16 -12
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.d.ts +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.js +7 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +12 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +137 -83
- package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +26 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.d.ts +57 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.js +109 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/model.js +2 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/status.d.ts +3 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/status.js +31 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/sync.js +4 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +17 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.d.ts +2 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.js +48 -12
- package/node_modules/@llblab/pi-telegram/dist/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +6 -5
- package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +2 -0
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +2 -2
- package/node_modules/@llblab/pi-telegram/docs/ui-style.md +4 -0
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +17 -10
- package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +8 -2
- package/node_modules/@llblab/pi-telegram/lib/commands.ts +135 -97
- package/node_modules/@llblab/pi-telegram/lib/extension.ts +25 -0
- package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +140 -4
- package/node_modules/@llblab/pi-telegram/lib/model.ts +2 -4
- package/node_modules/@llblab/pi-telegram/lib/status.ts +30 -1
- package/node_modules/@llblab/pi-telegram/lib/sync.ts +4 -4
- package/node_modules/@llblab/pi-telegram/lib/threads.ts +21 -3
- package/node_modules/@llblab/pi-telegram/lib/workspace-retirement.ts +46 -13
- package/node_modules/@llblab/pi-telegram/package.json +1 -1
- package/package.json +3 -3
package/BACKLOG.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Backlog
|
|
2
2
|
|
|
3
|
-
The 0.
|
|
3
|
+
The 0.27.0 composition is recorded in [CHANGELOG.md](./CHANGELOG.md). Package pins, resource order and bundled runtime ownership remain authoritative in `package.json`.
|
|
4
4
|
|
|
5
5
|
## Carried checks
|
|
6
6
|
|
|
7
|
-
- **Installed 0.
|
|
7
|
+
- **Installed 0.27.0 smoke (operator-owned):** After separately authorized installation/reload, check the exact released kit's terminal controls and optional Telegram rendering with disposable State Flow storage. Packed SDK validation does not certify the operator's running clients. Do not reconnect Telegram, change Pi settings or use live memory/usage/Recipe stores as fixtures without separate authorization.
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@llblab/pi-kit` are documented here.
|
|
4
4
|
|
|
5
|
+
## 0.27.0: Intent-Owned State Flow Memory
|
|
6
|
+
|
|
7
|
+
- `Intent-Owned Memory`: Advances the exact State Flow pin to published `0.25.0`. Deleting an intent removes the same-scope `working`/`lazy` keys it owns through structured `$ref`, so agents that work from intents keep memory self-cleaning. `/state-flow-status` shows per-scope plane sizes and intent-owned shares; lifecycle internals were consolidated without behaviour changes.
|
|
8
|
+
- `Telegram Connection Resume`: Advances the exact Telegram pin to `0.51.6`, carrying an in-flight or connected bridge into resumed sessions, recovering exhausted Workspace slots and isolating Thread bindings per session. Other pins, resources and load order are unchanged.
|
|
9
|
+
|
|
5
10
|
## 0.26.0: Pi 1.0 Cohort and Memory-Inert State Flow
|
|
6
11
|
|
|
7
12
|
- `Usage and Fast`: Advances Codex Usage to `0.12.0` and Claude Usage to `0.2.0`, sharing one persistent `/fast` command. Codex uses provider-level priority preference; Claude permits the Opus family. Both preserve quota coordination and redraw the terminal status without a quota request; backend capability and billing still apply.
|
package/README.md
CHANGED
|
@@ -17,15 +17,15 @@ Package links lead to the owning repositories for usage, documentation, issues,
|
|
|
17
17
|
| [`@llblab/pi-clean-room`](https://github.com/llblab/pi-clean-room) | `0.3.0` | Isolated nested Pi TUI with named npm extensions and compatible model selection |
|
|
18
18
|
| [`@llblab/pi-codex-usage`](https://github.com/llblab/pi-codex-usage) | `0.12.0` | Shared Codex quota/Business credit status and persistent priority Fast toggle |
|
|
19
19
|
| [`@llblab/pi-grow-loop`](https://github.com/llblab/pi-grow-loop) | `0.9.0` | Visible continuation scheduling and bounded worker Skills through compiled, manifest-owned resources |
|
|
20
|
-
| [`@llblab/pi-state-flow`](https://github.com/llblab/pi-state-flow) | `0.
|
|
21
|
-
| [`@llblab/pi-telegram`](https://github.com/llblab/pi-telegram) | `0.51.
|
|
20
|
+
| [`@llblab/pi-state-flow`](https://github.com/llblab/pi-state-flow) | `0.25.0` | Scoped context/memory compiler with intent-owned self-cleaning memory, memory-inert Off and ownership status |
|
|
21
|
+
| [`@llblab/pi-telegram`](https://github.com/llblab/pi-telegram) | `0.51.6` | Telegram companion with connection resume, Workspace slot recovery, follower Threads, filterable Skills, files, voice, and controls |
|
|
22
22
|
| [`@llblab/skills`](https://github.com/llblab/skills) | `1.15.0` | Portable workflows for engineering, review, design, context maintenance, and other focused tasks |
|
|
23
23
|
|
|
24
24
|
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.
|
|
25
25
|
|
|
26
26
|
## Install
|
|
27
27
|
|
|
28
|
-
Requires **Pi 1.0.0+** and **Node.js 22.19.0+**. State Flow requires its canonical checkpoint/tail storage format and does not convert unsupported stores in place. Preserve existing stores and consult the [owning package's storage guidance](https://github.com/llblab/pi-state-flow/blob/v0.
|
|
28
|
+
Requires **Pi 1.0.0+** and **Node.js 22.19.0+**. State Flow requires its canonical checkpoint/tail storage format and does not convert unsupported stores in place. Preserve existing stores and consult the [owning package's storage guidance](https://github.com/llblab/pi-state-flow/blob/v0.25.0/docs/usage.md#moving-a-store-and-supported-formats) before changing installations.
|
|
29
29
|
|
|
30
30
|
From npm:
|
|
31
31
|
|
|
@@ -45,7 +45,7 @@ Prefer the kit instead of separately loading the same packages. If you already u
|
|
|
45
45
|
|
|
46
46
|
## Development
|
|
47
47
|
|
|
48
|
-
The `0.
|
|
48
|
+
The `0.27.0` composition includes published State Flow `0.25.0` and Telegram `0.51.6` alongside the Pi 1.0 Actors, usage, Clean Room and Grow Loop cohort. The packed bundle loads all seven extensions on Pi 1.0.0 with no credentials or external requests. This does not certify installed-client rendering; carried checks remain in [Backlog](./BACKLOG.md).
|
|
49
49
|
|
|
50
50
|
```bash
|
|
51
51
|
npm install
|
|
@@ -30,7 +30,7 @@ The [relocation ledger](docs/agent-contract-relocation.md) maps every pre-compac
|
|
|
30
30
|
## Model and operator boundaries
|
|
31
31
|
|
|
32
32
|
- Treat state as a decision-relevant handoff, not a transcript: distinguish user requirements, confirmed decisions, observations, hypotheses, chosen intents and remaining checks. Revalidate volatile external effects before repeating actions; memory is neither an action ledger nor proof of current reality. See [operational guidance](docs/architecture.md#operational-guidance-and-memory-curation) and the [memory Skill](skills/state-flow-memory/SKILL.md).
|
|
33
|
-
- Resolve semantic `$` paths and structured references only when needed; never confer authority or existence by reference alone, scan to find broken references, automatically search all history or restore deleted values from hints. See [model tools](docs/architecture.md#model-tools) and [lazy navigation](docs/usage.md#lazy-navigation-and-historical-reading).
|
|
33
|
+
- Resolve semantic `$` paths and structured references only when needed; never confer authority or existence by reference alone, scan to find broken references, automatically search all history or restore deleted values from hints. Sole exception: a structured `{"$ref"}` inside an `intents` entry owns its same-scope `working`/`lazy` object-key target, so deleting that intent key deletes the target after authored operations in the same atomic cohort unless a remaining same-scope intent references it, an ancestor or a descendant. That bounded read of one scope's `intents` plane never validates, rejects, warns, archives or crosses scopes; textual `$path` mentions and structured references outside `intents` stay non-owning. See [model tools](docs/architecture.md#model-tools), [intent ownership](docs/lazy-state.md#intent-ownership) and [lazy navigation](docs/usage.md#lazy-navigation-and-historical-reading).
|
|
34
34
|
- Keep Skill acquisition optional and exact-path/provenance-derived; successful reads alone are volatile, and attempted durable compilations require scoped validated output plus runtime-owned hash evidence. See [artifact routing](docs/architecture.md#artifact-routing).
|
|
35
35
|
- Reconcile touched state without automatic whole-store audits. Dedicated curation needs a user request; intra-store moves use one verified multi-scope patch, and external transfers need verified destination acceptance before source deletion. See [operational guidance](docs/architecture.md#operational-guidance-and-memory-curation) and the [memory Skill](skills/state-flow-memory/SKILL.md).
|
|
36
36
|
- Keep opt-in diagnostic categories, failure elision/privacy and barrier-only names-only records outside canonical state; logging failures cannot change accepted state. Preserve a blank line between every tool name and its output. See [diagnostic privacy](docs/usage.md#diagnostic-logging-and-privacy) and [barrier diagnostics](docs/architecture.md#pi-lifecycle).
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# Backlog
|
|
2
2
|
|
|
3
|
-
The **0.
|
|
3
|
+
The **0.25.0: Intent-Owned Memory** scope is implemented and locally validated; outcomes are recorded in [CHANGELOG.md](CHANGELOG.md), with package, lockfile and changelog aligned at 0.25.0. This backlog keeps publication, installed-client evidence and later decisions open. Current contracts live in [architecture](docs/architecture.md), [usage](docs/usage.md) and [intent ownership](docs/lazy-state.md#intent-ownership).
|
|
4
4
|
|
|
5
5
|
## Carried gates
|
|
6
6
|
|
|
7
|
-
- **
|
|
7
|
+
- **0.25.0 publication (approval-gated):** Commit the prepared tree and push the exact `v0.25.0` tag; then verify the tag's successful release workflow, published GitHub Release and matching npm package before reporting release completion.
|
|
8
|
+
- **Installed 0.25.0 smoke (operator-owned):** After separately authorized installation/reload, use a disposable store. In `session` and in `cwd`: open an intent with owned `working` and `lazy` entries plus one textual mention, close it, and confirm the owned entries are gone, the mentioned one remains, the receipt lists the deletions and `/state-flow-status` shares update. Then supersede an intent in one patch and confirm its targets survive.
|
|
9
|
+
- **Installed 0.22.0 smoke (operator-owned):** Perform the carried check if it is not yet recorded, only against the exact released 0.22.0 installation; a reload of a later candidate cannot certify the old release. After an operator-authorized reload, confirm:
|
|
8
10
|
- terminal autocomplete exposes Active/Passive/Off and status;
|
|
9
11
|
- Telegram shows the single `off | passive | active` row plus the four inspection buttons;
|
|
10
12
|
- current-session mode agrees across tools, context and status, and survives reload.
|
|
@@ -12,10 +14,15 @@ The **0.24.0** release scope is recorded in [CHANGELOG.md](CHANGELOG.md); this b
|
|
|
12
14
|
- **Installed 0.23.0 smoke (operator-owned):** After separately authorized installation/reload, confirm an unconfigured *new* session is Off without semantic writes, retained choices and explicit global modes survive, and Telegram shows one `Off | Passive | Active` radio row with the selected 🟡/🟣/🟢 marker and ⚫️ inactive markers, followed by four direct scope inspections. Isolate test storage; do not use the live store as a fixture. SDK tests alone do not certify installed-client rendering.
|
|
13
15
|
- **Installed 0.24.0 smoke (approval/operator-owned):** Local lifecycle, cancellation, background-work and callback/inspection acceptance is complete; real-client behavior remains separate evidence. After separately authorized installation/reload of the exact 0.24.0 release, use a disposable store to check Off attachment and pending-work cancellation, no late memory warnings, current read-only inspections without private placeholders, preserved deferred Passive/Active/fork acquisition, and unchanged terminal/Telegram mode controls. Do not use the live store, treat its reload as evidence for older published releases, or change unrelated operator sessions.
|
|
14
16
|
|
|
15
|
-
## Deferred beyond 0.
|
|
17
|
+
## Deferred beyond 0.25.0 (decision inputs, not commitments)
|
|
18
|
+
|
|
19
|
+
- **Convention uptake:** After some real use, read the `/state-flow-status` ownership shares; if most `working`/`lazy` entries stay unowned, revisit the protocol wording, not the mechanism.
|
|
20
|
+
- **Behavioural evaluation:** Live-model comparison of Active against native compaction on one long cyclic task. Needs a policy decision first; current benchmarks are synthetic and make no model calls.
|
|
21
|
+
- **Lifecycle real-use measurement (gated):** The operator install runs with `logging: true` and has no recorded `publication-conflict`, `finalization` or `barrier-block` entries; cooperating-writer waits, Stop fences and fork/restore contention are not instrumented. Removing any awaited layer for rarity first needs a decision to add opt-in counters.
|
|
22
|
+
- **Lifecycle state as one tagged union:** The 0.25.0 lifecycle review left 17 closure bindings in `lib/extension.ts`, each a justified selection, run or host fact. Remaining candidates: the seven branch-selection facts (`snapshot`, `runtime`, `branchStartsWithoutRuntime`, `selectedHistoryExpired`, `modePersistenceError`, `forkInitialization`, `deferredBranch`) as one Off-deferred/attached union, and `passiveContinuation`/`bootstrapContinuation` as one continuation slot once their exclusivity is proven. Decide only with a behavioural reason; no further mechanical moves are pending.
|
|
23
|
+
- **Skill compilation fidelity:** Hashes detect source change, not a lossy first compilation. Measure before adding anything.
|
|
16
24
|
- **Compaction threshold:** `STATE_FLOW_COMPACTION_MIN_CONTEXT_TOKENS = 24_000` is documented as a margin above Pi's default 20,000-token retained suffix. Make it configurable only if a real workload or non-default Pi retention settings demonstrate a mismatch.
|
|
17
|
-
- **Lifecycle complexity review:** Measure how often cooperating-writer waits, Stop fences and fork/restore contention occur in real use before adding further awaited lifecycle layers. Use the result to decide whether any existing layer can be simplified.
|
|
18
25
|
|
|
19
26
|
## Release boundary
|
|
20
27
|
|
|
21
|
-
Release publication is owned by `.github/workflows/release.yml`. Align package, lockfile, tag and changelog at the release version, rebuild `dist/` from final sources, and verify the exact tag's successful workflow, published GitHub Release and matching npm package before reporting release completion. Installed-instance reload remains a separate operator action.
|
|
28
|
+
Release publication is owned by `.github/workflows/release.yml`. Align package, lockfile, tag and changelog at the release version, rebuild `dist/` from final sources, run `npm run validate` and the context validator after context edits, and verify the exact tag's successful workflow, published GitHub Release and matching npm package before reporting release completion. Installed-instance reload remains a separate operator action.
|
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
> Each release keeps at most 8 outcome records of at most 512 characters.
|
|
4
4
|
|
|
5
|
+
## 0.25.0: Intent-Owned Memory
|
|
6
|
+
|
|
7
|
+
- `Intent-owned memory`: Deleting an intent key now deletes the same-scope `working`/`lazy` object keys its structured `{"$ref"}` values own, after authored operations in the same atomic cohort and revision, unless a remaining intent of that scope references the target, an ancestor or a descendant. Other targets and textual `$path` mentions are skipped silently; nothing is rejected, warned about or archived. Records store the cascade as explicit deletions and receipts report it.
|
|
8
|
+
- `Work from intents`: Protocol, `patch_state` description, both Skills, README and docs present `intents` as the queue of chosen actions and `working` as their temporary context. Structured refs inside intents own; textual `$path` mentions only use. Before closing an intent, save survivors to unowned paths, with abandonment reasons in `contract`. Unowned entries stay legal. The always-injected protocol did not grow; duplicated read-path and barrier wording now lives only in the tool definitions.
|
|
9
|
+
- `Ownership status`: `/state-flow-status` adds a per-scope `Scope memory:` block with UTF-8 sizes of present planes and the share of top-level `working`/`lazy` entries owned by an open intent. Operator-only; no notices, thresholds or model-facing effects.
|
|
10
|
+
- `Composition root step one`: Completed-history compaction request state moves into `StateFlowCompactionRequests` and settled-turn backup/push state into `SettledTurnBackup`, cutting `lib/extension.ts` mutable closure bindings from 34 to 26 and adding an invariant ceiling. No behaviour change.
|
|
11
|
+
- `Lifecycle review`: `lib/extension.ts` mutable closure bindings drop from 26 to 17 under the invariant ceiling. Artifact invalidations/hints move into `ArtifactAcquisitionState`, five lifecycle operations share one `OwnedOperationSlot`, renewable lifetimes and the session-identity guard have one owner each, and `noUnusedLocals`/`noUnusedParameters` guard dead code. No behaviour change.
|
|
12
|
+
- `Readable documentation`: Human-facing guides use short sections, explicit operation steps and resource-specific recovery rules. They describe current behavior rather than release chronology; performance documentation retains reproducible workloads and metric limits, not obsolete measurements. The acceptance map includes intent ownership and lifecycle owners, and a test checks relative documentation links and anchors.
|
|
13
|
+
|
|
5
14
|
## 0.24.0: Memory-Inert Off and Pi 1.0
|
|
6
15
|
|
|
7
16
|
- `Memory-inert Off`: Off startup, resume, reload, tree navigation and automatic callbacks perform no semantic-store I/O or recovery reporting, even with logging enabled. Switching to Off cancels owned memory waits and clears model tools/context without altering accepted memory; native mode, continuation, write-fence and pending-fork policy survive.
|
|
@@ -4,13 +4,18 @@
|
|
|
4
4
|
|
|
5
5
|
**Incremental scoped context/memory compiler for Pi.**
|
|
6
6
|
|
|
7
|
-
When enabled, State Flow maintains explicit state across requests and sessions.
|
|
7
|
+
When enabled, State Flow maintains explicit state across requests and sessions. Instead of carrying every completed exchange into the next request, the agent incrementally compiles requirements, decisions, findings and source knowledge into durable memory.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
The idea comes from the explicit-state approach of [SKILL.state](https://arxiv.org/html/2608.26263v2), combined with Pi's native conversation context:
|
|
10
|
+
|
|
11
|
+
- **State carries continuity between user runs.**
|
|
12
|
+
- **Pi's native context carries the working trajectory within a run.**
|
|
13
|
+
|
|
14
|
+
The conversation is not reset after each model response or tool call. Pi keeps ownership of execution, session navigation and the full inspectable trace.
|
|
10
15
|
|
|
11
16
|
## How it works
|
|
12
17
|
|
|
13
|
-
In Active mode,
|
|
18
|
+
In Active mode, each user run starts with the effective memory plus the new request. The agent works through Pi's ordinary inference/tool loop, updates memory when useful information changes, and returns an ordinary answer. The next run receives the accepted state and compact recent transitions instead of the completed conversation history.
|
|
14
19
|
|
|
15
20
|
```text
|
|
16
21
|
Current state + Request
|
|
@@ -22,11 +27,23 @@ Patched state + Answer
|
|
|
22
27
|
Updated state
|
|
23
28
|
```
|
|
24
29
|
|
|
25
|
-
|
|
30
|
+
What the model sees during a run:
|
|
31
|
+
|
|
32
|
+
- The request, intermediate responses, tool results and steering stay in context.
|
|
33
|
+
- The initial memory head stays byte-stable. Accepted changes arrive in patch results, and changing runtime notices are appended at the tail without moving earlier messages.
|
|
34
|
+
- Projection IDs tell current updates apart from retained historical results.
|
|
35
|
+
- A state patch neither rewrites the head nor discards the working trajectory.
|
|
36
|
+
- Persistent context-bearing messages from other extensions are preserved.
|
|
37
|
+
|
|
38
|
+
Passive memory uses the same approach across ordinary user turns and rebases only at native or mode boundaries.
|
|
39
|
+
|
|
40
|
+
Why this helps:
|
|
26
41
|
|
|
27
|
-
|
|
42
|
+
- It reduces reliance on repeated model-generated summaries of a growing transcript.
|
|
43
|
+
- Keeping the current trajectory lets prompt caches be reused while the relevant prefix is unchanged.
|
|
44
|
+
- Avoiding summary calls and repeated prompt processing can improve responsiveness. The effect depends on the model, provider, workload and how often state changes; there is no fixed latency guarantee.
|
|
28
45
|
|
|
29
|
-
Native compaction remains available for long runs. State Flow may also request a completed-history boundary without another model summary,
|
|
46
|
+
Native compaction remains available for long runs. State Flow may also request a completed-history boundary without another model summary, keeping the complete latest accepted run. Neither mechanism deletes Pi's append-only session trace. See [lifecycle behavior](docs/usage.md#session-behavior) and [performance evidence](docs/performance.md).
|
|
30
47
|
|
|
31
48
|
## Installation and activation
|
|
32
49
|
|
|
@@ -50,22 +67,28 @@ Enable active State Flow on the current branch:
|
|
|
50
67
|
/state-flow-active
|
|
51
68
|
```
|
|
52
69
|
|
|
53
|
-
Starting in an existing conversation
|
|
70
|
+
Starting in an existing conversation keeps its context for one complete bootstrap run, so the agent can compile what matters.
|
|
71
|
+
|
|
72
|
+
Commands:
|
|
54
73
|
|
|
55
74
|
- `/state-flow-active`: Select the active, state-driven iteration workflow.
|
|
56
75
|
- `/state-flow-passive`: Select ordinary conversation with both memory tools and existing-memory projection.
|
|
57
76
|
- `/state-flow-off`: Remove both memory tools and all State Flow model context, without deleting memory.
|
|
58
77
|
- `/state-flow-status`: Inspect effective state, retained history and known recovery issues without scanning sources or changing state.
|
|
59
78
|
|
|
60
|
-
New sessions default to **Off
|
|
79
|
+
New sessions default to **Off**. Choose Passive for ordinary conversation with memory tools, or Active for state-driven episodes. A mode change affects only the current session; the global default applies only to new sessions. See [mode semantics](docs/usage.md#active-passive-and-configured-off), [configuration](docs/usage.md#configuration) and [status and controls](docs/usage.md#status-and-controls).
|
|
61
80
|
|
|
62
81
|
### Active, passive, off
|
|
63
82
|
|
|
64
|
-
- `Active`: Memory tools are available
|
|
65
|
-
- `Passive`: Both memory tools and existing
|
|
66
|
-
- `Off`:
|
|
83
|
+
- `Active`: Memory tools are available, and the agent consolidates necessary final state changes before completing an iteration. Later iterations use accepted state and new input rather than completed prior reasoning.
|
|
84
|
+
- `Passive`: Both memory tools are available, and existing state is projected once a validated memory view is available. The agent patches on demand, and ordinary conversation context continues without State Flow's active iteration reset.
|
|
85
|
+
- `Off`: The model sees neither memory tool nor any State Flow context, including a frozen passive handoff.
|
|
86
|
+
- Startup, reload and tree attachment do not read or restore memory.
|
|
87
|
+
- Selecting Off cancels pending memory waits and records only native policy/bookmarks.
|
|
88
|
+
- Choosing Passive or Active later acquires memory.
|
|
89
|
+
- Stored memory and Pi's native trace stay intact.
|
|
67
90
|
|
|
68
|
-
Modes select agent behavior
|
|
91
|
+
Modes select agent behavior; they do not change disk persistence or fork copying. Final consolidation needs no empty ceremonial patch, and context projection does not delete native history. See [mode semantics and bootstrap terminology](docs/usage.md#active-passive-and-configured-off).
|
|
69
92
|
|
|
70
93
|
## State model
|
|
71
94
|
|
|
@@ -77,30 +100,51 @@ Memory has three ownership scopes:
|
|
|
77
100
|
- `cwd`: Knowledge shared by sessions in the same working directory.
|
|
78
101
|
- `session`: State belonging to the current session and its selected branch.
|
|
79
102
|
|
|
80
|
-
They compose recursively in **global → CWD → session** order
|
|
103
|
+
They compose recursively in **global → CWD → session** order:
|
|
104
|
+
|
|
105
|
+
- More-specific values override broader ones, while object fields merge.
|
|
106
|
+
- Removing a local value can reveal an inherited value again.
|
|
81
107
|
|
|
82
|
-
The agent receives the **effective view** of this composition, not three unrelated memory dumps. It can read that view or inspect
|
|
108
|
+
The agent receives the **effective view** of this composition, not three unrelated memory dumps. It can read that view, or inspect one scope when ownership matters. `effective` is a computed view, not a fourth storage scope. Scope precedence does not turn memory into system-level instructions.
|
|
83
109
|
|
|
84
110
|
### Semantic planes
|
|
85
111
|
|
|
86
112
|
Runtime views provide these documented planes:
|
|
87
113
|
|
|
88
|
-
- `intents`:
|
|
89
|
-
- `contract`: Requirements, decisions, constraints and interface commitments.
|
|
90
|
-
- `working`:
|
|
114
|
+
- `intents`: The queue of chosen actions. Work from intents: a structured `{"$ref"}` inside an intent owns a same-scope `working`/`lazy` key, and deleting the intent deletes what it owns unless another intent still references it. Textual `$path` mentions only use; unowned entries remain legal.
|
|
115
|
+
- `contract`: Requirements, decisions, rejected approaches, constraints and interface commitments.
|
|
116
|
+
- `working`: Temporary context of those actions: observations, results and uncertainties.
|
|
91
117
|
- `artifacts`: Source-addressed descriptions and compiled knowledge.
|
|
92
|
-
- `response`: The exact latest accepted answer, including an empty string
|
|
93
|
-
- `lazy`: Supporting memory available through explicit reads
|
|
118
|
+
- `response`: The exact latest accepted answer, including an empty string. The runtime captures it only in Session; Global/CWD receive no newly accepted answers, and stored scopes may omit it entirely. Effective uses the highest-priority nonempty value.
|
|
119
|
+
- `lazy`: Supporting memory available through explicit reads; its body is omitted from baseline model context.
|
|
120
|
+
|
|
121
|
+
Every plane is optional on disk:
|
|
122
|
+
|
|
123
|
+
- Stored checkpoints and patches may omit any documented plane, including `intents`, `lazy` or `response`.
|
|
124
|
+
- Current and historical views assemble only the known fields actually present in the selected scopes; absent fields are not filled in. Empty `response` counts as absent.
|
|
125
|
+
- Readers ignore unknown top-level fields, and writers emit only known fields. Nested data inside known planes is unrestricted.
|
|
126
|
+
- Reading or starting never rewrites data just to normalize it, and a missing field is not a storage-format error.
|
|
127
|
+
|
|
128
|
+
These planes organize ordinary JSON; there is no project-specific schema. The model updates every plane except `response`, which is runtime-owned.
|
|
94
129
|
|
|
95
|
-
|
|
130
|
+
Revisions:
|
|
96
131
|
|
|
97
|
-
|
|
132
|
+
- Global and CWD revisions are shared by their canonical stores; Session has its own revision.
|
|
133
|
+
- Effective has no single owner: its identity is the `g#c#s#` vector.
|
|
134
|
+
- One atomic patch advances each materially changed scope once.
|
|
98
135
|
|
|
99
|
-
|
|
136
|
+
Memory remains fallible: storing an observation does not make it current or correct.
|
|
137
|
+
|
|
138
|
+
Registered Pi Skills may be compiled into source-addressed artifacts when durable guidance is useful. Pi's resource provenance decides the owner: user Skills map to global, project Skills to CWD and temporary Skills to session. A matching source hash needs no update. An uncompiled read stays ordinary volatile context and does not block unrelated patches.
|
|
100
139
|
|
|
101
140
|
## Incremental updates and history
|
|
102
141
|
|
|
103
|
-
`patch_state` updates one or more named scopes atomically
|
|
142
|
+
`patch_state` updates one or more named scopes atomically:
|
|
143
|
+
|
|
144
|
+
- It waits cancelably for the store lock, then applies authored Global/CWD patches to the current canonical values, so other writers' untouched fields survive.
|
|
145
|
+
- Overlapping assignments follow successful acceptance order. Correct repeats succeed without new semantic revisions.
|
|
146
|
+
- Session remains private.
|
|
147
|
+
- Object patches merge recursively, and `null` deletes an object key instead of being stored.
|
|
104
148
|
|
|
105
149
|
```json
|
|
106
150
|
{
|
|
@@ -114,7 +158,7 @@ Registered Pi Skills may be compiled into source-addressed artifacts when durabl
|
|
|
114
158
|
}
|
|
115
159
|
```
|
|
116
160
|
|
|
117
|
-
During an active episode, a material patch is an inference barrier: sibling tool calls are blocked, and the next inference sees the accepted effective state. Ordinary completion needs no finalization patch or
|
|
161
|
+
During an active episode, a material patch is an inference barrier: sibling tool calls are blocked, and the next inference sees the accepted effective state. Ordinary completion needs no finalization patch or extra State Flow reasoning loop.
|
|
118
162
|
|
|
119
163
|
`read_state` provides targeted current and historical access:
|
|
120
164
|
|
|
@@ -127,29 +171,67 @@ During an active episode, a material patch is an inference barrier: sibling tool
|
|
|
127
171
|
- `cwd.patches[0]`: The latest retained CWD semantic patch.
|
|
128
172
|
- `effective.lazy.memory[0..3]`: A bounded slice of a stored collection.
|
|
129
173
|
|
|
130
|
-
|
|
174
|
+
How much history is kept:
|
|
175
|
+
|
|
176
|
+
- Historical materializations and scope patch histories use the configurable **`historyLimit`**, from **0 to 100**, default **7**.
|
|
177
|
+
- Offsets count accepted semantic transitions, not user messages or a separate counter per scope.
|
|
178
|
+
- Requested history must still exist in the active lineage; raising the limit cannot recreate discarded history.
|
|
179
|
+
- Older patches fold into the checkpoint without removing current values.
|
|
131
180
|
|
|
132
|
-
Array ranges, structural `keys` reads and path-intersected `patch` projections
|
|
181
|
+
Array ranges, structural `keys` reads and path-intersected `patch` projections let the agent explore memory without loading whole collections. See [progressive memory](docs/lazy-state.md) and [tool contracts](docs/architecture.md#model-tools).
|
|
133
182
|
|
|
134
183
|
## Persistence, backups and continuity
|
|
135
184
|
|
|
136
|
-
The default store is `~/.pi/agent/state-flow/`, independent of registered source files.
|
|
185
|
+
The default store is `~/.pi/agent/state-flow/`, independent of registered source files. Each scope keeps its state, retained changes and metadata in canonical `checkpoint.json`, `patches.jsonl` and `meta.json` files. Session configuration and runtime identity are stored separately.
|
|
186
|
+
|
|
187
|
+
**Persistence is optimistic across abrupt shutdown.**
|
|
188
|
+
|
|
189
|
+
- Short publication exclusion keeps independent shared fields from cooperating writers; on overlap, the last accepted write wins.
|
|
190
|
+
- Per-file atomic replacement does not guarantee power-loss survival or crash-atomic recovery.
|
|
191
|
+
- This is an [accepted limitation](docs/filesystem-recovery.md#power-loss-durability), not a release gate; no additional recovery journal or storage format is planned.
|
|
192
|
+
|
|
193
|
+
**Git backups are optional.** When the store is a configured Git repository:
|
|
194
|
+
|
|
195
|
+
- Accepted active turns may create versioned backups of State Flow-owned files.
|
|
196
|
+
- If the store is busy and Pi provides no cancellable settlement wait, the backup is explicitly deferred. A later accepted turn retries, without changing memory or blocking native Abort.
|
|
197
|
+
- If the attached branch has an explicitly configured remote, State Flow then pushes the exact current backup commit there, asynchronously and without force.
|
|
198
|
+
- Within one Pi process, at most one push per repository runs at a time. Overlapping attempts are skipped, and a later accepted turn pushes the latest backup.
|
|
199
|
+
- Off cancels pending captures and its admitted push, without rolling back accepted state or backup commits; canceled work emits no late memory warnings.
|
|
200
|
+
- Shutdown cancels owned pushes and waits for that repository's active push to close or time out.
|
|
201
|
+
- Commit failures warn locally. Repeated push failures produce one concise warning until a push succeeds, with redacted Git detail kept in the local diagnostic log. Neither failure rejects or rolls back accepted memory.
|
|
202
|
+
- Backup needs a Git commit identity; accepting and persisting state does not.
|
|
203
|
+
- Git history can be inspected separately, but it is not the authority for `read_state` or for automatic restoration of expired semantic boundaries.
|
|
204
|
+
|
|
205
|
+
**Resume, tree navigation and forks:**
|
|
206
|
+
|
|
207
|
+
- Memory-enabled resume and tree navigation restore the selected retained session boundary over the current shared global/CWD memory. Off defers this acquisition.
|
|
208
|
+
- A new session gets its own session layer.
|
|
209
|
+
- Supported native forks copy the selected session state into a new owner without changing the parent's private data.
|
|
210
|
+
- Expired, incomplete or contradictory boundaries never silently substitute newer private state during restoration.
|
|
211
|
+
- Explicit Start is a mode change, not historical restoration. It activates the validated **current** memory of that same session, including private state, even after expired active/passive, interrupted or pre-runtime selections. Available aligned history and revisions survive; unavailable history is not recreated.
|
|
212
|
+
- Malformed storage, unsafe fork copying and concurrent writes remain fenced.
|
|
213
|
+
|
|
214
|
+
See [fork support](docs/usage.md#fork-support-and-limits) and [storage recovery](docs/usage.md#storage-and-recovery).
|
|
137
215
|
|
|
138
|
-
**
|
|
216
|
+
**For SDK and launcher integrations:**
|
|
139
217
|
|
|
140
|
-
|
|
218
|
+
- [Advisory continuation APIs](docs/architecture.md#session-continuation) inspect provenance and build candidates asynchronously with host cancellation. They neither open native sessions nor install automatic resume.
|
|
219
|
+
- Run preparation and missing-artifact maintenance wait cancelably before inference; embeddings can also use the [transaction APIs](docs/architecture.md#asynchronous-storage-transaction).
|
|
141
220
|
|
|
142
|
-
|
|
221
|
+
How mode changes interact with storage:
|
|
143
222
|
|
|
144
|
-
|
|
223
|
+
- Selecting Passive switches local policy/context immediately and awaits runtime-only persistence. Passive keeps independently owned restoration/fork work. If its persistence fails, local policy stays and publication is fenced until an accepted activation.
|
|
224
|
+
- Off cancels owned memory waits and saves only native mode/continuation/fork bookkeeping, without validating or rewriting canonical storage. It also cancels restoration/fork work and defers later acquisition.
|
|
225
|
+
- Explicit Off inspection may read current stored values through a disposable validated reader. It does not activate memory, install cache or change the selected historical/fork boundary. Missing private authority stays unavailable.
|
|
226
|
+
- Active waits for a coherent capture and acceptance; another mode or selection can withdraw its wait.
|
|
145
227
|
|
|
146
|
-
|
|
228
|
+
State Flow accepts only the canonical store contract and provides no in-place format converter. Preserve existing data and check the [format boundary](docs/usage.md#moving-a-store-and-supported-formats) before changing versions or moving a store.
|
|
147
229
|
|
|
148
230
|
## Operational boundaries
|
|
149
231
|
|
|
150
|
-
State Flow adds memory, not another agent controller. It does not
|
|
232
|
+
State Flow adds memory, not another agent controller. It does not add background reasoning, a scheduler, automatic reference hydration or rollback of external tool effects. State and the current trajectory are not size-capped; performance depends on how much useful information the agent retains.
|
|
151
233
|
|
|
152
|
-
The packaged `state-flow-guide` Skill covers concrete operations and recovery. `state-flow-memory` supports explicitly requested curation, for example: **“Review and clean State Flow state”** Normal handoffs reconcile
|
|
234
|
+
The packaged `state-flow-guide` Skill covers concrete operations and recovery. `state-flow-memory` supports explicitly requested curation, for example: **“Review and clean State Flow state”** Normal handoffs reconcile the memory they touch; dedicated cleanup is not an automatic audit after each task.
|
|
153
235
|
|
|
154
236
|
Treat state, diagnostic logs and backups as private data. Revalidate consequential observations before acting, and verify a transfer's destination before deleting its source. Removing a value from current state does not erase older histories or remote copies.
|
|
155
237
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type ArtifactInvalidationReason, type ArtifactInvalidationRequest, type ArtifactSourceIdentity } from "./artifact.ts";
|
|
1
|
+
import { type ArtifactInvalidationReason, type ArtifactInvalidationRequest, type ArtifactSourceIdentity, type ArtifactProvenanceRegistry } from "./artifact.ts";
|
|
2
|
+
import type { AtomicScopePatches, ScopedSemanticStates, ScopedStates, StateScope } from "./state.ts";
|
|
2
3
|
/** Why the caller is considering source-body acquisition. */
|
|
3
4
|
export type ArtifactAcquisitionIntent = "routine" | "new-session" | "relevant-gap" | "exact-source" | "exact-edit" | "contradiction-or-failure" | "explicit-request" | "maintenance";
|
|
4
5
|
export type ArtifactAcquisitionReason = ArtifactInvalidationReason | "materialized-gap" | "exact-source" | "exact-edit" | "contradiction-or-failure" | "explicit-request" | "maintenance";
|
|
@@ -37,3 +38,25 @@ export declare class ArtifactReadTracker {
|
|
|
37
38
|
* stay on materialized state; only a concrete source need permits rereading.
|
|
38
39
|
*/
|
|
39
40
|
export declare function decideArtifactAcquisition(source: ArtifactSourceIdentity, metadata: unknown, compiler: string, options: ArtifactAcquisitionOptions): ArtifactAcquisitionDecision;
|
|
41
|
+
export declare const SOURCE_CHANGED_HINT = "Source changed since this artifact was compiled. Read and recompile it before relying on it.";
|
|
42
|
+
/**
|
|
43
|
+
* One selected branch's ordinary-artifact acquisition plan: runtime-observed
|
|
44
|
+
* invalidations, their model hints and the read tracker correlated to them.
|
|
45
|
+
* The tracker's candidates always equal the current invalidation plan.
|
|
46
|
+
*/
|
|
47
|
+
export declare class ArtifactAcquisitionState {
|
|
48
|
+
#private;
|
|
49
|
+
readonly reads: ArtifactReadTracker;
|
|
50
|
+
get invalidations(): readonly ArtifactInvalidationRequest[];
|
|
51
|
+
get hints(): Record<string, string>;
|
|
52
|
+
/** Drop the invalidation plan; hints remain until the next refresh. */
|
|
53
|
+
clearInvalidations(): void;
|
|
54
|
+
/** Forget plan and hints when no memory view is selected. */
|
|
55
|
+
reset(): void;
|
|
56
|
+
/** Re-observe exact registered sources for the selected scope artifacts. */
|
|
57
|
+
refresh(states: ScopedStates, provenance: (scope: StateScope) => ArtifactProvenanceRegistry): void;
|
|
58
|
+
/** Accepted compilations leave the plan; correlated read evidence is single-use. */
|
|
59
|
+
acceptAcquired(paths?: ReadonlySet<string>): void;
|
|
60
|
+
}
|
|
61
|
+
/** Deletion patches for registered artifacts whose exact source is observed missing, in every owning scope. */
|
|
62
|
+
export declare function missingArtifactRemovals(states: ScopedSemanticStates): AtomicScopePatches;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { classifyArtifactCompilationNeed, inspectRegisteredArtifactPaths, sameArtifactSourceFingerprint, } from "./artifact.js";
|
|
1
|
+
import { classifyArtifactCompilationNeed, inspectRegisteredArtifactPaths, sameArtifactSourceFingerprint, ORDINARY_ARTIFACT_COMPILER, } from "./artifact.js";
|
|
2
2
|
import { isObject } from "./json.js";
|
|
3
3
|
function readPath(toolName, args) {
|
|
4
4
|
return toolName === "read" && isObject(args) && typeof args.path === "string"
|
|
@@ -89,3 +89,82 @@ export function decideArtifactAcquisition(source, metadata, compiler, options) {
|
|
|
89
89
|
return { kind: "read-source", reason: "maintenance" };
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
|
+
export const SOURCE_CHANGED_HINT = "Source changed since this artifact was compiled. Read and recompile it before relying on it.";
|
|
93
|
+
const SCOPES = ["global", "cwd", "session"];
|
|
94
|
+
/**
|
|
95
|
+
* One selected branch's ordinary-artifact acquisition plan: runtime-observed
|
|
96
|
+
* invalidations, their model hints and the read tracker correlated to them.
|
|
97
|
+
* The tracker's candidates always equal the current invalidation plan.
|
|
98
|
+
*/
|
|
99
|
+
export class ArtifactAcquisitionState {
|
|
100
|
+
reads = new ArtifactReadTracker();
|
|
101
|
+
#invalidations = [];
|
|
102
|
+
#hints = {};
|
|
103
|
+
get invalidations() { return this.#invalidations; }
|
|
104
|
+
get hints() { return this.#hints; }
|
|
105
|
+
/** Drop the invalidation plan; hints remain until the next refresh. */
|
|
106
|
+
clearInvalidations() { this.#setInvalidations([]); }
|
|
107
|
+
/** Forget plan and hints when no memory view is selected. */
|
|
108
|
+
reset() {
|
|
109
|
+
this.#hints = {};
|
|
110
|
+
this.#setInvalidations([]);
|
|
111
|
+
}
|
|
112
|
+
/** Re-observe exact registered sources for the selected scope artifacts. */
|
|
113
|
+
refresh(states, provenance) {
|
|
114
|
+
const paths = new Set();
|
|
115
|
+
for (const scope of SCOPES)
|
|
116
|
+
for (const path of Object.keys(states[scope].artifacts))
|
|
117
|
+
paths.add(path);
|
|
118
|
+
const observations = new Map(inspectRegisteredArtifactPaths(paths).map((observation) => [observation.path, observation]));
|
|
119
|
+
const hints = {};
|
|
120
|
+
const invalidations = new Map();
|
|
121
|
+
for (const scope of SCOPES) {
|
|
122
|
+
const registry = provenance(scope);
|
|
123
|
+
for (const [path, metadata] of Object.entries(states[scope].artifacts)) {
|
|
124
|
+
// A narrower owner replaces broader evidence for the same path.
|
|
125
|
+
delete hints[path];
|
|
126
|
+
invalidations.delete(path);
|
|
127
|
+
if (metadata.kind === "skill")
|
|
128
|
+
continue;
|
|
129
|
+
const observed = observations.get(path);
|
|
130
|
+
if (observed?.kind !== "present")
|
|
131
|
+
continue;
|
|
132
|
+
const need = classifyArtifactCompilationNeed({ path, scope, sourceFingerprint: observed.fingerprint }, metadata, ORDINARY_ARTIFACT_COMPILER, false, registry[path]);
|
|
133
|
+
if (need.kind !== "requires-compilation")
|
|
134
|
+
continue;
|
|
135
|
+
if (need.reason === "source-changed")
|
|
136
|
+
hints[path] = SOURCE_CHANGED_HINT;
|
|
137
|
+
invalidations.set(path, { path, scope, reason: need.reason });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
this.#hints = hints;
|
|
141
|
+
this.#setInvalidations([...invalidations.values()].sort((left, right) => left.path.localeCompare(right.path)));
|
|
142
|
+
}
|
|
143
|
+
/** Accepted compilations leave the plan; correlated read evidence is single-use. */
|
|
144
|
+
acceptAcquired(paths = new Set(this.reads.successful.keys())) {
|
|
145
|
+
this.#setInvalidations(this.#invalidations.filter(({ path }) => !paths.has(path)));
|
|
146
|
+
this.reads.clear();
|
|
147
|
+
}
|
|
148
|
+
#setInvalidations(invalidations) {
|
|
149
|
+
this.#invalidations = invalidations;
|
|
150
|
+
this.reads.setCandidates(invalidations);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/** Deletion patches for registered artifacts whose exact source is observed missing, in every owning scope. */
|
|
154
|
+
export function missingArtifactRemovals(states) {
|
|
155
|
+
const owners = new Map();
|
|
156
|
+
for (const scope of SCOPES)
|
|
157
|
+
for (const path of Object.keys(states[scope].artifacts ?? {})) {
|
|
158
|
+
owners.set(path, [...owners.get(path) ?? [], scope]);
|
|
159
|
+
}
|
|
160
|
+
const removals = {};
|
|
161
|
+
for (const observation of inspectRegisteredArtifactPaths(owners.keys())) {
|
|
162
|
+
if (observation.kind !== "missing")
|
|
163
|
+
continue;
|
|
164
|
+
for (const scope of owners.get(observation.path) ?? []) {
|
|
165
|
+
const artifacts = (removals[scope]?.artifacts ?? {});
|
|
166
|
+
removals[scope] = { ...(removals[scope] ?? {}), artifacts: { ...artifacts, [observation.path]: null } };
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return removals;
|
|
170
|
+
}
|
|
@@ -47,4 +47,21 @@ export declare function stateFlowCompactionResult(plan: StateFlowCompactionPlan,
|
|
|
47
47
|
}): CompactionResult<StateFlowCompactionDetails> | {
|
|
48
48
|
cancel: true;
|
|
49
49
|
} | undefined;
|
|
50
|
+
type CompactionEvent = Parameters<typeof stateFlowCompactionResult>[2];
|
|
51
|
+
/** Completed-history compaction request state owned by one extension instance. */
|
|
52
|
+
export declare class StateFlowCompactionRequests {
|
|
53
|
+
#private;
|
|
54
|
+
get inFlight(): boolean;
|
|
55
|
+
get stopped(): boolean;
|
|
56
|
+
/** Permanently refuse owned requests (shutdown). */
|
|
57
|
+
stop(): void;
|
|
58
|
+
/** Drop the run-local plan; a stale callback can no longer clear a newer one. */
|
|
59
|
+
clear(): void;
|
|
60
|
+
/** Install a plan and return its unique per-request marker. */
|
|
61
|
+
begin(plan: StateFlowCompactionPlan): string;
|
|
62
|
+
/** Native completion clears only its own request. */
|
|
63
|
+
finish(marker: string): void;
|
|
64
|
+
/** Answer session_before_compact: foreign requests pass, stale or unpermitted owned ones cancel. */
|
|
65
|
+
resolve(event: CompactionEvent, permitted: boolean): ReturnType<typeof stateFlowCompactionResult>;
|
|
66
|
+
}
|
|
50
67
|
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
1
2
|
import { estimateTokens } from "@earendil-works/pi-coding-agent";
|
|
2
3
|
export const STATE_FLOW_COMPACTION_SUMMARY = "State Flow accepted the completed work before this boundary. Current memory is restored from its retained semantic boundary and projected separately; use the retained native entries for subsequent work.";
|
|
3
4
|
/** A modest margin above Pi's default 20k retained suffix absorbs estimation drift. */
|
|
@@ -65,3 +66,42 @@ export function stateFlowCompactionResult(plan, marker, event) {
|
|
|
65
66
|
details: structuredClone(plan.details),
|
|
66
67
|
};
|
|
67
68
|
}
|
|
69
|
+
const STATE_FLOW_COMPACTION_MARKER_PREFIX = "state-flow-boundary:";
|
|
70
|
+
/** Completed-history compaction request state owned by one extension instance. */
|
|
71
|
+
export class StateFlowCompactionRequests {
|
|
72
|
+
#plan;
|
|
73
|
+
#marker;
|
|
74
|
+
#inFlight = false;
|
|
75
|
+
#stopped = false;
|
|
76
|
+
#prefix = `${STATE_FLOW_COMPACTION_MARKER_PREFIX}${randomUUID()}:`;
|
|
77
|
+
get inFlight() { return this.#inFlight; }
|
|
78
|
+
get stopped() { return this.#stopped; }
|
|
79
|
+
/** Permanently refuse owned requests (shutdown). */
|
|
80
|
+
stop() { this.#stopped = true; }
|
|
81
|
+
/** Drop the run-local plan; a stale callback can no longer clear a newer one. */
|
|
82
|
+
clear() {
|
|
83
|
+
this.#plan = undefined;
|
|
84
|
+
this.#inFlight = false;
|
|
85
|
+
}
|
|
86
|
+
/** Install a plan and return its unique per-request marker. */
|
|
87
|
+
begin(plan) {
|
|
88
|
+
this.#plan = plan;
|
|
89
|
+
const marker = this.#marker = `${this.#prefix}${randomUUID()}`;
|
|
90
|
+
this.#inFlight = true;
|
|
91
|
+
return marker;
|
|
92
|
+
}
|
|
93
|
+
/** Native completion clears only its own request. */
|
|
94
|
+
finish(marker) {
|
|
95
|
+
if (this.#marker === marker)
|
|
96
|
+
this.clear();
|
|
97
|
+
}
|
|
98
|
+
/** Answer session_before_compact: foreign requests pass, stale or unpermitted owned ones cancel. */
|
|
99
|
+
resolve(event, permitted) {
|
|
100
|
+
if (event.reason !== "manual" || !event.customInstructions?.startsWith(STATE_FLOW_COMPACTION_MARKER_PREFIX))
|
|
101
|
+
return undefined;
|
|
102
|
+
const marker = this.#marker;
|
|
103
|
+
if (!permitted || this.#stopped || !this.#plan || !marker || event.customInstructions !== marker)
|
|
104
|
+
return { cancel: true };
|
|
105
|
+
return stateFlowCompactionResult(this.#plan, marker, event);
|
|
106
|
+
}
|
|
107
|
+
}
|