@llblab/pi-kit 0.24.1 → 0.26.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 (162) hide show
  1. package/AGENTS.md +1 -1
  2. package/BACKLOG.md +5 -1
  3. package/CHANGELOG.md +12 -0
  4. package/README.md +11 -8
  5. package/node_modules/@llblab/pi-actors/AGENTS.md +2 -0
  6. package/node_modules/@llblab/pi-actors/CHANGELOG.md +4 -1
  7. package/node_modules/@llblab/pi-actors/LICENSE +21 -0
  8. package/node_modules/@llblab/pi-actors/README.md +1 -1
  9. package/node_modules/@llblab/pi-actors/docs/coordinator-delivery.md +1 -1
  10. package/node_modules/@llblab/pi-actors/package.json +4 -3
  11. package/node_modules/@llblab/pi-claude-usage/AGENTS.md +23 -0
  12. package/node_modules/@llblab/pi-claude-usage/BACKLOG.md +4 -0
  13. package/node_modules/@llblab/pi-claude-usage/CHANGELOG.md +21 -0
  14. package/node_modules/@llblab/pi-claude-usage/LICENSE +22 -0
  15. package/node_modules/@llblab/pi-claude-usage/README.md +155 -0
  16. package/node_modules/@llblab/pi-claude-usage/banner.jpg +0 -0
  17. package/node_modules/@llblab/pi-claude-usage/index.ts +8 -0
  18. package/node_modules/@llblab/pi-claude-usage/lib/extension.ts +30 -0
  19. package/node_modules/@llblab/pi-claude-usage/lib/fast.ts +24 -0
  20. package/node_modules/@llblab/pi-claude-usage/lib/query.ts +146 -0
  21. package/node_modules/@llblab/pi-claude-usage/lib/status-format.ts +297 -0
  22. package/node_modules/@llblab/pi-claude-usage/lib/status.ts +366 -0
  23. package/node_modules/@llblab/pi-claude-usage/lib/telegram.ts +44 -0
  24. package/node_modules/@llblab/pi-claude-usage/lib/usage-store.ts +221 -0
  25. package/node_modules/@llblab/pi-claude-usage/lib/usage.ts +128 -0
  26. package/node_modules/@llblab/pi-claude-usage/package.json +64 -0
  27. package/node_modules/@llblab/pi-clean-room/AGENTS.md +1 -0
  28. package/node_modules/@llblab/pi-clean-room/CHANGELOG.md +5 -0
  29. package/node_modules/@llblab/pi-clean-room/LICENSE +21 -0
  30. package/node_modules/@llblab/pi-clean-room/README.md +1 -1
  31. package/node_modules/@llblab/pi-clean-room/package.json +3 -2
  32. package/node_modules/@llblab/pi-codex-usage/AGENTS.md +9 -6
  33. package/node_modules/@llblab/pi-codex-usage/BACKLOG.md +2 -1
  34. package/node_modules/@llblab/pi-codex-usage/CHANGELOG.md +17 -0
  35. package/node_modules/@llblab/pi-codex-usage/README.md +75 -17
  36. package/node_modules/@llblab/pi-codex-usage/index.ts +8 -1602
  37. package/node_modules/@llblab/pi-codex-usage/lib/extension.ts +25 -0
  38. package/node_modules/@llblab/pi-codex-usage/lib/fast.ts +23 -0
  39. package/node_modules/@llblab/pi-codex-usage/lib/query.ts +368 -0
  40. package/node_modules/@llblab/pi-codex-usage/lib/status-format.ts +347 -0
  41. package/node_modules/@llblab/pi-codex-usage/lib/status.ts +435 -0
  42. package/node_modules/@llblab/pi-codex-usage/lib/telegram.ts +45 -0
  43. package/node_modules/@llblab/pi-codex-usage/lib/usage-store.ts +229 -0
  44. package/node_modules/@llblab/pi-codex-usage/lib/usage.ts +425 -0
  45. package/node_modules/@llblab/pi-codex-usage/package.json +11 -6
  46. package/node_modules/@llblab/pi-command-fast/AGENTS.md +7 -0
  47. package/node_modules/@llblab/pi-command-fast/BACKLOG.md +9 -0
  48. package/node_modules/@llblab/pi-command-fast/CHANGELOG.md +7 -0
  49. package/node_modules/@llblab/pi-command-fast/LICENSE +21 -0
  50. package/node_modules/@llblab/pi-command-fast/README.md +42 -0
  51. package/node_modules/@llblab/pi-command-fast/dist/command.d.ts +8 -0
  52. package/node_modules/@llblab/pi-command-fast/dist/command.js +52 -0
  53. package/node_modules/@llblab/pi-command-fast/dist/index.d.ts +3 -0
  54. package/node_modules/@llblab/pi-command-fast/dist/index.js +3 -0
  55. package/node_modules/@llblab/pi-command-fast/dist/models-json.d.ts +10 -0
  56. package/node_modules/@llblab/pi-command-fast/dist/models-json.js +81 -0
  57. package/node_modules/@llblab/pi-command-fast/package.json +49 -0
  58. package/node_modules/@llblab/pi-grow-loop/AGENTS.md +1 -0
  59. package/node_modules/@llblab/pi-grow-loop/CHANGELOG.md +4 -1
  60. package/node_modules/@llblab/pi-grow-loop/LICENSE +21 -0
  61. package/node_modules/@llblab/pi-grow-loop/README.md +1 -1
  62. package/node_modules/@llblab/pi-grow-loop/package.json +3 -2
  63. package/node_modules/@llblab/pi-state-flow/AGENTS.md +43 -56
  64. package/node_modules/@llblab/pi-state-flow/BACKLOG.md +17 -3
  65. package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +25 -0
  66. package/node_modules/@llblab/pi-state-flow/LICENSE +21 -0
  67. package/node_modules/@llblab/pi-state-flow/README.md +18 -15
  68. package/node_modules/@llblab/pi-state-flow/dist/index.d.ts +2 -2
  69. package/node_modules/@llblab/pi-state-flow/dist/index.js +2 -2
  70. package/node_modules/@llblab/pi-state-flow/dist/lib/compaction.d.ts +1 -1
  71. package/node_modules/@llblab/pi-state-flow/dist/lib/compaction.js +1 -1
  72. package/node_modules/@llblab/pi-state-flow/dist/lib/config.d.ts +7 -3
  73. package/node_modules/@llblab/pi-state-flow/dist/lib/config.js +16 -7
  74. package/node_modules/@llblab/pi-state-flow/dist/lib/context.d.ts +9 -9
  75. package/node_modules/@llblab/pi-state-flow/dist/lib/context.js +5 -4
  76. package/node_modules/@llblab/pi-state-flow/dist/lib/continuation.js +2 -2
  77. package/node_modules/@llblab/pi-state-flow/dist/lib/durable.d.ts +1 -1
  78. package/node_modules/@llblab/pi-state-flow/dist/lib/durable.js +7 -4
  79. package/node_modules/@llblab/pi-state-flow/dist/lib/episode.d.ts +3 -3
  80. package/node_modules/@llblab/pi-state-flow/dist/lib/episode.js +5 -5
  81. package/node_modules/@llblab/pi-state-flow/dist/lib/extension.d.ts +3 -5
  82. package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +503 -235
  83. package/node_modules/@llblab/pi-state-flow/dist/lib/git.d.ts +2 -2
  84. package/node_modules/@llblab/pi-state-flow/dist/lib/git.js +16 -5
  85. package/node_modules/@llblab/pi-state-flow/dist/lib/history.d.ts +11 -4
  86. package/node_modules/@llblab/pi-state-flow/dist/lib/history.js +6 -7
  87. package/node_modules/@llblab/pi-state-flow/dist/lib/logging.d.ts +4 -1
  88. package/node_modules/@llblab/pi-state-flow/dist/lib/logging.js +1 -0
  89. package/node_modules/@llblab/pi-state-flow/dist/lib/query.d.ts +4 -5
  90. package/node_modules/@llblab/pi-state-flow/dist/lib/query.js +13 -13
  91. package/node_modules/@llblab/pi-state-flow/dist/lib/recovery.d.ts +7 -6
  92. package/node_modules/@llblab/pi-state-flow/dist/lib/recovery.js +9 -9
  93. package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.d.ts +3 -2
  94. package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.js +17 -12
  95. package/node_modules/@llblab/pi-state-flow/dist/lib/session.d.ts +13 -1
  96. package/node_modules/@llblab/pi-state-flow/dist/lib/session.js +62 -2
  97. package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.d.ts +23 -8
  98. package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.js +52 -20
  99. package/node_modules/@llblab/pi-state-flow/dist/lib/state.d.ts +22 -3
  100. package/node_modules/@llblab/pi-state-flow/dist/lib/state.js +30 -10
  101. package/node_modules/@llblab/pi-state-flow/dist/lib/status.d.ts +5 -3
  102. package/node_modules/@llblab/pi-state-flow/dist/lib/status.js +19 -28
  103. package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.d.ts +17 -15
  104. package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.js +52 -52
  105. package/node_modules/@llblab/pi-state-flow/dist/lib/temporal.d.ts +8 -4
  106. package/node_modules/@llblab/pi-state-flow/dist/lib/temporal.js +34 -18
  107. package/node_modules/@llblab/pi-state-flow/dist/lib/transition.d.ts +5 -5
  108. package/node_modules/@llblab/pi-state-flow/dist/lib/transition.js +13 -19
  109. package/node_modules/@llblab/pi-state-flow/dist/package.json +12 -11
  110. package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-guide/SKILL.md +2 -2
  111. package/node_modules/@llblab/pi-state-flow/docs/README.md +2 -1
  112. package/node_modules/@llblab/pi-state-flow/docs/agent-contract-relocation.md +72 -0
  113. package/node_modules/@llblab/pi-state-flow/docs/architecture.md +44 -36
  114. package/node_modules/@llblab/pi-state-flow/docs/compatibility.md +14 -6
  115. package/node_modules/@llblab/pi-state-flow/docs/fork-contract.md +7 -5
  116. package/node_modules/@llblab/pi-state-flow/docs/lazy-state.md +6 -6
  117. package/node_modules/@llblab/pi-state-flow/docs/performance.md +1 -1
  118. package/node_modules/@llblab/pi-state-flow/docs/temporal-acceptance.md +13 -12
  119. package/node_modules/@llblab/pi-state-flow/docs/usage.md +37 -33
  120. package/node_modules/@llblab/pi-state-flow/index.ts +3 -2
  121. package/node_modules/@llblab/pi-state-flow/lib/compaction.ts +2 -2
  122. package/node_modules/@llblab/pi-state-flow/lib/config.ts +20 -10
  123. package/node_modules/@llblab/pi-state-flow/lib/context.ts +15 -14
  124. package/node_modules/@llblab/pi-state-flow/lib/continuation.ts +1 -1
  125. package/node_modules/@llblab/pi-state-flow/lib/durable.ts +8 -6
  126. package/node_modules/@llblab/pi-state-flow/lib/episode.ts +6 -6
  127. package/node_modules/@llblab/pi-state-flow/lib/extension.ts +484 -232
  128. package/node_modules/@llblab/pi-state-flow/lib/git.ts +14 -5
  129. package/node_modules/@llblab/pi-state-flow/lib/history.ts +16 -11
  130. package/node_modules/@llblab/pi-state-flow/lib/logging.ts +5 -1
  131. package/node_modules/@llblab/pi-state-flow/lib/query.ts +16 -16
  132. package/node_modules/@llblab/pi-state-flow/lib/recovery.ts +11 -11
  133. package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +19 -13
  134. package/node_modules/@llblab/pi-state-flow/lib/session.ts +57 -3
  135. package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +57 -22
  136. package/node_modules/@llblab/pi-state-flow/lib/state.ts +46 -13
  137. package/node_modules/@llblab/pi-state-flow/lib/status.ts +23 -32
  138. package/node_modules/@llblab/pi-state-flow/lib/telegram.ts +66 -65
  139. package/node_modules/@llblab/pi-state-flow/lib/temporal.ts +39 -19
  140. package/node_modules/@llblab/pi-state-flow/lib/transition.ts +19 -27
  141. package/node_modules/@llblab/pi-state-flow/package.json +12 -11
  142. package/node_modules/@llblab/pi-state-flow/skills/state-flow-guide/SKILL.md +2 -2
  143. package/node_modules/jsonc-parser/CHANGELOG.md +76 -0
  144. package/node_modules/jsonc-parser/LICENSE.md +21 -0
  145. package/node_modules/jsonc-parser/README.md +364 -0
  146. package/node_modules/jsonc-parser/SECURITY.md +41 -0
  147. package/node_modules/jsonc-parser/lib/esm/impl/edit.js +185 -0
  148. package/node_modules/jsonc-parser/lib/esm/impl/format.js +261 -0
  149. package/node_modules/jsonc-parser/lib/esm/impl/parser.js +659 -0
  150. package/node_modules/jsonc-parser/lib/esm/impl/scanner.js +443 -0
  151. package/node_modules/jsonc-parser/lib/esm/impl/string-intern.js +29 -0
  152. package/node_modules/jsonc-parser/lib/esm/main.d.ts +351 -0
  153. package/node_modules/jsonc-parser/lib/esm/main.js +178 -0
  154. package/node_modules/jsonc-parser/lib/umd/impl/edit.js +201 -0
  155. package/node_modules/jsonc-parser/lib/umd/impl/format.js +275 -0
  156. package/node_modules/jsonc-parser/lib/umd/impl/parser.js +682 -0
  157. package/node_modules/jsonc-parser/lib/umd/impl/scanner.js +456 -0
  158. package/node_modules/jsonc-parser/lib/umd/impl/string-intern.js +42 -0
  159. package/node_modules/jsonc-parser/lib/umd/main.d.ts +351 -0
  160. package/node_modules/jsonc-parser/lib/umd/main.js +194 -0
  161. package/node_modules/jsonc-parser/package.json +37 -0
  162. package/package.json +10 -6
