@llblab/pi-kit 0.3.1 → 0.4.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 (123) hide show
  1. package/AGENTS.md +3 -3
  2. package/BACKLOG.md +1 -1
  3. package/CHANGELOG.md +10 -0
  4. package/README.md +19 -10
  5. package/node_modules/@llblab/pi-actors/AGENTS.md +1 -0
  6. package/node_modules/@llblab/pi-actors/CHANGELOG.md +6 -0
  7. package/node_modules/@llblab/pi-actors/dist/skills/actors/SKILL.md +16 -6
  8. package/node_modules/@llblab/pi-actors/dist/skills/music-player/SKILL.md +3 -3
  9. package/node_modules/@llblab/pi-actors/dist/skills/music-player/genapps/music-player.mjs +6 -4
  10. package/node_modules/@llblab/pi-actors/dist/skills/music-player/scripts/playback.mjs +85 -18
  11. package/node_modules/@llblab/pi-actors/dist/skills/swarm/SKILL.md +2 -6
  12. package/node_modules/@llblab/pi-actors/dist/skills/swarm/references/development-swarm.md +2 -31
  13. package/node_modules/@llblab/pi-actors/docs/recipe-library.md +1 -1
  14. package/node_modules/@llblab/pi-actors/package.json +1 -1
  15. package/node_modules/@llblab/pi-actors/skills/actors/SKILL.md +16 -6
  16. package/node_modules/@llblab/pi-actors/skills/music-player/SKILL.md +3 -3
  17. package/node_modules/@llblab/pi-actors/skills/music-player/genapps/music-player.mjs +6 -4
  18. package/node_modules/@llblab/pi-actors/skills/music-player/scripts/playback.mjs +85 -18
  19. package/node_modules/@llblab/pi-actors/skills/swarm/SKILL.md +2 -6
  20. package/node_modules/@llblab/pi-actors/skills/swarm/references/development-swarm.md +2 -31
  21. package/node_modules/@llblab/pi-clean-room/AGENTS.md +5 -0
  22. package/node_modules/@llblab/pi-clean-room/BACKLOG.md +3 -0
  23. package/node_modules/@llblab/pi-clean-room/CHANGELOG.md +15 -0
  24. package/node_modules/@llblab/pi-clean-room/README.md +61 -0
  25. package/node_modules/@llblab/pi-clean-room/banner.jpg +0 -0
  26. package/node_modules/@llblab/pi-clean-room/index.ts +178 -0
  27. package/node_modules/@llblab/pi-clean-room/package.json +53 -0
  28. package/node_modules/@llblab/pi-grow-loop/AGENTS.md +1 -1
  29. package/node_modules/@llblab/pi-grow-loop/CHANGELOG.md +4 -0
  30. package/node_modules/@llblab/pi-grow-loop/package.json +1 -1
  31. package/node_modules/@llblab/pi-grow-loop/skills/grow-loop/SKILL.md +3 -17
  32. package/node_modules/@llblab/pi-state-flow/AGENTS.md +28 -0
  33. package/node_modules/@llblab/pi-state-flow/BACKLOG.md +3 -0
  34. package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +79 -0
  35. package/node_modules/@llblab/pi-state-flow/README.md +197 -0
  36. package/node_modules/@llblab/pi-state-flow/index.ts +4 -0
  37. package/node_modules/@llblab/pi-state-flow/lib/context.ts +88 -0
  38. package/node_modules/@llblab/pi-state-flow/lib/episode.ts +29 -0
  39. package/node_modules/@llblab/pi-state-flow/lib/extension.ts +238 -0
  40. package/node_modules/@llblab/pi-state-flow/lib/json.ts +78 -0
  41. package/node_modules/@llblab/pi-state-flow/lib/recovery.ts +38 -0
  42. package/node_modules/@llblab/pi-state-flow/lib/session.ts +49 -0
  43. package/node_modules/@llblab/pi-state-flow/lib/skills.ts +58 -0
  44. package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +103 -0
  45. package/node_modules/@llblab/pi-state-flow/lib/state.ts +20 -0
  46. package/node_modules/@llblab/pi-state-flow/lib/status.ts +16 -0
  47. package/node_modules/@llblab/pi-state-flow/lib/terminal.ts +148 -0
  48. package/node_modules/@llblab/pi-state-flow/lib/transition.ts +47 -0
  49. package/node_modules/@llblab/pi-state-flow/lib/validation.ts +27 -0
  50. package/node_modules/@llblab/pi-state-flow/package.json +55 -0
  51. package/node_modules/@llblab/pi-telegram/AGENTS.md +1 -1
  52. package/node_modules/@llblab/pi-telegram/BACKLOG.md +2 -0
  53. package/node_modules/@llblab/pi-telegram/CHANGELOG.md +16 -0
  54. package/node_modules/@llblab/pi-telegram/README.md +1 -1
  55. package/node_modules/@llblab/pi-telegram/docs/architecture.md +1 -1
  56. package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +17 -5
  57. package/node_modules/@llblab/pi-telegram/docs/outbound.md +3 -3
  58. package/node_modules/@llblab/pi-telegram/docs/public-api.md +2 -0
  59. package/node_modules/@llblab/pi-telegram/index.ts +9 -0
  60. package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +29 -18
  61. package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +15 -6
  62. package/node_modules/@llblab/pi-telegram/lib/bus.ts +11 -4
  63. package/node_modules/@llblab/pi-telegram/lib/outbound-markup.ts +34 -2
  64. package/node_modules/@llblab/pi-telegram/lib/outbound.ts +4 -1
  65. package/node_modules/@llblab/pi-telegram/lib/routing.ts +89 -32
  66. package/node_modules/@llblab/pi-telegram/lib/sync.ts +74 -15
  67. package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +32 -1
  68. package/node_modules/@llblab/pi-telegram/lib/thread-reconciler.ts +17 -0
  69. package/node_modules/@llblab/pi-telegram/lib/threads.ts +123 -15
  70. package/node_modules/@llblab/pi-telegram/package.json +1 -1
  71. package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/SKILL.md +1 -1
  72. package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/references/diagnosis.md +4 -2
  73. package/node_modules/@llblab/skills/abcd-context/AGENTS.md +40 -0
  74. package/node_modules/@llblab/skills/abcd-context/BACKLOG.md +3 -0
  75. package/node_modules/@llblab/skills/abcd-context/CHANGELOG.md +19 -0
  76. package/node_modules/@llblab/skills/abcd-context/README.md +30 -0
  77. package/node_modules/@llblab/skills/abcd-context/SKILL.md +122 -0
  78. package/node_modules/@llblab/skills/abcd-context/docs/README.md +9 -0
  79. package/node_modules/@llblab/skills/abcd-context/docs/protocols.md +199 -0
  80. package/node_modules/@llblab/skills/abcd-context/docs/templates.md +194 -0
  81. package/node_modules/@llblab/skills/abcd-context/docs/validation-design.md +117 -0
  82. package/node_modules/@llblab/skills/abcd-context/fixtures/abcd-project/AGENTS.md +10 -0
  83. package/node_modules/@llblab/skills/abcd-context/fixtures/abcd-project/BACKLOG.md +5 -0
  84. package/node_modules/@llblab/skills/abcd-context/fixtures/abcd-project/CHANGELOG.md +5 -0
  85. package/node_modules/@llblab/skills/abcd-context/fixtures/abcd-project/README.md +10 -0
  86. package/node_modules/@llblab/skills/abcd-context/fixtures/abcd-project/docs/README.md +7 -0
  87. package/node_modules/@llblab/skills/abcd-context/fixtures/abcd-project/docs/overview.md +3 -0
  88. package/node_modules/@llblab/skills/abcd-context/recipes/validate-context.json +7 -0
  89. package/node_modules/@llblab/skills/abcd-context/scripts/_self-test.mjs +244 -0
  90. package/node_modules/@llblab/skills/abcd-context/scripts/validate-context.mjs +497 -0
  91. package/node_modules/@llblab/skills/brain-storm/AGENTS.md +5 -0
  92. package/node_modules/@llblab/skills/brain-storm/SKILL.md +321 -0
  93. package/node_modules/@llblab/skills/coding-contract/SKILL.md +228 -0
  94. package/node_modules/@llblab/skills/domain-dag/AGENTS.md +6 -0
  95. package/node_modules/@llblab/skills/domain-dag/SKILL.md +257 -0
  96. package/node_modules/@llblab/skills/domain-dag/domain-dag.json +9 -0
  97. package/node_modules/@llblab/skills/domain-dag/recipes/validate-domain-dag.json +7 -0
  98. package/node_modules/@llblab/skills/domain-dag/scripts/validate-domain-dag.mjs +771 -0
  99. package/node_modules/@llblab/skills/domain-dag/scripts/validate-domain-dag.sh +4 -0
  100. package/node_modules/@llblab/skills/edge-tts/AGENTS.md +19 -0
  101. package/node_modules/@llblab/skills/edge-tts/SKILL.md +56 -0
  102. package/node_modules/@llblab/skills/edge-tts/recipes/say-edge.json +9 -0
  103. package/node_modules/@llblab/skills/edge-tts/scripts/say.mjs +1548 -0
  104. package/node_modules/@llblab/skills/edge-tts/scripts/say.sh +4 -0
  105. package/node_modules/@llblab/skills/extra-self/SKILL.md +229 -0
  106. package/node_modules/@llblab/skills/frontend-design/SKILL.md +78 -0
  107. package/node_modules/@llblab/skills/groq-stt/AGENTS.md +15 -0
  108. package/node_modules/@llblab/skills/groq-stt/SKILL.md +35 -0
  109. package/node_modules/@llblab/skills/groq-stt/recipes/transcribe-groq.json +10 -0
  110. package/node_modules/@llblab/skills/groq-stt/scripts/transcribe.mjs +190 -0
  111. package/node_modules/@llblab/skills/groq-stt/scripts/transcribe.sh +4 -0
  112. package/node_modules/@llblab/skills/mistral-stt/AGENTS.md +15 -0
  113. package/node_modules/@llblab/skills/mistral-stt/SKILL.md +35 -0
  114. package/node_modules/@llblab/skills/mistral-stt/recipes/transcribe-mistral.json +10 -0
  115. package/node_modules/@llblab/skills/mistral-stt/scripts/transcribe.mjs +191 -0
  116. package/node_modules/@llblab/skills/mistral-stt/scripts/transcribe.sh +4 -0
  117. package/node_modules/@llblab/skills/package.json +46 -0
  118. package/node_modules/@llblab/skills/re-review/SKILL.md +318 -0
  119. package/node_modules/@llblab/skills/release-flow/SKILL.md +456 -0
  120. package/node_modules/@llblab/skills/show-me/SKILL.md +144 -0
  121. package/package.json +20 -8
  122. package/node_modules/@llblab/pi-actors/dist/skills/music-player/scripts/playback-client.mjs +0 -143
  123. package/node_modules/@llblab/pi-actors/skills/music-player/scripts/playback-client.mjs +0 -143
