@llblab/pi-kit 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +3 -3
- package/node_modules/@llblab/pi-grow-loop/AGENTS.md +1 -0
- package/node_modules/@llblab/pi-grow-loop/CHANGELOG.md +5 -0
- package/node_modules/@llblab/pi-grow-loop/README.md +2 -0
- package/node_modules/@llblab/pi-grow-loop/index.ts +115 -8
- package/node_modules/@llblab/pi-grow-loop/package.json +1 -1
- package/node_modules/@llblab/pi-state-flow/AGENTS.md +4 -4
- package/node_modules/@llblab/pi-state-flow/BACKLOG.md +7 -0
- package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +10 -0
- package/node_modules/@llblab/pi-state-flow/README.md +4 -2
- package/node_modules/@llblab/pi-state-flow/docs/architecture.md +5 -3
- package/node_modules/@llblab/pi-state-flow/docs/temporal-acceptance.md +2 -2
- package/node_modules/@llblab/pi-state-flow/index.ts +22 -1
- package/node_modules/@llblab/pi-state-flow/lib/context.ts +1 -1
- package/node_modules/@llblab/pi-state-flow/lib/extension.ts +248 -144
- package/node_modules/@llblab/pi-state-flow/lib/telegram.ts +267 -0
- package/node_modules/@llblab/pi-state-flow/lib/terminal.ts +1 -1
- package/node_modules/@llblab/pi-state-flow/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/AGENTS.md +1 -1
- package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -1
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +8 -0
- package/node_modules/@llblab/pi-telegram/README.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +4 -4
- package/node_modules/@llblab/pi-telegram/docs/outbound.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +2 -2
- package/node_modules/@llblab/pi-telegram/docs/ui-style.md +1 -1
- package/node_modules/@llblab/pi-telegram/lib/config.ts +6 -4
- package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +2 -1
- package/node_modules/@llblab/pi-telegram/lib/preview.ts +20 -0
- package/node_modules/@llblab/pi-telegram/package.json +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@llblab/pi-kit` are documented here.
|
|
4
4
|
|
|
5
|
+
## 0.10.0 - 2026-09-11
|
|
6
|
+
|
|
7
|
+
- `State Flow Telegram Surface`: Advances the exact State Flow pin to `0.9.0`, adding the optional fail-open Telegram status line (`State Flow: on · step #N`) and the inline main-menu section button before Settings with Start, Stop, Refresh, and deferred-start control for the current session branch. The package set, resource inventory, and explicit load order remain unchanged.
|
|
8
|
+
- `Grow Loop Telegram Mirror`: Advances the exact Grow Loop pin to `0.8.0`, mirroring the visible loop rhythm (`Grow Loop: #N · waiting`, `#N · Ns`, `#N · running`) into the Telegram status screen with the same fail-open, no-scheduling-change guarantees. The package set, resource inventory, and explicit load order remain unchanged.
|
|
9
|
+
|
|
10
|
+
## 0.9.1 - 2026-09-11
|
|
11
|
+
|
|
12
|
+
- `Telegram Draft Cadence`: Advances the exact Telegram pin to `0.45.4`, so each preview segment holds its first frame for one full two-second interval from its first visible text; the opening draft is an accumulated passage instead of a single streamed word, later frames keep the trailing cadence, and rollover after tool work reopens the window while sealing or final publication still cancels pending timers. The package set, resource inventory, and explicit load order remain unchanged.
|
|
13
|
+
- `State Flow Preserved Answers`: Advances the exact State Flow pin to `0.8.1`, preserving the first terminal draft as the runtime-owned response even without `final:true` and bounding same-run fallback turns to eliciting the final patch without replacing the visible answer. The package set, resource inventory, and explicit load order remain unchanged.
|
|
14
|
+
|
|
5
15
|
## 0.9.0 - 2026-09-11
|
|
6
16
|
|
|
7
17
|
- `State Flow Atomic Cohorts`: Advances the exact State Flow pin to `0.8.0`, replacing the single-scope PATCH/UNCHANGED protocol with one `patch_state({global?, cwd?, session?, final?})` primitive whose supplied scopes commit as one atomic transition, turning `final:true` into an ephemeral per-iteration terminal-eligibility latch with bounded three-attempt steering, and retaining exact rejected-call arguments in opt-in diagnostics while preserving the package set, resource inventory, and explicit load order.
|
package/README.md
CHANGED
|
@@ -13,9 +13,9 @@ Package links lead to the owning repositories for usage, documentation, issues,
|
|
|
13
13
|
| [`@llblab/pi-actors`](https://github.com/llblab/pi-actors) | `0.53.0` | Inspectable local Runs, reusable Recipes, persistent tools, and delegation Skills |
|
|
14
14
|
| [`@llblab/pi-clean-room`](https://github.com/llblab/pi-clean-room) | `0.1.1` | Isolated nested Pi TUI with explicitly selected extensions |
|
|
15
15
|
| [`@llblab/pi-codex-usage`](https://github.com/llblab/pi-codex-usage) | `0.9.4` | Compact Codex/Spark subscription-limit status |
|
|
16
|
-
| [`@llblab/pi-grow-loop`](https://github.com/llblab/pi-grow-loop) | `0.
|
|
17
|
-
| [`@llblab/pi-state-flow`](https://github.com/llblab/pi-state-flow) | `0.
|
|
18
|
-
| [`@llblab/pi-telegram`](https://github.com/llblab/pi-telegram) | `0.45.
|
|
16
|
+
| [`@llblab/pi-grow-loop`](https://github.com/llblab/pi-grow-loop) | `0.8.0` | Visible continuation scheduling and bounded worker Skills |
|
|
17
|
+
| [`@llblab/pi-state-flow`](https://github.com/llblab/pi-state-flow) | `0.9.0` | Atomic scoped state cohorts with final-eligibility resolution |
|
|
18
|
+
| [`@llblab/pi-telegram`](https://github.com/llblab/pi-telegram) | `0.45.4` | Telegram companion, queues, files, voice, controls, and Generative Apps guidance |
|
|
19
19
|
| [`@llblab/skills`](https://github.com/llblab/skills) | `1.15.0` | Portable workflows for engineering, review, design, context maintenance, and other focused tasks |
|
|
20
20
|
|
|
21
21
|
Versions are exact by design. An upstream release does not change an installed kit until this repository explicitly advances the dependency and publishes a new kit version. Runtime defects and package-specific feature requests belong in the linked repository; package selection and kit installation issues belong here.
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
- There is no slash-command control surface. Any ordinary user prompt exits the active runtime rhythm by clearing pending scheduling and hiding loop status; restart/continuation intent belongs to the agent and Grow Loop skill, not to a runtime latch or regex.
|
|
28
28
|
- No start slash commands, budgets, cycle counts, hidden processes, or background agents.
|
|
29
29
|
- Loop status is dynamic: show warning-colored `grow-loop ∞N` only while the next iteration is deferred until idle, `grow-loop Ns` only during the configured countdown, and dim `grow-loop ∞N` only while a loop-scheduled turn is active. Clear active status only after Pi fully settles without an armed successor; low-level run endings may still lead to retry or compaction recovery. Any user input except the runtime's exact expected continuation prompt clears pending loop scheduling and hides loop status, including operator input injected through another extension.
|
|
30
|
+
- Telegram status mirror: when `pi-telegram` is installed, the optional fail-open status-line adapter mirrors the same visible rhythm (`Grow Loop: #N · waiting|Xs|running`) and hides with it. It reads the same runtime state, registers once per process lifetime, disposes on session shutdown, and a missing or unready transport changes nothing.
|
|
30
31
|
- Grow Loop does not own Escape/abort semantics; Escape remains baseline Pi behavior for active agent turns. The runtime does not block future `grow_loop` tool calls; the skill contract owns whether recent user context means continue, stop, restart, or change direction.
|
|
31
32
|
|
|
32
33
|
## Topology
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.0: Telegram Status Mirror
|
|
4
|
+
|
|
5
|
+
- `Telegram Mirror`: With the optional `pi-telegram` extension installed, the Telegram Status screen mirrors the same loop rhythm as the terminal status and hides with it: `Grow Loop: #N · waiting` while the next prompt is armed until idle, `#N · 2.4s` during the configured countdown, and `#N · running` while the loop-scheduled turn is active.
|
|
6
|
+
- `Compatibility`: The mirror is optional and fail-open, reads the same runtime state, registers once, disposes on session shutdown, and changes no scheduling behavior; absence of the transport changes nothing.
|
|
7
|
+
|
|
3
8
|
## 0.7.5: Status Identity Hotfix
|
|
4
9
|
|
|
5
10
|
- `Status Identity`: Labels deferred, countdown, and active runtime status as `grow-loop` instead of the ambiguous `loop`, matching the extension's public identity and the neighboring State Flow status convention.
|
|
@@ -150,6 +150,8 @@ The tool never blocks future calls. Whether to continue belongs to the agent and
|
|
|
150
150
|
|
|
151
151
|
`N` is monotonic within the current extension instance and advances only once when a turn arms its deferred successor. Repeated `grow_loop` calls before that turn ends retain `N`, replace the pending delay, and report that the iteration was already scheduled. Active status clears when the scheduled agent run fully settles without arming a successor, so automatic retry or compaction recovery does not produce a false idle state. There is no `grow-loop stopped` or `grow-loop paused` status; absence of Grow Loop status means the runtime rhythm is no longer active.
|
|
152
152
|
|
|
153
|
+
With the optional `pi-telegram` extension installed, the Telegram Status screen mirrors the same rhythm and stays hidden whenever the terminal status is hidden: `Grow Loop: #N · waiting`, `#N · 2.4s` during the countdown, and `#N · running` while the loop-scheduled turn is active. The mirror reads the same runtime state, fails open without the transport, and changes no scheduling behavior.
|
|
154
|
+
|
|
153
155
|
## Interruption Model
|
|
154
156
|
|
|
155
157
|
Any user prompt except the scheduler's exact expected continuation prompt exits the active runtime rhythm. This includes operator input delivered through Telegram, RPC bridges, or other extensions:
|
|
@@ -14,16 +14,47 @@ const DEFAULT_FOLLOW_UP_DELAY_MS = 3000;
|
|
|
14
14
|
const DEFAULT_COUNTDOWN_TICK_MS = 100;
|
|
15
15
|
const MIN_AFTER_SECONDS = 3;
|
|
16
16
|
const MAX_AFTER_SECONDS = 3600;
|
|
17
|
+
const TELEGRAM_STATUS_IMPORT_SPECIFIERS = [
|
|
18
|
+
"@llblab/pi-telegram/status",
|
|
19
|
+
new URL("../pi-telegram/api/status.ts", import.meta.url).href,
|
|
20
|
+
];
|
|
17
21
|
|
|
18
22
|
type Timer = ReturnType<typeof setTimeout> & { unref?: () => void };
|
|
19
23
|
type PendingIteration = {
|
|
20
24
|
interval: Timer;
|
|
21
25
|
timeout?: Timer;
|
|
26
|
+
countdownStartedAt?: number;
|
|
27
|
+
countdownDelayMs?: number;
|
|
22
28
|
};
|
|
23
29
|
|
|
30
|
+
export interface GrowLoopTelegramProgress {
|
|
31
|
+
iteration: number;
|
|
32
|
+
state: "waiting" | "countdown" | "running";
|
|
33
|
+
remainingSeconds?: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface GrowLoopTelegramStatusLine {
|
|
37
|
+
label: string;
|
|
38
|
+
value: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type GrowLoopTelegramStatusProvider = () => GrowLoopTelegramStatusLine | undefined;
|
|
42
|
+
export type GrowLoopTelegramStatusRegistrar = (
|
|
43
|
+
provider: GrowLoopTelegramStatusProvider,
|
|
44
|
+
) => (() => void) | undefined;
|
|
45
|
+
|
|
46
|
+
interface TelegramStatusLineModule {
|
|
47
|
+
registerTelegramStatusLineProvider?: (
|
|
48
|
+
provider: GrowLoopTelegramStatusProvider,
|
|
49
|
+
options: { id: string },
|
|
50
|
+
) => () => void;
|
|
51
|
+
}
|
|
52
|
+
|
|
24
53
|
type GrowLoopOptions = {
|
|
25
54
|
followUpDelayMs?: number;
|
|
26
55
|
countdownTickMs?: number;
|
|
56
|
+
/** Injection seam for the optional pi-telegram status line; defaults to the public pi-telegram membrane. */
|
|
57
|
+
registerTelegramStatusLine?: GrowLoopTelegramStatusRegistrar;
|
|
27
58
|
};
|
|
28
59
|
|
|
29
60
|
export function buildGrowLoopPrompt(): string {
|
|
@@ -39,6 +70,28 @@ export function getExistingExtensionSkillPaths(extensionUrl: string): string[] {
|
|
|
39
70
|
return existsSync(skillsDir) ? [skillsDir] : [];
|
|
40
71
|
}
|
|
41
72
|
|
|
73
|
+
export function formatGrowLoopTelegramValue(progress: GrowLoopTelegramProgress): string {
|
|
74
|
+
if (progress.state === "countdown") return `#${progress.iteration} · ${(progress.remainingSeconds ?? 0).toFixed(1)}s`;
|
|
75
|
+
if (progress.state === "running") return `#${progress.iteration} · running`;
|
|
76
|
+
return `#${progress.iteration} · waiting`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function registerGrowLoopTelegramStatus(
|
|
80
|
+
provider: GrowLoopTelegramStatusProvider,
|
|
81
|
+
): Promise<(() => void) | undefined> {
|
|
82
|
+
for (const specifier of TELEGRAM_STATUS_IMPORT_SPECIFIERS) {
|
|
83
|
+
try {
|
|
84
|
+
const imported = (await import(specifier)) as TelegramStatusLineModule;
|
|
85
|
+
if (typeof imported.registerTelegramStatusLineProvider === "function") {
|
|
86
|
+
return imported.registerTelegramStatusLineProvider(provider, { id: "@llblab/pi-grow-loop" });
|
|
87
|
+
}
|
|
88
|
+
} catch {
|
|
89
|
+
// pi-telegram is optional; its absence only disables the Telegram status line.
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
|
|
42
95
|
function statusCountdown(ctx: ExtensionContext, seconds: number) {
|
|
43
96
|
const theme = ctx.ui.theme;
|
|
44
97
|
ctx.ui.setStatus(
|
|
@@ -82,20 +135,21 @@ function scheduleIteration(
|
|
|
82
135
|
iteration: number,
|
|
83
136
|
clearPending: () => void,
|
|
84
137
|
expectOwnPrompt: () => void,
|
|
85
|
-
options: Required<GrowLoopOptions
|
|
138
|
+
options: Required<Pick<GrowLoopOptions, "followUpDelayMs" | "countdownTickMs">>,
|
|
86
139
|
): PendingIteration {
|
|
87
|
-
let countdownStartedAt: number | undefined;
|
|
88
140
|
statusDeferred(ctx, iteration);
|
|
89
141
|
const pending = {} as PendingIteration;
|
|
90
142
|
pending.interval = setInterval(() => {
|
|
91
|
-
if (
|
|
143
|
+
if (pending.countdownStartedAt === undefined) {
|
|
92
144
|
if (!ctx.isIdle() || ctx.hasPendingMessages()) return;
|
|
93
|
-
countdownStartedAt = Date.now();
|
|
145
|
+
pending.countdownStartedAt = Date.now();
|
|
146
|
+
pending.countdownDelayMs = options.followUpDelayMs;
|
|
94
147
|
statusCountdown(ctx, options.followUpDelayMs / 1000);
|
|
95
148
|
pending.timeout = setTimeout(() => {
|
|
96
149
|
pending.timeout = undefined;
|
|
97
150
|
if (!ctx.isIdle() || ctx.hasPendingMessages()) {
|
|
98
|
-
countdownStartedAt = undefined;
|
|
151
|
+
pending.countdownStartedAt = undefined;
|
|
152
|
+
pending.countdownDelayMs = undefined;
|
|
99
153
|
statusDeferred(ctx, iteration);
|
|
100
154
|
return;
|
|
101
155
|
}
|
|
@@ -105,7 +159,7 @@ function scheduleIteration(
|
|
|
105
159
|
pending.timeout.unref?.();
|
|
106
160
|
return;
|
|
107
161
|
}
|
|
108
|
-
const elapsed = Date.now() - countdownStartedAt;
|
|
162
|
+
const elapsed = Date.now() - pending.countdownStartedAt;
|
|
109
163
|
const remainingMs = Math.max(options.followUpDelayMs - elapsed, 0);
|
|
110
164
|
if (remainingMs > 0) statusCountdown(ctx, remainingMs / 1000);
|
|
111
165
|
}, options.countdownTickMs) as Timer;
|
|
@@ -122,23 +176,63 @@ export default function growLoopExtension(
|
|
|
122
176
|
partialOptions.followUpDelayMs ?? DEFAULT_FOLLOW_UP_DELAY_MS,
|
|
123
177
|
countdownTickMs:
|
|
124
178
|
partialOptions.countdownTickMs ?? DEFAULT_COUNTDOWN_TICK_MS,
|
|
179
|
+
registerTelegramStatusLine: partialOptions.registerTelegramStatusLine,
|
|
125
180
|
};
|
|
126
181
|
let iteration = 0;
|
|
127
182
|
let lastCtx: ExtensionContext | undefined;
|
|
128
183
|
let pendingIteration: PendingIteration | undefined;
|
|
129
184
|
let ownPromptPending = false;
|
|
130
185
|
let scheduledThisTurn = false;
|
|
186
|
+
let runningIteration: number | undefined;
|
|
187
|
+
let unregisterTelegramStatus: (() => void) | undefined;
|
|
188
|
+
let telegramRegistration: Promise<void> | undefined;
|
|
189
|
+
let telegramGeneration = 0;
|
|
131
190
|
const clearPending = () => {
|
|
132
191
|
if (!pendingIteration) return;
|
|
133
192
|
if (pendingIteration.timeout) clearTimeout(pendingIteration.timeout);
|
|
134
193
|
clearInterval(pendingIteration.interval);
|
|
135
194
|
pendingIteration = undefined;
|
|
136
195
|
};
|
|
196
|
+
const telegramStatusProvider = (): GrowLoopTelegramStatusLine | undefined => {
|
|
197
|
+
if (pendingIteration) {
|
|
198
|
+
if (pendingIteration.countdownStartedAt === undefined) {
|
|
199
|
+
return { label: "Grow Loop", value: formatGrowLoopTelegramValue({ iteration, state: "waiting" }) };
|
|
200
|
+
}
|
|
201
|
+
const elapsed = Date.now() - pendingIteration.countdownStartedAt;
|
|
202
|
+
const remainingSeconds = Math.max((pendingIteration.countdownDelayMs ?? 0) - elapsed, 0) / 1000;
|
|
203
|
+
return { label: "Grow Loop", value: formatGrowLoopTelegramValue({ iteration, state: "countdown", remainingSeconds }) };
|
|
204
|
+
}
|
|
205
|
+
if (runningIteration !== undefined) {
|
|
206
|
+
return { label: "Grow Loop", value: formatGrowLoopTelegramValue({ iteration: runningIteration, state: "running" }) };
|
|
207
|
+
}
|
|
208
|
+
return undefined;
|
|
209
|
+
};
|
|
210
|
+
const ensureTelegramStatusRegistered = () => {
|
|
211
|
+
if (unregisterTelegramStatus || telegramRegistration) return;
|
|
212
|
+
if (options.registerTelegramStatusLine) {
|
|
213
|
+
unregisterTelegramStatus = options.registerTelegramStatusLine(telegramStatusProvider) ?? undefined;
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const generation = telegramGeneration;
|
|
217
|
+
telegramRegistration = registerGrowLoopTelegramStatus(telegramStatusProvider)
|
|
218
|
+
.then((unregister) => {
|
|
219
|
+
if (generation !== telegramGeneration) {
|
|
220
|
+
unregister?.();
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
unregisterTelegramStatus = unregister;
|
|
224
|
+
})
|
|
225
|
+
.finally(() => {
|
|
226
|
+
if (generation === telegramGeneration) telegramRegistration = undefined;
|
|
227
|
+
});
|
|
228
|
+
};
|
|
137
229
|
const hideLoopStatus = (ctx: ExtensionContext) => {
|
|
138
230
|
ownPromptPending = false;
|
|
231
|
+
runningIteration = undefined;
|
|
139
232
|
clearPending();
|
|
140
233
|
ctx.ui.setStatus(STATUS_KEY, undefined);
|
|
141
234
|
};
|
|
235
|
+
ensureTelegramStatusRegistered();
|
|
142
236
|
pi.on("resources_discover", async () => {
|
|
143
237
|
const skillPaths = getExistingExtensionSkillPaths(import.meta.url);
|
|
144
238
|
if (skillPaths.length === 0) return;
|
|
@@ -147,12 +241,23 @@ export default function growLoopExtension(
|
|
|
147
241
|
pi.on("session_shutdown", async () => {
|
|
148
242
|
ownPromptPending = false;
|
|
149
243
|
scheduledThisTurn = false;
|
|
244
|
+
runningIteration = undefined;
|
|
150
245
|
clearPending();
|
|
246
|
+
telegramGeneration += 1;
|
|
247
|
+
unregisterTelegramStatus?.();
|
|
248
|
+
unregisterTelegramStatus = undefined;
|
|
249
|
+
telegramRegistration = undefined;
|
|
151
250
|
lastCtx?.ui.setStatus(STATUS_KEY, undefined);
|
|
152
251
|
});
|
|
252
|
+
pi.on("session_start", async () => {
|
|
253
|
+
ensureTelegramStatusRegistered();
|
|
254
|
+
});
|
|
153
255
|
pi.on("agent_settled", async (_event, ctx) => {
|
|
154
256
|
lastCtx = ctx;
|
|
155
|
-
if (!pendingIteration)
|
|
257
|
+
if (!pendingIteration) {
|
|
258
|
+
runningIteration = undefined;
|
|
259
|
+
ctx.ui.setStatus(STATUS_KEY, undefined);
|
|
260
|
+
}
|
|
156
261
|
});
|
|
157
262
|
pi.on("input", async (event, ctx) => {
|
|
158
263
|
lastCtx = ctx;
|
|
@@ -194,6 +299,7 @@ export default function growLoopExtension(
|
|
|
194
299
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
195
300
|
lastCtx = ctx;
|
|
196
301
|
ownPromptPending = false;
|
|
302
|
+
runningIteration = undefined;
|
|
197
303
|
clearPending();
|
|
198
304
|
const isReschedule = scheduledThisTurn;
|
|
199
305
|
if (!isReschedule) {
|
|
@@ -212,8 +318,9 @@ export default function growLoopExtension(
|
|
|
212
318
|
clearPending,
|
|
213
319
|
() => {
|
|
214
320
|
ownPromptPending = true;
|
|
321
|
+
runningIteration = nextIteration;
|
|
215
322
|
},
|
|
216
|
-
{
|
|
323
|
+
{ followUpDelayMs: delayMs, countdownTickMs: options.countdownTickMs },
|
|
217
324
|
);
|
|
218
325
|
return {
|
|
219
326
|
content: [
|
|
@@ -24,15 +24,15 @@
|
|
|
24
24
|
- Bind every loaded scope and active Pi branch/checkpoint to the corresponding State Flow Git revision. Resume and tree restoration must recover branch-correct runtime config and semantic layers from that revision without blindly importing Git `HEAD`; reading an older revision must use object-level Git reads and never reset or check out the shared repository worktree.
|
|
25
25
|
- Every accepted semantic change, including session-only and response-only changes, atomically updates affected checkpoint/tail pairs and temporal metadata and creates one immediate local Git commit when Git is available; each commit stages the complete non-ignored worktree delta (`git add -A` semantics, including manual deletions) before overlaying exact prepared State Flow outputs, respects `.gitignore`, keeps State Flow-owned active files under compare-and-swap protection, and synchronizes the caller-visible index to the committed tree. `remotePublication` is branch runtime policy: `turn-end` queues only the newest accepted target for asynchronous non-interactive push, `off` remains local-only, and `transition` preserves synchronous legacy behavior. Queue state is operational metadata beneath the Git common directory, not semantic history; use exact targets, destination identity, symlink-safe atomic CAS, descendant-only coalescing, cross-process worker leases, restart recovery, and truthful failure diagnostics. Remote failure never rolls back accepted state or regenerates an answer. A repository with no remote is intentionally local-only; only Git executable `ENOENT` authorizes file-only mode.
|
|
26
26
|
- Register `patch_state` as the sole model-authored semantic mutation protocol. It accepts optional fixed `global`, `cwd`, and `session` semantic patches plus optional `final:true`; require at least one scope or `final:true`, reject unknown fields, `final:false`, empty supplied scopes, material no-ops, and the retired `{scope, patch}` / `unchanged` grammar. Validate every supplied scope against one causal basis and publish it all-or-nothing with one identity, temporal boundary, and durable cohort. Never accept model-authored `response`.
|
|
27
|
-
- Treat every semantic `patch_state` as a strict inference barrier. Give it sequential execution mode, inspect Pi's synchronized current assistant message during tool preflight, require exactly one `patch_state` call in that response, and block every sibling tool call before execution. Every enabled iteration begins terminal-ineligible; only a successful call containing `final:true` latches eligibility for the next accepted `turn_end`, without stopping later reasoning, tools, or patches. If
|
|
28
|
-
- Reconcile `response` only from the actually accepted ordinary assistant answer at `turn_end`; it remains runtime-owned. State Flow has no terminal HTML-comment mutation protocol and does not parse generic service comments.
|
|
27
|
+
- Treat every semantic `patch_state` as a strict inference barrier. Give it sequential execution mode, inspect Pi's synchronized current assistant message during tool preflight, require exactly one `patch_state` call in that response, and block every sibling tool call before execution. Every enabled iteration begins terminal-ineligible; only a successful call containing `final:true` latches eligibility for the next accepted `turn_end`, without stopping later reasoning, tools, or patches. If a terminal draft ends before eligibility, preserve it as the runtime-owned response at that `turn_end` and start at most two same-run fallback turns whose only purpose is the `final:true` patch; an eligible draft whose final validation fails after a later acquisition follows the same path. Fallback turns never enter `response`, and their only instruction is to apply `patch_state` with any durable changes and `final:true`, or `{final:true}` alone. A successful fallback closes resolution with the preserved answer intact; two failed fallbacks close the iteration with the preserved answer and current state plus one bounded warning and a finalization diagnostic. Failed patch calls do not consume fallback turns. A subsequent legal patch remains possible, and only an accepted ordinary answer is reconciled.
|
|
28
|
+
- Reconcile `response` only from the actually accepted ordinary assistant answer at `turn_end`; it remains runtime-owned. State Flow has no terminal HTML-comment mutation protocol and does not parse generic service comments. The first terminal draft that cannot be reconciled is preserved as the iteration response rather than discarded; fallback turns during pending resolution never reach `response`. Other extensions retain ownership of their own comments and output handling.
|
|
29
29
|
- Treat terminal state as a decision-relevant handoff, not narration: retain source-addressed reusable operational knowledge in `artifacts`; compile stable requirements, confirmed decisions, rejected approaches, and interface commitments into `contract`; retain observations, validation, failures, current domain state, unresolved work, interaction consequences, and exact continuation in `working`. Preserve relevant completed prerequisites and verified outcomes while removing obsolete progress narration; reconcile only information affected by the run and relevant existing commitments, not every scope or repository surface.
|
|
30
30
|
- Preserve active constraints, unresolved questions, consequential negative results, and the next discriminating check before compression. Distinguish observations, user requirements, assistant decisions, and hypotheses; do not promote assistant conclusions to user requirements. Retain useful source locators and validity conditions for consequential facts without mandatory per-value metadata. Keep rejection reasons and reconsideration conditions. Reconcile contradictions through evidence or user clarification instead of silently overwriting established constraints or observations; retain unresolved conflicts and decision-relevant hypotheses as uncertain. These are protocol obligations, not deterministic semantic validation gates.
|
|
31
31
|
- Treat `working` as last observations, not a live workspace. Revalidate volatile facts before consequential actions; after interruption or branch navigation inspect relevant external effects before repeating operations. Failed state commits and restored memory do not undo tool effects. Missing evidence proves neither success nor absence of effects: retain uncertainty and the next check. Keep revalidation targeted, without action ledgers or runtime freshness/rollback guarantees.
|
|
32
32
|
- Treat each successful `SKILL.md` read as CWD artifact acquisition using the finalized tool-execution arguments after mutable interception: require a non-empty compiler output in the next `patch_state` call under `cwd.artifacts[exactReadPath]` with `description`, `kind: "skill"`, and a flexible non-empty `compilation` object; hash the executed source bytes and record runtime-owned `sourceHash` plus `skill-artifact-v1` `compilerRevision` in the CWD provenance registry; reject missing, unhashable, malformed, or forged freshness data; replace the complete prior Skill artifact and its provenance entry on refresh so obsolete evidence cannot survive. `contract.compiled_skills` is retired and rejected; migrate useful legacy entries into artifacts while preserving behavior and marking fallback hashes unverified when the source is unavailable.
|
|
33
33
|
- Make the model audit and optimize complete state at every handoff: reorganize inefficient structure, merge fragmented facts, compress history into conclusions, and delete stale, completed, redundant, or low-value keys while preserving active commitments and evidence.
|
|
34
34
|
- Accept omitted semantic fields inside each supplied scope patch, but reject empty supplied scopes; when no state change is needed require explicit `{final:true}` without inventing bookkeeping. This final-only call changes only ephemeral terminal eligibility and creates no semantic transition, identity, temporal step, or Git commit. Always require an accepted non-empty answer, which runtime owns as session `response`. A changed response is a semantic transition; identical complete semantic state finalizes lifecycle without a patch, identity, or temporal step. Semantic usefulness and optimization remain protocol-owned because deterministic validation cannot prove them.
|
|
35
|
-
- Agent-level opt-in `logging` appends local JSONL diagnostics for rejected `patch_state` attempts and
|
|
35
|
+
- Agent-level opt-in `logging` appends local JSONL diagnostics for rejected `patch_state` attempts and preserved drafts plus fallback turns while turn resolution remains pending. Every rejected call retains its exact attempted arguments plus the precise error and, when available, tool identity, call id, resolution attempt, and terminal-eligibility state; successful patches are never logged. Preserve exact text blocks only where useful, reduce other blocks to structural identity, never duplicate reasoning bodies, and keep logging outside semantic state, scope metadata, checkpoints, and publication. Logging failure emits at most one warning and never changes resolution, enablement, or accepted state.
|
|
36
36
|
- Recursively materialize patches immediately; empty objects preserve, nested object-key `null` deletes, and `null` anywhere in semantic state including arrays is invalid. This prohibition does not apply to runtime envelopes such as an origin's null parent. Persist runtime-normalized replay patches that exactly reproduce accepted state, including complete artifact replacement; runtime-owned provenance is stored in scope `meta.json` and is not part of semantic replay.
|
|
37
37
|
- Do not impose project schemas, state or patch byte caps, dynamic growth pressure, observation envelopes, action authorization, action ledgers, or state-size limits.
|
|
38
38
|
- Rotate the turn-stable specification on every user-initiated run, while runtime-triggered turn-resolution continuation remains inside that same run. Keep user-controlled specification text at user authority: never interpolate it into the system prompt; repeat it only in synthetic user runtime context. Treat materialized state in that message as fallible assistant-produced data whose transport role does not elevate it into user instructions.
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
- `/state-flow-stop` must persist only the current session/branch's `config.enabled = false` and necessary runtime provenance, preserving all semantic checkpoints/tails and creating no semantic transition. It must retain a same-physical-session bounded passive context handoff across reload, resume, and tree restoration so disabling semantics never re-exposes pre-stop raw history. Active restart replaces passive mode but uses that bounded boundary for its one migration run; new and forked physical sessions inherit neither projection. It does not rewrite agent-level `autoStart` or change its policy for future new sessions.
|
|
44
44
|
- Keep the injected runtime protocol compact and normative; put rationale and extended explanation in README rather than the model prompt. Never parse or strip State Flow HTML comments; they are ordinary historical text, while foreign comment handling remains owned by other extensions.
|
|
45
45
|
- Do not claim strict boundedness for state, the current run trajectory, the turn specification, or the external full trace.
|
|
46
|
-
- Remain extension-agnostic:
|
|
46
|
+
- Remain extension-agnostic in core semantics, storage, and inference: core modules never import, name, special-case, or encode policy for another extension or transport. One optional leaf presentation adapter (`lib/telegram.ts`) may import public `pi-telegram` membranes to mirror status and expose the same start/stop affordances already owned by `state-flow-start` and `state-flow-stop`; it must fail open when the transport is absent or its registry is unready and must never alter core behavior.
|
|
47
47
|
- Activate State Flow model tools only while enabled on the selected branch; preserve every unrelated active tool when toggling them. Keep mutation confined to `patch_state` and historical observation read-only.
|
|
48
48
|
- Keep `.github/workflows/release.yml` as the sole version-tag release owner: it validates immutable tag identity, publishes through npm Trusted Publisher with provenance, verifies the public package, and only then creates the GitHub Release. Keep package, lockfile, tag, and changelog versions aligned; never add a long-lived npm token fallback.
|
|
49
49
|
- Run `npm run validate` after retained code changes.
|
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Completed release work belongs in [CHANGELOG.md](CHANGELOG.md). No implementation item remains open for 0.8.0.
|
|
4
4
|
|
|
5
|
+
## Candidate evolution
|
|
6
|
+
|
|
7
|
+
- [ ] **Read-only global bootstrap layer:** Project the existing global materialization into enabled and non-enabled sessions by default, so even one-shot work starts carrying established cross-project facts, preferences, routing, and conventions. The global scope is the highest-value, lowest-cost half of the memory, while writing carries the protocol and curation tax; making the read side unconditional gives continuity without enabling mutation.
|
|
8
|
+
- Boundary: project the existing durable global materialization only. No writes, transitions, patches, barriers, session/CWD initialization, or temporal-history changes. Model tools and the full protocol remain opt-in behind explicit start, and the bootstrap adds no second semantic mode.
|
|
9
|
+
- Open questions: whether deferred Markdown freshness discovery must run ahead of the first inference, token cost of a stable global prefix, behavior under untrusted project contexts, and naming distinct from the existing bootstrap-run concept.
|
|
10
|
+
- Status: candidate, not scheduled. Do not start without a dedicated release contract.
|
|
11
|
+
|
|
5
12
|
## Deferred host integration
|
|
6
13
|
|
|
7
14
|
- [ ] **Native default session continuation:** Integrate the existing read-only recommendation, exact-selection, and rehydration contracts before Pi creates `SessionManager`, while preserving explicit new/resume and native-picker precedence, truthful notices, and cross-process session ownership. Pi 0.84.4 exposes no suitable pre-session resolver hook, so this requires upstream support or an SDK/launcher integration. Reverify the host API before taking the item; it is not a release dependency.
|
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
> Each release keeps at most 8 outcome records of at most 512 characters.
|
|
4
4
|
|
|
5
|
+
## 0.9.0: Telegram status surface
|
|
6
|
+
|
|
7
|
+
- `Interop`: Added an optional fail-open `pi-telegram` presentation adapter. With the transport installed, the status screen shows `State Flow: on · step #N` while enabled, and the inline main menu places a `🌀 State Flow: #N` or `⚫️ State Flow: off` section button directly before Settings.
|
|
8
|
+
- `Control`: The section submenu exposes the branch lifecycle already owned by the commands: Start, Stop, Refresh, and Cancel start while a start deferred behind an active run is pending. A busy start waits for the current run to settle, Stop applies immediately, and the adapter calls the same start/stop functions as `/state-flow-start` and `/state-flow-stop`.
|
|
9
|
+
- `Compatibility`: Registration fails open when pi-telegram is absent or its section registry is not ready, retries on session start, and disposes on session shutdown. Core semantics, storage, inference, and hosts without pi-telegram are unchanged.
|
|
10
|
+
|
|
11
|
+
## 0.8.1: Preserved answers and bounded fallback resolution
|
|
12
|
+
|
|
13
|
+
- `Resolution`: A terminal draft is never discarded. A draft ending before eligibility — or an eligible draft whose final validation fails for a later acquisition — is preserved as the runtime-owned response at `turn_end`, then at most two same-run fallback turns exist only to obtain the `final:true` patch. Fallback turns never enter `response`; success commits their patches with the preserved answer intact, and exhaustion closes the iteration with the preserved answer, current state, one bounded warning, and a finalization diagnostic.
|
|
14
|
+
|
|
5
15
|
## 0.8.0: Atomic cohorts and final eligibility
|
|
6
16
|
|
|
7
17
|
- `Protocol`: Replaced the single-scope PATCH and UNCHANGED forms with one canonical `patch_state({global?, cwd?, session?, final?})` call. Supplied scopes validate against one causal basis and publish as one atomic transition with a single identity, temporal boundary, durable cohort, and inference barrier; unknown fields, `final:false`, empty scopes, and material no-ops fail closed.
|
|
@@ -66,6 +66,8 @@ Stopping changes the current session/branch configuration, preserves semantic ch
|
|
|
66
66
|
|
|
67
67
|
The compact status is an accent `state-flow` and dim `#<step>`. Detailed status distinguishes runtime config/meta from semantic materialization, reports scope keys, the selected temporal head and runtime revision, available hot offsets, per-scope retained patch tails, artifact counts, stale Markdown reasons, and pending publication. Retained tails may include inherited pre-origin records, so their counts are not active history depth. It labels global/CWD/session/effective state without dumping source bodies. Failed inspection means unavailable evidence, never a falsely clean count.
|
|
68
68
|
|
|
69
|
+
When the optional `pi-telegram` extension is installed, State Flow mirrors the same identity there: the Status screen shows `State Flow: on · step #N` while enabled, and the inline main menu places a `🌀 State Flow: #N` or `⚫️ State Flow: off` section button directly before Settings. The button opens a compact control submenu for the current session branch: `Start` or `Stop`, `Refresh`, and `Cancel start` while a start deferred behind an active run is pending. Starting while a run is active waits for that run to settle; stopping applies immediately. Registration fails open when the transport is absent, retries when the section registry becomes ready, and disposes on session shutdown; the `/state-flow-*` commands remain the authority and nothing changes for hosts without `pi-telegram`.
|
|
70
|
+
|
|
69
71
|
## State, scope, and time
|
|
70
72
|
|
|
71
73
|
Every scope has exactly the same semantic shape:
|
|
@@ -219,7 +221,7 @@ patch_state({...?, final:true}) → atomic accepted state + terminal eligibility
|
|
|
219
221
|
patch_state({final:true}) → terminal eligibility only, with no semantic transition
|
|
220
222
|
```
|
|
221
223
|
|
|
222
|
-
Every enabled iteration begins terminal-ineligible. `final:true` latches eligibility for the next accepted `turn_end`; it does not terminate the turn, stop tools or reasoning, freeze state, or prevent later `patch_state` calls.
|
|
224
|
+
Every enabled iteration begins terminal-ineligible. `final:true` latches eligibility for the next accepted `turn_end`; it does not terminate the turn, stop tools or reasoning, freeze state, or prevent later `patch_state` calls. A terminal draft that ends before eligibility, or an eligible draft whose final validation fails after a later acquisition, is preserved: it is reconciled as the runtime-owned `response` at that `turn_end` instead of being discarded, and State Flow starts at most two same-run fallback turns whose only purpose is to obtain the `final:true` patch. Fallback turns are never the response. A successful `final:true` during a fallback commits its patches and closes resolution with the preserved answer intact; when both fallbacks pass without it, the iteration closes with the preserved answer and current state, plus one bounded warning and a finalization diagnostic. Failed patch calls do not consume fallback turns. Only an accepted answer reaches `response`; aborted, length-limited, and provider-error endings never do.
|
|
223
225
|
|
|
224
226
|
A semantic patch remains an inference barrier. The complete scope cohort is validated, durably accepted, and rematerialized before further reasoning. Empty supplied scopes and materially no-op scope cohorts are rejected; omit unchanged scopes and use `{final:true}` alone when no semantic update is needed. `response` is never accepted from the model. The final answer is ordinary assistant output, not a state transport. State Flow has no terminal `state_flow` HTML-comment protocol and does not parse generic service comments. Historical `state_flow` text is ordinary text. Other extensions retain ownership of their own comments such as `telegram_button`. Never put secrets in state.
|
|
225
227
|
|
|
@@ -294,7 +296,7 @@ npm install
|
|
|
294
296
|
npm run validate
|
|
295
297
|
```
|
|
296
298
|
|
|
297
|
-
Validation runs TypeScript checks, automated tests, and an import smoke check. Temporal algebra tests compare all available offsets with independently retained test snapshots through dense/sparse repeated folding, shared transitions, deletion overlays, no-ops, and forks. Temporary-repository tests also cover explicit migration, ten committed sparse transitions with folding and all hot offsets, cold Git scope recovery, and historical symlink rejection. Storage-level tests also prove atomic runtime/semantic publication, config-only stop without semantic history, runtime identity and lineage validation, self-reference resolution across unrelated commits, and exact accepted-commit retry. Real Pi tests also exercise configured automatic activation, deferred source discovery, strict draft → resolution → accepted-answer continuation, temporal barriers and next-inference state, lazy runtime offset-one reads, unchanged shared-stream inheritance, old-branch stop/resume, and bounded passive stop/restart projection. The complete twenty-case temporal acceptance audit is documented in the property map; the canonical backlog identifies any subsequent open work.
|
|
299
|
+
Validation runs TypeScript checks, automated tests, and an import smoke check. Temporal algebra tests compare all available offsets with independently retained test snapshots through dense/sparse repeated folding, shared transitions, deletion overlays, no-ops, and forks. Temporary-repository tests also cover explicit migration, ten committed sparse transitions with folding and all hot offsets, cold Git scope recovery, and historical symlink rejection. Storage-level tests also prove atomic runtime/semantic publication, config-only stop without semantic history, runtime identity and lineage validation, self-reference resolution across unrelated commits, and exact accepted-commit retry. Real Pi tests also exercise configured automatic activation, deferred source discovery, strict draft → preserved answer and fallback resolution → accepted-answer continuation, temporal barriers and next-inference state, lazy runtime offset-one reads, unchanged shared-stream inheritance, old-branch stop/resume, and bounded passive stop/restart projection. The complete twenty-case temporal acceptance audit is documented in the property map; the canonical backlog identifies any subsequent open work.
|
|
298
300
|
|
|
299
301
|
- [Canonical open work](BACKLOG.md)
|
|
300
302
|
- [Delivery history](CHANGELOG.md)
|
|
@@ -19,7 +19,7 @@ The extension owns durable memory while enabled. Global semantic memory is alway
|
|
|
19
19
|
- `memory`: external promotion records and memory diagnostics.
|
|
20
20
|
- `continuation`: native-header discovery, runtime-provenance inspection, deterministic recommendation, and host startup precedence.
|
|
21
21
|
- `publication`: remote policy, durable CAS queue/store, cross-process leases, and asynchronous worker lifecycle.
|
|
22
|
-
- `status`, `extension`: operator projection and Pi adapter wiring.
|
|
22
|
+
- `status`, `telegram`, `extension`: operator projection, the optional fail-open pi-telegram presentation adapter, and Pi adapter wiring.
|
|
23
23
|
|
|
24
24
|
## Semantic state
|
|
25
25
|
|
|
@@ -71,7 +71,7 @@ A final-only `patch_state({"final":true})` call changes only ephemeral terminal
|
|
|
71
71
|
|
|
72
72
|
`read_state` reads one cached effective or scoped projection at offsets zero through seven. It never publishes or advances history.
|
|
73
73
|
|
|
74
|
-
Every enabled assistant iteration starts terminal-ineligible. Only a successful `patch_state` call containing `final:true` latches eligibility for the next accepted `turn_end`; the call may atomically include global, CWD, and session patches. Eligibility does not stop later reasoning, tools, or patches. If terminal prose arrives before eligibility, State Flow
|
|
74
|
+
Every enabled assistant iteration starts terminal-ineligible. Only a successful `patch_state` call containing `final:true` latches eligibility for the next accepted `turn_end`; the call may atomically include global, CWD, and session patches. Eligibility does not stop later reasoning, tools, or patches. If terminal prose arrives before eligibility, State Flow preserves that draft and reconciles it into runtime-owned `response` at `turn_end`, then starts at most two same-run fallback turns whose only purpose is the `final:true` patch. The same path covers an eligible draft whose final validation fails after a later acquisition. Fallback turns never become the response: a successful `final:true` commits its patches and closes resolution with the preserved answer intact, while two failed fallbacks close the iteration with the preserved answer and current state plus one bounded warning and a finalization diagnostic. Failed patch calls do not consume the budget. A following legal patch remains possible, and only an accepted ordinary answer is reconciled into runtime-owned `response` at `turn_end`. State Flow no longer parses `state_flow` or generic HTML comments; historical comments are ordinary text and other extensions retain their own comment handling.
|
|
75
75
|
|
|
76
76
|
## Lifecycle planes
|
|
77
77
|
|
|
@@ -199,7 +199,9 @@ Unknown keys fail loading. State Flow memory ownership and global availability a
|
|
|
199
199
|
|
|
200
200
|
`/state-flow-status` reports branch mode, runtime revision, temporal head/history depth, scope keys, patch tails, artifact freshness, memory-bearing scopes, external-promotion summaries, remote policy/queue state, and pending publication. Unavailable materialization is reported as unavailable, never fabricated as empty. Artifact source bodies are not read for status.
|
|
201
201
|
|
|
202
|
-
|
|
202
|
+
With `pi-telegram` installed, the optional `telegram` adapter mirrors the same identity in Telegram: the Status screen row shows `State Flow: on · step #N` while enabled, and the main-menu section button before Settings opens the branch start/stop submenu. Registration is fail-open and retried on session start, disposal happens on session shutdown, the adapter reads the same snapshot as the compact terminal status, and its start/stop actions call the same functions as the commands.
|
|
203
|
+
|
|
204
|
+
Opt-in `logging` appends local JSONL diagnostics for rejected `patch_state` calls and preserved drafts plus fallback turns while turn resolution is pending. A rejected call retains its exact attempted arguments, the precise error, and, when available, tool identity, call id, resolution attempt, and terminal-eligibility state; accepted patches are never logged. Records preserve useful exact text blocks and reduce other blocks to structural identity without duplicating reasoning. They are never semantic state, scope `meta.json`, Pi checkpoints, or repository input. Write failure changes no resolution, enablement, or accepted state and reports at most one bounded local warning.
|
|
203
205
|
|
|
204
206
|
## Validation boundaries
|
|
205
207
|
|
|
@@ -17,8 +17,8 @@ The numbering below follows the twenty required tests in the temporal design cor
|
|
|
17
17
|
11. **Barrier shifts current to offset 1:** `tests/integration.test.ts` — “real Pi patch_state barriers rematerialize every scope before the next inference” observes the predecessor immediately after a barrier.
|
|
18
18
|
12. **Next inference sees new current state:** The same real-Pi test inspects actual model-input projections after session, CWD, and global barriers. “real Pi reads prior scoped state lazily after a barrier and rejects offset eight without a transition” adds model-tool access to the predecessor.
|
|
19
19
|
13. **No automatic old full-state duplication:** `tests/context.test.ts` — “projects only the latest seven compact accepted transitions” rejects full-state records in transition context. The real-Pi barrier test requires exactly one current runtime projection per inference. Explicitly requested history remains ordinary tool-result trajectory, not eager snapshot injection.
|
|
20
|
-
14. **Accepted response changes are transitions:** `tests/
|
|
21
|
-
15. **Final-only eligibility creates no fake history:** `tests/extension.test.ts` — “
|
|
20
|
+
14. **Accepted response changes are transitions:** `tests/extension.test.ts` — “the preserved answer stands after two fallback turns without final:true” and `tests/skills.test.ts` — “a late Skill obligation that outlives the fallback budget keeps the preserved answer” verify that a primary draft without a reconciled `final:true` is preserved as the runtime-owned response, fallback turns never enter `response`, and an exhausted fallback budget keeps the preserved answer with its current state. `tests/integration.test.ts` — “real Pi keeps the preserved answer after the fallback budget” repeats the closure through the native Pi turn loop.
|
|
21
|
+
15. **Final-only eligibility creates no fake history:** `tests/extension.test.ts` — “a primary draft without eligibility is preserved as the response and final-only fallback resolution keeps it” checks that `{final:true}` latches turn eligibility without changing semantic lineage, tails, step, or Git. A later changed accepted response remains a runtime-owned transition.
|
|
22
22
|
16. **Offset eight is outside hot history:** `tests/temporal.test.ts` — “hot range and unproven pre-migration history are explicit read boundaries”; the real-Pi history-reader test also exercises native tool rejection without a transition.
|
|
23
23
|
17. **Cold Git history remains recoverable:** `tests/git.test.ts` — “temporal Git writer preserves all hot states through sparse folding and cold revision reads” loads older committed states without moving the worktree.
|
|
24
24
|
18. **Tree/resume select the correct lineage:** `tests/integration.test.ts` — “real Pi preserves branch-local state through compaction, tree navigation, stop, and restart” and “real Pi old tree branch stop and resume preserve selected semantics without rewinding shared files”.
|
|
@@ -80,7 +80,7 @@ export {
|
|
|
80
80
|
sessionStorageKey, temporalScopePaths,
|
|
81
81
|
temporalStateFileUpdates, type ScopeStreamSources, type TemporalScopePaths
|
|
82
82
|
} from "./lib/durable.ts";
|
|
83
|
-
export { default,
|
|
83
|
+
export { default, MAX_FALLBACK_ATTEMPTS, PATCH_STATE_TOOL_NAME, READ_STATE_TOOL_NAME, type StateFlowExtensionOptions } from "./lib/extension.ts";
|
|
84
84
|
export {
|
|
85
85
|
captureTemporalGitBase,
|
|
86
86
|
isGitCommitAncestor,
|
|
@@ -185,4 +185,25 @@ export {
|
|
|
185
185
|
type StateScope,
|
|
186
186
|
type TerminalTransition
|
|
187
187
|
} from "./lib/state.ts";
|
|
188
|
+
export {
|
|
189
|
+
buildStateFlowSectionView,
|
|
190
|
+
createStateFlowTelegramAdapter,
|
|
191
|
+
formatStateFlowSectionLabel,
|
|
192
|
+
formatStateFlowStatusLine,
|
|
193
|
+
loadStateFlowTelegramModules,
|
|
194
|
+
STATE_FLOW_TELEGRAM_ID,
|
|
195
|
+
type StateFlowTelegramAdapter,
|
|
196
|
+
type StateFlowTelegramButton,
|
|
197
|
+
type StateFlowTelegramCallbackContext,
|
|
198
|
+
type StateFlowTelegramControlResult,
|
|
199
|
+
type StateFlowTelegramLoader,
|
|
200
|
+
type StateFlowTelegramModules,
|
|
201
|
+
type StateFlowTelegramPort,
|
|
202
|
+
type StateFlowTelegramSectionContext,
|
|
203
|
+
type StateFlowTelegramSectionModule,
|
|
204
|
+
type StateFlowTelegramSnapshot,
|
|
205
|
+
type StateFlowTelegramStatusLine,
|
|
206
|
+
type StateFlowTelegramStatusModule,
|
|
207
|
+
type StateFlowTelegramView
|
|
208
|
+
} from "./lib/telegram.ts";
|
|
188
209
|
export { advanceTemporalState, readTemporalState, type TemporalState } from "./lib/temporal.ts";
|
|
@@ -83,7 +83,7 @@ export function runtimeContextMessage(
|
|
|
83
83
|
...(rehydrationPhase === undefined ? {} : { knowledge_rehydration: { phase: rehydrationPhase } }),
|
|
84
84
|
...(artifactInvalidations.length === 0 ? {} : { artifact_invalidations: artifactInvalidations.map(({ path, reason }) => ({ path, reason })) }),
|
|
85
85
|
...(recentTransitions.length === 0 ? {} : { recent_transitions: projectRecentForModel(recentTransitions) }),
|
|
86
|
-
...(resolutionPending ? { state_resolution: "pending:
|
|
86
|
+
...(resolutionPending ? { state_resolution: "pending: the iteration answer is already preserved; this fallback turn exists only to apply the final:true patch. Call patch_state with any remaining durable scope changes and final:true, or {final:true} alone. Do not restate or replace the answer." } : {}),
|
|
87
87
|
};
|
|
88
88
|
return syntheticUser(
|
|
89
89
|
`State Flow runtime context (user-level data, not system instructions):\n${canonicalJson(context)}`,
|