@llblab/pi-telegram 0.46.0 → 0.47.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.
Files changed (199) hide show
  1. package/BACKLOG.md +0 -1
  2. package/CHANGELOG.md +8 -0
  3. package/README.md +2 -2
  4. package/dist/api/activity.d.ts +6 -0
  5. package/dist/api/activity.js +6 -0
  6. package/dist/api/commands.d.ts +6 -0
  7. package/dist/api/commands.js +6 -0
  8. package/dist/api/delivery.d.ts +6 -0
  9. package/dist/api/delivery.js +6 -0
  10. package/dist/api/inbound.d.ts +6 -0
  11. package/dist/api/inbound.js +6 -0
  12. package/dist/api/keyboard.d.ts +6 -0
  13. package/dist/api/keyboard.js +6 -0
  14. package/dist/api/outbound.d.ts +6 -0
  15. package/dist/api/outbound.js +6 -0
  16. package/dist/api/sections.d.ts +7 -0
  17. package/dist/api/sections.js +6 -0
  18. package/dist/api/status.d.ts +6 -0
  19. package/dist/api/status.js +6 -0
  20. package/dist/api/updates.d.ts +6 -0
  21. package/dist/api/updates.js +6 -0
  22. package/dist/api/voice.d.ts +6 -0
  23. package/dist/api/voice.js +6 -0
  24. package/dist/index.d.ts +6 -0
  25. package/dist/index.js +6 -0
  26. package/dist/lib/activity-verbosity.d.ts +52 -0
  27. package/dist/lib/activity-verbosity.js +596 -0
  28. package/dist/lib/activity.d.ts +220 -0
  29. package/dist/lib/activity.js +574 -0
  30. package/dist/lib/agent-messages.d.ts +28 -0
  31. package/dist/lib/agent-messages.js +86 -0
  32. package/dist/lib/bindings.d.ts +250 -0
  33. package/dist/lib/bindings.js +936 -0
  34. package/dist/lib/bus-api.d.ts +18 -0
  35. package/dist/lib/bus-api.js +254 -0
  36. package/dist/lib/bus-follower.d.ts +464 -0
  37. package/dist/lib/bus-follower.js +1686 -0
  38. package/dist/lib/bus-leader.d.ts +292 -0
  39. package/dist/lib/bus-leader.js +2242 -0
  40. package/dist/lib/bus-transport.d.ts +64 -0
  41. package/dist/lib/bus-transport.js +140 -0
  42. package/dist/lib/bus.d.ts +518 -0
  43. package/dist/lib/bus.js +2055 -0
  44. package/dist/lib/channel-posts.d.ts +170 -0
  45. package/dist/lib/channel-posts.js +611 -0
  46. package/dist/lib/command-templates.d.ts +70 -0
  47. package/dist/lib/command-templates.js +744 -0
  48. package/dist/lib/commands.d.ts +541 -0
  49. package/dist/lib/commands.js +1155 -0
  50. package/dist/lib/config.d.ts +252 -0
  51. package/dist/lib/config.js +889 -0
  52. package/dist/lib/delivery.d.ts +163 -0
  53. package/dist/lib/delivery.js +542 -0
  54. package/dist/lib/extension.d.ts +7 -0
  55. package/dist/lib/extension.js +1608 -0
  56. package/dist/lib/generative-app-worker.mjs +104 -0
  57. package/dist/lib/generative-apps.d.ts +212 -0
  58. package/dist/lib/generative-apps.js +1006 -0
  59. package/dist/lib/inbound.d.ts +91 -0
  60. package/dist/lib/inbound.js +502 -0
  61. package/dist/lib/journal.d.ts +665 -0
  62. package/dist/lib/journal.js +3701 -0
  63. package/dist/lib/keyboard.d.ts +23 -0
  64. package/dist/lib/keyboard.js +37 -0
  65. package/dist/lib/lifecycle.d.ts +157 -0
  66. package/dist/lib/lifecycle.js +395 -0
  67. package/dist/lib/locks.d.ts +164 -0
  68. package/dist/lib/locks.js +1208 -0
  69. package/dist/lib/logging.d.ts +50 -0
  70. package/dist/lib/logging.js +274 -0
  71. package/dist/lib/media.d.ts +181 -0
  72. package/dist/lib/media.js +602 -0
  73. package/dist/lib/menu-model.d.ts +217 -0
  74. package/dist/lib/menu-model.js +663 -0
  75. package/dist/lib/menu-queue.d.ts +37 -0
  76. package/dist/lib/menu-queue.js +408 -0
  77. package/dist/lib/menu-settings.d.ts +116 -0
  78. package/dist/lib/menu-settings.js +595 -0
  79. package/dist/lib/menu-status.d.ts +32 -0
  80. package/dist/lib/menu-status.js +112 -0
  81. package/dist/lib/menu-thinking.d.ts +29 -0
  82. package/dist/lib/menu-thinking.js +82 -0
  83. package/dist/lib/menu.d.ts +171 -0
  84. package/dist/lib/menu.js +323 -0
  85. package/dist/lib/model.d.ts +127 -0
  86. package/dist/lib/model.js +409 -0
  87. package/dist/lib/outbound-attachments.d.ts +241 -0
  88. package/dist/lib/outbound-attachments.js +639 -0
  89. package/dist/lib/outbound-buttons.d.ts +69 -0
  90. package/dist/lib/outbound-buttons.js +248 -0
  91. package/dist/lib/outbound-markup.d.ts +42 -0
  92. package/dist/lib/outbound-markup.js +678 -0
  93. package/dist/lib/outbound-voice.d.ts +55 -0
  94. package/dist/lib/outbound-voice.js +152 -0
  95. package/dist/lib/outbound.d.ts +185 -0
  96. package/dist/lib/outbound.js +516 -0
  97. package/dist/lib/ownership.d.ts +77 -0
  98. package/dist/lib/ownership.js +174 -0
  99. package/dist/lib/paths.d.ts +40 -0
  100. package/dist/lib/paths.js +94 -0
  101. package/dist/lib/pi.d.ts +72 -0
  102. package/dist/lib/pi.js +121 -0
  103. package/dist/lib/polling.d.ts +351 -0
  104. package/dist/lib/polling.js +1196 -0
  105. package/dist/lib/preview.d.ts +192 -0
  106. package/dist/lib/preview.js +614 -0
  107. package/dist/lib/prompt-templates.d.ts +24 -0
  108. package/dist/lib/prompt-templates.js +118 -0
  109. package/dist/lib/prompts.d.ts +57 -0
  110. package/dist/lib/prompts.js +167 -0
  111. package/dist/lib/queue.d.ts +766 -0
  112. package/dist/lib/queue.js +1965 -0
  113. package/dist/lib/recovery.d.ts +86 -0
  114. package/dist/lib/recovery.js +285 -0
  115. package/dist/lib/rendering.d.ts +20 -0
  116. package/dist/lib/rendering.js +983 -0
  117. package/dist/lib/replies.d.ts +214 -0
  118. package/dist/lib/replies.js +737 -0
  119. package/dist/lib/routing.d.ts +207 -0
  120. package/dist/lib/routing.js +2282 -0
  121. package/dist/lib/runtime.d.ts +172 -0
  122. package/dist/lib/runtime.js +402 -0
  123. package/dist/lib/sections.d.ts +165 -0
  124. package/dist/lib/sections.js +327 -0
  125. package/dist/lib/setup.d.ts +82 -0
  126. package/dist/lib/setup.js +150 -0
  127. package/dist/lib/skills.d.ts +8 -0
  128. package/dist/lib/skills.js +12 -0
  129. package/dist/lib/status.d.ts +442 -0
  130. package/dist/lib/status.js +1006 -0
  131. package/dist/lib/sync.d.ts +179 -0
  132. package/dist/lib/sync.js +782 -0
  133. package/dist/lib/target.d.ts +20 -0
  134. package/dist/lib/target.js +27 -0
  135. package/dist/lib/telegram-api.d.ts +541 -0
  136. package/dist/lib/telegram-api.js +1159 -0
  137. package/dist/lib/text-groups.d.ts +89 -0
  138. package/dist/lib/text-groups.js +317 -0
  139. package/dist/lib/thread-cleanup-manager.d.ts +288 -0
  140. package/dist/lib/thread-cleanup-manager.js +560 -0
  141. package/dist/lib/thread-display.d.ts +46 -0
  142. package/dist/lib/thread-display.js +257 -0
  143. package/dist/lib/thread-naming.d.ts +46 -0
  144. package/dist/lib/thread-naming.js +78 -0
  145. package/dist/lib/thread-reconciler.d.ts +239 -0
  146. package/dist/lib/thread-reconciler.js +644 -0
  147. package/dist/lib/threads.d.ts +621 -0
  148. package/dist/lib/threads.js +3679 -0
  149. package/dist/lib/time-injection.d.ts +15 -0
  150. package/dist/lib/time-injection.js +56 -0
  151. package/dist/lib/turns.d.ts +109 -0
  152. package/dist/lib/turns.js +500 -0
  153. package/dist/lib/updates.d.ts +1290 -0
  154. package/dist/lib/updates.js +3634 -0
  155. package/dist/lib/voice.d.ts +117 -0
  156. package/dist/lib/voice.js +174 -0
  157. package/dist/lib/workspace-admission.d.ts +264 -0
  158. package/dist/lib/workspace-admission.js +1136 -0
  159. package/dist/lib/workspace-retirement.d.ts +219 -0
  160. package/dist/lib/workspace-retirement.js +587 -0
  161. package/dist/lib/workspace-slots.d.ts +30 -0
  162. package/dist/lib/workspace-slots.js +54 -0
  163. package/dist/package.json +126 -0
  164. package/dist/pi-telegram/index.js +1 -0
  165. package/dist/skills/generated-control-surface/SKILL.md +107 -0
  166. package/dist/skills/generated-control-surface/references/capability-adapters.md +27 -0
  167. package/dist/skills/generated-control-surface/references/layout-and-state.md +37 -0
  168. package/dist/skills/generative-apps/SKILL.md +115 -0
  169. package/dist/skills/show-me/SKILL.md +166 -0
  170. package/dist/skills/show-me/references/telegram-surfaces.md +43 -0
  171. package/dist/skills/telegram-bridge/SKILL.md +129 -0
  172. package/dist/skills/telegram-bridge/references/configuration.md +15 -0
  173. package/dist/skills/telegram-bridge/references/delivery-and-threads.md +27 -0
  174. package/dist/skills/telegram-bridge/references/diagnosis.md +18 -0
  175. package/docs/architecture.md +1 -1
  176. package/docs/compact-matrix-literal.md +10 -3
  177. package/docs/generative-apps.md +15 -13
  178. package/docs/multi-instance-bus.md +4 -2
  179. package/docs/outbound.md +1 -1
  180. package/docs/public-api.md +2 -2
  181. package/lib/bindings.ts +89 -6
  182. package/lib/bus-follower.ts +7 -3
  183. package/lib/bus-leader.ts +44 -19
  184. package/lib/bus.ts +4 -1
  185. package/lib/commands.ts +2 -2
  186. package/lib/config.ts +14 -7
  187. package/lib/delivery.ts +38 -6
  188. package/lib/extension.ts +19 -0
  189. package/lib/generative-apps.ts +379 -13
  190. package/lib/menu-settings.ts +21 -5
  191. package/lib/outbound-markup.ts +16 -7
  192. package/lib/telegram-api.ts +12 -1
  193. package/lib/thread-display.ts +96 -18
  194. package/package.json +56 -13
  195. package/scripts/build-dist.mjs +44 -0
  196. package/scripts/measure-bus.mjs +8 -1
  197. package/scripts/measure-workspace.mjs +8 -1
  198. package/skills/generative-apps/SKILL.md +1 -1
  199. package/skills/telegram-bridge/SKILL.md +1 -1
