@llblab/pi-kit 0.27.6 → 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 +2 -5
- package/CHANGELOG.md +5 -0
- package/README.md +2 -2
- 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 +2 -2
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* Zones: filesystem diagnostics, unclean-shutdown recovery
|
|
4
4
|
* Owns fail-safe classification of temporary ownership and routing artifacts
|
|
5
5
|
*/
|
|
6
|
-
import { renameSync } from "node:fs";
|
|
7
6
|
import { type TelegramFileTransactionOptions } from "./locks.ts";
|
|
8
7
|
export type TelegramRuntimeArtifactKind = "owners" | "state" | "transaction";
|
|
9
8
|
export interface TelegramRuntimeCorruptArtifact {
|
|
@@ -35,19 +34,19 @@ export type TelegramRuntimeRecoveryResult = {
|
|
|
35
34
|
} | {
|
|
36
35
|
kind: "blocked-live-owner";
|
|
37
36
|
livePids: number[];
|
|
38
|
-
|
|
37
|
+
deletedPaths?: string[];
|
|
39
38
|
} | {
|
|
40
39
|
kind: "recovered";
|
|
41
40
|
artifacts: TelegramRuntimeCorruptArtifact[];
|
|
42
|
-
|
|
41
|
+
/** Damaged disposable artifacts deleted per the approved corruption policy; nothing is quarantined. */
|
|
42
|
+
deletedPaths: string[];
|
|
43
43
|
};
|
|
44
44
|
export interface TelegramRuntimeRecoveryOptions extends TelegramRuntimeRecoveryClassificationOptions {
|
|
45
45
|
recoveryTransactionPath?: string;
|
|
46
|
-
quarantineRoot?: string;
|
|
47
46
|
pid?: number;
|
|
48
47
|
getNowMs?: () => number;
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
/** Fault-injection seam; production uses bounded-retry recursive removal. */
|
|
49
|
+
removePath?: (path: string) => void;
|
|
51
50
|
transactionOptions?: TelegramFileTransactionOptions;
|
|
52
51
|
}
|
|
53
52
|
export type TelegramPollingStartRecoveryDecision = {
|
|
@@ -74,13 +73,34 @@ export interface TelegramPollingStartRecoveryHandlerDeps {
|
|
|
74
73
|
*/
|
|
75
74
|
export declare function classifyTelegramRuntimeRecovery(options: TelegramRuntimeRecoveryClassificationOptions): TelegramRuntimeRecoveryClassification;
|
|
76
75
|
/**
|
|
77
|
-
*
|
|
76
|
+
* Delete classifier-approved disposable corruption under two guards.
|
|
78
77
|
*
|
|
79
78
|
* A dedicated recovery transaction serializes recoverers. The ownership
|
|
80
79
|
* transaction then prevents a new Telegram owner from appearing between the
|
|
81
|
-
* final classification and mutation.
|
|
82
|
-
*
|
|
80
|
+
* final classification and mutation. Damaged ownership debris and canonical
|
|
81
|
+
* state are deleted (operator policy: unfinished Restores in unreadable state
|
|
82
|
+
* are acceptable loss). Durable config and diagnostics never enter the set.
|
|
83
83
|
*/
|
|
84
84
|
export declare function recoverTelegramRuntimeState(options: TelegramRuntimeRecoveryOptions): TelegramRuntimeRecoveryResult;
|
|
85
|
+
/**
|
|
86
|
+
* Remove recovery folders written by earlier releases (runtime root and session folders).
|
|
87
|
+
* Current releases delete damaged files instead of quarantining them; nothing reads these copies.
|
|
88
|
+
*/
|
|
89
|
+
export declare function removeTelegramLegacyRecoveryStorage(runtimeDir: string): string[];
|
|
90
|
+
export interface TelegramSessionFolderSweeperDeps {
|
|
91
|
+
getSessionsDir: () => string;
|
|
92
|
+
getProfileName: () => string | undefined;
|
|
93
|
+
/** Sessions holding a Workspace slot binding, live registrations and this process's own session. */
|
|
94
|
+
getKeptSessionIds: () => Iterable<string | undefined>;
|
|
95
|
+
getNowMs?: () => number;
|
|
96
|
+
intervalMs?: number;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Leader housekeeping (operator policy): a session without a Workspace slot loses its
|
|
100
|
+
* current-profile journal family; the folder disappears once no profile uses it.
|
|
101
|
+
*/
|
|
102
|
+
export declare function createTelegramSessionFolderSweeper(deps: TelegramSessionFolderSweeperDeps): {
|
|
103
|
+
sweep: () => string[];
|
|
104
|
+
};
|
|
85
105
|
/** Build the `/telegram-connect` recovery boundary around runtime artifacts. */
|
|
86
106
|
export declare function createTelegramPollingStartRecoveryHandler(deps: TelegramPollingStartRecoveryHandlerDeps): () => Promise<TelegramPollingStartRecoveryDecision>;
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
* Zones: filesystem diagnostics, unclean-shutdown recovery
|
|
4
4
|
* Owns fail-safe classification of temporary ownership and routing artifacts
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
7
|
-
import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, } from "node:fs";
|
|
6
|
+
import { existsSync, lstatSync, readFileSync, readdirSync, rmdirSync, rmSync, } from "node:fs";
|
|
8
7
|
import { basename, dirname, join } from "node:path";
|
|
9
|
-
import {
|
|
8
|
+
import { decodeTelegramSessionDirectoryName, getTelegramProfilePathSuffix } from "./paths.js";
|
|
9
|
+
import { isProcessAlive as defaultIsProcessAlive, parseTelegramLockEntry, TELEGRAM_BUS_LEADER_STALE_HEARTBEAT_MS, withTelegramFileTransaction, } from "./locks.js";
|
|
10
10
|
const OWNER_FILE_PATTERN = /^owner\.([A-Za-z0-9-]+)\.json$/u;
|
|
11
11
|
const RECLAIM_FILE_PATTERN = /^owner\.reclaim\.(\d+)\.([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.json$/u;
|
|
12
12
|
function corruption(kind, path, reason) {
|
|
@@ -40,6 +40,9 @@ function inspectState(path) {
|
|
|
40
40
|
if (!existsSync(path))
|
|
41
41
|
return { source: "state", ownerPids: [] };
|
|
42
42
|
try {
|
|
43
|
+
const stat = lstatSync(path);
|
|
44
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1 || stat.size > 8 * 1024 * 1024)
|
|
45
|
+
return corruption("state", path, `${basename(path)} cannot be safely inspected`);
|
|
43
46
|
JSON.parse(readFileSync(path, "utf8"));
|
|
44
47
|
return { source: "state", ownerPids: [] };
|
|
45
48
|
}
|
|
@@ -136,19 +139,19 @@ export function classifyTelegramRuntimeRecovery(options) {
|
|
|
136
139
|
: { kind: "recoverable-corruption", artifacts };
|
|
137
140
|
}
|
|
138
141
|
/**
|
|
139
|
-
*
|
|
142
|
+
* Delete classifier-approved disposable corruption under two guards.
|
|
140
143
|
*
|
|
141
144
|
* A dedicated recovery transaction serializes recoverers. The ownership
|
|
142
145
|
* transaction then prevents a new Telegram owner from appearing between the
|
|
143
|
-
* final classification and mutation.
|
|
144
|
-
*
|
|
146
|
+
* final classification and mutation. Damaged ownership debris and canonical
|
|
147
|
+
* state are deleted (operator policy: unfinished Restores in unreadable state
|
|
148
|
+
* are acceptable loss). Durable config and diagnostics never enter the set.
|
|
145
149
|
*/
|
|
146
150
|
export function recoverTelegramRuntimeState(options) {
|
|
147
|
-
const pid = options.pid ?? process.pid;
|
|
148
151
|
const transactionPath = options.transactionPath ?? `${options.ownersPath}.transaction`;
|
|
149
152
|
const recoveryTransactionPath = options.recoveryTransactionPath ??
|
|
150
153
|
join(dirname(options.ownersPath), "runtime-recovery.transaction");
|
|
151
|
-
const
|
|
154
|
+
const removePath = options.removePath ?? ((path) => rmSync(path, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 }));
|
|
152
155
|
const classificationOptions = {
|
|
153
156
|
ownersPath: options.ownersPath,
|
|
154
157
|
statePaths: options.statePaths,
|
|
@@ -167,29 +170,18 @@ export function recoverTelegramRuntimeState(options) {
|
|
|
167
170
|
livePids: initial.livePids,
|
|
168
171
|
};
|
|
169
172
|
}
|
|
170
|
-
|
|
173
|
+
const deletedPaths = [];
|
|
171
174
|
const recoveredArtifacts = [];
|
|
172
|
-
const
|
|
173
|
-
if (quarantineDir)
|
|
174
|
-
return quarantineDir;
|
|
175
|
-
quarantineDir = join(quarantineRoot, `${options.getNowMs?.() ?? Date.now()}-${pid}-${randomUUID()}`);
|
|
176
|
-
mkdirSync(quarantineDir, { recursive: true, mode: 0o700 });
|
|
177
|
-
return quarantineDir;
|
|
178
|
-
};
|
|
179
|
-
const quarantineArtifact = (artifact) => {
|
|
175
|
+
const deleteArtifact = (artifact) => {
|
|
180
176
|
if (!existsSync(artifact.path))
|
|
181
177
|
return;
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
retryDelayMs: options.quarantineRenameRetryDelayMs,
|
|
186
|
-
})) {
|
|
187
|
-
recoveredArtifacts.push(artifact);
|
|
188
|
-
}
|
|
178
|
+
removePath(artifact.path);
|
|
179
|
+
deletedPaths.push(artifact.path);
|
|
180
|
+
recoveredArtifacts.push(artifact);
|
|
189
181
|
};
|
|
190
182
|
for (const artifact of initial.artifacts) {
|
|
191
183
|
if (artifact.kind === "transaction")
|
|
192
|
-
|
|
184
|
+
deleteArtifact(artifact);
|
|
193
185
|
}
|
|
194
186
|
return withTelegramFileTransaction(transactionPath, () => {
|
|
195
187
|
const current = classifyTelegramRuntimeRecovery({
|
|
@@ -200,25 +192,107 @@ export function recoverTelegramRuntimeState(options) {
|
|
|
200
192
|
return {
|
|
201
193
|
kind: "blocked-live-owner",
|
|
202
194
|
livePids: current.livePids,
|
|
203
|
-
|
|
195
|
+
...(deletedPaths.length > 0 ? { deletedPaths } : {}),
|
|
204
196
|
};
|
|
205
197
|
}
|
|
206
198
|
if (current.kind === "recoverable-corruption") {
|
|
207
199
|
for (const artifact of current.artifacts) {
|
|
208
200
|
if (artifact.kind !== "transaction")
|
|
209
|
-
|
|
201
|
+
deleteArtifact(artifact);
|
|
210
202
|
}
|
|
211
203
|
}
|
|
212
|
-
return recoveredArtifacts.length > 0
|
|
204
|
+
return recoveredArtifacts.length > 0
|
|
213
205
|
? {
|
|
214
206
|
kind: "recovered",
|
|
215
207
|
artifacts: recoveredArtifacts,
|
|
216
|
-
|
|
208
|
+
deletedPaths,
|
|
217
209
|
}
|
|
218
210
|
: { kind: "not-needed" };
|
|
219
211
|
}, options.transactionOptions);
|
|
220
212
|
}, options.transactionOptions);
|
|
221
213
|
}
|
|
214
|
+
/**
|
|
215
|
+
* Remove recovery folders written by earlier releases (runtime root and session folders).
|
|
216
|
+
* Current releases delete damaged files instead of quarantining them; nothing reads these copies.
|
|
217
|
+
*/
|
|
218
|
+
export function removeTelegramLegacyRecoveryStorage(runtimeDir) {
|
|
219
|
+
const removed = [];
|
|
220
|
+
const remove = (path) => {
|
|
221
|
+
try {
|
|
222
|
+
if (!lstatSync(path).isDirectory())
|
|
223
|
+
return;
|
|
224
|
+
rmSync(path, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
|
|
225
|
+
removed.push(path);
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
// Best-effort housekeeping; a later startup retries.
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
remove(join(runtimeDir, "recovery"));
|
|
232
|
+
let sessions = [];
|
|
233
|
+
try {
|
|
234
|
+
sessions = readdirSync(join(runtimeDir, "sessions"));
|
|
235
|
+
}
|
|
236
|
+
catch { /* no sessions yet */ }
|
|
237
|
+
for (const session of sessions)
|
|
238
|
+
remove(join(runtimeDir, "sessions", session, "recovery"));
|
|
239
|
+
return removed;
|
|
240
|
+
}
|
|
241
|
+
const TELEGRAM_SESSION_SWEEP_INTERVAL_MS = 10 * 60 * 1000;
|
|
242
|
+
/**
|
|
243
|
+
* Leader housekeeping (operator policy): a session without a Workspace slot loses its
|
|
244
|
+
* current-profile journal family; the folder disappears once no profile uses it.
|
|
245
|
+
*/
|
|
246
|
+
export function createTelegramSessionFolderSweeper(deps) {
|
|
247
|
+
const getNowMs = deps.getNowMs ?? Date.now;
|
|
248
|
+
const intervalMs = deps.intervalMs ?? TELEGRAM_SESSION_SWEEP_INTERVAL_MS;
|
|
249
|
+
let lastSweepAtMs;
|
|
250
|
+
return {
|
|
251
|
+
sweep() {
|
|
252
|
+
const now = getNowMs();
|
|
253
|
+
if (lastSweepAtMs !== undefined && now - lastSweepAtMs < intervalMs)
|
|
254
|
+
return [];
|
|
255
|
+
lastSweepAtMs = now;
|
|
256
|
+
const sessionsDir = deps.getSessionsDir();
|
|
257
|
+
const kept = new Set(deps.getKeptSessionIds());
|
|
258
|
+
const suffix = getTelegramProfilePathSuffix(deps.getProfileName()).replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
259
|
+
const family = new RegExp(`^journal\\.[a-f0-9]{16}${suffix}\\.json(?:\\.segments|\\.retained)?$`, "u");
|
|
260
|
+
const removed = [];
|
|
261
|
+
let names;
|
|
262
|
+
try {
|
|
263
|
+
names = readdirSync(sessionsDir);
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
return removed;
|
|
267
|
+
}
|
|
268
|
+
for (const name of names) {
|
|
269
|
+
const sessionId = decodeTelegramSessionDirectoryName(name);
|
|
270
|
+
if (sessionId === undefined || kept.has(sessionId))
|
|
271
|
+
continue;
|
|
272
|
+
const folder = join(sessionsDir, name);
|
|
273
|
+
try {
|
|
274
|
+
if (!lstatSync(folder).isDirectory())
|
|
275
|
+
continue;
|
|
276
|
+
for (const entry of readdirSync(folder)) {
|
|
277
|
+
if (!family.test(entry))
|
|
278
|
+
continue;
|
|
279
|
+
const path = join(folder, entry);
|
|
280
|
+
rmSync(path, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
|
|
281
|
+
removed.push(path);
|
|
282
|
+
}
|
|
283
|
+
if (readdirSync(folder).length === 0) {
|
|
284
|
+
rmdirSync(folder);
|
|
285
|
+
removed.push(folder);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
// Best-effort housekeeping; the next sweep retries.
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return removed;
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
}
|
|
222
296
|
/** Build the `/telegram-connect` recovery boundary around runtime artifacts. */
|
|
223
297
|
export function createTelegramPollingStartRecoveryHandler(deps) {
|
|
224
298
|
return async () => {
|
|
@@ -257,10 +331,7 @@ export function createTelegramPollingStartRecoveryHandler(deps) {
|
|
|
257
331
|
});
|
|
258
332
|
}
|
|
259
333
|
try {
|
|
260
|
-
const recovery = recoverTelegramRuntimeState({
|
|
261
|
-
ownersPath,
|
|
262
|
-
statePaths,
|
|
263
|
-
});
|
|
334
|
+
const recovery = recoverTelegramRuntimeState({ ownersPath, statePaths });
|
|
264
335
|
if (recovery.kind === "blocked-live-owner") {
|
|
265
336
|
return {
|
|
266
337
|
kind: "blocked",
|
|
@@ -117,10 +117,6 @@ export declare function sendTelegramNativeMarkdownReply<TReplyMarkup = unknown>(
|
|
|
117
117
|
}, options?: TelegramReplyTargetOptions & {
|
|
118
118
|
replyMarkup?: TReplyMarkup;
|
|
119
119
|
}): Promise<number | undefined>;
|
|
120
|
-
export declare function sendTelegramNativeRichMessage(chatId: number, richMessage: TelegramInputRichMessage, deps: {
|
|
121
|
-
recordOwnership?: TelegramReplyOwnershipRecorder["record"];
|
|
122
|
-
sendRichMessage: (body: TelegramSendRichMessageBody) => Promise<TelegramSentMessage>;
|
|
123
|
-
}, options?: TelegramReplyTargetOptions): Promise<number>;
|
|
124
120
|
export type TelegramAssistantRenderingMode = "rich" | "html";
|
|
125
121
|
export interface TelegramRenderedMessageRuntimeDeps<TReplyMarkup> {
|
|
126
122
|
renderTelegramMessage: (text: string, options?: {
|
|
@@ -498,7 +498,7 @@ export async function sendTelegramNativeMarkdownReply(chatId, replyToMessageId,
|
|
|
498
498
|
}
|
|
499
499
|
return lastMessageId;
|
|
500
500
|
}
|
|
501
|
-
|
|
501
|
+
async function sendTelegramNativeRichMessage(chatId, richMessage, deps, options) {
|
|
502
502
|
const sent = await deps.sendRichMessage({
|
|
503
503
|
chat_id: chatId,
|
|
504
504
|
rich_message: richMessage,
|
|
@@ -7,6 +7,7 @@ import * as Bus from "./bus.ts";
|
|
|
7
7
|
import * as Commands from "./commands.ts";
|
|
8
8
|
import type { TelegramConfigStore } from "./config.ts";
|
|
9
9
|
import type { TelegramInboundHandlerRuntime } from "./inbound.ts";
|
|
10
|
+
import type { TelegramUpdateJournalQueuedCompletion, TelegramUpdateJournalQueuedReceiptEvidence } from "./journal.ts";
|
|
10
11
|
import * as Media from "./media.ts";
|
|
11
12
|
import * as Menu from "./menu.ts";
|
|
12
13
|
import * as Model from "./model.ts";
|
|
@@ -15,11 +16,12 @@ import * as PromptTemplates from "./prompt-templates.ts";
|
|
|
15
16
|
import * as Queue from "./queue.ts";
|
|
16
17
|
import type { TelegramBridgeRuntime } from "./runtime.ts";
|
|
17
18
|
import type { TelegramSectionRegistry } from "./sections.ts";
|
|
18
|
-
import type { TelegramInputRichMessage } from "./telegram-api.ts";
|
|
19
|
+
import type { TelegramApiCallOptions, TelegramInputRichMessage } from "./telegram-api.ts";
|
|
19
20
|
import * as TextGroups from "./text-groups.ts";
|
|
20
21
|
import * as ThreadReconciler from "./thread-reconciler.ts";
|
|
21
22
|
import type { TelegramInstanceThreadIdentityCandidate, TelegramTopicTargetRecord } from "./threads.ts";
|
|
22
23
|
import * as Turns from "./turns.ts";
|
|
24
|
+
import type { createTelegramWorkspaceExternalProtectionCapture } from "./workspace-retirement.ts";
|
|
23
25
|
interface TelegramPromptPeerView {
|
|
24
26
|
id?: unknown;
|
|
25
27
|
is_bot?: unknown;
|
|
@@ -37,9 +39,37 @@ export declare function resolveTelegramGuestPromptPeer(input: {
|
|
|
37
39
|
guestBotCallerChat?: TelegramPromptPeerView;
|
|
38
40
|
ownerUserId?: number;
|
|
39
41
|
}): string | undefined;
|
|
42
|
+
/** Stable file scope of the remote Guest Mode peer: username, else numeric id; never the bot's own scope. */
|
|
43
|
+
export declare function resolveTelegramGuestFileScope(input: Parameters<typeof resolveTelegramGuestPromptPeer>[0]): string;
|
|
40
44
|
import * as Threads from "./threads.ts";
|
|
41
45
|
import * as Updates from "./updates.ts";
|
|
46
|
+
/** One admitted Restore attempt through recipient readiness, never source dispatch or cleanup. */
|
|
47
|
+
export declare function advanceTelegramWorkspaceRestore(input: {
|
|
48
|
+
request: Threads.TelegramWorkspaceRestoreRequest;
|
|
49
|
+
authority: Threads.TelegramWorkspaceRestoreAuthority;
|
|
50
|
+
restoreStore: Threads.TelegramWorkspaceRestore;
|
|
51
|
+
getRecipient: () => Threads.TelegramWorkspaceRestoreRecipient | undefined;
|
|
52
|
+
/** Adapter proves current canonical ownership and authenticates observations; apply needs a fresh issuance. */
|
|
53
|
+
runRecipient: (input: {
|
|
54
|
+
intent: Threads.TelegramWorkspaceRestoreIntent;
|
|
55
|
+
mode: "apply" | "inspect";
|
|
56
|
+
isCurrent: () => boolean;
|
|
57
|
+
}) => Promise<{
|
|
58
|
+
operationId: string;
|
|
59
|
+
recipient: Threads.TelegramWorkspaceRestoreRecipient;
|
|
60
|
+
target: Threads.TelegramWorkspaceRestoreRequest["target"];
|
|
61
|
+
slot: string;
|
|
62
|
+
ready: boolean;
|
|
63
|
+
} | undefined>;
|
|
64
|
+
/**
|
|
65
|
+
* Recovery for a same-session successor already on the relocated target: it may receive the first grant, but only
|
|
66
|
+
* read-only inspection runs. It never commits a new relocation or applies a target.
|
|
67
|
+
*/
|
|
68
|
+
inspectOnly?: true;
|
|
69
|
+
}): Promise<Threads.TelegramWorkspaceRestoreIntent | undefined>;
|
|
42
70
|
export declare const TELEGRAM_ALL_TAB_COMMAND_MAX_AGE_MS: number;
|
|
71
|
+
/** One user-facing answer for expired or previous-process routing controls. */
|
|
72
|
+
export declare const TELEGRAM_ROUTING_CHOICE_EXPIRED = "\u231B Routing choice expired.";
|
|
43
73
|
export declare function isTelegramAllTabCommandExpired(message: {
|
|
44
74
|
date?: number;
|
|
45
75
|
message_thread_id?: number;
|
|
@@ -66,7 +96,7 @@ export interface TelegramInboundRouteRuntimeDeps<TMessage extends TelegramRouted
|
|
|
66
96
|
configStore: Pick<TelegramConfigStore, "get" | "getAllowedUserId" | "persistAllowedUserId" | "persist"> & {
|
|
67
97
|
set?: TelegramConfigStore["set"];
|
|
68
98
|
};
|
|
69
|
-
callApi?: <TResponse>(method: string, body: Record<string, unknown
|
|
99
|
+
callApi?: <TResponse>(method: string, body: Record<string, unknown>, options?: TelegramApiCallOptions) => Promise<TResponse>;
|
|
70
100
|
getCurrentInstanceId?: () => string | undefined;
|
|
71
101
|
getAdmissionScope?: () => string | undefined;
|
|
72
102
|
getAdmissionJournalBinding?: () => string | undefined;
|
|
@@ -87,11 +117,32 @@ export interface TelegramInboundRouteRuntimeDeps<TMessage extends TelegramRouted
|
|
|
87
117
|
handleTelegramTopicLifecycleUpdate?: (lifecycle: Updates.TelegramTopicLifecycleUpdate<TMessage>, ctx: TContext) => Promise<void> | void;
|
|
88
118
|
handleTelegramThreadTargetObserved?: (target: Threads.TelegramTopicTargetRecord["target"], ctx: TContext) => Promise<void> | void;
|
|
89
119
|
foreignOwnedUpdateForwarder?: Updates.TelegramForeignOwnedUpdateForwarder<TContext, Updates.TelegramMessageReactionUpdated, TCallbackQuery, TMessage>;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
120
|
+
getWorkspaceRestoreStore?: () => Threads.TelegramWorkspaceRestore | undefined;
|
|
121
|
+
captureWorkspaceExternalProtection?: ReturnType<typeof createTelegramWorkspaceExternalProtectionCapture>;
|
|
122
|
+
/** Strict committed abandonment plus retained original; shared by Restore and temporary-input cancellation. */
|
|
123
|
+
inspectRestoreSourceAbandonment?: (updateId: number, journalBindingKey: string) => Threads.TelegramTemporaryThreadCancellationEvidence | undefined;
|
|
124
|
+
/** Strict active-journal observation only; a hint or missing source never substitutes for this ACK. */
|
|
125
|
+
inspectRestoreSourceCompletion?: (expected: Updates.TelegramDeferredSourceEvidence & {
|
|
126
|
+
completionSha256: string;
|
|
127
|
+
}) => (Updates.TelegramDeferredSourceEvidence & {
|
|
128
|
+
completionSha256: string;
|
|
129
|
+
}) | undefined;
|
|
130
|
+
inspectRestoreQueuedReceipt?: (expected: TelegramUpdateJournalQueuedCompletion & {
|
|
131
|
+
journalBindingKey: string;
|
|
132
|
+
}) => TelegramUpdateJournalQueuedReceiptEvidence | undefined;
|
|
133
|
+
hasWorkspaceRestoreAuthority?: () => boolean;
|
|
134
|
+
/** Strict complete namespace plus exact current/historical references; only an empty result clears journal protection. */
|
|
135
|
+
inspectTemporaryThreadSources?: (target: Queue.TelegramQueueTarget, requiredJournalBindingKeys: readonly string[]) => readonly number[] | undefined;
|
|
136
|
+
/** Quiet period after the last cancelled input before one cleanup attempt; defaults to 1000 ms. */
|
|
137
|
+
temporaryThreadCleanupDelayMs?: number;
|
|
138
|
+
getSessionGeneration?: () => number;
|
|
139
|
+
workspaceRestoreRecipient?: {
|
|
140
|
+
getSessionId: (ctx: TContext) => string | undefined;
|
|
141
|
+
getCwd: (ctx: TContext) => string | undefined;
|
|
142
|
+
getLeaderIdentity: Threads.TelegramLeaderThreadStateRuntime["getIdentity"];
|
|
143
|
+
followerRegistry: Pick<Bus.TelegramBusFollowerRegistry, "get" | "register">;
|
|
144
|
+
runFollower: ReturnType<typeof Bus.createTelegramBusWorkspaceRestoreController>;
|
|
145
|
+
};
|
|
95
146
|
bridgeRuntime: TelegramBridgeRuntime;
|
|
96
147
|
activeTurnRuntime: Queue.TelegramActiveTurnStore;
|
|
97
148
|
mediaGroupRuntime: Media.TelegramMediaGroupController<TMessage, TContext>;
|
|
@@ -183,7 +234,21 @@ export declare function createTelegramInboundRouteRuntime<TUpdate extends Update
|
|
|
183
234
|
message?: TMessage;
|
|
184
235
|
edited_message?: TMessage;
|
|
185
236
|
callback_query?: TCallbackQuery;
|
|
186
|
-
}, TMessage extends TelegramRoutedMessage, TCallbackQuery extends TelegramRoutedCallbackQuery, TContext, TModel extends Model.MenuModel>(deps: TelegramInboundRouteRuntimeDeps<TMessage, TCallbackQuery, TContext, TModel>): Updates.TelegramUpdateRuntimeController<TContext, TUpdate
|
|
237
|
+
}, TMessage extends TelegramRoutedMessage, TCallbackQuery extends TelegramRoutedCallbackQuery, TContext, TModel extends Model.MenuModel>(deps: TelegramInboundRouteRuntimeDeps<TMessage, TCallbackQuery, TContext, TModel>): Updates.TelegramUpdateRuntimeController<TContext, TUpdate> & {
|
|
238
|
+
expireRoutingInput: NonNullable<Updates.TelegramUpdateWorkerRuntimeDeps<TContext>["expireRoutingInput"]>;
|
|
239
|
+
shouldReviewHistoricalInput: NonNullable<Updates.TelegramUpdateWorkerRuntimeDeps<TContext>["shouldReviewHistoricalInput"]>;
|
|
240
|
+
shouldHoldPendingInput: NonNullable<Updates.TelegramUpdateWorkerRuntimeDeps<TContext>["shouldHoldPendingInput"]>;
|
|
241
|
+
forgetPreviousWorld(input: Updates.TelegramHeldSourcePreparation<TContext>, captureTransport?: (ctx: TContext) => (() => boolean) | undefined): Promise<{
|
|
242
|
+
forgotten: number;
|
|
243
|
+
deleted: number;
|
|
244
|
+
}>;
|
|
245
|
+
beforeQueueReceiptPublished: NonNullable<Updates.TelegramUpdateWorkerRuntimeDeps<TContext>["beforeQueueReceiptPublished"]>;
|
|
246
|
+
onQueueReceiptCommitted: NonNullable<Updates.TelegramUpdateWorkerRuntimeDeps<TContext>["onQueueReceiptCommitted"]>;
|
|
247
|
+
onQueueReceiptCompleted: NonNullable<Updates.TelegramUpdateWorkerRuntimeDeps<TContext>["onQueueReceiptCompleted"]>;
|
|
248
|
+
onUpdateCompleted: NonNullable<Updates.TelegramUpdateWorkerRuntimeDeps<TContext>["onUpdateCompleted"]>;
|
|
249
|
+
onWorkspaceRestoreRecipientObserved(follower: Bus.TelegramBusFollowerView, isCurrent: () => boolean, ctx: TContext | undefined): Promise<void> | undefined;
|
|
250
|
+
waitForRestoreSettlement(): Promise<void>;
|
|
251
|
+
};
|
|
187
252
|
export interface TelegramAssistantOutputAuthority<TTransportStamp> {
|
|
188
253
|
transportStamp: TTransportStamp;
|
|
189
254
|
route: "direct" | "follower" | "none";
|