@llblab/pi-kit 0.10.7 → 0.11.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 +8 -0
- package/README.md +2 -2
- package/node_modules/@llblab/pi-state-flow/AGENTS.md +13 -10
- package/node_modules/@llblab/pi-state-flow/BACKLOG.md +15 -1
- package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +13 -0
- package/node_modules/@llblab/pi-state-flow/README.md +52 -261
- package/node_modules/@llblab/pi-state-flow/docs/README.md +5 -1
- package/node_modules/@llblab/pi-state-flow/docs/architecture.md +47 -26
- package/node_modules/@llblab/pi-state-flow/docs/compatibility.md +97 -0
- package/node_modules/@llblab/pi-state-flow/docs/fork-contract.md +47 -0
- package/node_modules/@llblab/pi-state-flow/docs/performance.md +459 -0
- package/node_modules/@llblab/pi-state-flow/docs/temporal-acceptance.md +35 -3
- package/node_modules/@llblab/pi-state-flow/docs/usage.md +134 -0
- package/node_modules/@llblab/pi-state-flow/lib/artifact.ts +19 -3
- package/node_modules/@llblab/pi-state-flow/lib/compaction.ts +74 -0
- package/node_modules/@llblab/pi-state-flow/lib/context.ts +12 -12
- package/node_modules/@llblab/pi-state-flow/lib/continuation.ts +4 -2
- package/node_modules/@llblab/pi-state-flow/lib/discovery.ts +21 -5
- package/node_modules/@llblab/pi-state-flow/lib/extension.ts +146 -37
- package/node_modules/@llblab/pi-state-flow/lib/git.ts +142 -42
- package/node_modules/@llblab/pi-state-flow/lib/publication.ts +80 -27
- package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +109 -7
- package/node_modules/@llblab/pi-state-flow/lib/status.ts +7 -12
- package/node_modules/@llblab/pi-state-flow/lib/storage.ts +2 -1
- package/node_modules/@llblab/pi-state-flow/lib/transition.ts +2 -3
- package/node_modules/@llblab/pi-state-flow/package.json +5 -4
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +4 -0
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +1 -1
- package/node_modules/@llblab/pi-telegram/lib/extension.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/ownership.ts +28 -7
- package/node_modules/@llblab/pi-telegram/lib/updates.ts +3 -10
- package/node_modules/@llblab/pi-telegram/package.json +1 -1
- package/package.json +3 -3
|
@@ -57,16 +57,11 @@ export function detailedStatus(snapshot: Snapshot, diagnostics: StatusDiagnostic
|
|
|
57
57
|
diagnostics.recent,
|
|
58
58
|
);
|
|
59
59
|
const available = diagnostics.temporal !== undefined && diagnostics.durableStateError === undefined;
|
|
60
|
-
const materialized = !available ? undefined :
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
diagnostics.scopeStates.global,
|
|
66
|
-
diagnostics.scopeStates.cwd,
|
|
67
|
-
diagnostics.scopeStates.session,
|
|
68
|
-
),
|
|
69
|
-
};
|
|
60
|
+
const materialized = !available ? undefined : overlayStates(
|
|
61
|
+
diagnostics.scopeStates.global,
|
|
62
|
+
diagnostics.scopeStates.cwd,
|
|
63
|
+
diagnostics.scopeStates.session,
|
|
64
|
+
);
|
|
70
65
|
const stateJson = materialized === undefined ? undefined : JSON.stringify(materialized, null, 2);
|
|
71
66
|
const freshnessError = diagnostics.artifactFreshnessError ?? (available ? undefined : "temporal global artifact registry is unavailable");
|
|
72
67
|
const stale = freshnessError === undefined
|
|
@@ -124,7 +119,7 @@ export function detailedStatus(snapshot: Snapshot, diagnostics: StatusDiagnostic
|
|
|
124
119
|
`Publication: ${publication}`,
|
|
125
120
|
...staleLines,
|
|
126
121
|
...(stateJson === undefined
|
|
127
|
-
? ["
|
|
128
|
-
: [`
|
|
122
|
+
? ["Effective memory: unavailable"]
|
|
123
|
+
: [`Effective memory (${Buffer.byteLength(stateJson, "utf8")} JSON bytes; global → CWD → session overlay):`, "", stateJson]),
|
|
129
124
|
].join("\n");
|
|
130
125
|
}
|
|
@@ -90,7 +90,8 @@ export function planTemporalPublication(
|
|
|
90
90
|
changedScopes.push(scope);
|
|
91
91
|
}
|
|
92
92
|
const provenanceUpdates: OwnedFileUpdate[] = [];
|
|
93
|
-
|
|
93
|
+
// Shared provenance belongs to its semantic scopes, not the session config being saved.
|
|
94
|
+
if (!runtimeOnly && provenance !== undefined) {
|
|
94
95
|
for (const scope of ["global", "cwd"] as const) {
|
|
95
96
|
const paths = temporalScopePaths(cwd, sessionId, scope, root, sessionKey);
|
|
96
97
|
const registry = provenance[scope];
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
ORDINARY_ARTIFACT_COMPILER,
|
|
4
4
|
validateArtifactMetadata,
|
|
5
5
|
validateArtifactRegistry,
|
|
6
|
+
validateModelArtifactPatch,
|
|
6
7
|
type ArtifactCompilerOutput,
|
|
7
8
|
type ArtifactProvenance,
|
|
8
9
|
} from "./artifact.ts";
|
|
@@ -77,9 +78,6 @@ function compileReadSkills(
|
|
|
77
78
|
if (!isObject(output)) {
|
|
78
79
|
throw new Error(`Every successfully read Skill must have a CWD artifact compiler output at artifacts[exactReadPath]; missing: ${read.path}`);
|
|
79
80
|
}
|
|
80
|
-
if (Object.hasOwn(output, "hash") || Object.hasOwn(output, "compiler")) {
|
|
81
|
-
throw new Error(`Skill artifact compiler output at ${read.path} cannot set runtime-owned hash or compiler fields`);
|
|
82
|
-
}
|
|
83
81
|
if (typeof output.description !== "string" || output.description.trim().length === 0) {
|
|
84
82
|
throw new Error(`Skill artifact compiler output at ${read.path} must have a non-empty description`);
|
|
85
83
|
}
|
|
@@ -133,6 +131,7 @@ function validateScopePatch(scope: unknown, patch: unknown): asserts patch is Sc
|
|
|
133
131
|
throw new Error(`Scoped State Flow patch field ${key} must be a JSON object`);
|
|
134
132
|
}
|
|
135
133
|
}
|
|
134
|
+
if (isObject(patch.artifacts)) validateModelArtifactPatch(patch.artifacts);
|
|
136
135
|
}
|
|
137
136
|
|
|
138
137
|
function completePatch(patch: ScopePatch, response: string): StatePatch {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llblab/pi-state-flow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Incremental scoped state/context compiler for Pi, inspired by SKILL.state",
|
|
6
6
|
"keywords": [
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"scripts": {
|
|
31
31
|
"check": "node --experimental-strip-types -e \"await import('./index.ts')\"",
|
|
32
32
|
"test": "node --experimental-strip-types --test tests/*.test.ts",
|
|
33
|
+
"benchmark": "node --experimental-strip-types benchmarks/benchmark.ts",
|
|
33
34
|
"typecheck": "tsc --noEmit",
|
|
34
35
|
"validate": "npm run typecheck && npm test && npm run check",
|
|
35
36
|
"prepack": "npm run validate"
|
|
@@ -50,9 +51,9 @@
|
|
|
50
51
|
"node": ">=22.19.0"
|
|
51
52
|
},
|
|
52
53
|
"peerDependencies": {
|
|
53
|
-
"@earendil-works/pi-agent-core": "^0.84.4",
|
|
54
|
-
"@earendil-works/pi-ai": "^0.84.4",
|
|
55
|
-
"@earendil-works/pi-coding-agent": "^0.84.4"
|
|
54
|
+
"@earendil-works/pi-agent-core": "^0.84.4 || ^0.85.1",
|
|
55
|
+
"@earendil-works/pi-ai": "^0.84.4 || ^0.85.1",
|
|
56
|
+
"@earendil-works/pi-coding-agent": "^0.84.4 || ^0.85.1"
|
|
56
57
|
},
|
|
57
58
|
"devDependencies": {
|
|
58
59
|
"@types/node": "latest",
|
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.45.8: Follower Forwarding Hotfix
|
|
8
|
+
|
|
9
|
+
- `Follower Forwarding`: Message-ownership lookups now project the matching live registration's protocol identity before forwarding. Voice/message and edit retries, message-only callbacks, and reactions no longer stall on an incomplete cached owner after a rejection or re-registration. Exact generation, binding, protocol, and durable-receipt checks remain enforced.
|
|
10
|
+
|
|
7
11
|
## 0.45.7: Queue Enqueue Race Hotfix
|
|
8
12
|
|
|
9
13
|
- `Queue Enqueue`: Asynchronous voice/file preparation no longer restores consumed prompts or overwrites newer queue changes, preventing a settled phantom head from blocking accepted work. Final assembly uses current queue state and allocates order at commit; abort-history folding retains only surviving intended prompts and receipts while preserving a handed-off head until `agent_start`.
|
|
@@ -429,7 +429,7 @@ The initial `offset: -1` cursor bootstrap is allowed only when both cursor and j
|
|
|
429
429
|
|
|
430
430
|
Threaded Mode forwarding is a two-journal handoff. Only peers that mutually advertise protocol v1 and `durable-follower-admission-v1` may route or become election-eligible. The follower validates its exact binding and registration generation, durably appends the source-bound delivery, and only then returns the exact receipt. The leader classifies each attempt as `accepted`, `retryable`, or `terminal-rejected` with its delivery identity and failure class; only `accepted` with the expected `deliveryId` and `sourceUpdateId` may complete leader journal authority. Missing, negative, stale-generation, or mismatched-receipt acknowledgements remain durable, and a callback error answer is only an operator-facing side effect.
|
|
431
431
|
|
|
432
|
-
Delivery ids derive only from envelope kind, source `update_id`, and stable recipient binding. Live registration generation remains a separate attempt fence
|
|
432
|
+
Delivery ids derive only from envelope kind, source `update_id`, and stable recipient binding. Live registration generation remains a separate attempt fence. Message ownership carries the stable binding and rebinds to its current authenticated follower registration after replacement. `ownership.getForwardOwnership()` projects the protocol identity from the exact matching live instance, registration generation, and binding on every lookup; protocol is not cached with message history. The same port serves messages, edits, callbacks, and reactions, including callbacks without a Thread ID. If a cache lookup returns a foreign record but no matching durable-capable protocol can be projected, that record remains foreign and fails forwarding validation rather than falling through to local execution. The final bus validator independently rechecks generation, binding, and complete protocol identity before transport. Lost acknowledgements therefore retain the same delivery identity; only the exact durable receipt completes the source. Package build skew is allowed only while protocol version and capabilities remain compatible.
|
|
433
433
|
|
|
434
434
|
Worker execution ownership is per `update_id` across same-runtime generations. Aborting a generation ends its authority but does not prove its handler settled; replacement replay remains blocked on that exact settlement. Late success and failure are both diagnostic events. Public and built-in handlers receive the same optional execution fence (`signal`, generation/update identity, and pre-effect assertion); the runtime binds it non-enumerably to every internal update carrier and checks it before routing-plan effects. Prompt construction rechecks after downloads and inbound handlers before queue mutation, pairing rechecks around persistence, command/menu and extension-command delegation retain the source fence across detached effects, lifecycle sync rechecks after store load before reconciliation, and reroute clones carry the source fence through forwarding, thread replacement, cleanup, persistence, and Bot API rename boundaries. Legacy handlers remain source-compatible but must not commit unfenced late effects.
|
|
435
435
|
|
|
@@ -934,7 +934,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
934
934
|
},
|
|
935
935
|
getAdmissionScope: getTelegramUpdateAdmissionScope,
|
|
936
936
|
getAdmissionJournalBinding: getTelegramQueueJournalBinding,
|
|
937
|
-
getMessageOwnership: messageOwnershipRuntime.
|
|
937
|
+
getMessageOwnership: messageOwnershipRuntime.getForwardOwnership,
|
|
938
938
|
recordMessageOwnership: messageOwnershipRuntime.recordRouted,
|
|
939
939
|
...inboundBusProjectionRuntime,
|
|
940
940
|
getDisplayTitle: getThreadDisplayTitle,
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Telegram message ownership helpers
|
|
3
3
|
* Zones: telegram routing, multi-instance bus, in-memory coordination
|
|
4
|
-
* Owns
|
|
4
|
+
* Owns message-id ownership and live forwarding projections so inbound updates resolve back to the instance/target that owns them
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import {
|
|
8
|
+
TELEGRAM_BUS_CAPABILITY_DURABLE_FOLLOWER_ADMISSION,
|
|
9
|
+
type TelegramBusFollowerView,
|
|
10
|
+
type TelegramBusForwardOwnership,
|
|
11
|
+
} from "./bus.ts";
|
|
7
12
|
import { getTelegramTargetKey, type TelegramTarget } from "./target.ts";
|
|
8
13
|
|
|
9
14
|
export interface TelegramMessageOwnershipRecord {
|
|
@@ -44,15 +49,17 @@ export interface TelegramMessageOwnershipStore {
|
|
|
44
49
|
clear: () => void;
|
|
45
50
|
}
|
|
46
51
|
|
|
47
|
-
export
|
|
48
|
-
|
|
49
|
-
connectedAtMs
|
|
50
|
-
|
|
51
|
-
registrationGeneration?: string;
|
|
52
|
-
}
|
|
52
|
+
export type TelegramFollowerOwnershipView = Pick<
|
|
53
|
+
TelegramBusFollowerView,
|
|
54
|
+
"instanceId" | "connectedAtMs" | "profileKey" | "registrationGeneration" | "protocol"
|
|
55
|
+
>;
|
|
53
56
|
|
|
54
57
|
export interface TelegramBusMessageOwnershipRuntime {
|
|
55
58
|
store: TelegramMessageOwnershipStore;
|
|
59
|
+
getForwardOwnership(
|
|
60
|
+
chatId: number,
|
|
61
|
+
messageId: number,
|
|
62
|
+
): TelegramBusForwardOwnership | undefined;
|
|
56
63
|
recordLocal(input: {
|
|
57
64
|
chatId: number;
|
|
58
65
|
messageId: number;
|
|
@@ -137,6 +144,20 @@ export function createTelegramBusMessageOwnershipRuntime(deps: {
|
|
|
137
144
|
};
|
|
138
145
|
return {
|
|
139
146
|
store,
|
|
147
|
+
getForwardOwnership(chatId, messageId) {
|
|
148
|
+
const record = store.get(chatId, messageId);
|
|
149
|
+
if (!record?.ownerGeneration || !record.recipientBindingKey) return record;
|
|
150
|
+
const follower = deps.listFollowers().find((candidate) =>
|
|
151
|
+
candidate.instanceId === record.instanceId &&
|
|
152
|
+
candidate.registrationGeneration === record.ownerGeneration &&
|
|
153
|
+
candidate.profileKey === record.recipientBindingKey,
|
|
154
|
+
);
|
|
155
|
+
// Protocol is live registration authority, not message-cache history.
|
|
156
|
+
// Keep an incomplete known owner foreign so the forwarder fails closed.
|
|
157
|
+
return follower?.protocol?.capabilities.includes(
|
|
158
|
+
TELEGRAM_BUS_CAPABILITY_DURABLE_FOLLOWER_ADMISSION,
|
|
159
|
+
) ? { ...record, protocolIdentity: follower.protocol } : record;
|
|
160
|
+
},
|
|
140
161
|
recordLocal(input) {
|
|
141
162
|
return store.record({ ...input, instanceId: deps.instanceId });
|
|
142
163
|
},
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import type {
|
|
15
15
|
TelegramBusEnvelope,
|
|
16
16
|
TelegramBusFollowerView,
|
|
17
|
+
TelegramBusForwardOwnership,
|
|
17
18
|
TelegramBusForeignUpdateSettlement,
|
|
18
19
|
TelegramProcessLiveness,
|
|
19
20
|
} from "./bus.ts";
|
|
@@ -345,22 +346,14 @@ export function getAuthorizedTelegramGuestMessage(
|
|
|
345
346
|
|
|
346
347
|
// --- Flow ---
|
|
347
348
|
|
|
348
|
-
export
|
|
349
|
-
instanceId: string;
|
|
350
|
-
ownerGeneration?: string;
|
|
351
|
-
recipientBindingKey?: string;
|
|
352
|
-
}
|
|
349
|
+
export type TelegramMessageOwnershipView = TelegramBusForwardOwnership;
|
|
353
350
|
|
|
354
351
|
export type TelegramMessageOwnershipLookup = (
|
|
355
352
|
chatId: number,
|
|
356
353
|
messageId: number,
|
|
357
354
|
) => TelegramMessageOwnershipView | undefined;
|
|
358
355
|
|
|
359
|
-
export
|
|
360
|
-
instanceId: string;
|
|
361
|
-
ownerGeneration?: string;
|
|
362
|
-
recipientBindingKey?: string;
|
|
363
|
-
}
|
|
356
|
+
export type TelegramTargetOwnershipView = TelegramBusForwardOwnership;
|
|
364
357
|
|
|
365
358
|
export type TelegramTargetOwnershipLookup = (
|
|
366
359
|
target: TelegramTarget,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llblab/pi-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"@llblab/pi-clean-room": "0.1.1",
|
|
45
45
|
"@llblab/pi-codex-usage": "0.9.4",
|
|
46
46
|
"@llblab/pi-grow-loop": "0.8.1",
|
|
47
|
-
"@llblab/pi-state-flow": "0.
|
|
48
|
-
"@llblab/pi-telegram": "0.45.
|
|
47
|
+
"@llblab/pi-state-flow": "0.10.0",
|
|
48
|
+
"@llblab/pi-telegram": "0.45.8",
|
|
49
49
|
"@llblab/skills": "1.15.0"
|
|
50
50
|
},
|
|
51
51
|
"bundledDependencies": [
|