@llblab/pi-telegram 0.52.2 → 0.53.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +9 -7
- package/BACKLOG.md +1 -18
- package/CHANGELOG.md +15 -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 +65 -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 +252 -127
- 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 +128 -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 +516 -301
- 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/generative-apps.ts
CHANGED
|
@@ -22,6 +22,8 @@ import { Worker } from "node:worker_threads";
|
|
|
22
22
|
import { Type } from "@sinclair/typebox";
|
|
23
23
|
|
|
24
24
|
import type { ExtensionAPI } from "./pi.ts";
|
|
25
|
+
import { renameTelegramPathWithRetry } from "./locks.ts";
|
|
26
|
+
import { isProcessAlive } from "./process-identity.ts";
|
|
25
27
|
|
|
26
28
|
const GENERATIVE_APP_NAME = /^[a-z][a-z0-9-]{0,31}$/u;
|
|
27
29
|
const GENERATIVE_APP_METHOD = /^[a-z][a-z0-9_]{0,31}$/u;
|
|
@@ -120,10 +122,10 @@ export interface GenerativeAppLiveSurfaceRuntimeDeps<THandle> {
|
|
|
120
122
|
result: GenerativeAppInvocationResult,
|
|
121
123
|
handle: THandle,
|
|
122
124
|
) => GenerativeAppLiveSurfaceFrame<THandle>;
|
|
123
|
-
edit: (
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
125
|
+
edit: (frame: GenerativeAppLiveSurfaceFrame<THandle>) => Promise<THandle>;
|
|
126
|
+
classifyEditError?: (
|
|
127
|
+
error: unknown,
|
|
128
|
+
) =>
|
|
127
129
|
| { kind: "retry"; retryAfterMs?: number }
|
|
128
130
|
| { kind: "terminal" | "unavailable" | "unknown" };
|
|
129
131
|
recordRuntimeEvent?: (
|
|
@@ -131,7 +133,10 @@ export interface GenerativeAppLiveSurfaceRuntimeDeps<THandle> {
|
|
|
131
133
|
error: unknown,
|
|
132
134
|
details?: Record<string, unknown>,
|
|
133
135
|
) => void;
|
|
134
|
-
setTimer?: (
|
|
136
|
+
setTimer?: (
|
|
137
|
+
callback: () => void,
|
|
138
|
+
delayMs: number,
|
|
139
|
+
) => ReturnType<typeof setTimeout>;
|
|
135
140
|
clearTimer?: (timer: ReturnType<typeof setTimeout>) => void;
|
|
136
141
|
}
|
|
137
142
|
|
|
@@ -159,7 +164,8 @@ export interface TelegramBindToolRegistrationDeps extends GenerativeAppRuntimeOp
|
|
|
159
164
|
liveSurfaceSetTimer?: GenerativeAppLiveSurfaceRuntimeDeps<unknown>["setTimer"];
|
|
160
165
|
liveSurfaceClearTimer?: GenerativeAppLiveSurfaceRuntimeDeps<unknown>["clearTimer"];
|
|
161
166
|
setLiveSurfaceRuntime?: (
|
|
162
|
-
runtime:
|
|
167
|
+
runtime:
|
|
168
|
+
GenerativeAppLiveSurfaceRuntime<TelegramBindLiveHandle> | undefined,
|
|
163
169
|
) => void;
|
|
164
170
|
isDeliveryHandleCurrent?: (handle: TelegramBindDeliveryHandle) => boolean;
|
|
165
171
|
editView?: (
|
|
@@ -211,21 +217,9 @@ function wait(ms: number): Promise<void> {
|
|
|
211
217
|
return new Promise((resolveWait) => setTimeout(resolveWait, ms));
|
|
212
218
|
}
|
|
213
219
|
|
|
214
|
-
function isProcessAlive(pid: number): boolean {
|
|
215
|
-
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
216
|
-
try {
|
|
217
|
-
process.kill(pid, 0);
|
|
218
|
-
return true;
|
|
219
|
-
} catch (error) {
|
|
220
|
-
return (error as NodeJS.ErrnoException).code === "EPERM";
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
220
|
function assertAppName(app: string): void {
|
|
225
221
|
if (!GENERATIVE_APP_NAME.test(app)) {
|
|
226
|
-
throw new Error(
|
|
227
|
-
"Generative App name must match /^[a-z][a-z0-9-]{0,31}$/.",
|
|
228
|
-
);
|
|
222
|
+
throw new Error("Generative App name must match /^[a-z][a-z0-9-]{0,31}$/.");
|
|
229
223
|
}
|
|
230
224
|
}
|
|
231
225
|
|
|
@@ -237,7 +231,10 @@ function assertMethod(method: string): void {
|
|
|
237
231
|
}
|
|
238
232
|
}
|
|
239
233
|
|
|
240
|
-
function assertJsonValue(
|
|
234
|
+
function assertJsonValue(
|
|
235
|
+
value: unknown,
|
|
236
|
+
label: string,
|
|
237
|
+
): GenerativeAppJsonValue {
|
|
241
238
|
let encoded: string;
|
|
242
239
|
try {
|
|
243
240
|
encoded = JSON.stringify(value);
|
|
@@ -247,7 +244,9 @@ function assertJsonValue(value: unknown, label: string): GenerativeAppJsonValue
|
|
|
247
244
|
if (encoded === undefined) throw new Error(`${label} must be a JSON value.`);
|
|
248
245
|
const parsed = JSON.parse(encoded) as GenerativeAppJsonValue;
|
|
249
246
|
if (byteLength(encoded) > GENERATIVE_APP_MAX_STATE_BYTES) {
|
|
250
|
-
throw new Error(
|
|
247
|
+
throw new Error(
|
|
248
|
+
`${label} exceeds ${GENERATIVE_APP_MAX_STATE_BYTES} bytes.`,
|
|
249
|
+
);
|
|
251
250
|
}
|
|
252
251
|
return parsed;
|
|
253
252
|
}
|
|
@@ -266,14 +265,19 @@ async function ensureManagedAppsRoot(
|
|
|
266
265
|
try {
|
|
267
266
|
const metadata = await lstat(appsRoot);
|
|
268
267
|
if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
|
|
269
|
-
throw new Error(
|
|
268
|
+
throw new Error(
|
|
269
|
+
"Generative App root must be a managed non-symlink directory.",
|
|
270
|
+
);
|
|
270
271
|
}
|
|
271
272
|
} catch (error) {
|
|
272
|
-
if ((error as NodeJS.ErrnoException).code !== "ENOENT" || !create)
|
|
273
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT" || !create)
|
|
274
|
+
throw error;
|
|
273
275
|
await mkdir(appsRoot, { mode: 0o700 });
|
|
274
276
|
const metadata = await lstat(appsRoot);
|
|
275
277
|
if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
|
|
276
|
-
throw new Error(
|
|
278
|
+
throw new Error(
|
|
279
|
+
"Generative App root must be a managed non-symlink directory.",
|
|
280
|
+
);
|
|
277
281
|
}
|
|
278
282
|
}
|
|
279
283
|
return appsRoot;
|
|
@@ -295,7 +299,11 @@ async function writeFileAtomic(path: string, content: string): Promise<void> {
|
|
|
295
299
|
await mkdir(dirname(path), { recursive: true });
|
|
296
300
|
const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
297
301
|
await writeFile(temporary, content, { encoding: "utf8", mode: 0o600 });
|
|
298
|
-
|
|
302
|
+
// Windows may briefly deny replacing a file another reader or scanner holds; share the bounded retry.
|
|
303
|
+
if (!renameTelegramPathWithRetry(temporary, path))
|
|
304
|
+
throw new Error(
|
|
305
|
+
"Generative App staging file disappeared before publication.",
|
|
306
|
+
);
|
|
299
307
|
}
|
|
300
308
|
|
|
301
309
|
async function acquireGenerativeAppTransitionLock(
|
|
@@ -371,13 +379,15 @@ async function acquireGenerativeAppTransitionLock(
|
|
|
371
379
|
} catch {
|
|
372
380
|
// Re-check ownerless staleness below.
|
|
373
381
|
}
|
|
374
|
-
const currentOwnerlessStale =
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
382
|
+
const currentOwnerlessStale =
|
|
383
|
+
currentOwnerPid === undefined
|
|
384
|
+
? await stat(lockDir)
|
|
385
|
+
.then((metadata) => Date.now() - metadata.mtimeMs > 1_000)
|
|
386
|
+
.catch(() => false)
|
|
387
|
+
: false;
|
|
379
388
|
if (
|
|
380
|
-
(currentOwnerPid !== undefined &&
|
|
389
|
+
(currentOwnerPid !== undefined &&
|
|
390
|
+
!isProcessAlive(currentOwnerPid)) ||
|
|
381
391
|
currentOwnerlessStale
|
|
382
392
|
) {
|
|
383
393
|
await rm(lockDir, { recursive: true, force: true });
|
|
@@ -388,7 +398,9 @@ async function acquireGenerativeAppTransitionLock(
|
|
|
388
398
|
continue;
|
|
389
399
|
}
|
|
390
400
|
if (Date.now() >= deadline) {
|
|
391
|
-
throw new Error(
|
|
401
|
+
throw new Error(
|
|
402
|
+
"Generative App transition is busy in another process.",
|
|
403
|
+
);
|
|
392
404
|
}
|
|
393
405
|
await wait(25);
|
|
394
406
|
}
|
|
@@ -398,23 +410,31 @@ async function acquireGenerativeAppTransitionLock(
|
|
|
398
410
|
async function assertGenerativeAppModule(path: string): Promise<void> {
|
|
399
411
|
const metadata = await lstat(path);
|
|
400
412
|
if (!metadata.isFile() || metadata.isSymbolicLink()) {
|
|
401
|
-
throw new Error(
|
|
413
|
+
throw new Error(
|
|
414
|
+
`Generative App module is not a regular managed file: ${path}`,
|
|
415
|
+
);
|
|
402
416
|
}
|
|
403
417
|
if (metadata.size > GENERATIVE_APP_MAX_MODULE_BYTES) {
|
|
404
|
-
throw new Error(
|
|
418
|
+
throw new Error(
|
|
419
|
+
`Generative App module exceeds ${GENERATIVE_APP_MAX_MODULE_BYTES} bytes.`,
|
|
420
|
+
);
|
|
405
421
|
}
|
|
406
422
|
}
|
|
407
423
|
|
|
408
424
|
async function assertManagedAppDir(appDir: string): Promise<void> {
|
|
409
425
|
const metadata = await lstat(appDir);
|
|
410
426
|
if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
|
|
411
|
-
throw new Error(
|
|
427
|
+
throw new Error(
|
|
428
|
+
"Generative App path must be a managed non-symlink directory.",
|
|
429
|
+
);
|
|
412
430
|
}
|
|
413
431
|
}
|
|
414
432
|
|
|
415
433
|
async function readGenerativeAppGeneration(appDir: string): Promise<string> {
|
|
416
434
|
await assertManagedAppDir(appDir);
|
|
417
|
-
const generation = (
|
|
435
|
+
const generation = (
|
|
436
|
+
await readFile(join(appDir, "generation"), "utf8")
|
|
437
|
+
).trim();
|
|
418
438
|
if (!/^[0-9a-f]{8}-[0-9a-f-]{27}$/u.test(generation)) {
|
|
419
439
|
throw new Error("Generative App installation generation is invalid.");
|
|
420
440
|
}
|
|
@@ -430,7 +450,9 @@ function normalizeMethodResult(value: unknown): GenerativeAppMethodResult {
|
|
|
430
450
|
throw new Error("Generative App method result.output must be a string.");
|
|
431
451
|
}
|
|
432
452
|
if (byteLength(result.output) > GENERATIVE_APP_MAX_OUTPUT_BYTES) {
|
|
433
|
-
throw new Error(
|
|
453
|
+
throw new Error(
|
|
454
|
+
`Generative App output exceeds ${GENERATIVE_APP_MAX_OUTPUT_BYTES} bytes.`,
|
|
455
|
+
);
|
|
434
456
|
}
|
|
435
457
|
const viewMode = result.viewMode ?? "new";
|
|
436
458
|
if (viewMode !== "new" && viewMode !== "edit") {
|
|
@@ -444,7 +466,9 @@ function normalizeMethodResult(value: unknown): GenerativeAppMethodResult {
|
|
|
444
466
|
!Number.isInteger(result.refreshAfterMs) ||
|
|
445
467
|
result.refreshAfterMs <= 0
|
|
446
468
|
) {
|
|
447
|
-
throw new Error(
|
|
469
|
+
throw new Error(
|
|
470
|
+
"Generative App refreshAfterMs must be a finite positive integer.",
|
|
471
|
+
);
|
|
448
472
|
}
|
|
449
473
|
refreshAfterMs = Math.min(
|
|
450
474
|
GENERATIVE_APP_MAX_REFRESH_AFTER_MS,
|
|
@@ -461,7 +485,9 @@ function normalizeMethodResult(value: unknown): GenerativeAppMethodResult {
|
|
|
461
485
|
};
|
|
462
486
|
}
|
|
463
487
|
|
|
464
|
-
async function readStateTimeline(
|
|
488
|
+
async function readStateTimeline(
|
|
489
|
+
appDir: string,
|
|
490
|
+
): Promise<GenerativeAppStateEnvelope[]> {
|
|
465
491
|
const path = join(appDir, "states.jsonl");
|
|
466
492
|
let content: string;
|
|
467
493
|
try {
|
|
@@ -483,11 +509,17 @@ async function readStateTimeline(appDir: string): Promise<GenerativeAppStateEnve
|
|
|
483
509
|
parsed.revision !== envelopes.length ||
|
|
484
510
|
typeof parsed.method !== "string" ||
|
|
485
511
|
!Object.hasOwn(parsed, "state")
|
|
486
|
-
)
|
|
487
|
-
|
|
512
|
+
)
|
|
513
|
+
throw new Error("invalid envelope");
|
|
514
|
+
parsed.state = assertJsonValue(
|
|
515
|
+
parsed.state,
|
|
516
|
+
"Generative App journal state",
|
|
517
|
+
);
|
|
488
518
|
envelopes.push(parsed);
|
|
489
519
|
} catch {
|
|
490
|
-
const hasLaterContent = lines
|
|
520
|
+
const hasLaterContent = lines
|
|
521
|
+
.slice(index + 1)
|
|
522
|
+
.some((entry) => entry.trim());
|
|
491
523
|
if (!hasLaterContent) {
|
|
492
524
|
await writeFileAtomic(
|
|
493
525
|
path,
|
|
@@ -496,7 +528,9 @@ async function readStateTimeline(appDir: string): Promise<GenerativeAppStateEnve
|
|
|
496
528
|
);
|
|
497
529
|
break;
|
|
498
530
|
}
|
|
499
|
-
throw new Error(
|
|
531
|
+
throw new Error(
|
|
532
|
+
`Generative App state journal is corrupt at line ${index + 1}.`,
|
|
533
|
+
);
|
|
500
534
|
}
|
|
501
535
|
}
|
|
502
536
|
return envelopes;
|
|
@@ -537,23 +571,26 @@ async function executeGenerativeAppWorker(options: {
|
|
|
537
571
|
options.execution?.assertCurrent();
|
|
538
572
|
await assertGenerativeAppModule(options.modulePath);
|
|
539
573
|
return await new Promise((resolveResult, rejectResult) => {
|
|
540
|
-
const worker = new Worker(
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
574
|
+
const worker = new Worker(
|
|
575
|
+
new URL("./generative-app-worker.mjs", import.meta.url),
|
|
576
|
+
{
|
|
577
|
+
execArgv: [],
|
|
578
|
+
workerData: {
|
|
579
|
+
argument: options.argument,
|
|
580
|
+
argumentPresent: options.argument !== undefined,
|
|
581
|
+
method: options.method,
|
|
582
|
+
methodTimeoutMs: options.methodTimeoutMs,
|
|
583
|
+
modulePath: options.modulePath,
|
|
584
|
+
app: options.app,
|
|
585
|
+
revision: options.revision,
|
|
586
|
+
runMaxArgs: GENERATIVE_APP_RUN_MAX_ARGS,
|
|
587
|
+
runMaxStreamBytes: GENERATIVE_APP_RUN_MAX_STREAM_BYTES,
|
|
588
|
+
runMaxTimeoutMs: GENERATIVE_APP_RUN_MAX_TIMEOUT_MS,
|
|
589
|
+
state: options.state,
|
|
590
|
+
statePresent: options.state !== undefined,
|
|
591
|
+
},
|
|
555
592
|
},
|
|
556
|
-
|
|
593
|
+
);
|
|
557
594
|
let settled = false;
|
|
558
595
|
let terminationError: Error | undefined;
|
|
559
596
|
let terminationStarted = false;
|
|
@@ -594,22 +631,25 @@ async function executeGenerativeAppWorker(options: {
|
|
|
594
631
|
}, options.methodTimeoutMs);
|
|
595
632
|
timeout.unref?.();
|
|
596
633
|
options.execution?.signal.addEventListener("abort", abort, { once: true });
|
|
597
|
-
worker.on(
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
634
|
+
worker.on(
|
|
635
|
+
"message",
|
|
636
|
+
(message: {
|
|
637
|
+
error?: string;
|
|
638
|
+
ok?: boolean;
|
|
639
|
+
result?: unknown;
|
|
640
|
+
type?: string;
|
|
641
|
+
}) => {
|
|
642
|
+
if (terminationError) {
|
|
643
|
+
if (message?.type === "abort-ack") terminateAndFinish();
|
|
644
|
+
} else if (message?.ok === true) {
|
|
645
|
+
finish(undefined, message.result);
|
|
646
|
+
void worker.terminate();
|
|
647
|
+
} else {
|
|
648
|
+
finish(new Error(message?.error || "Generative App worker failed."));
|
|
649
|
+
void worker.terminate();
|
|
650
|
+
}
|
|
651
|
+
},
|
|
652
|
+
);
|
|
613
653
|
worker.once("error", (error) => finish(terminationError ?? error));
|
|
614
654
|
worker.once("exit", (code) => {
|
|
615
655
|
if (terminationError) finish(terminationError);
|
|
@@ -622,7 +662,10 @@ async function executeGenerativeAppWorker(options: {
|
|
|
622
662
|
|
|
623
663
|
const invocationQueues = new Map<string, Promise<unknown>>();
|
|
624
664
|
|
|
625
|
-
function serializeInvocation<T>(
|
|
665
|
+
function serializeInvocation<T>(
|
|
666
|
+
key: string,
|
|
667
|
+
operation: () => Promise<T>,
|
|
668
|
+
): Promise<T> {
|
|
626
669
|
const previous = invocationQueues.get(key) ?? Promise.resolve();
|
|
627
670
|
const current = previous.catch(() => undefined).then(operation);
|
|
628
671
|
invocationQueues.set(key, current);
|
|
@@ -697,7 +740,10 @@ async function invokeMethod(options: {
|
|
|
697
740
|
const encodedEnvelope = `${JSON.stringify(envelope)}\n`;
|
|
698
741
|
const encodedState = `${JSON.stringify(result.state, null, 2)}\n`;
|
|
699
742
|
if (options.method === "init") {
|
|
700
|
-
await writeFileAtomic(
|
|
743
|
+
await writeFileAtomic(
|
|
744
|
+
join(options.appDir, "states.jsonl"),
|
|
745
|
+
encodedEnvelope,
|
|
746
|
+
);
|
|
701
747
|
} else {
|
|
702
748
|
await appendFile(join(options.appDir, "states.jsonl"), encodedEnvelope, {
|
|
703
749
|
encoding: "utf8",
|
|
@@ -721,21 +767,27 @@ async function invokeMethod(options: {
|
|
|
721
767
|
};
|
|
722
768
|
}
|
|
723
769
|
|
|
724
|
-
export async function invokeGenerativeApp(
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
770
|
+
export async function invokeGenerativeApp(
|
|
771
|
+
options: GenerativeAppRuntimeOptions & {
|
|
772
|
+
argument?: unknown;
|
|
773
|
+
expectedGeneration?: string;
|
|
774
|
+
expectedRevision?: number;
|
|
775
|
+
method: string;
|
|
776
|
+
app: string;
|
|
777
|
+
},
|
|
778
|
+
): Promise<GenerativeAppInvocationResult> {
|
|
731
779
|
assertAppName(options.app);
|
|
732
780
|
assertMethod(options.method);
|
|
733
|
-
const argument =
|
|
734
|
-
|
|
735
|
-
|
|
781
|
+
const argument =
|
|
782
|
+
options.argument === undefined
|
|
783
|
+
? undefined
|
|
784
|
+
: assertJsonValue(options.argument, "Generative App argument");
|
|
736
785
|
await ensureManagedAppsRoot(options.agentDir, false);
|
|
737
786
|
const appDir = resolveGenerativeAppDir(options.agentDir, options.app);
|
|
738
|
-
const modulePath = resolveGenerativeAppModulePath(
|
|
787
|
+
const modulePath = resolveGenerativeAppModulePath(
|
|
788
|
+
options.agentDir,
|
|
789
|
+
options.app,
|
|
790
|
+
);
|
|
739
791
|
return await serializeInvocation(appDir, async () => {
|
|
740
792
|
const releaseLock = await acquireGenerativeAppTransitionLock(appDir);
|
|
741
793
|
try {
|
|
@@ -750,7 +802,8 @@ export async function invokeGenerativeApp(options: GenerativeAppRuntimeOptions &
|
|
|
750
802
|
? { expectedRevision: options.expectedRevision }
|
|
751
803
|
: {}),
|
|
752
804
|
method: options.method,
|
|
753
|
-
methodTimeoutMs:
|
|
805
|
+
methodTimeoutMs:
|
|
806
|
+
options.methodTimeoutMs ?? GENERATIVE_APP_METHOD_TIMEOUT_MS,
|
|
754
807
|
modulePath,
|
|
755
808
|
app: options.app,
|
|
756
809
|
});
|
|
@@ -760,12 +813,14 @@ export async function invokeGenerativeApp(options: GenerativeAppRuntimeOptions &
|
|
|
760
813
|
});
|
|
761
814
|
}
|
|
762
815
|
|
|
763
|
-
export async function installGenerativeApp(
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
816
|
+
export async function installGenerativeApp(
|
|
817
|
+
options: GenerativeAppRuntimeOptions & {
|
|
818
|
+
argument?: unknown;
|
|
819
|
+
app: string;
|
|
820
|
+
replace?: boolean;
|
|
821
|
+
script: string;
|
|
822
|
+
},
|
|
823
|
+
): Promise<GenerativeAppInvocationResult> {
|
|
769
824
|
assertAppName(options.app);
|
|
770
825
|
const sourcePath = resolve(options.script);
|
|
771
826
|
if (extname(sourcePath) !== ".mjs") {
|
|
@@ -776,10 +831,14 @@ export async function installGenerativeApp(options: GenerativeAppRuntimeOptions
|
|
|
776
831
|
}
|
|
777
832
|
const sourceMetadata = await lstat(sourcePath);
|
|
778
833
|
if (!sourceMetadata.isFile() || sourceMetadata.isSymbolicLink()) {
|
|
779
|
-
throw new Error(
|
|
834
|
+
throw new Error(
|
|
835
|
+
"Generative App source must be a regular non-symlink file.",
|
|
836
|
+
);
|
|
780
837
|
}
|
|
781
838
|
if (sourceMetadata.size > GENERATIVE_APP_MAX_MODULE_BYTES) {
|
|
782
|
-
throw new Error(
|
|
839
|
+
throw new Error(
|
|
840
|
+
`Generative App module exceeds ${GENERATIVE_APP_MAX_MODULE_BYTES} bytes.`,
|
|
841
|
+
);
|
|
783
842
|
}
|
|
784
843
|
const appsRoot = await ensureManagedAppsRoot(options.agentDir, true);
|
|
785
844
|
const appDir = resolveGenerativeAppDir(options.agentDir, options.app);
|
|
@@ -788,17 +847,23 @@ export async function installGenerativeApp(options: GenerativeAppRuntimeOptions
|
|
|
788
847
|
try {
|
|
789
848
|
const metadata = await lstat(appDir);
|
|
790
849
|
if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
|
|
791
|
-
throw new Error(
|
|
850
|
+
throw new Error(
|
|
851
|
+
`Generative App ${options.app} path is not a managed directory.`,
|
|
852
|
+
);
|
|
792
853
|
}
|
|
793
854
|
installed = true;
|
|
794
855
|
} catch (error) {
|
|
795
856
|
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
796
857
|
}
|
|
797
858
|
if (installed && options.replace !== true) {
|
|
798
|
-
throw new Error(
|
|
859
|
+
throw new Error(
|
|
860
|
+
`Generative App ${options.app} is already installed; set replace to true.`,
|
|
861
|
+
);
|
|
799
862
|
}
|
|
800
863
|
if (!installed && options.replace === true) {
|
|
801
|
-
throw new Error(
|
|
864
|
+
throw new Error(
|
|
865
|
+
`Generative App ${options.app} is not installed and cannot be replaced.`,
|
|
866
|
+
);
|
|
802
867
|
}
|
|
803
868
|
const releaseLock = installed
|
|
804
869
|
? await acquireGenerativeAppTransitionLock(appDir)
|
|
@@ -815,15 +880,17 @@ export async function installGenerativeApp(options: GenerativeAppRuntimeOptions
|
|
|
815
880
|
encoding: "utf8",
|
|
816
881
|
mode: 0o600,
|
|
817
882
|
});
|
|
818
|
-
const argument =
|
|
819
|
-
|
|
820
|
-
|
|
883
|
+
const argument =
|
|
884
|
+
options.argument === undefined
|
|
885
|
+
? undefined
|
|
886
|
+
: assertJsonValue(options.argument, "Generative App argument");
|
|
821
887
|
const result = await invokeMethod({
|
|
822
888
|
appDir: stagingDir,
|
|
823
889
|
...(argument !== undefined ? { argument } : {}),
|
|
824
890
|
...(options.execution ? { execution: options.execution } : {}),
|
|
825
891
|
method: "init",
|
|
826
|
-
methodTimeoutMs:
|
|
892
|
+
methodTimeoutMs:
|
|
893
|
+
options.methodTimeoutMs ?? GENERATIVE_APP_METHOD_TIMEOUT_MS,
|
|
827
894
|
modulePath: stagingModule,
|
|
828
895
|
app: options.app,
|
|
829
896
|
});
|
|
@@ -864,18 +931,22 @@ export function parseGenerativeAppBoundAction(
|
|
|
864
931
|
prompt: string,
|
|
865
932
|
): GenerativeAppBoundAction | undefined {
|
|
866
933
|
if (!prompt.includes("::")) return undefined;
|
|
867
|
-
const match =
|
|
868
|
-
|
|
869
|
-
|
|
934
|
+
const match =
|
|
935
|
+
/^([a-z][a-z0-9-]{0,31})::([a-z][a-z0-9_]{0,31})(?:\(([\s\S]+)\))?$/u.exec(
|
|
936
|
+
prompt,
|
|
937
|
+
);
|
|
870
938
|
if (!match) throw new Error("Malformed Generative App bound action.");
|
|
871
939
|
const [, app, method, encodedArgument] = match;
|
|
872
|
-
if (!app || !method)
|
|
940
|
+
if (!app || !method)
|
|
941
|
+
throw new Error("Malformed Generative App bound action.");
|
|
873
942
|
if (encodedArgument === undefined) return { method, app };
|
|
874
943
|
let argument: unknown;
|
|
875
944
|
try {
|
|
876
945
|
argument = JSON.parse(encodedArgument);
|
|
877
946
|
} catch {
|
|
878
|
-
throw new Error(
|
|
947
|
+
throw new Error(
|
|
948
|
+
"Generative App bound action argument must be strict JSON.",
|
|
949
|
+
);
|
|
879
950
|
}
|
|
880
951
|
return {
|
|
881
952
|
argument: assertJsonValue(argument, "Generative App bound action argument"),
|
|
@@ -884,37 +955,15 @@ export function parseGenerativeAppBoundAction(
|
|
|
884
955
|
};
|
|
885
956
|
}
|
|
886
957
|
|
|
887
|
-
export async function
|
|
958
|
+
export async function bindGenerativeApp(
|
|
888
959
|
options: GenerativeAppRuntimeOptions & {
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
960
|
+
argument?: unknown;
|
|
961
|
+
method?: string;
|
|
962
|
+
app: string;
|
|
963
|
+
replace?: boolean;
|
|
964
|
+
script?: string;
|
|
892
965
|
},
|
|
893
|
-
): Promise<GenerativeAppInvocationResult
|
|
894
|
-
const action = parseGenerativeAppBoundAction(options.prompt);
|
|
895
|
-
if (!action) return undefined;
|
|
896
|
-
return await invokeGenerativeApp({
|
|
897
|
-
agentDir: options.agentDir,
|
|
898
|
-
...(action.argument !== undefined ? { argument: action.argument } : {}),
|
|
899
|
-
...(options.expectedGeneration !== undefined
|
|
900
|
-
? { expectedGeneration: options.expectedGeneration }
|
|
901
|
-
: {}),
|
|
902
|
-
...(options.expectedRevision !== undefined
|
|
903
|
-
? { expectedRevision: options.expectedRevision }
|
|
904
|
-
: {}),
|
|
905
|
-
method: action.method,
|
|
906
|
-
methodTimeoutMs: options.methodTimeoutMs,
|
|
907
|
-
app: action.app,
|
|
908
|
-
});
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
export async function bindGenerativeApp(options: GenerativeAppRuntimeOptions & {
|
|
912
|
-
argument?: unknown;
|
|
913
|
-
method?: string;
|
|
914
|
-
app: string;
|
|
915
|
-
replace?: boolean;
|
|
916
|
-
script?: string;
|
|
917
|
-
}): Promise<GenerativeAppInvocationResult> {
|
|
966
|
+
): Promise<GenerativeAppInvocationResult> {
|
|
918
967
|
const hasScript = typeof options.script === "string";
|
|
919
968
|
const hasMethod = typeof options.method === "string";
|
|
920
969
|
if (hasScript === hasMethod) {
|
|
@@ -926,7 +975,9 @@ export async function bindGenerativeApp(options: GenerativeAppRuntimeOptions & {
|
|
|
926
975
|
return hasScript
|
|
927
976
|
? await installGenerativeApp({
|
|
928
977
|
agentDir: options.agentDir,
|
|
929
|
-
...(options.argument !== undefined
|
|
978
|
+
...(options.argument !== undefined
|
|
979
|
+
? { argument: options.argument }
|
|
980
|
+
: {}),
|
|
930
981
|
methodTimeoutMs: options.methodTimeoutMs,
|
|
931
982
|
app: options.app,
|
|
932
983
|
replace: options.replace,
|
|
@@ -934,7 +985,9 @@ export async function bindGenerativeApp(options: GenerativeAppRuntimeOptions & {
|
|
|
934
985
|
})
|
|
935
986
|
: await invokeGenerativeApp({
|
|
936
987
|
agentDir: options.agentDir,
|
|
937
|
-
...(options.argument !== undefined
|
|
988
|
+
...(options.argument !== undefined
|
|
989
|
+
? { argument: options.argument }
|
|
990
|
+
: {}),
|
|
938
991
|
method: options.method!,
|
|
939
992
|
methodTimeoutMs: options.methodTimeoutMs,
|
|
940
993
|
app: options.app,
|
|
@@ -956,10 +1009,12 @@ export function createGenerativeAppLiveSurfaceRuntime<THandle>(
|
|
|
956
1009
|
};
|
|
957
1010
|
}
|
|
958
1011
|
const records = new Map<string, Record>();
|
|
959
|
-
const setTimer: NonNullable<
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
1012
|
+
const setTimer: NonNullable<
|
|
1013
|
+
GenerativeAppLiveSurfaceRuntimeDeps<THandle>["setTimer"]
|
|
1014
|
+
> = deps.setTimer ?? ((callback, delayMs) => setTimeout(callback, delayMs));
|
|
1015
|
+
const clearTimer: NonNullable<
|
|
1016
|
+
GenerativeAppLiveSurfaceRuntimeDeps<THandle>["clearTimer"]
|
|
1017
|
+
> = deps.clearTimer ?? ((timer) => clearTimeout(timer));
|
|
963
1018
|
let active = true;
|
|
964
1019
|
|
|
965
1020
|
const clearRecordTimer = (record: Record): void => {
|
|
@@ -1029,7 +1084,9 @@ export function createGenerativeAppLiveSurfaceRuntime<THandle>(
|
|
|
1029
1084
|
record.surface.refreshAfterMs = result.refreshAfterMs;
|
|
1030
1085
|
schedule(record, result.refreshAfterMs);
|
|
1031
1086
|
} catch (error) {
|
|
1032
|
-
const classification = deps.classifyEditError?.(error) ?? {
|
|
1087
|
+
const classification = deps.classifyEditError?.(error) ?? {
|
|
1088
|
+
kind: "unknown" as const,
|
|
1089
|
+
};
|
|
1033
1090
|
if (!ownsRecord(key, record)) return;
|
|
1034
1091
|
deps.recordRuntimeEvent?.("generative-app", error, {
|
|
1035
1092
|
phase: "live-surface-refresh",
|
|
@@ -1040,9 +1097,13 @@ export function createGenerativeAppLiveSurfaceRuntime<THandle>(
|
|
|
1040
1097
|
cancel(key);
|
|
1041
1098
|
return;
|
|
1042
1099
|
}
|
|
1043
|
-
const delay =
|
|
1044
|
-
|
|
1045
|
-
|
|
1100
|
+
const delay =
|
|
1101
|
+
classification.retryAfterMs === undefined
|
|
1102
|
+
? record.retryDelayMs
|
|
1103
|
+
: Math.max(
|
|
1104
|
+
GENERATIVE_APP_MIN_REFRESH_AFTER_MS,
|
|
1105
|
+
classification.retryAfterMs,
|
|
1106
|
+
);
|
|
1046
1107
|
record.retryDelayMs = Math.min(60_000, Math.max(4_000, delay * 2));
|
|
1047
1108
|
schedule(record, delay);
|
|
1048
1109
|
} finally {
|
|
@@ -1068,10 +1129,15 @@ export function createGenerativeAppLiveSurfaceRuntime<THandle>(
|
|
|
1068
1129
|
async resume(surface, result) {
|
|
1069
1130
|
if (!active || !deps.isCurrent(surface)) return;
|
|
1070
1131
|
const frame = deps.plan(result, surface.handle);
|
|
1071
|
-
const handle =
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1132
|
+
const handle =
|
|
1133
|
+
frame.digest === surface.initialDigest
|
|
1134
|
+
? surface.handle
|
|
1135
|
+
: await deps.edit(frame);
|
|
1136
|
+
if (
|
|
1137
|
+
result.refreshAfterMs === undefined ||
|
|
1138
|
+
!deps.isCurrent({ ...surface, handle })
|
|
1139
|
+
)
|
|
1140
|
+
return;
|
|
1075
1141
|
open({
|
|
1076
1142
|
...surface,
|
|
1077
1143
|
appGeneration: result.generation,
|
|
@@ -1100,7 +1166,9 @@ export function formatDisplayedGenerativeAppToolOutput(): string {
|
|
|
1100
1166
|
|
|
1101
1167
|
export function formatGenerativeAppToolError(error: unknown): Error {
|
|
1102
1168
|
const message = error instanceof Error ? error.message : String(error);
|
|
1103
|
-
return new Error(
|
|
1169
|
+
return new Error(
|
|
1170
|
+
`\n${message.replace(/^\n+/u, "") || "Generative App operation failed."}`,
|
|
1171
|
+
);
|
|
1104
1172
|
}
|
|
1105
1173
|
|
|
1106
1174
|
// Provider tool APIs reject recursive $ref schemas (OpenAI) and raw TypeBox
|
|
@@ -1139,53 +1207,73 @@ export function registerTelegramBindTool(
|
|
|
1139
1207
|
const view = {
|
|
1140
1208
|
text: planned.markdown,
|
|
1141
1209
|
parseMode: "markdown" as const,
|
|
1142
|
-
...(planned.replyMarkup !== undefined
|
|
1210
|
+
...(planned.replyMarkup !== undefined
|
|
1211
|
+
? { replyMarkup: planned.replyMarkup }
|
|
1212
|
+
: {}),
|
|
1143
1213
|
};
|
|
1144
1214
|
return {
|
|
1145
1215
|
digest: createHash("sha256").update(JSON.stringify(view)).digest("hex"),
|
|
1146
1216
|
handle: { delivery: handle.delivery, view },
|
|
1147
1217
|
};
|
|
1148
1218
|
};
|
|
1149
|
-
const liveSurfaces =
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
return {
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1219
|
+
const liveSurfaces =
|
|
1220
|
+
deps.planOutput && deps.editView && deps.isDeliveryHandleCurrent
|
|
1221
|
+
? createGenerativeAppLiveSurfaceRuntime<TelegramBindLiveHandle>({
|
|
1222
|
+
agentDir: deps.agentDir,
|
|
1223
|
+
isCurrent: (surface) =>
|
|
1224
|
+
deps.isDeliveryHandleCurrent!(surface.handle.delivery),
|
|
1225
|
+
plan: planFrame,
|
|
1226
|
+
async edit(frame) {
|
|
1227
|
+
const view = frame.handle.view;
|
|
1228
|
+
if (!view)
|
|
1229
|
+
throw new Error(
|
|
1230
|
+
"Generative App live frame is missing its planned view.",
|
|
1231
|
+
);
|
|
1232
|
+
const edited = await deps.editView!(frame.handle.delivery, view);
|
|
1233
|
+
if (!edited.ok)
|
|
1234
|
+
throw Object.assign(new Error(edited.message), {
|
|
1235
|
+
deliveryFailureReason: edited.reason,
|
|
1236
|
+
...(edited.retryAfterMs === undefined
|
|
1237
|
+
? {}
|
|
1238
|
+
: { retryAfterMs: edited.retryAfterMs }),
|
|
1239
|
+
});
|
|
1240
|
+
return { delivery: edited.value, view };
|
|
1241
|
+
},
|
|
1242
|
+
classifyEditError(error) {
|
|
1243
|
+
const failure = error as {
|
|
1244
|
+
deliveryFailureReason?: string;
|
|
1245
|
+
retryAfterMs?: number;
|
|
1246
|
+
};
|
|
1247
|
+
if (failure.deliveryFailureReason === "rate-limited") {
|
|
1248
|
+
return { kind: "retry", retryAfterMs: failure.retryAfterMs };
|
|
1249
|
+
}
|
|
1250
|
+
if (failure.deliveryFailureReason === "transport-retryable") {
|
|
1251
|
+
return { kind: "retry" };
|
|
1252
|
+
}
|
|
1253
|
+
if (failure.deliveryFailureReason === "message-unavailable") {
|
|
1254
|
+
return { kind: "unavailable" };
|
|
1255
|
+
}
|
|
1256
|
+
return {
|
|
1257
|
+
kind:
|
|
1258
|
+
failure.deliveryFailureReason === "commit-unknown"
|
|
1259
|
+
? "unknown"
|
|
1260
|
+
: "terminal",
|
|
1261
|
+
};
|
|
1262
|
+
},
|
|
1263
|
+
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
1264
|
+
...(deps.liveSurfaceSetTimer
|
|
1265
|
+
? { setTimer: deps.liveSurfaceSetTimer }
|
|
1266
|
+
: {}),
|
|
1267
|
+
...(deps.liveSurfaceClearTimer
|
|
1268
|
+
? { clearTimer: deps.liveSurfaceClearTimer }
|
|
1269
|
+
: {}),
|
|
1270
|
+
})
|
|
1271
|
+
: undefined;
|
|
1187
1272
|
deps.setLiveSurfaceRuntime?.(liveSurfaces);
|
|
1188
|
-
const surfaceKey = (
|
|
1273
|
+
const surfaceKey = (
|
|
1274
|
+
app: string,
|
|
1275
|
+
handle: TelegramBindDeliveryHandle,
|
|
1276
|
+
): string =>
|
|
1189
1277
|
getTelegramBindLiveSurfaceKey(
|
|
1190
1278
|
app,
|
|
1191
1279
|
deps.getActiveProfileName?.() ?? "default",
|
|
@@ -1196,21 +1284,28 @@ export function registerTelegramBindTool(
|
|
|
1196
1284
|
label: "Telegram Bind",
|
|
1197
1285
|
description:
|
|
1198
1286
|
"Install, explicitly replace, or invoke one named method on a managed Generative App; successful output displays directly in the active Telegram turn unless display is false.",
|
|
1199
|
-
parameters: Type.Object(
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1287
|
+
parameters: Type.Object(
|
|
1288
|
+
{
|
|
1289
|
+
app: Type.String(),
|
|
1290
|
+
script: Type.Optional(Type.String()),
|
|
1291
|
+
method: Type.Optional(Type.String()),
|
|
1292
|
+
replace: Type.Optional(Type.Boolean()),
|
|
1293
|
+
display: Type.Optional(Type.Boolean()),
|
|
1294
|
+
argument: Type.Optional(
|
|
1295
|
+
createTelegramBindJsonArgumentSchema(
|
|
1296
|
+
TELEGRAM_BIND_JSON_ARGUMENT_MAX_DEPTH,
|
|
1297
|
+
),
|
|
1298
|
+
),
|
|
1299
|
+
},
|
|
1300
|
+
{ additionalProperties: false },
|
|
1301
|
+
),
|
|
1209
1302
|
async execute(_toolCallId, params) {
|
|
1210
1303
|
try {
|
|
1211
1304
|
const result = await bindGenerativeApp({
|
|
1212
1305
|
agentDir: deps.agentDir,
|
|
1213
|
-
...(params.argument !== undefined
|
|
1306
|
+
...(params.argument !== undefined
|
|
1307
|
+
? { argument: params.argument }
|
|
1308
|
+
: {}),
|
|
1214
1309
|
...("method" in params && typeof params.method === "string"
|
|
1215
1310
|
? { method: params.method }
|
|
1216
1311
|
: {}),
|
|
@@ -1223,10 +1318,13 @@ export function registerTelegramBindTool(
|
|
|
1223
1318
|
: {}),
|
|
1224
1319
|
methodTimeoutMs: deps.methodTimeoutMs,
|
|
1225
1320
|
});
|
|
1226
|
-
const activeTurn =
|
|
1227
|
-
? undefined
|
|
1228
|
-
|
|
1229
|
-
|
|
1321
|
+
const activeTurn =
|
|
1322
|
+
params.display === false ? undefined : deps.getActiveTurn?.();
|
|
1323
|
+
if (
|
|
1324
|
+
activeTurn &&
|
|
1325
|
+
deps.planOutput &&
|
|
1326
|
+
(deps.sendView || deps.sendMarkdownReply)
|
|
1327
|
+
) {
|
|
1230
1328
|
try {
|
|
1231
1329
|
const planned = deps.planOutput(result.output, {
|
|
1232
1330
|
binding: {
|
|
@@ -1284,7 +1382,12 @@ export function registerTelegramBindTool(
|
|
|
1284
1382
|
);
|
|
1285
1383
|
}
|
|
1286
1384
|
return {
|
|
1287
|
-
content: [
|
|
1385
|
+
content: [
|
|
1386
|
+
{
|
|
1387
|
+
type: "text",
|
|
1388
|
+
text: formatDisplayedGenerativeAppToolOutput(),
|
|
1389
|
+
},
|
|
1390
|
+
],
|
|
1288
1391
|
details: { ...result, displayed: true, messageId },
|
|
1289
1392
|
};
|
|
1290
1393
|
} catch (error) {
|
|
@@ -1294,13 +1397,23 @@ export function registerTelegramBindTool(
|
|
|
1294
1397
|
method: result.method,
|
|
1295
1398
|
});
|
|
1296
1399
|
return {
|
|
1297
|
-
content: [
|
|
1400
|
+
content: [
|
|
1401
|
+
{
|
|
1402
|
+
type: "text",
|
|
1403
|
+
text: formatGenerativeAppToolOutput(result.output),
|
|
1404
|
+
},
|
|
1405
|
+
],
|
|
1298
1406
|
details: { ...result, displayed: false, displayFailed: true },
|
|
1299
1407
|
};
|
|
1300
1408
|
}
|
|
1301
1409
|
}
|
|
1302
1410
|
return {
|
|
1303
|
-
content: [
|
|
1411
|
+
content: [
|
|
1412
|
+
{
|
|
1413
|
+
type: "text",
|
|
1414
|
+
text: formatGenerativeAppToolOutput(result.output),
|
|
1415
|
+
},
|
|
1416
|
+
],
|
|
1304
1417
|
details: { ...result, displayed: false },
|
|
1305
1418
|
};
|
|
1306
1419
|
} catch (error) {
|