@llblab/pi-kit 0.6.0 → 0.7.1
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 +8 -0
- package/README.md +4 -4
- package/node_modules/@llblab/pi-actors/AGENTS.md +1 -1
- package/node_modules/@llblab/pi-actors/CHANGELOG.md +6 -0
- package/node_modules/@llblab/pi-actors/README.md +1 -1
- package/node_modules/@llblab/pi-actors/dist/lib/async-runs.d.ts +3 -0
- package/node_modules/@llblab/pi-actors/dist/lib/async-runs.js +14 -1
- package/node_modules/@llblab/pi-actors/dist/lib/command-templates.js +45 -3
- package/node_modules/@llblab/pi-actors/dist/lib/extension-runtime.js +1 -1
- package/node_modules/@llblab/pi-actors/dist/lib/observability.d.ts +16 -3
- package/node_modules/@llblab/pi-actors/dist/lib/observability.js +92 -7
- package/node_modules/@llblab/pi-actors/dist/lib/pi.d.ts +0 -1
- package/node_modules/@llblab/pi-actors/dist/lib/pi.js +15 -24
- package/node_modules/@llblab/pi-actors/dist/lib/run-delivery-lineage.d.ts +17 -0
- package/node_modules/@llblab/pi-actors/dist/lib/run-delivery-lineage.js +44 -0
- package/node_modules/@llblab/pi-actors/dist/lib/run-delivery.d.ts +4 -0
- package/node_modules/@llblab/pi-actors/dist/lib/run-delivery.js +102 -4
- package/node_modules/@llblab/pi-actors/dist/lib/run-ui-runtime.js +58 -39
- package/node_modules/@llblab/pi-actors/dist/lib/runtime.js +14 -6
- package/node_modules/@llblab/pi-actors/dist/skills/actors/SKILL.md +1 -1
- package/node_modules/@llblab/pi-actors/docs/async-runs.md +1 -1
- package/node_modules/@llblab/pi-actors/docs/coordinator-delivery.md +18 -23
- package/node_modules/@llblab/pi-actors/lib/async-runs.ts +18 -1
- package/node_modules/@llblab/pi-actors/lib/command-templates.ts +41 -3
- package/node_modules/@llblab/pi-actors/lib/extension-runtime.ts +1 -1
- package/node_modules/@llblab/pi-actors/lib/observability.ts +119 -5
- package/node_modules/@llblab/pi-actors/lib/pi.ts +15 -28
- package/node_modules/@llblab/pi-actors/lib/run-delivery-lineage.ts +68 -0
- package/node_modules/@llblab/pi-actors/lib/run-delivery.ts +120 -4
- package/node_modules/@llblab/pi-actors/lib/run-ui-runtime.ts +69 -44
- package/node_modules/@llblab/pi-actors/lib/runtime.ts +17 -6
- package/node_modules/@llblab/pi-actors/package.json +1 -1
- package/node_modules/@llblab/pi-actors/skills/actors/SKILL.md +1 -1
- package/node_modules/@llblab/pi-state-flow/AGENTS.md +33 -12
- package/node_modules/@llblab/pi-state-flow/BACKLOG.md +125 -2
- package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +87 -45
- package/node_modules/@llblab/pi-state-flow/README.md +203 -107
- package/node_modules/@llblab/pi-state-flow/docs/README.md +4 -0
- package/node_modules/@llblab/pi-state-flow/docs/architecture.md +188 -0
- package/node_modules/@llblab/pi-state-flow/docs/temporal-acceptance.md +42 -0
- package/node_modules/@llblab/pi-state-flow/index.ts +164 -2
- package/node_modules/@llblab/pi-state-flow/lib/acquisition.ts +138 -0
- package/node_modules/@llblab/pi-state-flow/lib/artifact.ts +273 -0
- package/node_modules/@llblab/pi-state-flow/lib/config.ts +48 -0
- package/node_modules/@llblab/pi-state-flow/lib/context.ts +18 -5
- package/node_modules/@llblab/pi-state-flow/lib/continuation.ts +268 -0
- package/node_modules/@llblab/pi-state-flow/lib/discovery.ts +117 -0
- package/node_modules/@llblab/pi-state-flow/lib/durable.ts +562 -0
- package/node_modules/@llblab/pi-state-flow/lib/episode.ts +24 -12
- package/node_modules/@llblab/pi-state-flow/lib/extension.ts +606 -70
- package/node_modules/@llblab/pi-state-flow/lib/git.ts +666 -0
- package/node_modules/@llblab/pi-state-flow/lib/history.ts +95 -0
- package/node_modules/@llblab/pi-state-flow/lib/json.ts +24 -0
- package/node_modules/@llblab/pi-state-flow/lib/maintenance.ts +141 -0
- package/node_modules/@llblab/pi-state-flow/lib/memory.ts +52 -0
- package/node_modules/@llblab/pi-state-flow/lib/migration.ts +88 -0
- package/node_modules/@llblab/pi-state-flow/lib/publication.ts +296 -0
- package/node_modules/@llblab/pi-state-flow/lib/recovery.ts +23 -7
- package/node_modules/@llblab/pi-state-flow/lib/rehydration.ts +79 -0
- package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +264 -0
- package/node_modules/@llblab/pi-state-flow/lib/session.ts +6 -0
- package/node_modules/@llblab/pi-state-flow/lib/skills.ts +99 -7
- package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +305 -48
- package/node_modules/@llblab/pi-state-flow/lib/state.ts +74 -7
- package/node_modules/@llblab/pi-state-flow/lib/status.ts +125 -6
- package/node_modules/@llblab/pi-state-flow/lib/storage.ts +196 -0
- package/node_modules/@llblab/pi-state-flow/lib/temporal.ts +233 -0
- package/node_modules/@llblab/pi-state-flow/lib/terminal.ts +70 -24
- package/node_modules/@llblab/pi-state-flow/lib/transition.ts +254 -29
- package/node_modules/@llblab/pi-state-flow/package.json +8 -2
- package/node_modules/@llblab/pi-state-flow/skills/state-flow-memory/SKILL.md +128 -0
- package/node_modules/@llblab/pi-telegram/AGENTS.md +14 -9
- package/node_modules/@llblab/pi-telegram/BACKLOG.md +23 -5
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +18 -0
- package/node_modules/@llblab/pi-telegram/README.md +13 -9
- package/node_modules/@llblab/pi-telegram/docs/README.md +1 -0
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +227 -23
- package/node_modules/@llblab/pi-telegram/docs/generative-apps.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +70 -19
- package/node_modules/@llblab/pi-telegram/docs/outbound.md +6 -6
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +13 -6
- package/node_modules/@llblab/pi-telegram/docs/ui-style.md +3 -1
- package/node_modules/@llblab/pi-telegram/index.ts +4 -1418
- package/node_modules/@llblab/pi-telegram/lib/agent-messages.ts +6 -3
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +46 -1
- package/node_modules/@llblab/pi-telegram/lib/bus-api.ts +32 -19
- package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +600 -135
- package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +962 -55
- package/node_modules/@llblab/pi-telegram/lib/bus.ts +355 -26
- package/node_modules/@llblab/pi-telegram/lib/channel-posts.ts +718 -0
- package/node_modules/@llblab/pi-telegram/lib/commands.ts +237 -11
- package/node_modules/@llblab/pi-telegram/lib/config.ts +242 -26
- package/node_modules/@llblab/pi-telegram/lib/extension.ts +1851 -0
- package/node_modules/@llblab/pi-telegram/lib/generative-apps.ts +20 -2
- package/node_modules/@llblab/pi-telegram/lib/journal.ts +2184 -126
- package/node_modules/@llblab/pi-telegram/lib/locks.ts +44 -2
- package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +152 -13
- package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +97 -10
- package/node_modules/@llblab/pi-telegram/lib/paths.ts +29 -0
- package/node_modules/@llblab/pi-telegram/lib/polling.ts +85 -17
- package/node_modules/@llblab/pi-telegram/lib/preview.ts +17 -0
- package/node_modules/@llblab/pi-telegram/lib/prompts.ts +6 -2
- package/node_modules/@llblab/pi-telegram/lib/queue.ts +118 -26
- package/node_modules/@llblab/pi-telegram/lib/rendering.ts +4 -1
- package/node_modules/@llblab/pi-telegram/lib/replies.ts +21 -2
- package/node_modules/@llblab/pi-telegram/lib/routing.ts +344 -112
- package/node_modules/@llblab/pi-telegram/lib/setup.ts +44 -4
- package/node_modules/@llblab/pi-telegram/lib/status.ts +51 -4
- package/node_modules/@llblab/pi-telegram/lib/sync.ts +308 -39
- package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +353 -22
- package/node_modules/@llblab/pi-telegram/lib/thread-cleanup-manager.ts +664 -0
- package/node_modules/@llblab/pi-telegram/lib/thread-display.ts +226 -0
- package/node_modules/@llblab/pi-telegram/lib/thread-naming.ts +118 -0
- package/node_modules/@llblab/pi-telegram/lib/threads.ts +1686 -129
- package/node_modules/@llblab/pi-telegram/lib/turns.ts +7 -0
- package/node_modules/@llblab/pi-telegram/lib/updates.ts +1319 -97
- package/node_modules/@llblab/pi-telegram/lib/workspace-admission.ts +1643 -0
- package/node_modules/@llblab/pi-telegram/lib/workspace-retirement.ts +968 -0
- package/node_modules/@llblab/pi-telegram/lib/workspace-slots.ts +84 -0
- package/node_modules/@llblab/pi-telegram/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/screenshot.png +0 -0
- package/node_modules/@llblab/pi-telegram/scripts/measure-bus.mjs +83 -0
- package/node_modules/@llblab/pi-telegram/scripts/measure-workspace.mjs +101 -0
- package/node_modules/@llblab/{skills → pi-telegram/skills}/show-me/SKILL.md +28 -6
- package/node_modules/@llblab/pi-telegram/skills/show-me/references/telegram-surfaces.md +43 -0
- package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/references/delivery-and-threads.md +1 -1
- package/node_modules/@llblab/skills/package.json +2 -3
- package/package.json +6 -5
- /package/node_modules/@llblab/pi-telegram/lib/{logs.ts → logging.ts} +0 -0
|
@@ -10,6 +10,7 @@ import * as Observability from "./observability.ts";
|
|
|
10
10
|
import * as Paths from "./paths.ts";
|
|
11
11
|
import * as Pi from "./pi.ts";
|
|
12
12
|
import * as RunDelivery from "./run-delivery.ts";
|
|
13
|
+
import * as RunDeliveryLineage from "./run-delivery-lineage.ts";
|
|
13
14
|
|
|
14
15
|
export interface RunUiRuntime {
|
|
15
16
|
close(): void;
|
|
@@ -43,12 +44,11 @@ export function createRunUiRuntime(deps: RunUiRuntimeDeps): RunUiRuntime {
|
|
|
43
44
|
let animationInterval: NodeJS.Timeout | undefined;
|
|
44
45
|
let deliveryTimeout: NodeJS.Timeout | undefined;
|
|
45
46
|
let notifyTimeout: NodeJS.Timeout | undefined;
|
|
46
|
-
let recoverQueuedBatch = false;
|
|
47
47
|
let recoverQueuedSteers = false;
|
|
48
48
|
let running = false;
|
|
49
49
|
let lastWatcherDiagnosticId = 0;
|
|
50
50
|
const observation = Observability.createRunUiObservationState();
|
|
51
|
-
const
|
|
51
|
+
const deliveryObservation = Observability.createRunUiObservationState();
|
|
52
52
|
const retirementAttempts = new Set<string>();
|
|
53
53
|
const steerDiagnostics = new Set<string>();
|
|
54
54
|
|
|
@@ -70,9 +70,7 @@ export function createRunUiRuntime(deps: RunUiRuntimeDeps): RunUiRuntime {
|
|
|
70
70
|
notifyTimeout = undefined;
|
|
71
71
|
if (deliveryTimeout) clearTimeout(deliveryTimeout);
|
|
72
72
|
deliveryTimeout = undefined;
|
|
73
|
-
recoverQueuedBatch = false;
|
|
74
73
|
recoverQueuedSteers = false;
|
|
75
|
-
deliveryRecoveryDiagnostics.clear();
|
|
76
74
|
steerDiagnostics.clear();
|
|
77
75
|
if (animationInterval) clearInterval(animationInterval);
|
|
78
76
|
animationInterval = undefined;
|
|
@@ -228,12 +226,14 @@ export function createRunUiRuntime(deps: RunUiRuntimeDeps): RunUiRuntime {
|
|
|
228
226
|
};
|
|
229
227
|
const admitCompletionTransitions = (
|
|
230
228
|
ownerId: string,
|
|
231
|
-
|
|
229
|
+
snapshot: Observability.RunUiSnapshot,
|
|
232
230
|
): boolean => {
|
|
233
231
|
const existing = journal(ownerId).completion_batch;
|
|
234
232
|
if (existing) return true;
|
|
235
|
-
const members = Observability.collectRunCompletionBatchMembers(
|
|
236
|
-
.
|
|
233
|
+
const members = Observability.collectRunCompletionBatchMembers(
|
|
234
|
+
snapshot.transitions,
|
|
235
|
+
snapshot.summary.runs,
|
|
236
|
+
).slice(0, Limits.RUN_DELIVERY_BATCH_MAX_MEMBERS);
|
|
237
237
|
if (members.length === 0) return false;
|
|
238
238
|
RunDelivery.admitRunCompletionBatch({
|
|
239
239
|
members,
|
|
@@ -244,6 +244,10 @@ export function createRunUiRuntime(deps: RunUiRuntimeDeps): RunUiRuntime {
|
|
|
244
244
|
};
|
|
245
245
|
const isIdle = (ctx: Pi.ExtensionContext): boolean =>
|
|
246
246
|
typeof ctx.isIdle !== "function" || ctx.isIdle();
|
|
247
|
+
const isDeliveryRoot = (ownerId: string): boolean => {
|
|
248
|
+
const inheritedOwner = RunDeliveryLineage.getProcessDeliveryOwnerId();
|
|
249
|
+
return inheritedOwner === undefined || inheritedOwner === ownerId;
|
|
250
|
+
};
|
|
247
251
|
let flushCompletionBatch = (_ctx: Pi.ExtensionContext): boolean => false;
|
|
248
252
|
const flushSteers = (ctx: Pi.ExtensionContext, ownerId: string): boolean => {
|
|
249
253
|
const recovering = recoverQueuedSteers;
|
|
@@ -334,11 +338,25 @@ export function createRunUiRuntime(deps: RunUiRuntimeDeps): RunUiRuntime {
|
|
|
334
338
|
if (!notify) return false;
|
|
335
339
|
const sink = Pi.createNotificationSink(deps.pi, ctx);
|
|
336
340
|
retireCandidateRuns(ctx, snapshot.summary);
|
|
337
|
-
const
|
|
338
|
-
Observability.
|
|
339
|
-
|
|
341
|
+
const deliverySnapshot = isDeliveryRoot(ownerId)
|
|
342
|
+
? Observability.readRunDeliverySnapshot(deliveryObservation, ownerId)
|
|
343
|
+
: undefined;
|
|
344
|
+
const hasCompletionCandidates = deliverySnapshot
|
|
345
|
+
? Observability.collectRunCompletionBatchMembers(
|
|
346
|
+
deliverySnapshot.transitions,
|
|
347
|
+
deliverySnapshot.summary.runs,
|
|
348
|
+
).length > 0
|
|
349
|
+
: false;
|
|
350
|
+
const hasCompletionBatch = isDeliveryRoot(ownerId) &&
|
|
351
|
+
Boolean(journal(ownerId).completion_batch);
|
|
340
352
|
admitSteerEvents(ctx, ownerId, snapshot.attentionEvents);
|
|
341
353
|
Observability.pruneRunUiObservationState(observation, snapshot);
|
|
354
|
+
if (deliverySnapshot) {
|
|
355
|
+
Observability.pruneRunUiObservationState(
|
|
356
|
+
deliveryObservation,
|
|
357
|
+
deliverySnapshot,
|
|
358
|
+
);
|
|
359
|
+
}
|
|
342
360
|
if (!terminalOnly) {
|
|
343
361
|
Observability.deliverRunAttentionNotifications(
|
|
344
362
|
snapshot.attentionEvents.filter((event) =>
|
|
@@ -403,54 +421,48 @@ export function createRunUiRuntime(deps: RunUiRuntimeDeps): RunUiRuntime {
|
|
|
403
421
|
});
|
|
404
422
|
|
|
405
423
|
flushCompletionBatch = (ctx: Pi.ExtensionContext): boolean => {
|
|
406
|
-
if (!running || activeContext !== ctx || deps.getActiveContext() !== ctx) return false;
|
|
407
424
|
const ownerId = activeOwnerId;
|
|
408
|
-
if (!ownerId) return false;
|
|
425
|
+
if (!running || !ownerId) return false;
|
|
426
|
+
try {
|
|
427
|
+
if (Pi.getSessionId(ctx) !== ownerId) return false;
|
|
428
|
+
} catch {
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
409
431
|
if (flushSteers(ctx, ownerId)) return true;
|
|
432
|
+
if (!isDeliveryRoot(ownerId)) return false;
|
|
410
433
|
let batch = journal(ownerId).completion_batch;
|
|
411
434
|
if (!batch) {
|
|
412
|
-
const snapshot = Observability.
|
|
413
|
-
|
|
414
|
-
|
|
435
|
+
const snapshot = Observability.readRunDeliverySnapshot(
|
|
436
|
+
deliveryObservation,
|
|
437
|
+
ownerId,
|
|
438
|
+
);
|
|
439
|
+
admitCompletionTransitions(ownerId, snapshot);
|
|
440
|
+
Observability.pruneRunUiObservationState(deliveryObservation, snapshot);
|
|
415
441
|
batch = journal(ownerId).completion_batch;
|
|
416
442
|
}
|
|
417
443
|
if (!batch) return false;
|
|
418
444
|
if (batch.phase === "presented") {
|
|
419
445
|
finishPresentedBatch(ownerId, batch);
|
|
420
|
-
const snapshot = Observability.
|
|
421
|
-
|
|
422
|
-
|
|
446
|
+
const snapshot = Observability.readRunDeliverySnapshot(
|
|
447
|
+
deliveryObservation,
|
|
448
|
+
ownerId,
|
|
449
|
+
);
|
|
450
|
+
admitCompletionTransitions(ownerId, snapshot);
|
|
451
|
+
Observability.pruneRunUiObservationState(deliveryObservation, snapshot);
|
|
423
452
|
batch = journal(ownerId).completion_batch;
|
|
424
453
|
if (!batch) return false;
|
|
425
454
|
}
|
|
426
455
|
if (!isIdle(ctx)) return true;
|
|
427
456
|
const content = RunDelivery.formatRunCompletionBatchMessage(batch);
|
|
428
457
|
if (batch.phase === "queued") {
|
|
429
|
-
if (!
|
|
430
|
-
recoverQueuedBatch = false;
|
|
431
|
-
const evidence = Pi.inspectRunCompletionBatchSessionEvidence(
|
|
432
|
-
ctx,
|
|
433
|
-
batch.batch_id,
|
|
434
|
-
content,
|
|
435
|
-
);
|
|
436
|
-
if (evidence.status === "present") return true;
|
|
437
|
-
if (evidence.status !== "absent") {
|
|
438
|
-
const diagnosticKey = `${batch.batch_id}:${evidence.status}:${evidence.reason ?? ""}`;
|
|
439
|
-
if (!deliveryRecoveryDiagnostics.has(diagnosticKey)) {
|
|
440
|
-
deliveryRecoveryDiagnostics.add(diagnosticKey);
|
|
441
|
-
ctx.ui.notify(
|
|
442
|
-
`Actor completion recovery is ${evidence.status}: ${evidence.reason ?? "conflicting session evidence"}. Batch ${batch.batch_id} remains queued.`,
|
|
443
|
-
"warning",
|
|
444
|
-
);
|
|
445
|
-
}
|
|
446
|
-
return true;
|
|
447
|
-
}
|
|
448
|
-
if (!RunDelivery.resetRunCompletionBatchPending({
|
|
458
|
+
if (!RunDelivery.markRunCompletionBatchPresented({
|
|
449
459
|
batchId: batch.batch_id,
|
|
450
460
|
ownerId,
|
|
451
461
|
tempDir: Paths.EXTENSION_RUNTIME_PATHS.tempDir,
|
|
452
462
|
})) return true;
|
|
453
|
-
|
|
463
|
+
const accepted = journal(ownerId).completion_batch;
|
|
464
|
+
if (accepted) finishPresentedBatch(ownerId, accepted);
|
|
465
|
+
return flushCompletionBatch(ctx);
|
|
454
466
|
}
|
|
455
467
|
if (!isIdle(ctx)) return true;
|
|
456
468
|
try {
|
|
@@ -471,7 +483,19 @@ export function createRunUiRuntime(deps: RunUiRuntimeDeps): RunUiRuntime {
|
|
|
471
483
|
})) {
|
|
472
484
|
throw new Error("Completion batch changed before queue acknowledgment");
|
|
473
485
|
}
|
|
474
|
-
|
|
486
|
+
if (!RunDelivery.markRunCompletionBatchPresented({
|
|
487
|
+
batchId: batch.batch_id,
|
|
488
|
+
ownerId,
|
|
489
|
+
tempDir: Paths.EXTENSION_RUNTIME_PATHS.tempDir,
|
|
490
|
+
})) {
|
|
491
|
+
throw new Error("Completion batch changed before durable acceptance");
|
|
492
|
+
}
|
|
493
|
+
const accepted = journal(ownerId).completion_batch;
|
|
494
|
+
if (accepted) finishPresentedBatch(ownerId, accepted);
|
|
495
|
+
ctx.ui.notify(
|
|
496
|
+
`Actor completions ready: ${batch.members.length}`,
|
|
497
|
+
"info",
|
|
498
|
+
);
|
|
475
499
|
return true;
|
|
476
500
|
};
|
|
477
501
|
|
|
@@ -479,11 +503,13 @@ export function createRunUiRuntime(deps: RunUiRuntimeDeps): RunUiRuntime {
|
|
|
479
503
|
close,
|
|
480
504
|
flushCompletionBatch,
|
|
481
505
|
projectContext(messages, ctx) {
|
|
482
|
-
|
|
506
|
+
const ownerId = activeOwnerId;
|
|
507
|
+
if (!running || !ownerId) return messages;
|
|
508
|
+
try {
|
|
509
|
+
if (Pi.getSessionId(ctx) !== ownerId) return messages;
|
|
510
|
+
} catch {
|
|
483
511
|
return messages;
|
|
484
512
|
}
|
|
485
|
-
const ownerId = activeOwnerId;
|
|
486
|
-
if (!ownerId) return messages;
|
|
487
513
|
const steerContext = Pi.dedupeRunSteerContext(messages);
|
|
488
514
|
let contextMessages = steerContext.messages;
|
|
489
515
|
for (const steer of journal(ownerId).steers) {
|
|
@@ -560,7 +586,6 @@ export function createRunUiRuntime(deps: RunUiRuntimeDeps): RunUiRuntime {
|
|
|
560
586
|
close();
|
|
561
587
|
activeContext = ctx;
|
|
562
588
|
activeOwnerId = ownerId;
|
|
563
|
-
recoverQueuedBatch = true;
|
|
564
589
|
recoverQueuedSteers = true;
|
|
565
590
|
running = true;
|
|
566
591
|
try {
|
|
@@ -325,6 +325,7 @@ export function createRecipeToolReloadWatcher(
|
|
|
325
325
|
let rootWatcher: FSWatcher | undefined;
|
|
326
326
|
let parentWatcher: FSWatcher | undefined;
|
|
327
327
|
let failureNotified = false;
|
|
328
|
+
let notifyAfterReload = false;
|
|
328
329
|
const setWatchStatus = (watchStatus: RecipeRegistryStatus["watch_status"]): void =>
|
|
329
330
|
runtime.setWatchStatus?.(watchStatus);
|
|
330
331
|
const close = (): void => {
|
|
@@ -336,6 +337,7 @@ export function createRecipeToolReloadWatcher(
|
|
|
336
337
|
closingParent?.close();
|
|
337
338
|
if (reloadTimeout) clearTimeout(reloadTimeout);
|
|
338
339
|
reloadTimeout = undefined;
|
|
340
|
+
notifyAfterReload = false;
|
|
339
341
|
setWatchStatus("closed");
|
|
340
342
|
};
|
|
341
343
|
const reportCallbackError = (error: unknown): void => {
|
|
@@ -358,14 +360,21 @@ export function createRecipeToolReloadWatcher(
|
|
|
358
360
|
reportCallbackError(error);
|
|
359
361
|
}
|
|
360
362
|
};
|
|
361
|
-
const scheduleReload = (
|
|
363
|
+
const scheduleReload = (
|
|
364
|
+
ctx: RuntimeContext,
|
|
365
|
+
notifyActiveChange = false,
|
|
366
|
+
): void => {
|
|
362
367
|
failureNotified = false;
|
|
368
|
+
notifyAfterReload ||= notifyActiveChange;
|
|
363
369
|
if (reloadTimeout) clearTimeout(reloadTimeout);
|
|
364
370
|
reloadTimeout = setTimeout(() => {
|
|
365
371
|
reloadTimeout = undefined;
|
|
366
372
|
try {
|
|
367
373
|
runtime.loadTools(ctx, deps.getResolutionContext?.());
|
|
368
|
-
|
|
374
|
+
if (notifyAfterReload) {
|
|
375
|
+
ctx.ui.notify("Recipe tools refreshed from ~/.pi/agent/recipes", "info");
|
|
376
|
+
}
|
|
377
|
+
notifyAfterReload = false;
|
|
369
378
|
} catch (error) {
|
|
370
379
|
notifyFailure(ctx);
|
|
371
380
|
reportCallbackError(error);
|
|
@@ -394,7 +403,7 @@ export function createRecipeToolReloadWatcher(
|
|
|
394
403
|
parentWatcher = undefined;
|
|
395
404
|
watcher.close();
|
|
396
405
|
watchRoot(ctx, recipeRoot);
|
|
397
|
-
scheduleReload(ctx);
|
|
406
|
+
scheduleReload(ctx, true);
|
|
398
407
|
});
|
|
399
408
|
parentWatcher = watcher;
|
|
400
409
|
setWatchStatus("watching_parent");
|
|
@@ -415,16 +424,18 @@ export function createRecipeToolReloadWatcher(
|
|
|
415
424
|
return;
|
|
416
425
|
}
|
|
417
426
|
try {
|
|
418
|
-
const watcher = watchPath(recipeRoot, () => {
|
|
427
|
+
const watcher = watchPath(recipeRoot, (_event, changedFile) => {
|
|
419
428
|
if (rootWatcher !== watcher) return;
|
|
420
429
|
if (!pathExists(recipeRoot)) {
|
|
421
430
|
rootWatcher = undefined;
|
|
422
431
|
watcher.close();
|
|
423
|
-
scheduleReload(ctx);
|
|
432
|
+
scheduleReload(ctx, true);
|
|
424
433
|
watchParent(ctx, recipeRoot);
|
|
425
434
|
return;
|
|
426
435
|
}
|
|
427
|
-
|
|
436
|
+
const relativeChange = changedFile ? String(changedFile) : "";
|
|
437
|
+
const firstSegment = relativeChange.split(/[\\/]/u)[0];
|
|
438
|
+
scheduleReload(ctx, Boolean(relativeChange) && firstSegment !== "drafts");
|
|
428
439
|
});
|
|
429
440
|
rootWatcher = watcher;
|
|
430
441
|
setWatchStatus("watching_root");
|
|
@@ -107,7 +107,7 @@ Run = Recipe + Trace + Control
|
|
|
107
107
|
```
|
|
108
108
|
|
|
109
109
|
1. Spawn with the exact logical Recipe identity and caller-owned values.
|
|
110
|
-
2. Retain the returned `run:<id>` and normally wait for
|
|
110
|
+
2. Retain the returned `run:<id>` and normally wait for the root coordinator's settled completion batch instead of polling. Nested actor completions accumulate under their containing top-level Run and arrive in that one tree-compressed batch; descendant sessions do not need notification options.
|
|
111
111
|
3. Inspect `view=trace` when retained observations or attention matter.
|
|
112
112
|
4. Inspect `view=control` before diagnosing service readiness, stale work, or saturation.
|
|
113
113
|
5. Send `message` only for an action declared and consumed by that controlled Recipe.
|
|
@@ -3,26 +3,47 @@
|
|
|
3
3
|
- Keep independent domain modules under `lib/`, mirror every domain with a same-named file under `tests/`, place cross-domain architecture checks in `tests/invariants.test.ts`, and keep `index.ts` as a minimal composition/public-export boundary.
|
|
4
4
|
- Keep the extension opt-in and preserve clear attribution to SKILL.state wherever the inherited explicit-state approach is described.
|
|
5
5
|
- Preserve Pi's native tool loop and complete inspectable session trace; project completed-run history only at user-run boundaries. Retain persistent and current-run context-bearing custom messages from other extensions, plus the complete current-run trajectory, except State Flow's own validation feedback when it is represented separately.
|
|
6
|
-
- Expose one
|
|
7
|
-
- Require
|
|
8
|
-
-
|
|
9
|
-
-
|
|
6
|
+
- Expose one canonical materialized state shape across global, CWD, and session scopes with exactly `artifacts`, `contract`, `working`, and `response`; the first three are flexible semantic objects and the fourth is the latest complete user-facing answer string where applicable.
|
|
7
|
+
- Key artifacts directly by their source path. Require every artifact to have a non-empty `description`, a canonical lowercase `sha256:<64 hex>` source hash, and a non-empty compiler revision; permit forward-compatible metadata without requiring `kind`.
|
|
8
|
+
- At session initialization, discover regular lowercase `*.md` files recursively beneath the configured Knowledge repository root; use canonical absolute paths, hash opaque source bytes and retain their byte counts without decoding or retaining bodies, skip symlinks, and never traverse outside that canonical root. Treat the current candidate set plus explicit removals as generic global artifact input without invoking Knowledge validators. Materialize removals deterministically as runtime-owned global transitions without rereading missing bodies or requesting model compiler output. Never reserve Knowledge document names, provide built-in Knowledge templates, or implement `save_knowledge`.
|
|
9
|
+
- Determine artifact freshness from source hash plus compiler revision. Require compilation for new sources, changed hashes, compiler revision changes, missing or invalid metadata, and explicit refreshes; do not use `compiled_at` as the correctness signal. Plan acquisition from path/hash identity without source bodies, project stale global candidates as runtime-owned `artifact_invalidations`, and apply validated compilation/removal cohorts immutably so artifact metadata and its materialized scope advance atomically.
|
|
10
|
+
- Correlate successful exact-path reads with the current ordinary global invalidation plan. Require each acquired stale source to have a compact compiler output at the same path in a global transition, reject model-authored hash/compiler fields, and attach the runtime-observed source hash plus current ordinary compiler revision before publication. Compile routing descriptions by default, never raw Markdown or a full-file summary; preserve uncertainty and reserve richer compilations for reusable operational semantics.
|
|
11
|
+
- Apply one materialized-first acquisition policy to every source: read only for a concrete relevant gap not covered by a sufficient compilation, an exact-source operation including edits, evidenced invalidation, contradiction/failure reconciliation, explicit request, or selection by bounded maintenance. New sessions, routine recall or activation, reassurance, and an index or description alone are not reasons to read. Changed hashes require rereading; prefer the smallest sufficient read.
|
|
12
|
+
- Keep artifact maintenance opt-in, low-frequency, and side-effect free until a selected source is acquired. Select only otherwise-fresh artifacts old enough for maintenance, rank missing/unparseable `compiled_at` first then oldest timestamp and path, and enforce strict per-cycle read-count and source-byte ceilings. Treat source bytes as the conservative source-token upper bound. Never let maintenance displace correctness invalidation; use explicit invalidation refresh for full rebuilds and never mutate source files.
|
|
13
|
+
- Load optional agent-level `state-flow.json` once per extension load/reload. Its `directory` selects the state store, `autoStart` defaults false for genuinely new sessions, and `remotePublication` selects `off`, `turn-end`, or compatibility `transition`. State Flow always owns durable memory while enabled and global semantic memory is always available; these are invariants, not configuration switches. Keep runtime configuration and provenance outside model-patchable semantic state.
|
|
14
|
+
- Keep runtime configuration and provenance separate from model-patchable semantic state. Persist session `config.json` for enablement/projection settings and `meta.json` for lineage, counters, branch identity, durable base, publication, and migration metadata. Neither participates in scope overlay. New Pi checkpoints contain only a durable revision pointer, or `{disabled:true}` when no durable runtime exists on that branch. Preserve exact Git hashes for Git-backed revisions and give file-only current-cohort references an unambiguous distinct identity; file references use `file:<64 lowercase hex>`, bind the store root and complete scoped/runtime cohort, and must not pretend to provide arbitrary cold or branch history. Operationally unavailable references (including expired file cohorts, missing Git, and publication exclusion) retain selection rather than falling through to older disabled markers; malformed immutable targets remain a distinct recovery case; they are not a parallel authoritative semantic or config/meta store. Emit a disabled marker only from proven pre-runtime branch provenance; missing/invalid revisions and failed restoration do not establish that permission. Predecessor checkpoint formats are read-only migration input. Validate immutable pointer targets before acquiring the live publication basis; transient publication failures preserve the selected retry reference rather than choosing older semantic state. In Git mode, runtime `revision: "self"` resolves to the commit owning the config/meta pair, not an unrelated later repository commit. File publication uses explicit `publication: "files"` provenance and resolves the exact complete live cohort instead of claiming a Git owner. Use a distinct `meta.temporalRevision` when a runtime-only write selects older semantic scopes, so stop or lifecycle persistence does not rewind another branch's live files. Persist publication intent as unconfirmed before pushing; reconcile that exact existing commit after restart rather than embedding an impossible self-hash or inventing semantic history.
|
|
15
|
+
- Overlay materialized state recursively in `global → cwd → session` order. Scope-local deletion removes only that scope's key so a lower-scope value becomes visible again; scope never changes instruction authority. Route branch/run-local continuation to session, project-local reusable state and Skill artifacts to CWD, and cross-project reusable state to global.
|
|
16
|
+
- Reserve `state` for the runtime materialized semantic view. For each scope, current state is exactly `materialize(checkpoint.json, patches.jsonl)`: an older anchored checkpoint plus its ordered tail of at most seven materially effective semantic patches. On overflow, apply the oldest patch into the checkpoint, advance its `through` boundary, remove it, and append the new patch. Never truncate unapplied replay records or replay patches over an already-current snapshot.
|
|
17
|
+
- Give every accepted semantic transition one opaque identity shared by all affected scope records, with explicit active causal lineage. A branch-local position can order identities but never acts as a global counter, substitutes for identity, or merges forks. Unchanged scopes contribute no record and remain unchanged at that boundary. Adopt revision-proven inherited streams at a new session origin without rewriting their checkpoints/tails; pre-origin coordinates are not one shared clock. True semantic no-ops and origin adoption do not advance semantic history.
|
|
18
|
+
- Define `state[n]` and `state.global[n]`, `state.cwd[n]`, `state.session[n]` at the same nth previous accepted transition boundary in the active lineage, never the nth local patch of each scope. Guarantee offsets zero through seven once the lineage has seven proven transitions; report earlier-than-origin history as unavailable for new/migrated lineages and reject offsets beyond seven on the hot interface. Reconstruct scopes at one target before overlaying them.
|
|
19
|
+
- Keep historical reads lazy through the smallest runtime/model read interface. Normal inference gets only current effective state and useful bounded compact transition context, never eight full snapshots. `patch_state` is the sole semantic mutation tool. `read_state` exposes one cached effective/global/CWD/session projection at offset 0–7, defaulting to effective/0; it returns the exact boundary with semantic state and never publishes, appends a checkpoint, or advances history. Both tools follow branch enablement and host tool restrictions; the patch barrier blocks reader siblings too. Live/cached checkpoint plus tails own the hot path; use Git for branch restoration and explicit cold inspection, not to rebuild current state on every inference.
|
|
20
|
+
- Store runtime state in its own directory with optional Git durability, defaulting to `state-flow/` beneath Pi's configured agent directory (`~/.pi/agent/state-flow/` normally), independently from the Knowledge Markdown source root. Use exactly these owned paths: `checkpoint.json` and `patches.jsonl` for global; `<cwd-key>/checkpoint.json` and `<cwd-key>/patches.jsonl` for CWD; `<cwd-key>/<session-key>/checkpoint.json`, `patches.jsonl`, `config.json`, and `meta.json` for session. Do not create `.state-flow`, `scopes`, or another storage/history namespace. Mirror Pi's native CWD session-directory encoding and JSONL-basename session key (deriving `<header timestamp>_<UUID>` for in-memory sessions), while retaining separately verifiable canonical identity provenance in owned state. Keep the UUID authoritative, reject unsafe segments and mismatches rather than selecting another scope, and use CWD checkpoint ownership to fail closed on Pi-name collisions.
|
|
21
|
+
- Migrate legacy current `state.json` losslessly into an initial anchored checkpoint with an empty tail; current snapshots are the sole semantic recovery basis, independent of the presence or validity of explanatory journals, including historical revision reads. Preserve only proven history, remove obsolete files within the successful scoped migration, and never retain two authoritative formats. Exercise migrations in temporary repositories rather than modifying the user's active Knowledge data during development.
|
|
22
|
+
- Read and write only regular non-symlink State Flow-owned files at those exact paths, publish each file by same-directory atomic rename, preserve arbitrary repository contents, and classify ownership exactly; never use repository-wide staging. Retain opaque source bytes for file identity and rollback, not decoded-text reconstructions. Use validated structural JSON equality for in-process semantic comparisons; reserve cryptographic hashes for compact identities that cross inference, persistence, process, or source-freshness boundaries. Serialize cooperating Git publications through a common-Git-directory publication lock; low-level file helpers require caller exclusion. Recheck prepared bases before each replacement/deletion and restore only bytes still matching the publisher's own output. Preserve detected concurrent changes and report unresolved rollback conflicts; do not claim kernel-atomic multi-file CAS against nonparticipating writers. Markdown discovery uses its independently configured source root, defaulting to `knowledge/` beneath Pi's agent directory; a storage-root override must not redirect source discovery.
|
|
23
|
+
- At instance initialization resolve the repository, CWD key, and session key; load each scope's anchored checkpoint and tail, materialize at the selected temporal boundary, then overlay `global → cwd → session`. Install cached view and publication basis atomically only after successful restoration/initialization; unavailable publication is an error, not a semantic no-op. Preserve the selected revision across transient restore failure so an explicit start can retry it. A genuinely new session gets an empty session layer and inherits only global/CWD values; it must never reuse another same-CWD session layer.
|
|
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
|
+
- 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. `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
|
+
- Register `patch_state` as the sole semantic mutation tool. It applies one validated `session`, `cwd`, or `global` semantic patch immediately through temporal folding/publication, records the accepted lineage and durable revision, returns a compact acknowledgement, and makes the next inference receive the new `state[0]`; the previous state becomes `state[1]`. Use it for established future-relevant state whose delayed materialization creates meaningful loss or recovery risk, or for a necessary write-and-verify step in explicitly requested curation; never use it for scratchpad, narration, routine progress, or speculative churn.
|
|
27
|
+
- Treat `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. After success, retain the tool call/result as current-run causality and let the model choose all subsequent actions from rematerialized context.
|
|
28
|
+
- Require exactly one terminal reconciliation after zero or more intermediate barriers in every successful enabled run. Accept a non-empty ordinary terminal answer without a State Flow comment as an empty model patch: preserve `artifacts`, `contract`, and `working`, replace session `response`, and persist that session semantic change through the same Git lifecycle. Explicit comment markers (including malformed or embedded ones) must not silently fall back to ordinary text. When a patch is present, require one top-level transcript-private `<!-- state_flow … -->` comment containing exactly a `transitions` array, one separating blank line, then the user-facing answer exactly once. Each transition must contain one unique known `scope` and an object `patch` limited to `artifacts`, `contract`, and `working`; reject model-authored `response`, runtime `config`, or provenance. Retain pre-scoped three-field envelopes only as session compatibility shorthand. At `message_end`, remove the comment and stage every affected scope against semantic hashes and the actual active causal-boundary identity, so equal values cannot authorize crossing to another lineage; at `turn_end`, reconcile session `response` with Pi's finalized assistant message after all chained handlers, atomically publish every affected checkpoint/tail pair and temporal metadata in one commit, and record its revision in the branch checkpoint. Do not write materially unchanged non-session scopes. Route missing text or a tool call added after terminal validation through the same bounded regeneration chain. Transcript-private does not mean confidential during streaming.
|
|
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.
|
|
10
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.
|
|
11
|
-
- Treat `working` as last observations, not a live workspace. Revalidate volatile facts before consequential actions; after interruption or branch navigation inspect relevant external effects before repeating operations. Failed state commits and restored memory do not undo tool effects. Missing evidence proves neither success nor absence of effects: retain uncertainty and the next check. Keep revalidation targeted, without
|
|
12
|
-
- Treat each successful `SKILL.md` read as
|
|
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
|
+
- 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 at the next intermediate or terminal CWD transition under `patch.artifacts[exactReadPath]` with `description`, `kind: "skill"`, and a flexible non-empty `compilation` object; hash the executed source bytes and attach runtime-owned `hash` plus `skill-artifact-v1` compiler metadata; reject missing, unhashable, malformed, or forged freshness data; replace the complete prior Skill artifact on refresh so obsolete metadata 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.
|
|
13
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.
|
|
14
|
-
- Accept
|
|
34
|
+
- Accept omitted fields and empty scope patches when nothing future-relevant changed and memory is already efficient; never force invented bookkeeping. The comment may be omitted when no model patch is needed. Always require a finalized non-empty answer, which the runtime owns as session `response`. A changed response is a semantic transition; identical complete semantic state finalizes the runtime lifecycle without a patch, identity, or temporal step. Semantic usefulness and optimization remain protocol-owned because deterministic validation cannot prove them.
|
|
15
35
|
- Keep validation retries hidden from finalized user-facing messages; expose diagnostic detail only through transcript-private feedback and persisted runtime state, while documenting that streaming observers can still see generated terminal envelopes. Tool-bearing retry turns must preserve cumulative validation-attempt accounting. If retries are exhausted, aborted, or cannot continue before the agent settles, keep State Flow enabled and the last committed state intact; abandon only the transient validation chain so the next user run rotates normally.
|
|
16
|
-
- Recursively materialize patches immediately; empty objects preserve, nested object-key `null` deletes, and `null` anywhere in
|
|
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 and trusted freshness metadata.
|
|
17
37
|
- Do not impose project schemas, state or patch byte caps, dynamic growth pressure, observation envelopes, action authorization, action ledgers, or state-size limits.
|
|
18
38
|
- Rotate the turn-stable specification on every non-retry user run, but not during its tool or terminal-validation retry chain. Keep user-controlled specification text at user authority: never interpolate it into the system prompt; repeat it only in synthetic user runtime context. Treat materialized state in that message as fallible assistant-produced data whose transport role does not elevate it into user instructions.
|
|
19
39
|
- When enabled inside an existing session, retain Pi's active context for exactly one complete bootstrap run and require its terminal handoff to migrate all future-relevant context.
|
|
20
|
-
- Restore extension state from the active session branch, not the full session entry list
|
|
21
|
-
- Render compact status as accent `state-flow` plus dim `#<
|
|
22
|
-
- `/state-flow-
|
|
40
|
+
- Restore extension state from the active Pi session branch's checkpoint and recorded State Flow revision, not the full session entry list or current Git `HEAD`, on both startup and successful in-session tree navigation.
|
|
41
|
+
- Render compact status as accent `state-flow` plus dim `#<step>`. `/state-flow-status` must distinguish session config/meta from semantic temporal materialization; show the CWD and session keys, step, active temporal head and State Flow revision, available hot-history depth, per-scope artifact/tail counts, discovered global Markdown stale reasons, pending push and terminal-retry state; and label global, CWD, session, and effective materializations without reading or dumping source bodies beyond the path/hash discovery needed for freshness diagnostics. Distinguish selected retained tail counts from active history depth; inherited tails may predate the origin. When temporal materialization is unavailable, report unknown counts/freshness and unavailable state rather than inventing empty projections.
|
|
42
|
+
- Explicit `/state-flow-start` creates the State Flow directory when missing. If Git is installed, initialize an exact-root Git repository when needed, including a populated file-only store, preserving all existing bytes and unrelated files; an ancestor repository is not a valid substitute. If Git is absent, use file persistence. Manual-mode startup/status/restore do not initialize Git; configured automatic start of a genuinely new session uses the same initialization as explicit start. Never create external accounts, remote repositories, credentials, or remote configuration; those remain operator-owned. Never auto-import, delete, or reset files/history in a previous Knowledge-backed store; old branch revisions require their original Git history to remain available in the selected store. `/state-flow-start` must initialize missing global, CWD, and current-session checkpoint/tail pairs plus session config/meta through compare-and-swap publication when required, enable only the current session branch, and bootstrap prior conversation when needed. Explicit start on a proven pre-runtime branch (no checkpoint or an ordinary-disabled marker) establishes an empty session origin rather than importing a later same-session layer; validate existing runtime identity, retain shared streams unchanged, and preserve later branch data in cold Git history. Ordinary new sessions remain manual unless agent-level `autoStart` is true; CWD materialization alone grants no automatic activation. Configured new sessions may initialize missing CWD state and receive distinct empty session layers while inheriting global/CWD values. Resumed and tree-selected branches restore their own config and temporal lineage, regardless of the global flag.
|
|
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 does not rewrite agent-level `autoStart` or change its policy for future new sessions.
|
|
23
44
|
- Keep the injected runtime protocol compact and normative; put rationale and extended explanation in README rather than the model prompt. Strip accidental State Flow comments from tool-bearing output only when a complete structurally valid envelope (valid JSON fields, exact separator, and non-empty response) leads the first text block; never delete malformed, quoted, or embedded examples.
|
|
24
45
|
- Do not claim strict boundedness for state, the current run trajectory, the turn specification, or the external full trace.
|
|
25
46
|
- Remain extension-agnostic: do not import, name, special-case, or encode policy for any other extension or transport.
|
|
26
|
-
-
|
|
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.
|
|
27
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.
|
|
28
49
|
- Run `npm run validate` after retained code changes.
|
|
@@ -1,3 +1,126 @@
|
|
|
1
|
-
#
|
|
1
|
+
# BACKLOG
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Canonical proposed work for **pi-state-flow 0.6.0 — Continuity without fixation**.
|
|
4
|
+
|
|
5
|
+
Baseline: **0.5.0**, commit `feed43c33db7f4a7f9c10a7eda619a2ffc2c91b5`, inspected on 2026-09-09. This document specifies work; it does not report implementation or validation results. Reconcile with newer repository work before editing.
|
|
6
|
+
|
|
7
|
+
## Release contract
|
|
8
|
+
|
|
9
|
+
A fresh State Flow run should inherit the consequences of previous work: active commitments, established observations, bounded negative results, meaningful corrections, unresolved decision-relevant uncertainty, and the exact point of continuation. It should not inherit an unapproved method as a requirement or an earlier inference as established truth.
|
|
10
|
+
|
|
11
|
+
Implement this through the existing runtime protocol, the explicit curation Skill, and focused regression coverage. Do not build a new memory engine. More novelty, smaller state, and zero repeated work are not independent success criteria.
|
|
12
|
+
|
|
13
|
+
**Done:** the runtime and Skill express consistent memory rules; the documented curation sequence is executable with current tools and compilation obligations; compatibility regressions pass. Semantic quality remains a model-judgment boundary rather than a deterministic transport claim.
|
|
14
|
+
|
|
15
|
+
## Fixed boundaries
|
|
16
|
+
|
|
17
|
+
- Keep the semantic shape exactly `artifacts`, `contract`, `working`, `response`, with existing flexible object contents and `global → cwd → session` overlay.
|
|
18
|
+
- Preserve `checkpoint.json` / `patches.jsonl`, shared causal boundaries, hot offsets 0–7, Git/file-only guarantees, branch restoration, and local acceptance versus remote publication.
|
|
19
|
+
- Preserve the native tool loop, complete current-run trajectory, inspectable Pi trace, bootstrap behavior, and removal of completed trajectories only at user-run boundaries. No extra resets or forced exploratory reruns.
|
|
20
|
+
- Keep `patch_state` single-scope and the sole semantic mutation tool. Keep terminal multi-scope reconciliation, runtime-owned response, and strict inference barriers.
|
|
21
|
+
- No required per-item confidence/provenance/status schema, additional memory store, semantic truth validator, automatic curation, state-size gate, or mandatory historical/source reread.
|
|
22
|
+
- No automatic permission expansion, external publication, or historical erasure. Scope is applicability, not instruction authority.
|
|
23
|
+
- No blockchain/IPFS/JAM integration, model marketplace, speculative alternate-agent execution, or native auto-resume work in this release.
|
|
24
|
+
|
|
25
|
+
## Ordered work
|
|
26
|
+
|
|
27
|
+
### [x] SF-060-01 — Make ordinary runtime handoffs preserve continuity without fixing the method
|
|
28
|
+
|
|
29
|
+
**Primary files:** `lib/terminal.ts`, `lib/extension.ts`, `tests/terminal.test.ts`. Align normative wording in `AGENTS.md`.
|
|
30
|
+
|
|
31
|
+
Replace or consolidate existing memory guidance rather than appending another large protocol block. Keep the protocol independent of user-controlled text and stable during the current run and its retry chain.
|
|
32
|
+
|
|
33
|
+
Required semantics:
|
|
34
|
+
|
|
35
|
+
- Remove the blanket prohibition on retaining speculation. Exclude speculative clutter and unsupported assertions, but retain explicitly uncertain hypotheses when they can affect an open decision.
|
|
36
|
+
- Distinguish user requirements, confirmed decisions, observations, assistant conclusions, and provisional methods. Silence or repeated assistant assertion is not user acceptance. Do not demote confirmed decisions merely to encourage search.
|
|
37
|
+
- Preserve interaction consequences when relevant: proposals awaiting acceptance, corrections, unresolved questions, settled explanations, and referents needed for the next follow-up. Do not synthesize shared history or a personality dossier.
|
|
38
|
+
- Retain a consequential result at its demonstrated boundary: tested mechanism, conditions, outcome, and an existing useful evidence locator. A failed implementation does not disprove every implementation; one success does not establish unrestricted validity.
|
|
39
|
+
- Preserve exact rejection reasons and known reconsideration conditions. Reconsideration may follow a different mechanism, changed conditions, a discriminating test, or a specific verification need; do not manufacture alternatives or rerun unchanged failures without a reason.
|
|
40
|
+
- Preserve completed prerequisites and verified outcomes that remain relevant; remove obsolete progress narration, not the fact that work was completed.
|
|
41
|
+
- Reconcile information affected by the current run and relevant existing commitments. Do not require a repository-wide or all-scope maintenance audit on every ordinary turn. Keep terminal reconciliation; keep explicit curation separate.
|
|
42
|
+
- Keep `working` as last observations, not live external state. Preserve targeted revalidation needs without promising rollback or exactly-once effects.
|
|
43
|
+
- Clarify that matching source hash/compiler metadata establishes source-version consistency, not semantic fidelity or higher instruction authority. Remove ambiguous use of "authoritative" for a fallible Skill compilation; current instructions remain controlling.
|
|
44
|
+
|
|
45
|
+
Update `patch_state` description/guidance to permit a necessary write-and-verify step in explicitly requested curation, in addition to meaningful loss/recovery-risk barriers. A known uncertainty may be worth preserving without asserting its underlying hypothesis as true. Do not create a new curation mode, flag, or automatic call requirement.
|
|
46
|
+
|
|
47
|
+
**Acceptance:** no conflicting blanket ban remains in the emitted protocol; no compulsory item schema or extra model call appears; ordinary no-memory-change answers remain valid; current-run tool/retry context and user authority are unchanged. Record before/after protocol size as a diagnostic, not an arbitrary pass/fail cap.
|
|
48
|
+
|
|
49
|
+
### [x] SF-060-02 — Ship the revised bounded curation Skill
|
|
50
|
+
|
|
51
|
+
**Primary files:** `skills/state-flow-memory/SKILL.md`, `tests/skills.test.ts`.
|
|
52
|
+
|
|
53
|
+
Use the revised Skill supplied with this task as the editorial baseline. Keep it self-contained and explicit-only; do not inject its full inventory/migration procedure into every runtime prompt.
|
|
54
|
+
|
|
55
|
+
Required outcomes:
|
|
56
|
+
|
|
57
|
+
- Add `reframe` alongside keep/update/narrow/promotion/remove. These are audit choices, not mandatory stored labels.
|
|
58
|
+
- Preserve useful uncertainty, commitments, interaction consequences, bounded learning, source recoverability, and the distinction between a requirement and a provisional method.
|
|
59
|
+
- Include one bounded fresh-run review: what must still hold; what changed; what remains open; what omission would cause repetition or lost commitments; what retained claim would impose an unjustified method.
|
|
60
|
+
- Use the narrowest valid scope. Resolve destination conflicts before movement; write and verify the destination before deleting the source; inspect the effective overlay afterward. Separate tool calls are not atomic migration.
|
|
61
|
+
- Preserve an accepted source copy on unverified external promotion. Verify destination identity, content and revision through the actual interface; a stored assertion of acceptance is not a receipt. Preserve routing necessary for later retrieval.
|
|
62
|
+
- Report partial migration, unavailable evidence, and historical secret-retention limits truthfully. Do not expose secrets in the report or equate active-state deletion with historical erasure.
|
|
63
|
+
- Stop after the requested cohort, including an unchanged or blocked result. Do not start a project investigation or an automatic audit merely to improve memory.
|
|
64
|
+
|
|
65
|
+
**Acceptance:** Pi discovers the Skill without diagnostics; its activation remains explicit; the revised policy does not contradict SF-060-01; tests assert the relevant contract rather than preserving obsolete wording. A justified reread of the changed Skill refreshes its existing path-keyed artifact through normal compilation. Do not invalidate unrelated artifacts or invent a new compiler revision solely for this prose change.
|
|
66
|
+
|
|
67
|
+
### [x] SF-060-03 — Prove the Skill can complete curation through existing barriers
|
|
68
|
+
|
|
69
|
+
**Primary files:** `lib/extension.ts`, `lib/transition.ts` (verification targets); `tests/skills.test.ts`, `tests/transition.test.ts`, `tests/integration.test.ts`; relevant protocol/Skill instructions.
|
|
70
|
+
|
|
71
|
+
The baseline already clears successful acquisition trackers after an accepted `commitStage`. Do not add a new tracker lifecycle on the assumption that accepted reads remain pending. The actual scheduling constraint is that pending Skill reads require CWD compilation and pending invalidated ordinary Markdown reads require global compilation at the next accepting transition.
|
|
72
|
+
|
|
73
|
+
Document and regression-test this executable sequence:
|
|
74
|
+
|
|
75
|
+
1. Read the curation Skill when needed; accept its required CWD compilation before accumulating a global compilation obligation.
|
|
76
|
+
2. Read the smallest required state projections. Read a stale ordinary Markdown source only for a justified gap; accept its global compilation before proceeding with unrelated single-scope writes.
|
|
77
|
+
3. Write the migration destination using `patch_state`, verify it with a separate `read_state`, then delete/narrow the source and verify the resulting scope/effective state.
|
|
78
|
+
4. Complete one terminal reconciliation without repeating already accepted compilations or inventing memory changes.
|
|
79
|
+
|
|
80
|
+
Do not depend on read-after-write inspection following a terminal answer; readback must occur after tool barriers, before final reporting. Do not weaken compiler validation or introduce multi-scope `patch_state` to solve a scheduling problem.
|
|
81
|
+
|
|
82
|
+
Cover these boundaries:
|
|
83
|
+
|
|
84
|
+
- A Skill read followed by an unrelated session/global patch without required compilation is rejected without accepting state; the documented compilation-first route succeeds.
|
|
85
|
+
- Simultaneously pending CWD/global acquisition remains subject to the existing complete-compilation rule. Test rejection of insufficient single-scope patches and acceptance through existing multi-scope terminal reconciliation. Do not silently discard obligations or claim an unperformed curation was verified.
|
|
86
|
+
- Accepted compilation clears only completed acquisition work; a failed acceptance does not authorize dropping it. Existing trusted hash/compiler and source-refresh checks remain effective.
|
|
87
|
+
- Destination failure preserves the source; source-deletion failure leaves a recoverable duplicate and is reported as incomplete. Inherited values exposed by deletion are visible to verification.
|
|
88
|
+
- No-op memory curation introduces no fabricated memory changes. A changed final response may still create a semantic transition under the existing contract.
|
|
89
|
+
- Reader siblings remain blocked by `patch_state`; read-only verification creates no transition. A successful local acceptance is not repeated because remote replication failed.
|
|
90
|
+
|
|
91
|
+
**Acceptance:** tests exercise actual tool/event ordering, not only hand-authored terminal patches. Use the real Pi SDK fixture where lifecycle behavior matters, clearly labeling its scripted provider. Existing validators and public tool schemas remain intact. Change production logic only for an observed regression within this sequence, with a reproducer; no speculative refactor.
|
|
92
|
+
|
|
93
|
+
### [x] SF-060-05 — Align documentation, preserve compatibility, and prepare release
|
|
94
|
+
|
|
95
|
+
**Primary files:** `AGENTS.md`, `README.md`, `docs/architecture.md`, `CHANGELOG.md`, `package.json`, `package-lock.json`, and this backlog.
|
|
96
|
+
|
|
97
|
+
- Describe ordinary handoff versus explicit curation consistently, including useful uncertainty, confirmed decisions, bounded negative results and interaction consequences.
|
|
98
|
+
- Remove conditional language suggesting global memory is a feature switch. Clarify that a valid state/receipt does not prove semantic truth, useful curation, or historical deletion.
|
|
99
|
+
- Document compilation-first curation scheduling and the existing mixed-acquisition limitation. Do not advertise a new atomic multi-scope tool operation.
|
|
100
|
+
- Keep existing 0.5.0 Git-backed and file-only states readable without schema migration, bulk rewrite or invented semantic transitions. Protocol changes do not retroactively certify old memory or rewrite historical revisions. Refresh changed Skill content through justified acquisition, not a global startup rebuild.
|
|
101
|
+
- Keep current-run/native trace behavior, temporal offsets, scope isolation, response finalization and publication behavior covered by the existing test suite. Add only focused compatibility cases that are missing.
|
|
102
|
+
- Prepare version 0.6.0 in package and lockfile, concise release notes, and a requirement-to-test/evidence map for SF-060-01 through SF-060-03. Record what was actually run and any remaining limitations.
|
|
103
|
+
|
|
104
|
+
**Acceptance:** `npm run validate` passes; packed content includes the revised Skill and documentation; no new storage/config/tool contract or production dependency is introduced. Packaging and release follow existing repository gates. Preparing the release does not authorize commits, tags, pushes, npm publication or GitHub release creation.
|
|
105
|
+
|
|
106
|
+
## Dependencies and stopping rule
|
|
107
|
+
|
|
108
|
+
`SF-060-01 → SF-060-02 → SF-060-03`; `SF-060-05` closes the release.
|
|
109
|
+
|
|
110
|
+
Each item closes with its diff, targeted proof and unresolved limitations. Split a large proof into smaller cases under the same item; do not enlarge product scope. Newly discovered adjacent issues go into a deferred section unless they prevent this release contract from holding.
|
|
111
|
+
|
|
112
|
+
Stop when the four items and release checks are satisfied. Do not prolong 0.6.0 to seek universal semantic guarantees or a redesigned memory system.
|
|
113
|
+
|
|
114
|
+
## Deferred host integration — preserved from 0.5.0
|
|
115
|
+
|
|
116
|
+
- [ ] **Native default session continuation:** Integrate existing read-only recommendation, exact selection and knowledge-bootstrap contracts before Pi creates `SessionManager`, preserving explicit new/resume precedence, truthful notices and cross-process session ownership. The recorded 0.5.0 blocker is the lack of a suitable Pi pre-session resolver hook; reverify upstream support when taking this item. It is not a 0.6.0 release dependency.
|
|
117
|
+
|
|
118
|
+
## Baseline evidence
|
|
119
|
+
|
|
120
|
+
The findings above come from inspection of this pinned revision, not an execution of its tests:
|
|
121
|
+
|
|
122
|
+
- [Runtime protocol and terminal handling](https://github.com/llblab/pi-state-flow/blob/feed43c33db7f4a7f9c10a7eda619a2ffc2c91b5/lib/terminal.ts): blanket speculation prohibition coexists with preserving decision-relevant hypotheses; terminal reconciliation already exists.
|
|
123
|
+
- [Extension wiring](https://github.com/llblab/pi-state-flow/blob/feed43c33db7f4a7f9c10a7eda619a2ffc2c91b5/lib/extension.ts): compilation trackers clear after accepted commit; context projection and barrier lifecycle are already implemented.
|
|
124
|
+
- [Transition validation](https://github.com/llblab/pi-state-flow/blob/feed43c33db7f4a7f9c10a7eda619a2ffc2c91b5/lib/transition.ts): required compiler outputs are scoped, and the single-scope tool and multi-scope terminal share staging.
|
|
125
|
+
- [Existing curation tests](https://github.com/llblab/pi-state-flow/blob/feed43c33db7f4a7f9c10a7eda619a2ffc2c91b5/tests/skills.test.ts): the existing narrowing example uses a terminal multi-scope patch rather than the complete read/compile/write/readback sequence.
|
|
126
|
+
- [Contributor invariants](https://github.com/llblab/pi-state-flow/blob/feed43c33db7f4a7f9c10a7eda619a2ffc2c91b5/AGENTS.md) and [existing backlog](https://github.com/llblab/pi-state-flow/blob/feed43c33db7f4a7f9c10a7eda619a2ffc2c91b5/BACKLOG.md): unchanged engine boundaries and deferred host work.
|