@@ -0,0 +1,28 @@
1
+ # Agent Instructions
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.
4
+ - Keep the extension opt-in and preserve clear attribution to SKILL.state wherever the inherited explicit-state approach is described.
5
+ - Preserve Pi's native tool loop and complete inspectable session trace; project completed-run history only at user-run boundaries. 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 fully materialized state with exactly `contract`, `working`, and `response`; the first two are flexible memory objects and the third is the latest complete user-facing answer string.
7
+ - Require no State Flow patch on tool-bearing assistant responses; retain the current run's user message, assistant tool calls, and tool results until one terminal response commits state.
8
+ - Accept a non-empty ordinary terminal answer without a State Flow comment as an empty memory patch: preserve `contract` and `working`, replace `response`, and use the same staging, Skill-compilation validation, and commit lifecycle. Explicit comment markers (including malformed or embedded ones) must not silently fall back to ordinary text. When a patch is present, require one top-level transcript-private `<!-- state_flow … -->` comment containing only `contract` and `working`, one separating blank line, then the user-facing answer exactly once. At `message_end`, remove the comment and stage the transition; at `turn_end`, reconcile `response` with Pi's finalized assistant message after all chained handlers by concatenating finalized text blocks without inserted characters, then commit. Route missing text or a tool call added after terminal validation through the same bounded regeneration chain. Transcript-private does not mean confidential during streaming.
9
+ - Treat terminal state as a decision-relevant handoff, not narration: compile stable requirements, decisions, rejected approaches, interface commitments, and reusable operational knowledge into `contract`; retain verified facts, artifacts, validation, failures, current domain state, unresolved work, and exact continuation in `working`.
10
+ - 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.
11
+ - 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 routine Skill rereads, action ledgers, or runtime freshness/rollback guarantees.
12
+ - Treat each successful `SKILL.md` read as episode-level acquisition using the finalized tool-execution arguments after mutable interception: require a non-empty `contract.compiled_skills[exactReadPath]`, keep its nested representation fully flexible without `coverage`/`rules` schema, reject the terminal commit when missing, treat complete matching compilations as authoritative, and forbid routine rereads without an uncovered detail, incomplete compilation, concrete source-change evidence, reconciliation failure, or explicit user request.
13
+ - Make the model audit and optimize complete state at every handoff: reorganize inefficient structure, merge fragmented facts, compress history into conclusions, and delete stale, completed, redundant, or low-value keys while preserving active commitments and evidence.
14
+ - Accept empty `contract` and `working` patches when nothing future-relevant changed and memory is already efficient; never force invented bookkeeping. The comment may be omitted for a no-op memory patch. Always require `response`. Semantic usefulness and optimization remain protocol-owned because deterministic validation cannot prove them.
15
+ - Keep validation retries hidden from finalized user-facing messages; expose diagnostic detail only through transcript-private feedback and persisted runtime state, while documenting that streaming observers can still see generated terminal envelopes. Tool-bearing retry turns must preserve cumulative validation-attempt accounting. If retries are exhausted, aborted, or cannot continue before the agent settles, keep State Flow enabled and the last committed state intact; abandon only the transient validation chain so the next user run rotates normally.
16
+ - Recursively materialize patches immediately; empty objects preserve, nested object-key `null` deletes, and `null` anywhere in materialized state including arrays is invalid.
17
+ - Do not impose project schemas, state or patch byte caps, dynamic growth pressure, observation envelopes, action authorization, action ledgers, or state-size limits.
18
+ - Rotate the turn-stable specification on every non-retry user run, but not during its tool or terminal-validation retry chain. 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.
19
+ - When enabled inside an existing session, retain Pi's active context for exactly one complete bootstrap run and require its terminal handoff to migrate all future-relevant context.
20
+ - Restore extension state from the active session branch, not the full session entry list, on both session startup and successful in-session tree navigation.
21
+ - Render compact status as accent `state-flow` plus dim `#<iteration>`; `/state-flow-status` must expose the complete materialized JSON state after a blank line separating it from summary metadata.
22
+ - `/state-flow-stop` must disable and clear the complete episode; no separate reset commands exist.
23
+ - Keep the injected runtime protocol compact and normative; put rationale and extended explanation in README rather than the model prompt. Strip accidental State Flow comments from tool-bearing output only when a complete structurally valid envelope (valid JSON fields, exact separator, and non-empty response) leads the first text block; never delete malformed, quoted, or embedded examples.
24
+ - Do not claim strict boundedness for state, the current run trajectory, the turn specification, or the external full trace.
25
+ - Remain extension-agnostic: do not import, name, special-case, or encode policy for any other extension or transport.
26
+ - Register no model tools.
27
+ - 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.
28
+ - Run `npm run validate` after retained code changes.
@@ -0,0 +1,3 @@
1
+ # Backlog
2
+
3
+ No planned work.
@@ -0,0 +1,79 @@
1
+ # Changelog
2
+
3
+ ## 0.3.0: Optional memory patches and evidence-aware handoffs
4
+
5
+ - Require targeted revalidation of volatile observations and inspection of external effects after interruption or branch navigation; missing memory proves neither success nor absence of effects. Document the absence of rollback/freshness guarantees and preserve the restriction on routine Skill rereads. Add instruction-presence tests, not semantic or integration guarantees.
6
+ - Strengthen the handoff protocol with evidence-aware continuation, requirement/observation/hypothesis distinctions, conditional rejections, and contradiction reconciliation; preserve decision-relevant uncertainty during optimization. Add instruction-presence regressions, not semantic guarantees. Increase the protocol-only character budget to 3,700 (3,900 with bootstrap) for these obligations; state remains uncapped.
7
+ - Accept ordinary non-empty terminal answers as empty memory patches: preserve memory and update the response without unnecessary regeneration, including answers split across text blocks.
8
+ - Keep malformed explicit envelopes and missing Skill compilations on the existing bounded validation path.
9
+ - Clear the backlog; documented integration and continuation-quality limitations remain, not release commitments.
10
+
11
+ ## 0.2.3: Document protocol and validation limitations
12
+
13
+ - Document that duplicate-comment detection also rejects literal State Flow envelope examples inside fenced or inline code in terminal answers, and provide a plain-JSON workaround.
14
+ - Clarify that mock lifecycle tests are not real Pi scheduler integration checks and that structural validation cannot guarantee semantic memory retention.
15
+ - No runtime behavior changes.
16
+
17
+ ## 0.2.2: README structure hotfix
18
+
19
+ - Separate installation from usage, remove the redundant quick-start section, and move architecture details after the user-facing documentation.
20
+
21
+ ## 0.2.1: README banner hotfix
22
+
23
+ - Add the project banner to the README.
24
+
25
+ ## 0.2.0: Modular runtime hardening
26
+
27
+ - Add a task-first README quick start with explicit compatibility, opt-in behavior, one-off trial instructions, and Pi package security guidance.
28
+ - Tighten the normative runtime protocol and enforce a regression-tested character budget; omit absent validation feedback from synthetic runtime context instead of serializing a redundant `null`.
29
+ - Redistribute lifecycle coverage from the former monolithic extension suite into the corresponding mirrored domain suites, leaving `extension.test.ts` focused on composition and using one shared test harness.
30
+ - Split the runtime into independent `json`, `state`, `episode`, `snapshot`, `session`, `recovery`, `status`, `context`, `skills`, `terminal`, `validation`, `transition`, and `extension` modules under `lib/`; isolate explicit episode transitions, active-branch selection and recovery, status rendering, bounded retry decisions, atomic state staging, and compare-and-swap commits; mirror each domain under `tests/`, enforce an acyclic dependency graph with cross-domain invariant tests, and reduce `index.ts` to composition and public exports.
31
+ - Register lifecycle commands in the canonical start, status, stop order.
32
+ - Centralize synthetic runtime-context construction and private validation-message filtering in the independently tested context domain, keeping user-controlled specifications out of system-prompt composition.
33
+ - Keep State Flow enabled and preserve the last committed state when terminal-validation retries are exhausted; only the transient retry chain is abandoned so the next user request can continue reliably.
34
+ - Track Skill reads through an independently tested lifecycle correlator in Pi's actual `tool_execution_start` → `tool_call` event order while retaining the mutable intercepted input reference, so later argument rewrites are attributed to the executed path.
35
+ - Discard mismatched lifecycle records defensively so a stale or reused tool-call id cannot produce false Skill acquisition.
36
+ - Bound restored iteration and validation-attempt counters before incrementing them, reject exhausted live iteration counters explicitly, and cover both safe-integer boundaries so malformed branch metadata cannot corrupt retry accounting or status output.
37
+ - Reject restored non-JSON state values such as non-finite numbers before they can break hashing or silently materialize as `null`.
38
+ - Fall back past malformed newer checkpoints to the newest valid active-branch snapshot, while still failing closed on cyclic or hostile snapshot objects when no valid checkpoint remains.
39
+ - Reject malformed three-field state shapes rather than reinterpreting them as legacy working memory, and contain failures while enumerating hostile branch entries.
40
+ - Make canonical serialization and exported patch validation reject lossy or cyclic non-JSON inputs explicitly instead of returning invalid results or silently rewriting values.
41
+
42
+ ## 0.1.5: Skill attribution and state model clarification
43
+
44
+ - Attribute successful Skill acquisition to finalized `tool_execution_start` arguments, with a tested compatibility fallback to the intercepted `tool_call` input.
45
+ - Clarify that the three-field materialized handoff coexists with mutable exogenous project and runtime state that can also shape model behavior.
46
+ - Remove the obsolete `/reload` step from the usage instructions.
47
+
48
+ ## 0.1.4: Release verification hotfix
49
+
50
+ - Kept npm pack lifecycle output out of release inventory JSON so Trusted Publisher releases can complete public verification and GitHub Release creation.
51
+
52
+ ## 0.1.3: Trusted Publisher release automation
53
+
54
+ - Added immutable-tag GitHub Actions automation that validates the package, publishes and verifies npm through Trusted Publisher with provenance, and creates the matching GitHub Release.
55
+
56
+ ## 0.1.2: Repository identity hotfix
57
+
58
+ - Aligned GitHub repository, package metadata, installation documentation, and release links under the canonical `llblab/pi-state-flow` identity.
59
+
60
+ ## 0.1.1: Public npm distribution
61
+
62
+ - Enabled public npm distribution for `@llblab/pi-state-flow`, including registry access and verified GitHub package metadata.
63
+ - Clarified that compiled Skill knowledge is stored persistently under `contract.compiled_skills`, never as another top-level state field.
64
+
65
+ ## 0.1.0
66
+
67
+ - Added an opt-in State Flow runtime inspired by SKILL.state, with argument-free lifecycle commands, per-user-run specifications, active-branch persistence, and one materialized explicit state.
68
+ - Added native Pi tool-loop preservation: tool-bearing responses require no patch, current-run tool trajectory remains model-visible, and one terminal handoff commits state after the complete agent run.
69
+ - Added required flexible `contract` and `working` objects plus the required latest user-facing `response` string, recursive materialization, nested object-key `null` deletion, materialized-null rejection, and deterministic state compare-and-swap.
70
+ - Kept arbitrary response content outside the transcript-private `<!-- state_flow … -->` memory-patch frame; the runtime strictly anchors the frame at the start of one terminal text block, requires exactly one blank separator, reconciles materialized `response` with Pi's finalized post-handler text at `turn_end`, and routes post-handler finalization failures through the same bounded hidden regeneration chain. Documented that the frame remains observable to streaming consumers before finalization.
71
+ - Kept user-controlled turn specifications at user authority by moving their repeated representation out of the system prompt and into synthetic user runtime context, including support for empty text in image-only prompts.
72
+ - Added active-branch restoration after `/tree` navigation, clean abandonment of interrupted terminal-validation retries before the next user run, and cumulative retry accounting across intervening tool-bearing turns.
73
+ - Preserved persistent and current-run custom context from other Pi extensions, attributed Skill acquisition from finalized executed arguments after mutable tool interception, and limited accidental intermediate-envelope stripping to a structurally valid leading frame.
74
+ - Hardened recursive patch materialization so JSON keys such as `__proto__` remain ordinary own data without changing object prototypes.
75
+ - Added an explicit release file allowlist, Pi package discovery keywords, package-safe README targets, Pi/Node compatibility bounds, and a prepack validation gate so generated packages contain only runtime and human-facing release files.
76
+ - Added bootstrap migration for enabling State Flow inside an existing session, per-run specification rotation, schema-free episode-level Skill compilation in `contract.compiled_skills` keyed by exact source path, and model-owned memory optimization that reorganizes inefficient structure and deletes stale, redundant, speculative, or low-value state.
77
+ - Removed per-tool state commits, action authorization, Delta Window, observation envelopes, action ledgers, project schemas, state and patch byte caps, dynamic growth pressure, and all coupling to other extensions.
78
+ - Compressed the static runtime protocol while retaining terminal, handoff, Skill-compilation, authority, and memory-optimization invariants.
79
+ - Added stop-as-complete-episode-reset semantics, a compact accent `state-flow` plus dim `#<iteration>` status, and complete pretty-formatted state JSON output from `/state-flow-status`.
@@ -0,0 +1,197 @@
1
+ # Pi State Flow
2
+
3
+ > Inspired by [SKILL.state](https://arxiv.org/html/2608.26263v2)
4
+
5
+ ![pi-state-flow banner](https://raw.githubusercontent.com/llblab/pi-state-flow/main/banner.jpg)
6
+
7
+ An opt-in Pi extension for explicit, validated state handoffs between complete agent runs.
8
+
9
+ State Flow preserves Pi's native tool loop inside each user request:
10
+
11
+ ```text
12
+ user request
13
+ → model → tool → model → tool → model
14
+ → one terminal answer (memory patch when needed)
15
+ → committed state + displayed response
16
+ ```
17
+
18
+ The current run keeps its user message, assistant tool calls, tool results, and persistent or current-run context-bearing custom messages from other Pi extensions model-visible. Only after the terminal handoff does the next run replace that trajectory with one materialized state. Pi's complete session trace remains inspectable.
19
+
20
+ ## Installation
21
+
22
+ Requirements: Pi `0.84.4–0.84.x` and Node.js `22.19.0` or newer.
23
+
24
+ Install from npm:
25
+
26
+ ```bash
27
+ pi install npm:@llblab/pi-state-flow
28
+ ```
29
+
30
+ Or install from GitHub:
31
+
32
+ ```bash
33
+ pi install git:github.com/llblab/pi-state-flow
34
+ ```
35
+
36
+ Pi packages execute with full user permissions; review the source before installing, as with any extension.
37
+
38
+ ## Usage
39
+
40
+ ```text
41
+ /state-flow-start
42
+ ```
43
+
44
+ In a fresh session, the next prompt starts the first stateful run. Each later non-retry user prompt becomes the current turn-stable specification while committed state remains intact. When enabled inside an existing session, the first complete run retains the active pre-Flow context and must migrate every future-relevant fact into its terminal handoff.
45
+
46
+ The extension registers no model tools. Tool-bearing assistant responses use ordinary Pi tools and contain no State Flow patch. When memory changes, a terminal assistant response contains one transcript-private memory-patch comment, one separating blank line, and the user-facing answer exactly once. When memory is unchanged, an ordinary non-empty answer is sufficient: the runtime treats the missing comment as an empty memory patch, preserving `contract` and `working` while replacing `response`. Transcript-private means removed from the finalized message and later model context, not confidential during streaming:
47
+
48
+ ```html
49
+ <!-- state_flow {"contract":{...},"working":{...}} -->
50
+
51
+ Complete user-facing answer
52
+ ```
53
+
54
+ At `message_end`, an explicit private comment must start the single terminal text block and be followed by exactly one blank line. Without a State Flow comment marker, ordinary text blocks are concatenated without inserted characters and preserved unchanged; no regeneration is triggered merely because the comment is absent. Malformed, incomplete, or embedded State Flow comment markers still fail explicit-envelope validation rather than silently becoming no-op patches. It removes only the comment and separator and stages all three state fields. At `turn_end`, it reconciles `response` with Pi's finalized assistant message after all chained `message_end` handlers, concatenating any text blocks without inserting characters, then commits. If a later handler removes all answer text or adds a tool call after terminal validation, the same hidden regeneration chain runs. Arbitrary response Markdown remains outside the HTML comment, so its own content cannot terminate the private frame. Invalid terminal commits are regenerated through hidden feedback up to three times. If all retries fail, State Flow remains enabled, preserves the last committed snapshot, abandons only the transient validation chain, and lets the next user request start cleanly from that state. Tool-bearing turns do not reset the attempt count. Aborting a regeneration has the same non-destructive behavior.
55
+
56
+ Commands:
57
+
58
+ ```text
59
+ /state-flow-start # Start a fresh episode
60
+ /state-flow-status # Inspect iteration metadata and the complete state JSON
61
+ /state-flow-stop # Stop and clear the complete episode
62
+ ```
63
+
64
+ The compact Pi status renders an accent `state-flow` label followed by the dimmed committed iteration number, for example `state-flow #7`. `/state-flow-status` reports iteration metadata, then separates the complete pretty-formatted materialized state JSON with a blank line for operator analysis.
65
+
66
+ `/state-flow-stop` clears the specification, materialized state, validation feedback, and committed-run counter. A later `/state-flow-start` always begins a fresh episode. State follows the active Pi session branch and is restored immediately after `/tree` navigation. If the newest checkpoint is malformed, restoration walks backward to the newest valid checkpoint instead of resetting an otherwise recoverable episode. Entering a branch with no State Flow snapshot leaves the mode disabled there.
67
+
68
+ ## Materialized state
69
+
70
+ The model sees one persistent state object before the current run trajectory:
71
+
72
+ ```json
73
+ {
74
+ "contract": {},
75
+ "working": {},
76
+ "response": "Latest complete user-facing answer"
77
+ }
78
+ ```
79
+
80
+ Materialized state has exactly three fields:
81
+
82
+ - `contract` is a flexible object containing durable user requirements, stable decisions, rejected approaches, interface commitments, and compact operational knowledge compiled from relevant Skills or documents.
83
+ - `working` is a flexible object containing verified facts, artifacts, validation, failures, unresolved work, current environment or domain state, and the exact continuation point.
84
+ - `response` is the exact non-empty answer body captured by the runtime. It replaces the previous response on every commit.
85
+
86
+ The transcript-private wire patch contains only `contract` and `working`; the runtime derives `response` from Pi's finalized answer body. Memory patches merge recursively and materialize immediately. Empty objects preserve existing content; arrays and primitives replace. Nested object-key `null` deletes that key:
87
+
88
+ ```html
89
+ <!-- state_flow {"contract":{},"working":{"move":null,"result":"ok"}} -->
90
+
91
+ The operation completed.
92
+ ```
93
+
94
+ This removes `working.move`, replaces `working.result`, preserves every other memory key, and stores `The operation completed.` as `response`. Materialized state cannot contain `null`, including inside arrays; represent semantic absence by omitting a key or using an explicit non-null domain value.
95
+
96
+ State and patch size have no byte, growth, pressure, or project-schema limit. Patch history remains only in the Pi trace, not in model context.
97
+
98
+ These three fields are the complete **materialized State Flow state**, but they are not the only state that can shape model behavior. A situational fourth, exogenous state lives outside the handoff: the current project, workspace, tools, processes, and runtime environment. It can change while work is in progress—including through the model's own tool effects—and later observations of those changes can alter subsequent behavior. State Flow neither snapshots nor rolls back this external state; `working` should retain only the decision-relevant facts needed to reconnect the next run to it.
99
+
100
+ ### Reconnecting to external state
101
+
102
+ Treat `working` as the last observation, not a live workspace. Before consequential actions, revalidate the volatile facts that action depends on: for example, the current revision and dirty files before editing, or the remote publication status before retrying a release. This is targeted inspection, not a requirement to reread stable knowledge or compiled Skills routinely.
103
+
104
+ After an interruption or session-branch navigation, inspect relevant external effects before repeating operations. A failed terminal commit does not undo file edits, running processes, or remote requests; restoring older memory does not restore the workspace. Missing memory is evidence of neither success nor absence of effects. If the effect cannot be verified, retain that uncertainty and the next discriminating check instead of blindly retrying or claiming completion.
105
+
106
+ These are protocol obligations, not runtime freshness checks, rollback, or exactly-once execution guarantees. No action ledger or new evidence-retrieval tool is introduced.
107
+
108
+ ## Terminal handoff quality
109
+
110
+ The terminal answer commits a handoff, not a progress phrase or transcript summary. A memory patch is required when future-relevant memory changes; omitting it is only shorthand for preserving existing memory, not a way to infer new memory from prose. The next run may see only this state plus its new user prompt. A fresh model should be able to continue without rereading, rediscovering, re-deriving decisions, or repeating failed approaches.
111
+
112
+ Useful handoffs capture:
113
+
114
+ - Stable requirements, constraints, decisions, rejected approaches, and compiled operational rules in `contract`.
115
+ - Verified facts, changed artifacts, validation evidence, failures, unresolved work, current domain state, and exact continuation in `working`.
116
+
117
+ Before compressing, preserve active constraints, unresolved questions, consequential negative results, and the next check that would distinguish competing explanations. Keep observations, user requirements, assistant decisions, and hypotheses distinguishable; an assistant conclusion is not a user requirement. Retain decision-relevant hypotheses as uncertain rather than deleting them merely because they are unverified.
118
+
119
+ For consequential facts, include a compact source locator and validity condition when useful (for example, a test command and the revision it checked), not mandatory metadata on every value. Record why an approach was rejected and what would justify reconsidering it. When new information conflicts with an established constraint or observation, reconcile it using evidence or user clarification; neither an unsupported new claim nor fallible old memory wins automatically. If unresolved, preserve the conflict and the next discriminating check.
120
+
121
+ These are model obligations, not semantic validation gates. Protocol tests check that the instructions remain present; only continuation evaluations can establish whether a fresh agent makes the next correct decision.
122
+
123
+ Do not store raw source, logs, tool output, reasoning traces, or vague values such as `"continue work"` and `"in progress"`.
124
+
125
+ ### Skill compilation
126
+
127
+ A successful `SKILL.md` read is treated as episode-level acquisition. Before terminal commit, the model compiles its future-relevant operational rules, applicability conditions, constraints, syntax, routing decisions, and failure conditions under the exact source path. This compilation lives inside persistent `contract` memory at `contract.compiled_skills`; `compiled_skills` never becomes a fourth top-level state field. The nested representation remains fully flexible:
128
+
129
+ ```json
130
+ {
131
+ "contract": {
132
+ "compiled_skills": {
133
+ "/exact/path/to/SKILL.md": {
134
+ "routing": "...",
135
+ "syntax": { "...": "..." },
136
+ "constraints": ["..."],
137
+ "reread_when": ["..."]
138
+ }
139
+ }
140
+ }
141
+ }
142
+ ```
143
+
144
+ No `coverage`/`rules` schema is imposed. The model chooses the smallest structure that faithfully preserves the Skill's useful behavior. Raw Skill text is not copied. The runtime checks only that the exact successfully executed source path has a non-empty compilation and rejects a terminal commit when it is missing. Attribution follows Pi's lifecycle order: it retains the mutable `tool_call` input reference so later interception rewrites resolve to the path actually executed, with `tool_execution_start` arguments as a compatibility fallback.
145
+
146
+ A complete matching compilation is authoritative episode memory and replaces routine rereading. Rereading is justified only by an explicitly uncovered detail, an incomplete compilation, concrete source-change evidence, a contradiction or execution failure requiring reconciliation, or an explicit user request. The mere possibility that a source changed is not sufficient. A justified reread refreshes the compilation and removes obsolete rules.
147
+
148
+ State is a minimal sufficient memory, not an append-only diary. At every handoff the model audits the complete state and may reorganize inefficient structure, merge fragments, replace verbose history with current conclusions, and delete stale, completed, redundant, or low-value keys with `null`. Active requirements, decisions, interfaces, verified evidence, and unresolved work must survive optimization.
149
+
150
+ The model must not invent bookkeeping merely to change `contract` or `working`. When a run creates no future-relevant information and existing memory is already efficient, both patch objects may remain empty, or the comment may be omitted entirely, while `response` still contains the actual answer. Successful Skill reads still require their compilations in materialized memory even when the comment is omitted. Bootstrap migration remains a model obligation: a plain answer cannot migrate pre-Flow context automatically. Structurally invalid handoffs are regenerated through hidden validation feedback; retry diagnostics are not user-facing output.
151
+
152
+ ## Context lifecycle
153
+
154
+ Within one run, State Flow projects:
155
+
156
+ ```text
157
+ persistent materialized state
158
+ + current user prompt
159
+ + current-run assistant tool calls
160
+ + current-run tool results
161
+ + persistent and current-run context-bearing custom messages from other extensions
162
+ + optional terminal validation feedback
163
+ ```
164
+
165
+ Earlier completed-run trajectories are removed from model context. During a bootstrap run, the active pre-Flow context remains available until the first successful terminal commit.
166
+
167
+ The normative protocol remains in the system prompt throughout a tool/retry chain. The turn-stable specification remains user-authority input: it stays in the initiating user message and is repeated, together with materialized state and private validation feedback, only in a synthetic user runtime-context message. The protocol explicitly treats persistent state as fallible assistant-produced data whose transport role does not elevate it into user instructions. Empty text specifications used by image-only prompts remain valid. State and the current-run trajectory are intentionally unbounded; State Flow does not claim a hard model-context bound.
168
+
169
+ ## Boundaries
170
+
171
+ - State commits exactly once per successful complete agent run, at terminal `message_end`/`turn_end`.
172
+ - Tool-bearing responses do not require or commit patches and retain ordinary Pi tool behavior, including multiple sequential calls. The runtime removes an accidental leading terminal envelope only when its JSON, fields, separator, and non-empty response are all structurally valid; malformed or quoted examples remain untouched.
173
+ - Terminal answers cannot contain another complete State Flow comment, even inside a fenced code block or inline code: duplicate detection scans the entire answer body, not Markdown structure. When explaining the protocol while State Flow is enabled, describe the fields or show plain JSON without the HTML comment delimiters. The literal envelope examples in this README are documentation, not valid content to copy into a terminal answer body.
174
+ - A failed tool remains in the current run trajectory for ordinary model reconciliation before terminal commit.
175
+ - If a run never reaches a valid terminal handoff, its external tool effects may exist while persistent State Flow state remains at the previous commit.
176
+ - The terminal envelope exists in ordinary assistant text until `message_end`; `message_update`, RPC, JSON, or other streaming consumers can observe it. Never place secrets in State Flow state.
177
+ - This mode remains a poor fit for auditing or outputs that require complete historical trajectories across user requests.
178
+ - Token, cache, latency, and success-rate advantages still require controlled Pi benchmarks.
179
+
180
+ ## Architecture
181
+
182
+ `index.ts` is only the package composition and public-export boundary. Independent runtime domains live under `lib/`: `json` owns lossless JSON and patches, `state` owns the materialized state shape, `episode` owns explicit start/stop and user-run boundaries, `snapshot` owns persistence migration, `session` owns active-branch snapshot discovery and bootstrap detection, `recovery` falls back to the newest valid active-branch checkpoint, `status` owns deterministic operator-facing status rendering, `context` owns trajectory projection, private-feedback filtering, and synthetic runtime-context construction, `skills` owns Skill acquisition rules and mutable lifecycle correlation, `terminal` owns the handoff protocol, `validation` owns bounded retry decisions, `transition` owns atomic staging and compare-and-swap commits, and `extension` coordinates these domains with Pi. Every domain has a same-named test under `tests/`; lifecycle scenarios are colocated with the domain whose contract they exercise, `tests/extension.test.ts` remains focused on composition, shared setup lives in `tests/harness.ts`, and cross-domain constraints live in `tests/invariants.test.ts`.
183
+
184
+ ## Validation
185
+
186
+ ```bash
187
+ npm install
188
+ npm run validate
189
+ ```
190
+
191
+ Validation covers TypeScript checking, automated tests, and an extension import smoke check. Lifecycle tests use a mock Pi event harness; they do not establish behavior under the real Pi scheduler, queued prompts, abort/compaction interactions, or combinations of extensions. Those scenarios still need integration checks. Structural patch validation also cannot prove that a handoff preserved every important requirement or compiled a Skill faithfully.
192
+
193
+ ## Project status
194
+
195
+ - [Open work](BACKLOG.md)
196
+ - [Release history](CHANGELOG.md)
197
+ - [Agent and contributor constraints](AGENTS.md)
@@ -0,0 +1,4 @@
1
+ export { applyPatch, canonicalJson, hashJson, isObject, validatePatch } from "./lib/json.ts";
2
+ export type { JsonObject, JsonValue } from "./lib/json.ts";
3
+ export { emptyState, isStateDocument, type StateDocument } from "./lib/state.ts";
4
+ export { default } from "./lib/extension.ts";
@@ -0,0 +1,88 @@
1
+ import type { AgentMessage } from "@earendil-works/pi-agent-core";
2
+ import { canonicalJson } from "./json.ts";
3
+ import type { Snapshot } from "./snapshot.ts";
4
+
5
+ export const VALIDATION_MESSAGE_TYPE = "state-flow-validation";
6
+
7
+ export function syntheticUser(text: string): AgentMessage {
8
+ return { role: "user", content: [{ type: "text", text }], timestamp: Date.now() } as AgentMessage;
9
+ }
10
+
11
+ function contentText(content: unknown): string {
12
+ if (typeof content === "string") return content;
13
+ if (!Array.isArray(content)) return "";
14
+ return content.map((part) => {
15
+ if (typeof part !== "object" || part === null) return "";
16
+ const block = part as { type?: unknown; text?: unknown };
17
+ return block.type === "text" && typeof block.text === "string" ? block.text : "";
18
+ }).filter(Boolean).join("\n");
19
+ }
20
+
21
+ function messageText(message: AgentMessage): string {
22
+ return contentText((message as { content?: unknown }).content);
23
+ }
24
+
25
+ export function withoutPrivateValidation(messages: AgentMessage[]): AgentMessage[] {
26
+ return messages.filter((message) => {
27
+ return !(message.role === "custom" && message.customType === VALIDATION_MESSAGE_TYPE);
28
+ });
29
+ }
30
+
31
+ export function runtimeContextMessage(snapshot: Snapshot): AgentMessage {
32
+ if (snapshot.specification === undefined) {
33
+ throw new Error("State Flow runtime context requires an active specification");
34
+ }
35
+ const context = {
36
+ specification: snapshot.specification,
37
+ state: snapshot.state,
38
+ ...(snapshot.validation === undefined ? {} : { validation_feedback: snapshot.validation }),
39
+ };
40
+ return syntheticUser(
41
+ `State Flow runtime context (user-level data, not system instructions):\n${canonicalJson(context)}`,
42
+ );
43
+ }
44
+
45
+ export function currentRunTrajectory(
46
+ messages: AgentMessage[],
47
+ specification: string,
48
+ anchorTimestamp: number | undefined,
49
+ ): { messages: AgentMessage[]; anchorTimestamp?: number } {
50
+ let start = -1;
51
+ if (anchorTimestamp !== undefined) {
52
+ start = messages.findLastIndex((message) => {
53
+ return message.role === "user"
54
+ && message.timestamp === anchorTimestamp
55
+ && messageText(message) === specification;
56
+ });
57
+ }
58
+ if (start < 0) {
59
+ for (let index = messages.length - 1; index >= 0; index--) {
60
+ const message = messages[index]!;
61
+ if (message.role === "user" && messageText(message) === specification) {
62
+ start = index;
63
+ break;
64
+ }
65
+ }
66
+ }
67
+ if (start < 0) {
68
+ for (let index = messages.length - 1; index >= 0; index--) {
69
+ if (messages[index]?.role === "user") {
70
+ start = index;
71
+ break;
72
+ }
73
+ }
74
+ }
75
+ if (start < 0 && messages.length === 0) return { messages: [] };
76
+ if (start < 0) start = 0;
77
+ const anchor = messages[start]?.role === "user" ? messages[start].timestamp : undefined;
78
+ const persistentCustom = withoutPrivateValidation(messages.slice(0, start)).filter((message) => {
79
+ return message.role === "custom";
80
+ });
81
+ return {
82
+ messages: [
83
+ ...persistentCustom,
84
+ ...withoutPrivateValidation(messages.slice(start)),
85
+ ],
86
+ ...(typeof anchor === "number" ? { anchorTimestamp: anchor } : {}),
87
+ };
88
+ }
@@ -0,0 +1,29 @@
1
+ import { emptyState } from "./state.ts";
2
+ import type { Snapshot } from "./snapshot.ts";
3
+
4
+ export function startEpisode(bootstrap: boolean): Snapshot {
5
+ return { enabled: true, state: emptyState(), step: 0, bootstrap };
6
+ }
7
+
8
+ export function stopEpisode(): Snapshot {
9
+ return { enabled: false, state: emptyState(), step: 0 };
10
+ }
11
+
12
+ /** Apply one user-run boundary while preserving the materialized state. */
13
+ export function prepareRun(snapshot: Snapshot, prompt: string, isRetry: boolean): boolean {
14
+ if (snapshot.specification === undefined) {
15
+ snapshot.specification = prompt;
16
+ return true;
17
+ }
18
+ if (isRetry) return false;
19
+ snapshot.specification = prompt;
20
+ snapshot.validation = undefined;
21
+ return true;
22
+ }
23
+
24
+ /** Clear only transient validation metadata; never disable or reset the episode. */
25
+ export function abandonValidation(snapshot: Snapshot): boolean {
26
+ if (snapshot.validation === undefined) return false;
27
+ snapshot.validation = undefined;
28
+ return true;
29
+ }