@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/thread-naming.ts
CHANGED
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
* Excludes occupancy/slot allocation, display-mode projection, transport and API/store effects.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
areTelegramTargetsEqual as sameTarget,
|
|
10
|
+
type TelegramTarget,
|
|
11
|
+
} from "./target.ts";
|
|
9
12
|
|
|
10
13
|
export const TELEGRAM_THREAD_NAME_DIALOG_TTL_MS = 5 * 60_000;
|
|
11
14
|
|
|
@@ -29,10 +32,39 @@ function cloneCandidate(
|
|
|
29
32
|
return { ...candidate, target: { ...candidate.target } };
|
|
30
33
|
}
|
|
31
34
|
|
|
35
|
+
export interface TelegramThreadNameDialogLifetime {
|
|
36
|
+
/** Independent recipient effect authority; never the input handle's deadline or ownership. */
|
|
37
|
+
readonly assertAuthority?: () => void;
|
|
38
|
+
isCurrent(): boolean;
|
|
39
|
+
publish(
|
|
40
|
+
dialogMessageId: number,
|
|
41
|
+
assertPublicationCurrent?: () => void,
|
|
42
|
+
): TelegramThreadNameDialogCandidate | undefined;
|
|
43
|
+
select(action: TelegramThreadNameDialogAction): {
|
|
44
|
+
kind: "reset" | "cancel" | "expired";
|
|
45
|
+
};
|
|
46
|
+
consumeName(
|
|
47
|
+
text: string,
|
|
48
|
+
): { kind: "name"; name: string } | { kind: "none" | "empty" };
|
|
49
|
+
reopen(): TelegramThreadNameDialogCandidate | undefined;
|
|
50
|
+
finish(): void;
|
|
51
|
+
}
|
|
52
|
+
|
|
32
53
|
export function createTelegramThreadNameDialogRuntime(options?: {
|
|
33
54
|
ttlMs?: number;
|
|
34
55
|
nowMs?: () => number;
|
|
35
56
|
}): {
|
|
57
|
+
prepare(input: {
|
|
58
|
+
scope: string;
|
|
59
|
+
target: TelegramTarget;
|
|
60
|
+
isCurrent?: () => boolean;
|
|
61
|
+
assertAuthority?: () => void;
|
|
62
|
+
}): TelegramThreadNameDialogLifetime | undefined;
|
|
63
|
+
capture(input: {
|
|
64
|
+
scope: string;
|
|
65
|
+
target: TelegramTarget;
|
|
66
|
+
dialogMessageId: number;
|
|
67
|
+
}): TelegramThreadNameDialogLifetime | undefined;
|
|
36
68
|
open(input: {
|
|
37
69
|
scope: string;
|
|
38
70
|
target: TelegramTarget;
|
|
@@ -50,65 +82,231 @@ export function createTelegramThreadNameDialogRuntime(options?: {
|
|
|
50
82
|
text: string;
|
|
51
83
|
}): { kind: "name"; name: string } | { kind: "none" | "empty" };
|
|
52
84
|
clearScope(scope: string): void;
|
|
53
|
-
inspect(
|
|
85
|
+
inspect(
|
|
86
|
+
target: TelegramTarget,
|
|
87
|
+
): TelegramThreadNameDialogCandidate | undefined;
|
|
54
88
|
} {
|
|
55
89
|
const ttlMs = options?.ttlMs ?? TELEGRAM_THREAD_NAME_DIALOG_TTL_MS;
|
|
56
90
|
const nowMs = options?.nowMs ?? Date.now;
|
|
57
|
-
|
|
91
|
+
interface Entry {
|
|
92
|
+
scope: string;
|
|
93
|
+
target: TelegramTarget;
|
|
94
|
+
dialogMessageId?: number;
|
|
95
|
+
phase: "delivery" | "input" | "consumed";
|
|
96
|
+
expiresAtMs: number;
|
|
97
|
+
isCurrent?: () => boolean;
|
|
98
|
+
assertAuthority?: () => void;
|
|
99
|
+
consumedBy?: object;
|
|
100
|
+
}
|
|
101
|
+
const candidates = new Map<string, Entry>();
|
|
102
|
+
const recipientCurrent = (entry: Entry): boolean => {
|
|
103
|
+
try {
|
|
104
|
+
return entry.isCurrent ? entry.isCurrent() === true : true;
|
|
105
|
+
} catch {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
const view = (entry: Entry): TelegramThreadNameDialogCandidate =>
|
|
110
|
+
cloneCandidate({
|
|
111
|
+
scope: entry.scope,
|
|
112
|
+
target: entry.target,
|
|
113
|
+
dialogMessageId: entry.dialogMessageId!,
|
|
114
|
+
phase: "input",
|
|
115
|
+
expiresAtMs: entry.expiresAtMs,
|
|
116
|
+
});
|
|
117
|
+
const createLifetime = (entry: Entry): TelegramThreadNameDialogLifetime => {
|
|
118
|
+
const key = targetKey(entry.target);
|
|
119
|
+
const claim = {};
|
|
120
|
+
let ended = false;
|
|
121
|
+
const finish = () => {
|
|
122
|
+
ended = true;
|
|
123
|
+
if (
|
|
124
|
+
candidates.get(key) === entry &&
|
|
125
|
+
(!entry.consumedBy || entry.consumedBy === claim)
|
|
126
|
+
)
|
|
127
|
+
candidates.delete(key);
|
|
128
|
+
};
|
|
129
|
+
const isCurrent = () => {
|
|
130
|
+
if (
|
|
131
|
+
ended ||
|
|
132
|
+
candidates.get(key) !== entry ||
|
|
133
|
+
(entry.consumedBy && entry.consumedBy !== claim)
|
|
134
|
+
) {
|
|
135
|
+
ended = true;
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
const current = entry.expiresAtMs > nowMs() && recipientCurrent(entry);
|
|
139
|
+
// Supplied authority/clock observations may synchronously replace this dialog.
|
|
140
|
+
if (
|
|
141
|
+
!current ||
|
|
142
|
+
entry.expiresAtMs <= nowMs() ||
|
|
143
|
+
candidates.get(key) !== entry ||
|
|
144
|
+
(entry.consumedBy && entry.consumedBy !== claim)
|
|
145
|
+
) {
|
|
146
|
+
finish();
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
return true;
|
|
150
|
+
};
|
|
151
|
+
return {
|
|
152
|
+
assertAuthority: entry.assertAuthority,
|
|
153
|
+
isCurrent,
|
|
154
|
+
publish(dialogMessageId, assertPublicationCurrent) {
|
|
155
|
+
if (!isCurrent() || entry.phase !== "delivery") return undefined;
|
|
156
|
+
// Source authority is needed for this publication only, never retained by future input/effects.
|
|
157
|
+
if (assertPublicationCurrent) {
|
|
158
|
+
assertPublicationCurrent();
|
|
159
|
+
if (entry.expiresAtMs <= nowMs()) return undefined;
|
|
160
|
+
assertPublicationCurrent();
|
|
161
|
+
if (
|
|
162
|
+
ended ||
|
|
163
|
+
candidates.get(key) !== entry ||
|
|
164
|
+
entry.phase !== "delivery"
|
|
165
|
+
)
|
|
166
|
+
return undefined;
|
|
167
|
+
}
|
|
168
|
+
if (!Number.isSafeInteger(dialogMessageId) || dialogMessageId <= 0) {
|
|
169
|
+
finish();
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
entry.dialogMessageId = dialogMessageId;
|
|
173
|
+
entry.phase = "input";
|
|
174
|
+
return view(entry);
|
|
175
|
+
},
|
|
176
|
+
select(action) {
|
|
177
|
+
if (!isCurrent() || entry.phase !== "input") return { kind: "expired" };
|
|
178
|
+
if (action === "cancel") finish();
|
|
179
|
+
else {
|
|
180
|
+
entry.phase = "consumed";
|
|
181
|
+
entry.consumedBy = claim;
|
|
182
|
+
}
|
|
183
|
+
return { kind: action };
|
|
184
|
+
},
|
|
185
|
+
consumeName(text) {
|
|
186
|
+
if (!isCurrent() || entry.phase !== "input") return { kind: "none" };
|
|
187
|
+
const name = text.trim();
|
|
188
|
+
if (!name) return { kind: "empty" };
|
|
189
|
+
entry.phase = "consumed";
|
|
190
|
+
entry.consumedBy = claim;
|
|
191
|
+
return { kind: "name", name };
|
|
192
|
+
},
|
|
193
|
+
reopen() {
|
|
194
|
+
if (
|
|
195
|
+
!isCurrent() ||
|
|
196
|
+
entry.phase !== "consumed" ||
|
|
197
|
+
entry.consumedBy !== claim
|
|
198
|
+
) {
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
// A failed effect may restore input, never renew its deadline or issued handle.
|
|
202
|
+
const replacement: Entry = {
|
|
203
|
+
...entry,
|
|
204
|
+
phase: "input",
|
|
205
|
+
consumedBy: undefined,
|
|
206
|
+
};
|
|
207
|
+
candidates.set(key, replacement);
|
|
208
|
+
ended = true;
|
|
209
|
+
return view(replacement);
|
|
210
|
+
},
|
|
211
|
+
finish,
|
|
212
|
+
};
|
|
213
|
+
};
|
|
58
214
|
const current = (
|
|
59
215
|
scope: string,
|
|
60
216
|
target: TelegramTarget,
|
|
61
|
-
):
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
217
|
+
): Entry | undefined => {
|
|
218
|
+
const entry = candidates.get(targetKey(target));
|
|
219
|
+
if (
|
|
220
|
+
!entry ||
|
|
221
|
+
entry.scope !== scope ||
|
|
222
|
+
entry.phase !== "input" ||
|
|
223
|
+
!sameTarget(entry.target, target) ||
|
|
224
|
+
!createLifetime(entry).isCurrent()
|
|
225
|
+
)
|
|
68
226
|
return undefined;
|
|
69
|
-
|
|
70
|
-
|
|
227
|
+
return entry;
|
|
228
|
+
};
|
|
229
|
+
const capture = (input: {
|
|
230
|
+
scope: string;
|
|
231
|
+
target: TelegramTarget;
|
|
232
|
+
dialogMessageId: number;
|
|
233
|
+
}): TelegramThreadNameDialogLifetime | undefined => {
|
|
234
|
+
const { scope, dialogMessageId } = input;
|
|
235
|
+
const target = { ...input.target };
|
|
236
|
+
const entry = current(scope, target);
|
|
237
|
+
if (!entry || entry.dialogMessageId !== dialogMessageId) return undefined;
|
|
238
|
+
return createLifetime(entry);
|
|
71
239
|
};
|
|
72
240
|
return {
|
|
241
|
+
prepare(input) {
|
|
242
|
+
const scope = input.scope;
|
|
243
|
+
const target = { ...input.target };
|
|
244
|
+
const isCurrent = input.isCurrent,
|
|
245
|
+
assertAuthority = input.assertAuthority;
|
|
246
|
+
const key = targetKey(target);
|
|
247
|
+
const previous = candidates.get(key);
|
|
248
|
+
const entry: Entry = {
|
|
249
|
+
scope,
|
|
250
|
+
target,
|
|
251
|
+
phase: "delivery",
|
|
252
|
+
expiresAtMs: nowMs() + ttlMs,
|
|
253
|
+
isCurrent,
|
|
254
|
+
assertAuthority,
|
|
255
|
+
};
|
|
256
|
+
if (
|
|
257
|
+
!recipientCurrent(entry) ||
|
|
258
|
+
entry.expiresAtMs <= nowMs() ||
|
|
259
|
+
candidates.get(key) !== previous
|
|
260
|
+
)
|
|
261
|
+
return undefined;
|
|
262
|
+
candidates.set(key, entry);
|
|
263
|
+
return createLifetime(entry);
|
|
264
|
+
},
|
|
265
|
+
capture,
|
|
73
266
|
open(input) {
|
|
74
|
-
const
|
|
267
|
+
const entry: Entry = {
|
|
75
268
|
scope: input.scope,
|
|
76
269
|
target: { ...input.target },
|
|
77
|
-
dialogMessageId: input.dialogMessageId,
|
|
78
270
|
phase: "input",
|
|
271
|
+
dialogMessageId: input.dialogMessageId,
|
|
79
272
|
expiresAtMs: nowMs() + ttlMs,
|
|
80
273
|
};
|
|
81
|
-
candidates.set(targetKey(
|
|
82
|
-
return
|
|
274
|
+
candidates.set(targetKey(entry.target), entry);
|
|
275
|
+
return view(entry);
|
|
83
276
|
},
|
|
84
277
|
select(input) {
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return
|
|
278
|
+
const action = input.action;
|
|
279
|
+
const lifetime = capture(input);
|
|
280
|
+
if (!lifetime) return { kind: "expired" };
|
|
281
|
+
const result = lifetime.select(action);
|
|
282
|
+
lifetime.finish();
|
|
283
|
+
return result;
|
|
91
284
|
},
|
|
92
285
|
consumeName(input) {
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
286
|
+
const text = input.text;
|
|
287
|
+
const entry = current(input.scope, input.target);
|
|
288
|
+
if (!entry) return { kind: "none" };
|
|
289
|
+
const lifetime = createLifetime(entry);
|
|
290
|
+
const result = lifetime.consumeName(text);
|
|
291
|
+
if (result.kind === "name") lifetime.finish();
|
|
292
|
+
return result;
|
|
99
293
|
},
|
|
100
294
|
clearScope(scope) {
|
|
101
|
-
for (const [key,
|
|
102
|
-
if (
|
|
295
|
+
for (const [key, entry] of candidates) {
|
|
296
|
+
if (entry.scope === scope) candidates.delete(key);
|
|
103
297
|
}
|
|
104
298
|
},
|
|
105
299
|
inspect(target) {
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
300
|
+
const key = targetKey(target);
|
|
301
|
+
const entry = candidates.get(key);
|
|
302
|
+
if (!entry) return undefined;
|
|
303
|
+
if (entry.expiresAtMs <= nowMs()) {
|
|
304
|
+
if (candidates.get(key) === entry) candidates.delete(key);
|
|
109
305
|
return undefined;
|
|
110
306
|
}
|
|
111
|
-
|
|
307
|
+
if (entry.phase !== "input" || !createLifetime(entry).isCurrent())
|
|
308
|
+
return undefined;
|
|
309
|
+
return view(entry);
|
|
112
310
|
},
|
|
113
311
|
};
|
|
114
312
|
}
|
|
@@ -119,59 +317,6 @@ export interface TelegramThreadTitleInput {
|
|
|
119
317
|
threadName?: string;
|
|
120
318
|
}
|
|
121
319
|
|
|
122
|
-
export interface TelegramThreadNameInput {
|
|
123
|
-
seed: string;
|
|
124
|
-
cwd?: string;
|
|
125
|
-
role?: "leader" | "follower";
|
|
126
|
-
peers?: readonly string[];
|
|
127
|
-
slot?: string;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function hashString(value: string): number {
|
|
131
|
-
let hash = 2166136261;
|
|
132
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
133
|
-
hash ^= value.charCodeAt(index);
|
|
134
|
-
hash = Math.imul(hash, 16777619);
|
|
135
|
-
}
|
|
136
|
-
return hash >>> 0;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function getWorkspaceHint(cwd: string | undefined): string | undefined {
|
|
140
|
-
if (!cwd) return undefined;
|
|
141
|
-
const parts = cwd.split("/").filter(Boolean);
|
|
142
|
-
const last = parts.at(-1)?.trim();
|
|
143
|
-
if (!last) return undefined;
|
|
144
|
-
return (
|
|
145
|
-
last
|
|
146
|
-
.replace(/[^\p{L}\p{N}._-]+/gu, " ")
|
|
147
|
-
.trim()
|
|
148
|
-
.slice(0, 32) || undefined
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export function createTelegramThreadName(
|
|
153
|
-
input: TelegramThreadNameInput,
|
|
154
|
-
): string {
|
|
155
|
-
const workspace = getWorkspaceHint(input.cwd);
|
|
156
|
-
const roleMark =
|
|
157
|
-
input.role === "leader"
|
|
158
|
-
? "Leader"
|
|
159
|
-
: input.role === "follower"
|
|
160
|
-
? "Follower"
|
|
161
|
-
: undefined;
|
|
162
|
-
const slot = input.slot ? `Thread ${input.slot}` : undefined;
|
|
163
|
-
const peerSalt = input.peers?.slice().sort().join("|") ?? "";
|
|
164
|
-
const fallback = `Instance ${hashString(
|
|
165
|
-
`${input.seed}|${input.cwd ?? ""}|${input.role ?? ""}|${peerSalt}|${input.slot ?? ""}`,
|
|
166
|
-
)
|
|
167
|
-
.toString(36)
|
|
168
|
-
.slice(0, 4)}`;
|
|
169
|
-
return (
|
|
170
|
-
[slot, workspace, roleMark].filter(Boolean).join(" ").slice(0, 96) ||
|
|
171
|
-
fallback
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
320
|
export function normalizeTelegramTopicTargetThreadName(
|
|
176
321
|
threadName: string,
|
|
177
322
|
): string {
|