@llblab/pi-kit 0.15.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +2 -2
  3. package/node_modules/@llblab/pi-state-flow/AGENTS.md +12 -12
  4. package/node_modules/@llblab/pi-state-flow/BACKLOG.md +2 -13
  5. package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +31 -0
  6. package/node_modules/@llblab/pi-state-flow/README.md +11 -7
  7. package/node_modules/@llblab/pi-state-flow/dist/lib/config.d.ts +5 -2
  8. package/node_modules/@llblab/pi-state-flow/dist/lib/config.js +17 -16
  9. package/node_modules/@llblab/pi-state-flow/dist/lib/context.d.ts +8 -0
  10. package/node_modules/@llblab/pi-state-flow/dist/lib/context.js +24 -0
  11. package/node_modules/@llblab/pi-state-flow/dist/lib/continuation.js +4 -3
  12. package/node_modules/@llblab/pi-state-flow/dist/lib/durable.d.ts +2 -1
  13. package/node_modules/@llblab/pi-state-flow/dist/lib/durable.js +15 -3
  14. package/node_modules/@llblab/pi-state-flow/dist/lib/episode.d.ts +2 -0
  15. package/node_modules/@llblab/pi-state-flow/dist/lib/episode.js +4 -0
  16. package/node_modules/@llblab/pi-state-flow/dist/lib/extension.d.ts +7 -4
  17. package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +124 -274
  18. package/node_modules/@llblab/pi-state-flow/dist/lib/git.js +20 -23
  19. package/node_modules/@llblab/pi-state-flow/dist/lib/history.js +8 -4
  20. package/node_modules/@llblab/pi-state-flow/dist/lib/json.js +29 -3
  21. package/node_modules/@llblab/pi-state-flow/dist/lib/logging.d.ts +18 -0
  22. package/node_modules/@llblab/pi-state-flow/dist/lib/logging.js +44 -1
  23. package/node_modules/@llblab/pi-state-flow/dist/lib/migration.d.ts +2 -2
  24. package/node_modules/@llblab/pi-state-flow/dist/lib/migration.js +55 -21
  25. package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.d.ts +7 -0
  26. package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.js +65 -17
  27. package/node_modules/@llblab/pi-state-flow/dist/lib/publication.d.ts +17 -0
  28. package/node_modules/@llblab/pi-state-flow/dist/lib/publication.js +102 -0
  29. package/node_modules/@llblab/pi-state-flow/dist/lib/query.d.ts +43 -1
  30. package/node_modules/@llblab/pi-state-flow/dist/lib/query.js +268 -10
  31. package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.d.ts +2 -0
  32. package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.js +34 -5
  33. package/node_modules/@llblab/pi-state-flow/dist/lib/session.d.ts +17 -0
  34. package/node_modules/@llblab/pi-state-flow/dist/lib/session.js +38 -0
  35. package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.d.ts +5 -5
  36. package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.js +20 -24
  37. package/node_modules/@llblab/pi-state-flow/dist/lib/state.d.ts +11 -3
  38. package/node_modules/@llblab/pi-state-flow/dist/lib/state.js +14 -4
  39. package/node_modules/@llblab/pi-state-flow/dist/lib/status.js +1 -1
  40. package/node_modules/@llblab/pi-state-flow/dist/lib/storage.d.ts +2 -0
  41. package/node_modules/@llblab/pi-state-flow/dist/lib/storage.js +55 -20
  42. package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.d.ts +9 -6
  43. package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.js +10 -3
  44. package/node_modules/@llblab/pi-state-flow/dist/lib/transition.js +8 -3
  45. package/node_modules/@llblab/pi-state-flow/dist/package.json +1 -1
  46. package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-guide/SKILL.md +79 -0
  47. package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-memory/SKILL.md +23 -121
  48. package/node_modules/@llblab/pi-state-flow/docs/README.md +1 -0
  49. package/node_modules/@llblab/pi-state-flow/docs/architecture.md +27 -20
  50. package/node_modules/@llblab/pi-state-flow/docs/compatibility.md +1 -1
  51. package/node_modules/@llblab/pi-state-flow/docs/filesystem-recovery.md +3 -2
  52. package/node_modules/@llblab/pi-state-flow/docs/lazy-state.md +513 -0
  53. package/node_modules/@llblab/pi-state-flow/docs/usage.md +13 -10
  54. package/node_modules/@llblab/pi-state-flow/lib/config.ts +18 -15
  55. package/node_modules/@llblab/pi-state-flow/lib/context.ts +24 -0
  56. package/node_modules/@llblab/pi-state-flow/lib/continuation.ts +4 -3
  57. package/node_modules/@llblab/pi-state-flow/lib/durable.ts +14 -5
  58. package/node_modules/@llblab/pi-state-flow/lib/episode.ts +5 -0
  59. package/node_modules/@llblab/pi-state-flow/lib/extension.ts +119 -262
  60. package/node_modules/@llblab/pi-state-flow/lib/git.ts +23 -22
  61. package/node_modules/@llblab/pi-state-flow/lib/history.ts +8 -4
  62. package/node_modules/@llblab/pi-state-flow/lib/json.ts +31 -3
  63. package/node_modules/@llblab/pi-state-flow/lib/logging.ts +53 -1
  64. package/node_modules/@llblab/pi-state-flow/lib/migration.ts +51 -20
  65. package/node_modules/@llblab/pi-state-flow/lib/protocol.ts +61 -17
  66. package/node_modules/@llblab/pi-state-flow/lib/publication.ts +96 -0
  67. package/node_modules/@llblab/pi-state-flow/lib/query.ts +261 -9
  68. package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +32 -4
  69. package/node_modules/@llblab/pi-state-flow/lib/session.ts +45 -0
  70. package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +22 -25
  71. package/node_modules/@llblab/pi-state-flow/lib/state.ts +22 -6
  72. package/node_modules/@llblab/pi-state-flow/lib/status.ts +1 -1
  73. package/node_modules/@llblab/pi-state-flow/lib/storage.ts +46 -18
  74. package/node_modules/@llblab/pi-state-flow/lib/telegram.ts +20 -6
  75. package/node_modules/@llblab/pi-state-flow/lib/transition.ts +8 -3
  76. package/node_modules/@llblab/pi-state-flow/package.json +1 -1
  77. package/node_modules/@llblab/pi-state-flow/skills/state-flow-guide/SKILL.md +79 -0
  78. package/node_modules/@llblab/pi-state-flow/skills/state-flow-memory/SKILL.md +23 -121
  79. package/node_modules/@llblab/pi-telegram/AGENTS.md +1 -1
  80. package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -0
  81. package/node_modules/@llblab/pi-telegram/CHANGELOG.md +7 -0
  82. package/node_modules/@llblab/pi-telegram/README.md +1 -0
  83. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +2 -1
  84. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +2 -1
  85. package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +134 -2
  86. package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +297 -16
  87. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.d.ts +2 -0
  88. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.js +11 -0
  89. package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +68 -8
  90. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.d.ts +4 -3
  91. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.js +17 -13
  92. package/node_modules/@llblab/pi-telegram/dist/lib/pi.d.ts +2 -1
  93. package/node_modules/@llblab/pi-telegram/dist/lib/pi.js +1 -0
  94. package/node_modules/@llblab/pi-telegram/dist/lib/routing.d.ts +1 -0
  95. package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +55 -5
  96. package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.d.ts +23 -0
  97. package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.js +20 -0
  98. package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +18 -0
  99. package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +152 -6
  100. package/node_modules/@llblab/pi-telegram/dist/lib/updates.d.ts +1 -0
  101. package/node_modules/@llblab/pi-telegram/dist/lib/updates.js +5 -3
  102. package/node_modules/@llblab/pi-telegram/dist/package.json +1 -1
  103. package/node_modules/@llblab/pi-telegram/docs/architecture.md +3 -3
  104. package/node_modules/@llblab/pi-telegram/docs/callback-namespaces.md +1 -1
  105. package/node_modules/@llblab/pi-telegram/docs/public-api.md +4 -3
  106. package/node_modules/@llblab/pi-telegram/docs/sections.md +2 -2
  107. package/node_modules/@llblab/pi-telegram/docs/ui-style.md +2 -1
  108. package/node_modules/@llblab/pi-telegram/docs/updates.md +1 -1
  109. package/node_modules/@llblab/pi-telegram/lib/bindings.ts +3 -0
  110. package/node_modules/@llblab/pi-telegram/lib/commands.ts +466 -21
  111. package/node_modules/@llblab/pi-telegram/lib/delivery.ts +15 -0
  112. package/node_modules/@llblab/pi-telegram/lib/extension.ts +77 -9
  113. package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +25 -10
  114. package/node_modules/@llblab/pi-telegram/lib/pi.ts +3 -0
  115. package/node_modules/@llblab/pi-telegram/lib/routing.ts +84 -17
  116. package/node_modules/@llblab/pi-telegram/lib/thread-display.ts +30 -0
  117. package/node_modules/@llblab/pi-telegram/lib/threads.ts +199 -6
  118. package/node_modules/@llblab/pi-telegram/lib/updates.ts +5 -2
  119. package/node_modules/@llblab/pi-telegram/package.json +1 -1
  120. package/package.json +3 -3
