@llblab/pi-telegram 0.52.2 → 0.53.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/AGENTS.md +9 -7
- package/BACKLOG.md +1 -18
- package/CHANGELOG.md +11 -0
- package/README.md +7 -11
- package/api/commands.ts +3 -0
- package/dist/api/commands.d.ts +1 -1
- package/dist/lib/activity-verbosity.d.ts +1 -5
- package/dist/lib/activity-verbosity.js +15 -42
- package/dist/lib/activity.d.ts +9 -0
- package/dist/lib/activity.js +39 -10
- package/dist/lib/agent-messages.js +7 -3
- package/dist/lib/bindings.d.ts +43 -5
- package/dist/lib/bindings.js +163 -20
- package/dist/lib/bus-api.d.ts +12 -0
- package/dist/lib/bus-api.js +182 -79
- package/dist/lib/bus-follower.d.ts +111 -31
- package/dist/lib/bus-follower.js +1324 -359
- package/dist/lib/bus-leader.d.ts +41 -91
- package/dist/lib/bus-leader.js +969 -637
- package/dist/lib/bus-transport.js +16 -9
- package/dist/lib/bus.d.ts +215 -18
- package/dist/lib/bus.js +1048 -327
- package/dist/lib/channel-posts.d.ts +5 -0
- package/dist/lib/channel-posts.js +434 -261
- package/dist/lib/command-templates.d.ts +26 -5
- package/dist/lib/command-templates.js +92 -176
- package/dist/lib/commands.d.ts +168 -82
- package/dist/lib/commands.js +1409 -291
- package/dist/lib/config.d.ts +1 -8
- package/dist/lib/config.js +34 -76
- package/dist/lib/delivery.d.ts +2 -2
- package/dist/lib/delivery.js +16 -16
- package/dist/lib/extension.js +639 -171
- package/dist/lib/generative-app-worker.mjs +19 -7
- package/dist/lib/generative-apps.d.ts +0 -5
- package/dist/lib/generative-apps.js +61 -49
- package/dist/lib/inbound.d.ts +5 -0
- package/dist/lib/inbound.js +35 -59
- package/dist/lib/journal.d.ts +33 -3
- package/dist/lib/journal.js +2063 -782
- package/dist/lib/lifecycle.d.ts +2 -6
- package/dist/lib/lifecycle.js +75 -58
- package/dist/lib/locks.d.ts +29 -3
- package/dist/lib/locks.js +289 -108
- package/dist/lib/logging.js +50 -14
- package/dist/lib/media.d.ts +22 -1
- package/dist/lib/media.js +46 -28
- package/dist/lib/menu-model.d.ts +19 -5
- package/dist/lib/menu-model.js +53 -11
- package/dist/lib/menu-queue.d.ts +5 -4
- package/dist/lib/menu-queue.js +45 -63
- package/dist/lib/menu-settings.d.ts +5 -4
- package/dist/lib/menu-settings.js +114 -78
- package/dist/lib/menu-status.d.ts +1 -1
- package/dist/lib/menu-status.js +8 -25
- package/dist/lib/menu-thinking.d.ts +6 -1
- package/dist/lib/menu-thinking.js +14 -21
- package/dist/lib/menu.d.ts +8 -61
- package/dist/lib/menu.js +92 -164
- package/dist/lib/model.d.ts +1 -1
- package/dist/lib/model.js +2 -16
- package/dist/lib/outbound-attachments.d.ts +6 -27
- package/dist/lib/outbound-attachments.js +45 -38
- package/dist/lib/outbound-buttons.js +22 -16
- package/dist/lib/outbound-markup.d.ts +0 -2
- package/dist/lib/outbound-markup.js +51 -64
- package/dist/lib/outbound-voice.js +6 -3
- package/dist/lib/outbound.d.ts +6 -6
- package/dist/lib/outbound.js +61 -51
- package/dist/lib/ownership.js +6 -2
- package/dist/lib/paths.d.ts +5 -0
- package/dist/lib/paths.js +37 -21
- package/dist/lib/pi.d.ts +2 -0
- package/dist/lib/pi.js +8 -1
- package/dist/lib/polling.d.ts +21 -37
- package/dist/lib/polling.js +59 -50
- package/dist/lib/preview.js +39 -19
- package/dist/lib/process-identity.d.ts +24 -0
- package/dist/lib/process-identity.js +133 -0
- package/dist/lib/prompts.js +2 -7
- package/dist/lib/queue.d.ts +14 -16
- package/dist/lib/queue.js +249 -126
- package/dist/lib/recovery.d.ts +2 -83
- package/dist/lib/recovery.js +20 -274
- package/dist/lib/replies.d.ts +16 -24
- package/dist/lib/replies.js +99 -116
- package/dist/lib/routing.d.ts +120 -7
- package/dist/lib/routing.js +4667 -1152
- package/dist/lib/runtime.d.ts +1 -11
- package/dist/lib/sections.d.ts +4 -29
- package/dist/lib/sections.js +7 -20
- package/dist/lib/skills.js +4 -3
- package/dist/lib/status.js +86 -57
- package/dist/lib/sync.d.ts +1 -3
- package/dist/lib/sync.js +67 -43
- package/dist/lib/target.d.ts +4 -0
- package/dist/lib/target.js +18 -0
- package/dist/lib/telegram-api.d.ts +18 -13
- package/dist/lib/telegram-api.js +148 -53
- package/dist/lib/text-groups.js +4 -39
- package/dist/lib/thread-cleanup-manager.d.ts +6 -27
- package/dist/lib/thread-cleanup-manager.js +369 -186
- package/dist/lib/thread-display.d.ts +1 -2
- package/dist/lib/thread-display.js +58 -38
- package/dist/lib/thread-naming.d.ts +28 -8
- package/dist/lib/thread-naming.js +175 -66
- package/dist/lib/thread-reconciler.d.ts +42 -16
- package/dist/lib/thread-reconciler.js +90 -50
- package/dist/lib/threads.d.ts +82 -20
- package/dist/lib/threads.js +2247 -855
- package/dist/lib/time-injection.js +1 -2
- package/dist/lib/turns.js +1 -19
- package/dist/lib/updates.d.ts +169 -80
- package/dist/lib/updates.js +2418 -641
- package/dist/lib/voice.d.ts +7 -22
- package/dist/lib/voice.js +7 -22
- package/dist/lib/wire.js +1 -1
- package/dist/lib/workspace-admission.d.ts +3 -1
- package/dist/lib/workspace-admission.js +425 -211
- package/dist/lib/workspace-identity.js +8 -13
- package/dist/lib/workspace-retirement.d.ts +1 -1
- package/dist/lib/workspace-retirement.js +328 -140
- package/dist/lib/workspace-slots.js +8 -4
- package/dist/package.json +1 -1
- package/docs/architecture.md +31 -29
- package/docs/inbound.md +1 -1
- package/docs/multi-instance-bus.md +15 -3
- package/docs/outbound.md +1 -1
- package/docs/public-api.md +29 -1
- package/docs/ui-style.md +25 -8
- package/lib/activity-verbosity.ts +59 -82
- package/lib/activity.ts +146 -102
- package/lib/agent-messages.ts +12 -5
- package/lib/bindings.ts +389 -82
- package/lib/bus-api.ts +263 -102
- package/lib/bus-follower.ts +2440 -601
- package/lib/bus-leader.ts +2209 -1290
- package/lib/bus-transport.ts +46 -12
- package/lib/bus.ts +1673 -444
- package/lib/channel-posts.ts +848 -327
- package/lib/command-templates.ts +152 -265
- package/lib/commands.ts +2398 -555
- package/lib/config.ts +177 -170
- package/lib/delivery.ts +41 -29
- package/lib/extension.ts +1206 -433
- package/lib/generative-app-worker.mjs +19 -7
- package/lib/generative-apps.ts +324 -211
- package/lib/inbound.ts +95 -97
- package/lib/journal.ts +5311 -1763
- package/lib/keyboard.ts +1 -3
- package/lib/lifecycle.ts +128 -78
- package/lib/locks.ts +677 -203
- package/lib/logging.ts +147 -58
- package/lib/media.ts +82 -43
- package/lib/menu-model.ts +85 -18
- package/lib/menu-queue.ts +88 -129
- package/lib/menu-settings.ts +260 -155
- package/lib/menu-status.ts +20 -60
- package/lib/menu-thinking.ts +41 -56
- package/lib/menu.ts +112 -285
- package/lib/model.ts +8 -22
- package/lib/outbound-attachments.ts +156 -125
- package/lib/outbound-buttons.ts +76 -49
- package/lib/outbound-markup.ts +96 -94
- package/lib/outbound-voice.ts +33 -27
- package/lib/outbound.ts +125 -125
- package/lib/ownership.ts +19 -10
- package/lib/paths.ts +148 -49
- package/lib/pi.ts +35 -15
- package/lib/polling.ts +144 -147
- package/lib/preview.ts +93 -40
- package/lib/process-identity.ts +164 -0
- package/lib/prompts.ts +4 -11
- package/lib/queue.ts +511 -300
- package/lib/recovery.ts +50 -454
- package/lib/rendering.ts +1 -3
- package/lib/replies.ts +248 -209
- package/lib/routing.ts +8509 -2186
- package/lib/runtime.ts +1 -12
- package/lib/sections.ts +24 -89
- package/lib/setup.ts +1 -3
- package/lib/skills.ts +9 -4
- package/lib/status.ts +215 -110
- package/lib/sync.ts +448 -361
- package/lib/target.ts +22 -3
- package/lib/telegram-api.ts +302 -127
- package/lib/text-groups.ts +5 -39
- package/lib/thread-cleanup-manager.ts +833 -331
- package/lib/thread-display.ts +213 -97
- package/lib/thread-naming.ts +232 -87
- package/lib/thread-reconciler.ts +155 -72
- package/lib/threads.ts +5318 -1845
- package/lib/time-injection.ts +1 -4
- package/lib/turns.ts +15 -28
- package/lib/updates.ts +5184 -1612
- package/lib/voice.ts +7 -35
- package/lib/wire.ts +1 -1
- package/lib/workspace-admission.ts +996 -416
- package/lib/workspace-identity.ts +14 -17
- package/lib/workspace-retirement.ts +878 -376
- package/lib/workspace-slots.ts +22 -13
- package/package.json +1 -1
- package/scripts/check-downgrade.mjs +0 -472
- package/scripts/measure-workspace.mjs +0 -108
package/lib/paths.ts
CHANGED
|
@@ -2,16 +2,20 @@
|
|
|
2
2
|
* Telegram bridge path resolution for Pi-compatible runtimes
|
|
3
3
|
* Zones: telemetry paths, filesystem, runtime identity
|
|
4
4
|
* Owns agent-dir detection and extension-local path derivation
|
|
5
|
-
*
|
|
6
|
-
* This domain is path-only: it resolves directories and file paths from
|
|
7
|
-
* environment and runtime identity. Its only filesystem read canonicalizes the
|
|
8
|
-
* existing agent-dir prefix; it does not read config, manage state, or import
|
|
9
|
-
* broader Telegram domains.
|
|
10
5
|
*/
|
|
6
|
+
|
|
11
7
|
import { createHash } from "node:crypto";
|
|
12
8
|
import { realpathSync } from "node:fs";
|
|
13
9
|
import { homedir } from "node:os";
|
|
14
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
basename,
|
|
12
|
+
dirname,
|
|
13
|
+
isAbsolute,
|
|
14
|
+
join,
|
|
15
|
+
relative,
|
|
16
|
+
resolve,
|
|
17
|
+
sep,
|
|
18
|
+
} from "node:path";
|
|
15
19
|
|
|
16
20
|
export const TELEGRAM_DEFAULT_PROFILE_NAME = "default";
|
|
17
21
|
|
|
@@ -50,11 +54,22 @@ export function resolveAgentDir(
|
|
|
50
54
|
* Reject aliases; never repair relative historical references or redirect storage.
|
|
51
55
|
* Equality proves spelling only, not file identity, consumer closure or migration readiness.
|
|
52
56
|
*/
|
|
53
|
-
export function requireTelegramStoragePathReference(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
export function requireTelegramStoragePathReference(
|
|
58
|
+
path: string,
|
|
59
|
+
expectedPath: string,
|
|
60
|
+
): string {
|
|
61
|
+
if (
|
|
62
|
+
typeof path !== "string" ||
|
|
63
|
+
typeof expectedPath !== "string" ||
|
|
64
|
+
!isAbsolute(path) ||
|
|
65
|
+
resolve(path) !== path ||
|
|
66
|
+
!isAbsolute(expectedPath) ||
|
|
67
|
+
resolve(expectedPath) !== expectedPath ||
|
|
68
|
+
path !== expectedPath
|
|
69
|
+
) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
"Telegram storage reference does not match its approved absolute path.",
|
|
72
|
+
);
|
|
58
73
|
}
|
|
59
74
|
return path;
|
|
60
75
|
}
|
|
@@ -67,17 +82,26 @@ export function resolveTelegramConfigPath(): string {
|
|
|
67
82
|
/**
|
|
68
83
|
* Resolve symlinks in the longest existing prefix of `path` and keep the missing suffix. Strict journal reads require
|
|
69
84
|
* canonical anchors, so a symlinked agent directory (for example macOS `/var` → `/private/var`) must not leak in.
|
|
70
|
-
* Relative input is returned unchanged so callers' exact-absolute-path guards still reject it. Windows
|
|
71
|
-
*
|
|
85
|
+
* Relative input is returned unchanged so callers' exact-absolute-path guards still reject it. On Windows only
|
|
86
|
+
* drive-letter and UNC paths resolve; resolving a drive-less root would add the current drive.
|
|
72
87
|
*/
|
|
73
88
|
function canonicalizeExistingPrefix(path: string): string {
|
|
74
|
-
if (
|
|
89
|
+
if (
|
|
90
|
+
!isAbsolute(path) ||
|
|
91
|
+
(process.platform === "win32" && !/^(?:[a-zA-Z]:[\\/]|\\\\)/u.test(path))
|
|
92
|
+
)
|
|
93
|
+
return path;
|
|
75
94
|
const absolute = resolve(path);
|
|
76
95
|
const missing: string[] = [];
|
|
77
96
|
for (let current = absolute; ; current = dirname(current)) {
|
|
78
|
-
try {
|
|
79
|
-
|
|
80
|
-
|
|
97
|
+
try {
|
|
98
|
+
return join(realpathSync(current), ...missing.reverse());
|
|
99
|
+
} catch (error) {
|
|
100
|
+
if (
|
|
101
|
+
(error as { code?: unknown }).code !== "ENOENT" ||
|
|
102
|
+
dirname(current) === current
|
|
103
|
+
)
|
|
104
|
+
return absolute;
|
|
81
105
|
missing.push(basename(current));
|
|
82
106
|
}
|
|
83
107
|
}
|
|
@@ -89,12 +113,16 @@ export function resolveTelegramTempDir(agentDir = resolveAgentDir()): string {
|
|
|
89
113
|
}
|
|
90
114
|
|
|
91
115
|
/** Consolidated-root service artifacts (transaction guards, staging and IPC), never session custody. */
|
|
92
|
-
export function resolveTelegramRuntimeDir(
|
|
116
|
+
export function resolveTelegramRuntimeDir(
|
|
117
|
+
agentDir = resolveAgentDir(),
|
|
118
|
+
): string {
|
|
93
119
|
return join(resolveTelegramTempDir(agentDir), "runtime");
|
|
94
120
|
}
|
|
95
121
|
|
|
96
122
|
/** Rotated shared runtime event segments, outside the persistent root-file census. */
|
|
97
|
-
export function resolveTelegramRuntimeLogsDir(
|
|
123
|
+
export function resolveTelegramRuntimeLogsDir(
|
|
124
|
+
agentDir = resolveAgentDir(),
|
|
125
|
+
): string {
|
|
98
126
|
return join(resolveTelegramTempDir(agentDir), "logs");
|
|
99
127
|
}
|
|
100
128
|
|
|
@@ -104,12 +132,16 @@ export function resolveTelegramStatePath(agentDir = resolveAgentDir()): string {
|
|
|
104
132
|
}
|
|
105
133
|
|
|
106
134
|
/** Flat attachment scratch directory (<agentDir>/tmp/pi-telegram/attachments). */
|
|
107
|
-
export function resolveTelegramAttachmentsDir(
|
|
135
|
+
export function resolveTelegramAttachmentsDir(
|
|
136
|
+
agentDir = resolveAgentDir(),
|
|
137
|
+
): string {
|
|
108
138
|
return join(resolveTelegramTempDir(agentDir), "attachments");
|
|
109
139
|
}
|
|
110
140
|
|
|
111
141
|
/** Read-only pre-0.52.0 ownership file, consulted only to refuse a second poller beside a live older leader. */
|
|
112
|
-
export function resolveLegacyTelegramOwnersPath(
|
|
142
|
+
export function resolveLegacyTelegramOwnersPath(
|
|
143
|
+
agentDir = resolveAgentDir(),
|
|
144
|
+
): string {
|
|
113
145
|
return join(agentDir, "tmp", "telegram", "owners.json");
|
|
114
146
|
}
|
|
115
147
|
|
|
@@ -173,21 +205,38 @@ export function resolveTelegramServiceJournalStorage(
|
|
|
173
205
|
): { path: string; runtimeDir: string } {
|
|
174
206
|
const profileHash = createHash("sha256").update(profileName).digest("hex");
|
|
175
207
|
return {
|
|
176
|
-
path: join(
|
|
208
|
+
path: join(
|
|
209
|
+
resolveTelegramTempDir(agentDir),
|
|
210
|
+
"journals",
|
|
211
|
+
`${kind}.${profileHash}.json`,
|
|
212
|
+
),
|
|
177
213
|
runtimeDir: resolveTelegramRuntimeDir(agentDir),
|
|
178
214
|
};
|
|
179
215
|
}
|
|
180
216
|
|
|
181
217
|
/** Bind one non-session journal to its exact colocated-root service namespace, never a format fallback. */
|
|
182
|
-
export function getTelegramJournalPublicationPaths(
|
|
218
|
+
export function getTelegramJournalPublicationPaths(
|
|
219
|
+
path: string,
|
|
220
|
+
runtimeDir?: string,
|
|
221
|
+
): {
|
|
183
222
|
transactionPath: string;
|
|
184
223
|
temporaryBasePath: string;
|
|
185
224
|
} {
|
|
186
|
-
if (runtimeDir === undefined)
|
|
225
|
+
if (runtimeDir === undefined)
|
|
226
|
+
return { transactionPath: `${path}.transaction`, temporaryBasePath: path };
|
|
187
227
|
requireTelegramStoragePathReference(path, resolve(path));
|
|
188
|
-
requireTelegramStoragePathReference(
|
|
189
|
-
|
|
190
|
-
|
|
228
|
+
requireTelegramStoragePathReference(
|
|
229
|
+
runtimeDir,
|
|
230
|
+
join(dirname(dirname(path)), "runtime"),
|
|
231
|
+
);
|
|
232
|
+
if (basename(dirname(path)) !== "journals")
|
|
233
|
+
throw new Error(
|
|
234
|
+
"Telegram service journal must use its journals namespace.",
|
|
235
|
+
);
|
|
236
|
+
return {
|
|
237
|
+
transactionPath: join(runtimeDir, `${basename(path)}.transaction`),
|
|
238
|
+
temporaryBasePath: join(runtimeDir, basename(path)),
|
|
239
|
+
};
|
|
191
240
|
}
|
|
192
241
|
|
|
193
242
|
/** Durable inactive Thread cleanup work-set journal. */
|
|
@@ -195,7 +244,12 @@ export function resolveTelegramThreadCleanupWorkPath(
|
|
|
195
244
|
agentDir = resolveAgentDir(),
|
|
196
245
|
profileName?: string,
|
|
197
246
|
): string {
|
|
198
|
-
return resolveTelegramProfileTempFilePath(
|
|
247
|
+
return resolveTelegramProfileTempFilePath(
|
|
248
|
+
"thread-cleanup",
|
|
249
|
+
"json",
|
|
250
|
+
agentDir,
|
|
251
|
+
profileName,
|
|
252
|
+
);
|
|
199
253
|
}
|
|
200
254
|
|
|
201
255
|
/** Durable agent-authored channel post journal. */
|
|
@@ -203,7 +257,12 @@ export function resolveTelegramChannelPostJournalPath(
|
|
|
203
257
|
agentDir = resolveAgentDir(),
|
|
204
258
|
profileName?: string,
|
|
205
259
|
): string {
|
|
206
|
-
return resolveTelegramProfileTempFilePath(
|
|
260
|
+
return resolveTelegramProfileTempFilePath(
|
|
261
|
+
"channel-posts",
|
|
262
|
+
"json",
|
|
263
|
+
agentDir,
|
|
264
|
+
profileName,
|
|
265
|
+
);
|
|
207
266
|
}
|
|
208
267
|
|
|
209
268
|
/** Durable inbound update journal (<agentDir>/tmp/pi-telegram/inbox[.<profile>].json). */
|
|
@@ -248,7 +307,8 @@ export function resolveTelegramFollowerJournalPath(
|
|
|
248
307
|
}
|
|
249
308
|
|
|
250
309
|
const TELEGRAM_SESSION_DIRECTORY_MAX_LENGTH = 200;
|
|
251
|
-
const WINDOWS_RESERVED_NAME =
|
|
310
|
+
const WINDOWS_RESERVED_NAME =
|
|
311
|
+
/^(?:con|prn|aux|nul|com[0-9]|lpt[0-9])(?:\..*)?$/u;
|
|
252
312
|
|
|
253
313
|
/**
|
|
254
314
|
* Directory name for a session id. Lowercase letters, digits, `_`, `-` and inner `.` stay verbatim, so every real
|
|
@@ -256,22 +316,33 @@ const WINDOWS_RESERVED_NAME = /^(?:con|prn|aux|nul|com[0-9]|lpt[0-9])(?:\..*)?$/
|
|
|
256
316
|
* filesystems never merge two sessions; a leading or trailing dot and Windows reserved names are encoded so the name
|
|
257
317
|
* can never be `.`/`..`, hidden, or a device. The encoding is reversible; an empty or over-long result is refused.
|
|
258
318
|
*/
|
|
259
|
-
export function encodeTelegramSessionDirectoryName(
|
|
319
|
+
export function encodeTelegramSessionDirectoryName(
|
|
320
|
+
sessionId: string,
|
|
321
|
+
): string | undefined {
|
|
260
322
|
if (typeof sessionId !== "string" || sessionId.length === 0) return undefined;
|
|
261
323
|
const bytes = Buffer.from(sessionId, "utf8");
|
|
262
324
|
if (bytes.toString("utf8") !== sessionId) return undefined;
|
|
263
325
|
let name = "";
|
|
264
326
|
bytes.forEach((byte, index) => {
|
|
265
327
|
const char = String.fromCharCode(byte);
|
|
266
|
-
const safe =
|
|
267
|
-
|
|
328
|
+
const safe =
|
|
329
|
+
/[a-z0-9_-]/u.test(char) ||
|
|
330
|
+
(char === "." && index > 0 && index < bytes.length - 1);
|
|
331
|
+
name += safe
|
|
332
|
+
? char
|
|
333
|
+
: `%${byte.toString(16).toUpperCase().padStart(2, "0")}`;
|
|
268
334
|
});
|
|
269
|
-
if (WINDOWS_RESERVED_NAME.test(name))
|
|
270
|
-
|
|
335
|
+
if (WINDOWS_RESERVED_NAME.test(name))
|
|
336
|
+
name = `%${name.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0")}${name.slice(1)}`;
|
|
337
|
+
return name.length <= TELEGRAM_SESSION_DIRECTORY_MAX_LENGTH
|
|
338
|
+
? name
|
|
339
|
+
: undefined;
|
|
271
340
|
}
|
|
272
341
|
|
|
273
342
|
/** Inverse of `encodeTelegramSessionDirectoryName`; undefined unless the name is exactly canonical. */
|
|
274
|
-
export function decodeTelegramSessionDirectoryName(
|
|
343
|
+
export function decodeTelegramSessionDirectoryName(
|
|
344
|
+
name: string,
|
|
345
|
+
): string | undefined {
|
|
275
346
|
if (!/^[a-z0-9_.%A-F-]+$/u.test(name)) return undefined;
|
|
276
347
|
const bytes: number[] = [];
|
|
277
348
|
for (let index = 0; index < name.length; index++) {
|
|
@@ -283,25 +354,39 @@ export function decodeTelegramSessionDirectoryName(name: string): string | undef
|
|
|
283
354
|
} else bytes.push(name.charCodeAt(index));
|
|
284
355
|
}
|
|
285
356
|
const decoded = Buffer.from(bytes).toString("utf8");
|
|
286
|
-
return encodeTelegramSessionDirectoryName(decoded) === name
|
|
357
|
+
return encodeTelegramSessionDirectoryName(decoded) === name
|
|
358
|
+
? decoded
|
|
359
|
+
: undefined;
|
|
287
360
|
}
|
|
288
361
|
|
|
289
362
|
/** Root of every session folder (<agentDir>/tmp/pi-telegram/sessions). */
|
|
290
|
-
export function resolveTelegramSessionsDir(
|
|
363
|
+
export function resolveTelegramSessionsDir(
|
|
364
|
+
agentDir = resolveAgentDir(),
|
|
365
|
+
): string {
|
|
291
366
|
return join(resolveTelegramTempDir(agentDir), "sessions");
|
|
292
367
|
}
|
|
293
368
|
|
|
294
369
|
/** One session's folder (<agentDir>/tmp/pi-telegram/sessions/<session id>). */
|
|
295
|
-
export function resolveTelegramSessionDir(
|
|
370
|
+
export function resolveTelegramSessionDir(
|
|
371
|
+
sessionId: string,
|
|
372
|
+
agentDir = resolveAgentDir(),
|
|
373
|
+
): string {
|
|
296
374
|
const name = encodeTelegramSessionDirectoryName(sessionId);
|
|
297
|
-
if (!name)
|
|
375
|
+
if (!name)
|
|
376
|
+
throw new Error("Telegram session directory requires a usable session id.");
|
|
298
377
|
return join(resolveTelegramSessionsDir(agentDir), name);
|
|
299
378
|
}
|
|
300
379
|
|
|
301
380
|
/** Hash naming one recipient incarnation inside its session folder; unchanged from the former follower inbox. */
|
|
302
|
-
export function getTelegramRecipientJournalHash(
|
|
303
|
-
|
|
304
|
-
|
|
381
|
+
export function getTelegramRecipientJournalHash(
|
|
382
|
+
recipientBindingKey: string,
|
|
383
|
+
): string {
|
|
384
|
+
if (!recipientBindingKey)
|
|
385
|
+
throw new Error("Telegram session journal binding key is required.");
|
|
386
|
+
return createHash("sha256")
|
|
387
|
+
.update(recipientBindingKey)
|
|
388
|
+
.digest("hex")
|
|
389
|
+
.slice(0, 16);
|
|
305
390
|
}
|
|
306
391
|
|
|
307
392
|
/** Durable per-session journal (`sessions/<session id>/journal.<hash>[.<profile>].json`); the name never reveals a role. */
|
|
@@ -323,22 +408,36 @@ export function resolveTelegramSessionPollingJournalPath(
|
|
|
323
408
|
agentDir = resolveAgentDir(),
|
|
324
409
|
profileName?: string,
|
|
325
410
|
): string {
|
|
326
|
-
return join(
|
|
411
|
+
return join(
|
|
412
|
+
resolveTelegramSessionDir(sessionId, agentDir),
|
|
413
|
+
`inbox${getTelegramProfilePathSuffix(profileName)}.json`,
|
|
414
|
+
);
|
|
327
415
|
}
|
|
328
416
|
|
|
329
417
|
/** Accept only a canonical `sessions/<id>/inbox[.<profile>].json` beneath this agent's runtime. */
|
|
330
|
-
export function isTelegramSessionPollingJournalPath(
|
|
418
|
+
export function isTelegramSessionPollingJournalPath(
|
|
419
|
+
path: string,
|
|
420
|
+
agentDir = resolveAgentDir(),
|
|
421
|
+
): boolean {
|
|
331
422
|
const parts = relative(resolveTelegramSessionsDir(agentDir), path).split(sep);
|
|
332
|
-
return
|
|
333
|
-
|
|
423
|
+
return (
|
|
424
|
+
isAbsolute(path) &&
|
|
425
|
+
parts.length === 2 &&
|
|
426
|
+
decodeTelegramSessionDirectoryName(parts[0]!) !== undefined &&
|
|
427
|
+
/^inbox(?:\.[a-zA-Z0-9._-]+)?\.json$/u.test(parts[1]!)
|
|
428
|
+
);
|
|
334
429
|
}
|
|
335
430
|
|
|
336
431
|
/** Runtime event log (<agentDir>/tmp/pi-telegram/logs.jsonl). */
|
|
337
|
-
export function resolveTelegramRuntimeLogPath(
|
|
432
|
+
export function resolveTelegramRuntimeLogPath(
|
|
433
|
+
agentDir = resolveAgentDir(),
|
|
434
|
+
): string {
|
|
338
435
|
return resolveTelegramProfileTempFilePath("logs", "jsonl", agentDir);
|
|
339
436
|
}
|
|
340
437
|
|
|
341
438
|
/** Bounded previous shared log segment below `logs/`. */
|
|
342
|
-
export function resolveTelegramPreviousSharedRuntimeLogPath(
|
|
439
|
+
export function resolveTelegramPreviousSharedRuntimeLogPath(
|
|
440
|
+
agentDir = resolveAgentDir(),
|
|
441
|
+
): string {
|
|
343
442
|
return join(resolveTelegramRuntimeLogsDir(agentDir), "logs._prev.jsonl");
|
|
344
443
|
}
|
package/lib/pi.ts
CHANGED
|
@@ -93,6 +93,15 @@ function isPiRunMode(value: unknown): value is PiRunMode {
|
|
|
93
93
|
);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
/** Pi rejects a context captured before session replacement with a stale-context error; this is not a domain failure. */
|
|
97
|
+
export function isPiStaleContextError(error: unknown): boolean {
|
|
98
|
+
return (
|
|
99
|
+
error instanceof Error &&
|
|
100
|
+
(error.message.includes("stale after session") ||
|
|
101
|
+
error.message.includes("stale ctx"))
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
96
105
|
export function getExtensionContextMode(ctx: unknown): PiRunMode | undefined {
|
|
97
106
|
const mode =
|
|
98
107
|
typeof ctx === "object" && ctx !== null
|
|
@@ -184,10 +193,15 @@ type HostSettingsManager = {
|
|
|
184
193
|
|
|
185
194
|
function readEnabledModels(value: unknown): string[] | undefined {
|
|
186
195
|
if (value === undefined) return undefined;
|
|
187
|
-
if (
|
|
196
|
+
if (
|
|
197
|
+
Array.isArray(value) &&
|
|
198
|
+
value.every((entry) => typeof entry === "string")
|
|
199
|
+
) {
|
|
188
200
|
return [...value];
|
|
189
201
|
}
|
|
190
|
-
throw new TypeError(
|
|
202
|
+
throw new TypeError(
|
|
203
|
+
"Host settings enabledModels must be a string array or undefined.",
|
|
204
|
+
);
|
|
191
205
|
}
|
|
192
206
|
|
|
193
207
|
export function normalizeSettingsManager(manager: unknown): PiSettingsManager {
|
|
@@ -198,18 +212,20 @@ export function normalizeSettingsManager(manager: unknown): PiSettingsManager {
|
|
|
198
212
|
if (typeof host.flush !== "function") {
|
|
199
213
|
throw new TypeError("Host settings manager must provide flush().");
|
|
200
214
|
}
|
|
201
|
-
const read =
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
: typeof host.set === "function"
|
|
215
|
+
const read =
|
|
216
|
+
typeof host.getEnabledModels === "function"
|
|
217
|
+
? () => host.getEnabledModels!.call(host)
|
|
218
|
+
: typeof host.get === "function"
|
|
219
|
+
? () => host.get!.call(host, "enabledModels")
|
|
220
|
+
: undefined;
|
|
221
|
+
const write =
|
|
222
|
+
typeof host.setEnabledModels === "function"
|
|
210
223
|
? (patterns: string[] | undefined) =>
|
|
211
|
-
host.
|
|
212
|
-
:
|
|
224
|
+
host.setEnabledModels!.call(host, patterns)
|
|
225
|
+
: typeof host.set === "function"
|
|
226
|
+
? (patterns: string[] | undefined) =>
|
|
227
|
+
host.set!.call(host, "enabledModels", patterns ?? [])
|
|
228
|
+
: undefined;
|
|
213
229
|
if (!read || !write) {
|
|
214
230
|
throw new TypeError(
|
|
215
231
|
"Host settings manager must provide enabled-model read and write capabilities.",
|
|
@@ -264,8 +280,12 @@ export function getExtensionContextCwd(ctx: ExtensionContext): string {
|
|
|
264
280
|
}
|
|
265
281
|
|
|
266
282
|
export function getExtensionContextSessionId(ctx: ExtensionContext): string;
|
|
267
|
-
export function getExtensionContextSessionId(
|
|
268
|
-
|
|
283
|
+
export function getExtensionContextSessionId(
|
|
284
|
+
ctx: ExtensionContext | undefined,
|
|
285
|
+
): string | undefined;
|
|
286
|
+
export function getExtensionContextSessionId(
|
|
287
|
+
ctx: ExtensionContext | undefined,
|
|
288
|
+
): string | undefined {
|
|
269
289
|
// Minimal hosts and stale contexts may omit a session manager; absence is not an invented ID.
|
|
270
290
|
return ctx?.sessionManager?.getSessionId?.();
|
|
271
291
|
}
|