@llblab/pi-kit 0.37.0 → 0.38.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 (152) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +3 -3
  3. package/node_modules/@llblab/pi-state-flow/AGENTS.md +7 -3
  4. package/node_modules/@llblab/pi-state-flow/BACKLOG.md +679 -2
  5. package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +17 -0
  6. package/node_modules/@llblab/pi-state-flow/README.md +30 -14
  7. package/node_modules/@llblab/pi-state-flow/api/modules.ts +11 -0
  8. package/node_modules/@llblab/pi-state-flow/dist/api/modules.d.ts +3 -0
  9. package/node_modules/@llblab/pi-state-flow/dist/api/modules.js +3 -0
  10. package/node_modules/@llblab/pi-state-flow/dist/lib/acquisition.d.ts +7 -4
  11. package/node_modules/@llblab/pi-state-flow/dist/lib/acquisition.js +77 -36
  12. package/node_modules/@llblab/pi-state-flow/dist/lib/artifact.d.ts +2 -0
  13. package/node_modules/@llblab/pi-state-flow/dist/lib/artifact.js +26 -27
  14. package/node_modules/@llblab/pi-state-flow/dist/lib/config.d.ts +3 -0
  15. package/node_modules/@llblab/pi-state-flow/dist/lib/config.js +13 -1
  16. package/node_modules/@llblab/pi-state-flow/dist/lib/context.d.ts +27 -21
  17. package/node_modules/@llblab/pi-state-flow/dist/lib/context.js +144 -46
  18. package/node_modules/@llblab/pi-state-flow/dist/lib/continuation.js +14 -18
  19. package/node_modules/@llblab/pi-state-flow/dist/lib/durable.d.ts +6 -0
  20. package/node_modules/@llblab/pi-state-flow/dist/lib/durable.js +60 -50
  21. package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +1049 -310
  22. package/node_modules/@llblab/pi-state-flow/dist/lib/history.d.ts +4 -4
  23. package/node_modules/@llblab/pi-state-flow/dist/lib/history.js +15 -3
  24. package/node_modules/@llblab/pi-state-flow/dist/lib/json.d.ts +2 -0
  25. package/node_modules/@llblab/pi-state-flow/dist/lib/json.js +2 -1
  26. package/node_modules/@llblab/pi-state-flow/dist/lib/memory.d.ts +3 -1
  27. package/node_modules/@llblab/pi-state-flow/dist/lib/memory.js +7 -7
  28. package/node_modules/@llblab/pi-state-flow/dist/lib/migration.d.ts +29 -0
  29. package/node_modules/@llblab/pi-state-flow/dist/lib/migration.js +186 -0
  30. package/node_modules/@llblab/pi-state-flow/dist/lib/model-interface.d.ts +17 -0
  31. package/node_modules/@llblab/pi-state-flow/dist/lib/model-interface.js +158 -0
  32. package/node_modules/@llblab/pi-state-flow/dist/lib/module-registry.d.ts +21 -0
  33. package/node_modules/@llblab/pi-state-flow/dist/lib/module-registry.js +108 -0
  34. package/node_modules/@llblab/pi-state-flow/dist/lib/operation.js +3 -2
  35. package/node_modules/@llblab/pi-state-flow/dist/lib/ownership.d.ts +4 -4
  36. package/node_modules/@llblab/pi-state-flow/dist/lib/ownership.js +5 -5
  37. package/node_modules/@llblab/pi-state-flow/dist/lib/planes.d.ts +29 -0
  38. package/node_modules/@llblab/pi-state-flow/dist/lib/planes.js +84 -0
  39. package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.d.ts +6 -3
  40. package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.js +33 -16
  41. package/node_modules/@llblab/pi-state-flow/dist/lib/query.d.ts +21 -4
  42. package/node_modules/@llblab/pi-state-flow/dist/lib/query.js +53 -13
  43. package/node_modules/@llblab/pi-state-flow/dist/lib/recovery.js +5 -2
  44. package/node_modules/@llblab/pi-state-flow/dist/lib/rehydration.d.ts +3 -0
  45. package/node_modules/@llblab/pi-state-flow/dist/lib/rehydration.js +10 -1
  46. package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.d.ts +84 -5
  47. package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.js +570 -20
  48. package/node_modules/@llblab/pi-state-flow/dist/lib/selection.d.ts +10 -0
  49. package/node_modules/@llblab/pi-state-flow/dist/lib/selection.js +32 -0
  50. package/node_modules/@llblab/pi-state-flow/dist/lib/semantic-module.d.ts +79 -0
  51. package/node_modules/@llblab/pi-state-flow/dist/lib/semantic-module.js +187 -0
  52. package/node_modules/@llblab/pi-state-flow/dist/lib/session.d.ts +98 -1
  53. package/node_modules/@llblab/pi-state-flow/dist/lib/session.js +398 -3
  54. package/node_modules/@llblab/pi-state-flow/dist/lib/skills.d.ts +7 -2
  55. package/node_modules/@llblab/pi-state-flow/dist/lib/skills.js +55 -3
  56. package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.d.ts +67 -14
  57. package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.js +129 -9
  58. package/node_modules/@llblab/pi-state-flow/dist/lib/state.d.ts +26 -14
  59. package/node_modules/@llblab/pi-state-flow/dist/lib/state.js +61 -19
  60. package/node_modules/@llblab/pi-state-flow/dist/lib/status.d.ts +14 -9
  61. package/node_modules/@llblab/pi-state-flow/dist/lib/status.js +59 -22
  62. package/node_modules/@llblab/pi-state-flow/dist/lib/storage.d.ts +111 -10
  63. package/node_modules/@llblab/pi-state-flow/dist/lib/storage.js +728 -11
  64. package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.d.ts +10 -7
  65. package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.js +50 -14
  66. package/node_modules/@llblab/pi-state-flow/dist/lib/temporal.d.ts +40 -4
  67. package/node_modules/@llblab/pi-state-flow/dist/lib/temporal.js +247 -43
  68. package/node_modules/@llblab/pi-state-flow/dist/lib/transition.d.ts +41 -8
  69. package/node_modules/@llblab/pi-state-flow/dist/lib/transition.js +132 -39
  70. package/node_modules/@llblab/pi-state-flow/dist/package.json +6 -1
  71. package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-guide/SKILL.md +28 -16
  72. package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-memory/SKILL.md +12 -8
  73. package/node_modules/@llblab/pi-state-flow/docs/README.md +9 -0
  74. package/node_modules/@llblab/pi-state-flow/docs/architecture.md +76 -43
  75. package/node_modules/@llblab/pi-state-flow/docs/compatibility.md +9 -1
  76. package/node_modules/@llblab/pi-state-flow/docs/fork-contract.md +6 -0
  77. package/node_modules/@llblab/pi-state-flow/docs/lazy-state.md +23 -19
  78. package/node_modules/@llblab/pi-state-flow/docs/protocol-composition-decision.md +66 -0
  79. package/node_modules/@llblab/pi-state-flow/docs/public-module-contract.md +73 -0
  80. package/node_modules/@llblab/pi-state-flow/docs/scope-selection-contract.md +814 -0
  81. package/node_modules/@llblab/pi-state-flow/docs/semantic-compatibility-decision.md +212 -0
  82. package/node_modules/@llblab/pi-state-flow/docs/semantic-extensibility-decision.md +75 -0
  83. package/node_modules/@llblab/pi-state-flow/docs/semantic-handoff-contract.md +174 -0
  84. package/node_modules/@llblab/pi-state-flow/docs/semantic-memory-evaluation.md +166 -0
  85. package/node_modules/@llblab/pi-state-flow/docs/semantic-module-contract.md +84 -0
  86. package/node_modules/@llblab/pi-state-flow/docs/single-scope-interface-contract.md +201 -0
  87. package/node_modules/@llblab/pi-state-flow/docs/temporal-acceptance.md +22 -1
  88. package/node_modules/@llblab/pi-state-flow/docs/usage.md +32 -18
  89. package/node_modules/@llblab/pi-state-flow/lib/acquisition.ts +68 -31
  90. package/node_modules/@llblab/pi-state-flow/lib/artifact.ts +21 -22
  91. package/node_modules/@llblab/pi-state-flow/lib/config.ts +14 -1
  92. package/node_modules/@llblab/pi-state-flow/lib/context.ts +142 -46
  93. package/node_modules/@llblab/pi-state-flow/lib/continuation.ts +14 -17
  94. package/node_modules/@llblab/pi-state-flow/lib/durable.ts +53 -44
  95. package/node_modules/@llblab/pi-state-flow/lib/extension.ts +897 -297
  96. package/node_modules/@llblab/pi-state-flow/lib/history.ts +16 -7
  97. package/node_modules/@llblab/pi-state-flow/lib/json.ts +2 -1
  98. package/node_modules/@llblab/pi-state-flow/lib/memory.ts +10 -9
  99. package/node_modules/@llblab/pi-state-flow/lib/migration.ts +189 -0
  100. package/node_modules/@llblab/pi-state-flow/lib/model-interface.ts +136 -0
  101. package/node_modules/@llblab/pi-state-flow/lib/module-registry.ts +119 -0
  102. package/node_modules/@llblab/pi-state-flow/lib/operation.ts +3 -2
  103. package/node_modules/@llblab/pi-state-flow/lib/ownership.ts +6 -6
  104. package/node_modules/@llblab/pi-state-flow/lib/planes.ts +98 -0
  105. package/node_modules/@llblab/pi-state-flow/lib/protocol.ts +37 -16
  106. package/node_modules/@llblab/pi-state-flow/lib/query.ts +59 -18
  107. package/node_modules/@llblab/pi-state-flow/lib/recovery.ts +5 -2
  108. package/node_modules/@llblab/pi-state-flow/lib/rehydration.ts +10 -1
  109. package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +574 -21
  110. package/node_modules/@llblab/pi-state-flow/lib/selection.ts +39 -0
  111. package/node_modules/@llblab/pi-state-flow/lib/semantic-module.ts +239 -0
  112. package/node_modules/@llblab/pi-state-flow/lib/session.ts +387 -3
  113. package/node_modules/@llblab/pi-state-flow/lib/skills.ts +45 -4
  114. package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +156 -16
  115. package/node_modules/@llblab/pi-state-flow/lib/state.ts +69 -28
  116. package/node_modules/@llblab/pi-state-flow/lib/status.ts +65 -31
  117. package/node_modules/@llblab/pi-state-flow/lib/storage.ts +770 -18
  118. package/node_modules/@llblab/pi-state-flow/lib/telegram.ts +58 -20
  119. package/node_modules/@llblab/pi-state-flow/lib/temporal.ts +267 -50
  120. package/node_modules/@llblab/pi-state-flow/lib/transition.ts +184 -52
  121. package/node_modules/@llblab/pi-state-flow/package.json +6 -1
  122. package/node_modules/@llblab/pi-state-flow/skills/state-flow-guide/SKILL.md +28 -16
  123. package/node_modules/@llblab/pi-state-flow/skills/state-flow-memory/SKILL.md +12 -8
  124. package/node_modules/@llblab/pi-telegram/AGENTS.md +1 -1
  125. package/node_modules/@llblab/pi-telegram/BACKLOG.md +8 -10
  126. package/node_modules/@llblab/pi-telegram/CHANGELOG.md +10 -0
  127. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-buttons.js +20 -18
  128. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.d.ts +6 -6
  129. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.js +23 -65
  130. package/node_modules/@llblab/pi-telegram/dist/lib/outbound.js +3 -3
  131. package/node_modules/@llblab/pi-telegram/dist/lib/prompts.d.ts +1 -1
  132. package/node_modules/@llblab/pi-telegram/dist/lib/prompts.js +1 -1
  133. package/node_modules/@llblab/pi-telegram/dist/lib/runtime.d.ts +1 -0
  134. package/node_modules/@llblab/pi-telegram/dist/lib/runtime.js +1 -0
  135. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.d.ts +4 -2
  136. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.js +24 -13
  137. package/node_modules/@llblab/pi-telegram/dist/package.json +1 -1
  138. package/node_modules/@llblab/pi-telegram/dist/skills/telegram-bridge/SKILL.md +5 -5
  139. package/node_modules/@llblab/pi-telegram/docs/architecture.md +5 -5
  140. package/node_modules/@llblab/pi-telegram/docs/compact-matrix-literal.md +1 -1
  141. package/node_modules/@llblab/pi-telegram/docs/generative-apps.md +1 -1
  142. package/node_modules/@llblab/pi-telegram/docs/outbound.md +8 -8
  143. package/node_modules/@llblab/pi-telegram/docs/public-api.md +2 -2
  144. package/node_modules/@llblab/pi-telegram/lib/outbound-buttons.ts +17 -15
  145. package/node_modules/@llblab/pi-telegram/lib/outbound-markup.ts +28 -68
  146. package/node_modules/@llblab/pi-telegram/lib/outbound.ts +3 -3
  147. package/node_modules/@llblab/pi-telegram/lib/prompts.ts +1 -1
  148. package/node_modules/@llblab/pi-telegram/lib/runtime.ts +2 -1
  149. package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +25 -12
  150. package/node_modules/@llblab/pi-telegram/package.json +1 -1
  151. package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/SKILL.md +5 -5
  152. package/package.json +3 -3