@@ -7,9 +7,19 @@ export declare class RevisionUnavailableError extends Error {
7
7
  /** Expired history cannot be restored, but explicit activation may use validated current memory. */
8
8
  export declare class HistoryBoundaryExpiredError extends RevisionUnavailableError {
9
9
  }
10
+ export type StateFlowMode = "active" | "passive" | "off";
11
+ export type InactiveMode = Exclude<StateFlowMode, "active">;
12
+ export declare function isStateFlowMode(value: unknown): value is StateFlowMode;
13
+ /** The session's selected mode is the only serialized behavior switch. */
10
14
  export interface SnapshotConfig {
11
- enabled: boolean;
15
+ mode: StateFlowMode;
12
16
  }
17
+ /**
18
+ * Read only mode policy, without validating or accessing semantic checkpoint metadata.
19
+ * Missing/invalid policy stays undecided; callers must not treat this as restoration proof.
20
+ * Legacy `enabled:false` follows the caller's inactive policy.
21
+ */
22
+ export declare function readCheckpointMode(value: unknown, inactiveMode?: InactiveMode): StateFlowMode | undefined;
13
23
  interface LegacyValidationFeedback {
14
24
  attempt: number;
15
25
  error: string;
@@ -52,23 +62,28 @@ export declare function serializeSessionRuntime(runtime: SessionRuntime, cwd: st
52
62
  config: string;
53
63
  runtime: string;
54
64
  };
65
+ /** Legacy `enabled:false` decodes as non-active; native checkpoints, not this file, select branch policy. */
55
66
  export declare function parseSessionRuntime(config: string | undefined, runtimeSource: string | undefined, cwd: string, sessionId: string): SessionRuntime | undefined;
56
- export declare function emptySnapshot(enabled?: boolean): Snapshot;
67
+ export declare function emptySnapshot(mode?: StateFlowMode): Snapshot;
57
68
  export type RetainedBoundaryCheckpoint = {
58
69
  boundary: string;
59
- enabled: boolean;
70
+ mode: StateFlowMode;
60
71
  step: number;
61
72
  bootstrap?: true;
62
73
  specification?: string;
63
74
  };
64
- export type RetainedPiCheckpoint = RetainedBoundaryCheckpoint | {
65
- disabled: true;
75
+ /** A proven pre-runtime branch retains only its explicit inactive choice, never semantic storage. */
76
+ export type PreRuntimeCheckpoint = {
77
+ mode: InactiveMode;
66
78
  };
79
+ export type RetainedPiCheckpoint = RetainedBoundaryCheckpoint | PreRuntimeCheckpoint;
67
80
  export type FileRevision = `file:${string}`;
68
81
  export declare function isFileRevision(value: unknown): value is FileRevision;
69
82
  /** Encode branch lifecycle against one retained temporal identity without semantic or backup data. */
70
83
  export declare function retainedBoundaryCheckpoint(snapshot: Snapshot, boundary: string): RetainedBoundaryCheckpoint;
71
- /** Decode the 0.17 retained-window checkpoint contract. */
72
- export declare function parseRetainedPiCheckpoint(value: unknown): RetainedPiCheckpoint;
73
- export declare function migrationFailure(data: JsonObject, error: string): Snapshot;
84
+ /** Encode an explicit inactive choice on a branch that has no accepted runtime. */
85
+ export declare function preRuntimeCheckpoint(mode: StateFlowMode): PreRuntimeCheckpoint;
86
+ /** Decode the retained-window checkpoint contract; legacy `enabled`/`{disabled:true}` markers map through `inactiveMode`. */
87
+ export declare function parseRetainedPiCheckpoint(value: unknown, inactiveMode?: InactiveMode): RetainedPiCheckpoint;
88
+ export declare function migrationFailure(data: JsonObject, error: string, mode?: InactiveMode): Snapshot;
74
89
  export {};
@@ -11,6 +11,21 @@ export class RevisionUnavailableError extends Error {
11
11
  /** Expired history cannot be restored, but explicit activation may use validated current memory. */
12
12
  export class HistoryBoundaryExpiredError extends RevisionUnavailableError {
13
13
  }
14
+ export function isStateFlowMode(value) {
15
+ return value === "active" || value === "passive" || value === "off";
16
+ }
17
+ /**
18
+ * Read only mode policy, without validating or accessing semantic checkpoint metadata.
19
+ * Missing/invalid policy stays undecided; callers must not treat this as restoration proof.
20
+ * Legacy `enabled:false` follows the caller's inactive policy.
21
+ */
22
+ export function readCheckpointMode(value, inactiveMode = "passive") {
23
+ if (!isObject(value))
24
+ return undefined;
25
+ if (Object.hasOwn(value, "mode"))
26
+ return Object.hasOwn(value, "enabled") || !isStateFlowMode(value.mode) ? undefined : value.mode;
27
+ return typeof value.enabled === "boolean" ? value.enabled ? "active" : inactiveMode : undefined;
28
+ }
14
29
  export function validateSessionRuntime(value, cwd, sessionId) {
15
30
  if (!isJsonValue(value) || !isObject(value) || Object.keys(value).sort().join(",") !== "config,meta"
16
31
  || !isObject(value.config) || !isObject(value.meta))
@@ -31,7 +46,7 @@ export function validateSessionRuntime(value, cwd, sessionId) {
31
46
  }
32
47
  const runtimeFields = Object.fromEntries(Object.entries(fields).filter(([key]) => known.has(key)));
33
48
  const normalized = {
34
- config: { enabled: value.config.enabled === true },
49
+ config: { mode: isStateFlowMode(value.config.mode) ? value.config.mode : "off" },
35
50
  meta: restoredMeta(runtimeFields),
36
51
  };
37
52
  if (runtimeFields.bootstrap === false)
@@ -61,6 +76,7 @@ export function serializeSessionRuntime(runtime, cwd, sessionId) {
61
76
  const { temporal: _retiredTemporal, artifacts: _legacyArtifacts, ...runtimeMeta } = runtime.meta;
62
77
  return { config: `${canonicalJson(runtime.config)}\n`, runtime: `${canonicalJson(runtimeMeta)}\n` };
63
78
  }
79
+ /** Legacy `enabled:false` decodes as non-active; native checkpoints, not this file, select branch policy. */
64
80
  export function parseSessionRuntime(config, runtimeSource, cwd, sessionId) {
65
81
  if (config === undefined && runtimeSource === undefined)
66
82
  return undefined;
@@ -68,14 +84,20 @@ export function parseSessionRuntime(config, runtimeSource, cwd, sessionId) {
68
84
  throw new Error("Incomplete State Flow config/runtime pair");
69
85
  let runtime;
70
86
  try {
71
- runtime = { config: JSON.parse(config), meta: JSON.parse(runtimeSource) };
87
+ const settings = JSON.parse(config);
88
+ const mode = isObject(settings) && Object.keys(settings).length === 1 ? readCheckpointMode(settings, "passive") : undefined;
89
+ if (mode === undefined)
90
+ throw new Error("Invalid State Flow runtime configuration or counters");
91
+ runtime = { config: { mode }, meta: JSON.parse(runtimeSource) };
72
92
  }
73
- catch {
74
- throw new Error("State Flow session runtime contains invalid JSON");
93
+ catch (error) {
94
+ if (error instanceof SyntaxError)
95
+ throw new Error("State Flow session runtime contains invalid JSON");
96
+ throw error;
75
97
  }
76
98
  validateSessionRuntime(runtime, cwd, sessionId);
77
99
  const { temporal: _legacyTemporal, ...runtimeMeta } = runtime.meta;
78
- return { config: { enabled: runtime.config.enabled }, meta: runtimeMeta };
100
+ return { config: { mode: runtime.config.mode }, meta: runtimeMeta };
79
101
  }
80
102
  function restoredStep(value) {
81
103
  return typeof value === "number"
@@ -109,11 +131,11 @@ function restoredMeta(value, legacy = {}) {
109
131
  ...(meta.bootstrap === true ? { bootstrap: true } : {}),
110
132
  };
111
133
  }
112
- function envelope(enabled, meta) {
113
- return { config: { enabled }, meta };
134
+ function envelope(mode, meta) {
135
+ return { config: { mode }, meta };
114
136
  }
115
- export function emptySnapshot(enabled = false) {
116
- return envelope(enabled, { step: 0 });
137
+ export function emptySnapshot(mode = "passive") {
138
+ return envelope(mode, { step: 0 });
117
139
  }
118
140
  export function isFileRevision(value) {
119
141
  return typeof value === "string" && /^file:[0-9a-f]{64}$/.test(value);
@@ -124,23 +146,33 @@ export function retainedBoundaryCheckpoint(snapshot, boundary) {
124
146
  throw new Error("Checkpoint requires a retained temporal boundary identity");
125
147
  const checkpoint = {
126
148
  boundary,
127
- enabled: snapshot.config.enabled,
149
+ mode: snapshot.config.mode,
128
150
  step: snapshot.meta.step,
129
151
  ...(snapshot.meta.bootstrap === true ? { bootstrap: true } : {}),
130
152
  ...(snapshot.meta.specification === undefined ? {} : { specification: snapshot.meta.specification }),
131
153
  };
132
154
  return parseRetainedPiCheckpoint(checkpoint);
133
155
  }
134
- /** Decode the 0.17 retained-window checkpoint contract. */
135
- export function parseRetainedPiCheckpoint(value) {
156
+ /** Encode an explicit inactive choice on a branch that has no accepted runtime. */
157
+ export function preRuntimeCheckpoint(mode) {
158
+ if (mode === "active")
159
+ throw new Error("An active State Flow branch requires a retained semantic boundary");
160
+ return { mode };
161
+ }
162
+ /** Decode the retained-window checkpoint contract; legacy `enabled`/`{disabled:true}` markers map through `inactiveMode`. */
163
+ export function parseRetainedPiCheckpoint(value, inactiveMode = "passive") {
136
164
  if (!isObject(value))
137
165
  throw new Error("Invalid State Flow retained-boundary checkpoint");
138
- if (Object.keys(value).length === 1 && value.disabled === true)
139
- return { disabled: true };
140
- const allowed = new Set(["boundary", "enabled", "step", "bootstrap", "specification"]);
141
- if (Object.keys(value).some((key) => !allowed.has(key))
166
+ const keys = Object.keys(value);
167
+ if (keys.length === 1 && value.disabled === true)
168
+ return { mode: inactiveMode };
169
+ if (keys.length === 1 && (value.mode === "passive" || value.mode === "off"))
170
+ return { mode: value.mode };
171
+ const allowed = new Set(["boundary", "mode", "enabled", "step", "bootstrap", "specification"]);
172
+ const mode = readCheckpointMode(value, inactiveMode);
173
+ if (keys.some((key) => !allowed.has(key))
142
174
  || typeof value.boundary !== "string" || value.boundary.trim().length === 0
143
- || typeof value.enabled !== "boolean"
175
+ || mode === undefined
144
176
  || !Number.isSafeInteger(value.step) || value.step < 0 || value.step > MAX_RESTORED_STEP
145
177
  || (value.bootstrap !== undefined && value.bootstrap !== true)
146
178
  || (value.specification !== undefined && typeof value.specification !== "string")) {
@@ -148,18 +180,18 @@ export function parseRetainedPiCheckpoint(value) {
148
180
  }
149
181
  return {
150
182
  boundary: value.boundary,
151
- enabled: value.enabled,
183
+ mode,
152
184
  step: value.step,
153
185
  ...(value.bootstrap === true ? { bootstrap: true } : {}),
154
186
  ...(typeof value.specification === "string" ? { specification: value.specification } : {}),
155
187
  };
156
188
  }
157
- export function migrationFailure(data, error) {
189
+ export function migrationFailure(data, error, mode = "passive") {
158
190
  const meta = restoredMeta(data.meta, data);
159
191
  meta.validation = {
160
192
  attempt: 0,
161
193
  error,
162
194
  instruction: "Start a fresh State Flow episode; null is reserved for patch deletion.",
163
195
  };
164
- return envelope(false, meta);
196
+ return envelope(mode, meta);
165
197
  }
@@ -1,6 +1,6 @@
1
1
  import { type ArtifactCompilationUpdate, type ArtifactModelHints, type ArtifactRegistry } from "./artifact.ts";
2
2
  import { type JsonObject } from "./json.ts";
3
- /** The canonical semantic state shape shared by global, CWD, and session scopes. */
3
+ /** Runtime defaults for documented semantic planes; stored objects may omit them or retain other fields. */
4
4
  export type MaterializedState = JsonObject & {
5
5
  intents: JsonObject;
6
6
  contract: JsonObject;
@@ -9,6 +9,16 @@ export type MaterializedState = JsonObject & {
9
9
  response: string;
10
10
  lazy: JsonObject;
11
11
  };
12
+ /** Sparse semantic state: documented planes may be absent. Disk codecs select only known fields. */
13
+ export type SemanticState = JsonObject & Partial<{
14
+ intents: JsonObject;
15
+ contract: JsonObject;
16
+ working: JsonObject;
17
+ artifacts: ArtifactRegistry;
18
+ response: string;
19
+ lazy: JsonObject;
20
+ }>;
21
+ export type ScopedSemanticStates = Record<StateScope, SemanticState>;
12
22
  /** Compatibility name for callers that still treat materialized state as a document. */
13
23
  export type StateDocument = MaterializedState;
14
24
  /** Model patch shape; artifact entries may be compiler outputs before trusted metadata is attached. */
@@ -52,11 +62,14 @@ export interface ScopedStates {
52
62
  }
53
63
  export declare function emptyState(): MaterializedState;
54
64
  export declare function isMaterializedState(value: unknown): value is MaterializedState;
65
+ /** Missing planes are valid storage, not missing authority. Present values retain their type checks. */
66
+ export declare function isSemanticState(value: unknown): value is SemanticState;
55
67
  export declare const isStateDocument: typeof isMaterializedState;
56
68
  /** Atomically replace compiled and removed artifacts inside one materialized scope. */
57
69
  export declare function updateMaterializedArtifacts(state: MaterializedState, updates: readonly ArtifactCompilationUpdate[], removed?: readonly string[]): MaterializedState;
58
70
  /** Overlay lower-to-higher scopes without mutating any scope document. */
59
- export declare function overlayStates(...scopes: readonly MaterializedState[]): MaterializedState;
71
+ export declare function overlayStates(...scopes: readonly JsonObject[]): MaterializedState;
72
+ /** Default-bearing SDK compatibility view; model transport uses sparse SemanticState instead. */
60
73
  export type ModelState = JsonObject & {
61
74
  intents: JsonObject;
62
75
  contract: JsonObject;
@@ -64,5 +77,11 @@ export type ModelState = JsonObject & {
64
77
  artifacts: ArtifactRegistry;
65
78
  response: string;
66
79
  };
80
+ /** Select only owned top-level fields, preserving nested data and replay deletion markers. */
81
+ export declare function selectSemanticFields(value: JsonObject): JsonObject;
82
+ /** Read only documented, present planes; empty responses carry no semantic value. */
83
+ export declare function projectSemanticState(state: JsonObject): SemanticState;
84
+ /** Preserve deletion meaning in visible history without exposing ignored fields or empty responses. */
85
+ export declare function projectSemanticPatch(patch: JsonObject): JsonObject;
67
86
  /** Model-visible projection: lazy bodies and runtime artifact bookkeeping stay out of ordinary context. */
68
- export declare function projectStateForModel(state: MaterializedState, artifactHints?: ArtifactModelHints): ModelState;
87
+ export declare function projectStateForModel(state: SemanticState, artifactHints?: ArtifactModelHints): SemanticState;
@@ -10,8 +10,11 @@ export function isMaterializedState(value) {
10
10
  && isObject(value.working)
11
11
  && isObject(value.intents)
12
12
  && typeof value.response === "string"
13
- && isObject(value.lazy)
14
- && Object.keys(value).every((key) => key === "artifacts" || key === "contract" || key === "working" || key === "intents" || key === "response" || key === "lazy");
13
+ && isObject(value.lazy);
14
+ }
15
+ /** Missing planes are valid storage, not missing authority. Present values retain their type checks. */
16
+ export function isSemanticState(value) {
17
+ return isObject(value) && isMaterializedState({ ...emptyState(), ...value });
15
18
  }
16
19
  export const isStateDocument = isMaterializedState;
17
20
  /** Atomically replace compiled and removed artifacts inside one materialized scope. */
@@ -27,14 +30,31 @@ export function overlayStates(...scopes) {
27
30
  return applyPatch(effective, scope);
28
31
  }, emptyState());
29
32
  }
33
+ /** Select only owned top-level fields, preserving nested data and replay deletion markers. */
34
+ export function selectSemanticFields(value) {
35
+ return structuredClone(Object.fromEntries(Object.keys(emptyState())
36
+ .filter((key) => Object.hasOwn(value, key))
37
+ .map((key) => [key, value[key]])));
38
+ }
39
+ /** Read only documented, present planes; empty responses carry no semantic value. */
40
+ export function projectSemanticState(state) {
41
+ const projected = selectSemanticFields(state);
42
+ if (projected.response === "")
43
+ delete projected.response;
44
+ return projected;
45
+ }
46
+ /** Preserve deletion meaning in visible history without exposing ignored fields or empty responses. */
47
+ export function projectSemanticPatch(patch) {
48
+ const projected = selectSemanticFields(patch);
49
+ if (projected.response === "")
50
+ projected.response = null;
51
+ return projected;
52
+ }
30
53
  /** Model-visible projection: lazy bodies and runtime artifact bookkeeping stay out of ordinary context. */
31
54
  export function projectStateForModel(state, artifactHints = {}) {
32
- const cloned = structuredClone(state);
33
- return {
34
- intents: cloned.intents,
35
- contract: cloned.contract,
36
- working: cloned.working,
37
- artifacts: projectArtifactsForModel(cloned.artifacts, artifactHints),
38
- response: cloned.response,
39
- };
55
+ const { lazy: _hidden, ...visible } = state;
56
+ const projected = projectSemanticState(visible);
57
+ if (projected.artifacts)
58
+ projected.artifacts = projectArtifactsForModel(projected.artifacts, artifactHints);
59
+ return projected;
40
60
  }
@@ -1,7 +1,7 @@
1
1
  import type { ArtifactInvalidationReason } from "./artifact.ts";
2
- import { type RecentTransitionWindow } from "./history.ts";
2
+ import type { RecentTransitionWindow } from "./history.ts";
3
3
  import type { Snapshot } from "./snapshot.ts";
4
- import { type ScopedStates, type StateScope } from "./state.ts";
4
+ import { type SemanticState, type ScopedStates, type StateScope } from "./state.ts";
5
5
  import type { ScopeRevisions, TransitionBoundary } from "./temporal.ts";
6
6
  export declare const STATUS_KEY = "state-flow";
7
7
  export type Colorize = (color: "accent" | "dim", text: string) => string;
@@ -16,6 +16,8 @@ export interface StatusDiagnostics {
16
16
  cwdScopeKey: string;
17
17
  sessionScopeKey: string;
18
18
  scopeStates: ScopedStates;
19
+ /** Overlay raw scopes before defaults so absent scalar planes cannot mask lower scopes. */
20
+ effectiveState?: SemanticState;
19
21
  recent: RecentTransitionWindow;
20
22
  historyLimit: number;
21
23
  temporal?: {
@@ -29,5 +31,5 @@ export interface StatusDiagnostics {
29
31
  publicationError?: string;
30
32
  }
31
33
  export declare function formatScopeRevisionVector(revisions: ScopeRevisions): string;
32
- export declare function compactStatus(snapshot: Snapshot, revisions: ScopeRevisions, colorize: Colorize): string | undefined;
34
+ export declare function compactStatus(snapshot: Snapshot, _revisions: ScopeRevisions, colorize: Colorize): string | undefined;
33
35
  export declare function detailedStatus(snapshot: Snapshot, diagnostics: StatusDiagnostics): string;
@@ -1,27 +1,26 @@
1
- import { projectRecentTransitionsWithLimit } from "./history.js";
2
- import { retainedMemoryScopes } from "./memory.js";
3
1
  import { conciseDiagnostic } from "./protocol.js";
4
- import { overlayStates } from "./state.js";
2
+ import { overlayStates, projectSemanticState } from "./state.js";
5
3
  export const STATUS_KEY = "state-flow";
6
4
  export function formatScopeRevisionVector(revisions) {
7
- return `G${revisions.global}/C${revisions.cwd}/S${revisions.session}`;
5
+ return `g${revisions.global}c${revisions.cwd}s${revisions.session}`;
8
6
  }
9
- export function compactStatus(snapshot, revisions, colorize) {
10
- if (!snapshot.config.enabled)
7
+ export function compactStatus(snapshot, _revisions, colorize) {
8
+ const { mode } = snapshot.config;
9
+ if (mode === "off")
11
10
  return undefined;
12
- return `${colorize("accent", "state-flow")} ${colorize("dim", formatScopeRevisionVector(revisions))}`;
11
+ return `${colorize("accent", "state-flow")} ${colorize("dim", mode)}`;
13
12
  }
14
13
  function countArtifacts(states, scope) {
15
14
  return Object.keys(states[scope].artifacts).length;
16
15
  }
17
16
  export function detailedStatus(snapshot, diagnostics) {
18
- const projectedRecent = projectRecentTransitionsWithLimit(diagnostics.historyLimit, diagnostics.recent);
19
17
  const available = diagnostics.temporal !== undefined && diagnostics.durableStateError === undefined;
20
- const materialized = !available ? undefined : overlayStates(diagnostics.scopeStates.global, diagnostics.scopeStates.cwd, diagnostics.scopeStates.session);
21
- const stateJson = materialized === undefined ? undefined : JSON.stringify(materialized, null, 2);
18
+ const materialized = !available ? undefined : diagnostics.effectiveState ?? projectSemanticState(overlayStates(diagnostics.scopeStates.global, diagnostics.scopeStates.cwd, diagnostics.scopeStates.session));
19
+ // Only top-level plane boundaries gain whitespace; nested user JSON stays unchanged.
20
+ const stateJson = materialized === undefined ? undefined : JSON.stringify(materialized, null, 2).replace(/,\n(?= ")/g, ",\n\n");
22
21
  const invalidated = available ? String(diagnostics.staleArtifacts.length) : "unavailable";
23
22
  const invalidationLines = diagnostics.staleArtifacts.length === 0
24
- ? ["Pending artifact invalidations: none"]
23
+ ? []
25
24
  : [
26
25
  "Pending artifact invalidations:",
27
26
  ...diagnostics.staleArtifacts.map(({ scope, path, reason }) => `- [${scope}] ${path} — ${reason}`),
@@ -29,29 +28,21 @@ export function detailedStatus(snapshot, diagnostics) {
29
28
  const temporal = available ? diagnostics.temporal : undefined;
30
29
  const temporalLines = temporal === undefined
31
30
  ? [`Temporal materialization unavailable: ${conciseDiagnostic(diagnostics.durableStateError ?? "no selected branch runtime")}`,
32
- `Hot history: unavailable; configured maximum depth ${diagnostics.historyLimit}`,
33
- "Retained patch tails: unavailable"]
34
- : [`Temporal head: ${JSON.stringify(temporal.head.id)}; branch-local position ${temporal.head.position}`,
35
- `Scope revisions: global #${temporal.revisions.global}; CWD #${temporal.revisions.cwd}; session #${temporal.revisions.session}; effective ${formatScopeRevisionVector(temporal.revisions)}`,
36
- `Hot history: offsets 0..${temporal.historyDepth}; maximum depth ${diagnostics.historyLimit}`,
37
- `Retained patch tails: global ${temporal.tailCounts.global}; CWD ${temporal.tailCounts.cwd}; session ${temporal.tailCounts.session}`];
38
- const artifacts = (scope) => available ? countArtifacts(diagnostics.scopeStates, scope) : "unknown";
39
- const memoryScopes = available ? retainedMemoryScopes(diagnostics.scopeStates) : undefined;
31
+ `Hot history: unavailable; configured maximum depth ${diagnostics.historyLimit}`]
32
+ : [`Scope revisions: ${formatScopeRevisionVector(temporal.revisions)}`,
33
+ `Runtime metadata: step #${snapshot.meta.step}${snapshot.meta.bootstrap ? "; bootstrap" : ""}`,
34
+ `Hot history: offsets 0..${temporal.historyDepth}; maximum depth ${diagnostics.historyLimit}`];
35
+ const artifacts = (scope) => countArtifacts(diagnostics.scopeStates, scope);
36
+ const hasArtifacts = available && ["global", "cwd", "session"].some((scope) => artifacts(scope) > 0);
40
37
  return [
41
- `State Flow diagnostics — config.enabled=${snapshot.config.enabled}; branch mode=${snapshot.config.enabled ? "active" : "inactive"}`,
42
38
  `Repository: ${diagnostics.repositoryRoot}`,
43
39
  `Scope keys: CWD ${diagnostics.cwdScopeKey}; session ${diagnostics.sessionScopeKey}`,
44
- "Session files: config.json owns behavior; runtime.json owns branch recovery; meta.json owns scope provenance",
45
- `Runtime metadata: step #${snapshot.meta.step}; bootstrap ${snapshot.meta.bootstrap === true}`,
46
- "Memory: owner state-flow; global retention enabled; global fallback active",
47
- `Memory-bearing scopes: global ${memoryScopes?.global ?? "unknown"}; CWD ${memoryScopes?.cwd ?? "unknown"}; session ${memoryScopes?.session ?? "unknown"}`,
48
40
  ...temporalLines,
49
- ...(diagnostics.publicationError === undefined ? [] : [`Memory writes paused after Stop: ${conciseDiagnostic(diagnostics.publicationError)}`]),
50
- `Artifacts: global ${artifacts("global")}; CWD ${artifacts("cwd")}; session ${artifacts("session")}; pending invalidations ${invalidated}`,
51
- available ? `Recent transitions: global ${diagnostics.recent.filter(({ transitions }) => transitions.some(({ scope }) => scope === "global")).length}; CWD ${diagnostics.recent.filter(({ transitions }) => transitions.some(({ scope }) => scope === "cwd")).length}; session ${diagnostics.recent.filter(({ transitions }) => transitions.some(({ scope }) => scope === "session")).length}; active ${projectedRecent.length}` : "Recent transitions: unavailable",
41
+ ...(diagnostics.publicationError === undefined ? [] : [`Memory writes paused after mode change: ${conciseDiagnostic(diagnostics.publicationError)}`]),
42
+ ...(hasArtifacts ? [`Artifacts: global ${artifacts("global")}; CWD ${artifacts("cwd")}; session ${artifacts("session")}; pending invalidations ${invalidated}`] : []),
52
43
  ...invalidationLines,
53
44
  ...(stateJson === undefined
54
45
  ? ["Effective memory: unavailable"]
55
- : [`Effective memory (${Buffer.byteLength(stateJson, "utf8")} JSON bytes; global → CWD → session overlay):`, "", stateJson]),
46
+ : ["Effective memory:", "", stateJson]),
56
47
  ].join("\n");
57
48
  }
@@ -1,9 +1,11 @@
1
+ import type { StateFlowMode } from "./snapshot.ts";
1
2
  import type { ScopeRevisions } from "./temporal.ts";
2
3
  export declare const STATE_FLOW_TELEGRAM_ID = "@llblab/pi-state-flow";
3
4
  /** Resolve the package export or the compiled sibling-extension layout used in local development. */
4
5
  export declare function stateFlowTelegramSectionSpecifiers(moduleUrl?: string): string[];
5
6
  export interface StateFlowTelegramSnapshot {
6
- enabled: boolean;
7
+ /** The current session's selected mode. */
8
+ mode: StateFlowMode;
7
9
  /** Legacy branch step retained for existing adapter ports; current runtime ports also supply owner revisions. */
8
10
  step: number;
9
11
  revisions?: ScopeRevisions;
@@ -12,11 +14,11 @@ export interface StateFlowTelegramSnapshot {
12
14
  }
13
15
  export type StateFlowTelegramScope = "global" | "cwd" | "session" | "effective";
14
16
  export interface StateFlowTelegramState {
15
- artifacts: Record<string, unknown>;
16
- contract: Record<string, unknown>;
17
- working: Record<string, unknown>;
18
- intents: Record<string, unknown>;
19
- response: string;
17
+ artifacts?: Record<string, unknown>;
18
+ contract?: Record<string, unknown>;
19
+ working?: Record<string, unknown>;
20
+ intents?: Record<string, unknown>;
21
+ response?: string;
20
22
  lazy?: unknown;
21
23
  }
22
24
  export type StateFlowTelegramRichText = string | StateFlowTelegramRichText[] | {
@@ -92,26 +94,26 @@ export interface StateFlowTelegramPort {
92
94
  state(scope: StateFlowTelegramScope): StateFlowTelegramState;
93
95
  /** Optional additive capability; absent legacy ports retain their branch-step presentation. */
94
96
  revisions?(): ScopeRevisions;
97
+ /** Active may need a settled native boundary; inactive modes apply immediately. */
95
98
  canStartNow(): boolean;
96
- start(): StateFlowTelegramControlResult;
97
- stop(): StateFlowTelegramControlResult;
99
+ /** Select the current session's mode through the same lifecycle owners as the terminal commands. */
100
+ select(mode: StateFlowMode): StateFlowTelegramControlResult;
98
101
  deferStart(): void;
99
102
  cancelStart(): void;
100
103
  }
101
- export interface StateFlowTelegramInspectionPort extends Omit<StateFlowTelegramPort, "state" | "revisions" | "start" | "stop"> {
104
+ export interface StateFlowTelegramInspectionPort extends Omit<StateFlowTelegramPort, "state" | "revisions" | "select"> {
102
105
  inspect(scope: StateFlowTelegramScope): StateFlowTelegramInspection | Promise<StateFlowTelegramInspection>;
103
- start(): StateFlowTelegramControlResult | Promise<StateFlowTelegramControlResult>;
104
- stop(): StateFlowTelegramControlResult | Promise<StateFlowTelegramControlResult>;
106
+ select(mode: StateFlowMode): StateFlowTelegramControlResult | Promise<StateFlowTelegramControlResult>;
105
107
  }
106
108
  export interface StateFlowTelegramAdapter {
107
109
  ensure(): Promise<boolean>;
108
110
  dispose(): void;
109
111
  }
110
- /** Main-menu section label doubles as the live status value: the spiral identity is constant, the value is not. */
112
+ /** Main-menu section label shows only the current session mode. */
111
113
  export declare function formatStateFlowSectionLabel(snapshot: StateFlowTelegramSnapshot): string;
112
- /** The submenu header repeats the button's state line; the single action matches the current state. */
113
- export declare function buildStateFlowSectionView(snapshot: StateFlowTelegramSnapshot, callbackData: (action: string) => string): StateFlowTelegramView;
114
- export declare function buildStateFlowScopeChooser(callbackData: (action: string, payload?: string) => string): StateFlowTelegramView;
114
+ export declare const STATE_FLOW_MODES: readonly ["off", "passive", "active"];
115
+ /** One radio-style mode row followed directly by read-only scope actions. */
116
+ export declare function buildStateFlowSectionView(snapshot: StateFlowTelegramSnapshot, callbackData: (action: string, payload?: string) => string): StateFlowTelegramView;
115
117
  export declare function renderStateFlowRichState(scope: StateFlowTelegramScope, revisions: ScopeRevisions, state: StateFlowTelegramState): StateFlowTelegramRichMessage;
116
118
  /** Default loader; injectable so tests and embedded hosts can control transport presence. */
117
119
  export declare function loadStateFlowTelegramModules(): Promise<StateFlowTelegramModules>;