@@ -26,30 +26,88 @@ function boundedLabel(base: string, suffix = ""): string {
26
26
  return `${prefix}${suffix}`;
27
27
  }
28
28
 
29
- function directoryLabel(cwd: string, directories: readonly string[]): string {
30
- const parts = cwd.split("/").filter(Boolean);
31
- if (!parts.length) return "/";
29
+ function directorySegments(cwd: string): string[] {
30
+ return cwd.split("/").filter(Boolean);
31
+ }
32
+
33
+ function distinguishingDirectorySegments(cwd: string, directories: readonly string[]): string[] {
34
+ const parts = directorySegments(cwd);
35
+ if (!parts.length) return [];
32
36
  for (let depth = 1; depth <= parts.length; depth++) {
33
37
  const candidate = labelText(parts.slice(-depth).join("/"));
34
38
  const collides = directories.some((other) => other !== cwd &&
35
- labelText(other.split("/").filter(Boolean).slice(-depth).join("/")).toLowerCase() ===
39
+ labelText(directorySegments(other).slice(-depth).join("/")).toLowerCase() ===
36
40
  candidate.toLowerCase(),
37
41
  );
38
- if (!collides) return candidate;
42
+ if (!collides) return parts.slice(-depth);
39
43
  }
40
- return labelText(cwd);
44
+ return parts;
45
+ }
46
+
47
+ function directoryLabel(cwd: string, directories: readonly string[]): string {
48
+ const parts = distinguishingDirectorySegments(cwd, directories);
49
+ return parts.length ? labelText(parts.join("/")) : "/";
50
+ }
51
+
52
+ /** Pure directory tokenization shared by previews, initial titles, and reconciliation. */
53
+ export function tokenizeTelegramDirectorySegment(segment: string): string[] {
54
+ return segment
55
+ .replace(/([\p{Ll}\p{N}])(\p{Lu})/gu, "$1\u0000$2")
56
+ .replace(/(\p{Lu})(\p{Lu}\p{Ll})/gu, "$1\u0000$2")
57
+ .split(/[^\p{L}\p{N}]+|\u0000/gu)
58
+ .filter(Boolean);
59
+ }
60
+
61
+ function formatDirectoryLabel(
62
+ cwd: string,
63
+ directories: readonly string[],
64
+ mode: "directory-snake" | "directory-title",
65
+ ): string {
66
+ const segments = distinguishingDirectorySegments(cwd, directories);
67
+ if (!segments.length) return "/";
68
+ const formatted = segments.map((segment) => {
69
+ const tokens = tokenizeTelegramDirectorySegment(segment);
70
+ if (!tokens.length) return "";
71
+ if (mode === "directory-snake") return tokens.map((token) => token.toLowerCase()).join("_");
72
+ return tokens.map((token) => /\p{L}/u.test(token) && token === token.toUpperCase()
73
+ ? token
74
+ : `${token.slice(0, 1).toUpperCase()}${token.slice(1).toLowerCase()}`).join(" ");
75
+ }).filter(Boolean);
76
+ if (!formatted.length) return directoryLabel(cwd, directories);
77
+ return formatted.join(mode === "directory-snake" ? "_" : " / ");
78
+ }
79
+
80
+ /** Maps one leader-captured authenticated owner roster onto retained binding identities. */
81
+ export function resolveTelegramLiveWorkspaceBindingKeys(
82
+ bindings: readonly TelegramWorkspaceThreadBinding[],
83
+ leaderTarget: { chatId: number; threadId?: number } | undefined,
84
+ followers: readonly { target?: { chatId: number; threadId?: number } }[],
85
+ ): ReadonlySet<string> {
86
+ const targets = new Set<string>();
87
+ const add = (target: { chatId: number; threadId?: number } | undefined) => {
88
+ if (target && typeof target.threadId === "number") {
89
+ targets.add(`${target.chatId}:${target.threadId}`);
90
+ }
91
+ };
92
+ add(leaderTarget);
93
+ for (const follower of followers) add(follower.target);
94
+ return new Set(bindings.filter((binding) =>
95
+ targets.has(`${binding.target.chatId}:${binding.target.threadId}`),
96
+ ).map((binding) => binding.bindingKey));
41
97
  }
42
98
 
43
99
  /** Missing or ambiguous metadata yields no label rather than inventing identity. */
44
100
  export function resolveTelegramWorkspaceDisplayNames(
45
101
  bindings: readonly TelegramWorkspaceDisplayBinding[],
46
102
  mode: TelegramThreadDisplayMode,
103
+ liveBindingKeys: ReadonlySet<string> = new Set(),
47
104
  ): Map<string, string> {
48
105
  const labels = new Map<string, string>();
49
106
  const directories = Array.from(new Set(bindings.map((binding) => binding.cwd)));
50
- const directoryCounts = new Map<string, number>();
107
+ const liveDirectoryCounts = new Map<string, number>();
51
108
  for (const binding of bindings) {
52
- directoryCounts.set(binding.cwd, (directoryCounts.get(binding.cwd) ?? 0) + 1);
109
+ if (!liveBindingKeys.has(binding.bindingKey)) continue;
110
+ liveDirectoryCounts.set(binding.cwd, (liveDirectoryCounts.get(binding.cwd) ?? 0) + 1);
53
111
  }
54
112
  const bases = new Map<string, string>();
55
113
  for (const binding of bindings) {
@@ -65,18 +123,27 @@ export function resolveTelegramWorkspaceDisplayNames(
65
123
  const name = binding.threadName ? labelText(binding.threadName) : slot;
66
124
  if (name) labels.set(binding.bindingKey, boundedLabel(name));
67
125
  } else {
68
- const base = directoryLabel(binding.cwd, directories);
126
+ const base = mode === "directories"
127
+ ? directoryLabel(binding.cwd, directories)
128
+ : formatDirectoryLabel(binding.cwd, directories, mode);
69
129
  bases.set(binding.bindingKey, base);
70
- const showSuffix = binding.showSlotSuffix ||
71
- (directoryCounts.get(binding.cwd) ?? 0) > 1;
130
+ const showSuffix = mode === "directories"
131
+ ? binding.showSlotSuffix || bindings.filter((candidate) => candidate.cwd === binding.cwd).length > 1
132
+ : (liveDirectoryCounts.get(binding.cwd) ?? 0) > 1;
72
133
  if (showSuffix && !slot) continue;
73
- labels.set(binding.bindingKey, boundedLabel(base, showSuffix ? `_${slot!.toLowerCase()}` : ""));
134
+ const suffix = !showSuffix ? "" : mode === "directory-title"
135
+ ? ` · ${slot}`
136
+ : `_${slot!.toLowerCase()}`;
137
+ labels.set(binding.bindingKey, boundedLabel(base, suffix));
74
138
  }
75
139
  }
76
140
  // Long or whitespace-normalized paths can collide even after qualification.
77
- if (mode === "directories") {
141
+ if (mode === "directories" || mode === "directory-snake" || mode === "directory-title") {
78
142
  const counts = new Map<string, number>();
79
- for (const label of labels.values()) {
143
+ for (const binding of bindings) {
144
+ const label = labels.get(binding.bindingKey);
145
+ if (!label || ((mode === "directory-snake" || mode === "directory-title") &&
146
+ !liveBindingKeys.has(binding.bindingKey))) continue;
80
147
  const key = label.toLowerCase();
81
148
  counts.set(key, (counts.get(key) ?? 0) + 1);
82
149
  }
@@ -84,16 +151,24 @@ export function resolveTelegramWorkspaceDisplayNames(
84
151
  const label = labels.get(binding.bindingKey);
85
152
  if (binding.manualThreadName || !label ||
86
153
  (counts.get(label.toLowerCase()) ?? 0) < 2) continue;
154
+ if ((mode === "directory-snake" || mode === "directory-title") &&
155
+ (liveDirectoryCounts.get(binding.cwd) ?? 0) < 2) {
156
+ labels.delete(binding.bindingKey);
157
+ continue;
158
+ }
87
159
  if (!binding.slot || !/^[A-Z]$/u.test(binding.slot)) {
88
160
  labels.delete(binding.bindingKey);
89
161
  continue;
90
162
  }
91
163
  labels.set(binding.bindingKey, boundedLabel(bases.get(binding.bindingKey)!,
92
- `_${binding.slot.toLowerCase()}`));
164
+ mode === "directory-title" ? ` · ${binding.slot}` : `_${binding.slot.toLowerCase()}`));
93
165
  }
94
166
  }
95
167
  const counts = new Map<string, number>();
96
- for (const label of labels.values()) {
168
+ for (const binding of bindings) {
169
+ const label = labels.get(binding.bindingKey);
170
+ if (!label || ((mode === "directory-snake" || mode === "directory-title") &&
171
+ !liveBindingKeys.has(binding.bindingKey))) continue;
97
172
  const key = label.toLowerCase();
98
173
  counts.set(key, (counts.get(key) ?? 0) + 1);
99
174
  }
@@ -108,6 +183,7 @@ export function resolveTelegramInitialWorkspaceDisplayName(input: {
108
183
  binding: TelegramWorkspaceDisplayBinding;
109
184
  mode: TelegramThreadDisplayMode;
110
185
  preserveRetainedManualName?: boolean;
186
+ liveBindingKeys?: ReadonlySet<string>;
111
187
  }): string | undefined {
112
188
  const retained = input.bindings.find((binding) =>
113
189
  binding.bindingKey === input.binding.bindingKey,
@@ -126,7 +202,7 @@ export function resolveTelegramInitialWorkspaceDisplayName(input: {
126
202
  candidate.bindingKey !== binding.bindingKey,
127
203
  ),
128
204
  binding,
129
- ], input.mode).get(binding.bindingKey);
205
+ ], input.mode, new Set([...(input.liveBindingKeys ?? []), binding.bindingKey])).get(binding.bindingKey);
130
206
  }
131
207
 
132
208
  export async function applyTelegramThreadDisplaySetting(
@@ -155,6 +231,7 @@ export interface TelegramThreadDisplayReconcilerDeps {
155
231
  getProfileKey(): string;
156
232
  getLeaderEpoch(): string | number | undefined;
157
233
  captureBindingAuthority(binding: TelegramWorkspaceThreadBinding): (() => boolean) | undefined;
234
+ captureLiveBindingKeys(bindings: readonly TelegramWorkspaceThreadBinding[]): ReadonlySet<string>;
158
235
  callApi<TResponse>(
159
236
  method: string,
160
237
  body: Record<string, unknown>,
@@ -179,7 +256,8 @@ export function createTelegramThreadDisplayReconciler(
179
256
  };
180
257
  assertAuthority();
181
258
  const bindings = deps.store.listWorkspaceBindings();
182
- const titles = resolveTelegramWorkspaceDisplayNames(bindings, mode);
259
+ const liveBindingKeys = deps.captureLiveBindingKeys(bindings);
260
+ const titles = resolveTelegramWorkspaceDisplayNames(bindings, mode, liveBindingKeys);
183
261
  let changed = 0;
184
262
  for (const binding of bindings) {
185
263
  const isBindingCurrent = deps.captureBindingAuthority(binding);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-telegram",
3
- "version": "0.46.0",
3
+ "version": "0.47.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -30,15 +30,19 @@
30
30
  "test": "node --experimental-strip-types --test --test-concurrency=4 --test-reporter=dot tests/*.test.ts",
31
31
  "test:verbose": "node --experimental-strip-types --test --test-reporter=spec tests/*.test.ts",
32
32
  "typecheck": "tsc --noEmit",
33
+ "build": "node scripts/build-dist.mjs",
34
+ "prepack": "npm run build",
35
+ "check": "node -e \"await import('./dist/pi-telegram/index.js'); console.log('pi-telegram: extension import ok')\"",
33
36
  "audit": "npm audit --omit=peer",
34
37
  "audit:host": "npm audit",
35
38
  "pack:check": "npm pack --dry-run",
36
- "validate": "npm run typecheck && npm test && npm run audit && npm run pack:check"
39
+ "validate": "npm run build && npm run typecheck && npm test && npm run audit && npm run check && npm run pack:check"
37
40
  },
38
41
  "files": [
39
42
  "index.ts",
40
43
  "api/",
41
44
  "lib/",
45
+ "dist/",
42
46
  "README.md",
43
47
  "AGENTS.md",
44
48
  "BACKLOG.md",
@@ -49,23 +53,62 @@
49
53
  "screenshot.png"
50
54
  ],
51
55
  "exports": {
52
- ".": "./index.ts",
53
- "./inbound": "./api/inbound.ts",
54
- "./outbound": "./api/outbound.ts",
55
- "./delivery": "./api/delivery.ts",
56
- "./activity": "./api/activity.ts",
57
- "./updates": "./api/updates.ts",
58
- "./commands": "./api/commands.ts",
59
- "./sections": "./api/sections.ts",
60
- "./status": "./api/status.ts",
61
- "./voice": "./api/voice.ts",
62
- "./keyboard": "./api/keyboard.ts"
56
+ ".": {
57
+ "types": "./dist/index.d.ts",
58
+ "default": "./dist/index.js"
59
+ },
60
+ "./inbound": {
61
+ "types": "./dist/api/inbound.d.ts",
62
+ "default": "./dist/api/inbound.js"
63
+ },
64
+ "./outbound": {
65
+ "types": "./dist/api/outbound.d.ts",
66
+ "default": "./dist/api/outbound.js"
67
+ },
68
+ "./delivery": {
69
+ "types": "./dist/api/delivery.d.ts",
70
+ "default": "./dist/api/delivery.js"
71
+ },
72
+ "./activity": {
73
+ "types": "./dist/api/activity.d.ts",
74
+ "default": "./dist/api/activity.js"
75
+ },
76
+ "./updates": {
77
+ "types": "./dist/api/updates.d.ts",
78
+ "default": "./dist/api/updates.js"
79
+ },
80
+ "./commands": {
81
+ "types": "./dist/api/commands.d.ts",
82
+ "default": "./dist/api/commands.js"
83
+ },
84
+ "./sections": {
85
+ "types": "./dist/api/sections.d.ts",
86
+ "default": "./dist/api/sections.js"
87
+ },
88
+ "./status": {
89
+ "types": "./dist/api/status.d.ts",
90
+ "default": "./dist/api/status.js"
91
+ },
92
+ "./voice": {
93
+ "types": "./dist/api/voice.d.ts",
94
+ "default": "./dist/api/voice.js"
95
+ },
96
+ "./keyboard": {
97
+ "types": "./dist/api/keyboard.d.ts",
98
+ "default": "./dist/api/keyboard.js"
99
+ }
63
100
  },
64
101
  "pi": {
65
102
  "extensions": [
103
+ "./dist/pi-telegram/index.js"
104
+ ],
105
+ "sourceExtensions": [
66
106
  "./index.ts"
67
107
  ],
68
108
  "skills": [
109
+ "./dist/skills"
110
+ ],
111
+ "sourceSkills": [
69
112
  "./skills"
70
113
  ],
71
114
  "image": "https://raw.githubusercontent.com/llblab/pi-telegram/main/screenshot.png"
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Builds the distributive JavaScript, declarations, Pi entrypoint, and Skills.
5
+ * Usage: node scripts/build-dist.mjs
6
+ *
7
+ * The build replaces `dist`, compiles with `tsconfig.build.json`, then copies
8
+ * package-owned runtime assets. It does not publish, pack, or modify sources.
9
+ */
10
+
11
+ import { spawnSync } from "node:child_process";
12
+ import { cpSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
13
+ import { join } from "node:path";
14
+
15
+ function run(command, args) {
16
+ const result = spawnSync(command, args, { stdio: "inherit" });
17
+ if (result.error) throw result.error;
18
+ if (result.status !== 0) process.exit(result.status ?? 1);
19
+ }
20
+
21
+ rmSync("dist", { recursive: true, force: true });
22
+ mkdirSync("dist", { recursive: true });
23
+
24
+ run(process.execPath, [
25
+ join("node_modules", "typescript", "bin", "tsc"),
26
+ "-p",
27
+ "tsconfig.build.json",
28
+ ]);
29
+
30
+ mkdirSync(join("dist", "pi-telegram"), { recursive: true });
31
+ writeFileSync(
32
+ join("dist", "pi-telegram", "index.js"),
33
+ 'export { default } from "../index.js";\n',
34
+ "utf8",
35
+ );
36
+
37
+ cpSync("skills", join("dist", "skills"), { recursive: true });
38
+ cpSync("package.json", join("dist", "package.json"));
39
+ cpSync(
40
+ join("lib", "generative-app-worker.mjs"),
41
+ join("dist", "lib", "generative-app-worker.mjs"),
42
+ );
43
+
44
+ run(process.execPath, ["--check", join("dist", "pi-telegram", "index.js")]);
@@ -1,4 +1,11 @@
1
- // Counts isolated registry work without sockets, configured profiles, or Telegram calls.
1
+ /**
2
+ * Counts isolated synchronous bus-registry work at representative sizes.
3
+ * Usage: node --experimental-strip-types scripts/measure-bus.mjs
4
+ *
5
+ * This assertion-backed fixture instruments Map operations only. It does not
6
+ * open sockets, read configured profiles, call Telegram, or measure latency.
7
+ */
8
+
2
9
  import assert from "node:assert/strict";
3
10
  import { createTelegramBusFollowerRegistry, createTelegramBusProtocolIdentity } from "../lib/bus.ts";
4
11
 
@@ -1,4 +1,11 @@
1
- // Measures isolated Thread-store work; never reads configured profiles or calls Telegram.
1
+ /**
2
+ * Measures isolated Thread-store filesystem work at representative sizes.
3
+ * Usage: node --experimental-strip-types scripts/measure-workspace.mjs
4
+ *
5
+ * This assertion-backed fixture uses and removes its own temporary directory.
6
+ * It never reads configured profiles, calls Telegram, or measures latency.
7
+ */
8
+
2
9
  import assert from "node:assert/strict";
3
10
  import fs from "node:fs/promises";
4
11
  import { syncBuiltinESMExports } from "node:module";
@@ -97,7 +97,7 @@ ordinary prompt → model-mediated interpretation or explanation
97
97
  - Keep credentials and unrelated private state out of source, state, output, and diagnostics.
98
98
  - Route consequences requiring contextual judgment through an ordinary model prompt.
99
99
  - Fail closed on unavailable owners, stale actions, malformed state, absent methods, process failures, or uncertain effects.
100
- - Do not claim automatic refresh, removal, voice output, or other behavior still marked incomplete in the runtime document.
100
+ - Treat `refreshAfterMs` validation, output-only `refresh`, scheduling after a successful hinted explicit `telegram_bind` delivery, same-handle rescheduling after a current-surface bound action, Delivery edit-failure classification, known unavailable-message invalidation with bounded classified diagnostics, session/runtime live-surface cancellation, and exact direct classic/leader/authenticated-follower target retention as implemented. The Bot API has no ordinary private-message deletion update; `deleted_business_messages` is limited to connected business accounts. Do not claim removal, voice output, proactive deletion-update invalidation, or other behavior still marked incomplete in the runtime document.
101
101
 
102
102
  ## Validation
103
103
 
@@ -60,7 +60,7 @@ Every enabled button has a self-contained prompt and an optional selection style
60
60
  - `{label|prompt}` separates visible label from queued prompt.
61
61
  - `{label|prompt|selected_style}` and `{|prompt|selected_style}` accept `primary`, `success`, or `danger`.
62
62
  - Fourth-position `1`/`true` disables, `0`/`false` enables; omission means enabled. JSON uses boolean `disabled`. `{|Next||1}` omits label/style; `{Next|||1}` omits prompt/style; `{|||1}` is blank (Telegram receives a non-breaking space). Prefer meaningful labels and retain a useful enabled action. Enabled CML requires a prompt. Disabled controls stay visible but have no callback, queued prompt, or bound-method invocation.
63
- - Top-level cells form vertical rows; one nested row groups horizontal peers.
63
+ - Top-level cells form vertical rows; inside a fenced block, adjacent top-level JSON/CML objects may omit the outer array and commas. One nested row groups horizontal peers.
64
64
  - Prefer one matrix per related group. Fenced blocks stay in place in Rich mode; HTML compatibility moves them to the footer. Native rows allow eight buttons. Malformed/oversized/unclosed blocks activate nothing; drafts hide them. Outer code fences and quoted/indented examples remain literal.
65
65
  - Both placements share prompt/app routing. In-body clicks acknowledge without recoloring; selected-style highlighting remains footer-only.
66
66