@@ -1,14 +1,16 @@
1
- // Domain: agent-level State Flow configuration, independent of session runtime and semantic state.
1
+ // Domain: repository-global State Flow configuration, independent of session runtime and semantic state.
2
2
  import { lstatSync, readFileSync } from "node:fs";
3
- import { homedir } from "node:os";
4
- import { dirname, resolve } from "node:path";
3
+ import { join } from "node:path";
5
4
  import { getAgentDir } from "@earendil-works/pi-coding-agent";
6
5
  import { getDurableRepositoryRoot } from "./durable.ts";
7
6
  import { isObject } from "./json.ts";
8
7
 
9
8
  export interface StateFlowConfig {
9
+ /** Canonical State Flow repository. SDK callers may still override it explicitly. */
10
10
  directory: string;
11
11
  autoStart: boolean;
12
+ passiveBootstrap: boolean;
13
+ passiveTools: boolean;
12
14
  /** Opt-in local capture of rejected patch attempts and unresolved terminal drafts. */
13
15
  logging: boolean;
14
16
  /** Show successful patch_state arguments in the interactive tool row. */
@@ -16,12 +18,15 @@ export interface StateFlowConfig {
16
18
  remotePublication?: "off" | "turn-end" | "transition";
17
19
  }
18
20
 
19
- /** Read once at extension load/reload. Missing config uses defaults; invalid config never falls back. */
20
- export function loadStateFlowConfig(agentDir = getAgentDir()): StateFlowConfig {
21
- const path = resolve(agentDir, "state-flow.json");
21
+ /** Read the repository-global config once at extension load/reload. Missing config uses defaults; invalid config never falls back. */
22
+ export function loadStateFlowConfig(agentDir = getAgentDir(), repositoryRoot = getDurableRepositoryRoot(agentDir)): StateFlowConfig {
23
+ const directory = repositoryRoot;
24
+ const path = join(directory, "config.json");
22
25
  const defaults: StateFlowConfig = {
23
- directory: getDurableRepositoryRoot(agentDir),
26
+ directory,
24
27
  autoStart: false,
28
+ passiveBootstrap: true,
29
+ passiveTools: true,
25
30
  logging: false,
26
31
  showSuccessfulPatches: true,
27
32
  };
@@ -32,25 +37,23 @@ export function loadStateFlowConfig(agentDir = getAgentDir()): StateFlowConfig {
32
37
  } catch (error) {
33
38
  throw new Error(`Cannot read State Flow configuration: ${path}`, { cause: error });
34
39
  }
35
- const allowed = new Set(["directory", "autoStart", "logging", "showSuccessfulPatches", "remotePublication"]);
40
+ const allowed = new Set(["autoStart", "passiveBootstrap", "passiveTools", "logging", "showSuccessfulPatches", "remotePublication"]);
36
41
  if (!isObject(value) || Object.keys(value).some((key) => !allowed.has(key))) {
37
42
  throw new Error(`State Flow configuration contains unknown settings: ${path}`);
38
43
  }
39
44
  if (Object.hasOwn(value, "autoStart") && typeof value.autoStart !== "boolean") throw new Error(`State Flow autoStart must be a boolean: ${path}`);
45
+ if (Object.hasOwn(value, "passiveBootstrap") && typeof value.passiveBootstrap !== "boolean") throw new Error(`State Flow passiveBootstrap must be a boolean: ${path}`);
46
+ if (Object.hasOwn(value, "passiveTools") && typeof value.passiveTools !== "boolean") throw new Error(`State Flow passiveTools must be a boolean: ${path}`);
40
47
  if (Object.hasOwn(value, "logging") && typeof value.logging !== "boolean") throw new Error(`State Flow logging must be a boolean: ${path}`);
41
48
  if (Object.hasOwn(value, "showSuccessfulPatches") && typeof value.showSuccessfulPatches !== "boolean") throw new Error(`State Flow showSuccessfulPatches must be a boolean: ${path}`);
42
49
  if (Object.hasOwn(value, "remotePublication") && value.remotePublication !== "off" && value.remotePublication !== "turn-end" && value.remotePublication !== "transition") {
43
50
  throw new Error(`State Flow remotePublication must be off, turn-end, or transition: ${path}`);
44
51
  }
45
- if (Object.hasOwn(value, "directory") && (typeof value.directory !== "string" || !value.directory.trim() || value.directory.includes("\0"))) {
46
- throw new Error(`State Flow directory must be a non-empty path: ${path}`);
47
- }
48
- const directory = value.directory as string | undefined;
49
- if (directory?.startsWith("~") && directory !== "~" && !directory.startsWith("~/")) throw new Error(`State Flow directory supports ~ or ~/ paths, not named-user expansion: ${path}`);
50
- const expanded = directory === "~" ? homedir() : directory?.startsWith("~/") ? resolve(homedir(), directory.slice(2)) : directory;
51
52
  return {
52
- directory: expanded === undefined ? defaults.directory : resolve(dirname(path), expanded),
53
+ directory,
53
54
  autoStart: value.autoStart === true,
55
+ passiveBootstrap: value.passiveBootstrap !== false,
56
+ passiveTools: value.passiveTools !== false,
54
57
  logging: value.logging === true,
55
58
  showSuccessfulPatches: value.showSuccessfulPatches !== false,
56
59
  ...(value.remotePublication === undefined ? {} : { remotePublication: value.remotePublication as "off" | "turn-end" | "transition" }),
@@ -7,6 +7,29 @@ import type { RehydrationPhase } from "./rehydration.ts";
7
7
  import { projectStateForModel, type MaterializedState } from "./state.ts";
8
8
 
9
9
  export const VALIDATION_MESSAGE_TYPE = "state-flow-validation";
10
+ const LAZY_HINT_PATH = "effective.lazy";
11
+ const LAZY_HINT_MAX_KEYS = 32;
12
+ const LAZY_HINT_MAX_JSON_CHARS = 1024;
13
+
14
+ type LazyValueKind = "array" | "boolean" | "null" | "number" | "object" | "string";
15
+
16
+ function lazyValueKind(value: Exclude<MaterializedState["lazy"], undefined>): LazyValueKind {
17
+ if (value === null) return "null";
18
+ if (Array.isArray(value)) return "array";
19
+ if (typeof value === "object") return "object";
20
+ return typeof value as Exclude<LazyValueKind, "array" | "object">;
21
+ }
22
+
23
+ /** Fixed-budget navigation only: never place lazy bodies or partial key catalogs in baseline context. */
24
+ export function lazyNavigationHint(state: MaterializedState): { available: boolean; path: string; keys?: Record<string, LazyValueKind> } {
25
+ const base = { available: Object.hasOwn(state, "lazy"), path: LAZY_HINT_PATH };
26
+ if (!base.available || typeof state.lazy !== "object" || state.lazy === null || Array.isArray(state.lazy)) return base;
27
+ const entries = Object.entries(state.lazy);
28
+ if (entries.length > LAZY_HINT_MAX_KEYS) return base;
29
+ const keys = Object.fromEntries(entries.map(([key, value]) => [key, lazyValueKind(value)]));
30
+ return JSON.stringify(keys).length <= LAZY_HINT_MAX_JSON_CHARS ? { ...base, keys } : base;
31
+ }
32
+
10
33
 
11
34
  /** Bounded context retained after semantic State Flow is stopped in this physical session. */
12
35
  export interface PassiveContinuation {
@@ -85,6 +108,7 @@ export function runtimeContextMessage(
85
108
  const context = {
86
109
  specification: snapshot.meta.specification,
87
110
  state: projectStateForModel(state),
111
+ lazy_navigation: lazyNavigationHint(state),
88
112
  ...(rehydrationPhase === undefined ? {} : { knowledge_rehydration: { phase: rehydrationPhase } }),
89
113
  ...(artifactInvalidations.length === 0 ? {} : { artifact_invalidations: artifactInvalidations.map(({ path, reason }) => ({ path, reason })) }),
90
114
  ...(recentTransitions.length === 0 ? {} : { recent_transitions: projectRecentForModel(recentTransitions) }),
@@ -223,11 +223,12 @@ export function inspectStateFlowContinuationProvenance(
223
223
  const paths = sessionRuntimePaths(header.cwd, header.id, repositoryRoot, sessionKey);
224
224
  try {
225
225
  const config = readRegular(paths.config);
226
+ const runtimeSource = readRegular(paths.runtime);
226
227
  const meta = readRegular(paths.meta);
227
- if (config === undefined && meta === undefined) {
228
+ if (config === undefined && runtimeSource === undefined && meta === undefined) {
228
229
  return { stateFlow: { enabled: false, restorable: true }, reason: "no State Flow session runtime" };
229
230
  }
230
- const runtime = parseSessionRuntime(config, meta, header.cwd, header.id);
231
+ const runtime = parseSessionRuntime(config, runtimeSource, header.cwd, header.id, meta);
231
232
  if (!runtime) return { stateFlow: { enabled: false, restorable: true }, reason: "no State Flow session runtime" };
232
233
  if (!runtime.config.enabled) return { stateFlow: { enabled: false, restorable: true }, reason: "State Flow stopped on selected runtime" };
233
234
  if (runtime.meta.publication === "files") {
@@ -238,7 +239,7 @@ export function inspectStateFlowContinuationProvenance(
238
239
  validateTemporalState({ lineage: runtime.meta.lineage, scopes: { global, cwd, session } });
239
240
  return { stateFlow: { enabled: true, restorable: true }, reason: "exact file-only runtime cohort is restorable" };
240
241
  }
241
- const runtimePath = relative(resolve(repositoryRoot), paths.meta);
242
+ const runtimePath = relative(resolve(repositoryRoot), runtimeSource === undefined ? paths.meta : paths.runtime);
242
243
  if (runtimePath.startsWith("..")) throw new Error("runtime provenance escapes repository");
243
244
  const owner = execFileSync("git", ["-C", repositoryRoot, "log", "-1", "--format=%H", "--", runtimePath], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
244
245
  if (!owner) throw new Error("runtime owner commit is unavailable");
@@ -20,13 +20,14 @@ import {
20
20
  } from "./artifact.ts";
21
21
  import { canonicalJson, isJsonValue, isObject } from "./json.ts";
22
22
  import { validateScopeStream, validateTemporalState, type ScopeStream, type TemporalState } from "./temporal.ts";
23
- import type { StateScope } from "./state.ts";
23
+ import { migratePreIntentState, type StateScope } from "./state.ts";
24
24
 
25
25
  const SESSION_KEY_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/;
26
26
  const STATE_FILE = "state.json";
27
27
  const CHECKPOINT_FILE = "checkpoint.json";
28
28
  const PATCHES_FILE = "patches.jsonl";
29
29
  const META_FILE = "meta.json";
30
+ const RUNTIME_FILE = "runtime.json";
30
31
 
31
32
  /** Canonical semantic sources plus runtime-owned temporal metadata. */
32
33
  export interface ScopeStreamSources {
@@ -99,6 +100,7 @@ export function classifyScopeStream(
99
100
  }
100
101
  const temporal = meta?.temporal;
101
102
  if (isObject(temporal) && Object.hasOwn(temporal, "checkpoint") && Array.isArray(temporal.patches)) {
103
+ checkpoint = migratePreIntentState(checkpoint) ?? checkpoint;
102
104
  const owner = meta?.owner;
103
105
  if (scope === "cwd" && expectedCwd !== undefined
104
106
  && (!isObject(owner) || Object.keys(owner).join(",") !== "cwd" || owner.cwd !== resolve(expectedCwd))) {
@@ -125,6 +127,10 @@ export function classifyScopeStream(
125
127
  } else if (scope === "cwd" && expectedCwd !== undefined) {
126
128
  throw new Error("State Flow CWD scope identity is missing");
127
129
  }
130
+ if (isObject(legacyCheckpoint) && isObject(legacyCheckpoint.state)) {
131
+ const migrated = migratePreIntentState(legacyCheckpoint.state);
132
+ if (migrated) legacyCheckpoint = { ...legacyCheckpoint, state: migrated };
133
+ }
128
134
  const stream = { checkpoint: legacyCheckpoint, patches };
129
135
  validateScopeStream(stream, scope);
130
136
  return { kind: "present", stream };
@@ -159,9 +165,9 @@ export function temporalScopePaths(cwd: string, sessionId: string, scope: StateS
159
165
  return { directory, checkpoint: join(directory, CHECKPOINT_FILE), patches: join(directory, PATCHES_FILE), meta: join(directory, META_FILE) };
160
166
  }
161
167
 
162
- export function sessionRuntimePaths(cwd: string, sessionId: string, repositoryRoot: string, sessionKey = sessionId): { config: string; meta: string } {
168
+ export function sessionRuntimePaths(cwd: string, sessionId: string, repositoryRoot: string, sessionKey = sessionId): { config: string; runtime: string; meta: string } {
163
169
  const directory = sessionScopePaths(cwd, sessionId, repositoryRoot, sessionKey).directory;
164
- return { config: join(directory, "config.json"), meta: join(directory, META_FILE) };
170
+ return { config: join(directory, "config.json"), runtime: join(directory, RUNTIME_FILE), meta: join(directory, META_FILE) };
165
171
  }
166
172
 
167
173
  /** Unsupported state.json presence never becomes an anchored checkpoint. */
@@ -178,7 +184,7 @@ export function captureTemporalFileBases(cwd: string, sessionId: string, reposit
178
184
  const paths = (["global", "cwd", "session"] as const).flatMap((scope) => {
179
185
  const pair = temporalScopePaths(cwd, sessionId, scope, repositoryRoot, sessionKey);
180
186
  const runtime = scope === "session" ? sessionRuntimePaths(cwd, sessionId, repositoryRoot, sessionKey) : undefined;
181
- return [pair.checkpoint, pair.patches, join(pair.directory, STATE_FILE), ...(runtime === undefined ? [pair.meta] : [runtime.config, runtime.meta])];
187
+ return [pair.checkpoint, pair.patches, join(pair.directory, STATE_FILE), ...(runtime === undefined ? [pair.meta] : [runtime.config, runtime.runtime, runtime.meta])];
182
188
  });
183
189
  return captureOwnedFileBases(paths, repositoryRoot);
184
190
  }
@@ -218,6 +224,9 @@ export function serializeScopeMetadata(
218
224
  cwdIdentity?: string, existingSource?: string,
219
225
  ): string {
220
226
  const existing = parseMetadataDocument(existingSource, `State Flow metadata`);
227
+ if (scope === "session") {
228
+ for (const key of ["identity", "lineage", "step", "specification", "validation", "bootstrap", "remotePublication", "revision", "temporalRevision", "publication"]) delete existing[key];
229
+ }
221
230
  const sources = serializeScopeStream(stream, scope, cwdIdentity);
222
231
  const value = {
223
232
  ...existing,
@@ -359,7 +368,7 @@ export function isStateFlowOwnedPath(candidate: string, repositoryRoot = getDura
359
368
  return cwdKey(segments[0]!)
360
369
  && sessionKey(segments[1]!)
361
370
  && (segments[2] === STATE_FILE || segments[2] === CHECKPOINT_FILE || segments[2] === PATCHES_FILE
362
- || segments[2] === "config.json" || segments[2] === META_FILE);
371
+ || segments[2] === "config.json" || segments[2] === RUNTIME_FILE || segments[2] === META_FILE);
363
372
  }
364
373
  return false;
365
374
  }
@@ -27,3 +27,8 @@ export function prepareRun(snapshot: Snapshot, prompt: string): boolean {
27
27
  snapshot.meta.validation = undefined;
28
28
  return true;
29
29
  }
30
+
31
+ /** Retain the full prompt only while its run remains recoverable and unfinished. */
32
+ export function completeRun(snapshot: Snapshot): void {
33
+ delete snapshot.meta.specification;
34
+ }