@llblab/pi-kit 0.27.5 → 0.28.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/BACKLOG.md +3 -1
- package/CHANGELOG.md +9 -0
- package/README.md +4 -4
- package/node_modules/@llblab/pi-state-flow/AGENTS.md +1 -1
- package/node_modules/@llblab/pi-state-flow/BACKLOG.md +6 -2
- package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +4 -1
- package/node_modules/@llblab/pi-state-flow/dist/lib/compaction.js +4 -3
- package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +26 -13
- package/node_modules/@llblab/pi-state-flow/dist/package.json +1 -1
- package/node_modules/@llblab/pi-state-flow/docs/architecture.md +3 -1
- package/node_modules/@llblab/pi-state-flow/docs/compatibility.md +2 -2
- package/node_modules/@llblab/pi-state-flow/docs/usage.md +2 -1
- package/node_modules/@llblab/pi-state-flow/lib/compaction.ts +4 -3
- package/node_modules/@llblab/pi-state-flow/lib/extension.ts +25 -12
- package/node_modules/@llblab/pi-state-flow/package.json +1 -1
- package/node_modules/@llblab/pi-telegram/AGENTS.md +10 -8
- package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -9
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +13 -0
- package/node_modules/@llblab/pi-telegram/LICENSE +21 -0
- package/node_modules/@llblab/pi-telegram/README.md +15 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.d.ts +1 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.js +5 -7
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +5 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +6 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.js +4 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.d.ts +54 -56
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.js +206 -139
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.d.ts +21 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.js +181 -25
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.d.ts +4 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.js +28 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/bus.d.ts +52 -56
- package/node_modules/@llblab/pi-telegram/dist/lib/bus.js +78 -246
- package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.d.ts +1 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.js +23 -26
- package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.d.ts +0 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.js +5 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +0 -23
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +15 -15
- package/node_modules/@llblab/pi-telegram/dist/lib/config.d.ts +0 -17
- package/node_modules/@llblab/pi-telegram/dist/lib/config.js +12 -14
- package/node_modules/@llblab/pi-telegram/dist/lib/delivery.d.ts +0 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/delivery.js +2 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +255 -61
- package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.d.ts +0 -19
- package/node_modules/@llblab/pi-telegram/dist/lib/inbound.d.ts +0 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/inbound.js +2 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/journal.d.ts +177 -16
- package/node_modules/@llblab/pi-telegram/dist/lib/journal.js +1115 -249
- package/node_modules/@llblab/pi-telegram/dist/lib/keyboard.d.ts +0 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/keyboard.js +2 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/locks.d.ts +100 -12
- package/node_modules/@llblab/pi-telegram/dist/lib/locks.js +380 -14
- package/node_modules/@llblab/pi-telegram/dist/lib/logging.d.ts +30 -12
- package/node_modules/@llblab/pi-telegram/dist/lib/logging.js +129 -72
- package/node_modules/@llblab/pi-telegram/dist/lib/media.d.ts +28 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/media.js +26 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.d.ts +0 -11
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.js +8 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.d.ts +0 -18
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.js +18 -18
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.d.ts +4 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.js +12 -7
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.d.ts +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/menu.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/menu.js +4 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.d.ts +0 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-buttons.js +1 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.d.ts +1 -6
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.js +4 -21
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound.d.ts +0 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound.js +5 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/paths.d.ts +50 -7
- package/node_modules/@llblab/pi-telegram/dist/lib/paths.js +155 -17
- package/node_modules/@llblab/pi-telegram/dist/lib/pi.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/pi.js +2 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/polling.d.ts +0 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/polling.js +5 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/preview.d.ts +0 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/preview.js +3 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/prompt-templates.d.ts +0 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/prompt-templates.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/queue.d.ts +3 -15
- package/node_modules/@llblab/pi-telegram/dist/lib/queue.js +69 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/recovery.d.ts +29 -9
- package/node_modules/@llblab/pi-telegram/dist/lib/recovery.js +104 -33
- package/node_modules/@llblab/pi-telegram/dist/lib/replies.d.ts +0 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/replies.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/routing.d.ts +73 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +2206 -307
- package/node_modules/@llblab/pi-telegram/dist/lib/sections.d.ts +0 -2
- package/node_modules/@llblab/pi-telegram/dist/lib/sections.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/status.d.ts +44 -9
- package/node_modules/@llblab/pi-telegram/dist/lib/status.js +149 -27
- package/node_modules/@llblab/pi-telegram/dist/lib/sync.d.ts +0 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/sync.js +5 -4
- package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.d.ts +11 -3
- package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.js +34 -23
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.d.ts +1 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.js +18 -21
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.d.ts +32 -5
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.js +191 -7
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.d.ts +2 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +291 -59
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +1647 -369
- package/node_modules/@llblab/pi-telegram/dist/lib/turns.d.ts +0 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/turns.js +1 -1
- package/node_modules/@llblab/pi-telegram/dist/lib/updates.d.ts +184 -28
- package/node_modules/@llblab/pi-telegram/dist/lib/updates.js +1032 -114
- package/node_modules/@llblab/pi-telegram/dist/lib/wire.d.ts +12 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/wire.js +21 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.d.ts +25 -17
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.js +95 -20
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-identity.d.ts +20 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-identity.js +103 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.d.ts +43 -8
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.js +148 -52
- package/node_modules/@llblab/pi-telegram/dist/package.json +6 -6
- package/node_modules/@llblab/pi-telegram/dist/skills/generated-control-surface/SKILL.md +3 -3
- package/node_modules/@llblab/pi-telegram/dist/skills/telegram-bridge/references/diagnosis.md +3 -3
- package/node_modules/@llblab/pi-telegram/docs/README.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/activity.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +152 -34
- package/node_modules/@llblab/pi-telegram/docs/callback-namespaces.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/delivery.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/generative-apps.md +2 -2
- package/node_modules/@llblab/pi-telegram/docs/inbound.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +125 -19
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +3 -1
- package/node_modules/@llblab/pi-telegram/docs/ui-style.md +22 -8
- package/node_modules/@llblab/pi-telegram/docs/updates.md +1 -1
- package/node_modules/@llblab/pi-telegram/lib/activity-verbosity.ts +5 -9
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +6 -0
- package/node_modules/@llblab/pi-telegram/lib/bus-api.ts +5 -2
- package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +243 -224
- package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +178 -31
- package/node_modules/@llblab/pi-telegram/lib/bus-transport.ts +30 -8
- package/node_modules/@llblab/pi-telegram/lib/bus.ts +110 -334
- package/node_modules/@llblab/pi-telegram/lib/channel-posts.ts +29 -29
- package/node_modules/@llblab/pi-telegram/lib/command-templates.ts +5 -5
- package/node_modules/@llblab/pi-telegram/lib/commands.ts +15 -15
- package/node_modules/@llblab/pi-telegram/lib/config.ts +12 -17
- package/node_modules/@llblab/pi-telegram/lib/delivery.ts +2 -8
- package/node_modules/@llblab/pi-telegram/lib/extension.ts +257 -72
- package/node_modules/@llblab/pi-telegram/lib/generative-apps.ts +0 -22
- package/node_modules/@llblab/pi-telegram/lib/inbound.ts +2 -2
- package/node_modules/@llblab/pi-telegram/lib/journal.ts +1182 -326
- package/node_modules/@llblab/pi-telegram/lib/keyboard.ts +2 -2
- package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/locks.ts +402 -23
- package/node_modules/@llblab/pi-telegram/lib/logging.ts +151 -101
- package/node_modules/@llblab/pi-telegram/lib/media.ts +51 -9
- package/node_modules/@llblab/pi-telegram/lib/menu-model.ts +8 -8
- package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +18 -18
- package/node_modules/@llblab/pi-telegram/lib/menu-status.ts +11 -0
- package/node_modules/@llblab/pi-telegram/lib/menu-thinking.ts +2 -2
- package/node_modules/@llblab/pi-telegram/lib/menu.ts +5 -1
- package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/outbound-buttons.ts +1 -10
- package/node_modules/@llblab/pi-telegram/lib/outbound-markup.ts +5 -24
- package/node_modules/@llblab/pi-telegram/lib/outbound.ts +5 -5
- package/node_modules/@llblab/pi-telegram/lib/paths.ts +177 -17
- package/node_modules/@llblab/pi-telegram/lib/pi.ts +5 -2
- package/node_modules/@llblab/pi-telegram/lib/polling.ts +5 -5
- package/node_modules/@llblab/pi-telegram/lib/preview.ts +3 -3
- package/node_modules/@llblab/pi-telegram/lib/prompt-templates.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/queue.ts +67 -9
- package/node_modules/@llblab/pi-telegram/lib/recovery.ts +104 -44
- package/node_modules/@llblab/pi-telegram/lib/replies.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/routing.ts +1955 -375
- package/node_modules/@llblab/pi-telegram/lib/sections.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/status.ts +156 -36
- package/node_modules/@llblab/pi-telegram/lib/sync.ts +4 -4
- package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +48 -33
- package/node_modules/@llblab/pi-telegram/lib/thread-cleanup-manager.ts +24 -21
- package/node_modules/@llblab/pi-telegram/lib/thread-naming.ts +267 -9
- package/node_modules/@llblab/pi-telegram/lib/thread-reconciler.ts +3 -1
- package/node_modules/@llblab/pi-telegram/lib/threads.ts +1697 -488
- package/node_modules/@llblab/pi-telegram/lib/turns.ts +1 -1
- package/node_modules/@llblab/pi-telegram/lib/updates.ts +1072 -151
- package/node_modules/@llblab/pi-telegram/lib/wire.ts +28 -0
- package/node_modules/@llblab/pi-telegram/lib/workspace-admission.ts +97 -47
- package/node_modules/@llblab/pi-telegram/lib/workspace-identity.ts +147 -0
- package/node_modules/@llblab/pi-telegram/lib/workspace-retirement.ts +188 -89
- package/node_modules/@llblab/pi-telegram/package.json +6 -6
- package/node_modules/@llblab/pi-telegram/scripts/audit-exports.mjs +100 -0
- package/node_modules/@llblab/pi-telegram/scripts/check-downgrade.mjs +80 -43
- package/node_modules/@llblab/pi-telegram/skills/generated-control-surface/SKILL.md +3 -3
- package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/references/diagnosis.md +3 -3
- package/package.json +3 -3
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* successor recovery, and fenced one-shot deletion before durable slot reuse.
|
|
6
6
|
*/
|
|
7
7
|
import { isDeepStrictEqual } from "node:util";
|
|
8
|
+
import { areTelegramTargetsEqual as sameTarget } from "./target.js";
|
|
8
9
|
import { getTelegramApiErrorRequestTarget, isTelegramApiRequestRejected, } from "./telegram-api.js";
|
|
9
10
|
import { planTelegramWorkspaceSlotAllocation, TelegramWorkspaceSlotUnavailableError, } from "./workspace-slots.js";
|
|
10
11
|
import { createTelegramWorkspaceAdmissionOperationId, isTelegramWorkspaceRetirementFence, runWithTelegramWorkspaceAdmissionsAsync, } from "./workspace-admission.js";
|
|
@@ -55,7 +56,8 @@ export function captureTelegramWorkspaceJournalProtectionSources(input) {
|
|
|
55
56
|
const snapshot = input.withJournalReference
|
|
56
57
|
? input.withJournalReference(binding, read)
|
|
57
58
|
: read();
|
|
58
|
-
sources.push({ kind: "available", scope, entries: snapshot.entries
|
|
59
|
+
sources.push({ kind: "available", scope, entries: snapshot.entries,
|
|
60
|
+
...(snapshot.exists === undefined ? {} : { exists: snapshot.exists }) });
|
|
59
61
|
}
|
|
60
62
|
catch {
|
|
61
63
|
complete = false;
|
|
@@ -70,6 +72,10 @@ export function captureTelegramWorkspaceJournalProtectionSources(input) {
|
|
|
70
72
|
journalBindingKey,
|
|
71
73
|
}, input.createFollowerResolver(journalBindingKey));
|
|
72
74
|
}
|
|
75
|
+
for (const source of input.binding.journalSources ?? []) {
|
|
76
|
+
capture({ kind: "binding", bindingKey: input.binding.bindingKey,
|
|
77
|
+
journalBindingKey: source.recipientBindingKey, sessionId: source.sessionId }, input.createSessionResolver?.(source.recipientBindingKey, source.sessionId) ?? (() => undefined));
|
|
78
|
+
}
|
|
73
79
|
for (const path of input.discovery?.paths ?? []) {
|
|
74
80
|
capture({ kind: "discovered", path }, input.discovery.createResolver(path));
|
|
75
81
|
}
|
|
@@ -101,9 +107,6 @@ function getJournalUpdateTarget(update) {
|
|
|
101
107
|
...(typeof threadId === "number" ? { threadId } : {}),
|
|
102
108
|
};
|
|
103
109
|
}
|
|
104
|
-
function sameTarget(left, right) {
|
|
105
|
-
return left.chatId === right.chatId && left.threadId === right.threadId;
|
|
106
|
-
}
|
|
107
110
|
export function resolveTelegramWorkspaceAcceptedWorkProtection(input) {
|
|
108
111
|
if (input.localAcceptedTargets.some((target) => sameTarget(target, input.binding.target))) {
|
|
109
112
|
return "protected";
|
|
@@ -118,9 +121,17 @@ export function resolveTelegramWorkspaceAcceptedWorkProtection(input) {
|
|
|
118
121
|
unknown = true;
|
|
119
122
|
continue;
|
|
120
123
|
}
|
|
124
|
+
if (input.requireBindingProvenance && source.scope.kind === "binding" && source.exists === false) {
|
|
125
|
+
unknown = true;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
121
128
|
if (source.scope.kind === "binding" && source.entries.length > 0) {
|
|
122
129
|
return "protected";
|
|
123
130
|
}
|
|
131
|
+
if (input.requireBindingProvenance && source.entries.length > 0) {
|
|
132
|
+
unknown = true;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
124
135
|
for (const entry of source.entries) {
|
|
125
136
|
const target = getJournalUpdateTarget(entry.update);
|
|
126
137
|
if (!target) {
|
|
@@ -144,49 +155,73 @@ export async function pruneTelegramWorkspaceJournalEvidence(input) {
|
|
|
144
155
|
if (!isCurrent()) {
|
|
145
156
|
throw new Error("Telegram Workspace journal pruning requires current leader authority.");
|
|
146
157
|
}
|
|
147
|
-
|
|
148
|
-
|
|
158
|
+
let capture;
|
|
159
|
+
try {
|
|
160
|
+
capture = input.capture();
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
return { kind: "blocked", reason: "incomplete-evidence" };
|
|
164
|
+
}
|
|
165
|
+
const address = (key, sessionId) => JSON.stringify([sessionId ?? null, key]);
|
|
166
|
+
const keys = input.binding.journalBindingKeys ?? [];
|
|
167
|
+
const sessions = input.binding.journalSources ?? [];
|
|
168
|
+
const expected = [
|
|
169
|
+
...keys.map(key => address(key)),
|
|
170
|
+
...sessions.map(source => address(source.recipientBindingKey, source.sessionId)),
|
|
171
|
+
];
|
|
172
|
+
const shared = capture.sources.filter(source => source.scope.kind === "shared");
|
|
173
|
+
const bindingSources = capture.sources.filter(source => source.scope.kind === "binding" &&
|
|
149
174
|
source.scope.bindingKey === input.binding.bindingKey);
|
|
150
|
-
const
|
|
151
|
-
? [[source.scope.journalBindingKey, source]]
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
shared[0]?.kind !== "available" ||
|
|
156
|
-
bindingSources.length !==
|
|
157
|
-
(input.binding.journalBindingKeys ?? []).length ||
|
|
158
|
-
sourceByKey.size !== (input.binding.journalBindingKeys ?? []).length ||
|
|
159
|
-
Array.from(sourceByKey.values()).some((source) => source.kind !== "available")) {
|
|
175
|
+
const sourceByAddress = new Map(bindingSources.flatMap(source => source.scope.kind === "binding"
|
|
176
|
+
? [[address(source.scope.journalBindingKey, source.scope.sessionId), source]] : []));
|
|
177
|
+
if (!capture.complete || shared.length !== 1 || shared[0]?.kind !== "available" ||
|
|
178
|
+
bindingSources.length !== expected.length || sourceByAddress.size !== expected.length ||
|
|
179
|
+
expected.some(key => sourceByAddress.get(key)?.kind !== "available")) {
|
|
160
180
|
return { kind: "blocked", reason: "incomplete-evidence" };
|
|
161
181
|
}
|
|
162
|
-
const
|
|
163
|
-
const source =
|
|
182
|
+
const empty = new Set(expected.filter(key => {
|
|
183
|
+
const source = sourceByAddress.get(key);
|
|
164
184
|
return source?.kind === "available" && source.entries.length === 0;
|
|
165
|
-
});
|
|
166
|
-
|
|
185
|
+
}));
|
|
186
|
+
const emptyWriterKeys = new Set([
|
|
187
|
+
...keys.filter(key => empty.has(address(key))),
|
|
188
|
+
...sessions.filter(source => empty.has(address(source.recipientBindingKey, source.sessionId)))
|
|
189
|
+
.map(source => source.recipientBindingKey),
|
|
190
|
+
]);
|
|
191
|
+
const quiescent = new Set([...emptyWriterKeys].filter(key => input.getJournalWriterProtection(key) === "clear"));
|
|
192
|
+
const removedKeys = keys.filter(key => empty.has(address(key)) && quiescent.has(key));
|
|
193
|
+
const removedSources = sessions.filter(source => empty.has(address(source.recipientBindingKey, source.sessionId)) &&
|
|
194
|
+
quiescent.has(source.recipientBindingKey));
|
|
195
|
+
if (empty.size > 0 && removedKeys.length === 0 && removedSources.length === 0) {
|
|
167
196
|
return { kind: "blocked", reason: "writer-not-quiescent" };
|
|
168
197
|
}
|
|
169
|
-
const retainedKeys =
|
|
198
|
+
const retainedKeys = keys.filter(key => !removedKeys.includes(key));
|
|
199
|
+
const retainedSources = sessions.filter(source => !removedSources.includes(source));
|
|
170
200
|
if (!isCurrent()) {
|
|
171
201
|
throw new Error("Telegram Workspace journal pruning lost leader authority.");
|
|
172
202
|
}
|
|
173
|
-
const binding = input.store.commitWorkspaceJournalEvidence(input.binding, retainedKeys, input.binding.journalBindingsComplete === true);
|
|
203
|
+
const binding = input.store.commitWorkspaceJournalEvidence(input.binding, retainedKeys, input.binding.journalBindingsComplete === true, retainedSources);
|
|
174
204
|
if (!binding)
|
|
175
205
|
return { kind: "blocked", reason: "state-changed" };
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
206
|
+
if (removedKeys.length > 0 || removedSources.length > 0) {
|
|
207
|
+
const published = await input.store.persistWorkspaceJournalEvidence(binding, isCurrent);
|
|
208
|
+
if (!isCurrent())
|
|
209
|
+
throw new Error("Telegram Workspace journal pruning lost leader authority.");
|
|
210
|
+
if (!published)
|
|
211
|
+
return { kind: "blocked", reason: "publication-refused" };
|
|
212
|
+
}
|
|
179
213
|
if (!isCurrent()) {
|
|
180
214
|
throw new Error("Telegram Workspace journal pruning lost leader authority.");
|
|
181
215
|
}
|
|
182
|
-
return { kind: "committed", binding, removedKeys
|
|
216
|
+
return { kind: "committed", binding, removedKeys,
|
|
217
|
+
...(removedSources.length ? { removedSources: removedSources.map(source => ({ ...source })) } : {}) };
|
|
183
218
|
};
|
|
184
219
|
return runWithTelegramWorkspaceAdmissionsAsync({
|
|
185
220
|
ledger: input.admission,
|
|
186
221
|
operationId: createTelegramWorkspaceAdmissionOperationId(),
|
|
187
222
|
operationKind: "workspace.prune-journal-evidence",
|
|
188
223
|
scopes: [{ kind: "target", target: input.binding.target }],
|
|
189
|
-
operation,
|
|
224
|
+
operation: () => input.runExclusive(operation),
|
|
190
225
|
onReleaseError(error) {
|
|
191
226
|
input.onAdmissionReleaseError?.(error);
|
|
192
227
|
},
|
|
@@ -210,6 +245,7 @@ export function captureTelegramWorkspaceExternalProtection(input) {
|
|
|
210
245
|
localAcceptedTargets: local.targets,
|
|
211
246
|
journalSources: journals.sources,
|
|
212
247
|
sourcesComplete: journals.complete && local.complete,
|
|
248
|
+
requireBindingProvenance: input.requireBindingProvenance,
|
|
213
249
|
});
|
|
214
250
|
}
|
|
215
251
|
catch {
|
|
@@ -223,17 +259,45 @@ export function captureTelegramWorkspaceExternalProtection(input) {
|
|
|
223
259
|
}
|
|
224
260
|
return { liveOwner, acceptedWork, deliveryAuthority };
|
|
225
261
|
}
|
|
226
|
-
|
|
227
|
-
|
|
262
|
+
/** Shared read-only observer: protection and metadata pruning use the same scoped evidence path. */
|
|
263
|
+
export function createTelegramWorkspaceProtectionObserver(deps) {
|
|
264
|
+
const captureJournalSources = (candidate) => {
|
|
265
|
+
const namespace = deps.inspectJournalNamespace?.();
|
|
266
|
+
if (deps.inspectJournalNamespace && (!namespace || namespace.sources.filter(source => source.role === "polling").length !== 1))
|
|
267
|
+
throw new Error("Telegram strict namespace evidence is unavailable or incomplete.");
|
|
268
|
+
if (namespace?.retainedInputs?.some(original => original.state !== "committed"))
|
|
269
|
+
throw new Error("Telegram namespace contains uncommitted private retention evidence.");
|
|
270
|
+
const discovery = namespace
|
|
271
|
+
? { paths: namespace.sources.filter(source => source.role !== "polling" && source.evidence.kind === "present")
|
|
272
|
+
.map(source => source.path), complete: true }
|
|
273
|
+
: candidate.journalBindingsComplete === true ? undefined : deps.discoverFollowerJournals?.();
|
|
274
|
+
if (namespace && !deps.createJournalPathResolver)
|
|
275
|
+
throw new Error("Telegram strict namespace protection requires discovered source resolution.");
|
|
276
|
+
return captureTelegramWorkspaceJournalProtectionSources({
|
|
277
|
+
binding: candidate,
|
|
278
|
+
resolveLeader: deps.resolveLeaderJournal,
|
|
279
|
+
createFollowerResolver: deps.createFollowerJournalResolver,
|
|
280
|
+
...(deps.createSessionJournalResolver ? { createSessionResolver: deps.createSessionJournalResolver } : {}),
|
|
281
|
+
...(deps.withJournalReference ? { withJournalReference: deps.withJournalReference } : {}),
|
|
282
|
+
...(discovery && deps.createJournalPathResolver
|
|
283
|
+
? { discovery: { ...discovery, createResolver: deps.createJournalPathResolver } } : {}),
|
|
284
|
+
});
|
|
285
|
+
};
|
|
286
|
+
const capture = function (binding, options) {
|
|
228
287
|
return captureTelegramWorkspaceExternalProtection({
|
|
229
288
|
binding,
|
|
289
|
+
requireBindingProvenance: options?.requireBindingProvenance,
|
|
230
290
|
getLiveOwnerProtection(candidate) {
|
|
231
291
|
if (deps.listFollowers().some((follower) => !!follower.target && sameTarget(follower.target, candidate.target)))
|
|
232
292
|
return "protected";
|
|
233
293
|
if (!deps.getJournalWriterProtection)
|
|
234
294
|
return "unknown";
|
|
235
295
|
let unknown = candidate.journalBindingsComplete !== true;
|
|
236
|
-
|
|
296
|
+
const writerKeys = new Set([
|
|
297
|
+
...(candidate.journalBindingKeys ?? []),
|
|
298
|
+
...(candidate.journalSources ?? []).map(source => source.recipientBindingKey),
|
|
299
|
+
]);
|
|
300
|
+
for (const journalBindingKey of writerKeys) {
|
|
237
301
|
const protection = deps.getJournalWriterProtection(journalBindingKey);
|
|
238
302
|
if (protection === "protected")
|
|
239
303
|
return "protected";
|
|
@@ -257,29 +321,34 @@ export function createTelegramWorkspaceExternalProtectionCapture(deps) {
|
|
|
257
321
|
}
|
|
258
322
|
return { targets, complete };
|
|
259
323
|
},
|
|
260
|
-
captureJournalSources
|
|
261
|
-
const discovery = candidate.journalBindingsComplete === true
|
|
262
|
-
? undefined
|
|
263
|
-
: deps.discoverFollowerJournals?.();
|
|
264
|
-
return captureTelegramWorkspaceJournalProtectionSources({
|
|
265
|
-
binding: candidate,
|
|
266
|
-
resolveLeader: deps.resolveLeaderJournal,
|
|
267
|
-
createFollowerResolver: deps.createFollowerJournalResolver,
|
|
268
|
-
...(deps.withJournalReference
|
|
269
|
-
? { withJournalReference: deps.withJournalReference } : {}),
|
|
270
|
-
...(discovery && deps.createJournalPathResolver
|
|
271
|
-
? { discovery: {
|
|
272
|
-
...discovery,
|
|
273
|
-
createResolver: deps.createJournalPathResolver,
|
|
274
|
-
} }
|
|
275
|
-
: {}),
|
|
276
|
-
});
|
|
277
|
-
},
|
|
324
|
+
captureJournalSources,
|
|
278
325
|
...(deps.getDeliveryAuthorityProtection
|
|
279
326
|
? { getDeliveryAuthorityProtection: deps.getDeliveryAuthorityProtection }
|
|
280
327
|
: {}),
|
|
281
328
|
});
|
|
282
329
|
};
|
|
330
|
+
return { capture, captureJournalSources,
|
|
331
|
+
getJournalWriterProtection: deps.getJournalWriterProtection ?? (() => "unknown") };
|
|
332
|
+
}
|
|
333
|
+
/** Callable protection view retained for callers that need no metadata observation ports. */
|
|
334
|
+
export function createTelegramWorkspaceExternalProtectionCapture(deps) {
|
|
335
|
+
return createTelegramWorkspaceProtectionObserver(deps).capture;
|
|
336
|
+
}
|
|
337
|
+
export function createTelegramWorkspaceJournalEvidencePruner(deps) {
|
|
338
|
+
return async (binding, isCurrent) => {
|
|
339
|
+
const admission = deps.getAdmission();
|
|
340
|
+
if (!admission)
|
|
341
|
+
return { kind: "blocked", reason: "state-changed" };
|
|
342
|
+
const profileKey = admission.getProfileKey();
|
|
343
|
+
return pruneTelegramWorkspaceJournalEvidence({
|
|
344
|
+
store: deps.store, binding, admission, runExclusive: deps.runExclusive,
|
|
345
|
+
capture: () => deps.protection.captureJournalSources(binding),
|
|
346
|
+
getJournalWriterProtection: deps.protection.getJournalWriterProtection,
|
|
347
|
+
getLeaderEpoch: deps.getLeaderEpoch,
|
|
348
|
+
getProfileKey: () => deps.getAdmission()?.getProfileKey() ?? "",
|
|
349
|
+
isCurrent: () => isCurrent() && deps.getAdmission()?.getProfileKey() === profileKey,
|
|
350
|
+
});
|
|
351
|
+
};
|
|
283
352
|
}
|
|
284
353
|
export function isCurrentTelegramWorkspaceBinding(store, expected) {
|
|
285
354
|
return store.listWorkspaceBindings().some((binding) => isDeepStrictEqual(binding, expected));
|
|
@@ -307,6 +376,9 @@ export function createTelegramWorkspaceDeadOwnerQueueReclaimer(deps) {
|
|
|
307
376
|
if (!isCurrent() || !deps.isBindingCurrent(binding)) {
|
|
308
377
|
return { kind: "blocked", reason: "authority-changed" };
|
|
309
378
|
}
|
|
379
|
+
// Session addresses require exact resolution; never substitute a flat same-hash journal.
|
|
380
|
+
if (binding.journalSources?.length && !deps.createSessionJournalResolver)
|
|
381
|
+
return { kind: "blocked", reason: "incomplete-source" };
|
|
310
382
|
const initial = deps.getExternalProtection(binding);
|
|
311
383
|
if (initial.liveOwner !== "clear" || initial.deliveryAuthority !== "clear") {
|
|
312
384
|
return { kind: "blocked", reason: "live-owner" };
|
|
@@ -345,6 +417,9 @@ export function createTelegramWorkspaceDeadOwnerQueueReclaimer(deps) {
|
|
|
345
417
|
for (const key of binding.journalBindingKeys ?? []) {
|
|
346
418
|
capture("binding", deps.createFollowerJournalResolver(key));
|
|
347
419
|
}
|
|
420
|
+
for (const source of binding.journalSources ?? []) {
|
|
421
|
+
capture("binding", deps.createSessionJournalResolver(source.recipientBindingKey, source.sessionId));
|
|
422
|
+
}
|
|
348
423
|
for (const path of discovery?.paths ?? []) {
|
|
349
424
|
if (!deps.createJournalPathResolver) {
|
|
350
425
|
complete = false;
|
|
@@ -957,21 +1032,42 @@ export function createTelegramWorkspaceSlotRotation(input) {
|
|
|
957
1032
|
catch (error) {
|
|
958
1033
|
if (!(error instanceof TelegramWorkspaceSlotUnavailableError))
|
|
959
1034
|
throw error;
|
|
960
|
-
if (input.reclaimDeadOwnerQueuedWork) {
|
|
1035
|
+
if (input.pruneJournalEvidence || input.reclaimDeadOwnerQueuedWork) {
|
|
961
1036
|
const candidates = await input.runExclusive(async () => {
|
|
962
1037
|
await input.store.load();
|
|
963
1038
|
if (!isCurrent())
|
|
964
1039
|
throw new Error("Telegram Workspace reclamation lost leader authority.");
|
|
965
1040
|
const snapshot = input.store.captureWorkspaceSlotOccupancy(input.getExternalProtection);
|
|
966
1041
|
const allocation = planTelegramWorkspaceSlotAllocation({ ...snapshot, nowMs: Date.now() });
|
|
967
|
-
if (allocation.kind
|
|
1042
|
+
if (allocation.kind === "free" || (allocation.kind === "blocked" && allocation.reason !== "protected-capacity"))
|
|
1043
|
+
return [];
|
|
1044
|
+
if (allocation.kind === "reclaim" && !input.pruneJournalEvidence)
|
|
968
1045
|
return [];
|
|
969
|
-
return input.store.listWorkspaceBindings().filter((binding) =>
|
|
1046
|
+
return input.store.listWorkspaceBindings().filter((binding) => (allocation.kind !== "reclaim" || binding.bindingKey === allocation.candidate.bindingKey) &&
|
|
1047
|
+
typeof binding.inactiveSinceMs === "number" &&
|
|
970
1048
|
Number.isFinite(binding.inactiveSinceMs) &&
|
|
971
1049
|
binding.inactiveSinceMs >= 0).sort((left, right) => left.inactiveSinceMs - right.inactiveSinceMs ||
|
|
972
1050
|
(left.slot ?? "").localeCompare(right.slot ?? ""));
|
|
973
1051
|
});
|
|
974
|
-
for (const
|
|
1052
|
+
for (const original of candidates) {
|
|
1053
|
+
let binding = original;
|
|
1054
|
+
if (input.pruneJournalEvidence && ((binding.journalBindingKeys?.length ?? 0) + (binding.journalSources?.length ?? 0) > 0)) {
|
|
1055
|
+
const pruned = await input.pruneJournalEvidence(binding, isCurrent);
|
|
1056
|
+
if (!isCurrent())
|
|
1057
|
+
throw new Error("Telegram Workspace pruning lost leader authority.");
|
|
1058
|
+
if (pruned.kind !== "committed")
|
|
1059
|
+
continue;
|
|
1060
|
+
binding = pruned.binding;
|
|
1061
|
+
const removed = pruned.removedKeys.length + (pruned.removedSources?.length ?? 0);
|
|
1062
|
+
if (removed > 0) {
|
|
1063
|
+
try {
|
|
1064
|
+
input.recordEvent("Telegram Workspace journal evidence pruned.", { slot: binding.slot, removed });
|
|
1065
|
+
}
|
|
1066
|
+
catch { /* Diagnostics cannot revoke a published metadata subset. */ }
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
if (!input.reclaimDeadOwnerQueuedWork)
|
|
1070
|
+
continue;
|
|
975
1071
|
const evidence = input.getExternalProtection(binding);
|
|
976
1072
|
if (evidence.liveOwner !== "clear" ||
|
|
977
1073
|
evidence.acceptedWork !== "protected" ||
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llblab/pi-telegram",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.52.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"node": ">=22.19.0"
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
30
|
-
"test": "node --experimental-strip-types --test --test-concurrency=4 --test-reporter=dot tests/*.test.ts",
|
|
31
|
-
"test:verbose": "node --experimental-strip-types --test --test-reporter=spec tests/*.test.ts",
|
|
30
|
+
"test": "node --experimental-strip-types --import ./tests/fixtures/canonical-tmpdir.ts --test --test-concurrency=4 --test-reporter=dot tests/*.test.ts",
|
|
31
|
+
"test:verbose": "node --experimental-strip-types --import ./tests/fixtures/canonical-tmpdir.ts --test --test-reporter=spec tests/*.test.ts",
|
|
32
32
|
"typecheck": "tsc --noEmit",
|
|
33
33
|
"build": "node scripts/build-dist.mjs",
|
|
34
34
|
"build:check": "node scripts/build-dist.mjs --check",
|
|
@@ -109,9 +109,9 @@
|
|
|
109
109
|
"image": "https://raw.githubusercontent.com/llblab/pi-telegram/main/screenshot.png"
|
|
110
110
|
},
|
|
111
111
|
"peerDependencies": {
|
|
112
|
-
"@earendil-works/pi-agent-core": ">=0.
|
|
113
|
-
"@earendil-works/pi-ai": ">=0.
|
|
114
|
-
"@earendil-works/pi-coding-agent": ">=0.
|
|
112
|
+
"@earendil-works/pi-agent-core": ">=1.0.0",
|
|
113
|
+
"@earendil-works/pi-ai": ">=1.0.0",
|
|
114
|
+
"@earendil-works/pi-coding-agent": ">=1.0.0",
|
|
115
115
|
"@sinclair/typebox": "*"
|
|
116
116
|
},
|
|
117
117
|
"devDependencies": {
|
|
@@ -46,7 +46,7 @@ A disabled control is not an action: it needs no prompt or selected style and mu
|
|
|
46
46
|
|
|
47
47
|
Prompts must name any target, operation, constraint, or freshness identity whose omission could change the action. Reuse visible context only when it remains unambiguous under delayed or reordered clicks. Never encode volatile output that should be freshly inspected.
|
|
48
48
|
|
|
49
|
-
Every generated human-readable action label must use `emoji + space + text`; emoji-free text labels are invalid. Choose the emoji by action semantics, keep its meaning consistent, and never rely on emoji or color alone. Coordinates, established symbolic controls, and intentionally spatial glyphs already satisfy the marker role through their domain grammar. If prompt buttons are unavailable, preserve the same choices as a numbered list.
|
|
49
|
+
Every generated human-readable action label must use `emoji + space + text`; emoji-free text labels are invalid. Choose the emoji by action semantics, keep its meaning consistent, and never rely on emoji or color alone. State/value controls follow their UI-kit grammar; unmarked inactive list items are not action labels. Coordinates, established symbolic controls, and intentionally spatial glyphs already satisfy the marker role through their domain grammar. If prompt buttons are unavailable, preserve the same choices as a numbered list.
|
|
50
50
|
|
|
51
51
|
## Layout Kernel
|
|
52
52
|
|
|
@@ -97,10 +97,10 @@ Show concise failure evidence and emit valid recovery controls such as diagnose,
|
|
|
97
97
|
Before sending:
|
|
98
98
|
|
|
99
99
|
- At least one useful button is present whenever the transport supports prompt buttons.
|
|
100
|
-
- Every human-readable
|
|
100
|
+
- Every human-readable action label begins with a semantic emoji and one ASCII space; state/value controls follow their owning documented grammar.
|
|
101
101
|
- State, actions, owner, and target agree.
|
|
102
102
|
- Live claims are fresh; adaptation and incompleteness are explicit.
|
|
103
|
-
- Every prompt is sufficient
|
|
103
|
+
- Every prompt is sufficient; labels follow their control grammar.
|
|
104
104
|
- Row grouping reflects real hierarchy or peer relationships.
|
|
105
105
|
- High-impact actions require confirmation.
|
|
106
106
|
- No secret or hidden reasoning appears.
|
package/node_modules/@llblab/pi-telegram/dist/skills/telegram-bridge/references/diagnosis.md
CHANGED
|
@@ -6,12 +6,12 @@ Inspect in this order:
|
|
|
6
6
|
|
|
7
7
|
1. `/telegram-status` for compact health in the Pi TUI.
|
|
8
8
|
2. `/telegram-status --debug` for bounded human-readable diagnostics in the Pi TUI.
|
|
9
|
-
3. `~/.pi/agent/tmp/telegram/state.json` and `logs.jsonl`
|
|
10
|
-
4. `
|
|
9
|
+
3. `~/.pi/agent/tmp/pi-telegram/state.json` (per-profile `transport`, `workspace` canonical bindings and `runtime` roster/diagnostics sections) and `logs.jsonl` (profile-labelled redacted evidence); rotated history is `logs/logs._prev.jsonl`.
|
|
10
|
+
4. For a named profile, read its `profiles.<name>` entry and filter log records by `profile`; service journals are `journals/<kind>.<profile sha256>.json`.
|
|
11
11
|
|
|
12
12
|
These slash commands are registered Pi commands, not shell executables or agent tools. If the agent cannot invoke them through a supported Pi surface, read the diagnostic files directly; do not run them in Bash or inject terminal input.
|
|
13
13
|
|
|
14
|
-
When `PI_CODING_AGENT_DIR` selects another compatible runtime, resolve its equivalent `tmp/telegram` directory.
|
|
14
|
+
When `PI_CODING_AGENT_DIR` selects another compatible runtime, resolve its equivalent `tmp/pi-telegram` directory.
|
|
15
15
|
|
|
16
16
|
A `persistent-conflict` polling stop means the bounded competing-`getUpdates` threshold triggered full transport stand-down, not cancellation of accepted local Pi work. The terminal diagnostic distinguishes lost local ownership from a competing client despite an apparently owned lock. Check other profiles, agent directories, installations, or non-Pi clients sharing the bot; after removing the competition, reconnect through the supported Pi command. Do not restart repeatedly or alter lock files to compete for the stream.
|
|
17
17
|
|
|
@@ -22,7 +22,7 @@ Living index of project documentation in `/docs`.
|
|
|
22
22
|
- [generative-apps.md](./generative-apps.md) — Generative Apps runtime and wire contract for managed `.mjs` identity, inference-bypass bindings, persistent state timelines, bounded adapters, replacement, and lifecycle; agent operation lives in the bundled `generative-apps` Skill
|
|
23
23
|
- [callback-namespaces.md](./callback-namespaces.md) — Shared Telegram `callback_data` namespace standard for layered extensions
|
|
24
24
|
- [updates.md](./updates.md) — Update classification and runtime handler registry that lets layered extensions observe and consume Telegram updates without owning their own polling connection
|
|
25
|
-
- [multi-instance-bus.md](./multi-instance-bus.md) — Optional multi-instance Telegram bus architecture: profile-scoped transport, leader/follower routing, thread targets, instance slots, manual follower registration, and recovery semantics
|
|
25
|
+
- [multi-instance-bus.md](./multi-instance-bus.md) — Optional multi-instance Telegram bus architecture: profile-scoped transport, leader/follower routing, thread targets, instance slots, session-owned journal storage and sweeping, manual follower registration, and recovery semantics
|
|
26
26
|
- [sections.md](./sections.md) — Telegram Extension Sections Standard: registration contract, context ports, callback routing, navigation hierarchy, and demo reference for pi extensions that want Telegram UI surfaces
|
|
27
27
|
- [voice.md](./voice.md) — Voice integration guide: detection, reply policy, STT/TTS provider registration, provider-owned conversion, and transparent interception
|
|
28
28
|
- [ui-style.md](./ui-style.md) — Inline UI style guide for buttons, toggles, tabs, option lists, cards, and dialogs
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
} from "@llblab/pi-telegram/activity";
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
An
|
|
17
|
+
An external consumer can own optional Settings policy for reasoning, intermediate assistant prose, and tool rows. Those visibility choices do not become mandatory bridge-core settings.
|
|
18
18
|
|
|
19
19
|
## Ownership Boundary
|
|
20
20
|
|