@@ -1,7 +1,44 @@
1
+ import { captureScopeSelection } from "./selection.js";
1
2
  import { DEFAULT_HISTORY_LIMIT, MAX_HISTORY_LIMIT, validateRecentTransition } from "./history.js";
2
3
  import { applyPatch, containsNull, isJsonValue, isObject, sameJson } from "./json.js";
3
- import { emptyState, isSemanticState, overlayStates, projectSemanticState } from "./state.js";
4
+ import { validateRetainedModules } from "./semantic-module.js";
5
+ import { validateExternalPlanes, emptyState, isSemanticState, overlayStates, projectSemanticState } from "./state.js";
4
6
  const SCOPES = ["global", "cwd", "session"];
7
+ function selected(view) {
8
+ return Object.hasOwn(view, "selection");
9
+ }
10
+ function owners(view) {
11
+ return selected(view) ? view.selection : SCOPES;
12
+ }
13
+ function matchesOwnerKeys(value, selection) {
14
+ return isObject(value) && Object.keys(value).sort().join(",") === [...selection].sort().join(",");
15
+ }
16
+ function streamFor(view, scope) {
17
+ const stream = view.scopes[scope];
18
+ if (!stream)
19
+ throw new Error(`State Flow ${scope} scope is excluded or unavailable`);
20
+ return stream;
21
+ }
22
+ function validateCohort(view, historyLimit) {
23
+ if (selected(view))
24
+ validateSelectedTemporalState(view, historyLimit);
25
+ else
26
+ validateTemporalState(view, historyLimit);
27
+ }
28
+ function cloneCohort(view) {
29
+ const next = structuredClone(view);
30
+ if (selected(next))
31
+ next.selection = captureScopeSelection(next.selection);
32
+ return next;
33
+ }
34
+ function trimCausalBasis(view) {
35
+ if (!selected(view))
36
+ return;
37
+ const retained = new Set(view.lineage.slice(1).map((boundary) => boundary.id));
38
+ view.causalBasis = view.causalBasis.filter((witness) => retained.has(witness.transition.id));
39
+ for (const witness of view.causalBasis)
40
+ witness.owners = captureScopeSelection(witness.owners);
41
+ }
5
42
  function validateHistoryLimit(limit) {
6
43
  if (!Number.isSafeInteger(limit) || limit < 0 || limit > MAX_HISTORY_LIMIT) {
7
44
  throw new Error(`State Flow history limit must be an integer from 0 to ${MAX_HISTORY_LIMIT}`);
@@ -16,9 +53,11 @@ function validateBoundary(boundary) {
16
53
  throw new Error("Invalid State Flow temporal boundary");
17
54
  }
18
55
  }
19
- function validateState(state, location) {
56
+ function validateState(state, location, modules) {
57
+ if (modules !== undefined)
58
+ validateExternalPlanes(state, modules);
20
59
  const json = isJsonValue(state);
21
- const hasNull = json && isObject(state) && Object.keys(emptyState()).some((key) => containsNull(state[key]));
60
+ const hasNull = json && isObject(state) && [...Object.keys(emptyState()), ...Object.keys(modules ?? {})].some((key) => containsNull(state[key]));
22
61
  if (json && isSemanticState(state) && !hasNull)
23
62
  return;
24
63
  const reason = !json ? "expected finite, acyclic JSON data"
@@ -27,9 +66,11 @@ function validateState(state, location) {
27
66
  : "invalid semantic fields or artifact metadata";
28
67
  throw new Error(`Invalid temporal materialized semantic state${location ? ` in ${location}` : ""}: ${reason}`);
29
68
  }
30
- function apply(state, patch, location) {
69
+ function apply(state, patch, location, modules) {
70
+ if (modules !== undefined)
71
+ validateExternalPlanes(patch, modules, true);
31
72
  const next = applyPatch(state, patch);
32
- validateState(next, location);
73
+ validateState(next, location, modules);
33
74
  return next;
34
75
  }
35
76
  function sameBoundary(left, right) {
@@ -40,7 +81,7 @@ export function validateScopeStream(value, scope, historyLimit = DEFAULT_HISTORY
40
81
  validateHistoryLimit(historyLimit);
41
82
  if (!SCOPES.includes(scope))
42
83
  throw new Error("Unknown temporal scope");
43
- if (!isJsonValue(value) || !isObject(value) || Object.keys(value).sort().join(",") !== "checkpoint,patches,revision"
84
+ if (!isJsonValue(value) || !isObject(value) || Object.keys(value).sort().join(",") !== (Object.hasOwn(value, "modules") ? "checkpoint,modules,patches,revision" : "checkpoint,patches,revision")
44
85
  || !Number.isSafeInteger(value.revision) || value.revision < 0
45
86
  || !isObject(value.checkpoint) || Object.keys(value.checkpoint).sort().join(",") !== "state,through"
46
87
  || !Array.isArray(value.patches)) {
@@ -48,7 +89,9 @@ export function validateScopeStream(value, scope, historyLimit = DEFAULT_HISTORY
48
89
  }
49
90
  const stream = value;
50
91
  validateBoundary(stream.checkpoint.through);
51
- validateState(stream.checkpoint.state, `${scope} checkpoint`);
92
+ if (Object.hasOwn(stream, "modules"))
93
+ validateRetainedModules(stream.modules);
94
+ validateState(stream.checkpoint.state, `${scope} checkpoint`, stream.modules);
52
95
  if (stream.patches.length > historyLimit)
53
96
  throw new Error(`Temporal scope tail exceeds configured history limit ${historyLimit}`);
54
97
  if (stream.revision < stream.patches.length)
@@ -68,7 +111,7 @@ export function validateScopeStream(value, scope, historyLimit = DEFAULT_HISTORY
68
111
  throw new Error("Disconnected State Flow temporal ancestry");
69
112
  }
70
113
  validateRecentTransition({ id: record.transition.id, at: 0, transitions: [{ scope, patch: record.patch }] });
71
- const next = apply(state, record.patch, `${scope} tail`);
114
+ const next = apply(state, record.patch, `${scope} tail`, stream.modules);
72
115
  state = next;
73
116
  previous = record.transition;
74
117
  identities.add(previous.id);
@@ -96,23 +139,71 @@ export function validateTemporalLineage(value, historyLimit = DEFAULT_HISTORY_LI
96
139
  export function validateScopeLineage(stream, scope, lineage, historyLimit = DEFAULT_HISTORY_LIMIT) {
97
140
  validateTemporalLineage(lineage, historyLimit);
98
141
  validateScopeStream(stream, scope, historyLimit);
142
+ const mismatch = scopeLineageMismatch(stream, lineage);
143
+ if (mismatch)
144
+ throw new Error(mismatch);
145
+ }
146
+ /** Classify live shared drift only after both stream and lineage have passed their own validators. */
147
+ export function scopeLineageMatches(stream, scope, lineage, historyLimit = DEFAULT_HISTORY_LIMIT) {
148
+ validateTemporalLineage(lineage, historyLimit);
149
+ validateScopeStream(stream, scope, historyLimit);
150
+ return scopeLineageMismatch(stream, lineage) === undefined;
151
+ }
152
+ function scopeLineageMismatch(stream, lineage) {
99
153
  const oldest = lineage[0];
100
154
  const head = lineage.at(-1);
101
155
  if (stream.checkpoint.through.position > oldest.position)
102
- throw new Error("Scope checkpoint is newer than the guaranteed hot boundary");
156
+ return "Scope checkpoint is newer than the guaranteed hot boundary";
103
157
  const identities = new Map(lineage.map((boundary) => [boundary.id, boundary]));
104
158
  for (const boundary of [stream.checkpoint.through, ...stream.patches.map((record) => record.transition)]) {
105
159
  if (boundary.position > head.position)
106
- throw new Error("Temporal scope patch is beyond the active head");
160
+ return "Temporal scope patch is beyond the active head";
107
161
  const identity = identities.get(boundary.id);
108
162
  const position = lineage[boundary.position - oldest.position];
109
163
  if ((identity && !sameBoundary(identity, boundary)) || (position && !sameBoundary(position, boundary))) {
110
- throw new Error("Conflicting State Flow temporal lineage");
164
+ return "Conflicting State Flow temporal lineage";
111
165
  }
112
166
  }
167
+ return undefined;
113
168
  }
114
169
  /** Validate one revision-selected cohort. Its older ancestry must be bound by the durable loader. */
115
170
  export function validateTemporalState(view, historyLimit = DEFAULT_HISTORY_LIMIT) {
171
+ if (Object.hasOwn(view, "selection") || Object.hasOwn(view, "causalBasis"))
172
+ throw new Error("Selected temporal state requires selected-cohort admission");
173
+ validateTemporalCohort(view, SCOPES, historyLimit);
174
+ }
175
+ /** Structural consistency only: storage must additionally bind witnesses to accepted source authority. */
176
+ export function validateSelectedTemporalState(view, historyLimit = DEFAULT_HISTORY_LIMIT) {
177
+ if (!isObject(view) || Object.keys(view).sort().join(",") !== "causalBasis,lineage,scopes,selection"
178
+ || !isObject(view.scopes) || !Array.isArray(view.causalBasis))
179
+ throw new Error("Invalid selected temporal envelope");
180
+ const selection = captureScopeSelection(view.selection);
181
+ if (!sameJson(selection, view.selection) || Object.keys(view.scopes).sort().join(",") !== [...selection].sort().join(",")) {
182
+ throw new Error("Selected temporal owners must match the canonical selection exactly");
183
+ }
184
+ validateTemporalLineage(view.lineage, historyLimit);
185
+ const boundaries = new Map(view.lineage.slice(1).map((boundary) => [boundary.id, boundary]));
186
+ const witnessed = new Set();
187
+ let position = view.lineage[0].position;
188
+ for (const witness of view.causalBasis) {
189
+ if (!isObject(witness) || Object.keys(witness).sort().join(",") !== "owners,transition")
190
+ throw new Error("Invalid excluded temporal witness");
191
+ validateBoundary(witness.transition);
192
+ const identity = boundaries.get(witness.transition.id);
193
+ const sourceOwners = captureScopeSelection(witness.owners);
194
+ if (!sameJson(sourceOwners, witness.owners) || sourceOwners.some((scope) => selection.includes(scope))
195
+ || !identity || !sameBoundary(identity, witness.transition) || witness.transition.position <= position
196
+ || witnessed.has(identity.id))
197
+ throw new Error("Invalid excluded temporal witness basis");
198
+ witnessed.add(identity.id);
199
+ position = witness.transition.position;
200
+ }
201
+ validateTemporalCohort(view, selection, historyLimit, witnessed);
202
+ if (view.causalBasis.some((witness) => selection.some((scope) => streamFor(view, scope).patches.some((record) => record.transition.id === witness.transition.id)))) {
203
+ throw new Error("Excluded temporal witness duplicates a selected patch");
204
+ }
205
+ }
206
+ function validateTemporalCohort(view, activeOwners, historyLimit, witnessed = new Set()) {
116
207
  validateHistoryLimit(historyLimit);
117
208
  validateTemporalLineage(view.lineage, historyLimit);
118
209
  const oldest = view.lineage[0];
@@ -132,8 +223,8 @@ export function validateTemporalState(view, historyLimit = DEFAULT_HISTORY_LIMIT
132
223
  for (const boundary of view.lineage)
133
224
  remember(boundary);
134
225
  const head = view.lineage.at(-1);
135
- for (const scope of SCOPES) {
136
- const stream = view.scopes[scope];
226
+ for (const scope of activeOwners) {
227
+ const stream = streamFor(view, scope);
137
228
  validateScopeStream(stream, scope, historyLimit);
138
229
  remember(stream.checkpoint.through);
139
230
  if (stream.checkpoint.through.position > oldest.position) {
@@ -151,7 +242,7 @@ export function validateTemporalState(view, historyLimit = DEFAULT_HISTORY_LIMIT
151
242
  throw new Error("Disconnected State Flow temporal ancestry");
152
243
  }
153
244
  for (const boundary of view.lineage.slice(1)) {
154
- if (!SCOPES.some((scope) => view.scopes[scope].patches.some((record) => record.transition.id === boundary.id))) {
245
+ if (!activeOwners.some((scope) => streamFor(view, scope).patches.some((record) => record.transition.id === boundary.id)) && !witnessed.has(boundary.id)) {
155
246
  throw new Error("Temporal boundary has no semantic patch");
156
247
  }
157
248
  }
@@ -175,38 +266,143 @@ export function createTemporalState(states, id, historyLimit = DEFAULT_HISTORY_L
175
266
  validateTemporalState(view, historyLimit);
176
267
  return view;
177
268
  }
269
+ /** Restrict a validated value cohort; expanding selection requires separately acquired current streams. */
270
+ export function selectTemporalState(source, selection, historyLimit = DEFAULT_HISTORY_LIMIT) {
271
+ validateCohort(source, historyLimit);
272
+ const wanted = captureScopeSelection(selection);
273
+ if (wanted.some((scope) => !owners(source).includes(scope)))
274
+ throw new Error("Cannot select an unacquired temporal owner");
275
+ const inherited = new Map(selected(source) ? source.causalBasis.map((witness) => [witness.transition.id, witness]) : []);
276
+ const causalBasis = [];
277
+ for (const boundary of source.lineage.slice(1)) {
278
+ const live = owners(source).filter((scope) => streamFor(source, scope).patches.some((record) => record.transition.id === boundary.id));
279
+ if (live.some((scope) => wanted.includes(scope)))
280
+ continue;
281
+ const evidence = [...new Set([...live, ...(inherited.get(boundary.id)?.owners ?? [])])];
282
+ causalBasis.push({ transition: structuredClone(boundary), owners: captureScopeSelection(evidence) });
283
+ }
284
+ const view = {
285
+ selection: wanted, lineage: structuredClone(source.lineage),
286
+ scopes: Object.fromEntries(wanted.map((scope) => [scope, structuredClone(streamFor(source, scope))])), causalBasis,
287
+ };
288
+ validateSelectedTemporalState(view, historyLimit);
289
+ return view;
290
+ }
291
+ /** Complete retained witness skeleton, derived from validated semantic records/basis, never from lineage alone. */
292
+ export function temporalCausalWitnesses(view, historyLimit = DEFAULT_HISTORY_LIMIT) {
293
+ validateCohort(view, historyLimit);
294
+ const inherited = new Map(selected(view) ? view.causalBasis.map((witness) => [witness.transition.id, witness]) : []);
295
+ return view.lineage.slice(1).map((transition) => {
296
+ const live = owners(view).filter((scope) => streamFor(view, scope).patches.some((record) => record.transition.id === transition.id));
297
+ return { transition: structuredClone(transition), owners: captureScopeSelection([...new Set([...live, ...(inherited.get(transition.id)?.owners ?? [])])]) };
298
+ });
299
+ }
300
+ /** Metadata syntax only; a skeleton is neither semantic replay nor acquisition/publication authority. */
301
+ export function captureTemporalWitnesses(lineage, witnesses, historyLimit = DEFAULT_HISTORY_LIMIT) {
302
+ validateTemporalLineage(lineage, historyLimit);
303
+ if (!Array.isArray(witnesses))
304
+ throw new Error("Invalid accepted temporal witness array");
305
+ const boundaries = new Map(lineage.slice(1).map((boundary) => [boundary.id, boundary]));
306
+ const admitted = [];
307
+ let position = lineage[0].position;
308
+ for (const witness of witnesses) {
309
+ if (!isObject(witness) || Object.keys(witness).sort().join(",") !== "owners,transition")
310
+ throw new Error("Invalid accepted temporal witness");
311
+ const transition = witness.transition;
312
+ validateBoundary(transition);
313
+ const identity = boundaries.get(transition.id);
314
+ const sourceOwners = captureScopeSelection(witness.owners);
315
+ if (!identity || !sameBoundary(identity, transition) || transition.position <= position || !sameJson(sourceOwners, witness.owners)) {
316
+ throw new Error("Accepted temporal witness does not match its lineage");
317
+ }
318
+ admitted.push({ transition: structuredClone(transition), owners: sourceOwners });
319
+ position = transition.position;
320
+ }
321
+ return admitted;
322
+ }
323
+ /** Reconstruct selected values from caller-bound accepted evidence; this validates syntax/consistency, not physical authority. */
324
+ export function restoreSelectedTemporalState(scopes, selection, lineage, witnesses, historyLimit = DEFAULT_HISTORY_LIMIT) {
325
+ const wanted = captureScopeSelection(selection);
326
+ if (!matchesOwnerKeys(scopes, wanted))
327
+ throw new Error("Selected temporal streams must match their owners exactly");
328
+ validateTemporalLineage(lineage, historyLimit);
329
+ for (const scope of wanted)
330
+ validateScopeLineage(scopes[scope], scope, lineage, historyLimit);
331
+ const skeleton = captureTemporalWitnesses(lineage, witnesses, historyLimit);
332
+ const admitted = new Map(skeleton.map((witness) => [witness.transition.id, witness]));
333
+ for (const witness of skeleton)
334
+ for (const scope of witness.owners.filter((owner) => wanted.includes(owner))) {
335
+ if (!scopes[scope].patches.some((record) => sameBoundary(record.transition, witness.transition)))
336
+ throw new Error("Accepted temporal witness is missing its selected owner patch");
337
+ }
338
+ const causalBasis = [];
339
+ for (const transition of lineage.slice(1)) {
340
+ if (wanted.some((scope) => scopes[scope].patches.some((record) => record.transition.id === transition.id)))
341
+ continue;
342
+ const witness = admitted.get(transition.id);
343
+ if (!witness)
344
+ throw new Error("Temporal boundary has no accepted owner witness");
345
+ causalBasis.push(witness);
346
+ }
347
+ const view = { selection: wanted, lineage: structuredClone([...lineage]), scopes: structuredClone(scopes), causalBasis };
348
+ validateSelectedTemporalState(view, historyLimit);
349
+ return view;
350
+ }
351
+ /** Explicit selected values at a new origin; this constructor grants no initialization/acquisition permission. */
352
+ export function createSelectedTemporalState(states, selection, id, historyLimit = DEFAULT_HISTORY_LIMIT) {
353
+ const wanted = captureScopeSelection(selection);
354
+ if (!matchesOwnerKeys(states, wanted))
355
+ throw new Error("Selected temporal values must match their owners exactly");
356
+ const through = { id, position: 0, parent: null };
357
+ const view = {
358
+ selection: wanted, lineage: [through], causalBasis: [],
359
+ scopes: Object.fromEntries(wanted.map((scope) => [scope, { revision: 0, checkpoint: { through: structuredClone(through), state: structuredClone(states[scope]) }, patches: [] }])),
360
+ };
361
+ validateSelectedTemporalState(view, historyLimit);
362
+ return view;
363
+ }
364
+ /** Adopt explicitly admitted selected streams at current values only, without invented aligned history. */
365
+ export function adoptSelectedTemporalStreams(scopes, selection, id, historyLimit = DEFAULT_HISTORY_LIMIT) {
366
+ const wanted = captureScopeSelection(selection);
367
+ if (!matchesOwnerKeys(scopes, wanted))
368
+ throw new Error("Selected temporal streams must match their owners exactly");
369
+ for (const scope of wanted)
370
+ validateScopeStream(scopes[scope], scope, MAX_HISTORY_LIMIT);
371
+ const boundaries = wanted.flatMap((scope) => [scopes[scope].checkpoint.through, ...scopes[scope].patches.map((record) => record.transition)]);
372
+ const origin = { id, position: Math.max(...boundaries.map((boundary) => boundary.position)) + 1, parent: null };
373
+ const view = { selection: wanted, lineage: [origin], scopes: structuredClone(scopes), causalBasis: [] };
374
+ return constrainTemporalState(view, historyLimit);
375
+ }
178
376
  function scopeAt(stream, boundary) {
179
377
  let state = structuredClone(stream.checkpoint.state);
180
378
  for (const record of stream.patches) {
181
379
  if (record.transition.position > boundary.position)
182
380
  break;
183
- state = apply(state, record.patch);
381
+ state = apply(state, record.patch, undefined, stream.modules);
184
382
  }
185
383
  return state;
186
384
  }
187
385
  /** Fold retained tails to a lower configured limit without inventing history. */
188
386
  export function constrainTemporalState(view, historyLimit) {
189
387
  validateHistoryLimit(historyLimit);
190
- validateTemporalState(view, MAX_HISTORY_LIMIT);
191
- const next = structuredClone(view);
192
- for (const scope of SCOPES) {
193
- const stream = next.scopes[scope];
388
+ validateCohort(view, MAX_HISTORY_LIMIT);
389
+ const next = cloneCohort(view);
390
+ for (const scope of owners(next)) {
391
+ const stream = streamFor(next, scope);
194
392
  while (stream.patches.length > historyLimit) {
195
393
  const folded = stream.patches.shift();
196
- stream.checkpoint = { through: folded.transition, state: apply(stream.checkpoint.state, folded.patch) };
394
+ stream.checkpoint = { through: folded.transition, state: apply(stream.checkpoint.state, folded.patch, undefined, stream.modules) };
197
395
  }
198
396
  }
199
397
  next.lineage = next.lineage.slice(-(historyLimit + 1));
200
- validateTemporalState(next, historyLimit);
398
+ trimCausalBasis(next);
399
+ validateCohort(next, historyLimit);
201
400
  return next;
202
401
  }
203
- /** Current independent scope revisions; Effective uses this vector rather than inventing a scalar owner. */
204
402
  export function temporalScopeRevisions(view) {
205
- const revisions = {
206
- global: view.scopes.global.revision,
207
- cwd: view.scopes.cwd.revision,
208
- session: view.scopes.session.revision,
209
- };
403
+ if (selected(view))
404
+ validateSelectedTemporalState(view, MAX_HISTORY_LIMIT);
405
+ const revisions = Object.fromEntries(owners(view).map((scope) => [scope, streamFor(view, scope).revision]));
210
406
  if (Object.values(revisions).some((revision) => !Number.isSafeInteger(revision) || revision < 0)) {
211
407
  throw new Error("Invalid State Flow scope revision vector");
212
408
  }
@@ -217,24 +413,29 @@ function readBoundary(view, offset, historyLimit) {
217
413
  if (!Number.isSafeInteger(offset) || offset < 0 || offset > historyLimit) {
218
414
  throw new Error(`State Flow hot-history offset must be an integer from 0 to ${historyLimit}`);
219
415
  }
220
- validateTemporalState(view, historyLimit);
416
+ validateCohort(view, historyLimit);
221
417
  const boundary = view.lineage[view.lineage.length - 1 - offset];
222
418
  if (!boundary)
223
419
  throw new Error("Requested history predates the proven temporal origin");
224
420
  return boundary;
225
421
  }
226
- /** Exact scope semantics for authored staging; defaults must never become implicit writes. */
227
422
  export function readTemporalScopes(view, offset = 0, historyLimit = DEFAULT_HISTORY_LIMIT) {
228
423
  const boundary = readBoundary(view, offset, historyLimit);
229
- return { global: scopeAt(view.scopes.global, boundary), cwd: scopeAt(view.scopes.cwd, boundary), session: scopeAt(view.scopes.session, boundary) };
424
+ return Object.fromEntries(owners(view).map((scope) => [scope, scopeAt(streamFor(view, scope), boundary)]));
230
425
  }
231
426
  /** Sparse current/historical view: unknown planes and absent values never become effective data. */
232
427
  export function readTemporalView(view, offset = 0, scope, historyLimit = DEFAULT_HISTORY_LIMIT) {
233
428
  if (scope !== undefined && !SCOPES.includes(scope))
234
429
  throw new Error("Unknown temporal scope");
430
+ if (scope !== undefined && !owners(view).includes(scope))
431
+ throw new Error(`State Flow ${scope} scope is excluded`);
235
432
  const boundary = readBoundary(view, offset, historyLimit);
236
- const owners = scope === undefined ? SCOPES : [scope];
237
- return owners.reduce((state, owner) => applyPatch(state, projectSemanticState(scopeAt(view.scopes[owner], boundary))), {});
433
+ const active = scope === undefined ? owners(view) : [scope];
434
+ return active.reduce((state, owner) => {
435
+ const stream = streamFor(view, owner), raw = scopeAt(stream, boundary);
436
+ const projected = projectSemanticState(raw, stream.modules);
437
+ return applyPatch(state, projected);
438
+ }, {});
238
439
  }
239
440
  /** Internal defaulted materialization for consumers that require object registries. */
240
441
  export function readTemporalState(view, offset = 0, scope, historyLimit = DEFAULT_HISTORY_LIMIT) {
@@ -243,45 +444,48 @@ export function readTemporalState(view, offset = 0, scope, historyLimit = DEFAUL
243
444
  /** Allocate the identity outside this algebra; only materially effective patches accept it. */
244
445
  export function advanceTemporalState(view, transitions, id, historyLimit = DEFAULT_HISTORY_LIMIT) {
245
446
  validateHistoryLimit(historyLimit);
246
- validateTemporalState(view, historyLimit);
447
+ validateCohort(view, historyLimit);
247
448
  if (transitions.length === 0)
248
449
  return view;
249
450
  validateRecentTransition({ id, at: 0, transitions });
451
+ if (transitions.some(({ scope }) => !owners(view).includes(scope)))
452
+ throw new Error("State Flow patch contains an excluded temporal owner");
250
453
  const head = view.lineage.at(-1);
251
454
  const changes = transitions.filter(({ scope, patch }) => {
252
- const current = scopeAt(view.scopes[scope], head);
253
- return !sameJson(current, apply(current, patch));
455
+ const current = scopeAt(streamFor(view, scope), head);
456
+ return !sameJson(current, apply(current, patch, undefined, streamFor(view, scope).modules));
254
457
  });
255
458
  if (changes.length === 0)
256
459
  return view;
257
460
  const knownIds = new Set(view.lineage.map((boundary) => boundary.id));
258
- for (const scope of SCOPES) {
259
- knownIds.add(view.scopes[scope].checkpoint.through.id);
260
- for (const record of view.scopes[scope].patches)
461
+ for (const scope of owners(view)) {
462
+ knownIds.add(streamFor(view, scope).checkpoint.through.id);
463
+ for (const record of streamFor(view, scope).patches)
261
464
  knownIds.add(record.transition.id);
262
465
  }
263
466
  if (knownIds.has(id))
264
467
  throw new Error("State Flow transition identity has already been used");
265
468
  const boundary = { id, position: head.position + 1, parent: head.id };
266
469
  validateBoundary(boundary);
267
- const next = structuredClone(view);
470
+ const next = cloneCohort(view);
268
471
  for (const { scope, patch } of changes) {
269
- const stream = next.scopes[scope];
472
+ const stream = streamFor(next, scope);
270
473
  if (stream.revision >= Number.MAX_SAFE_INTEGER)
271
474
  throw new Error(`State Flow ${scope} scope revision is exhausted`);
272
475
  stream.revision += 1;
273
476
  if (historyLimit === 0) {
274
- stream.checkpoint = { through: structuredClone(boundary), state: apply(scopeAt(stream, head), patch) };
477
+ stream.checkpoint = { through: structuredClone(boundary), state: apply(scopeAt(stream, head), patch, undefined, stream.modules) };
275
478
  stream.patches = [];
276
479
  continue;
277
480
  }
278
481
  while (stream.patches.length >= historyLimit) {
279
482
  const folded = stream.patches.shift();
280
- stream.checkpoint = { through: folded.transition, state: apply(stream.checkpoint.state, folded.patch) };
483
+ stream.checkpoint = { through: folded.transition, state: apply(stream.checkpoint.state, folded.patch, undefined, stream.modules) };
281
484
  }
282
485
  stream.patches.push({ transition: structuredClone(boundary), patch: structuredClone(patch) });
283
486
  }
284
487
  next.lineage = [...next.lineage, boundary].slice(-(historyLimit + 1));
285
- validateTemporalState(next, historyLimit);
488
+ trimCausalBasis(next);
489
+ validateCohort(next, historyLimit);
286
490
  return next;
287
491
  }
@@ -1,29 +1,62 @@
1
1
  import { type ArtifactProvenance } from "./artifact.ts";
2
2
  import type { SuccessfulArtifactRead } from "./acquisition.ts";
3
3
  import { type AcceptedTransition } from "./history.ts";
4
- import { type MissingDeletion } from "./json.ts";
4
+ import { type JsonObject, type MissingDeletion } from "./json.ts";
5
5
  import { type OwnedPath } from "./ownership.ts";
6
6
  import { type SuccessfulSkillRead } from "./skills.ts";
7
7
  import type { Snapshot } from "./snapshot.ts";
8
- import type { AtomicScopePatches, ScopedSemanticStates, StateScope, TerminalTransition } from "./state.ts";
9
- export interface StagedScopedTransition {
10
- nextStates: ScopedSemanticStates;
8
+ import { type ScopeSelection } from "./selection.ts";
9
+ import { type RetainedModules, type StateFlowModule } from "./semantic-module.ts";
10
+ import type { JsonValue } from "./json.ts";
11
+ import type { AtomicScopePatches, ScopedSemanticStates, ScopeStateValues, StateScope, TerminalTransition } from "./state.ts";
12
+ interface StagedTransition<States extends ScopeStateValues> {
13
+ nextStates: States;
11
14
  /** Authored no-op deletions; presentation evidence only, never replay input. */
12
15
  missingDeletions: MissingDeletion[];
13
- /** Scope-local targets removed by the staged intent cascade, not replay input. */
14
- cascades: Record<StateScope, OwnedPath[]>;
15
- stateHashes: Record<StateScope, string>;
16
+ /** Scope-local targets cleared before other writes; may be recreated, never replay input. */
17
+ cascades: {
18
+ [Owner in keyof States]: OwnedPath[];
19
+ };
20
+ stateHashes: {
21
+ [Owner in keyof States]: string;
22
+ };
16
23
  /** Fresh runtime-owned provenance for artifacts compiled in this transition. */
17
- provenanceUpdates: Record<StateScope, Record<string, ArtifactProvenance>>;
24
+ provenanceUpdates: {
25
+ [Owner in keyof States]: Record<string, ArtifactProvenance>;
26
+ };
18
27
  causalBasis: string;
19
28
  committed: boolean;
29
+ moduleAdditions?: Partial<Record<StateScope, RetainedModules>>;
30
+ }
31
+ export type StagedScopedTransition = StagedTransition<ScopedSemanticStates> & {
32
+ selection?: never;
33
+ };
34
+ export type StagedSelectedTransition = StagedTransition<ScopeStateValues> & {
35
+ selection: ScopeSelection;
36
+ };
37
+ /** Internal host proposals only; never accepted from model tool arguments. */
38
+ export interface ModuleReplacement {
39
+ scope: StateScope;
40
+ id: string;
41
+ content: JsonObject;
42
+ }
43
+ export interface SelectedSemanticBasis {
44
+ selection: ScopeSelection;
45
+ states: ScopeStateValues;
46
+ modules?: Partial<Record<StateScope, RetainedModules>>;
20
47
  }
21
48
  /** Stage one canonical atomic scope cohort without changing the finalized response. */
22
49
  export declare function stageAtomicScopePatches(currentStates: ScopedSemanticStates, patches: AtomicScopePatches, successfulSkillReads: Iterable<SuccessfulSkillRead>, causalBasis: string, successfulArtifactReads?: Iterable<SuccessfulArtifactRead>): StagedScopedTransition;
23
50
  export declare function stageScopedTransition(currentStates: ScopedSemanticStates, transition: TerminalTransition, successfulSkillReads: Iterable<SuccessfulSkillRead>, causalBasis: string, successfulArtifactReads?: Iterable<SuccessfulArtifactRead>): StagedScopedTransition;
51
+ /** Value-only selected staging; a selection or causal token is not physical publication authority. */
52
+ export declare function stageSelectedScopePatches(current: SelectedSemanticBasis, patches: AtomicScopePatches, successfulSkillReads: Iterable<SuccessfulSkillRead>, causalBasis: string, successfulArtifactReads?: Iterable<SuccessfulArtifactRead>, definitions?: readonly StateFlowModule<JsonObject, JsonValue>[], replacements?: readonly ModuleReplacement[]): StagedSelectedTransition;
53
+ /** Excluded Session suppresses capture, not ordinary-answer delivery or lifecycle completion. */
54
+ export declare function stageSelectedScopedTransition(current: SelectedSemanticBasis, transition: TerminalTransition, successfulSkillReads: Iterable<SuccessfulSkillRead>, causalBasis: string, successfulArtifactReads?: Iterable<SuccessfulArtifactRead>): StagedSelectedTransition;
24
55
  /** Commit one accepted transition; durable publication receives all changed scopes as one cohort. */
25
56
  export interface CommitScopedTransitionOptions {
26
57
  /** Runtime response reconciliation finalizes bootstrap lifecycle state. */
27
58
  finalizeRun?: boolean;
28
59
  }
29
60
  export declare function commitScopedTransition(snapshot: Snapshot, states: ScopedSemanticStates, stage: StagedScopedTransition, publishDurable: (accepted: AcceptedTransition | undefined, nextSnapshot: Snapshot) => void, causalBasis: string, options?: CommitScopedTransitionOptions): boolean;
61
+ export declare function commitSelectedTransition(snapshot: Snapshot, current: SelectedSemanticBasis, stage: StagedSelectedTransition, publishDurable: (accepted: AcceptedTransition | undefined, nextSnapshot: Snapshot) => void, causalBasis: string, options?: CommitScopedTransitionOptions): boolean;
62
+ export {};