pi-harness-runtime 1.1.427 → 1.1.429

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 (156) hide show
  1. package/AGENTS.md +2 -2
  2. package/RULES.md +2 -3
  3. package/package.json +1 -1
  4. package/packages/a2a-adapter/package.json +1 -1
  5. package/packages/agent-policy/package.json +1 -1
  6. package/packages/architecture-generator/package.json +1 -1
  7. package/packages/auth/package.json +1 -1
  8. package/packages/autonomous-refactor/package.json +1 -1
  9. package/packages/autonomous-runtime/package.json +1 -1
  10. package/packages/cache-strategy/package.json +1 -1
  11. package/packages/capability-registry/package.json +1 -1
  12. package/packages/checkpoint/package.json +1 -1
  13. package/packages/cli-plugin-sdk/package.json +1 -1
  14. package/packages/clipboard/package.json +1 -1
  15. package/packages/clipboard-plugin/dist/index.js +1 -1
  16. package/packages/clipboard-plugin/dist/index.js.map +1 -1
  17. package/packages/clipboard-plugin/package.json +1 -2
  18. package/packages/clipboard-plugin/src/index.ts +1 -1
  19. package/packages/code-generation/package.json +1 -1
  20. package/packages/code-review/package.json +1 -1
  21. package/packages/codex-adapter/package.json +1 -1
  22. package/packages/codex-watcher/package.json +1 -1
  23. package/packages/config-capture/package.json +1 -1
  24. package/packages/connection-runtime/package.json +1 -1
  25. package/packages/context-compiler/package.json +1 -1
  26. package/packages/context-discovery/package.json +1 -1
  27. package/packages/context-manager/package.json +1 -1
  28. package/packages/contracts/package.json +1 -1
  29. package/packages/cookie-sanitizer/package.json +1 -1
  30. package/packages/cost-optimizer/package.json +1 -1
  31. package/packages/dependency-analyzer/package.json +1 -1
  32. package/packages/django-plugin/package.json +1 -1
  33. package/packages/doc-generator/package.json +1 -1
  34. package/packages/durable-review/package.json +1 -1
  35. package/packages/e2e-tester/package.json +1 -1
  36. package/packages/evaluation-engine/package.json +1 -1
  37. package/packages/evaluation-runner/package.json +1 -1
  38. package/packages/event-bus/package.json +1 -1
  39. package/packages/event-sink/package.json +1 -1
  40. package/packages/event-store/package.json +1 -1
  41. package/packages/experience-replay/package.json +1 -1
  42. package/packages/feedback-collector/package.json +1 -1
  43. package/packages/file-copy-helper/package.json +1 -1
  44. package/packages/framework-detector/package.json +1 -1
  45. package/packages/framework-plugin-sdk/package.json +1 -1
  46. package/packages/frappe-plugin/package.json +1 -1
  47. package/packages/generic-web-plugin/package.json +1 -1
  48. package/packages/health-monitor/package.json +1 -1
  49. package/packages/intent-analyzer/package.json +1 -1
  50. package/packages/jev-judge/package.json +1 -1
  51. package/packages/knowledge-graph/package.json +1 -1
  52. package/packages/knowledge-retrieval/package.json +1 -1
  53. package/packages/laravel-plugin/package.json +1 -1
  54. package/packages/learning-engine/package.json +1 -1
  55. package/packages/logging/package.json +1 -1
  56. package/packages/mcp-adapter/package.json +1 -1
  57. package/packages/memory-engine/package.json +1 -1
  58. package/packages/milestone-manager/package.json +1 -1
  59. package/packages/model-registry/package.json +1 -1
  60. package/packages/nextjs-plugin/package.json +1 -1
  61. package/packages/notification/package.json +1 -1
  62. package/packages/observability/package.json +1 -1
  63. package/packages/okf-indexer/package.json +1 -1
  64. package/packages/paperclip-integration/package.json +1 -1
  65. package/packages/performance-optimizer/package.json +1 -1
  66. package/packages/permission-mode/package.json +1 -1
  67. package/packages/privilege-broker/package.json +1 -1
  68. package/packages/project-analyzer/package.json +1 -1
  69. package/packages/project-bootstrap/package.json +1 -1
  70. package/packages/projection-engine/package.json +1 -1
  71. package/packages/prompt-compiler/package.json +1 -1
  72. package/packages/prompt-versioning/package.json +1 -1
  73. package/packages/provider-adapter-sdk/package.json +1 -1
  74. package/packages/provider-router/package.json +1 -1
  75. package/packages/provider-selector/package.json +1 -1
  76. package/packages/providers/package.json +1 -1
  77. package/packages/qdrant-skills/package.json +1 -1
  78. package/packages/quota-manager/package.json +1 -1
  79. package/packages/rate-limiter/package.json +1 -1
  80. package/packages/react-vite-plugin/package.json +1 -1
  81. package/packages/relay/package.json +1 -1
  82. package/packages/release-manager/package.json +1 -1
  83. package/packages/requirement-compiler/package.json +1 -1
  84. package/packages/runtime/package.json +1 -1
  85. package/packages/scheduler/package.json +1 -1
  86. package/packages/scheduler-adapter/package.json +1 -1
  87. package/packages/session/package.json +1 -1
  88. package/packages/session-api/package.json +1 -1
  89. package/packages/session-export/package.json +1 -1
  90. package/packages/shared-context/package.json +1 -1
  91. package/packages/skill-mcp-client/package.json +1 -1
  92. package/packages/skill-registry/package.json +1 -1
  93. package/packages/skills/package.json +1 -1
  94. package/packages/sprint-planner/package.json +1 -1
  95. package/packages/subscription-engine/package.json +1 -1
  96. package/packages/task-compiler/package.json +1 -1
  97. package/packages/tencentdb-memory/package.json +1 -1
  98. package/packages/tencentdb-sync/package.json +1 -1
  99. package/packages/test-data-generator/package.json +1 -1
  100. package/packages/test-generator/package.json +1 -1
  101. package/packages/todo-bd-sync/package.json +1 -1
  102. package/packages/token-estimation/package.json +1 -1
  103. package/packages/token-optimizer/package.json +1 -1
  104. package/packages/tooling-preference/package.json +1 -1
  105. package/packages/trajectory/package.json +1 -1
  106. package/packages/tui/package.json +1 -1
  107. package/packages/two-phase-review/package.json +1 -1
  108. package/packages/types/package.json +1 -1
  109. package/packages/voice-api/package.json +1 -1
  110. package/packages/webmcp-tester/package.json +1 -1
  111. package/packages/wiki/package.json +1 -1
  112. package/packages/workflow-events/package.json +1 -1
  113. package/packages/workspace-scanner/package.json +1 -1
  114. package/packages/worktree/package.json +1 -1
  115. package/packages/write-review/package.json +1 -1
  116. package/scripts/lib/runtime-imports.ts +263 -0
  117. package/scripts/verify-package-contents.ts +112 -18
  118. package/packages/telegram/package.json +0 -22
  119. package/packages/telegram/src/agent-messages.d.ts +0 -28
  120. package/packages/telegram/src/agent-messages.js +0 -90
  121. package/packages/telegram/src/bindings.d.ts +0 -296
  122. package/packages/telegram/src/bindings.js +0 -1086
  123. package/packages/telegram/src/bus.d.ts +0 -759
  124. package/packages/telegram/src/bus.js +0 -2732
  125. package/packages/telegram/src/commands.d.ts +0 -761
  126. package/packages/telegram/src/commands.js +0 -2622
  127. package/packages/telegram/src/config.d.ts +0 -222
  128. package/packages/telegram/src/config.js +0 -844
  129. package/packages/telegram/src/delivery.d.ts +0 -161
  130. package/packages/telegram/src/delivery.js +0 -550
  131. package/packages/telegram/src/extension.d.ts +0 -7
  132. package/packages/telegram/src/extension.js +0 -2444
  133. package/packages/telegram/src/inbound.d.ts +0 -94
  134. package/packages/telegram/src/inbound.js +0 -478
  135. package/packages/telegram/src/index.d.ts +0 -6
  136. package/packages/telegram/src/index.js +0 -6
  137. package/packages/telegram/src/keyboard.d.ts +0 -21
  138. package/packages/telegram/src/keyboard.js +0 -37
  139. package/packages/telegram/src/lifecycle.d.ts +0 -210
  140. package/packages/telegram/src/lifecycle.js +0 -538
  141. package/packages/telegram/src/pi.d.ts +0 -76
  142. package/packages/telegram/src/pi.js +0 -130
  143. package/packages/telegram/src/polling.d.ts +0 -332
  144. package/packages/telegram/src/polling.js +0 -1219
  145. package/packages/telegram/src/replies.d.ts +0 -210
  146. package/packages/telegram/src/replies.js +0 -752
  147. package/packages/telegram/src/routing.d.ts +0 -388
  148. package/packages/telegram/src/routing.js +0 -7886
  149. package/packages/telegram/src/runtime.d.ts +0 -159
  150. package/packages/telegram/src/runtime.js +0 -351
  151. package/packages/telegram/src/telegram-api.d.ts +0 -564
  152. package/packages/telegram/src/telegram-api.js +0 -1332
  153. package/packages/telegram/src/turns.d.ts +0 -108
  154. package/packages/telegram/src/turns.js +0 -482
  155. package/packages/telegram/src/updates.d.ts +0 -1545
  156. package/packages/telegram/src/updates.js +0 -6420
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Resolve the relative imports of a published file.
3
+ *
4
+ * The published package is a single app: every internal package lives in
5
+ * `packages/<name>/` and the runtime reaches it through relative paths. A
6
+ * relative import that does not resolve is a broken module graph, and a
7
+ * consumer finds out when the import runs - long after the release.
8
+ *
9
+ * The check that used to guard this only followed specifiers that contained
10
+ * "packages/", so an import inside a package (`./locks.js`) was never
11
+ * looked at, and the compiled output was never scanned at all. That gap
12
+ * let a package whose entry point could not be imported ship.
13
+ *
14
+ * The rules:
15
+ *
16
+ * 1. A TYPE-only import is erased at runtime, so it does not need a
17
+ * runtime target. It is skipped.
18
+ * 2. Comments are removed before the scan, so a commented-out import is
19
+ * not a broken import. The same holds for text inside a string or a
20
+ * template literal: documentation that shows an import is not an
21
+ * import.
22
+ * 3. A relative import resolves when the target file exists:
23
+ * - the specifier as written (`./x.js` -> `x.js`),
24
+ * - the TypeScript source of a `.js` specifier (`./x.js` -> `x.ts`),
25
+ * because the compiler and the Bun runtime map it, or
26
+ * - a directory entry point (`./x` -> `x/index.js`).
27
+ * 4. A compiled file (`.js`) must reach a file that is PUBLISHED. A
28
+ * TypeScript source (`.ts`) is resolved at compile time, so it may
29
+ * reach a source file in the repository.
30
+ * 5. A bare import that names a WORKSPACE package cannot resolve: the
31
+ * workspace packages ship inside this one tarball and are never
32
+ * installed as separate packages, so the name does not exist at
33
+ * runtime. Such an import must point at the built artifact instead.
34
+ */
35
+
36
+ import { existsSync, statSync } from "node:fs";
37
+ import { dirname, resolve } from "node:path";
38
+
39
+ /** The quoting state of the scanner. */
40
+ type Quote = "" | '"' | "'" | "`";
41
+
42
+ /** A source with its comments removed and its string content marked. */
43
+ export interface ScannedSource {
44
+ /** The source without its comments. */
45
+ code: string;
46
+ /**
47
+ * Whether the character at the given index of {@link code} is code.
48
+ *
49
+ * The content of a string or a template literal is not code, so an
50
+ * import that only appears inside one is documentation, not an import.
51
+ */
52
+ isCodeAt: (index: number) => boolean;
53
+ }
54
+
55
+ /**
56
+ * Remove the comments from a source file and mark its string content.
57
+ *
58
+ * A string or a template literal may contain something that looks like a
59
+ * comment, so the scanner tracks the quoting state instead of using a
60
+ * regular expression.
61
+ */
62
+ export function scanSource(source: string): ScannedSource {
63
+ let code = "";
64
+ const isCode: boolean[] = [];
65
+ let i = 0;
66
+ let quote: Quote = "";
67
+
68
+ const emit = (char: string, isCodeChar: boolean) => {
69
+ code += char;
70
+ isCode.push(isCodeChar);
71
+ };
72
+
73
+ while (i < source.length) {
74
+ const char = source[i];
75
+ const next = source[i + 1];
76
+
77
+ if (quote) {
78
+ // The content of a literal is not code. The quotes are.
79
+ emit(char, char === quote && source[i - 1] !== "\\");
80
+ if (char === "\\") {
81
+ // An escaped character cannot end the literal.
82
+ emit(next ?? "", false);
83
+ i += 2;
84
+ continue;
85
+ }
86
+ if (char === quote) quote = "";
87
+ i += 1;
88
+ continue;
89
+ }
90
+
91
+ if (char === '"' || char === "'" || char === "`") {
92
+ quote = char;
93
+ emit(char, true);
94
+ i += 1;
95
+ continue;
96
+ }
97
+
98
+ if (char === "/" && next === "/") {
99
+ // A line comment runs to the end of the line.
100
+ while (i < source.length && source[i] !== "\n") i += 1;
101
+ continue;
102
+ }
103
+
104
+ if (char === "/" && next === "*") {
105
+ // A block comment runs to its closing marker.
106
+ const end = source.indexOf("*/", i + 2);
107
+ if (end === -1) {
108
+ i = source.length;
109
+ break;
110
+ }
111
+ // Keep the newlines, so a line number stays meaningful.
112
+ for (let k = i; k < end; k += 1) {
113
+ if (source[k] === "\n") emit("\n", false);
114
+ }
115
+ i = end + 2;
116
+ continue;
117
+ }
118
+
119
+ emit(char, true);
120
+ i += 1;
121
+ }
122
+
123
+ return { code, isCodeAt: (index: number) => isCode[index] === true };
124
+ }
125
+
126
+ /**
127
+ * Remove the comments from a source file.
128
+ */
129
+ export function stripComments(source: string): string {
130
+ return scanSource(source).code;
131
+ }
132
+
133
+ /** The import forms that carry a specifier. */
134
+ const IMPORT_PATTERNS: RegExp[] = [
135
+ // import ... from "..." / export ... from "..."
136
+ /(?:^|[\n;])[ \t]*(import|export)\s+(type\s+)?[^;\n]*?from\s*["']([^"']+)["']/g,
137
+ // import "..." (side effect)
138
+ /(?:^|[\n;])[ \t]*import\s*["']([^"']+)["']/g,
139
+ // import("...") (dynamic)
140
+ /import\s*\(\s*["']([^"']+)["']\s*\)/g,
141
+ ];
142
+
143
+ /**
144
+ * Every VALUE import of a source file.
145
+ *
146
+ * A type-only import is skipped: `import type { X } from "..."` is erased
147
+ * by the compiler and never reaches the runtime. An import that only
148
+ * appears inside a string or a template literal is skipped as well: that
149
+ * is documentation, not an import.
150
+ */
151
+ export function extractValueImports(source: string): string[] {
152
+ const { code, isCodeAt } = scanSource(source);
153
+ const specifiers: string[] = [];
154
+
155
+ for (const pattern of IMPORT_PATTERNS) {
156
+ for (const match of code.matchAll(pattern)) {
157
+ // The match must start in code, not inside a literal.
158
+ if (match.index === undefined || !isCodeAt(match.index)) continue;
159
+ // `type` marks an import that the compiler erases.
160
+ if (match[2] && match[2].trim() === "type") continue;
161
+ const specifier = match[3] ?? match[1];
162
+ if (!specifier) continue;
163
+ specifiers.push(specifier);
164
+ }
165
+ }
166
+
167
+ return [...new Set(specifiers)];
168
+ }
169
+
170
+ /**
171
+ * Every RELATIVE value import of a source file.
172
+ */
173
+ export function extractRelativeValueImports(source: string): string[] {
174
+ return extractValueImports(source).filter((specifier) => specifier.startsWith("."));
175
+ }
176
+
177
+ /**
178
+ * Every BARE value import of a source file.
179
+ *
180
+ * A bare import names a package, not a file in this repository.
181
+ */
182
+ export function extractBareImports(source: string): string[] {
183
+ return extractValueImports(source).filter((specifier) => !specifier.startsWith("."));
184
+ }
185
+
186
+ /**
187
+ * The package a specifier names.
188
+ *
189
+ * `@scope/name/sub/path` and `name/sub/path` both name `@scope/name` and
190
+ * `name`.
191
+ */
192
+ export function packageNameOf(specifier: string): string {
193
+ if (specifier.startsWith("@")) return specifier.split("/").slice(0, 2).join("/");
194
+ return specifier.split("/")[0];
195
+ }
196
+
197
+ /**
198
+ * The files a relative specifier may point at.
199
+ *
200
+ * The candidates are absolute paths, most specific first.
201
+ */
202
+ export function importTargetCandidates(
203
+ importer: string,
204
+ specifier: string,
205
+ ): string[] {
206
+ const base = resolve(dirname(importer), specifier);
207
+ const candidates = [base];
208
+ if (base.endsWith(".js")) candidates.push(`${base.slice(0, -3)}.ts`);
209
+ candidates.push(`${base}.js`, `${base}.ts`);
210
+ candidates.push(`${base}/index.js`, `${base}/index.ts`);
211
+ return [...new Set(candidates)];
212
+ }
213
+
214
+ /** The result of resolving one relative import. */
215
+ export interface ResolvedImport {
216
+ ok: boolean;
217
+ /** The file the import points at, when one was found. */
218
+ target?: string;
219
+ /** Why the import does not resolve. */
220
+ reason?: string;
221
+ }
222
+
223
+ /** The options of {@link resolveRelativeImport}. */
224
+ export interface ResolveOptions {
225
+ /** Absolute path of the importing file. */
226
+ importer: string;
227
+ /** The relative specifier. */
228
+ specifier: string;
229
+ /** Existence test, overridable for a test. */
230
+ exists?: (path: string) => boolean;
231
+ /** Publication test, overridable for a test. */
232
+ isPublished?: (path: string) => boolean;
233
+ }
234
+
235
+ /**
236
+ * Resolve one relative import.
237
+ */
238
+ export function resolveRelativeImport({
239
+ importer,
240
+ specifier,
241
+ exists = existsSync,
242
+ isPublished = () => true,
243
+ }: ResolveOptions): ResolvedImport {
244
+ const candidates = importTargetCandidates(importer, specifier);
245
+ const found = candidates.find((candidate) => {
246
+ try {
247
+ return exists(candidate) && statSync(candidate).isFile();
248
+ } catch {
249
+ return false;
250
+ }
251
+ });
252
+ if (!found) {
253
+ return { ok: false, reason: "the target does not exist" };
254
+ }
255
+ // A compiled file has to reach a published file: a consumer never sees
256
+ // the repository. A TypeScript source is resolved by the compiler, so it
257
+ // may point at a source file in the repository.
258
+ const isCompiled = /\.(js|mjs|cjs)$/.test(importer);
259
+ if (isCompiled && !isPublished(found)) {
260
+ return { ok: false, target: found, reason: "the target is not published" };
261
+ }
262
+ return { ok: true, target: found };
263
+ }
@@ -30,6 +30,12 @@ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
30
30
  import { execFile, spawnSync } from "node:child_process";
31
31
  import { join, resolve, dirname, relative } from "node:path";
32
32
  import { fileURLToPath } from "node:url";
33
+ import {
34
+ extractBareImports,
35
+ extractRelativeValueImports,
36
+ packageNameOf,
37
+ resolveRelativeImport,
38
+ } from "./lib/runtime-imports.js";
33
39
 
34
40
  const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
35
41
  const verifyTarball = process.argv.includes("--pack");
@@ -38,6 +44,7 @@ interface Problem {
38
44
  kind:
39
45
  | "missing-dist"
40
46
  | "missing-import"
47
+ | "broken-import"
41
48
  | "missing-tarball"
42
49
  | "missing-package"
43
50
  | "forbidden-path"
@@ -406,8 +413,13 @@ function checkStaleOutputs(): void {
406
413
  }
407
414
  }
408
415
 
409
- /** The tarball npm would publish must contain the built output. */
410
- async function inspectTarball(): Promise<void> {
416
+ /**
417
+ * The files npm would publish.
418
+ *
419
+ * The list comes from npm itself, so a check sees exactly what a consumer
420
+ * gets - not what the repository happens to contain.
421
+ */
422
+ async function packFileList(): Promise<string[]> {
411
423
  const stdout = await new Promise<string>((resolveText, rejectText) => {
412
424
  execFile(
413
425
  "npm",
@@ -423,29 +435,99 @@ async function inspectTarball(): Promise<void> {
423
435
  );
424
436
  });
425
437
 
426
- let files: string[] = [];
427
438
  try {
428
439
  // `npm pack --json` returns an array with one entry per packed tarball.
429
440
  const parsed = JSON.parse(stdout) as
430
441
  | { files?: { path: string }[] }[]
431
442
  | { files?: { path: string }[] };
432
443
  const entries = Array.isArray(parsed) ? parsed : [parsed];
433
- files = entries.flatMap((entry) => entry?.files ?? []).map((file) => file.path);
434
- if (files.length === 0) {
444
+ return entries.flatMap((entry) => entry?.files ?? []).map((file) => file.path);
445
+ } catch {
446
+ return [];
447
+ }
448
+ }
449
+
450
+ /**
451
+ * The names of the workspace packages.
452
+ *
453
+ * The workspace packages ship inside this one tarball and are never
454
+ * installed as separate packages, so a bare import that names one of them
455
+ * cannot resolve at runtime - not in a consumer's installation, and not in
456
+ * the repository either, because the workspace is never linked into
457
+ * `node_modules` under its own name.
458
+ */
459
+ function workspacePackageNames(): Set<string> {
460
+ const names = new Set<string>();
461
+ const packagesDir = join(ROOT, "packages");
462
+ if (!existsSync(packagesDir)) return names;
463
+ for (const name of readdirSync(packagesDir)) {
464
+ const manifestPath = join(packagesDir, name, "package.json");
465
+ if (!existsSync(manifestPath)) continue;
466
+ try {
467
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf8")) as {
468
+ name?: string;
469
+ };
470
+ if (manifest.name) names.add(manifest.name);
471
+ } catch {
472
+ // A manifest that cannot be read names no package.
473
+ }
474
+ }
475
+ return names;
476
+ }
477
+
478
+ /**
479
+ * Every relative import of a published file must resolve.
480
+ *
481
+ * The published app reaches its internal packages through relative paths,
482
+ * so an import that does not resolve is a module graph that breaks when it
483
+ * runs. A published `.js` file has to reach a published file: a consumer
484
+ * never sees the repository. A published `.ts` file is resolved by the
485
+ * compiler, so it may reach a source file in the repository.
486
+ *
487
+ * The earlier import check only followed specifiers that contained
488
+ * "packages/" and never looked at the compiled output, so an import inside
489
+ * a package was never verified. That is how a package whose entry point
490
+ * could not be imported reached the registry.
491
+ */
492
+ function checkRelativeImports(files: string[]): void {
493
+ const published = new Set(files);
494
+ const workspaceNames = workspacePackageNames();
495
+ for (const path of files) {
496
+ if (!/\.(ts|mts|cts|mjs|js|cjs)$/.test(path)) continue;
497
+ if (path.endsWith(".d.ts") || path.endsWith(".d.mts")) continue;
498
+ const full = join(ROOT, path);
499
+ if (!existsSync(full)) continue;
500
+ const content = readFileSync(full, "utf8");
501
+ for (const specifier of extractRelativeValueImports(content)) {
502
+ const result = resolveRelativeImport({
503
+ importer: full,
504
+ specifier,
505
+ exists: existsSync,
506
+ isPublished: (candidate) =>
507
+ published.has(relative(ROOT, candidate).split("\\").join("/")),
508
+ });
509
+ if (result.ok) continue;
435
510
  problems.push({
436
- kind: "missing-tarball",
437
- detail: "npm pack --dry-run did not return a file list",
511
+ kind: "broken-import",
512
+ detail: `${path} imports ${specifier} but ${result.reason}`,
513
+ });
514
+ }
515
+ // A bare import that names a workspace package names something that
516
+ // is never installed: the workspace packages are inside this one
517
+ // tarball. Such an import has to point at the built artifact.
518
+ for (const specifier of extractBareImports(content)) {
519
+ const name = packageNameOf(specifier);
520
+ if (!workspaceNames.has(name)) continue;
521
+ problems.push({
522
+ kind: "broken-import",
523
+ detail: `${path} imports ${specifier} but ${name} is a workspace package: it ships inside this tarball and is never installed, so the name cannot resolve`,
438
524
  });
439
- return;
440
525
  }
441
- } catch {
442
- problems.push({
443
- kind: "missing-tarball",
444
- detail: "npm pack --dry-run did not return a file list",
445
- });
446
- return;
447
526
  }
527
+ }
448
528
 
529
+ /** The tarball npm would publish must contain the built output. */
530
+ function inspectTarball(files: string[]): void {
449
531
  for (const file of files) {
450
532
  const posix = file.split("\\").join("/");
451
533
  for (const prefix of FORBIDDEN_PACKAGE_PREFIXES) {
@@ -503,7 +585,19 @@ checkPluginCopySource();
503
585
  checkImports();
504
586
  checkPackageEntries();
505
587
  checkStaleOutputs();
506
- if (verifyTarball) await inspectTarball();
588
+
589
+ // The published file list drives both the import check and the tarball
590
+ // check: it is what a consumer receives.
591
+ const publishedFiles = await packFileList();
592
+ if (publishedFiles.length === 0) {
593
+ problems.push({
594
+ kind: "missing-tarball",
595
+ detail: "npm pack --dry-run did not return a file list",
596
+ });
597
+ } else {
598
+ checkRelativeImports(publishedFiles);
599
+ if (verifyTarball) inspectTarball(publishedFiles);
600
+ }
507
601
 
508
602
  if (problems.length > 0) {
509
603
  console.error(`[verify-package-contents] FAILED with ${problems.length} problem(s):`);
@@ -518,7 +612,7 @@ if (problems.length > 0) {
518
612
  }
519
613
 
520
614
  console.log(
521
- `[verify-package-contents] OK - ${runtimeImports.size} runtime import(s) resolved${
522
- verifyTarball ? ", tarball verified" : ""
523
- }`,
615
+ `[verify-package-contents] OK - ${runtimeImports.size} runtime import(s) resolved, ${
616
+ publishedFiles.length
617
+ } published file(s) checked${verifyTarball ? ", tarball verified" : ""}`,
524
618
  );
@@ -1,22 +0,0 @@
1
- {
2
- "name": "@pi-harness/telegram",
3
- "version": "1.1.427",
4
- "description": "pi-harness-runtime — Telegram inbound/outbound",
5
- "type": "module",
6
- "main": "./src/index.js",
7
- "types": "./src/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "import": "./src/index.js",
11
- "types": "./src/index.d.ts"
12
- }
13
- },
14
- "files": [
15
- "src"
16
- ],
17
- "scripts": {
18
- "build": "tsc",
19
- "test": "bun test"
20
- },
21
- "dependencies": {}
22
- }
@@ -1,28 +0,0 @@
1
- /**
2
- * Cross-instance Telegram agent message resolution and turn injection
3
- * Zones: multi-instance bus, inbound routing, operational delivery
4
- * Owns live target resolution, source attribution, same-target fencing, and synthetic turn construction; excludes Bot API delivery and bus transport.
5
- */
6
- import type { TelegramBusAgentMessage, TelegramBusAgentTargetSelector, TelegramBusFollowerRegistry } from "./bus.ts";
7
- import type { TelegramTarget } from "./target.ts";
8
- export interface TelegramAgentMessageRuntimeDeps<TContext, TUpdate> {
9
- instanceId: string;
10
- getAllowedChatId: () => number | undefined;
11
- getLeaderTarget: () => TelegramTarget | undefined;
12
- getLeaderThreadName: () => string | undefined;
13
- getDisplayTitle?: (target: TelegramTarget) => string | undefined;
14
- followerRegistry: TelegramBusFollowerRegistry;
15
- getContext: () => TContext | undefined;
16
- handleUpdate: (update: TUpdate, ctx: TContext) => Promise<void>;
17
- getNowMs?: () => number;
18
- }
19
- export declare function createTelegramAgentMessageRuntime<TContext, TUpdate>(deps: TelegramAgentMessageRuntimeDeps<TContext, TUpdate>): {
20
- resolveTarget: (selector: TelegramBusAgentTargetSelector, sourceTarget?: TelegramTarget) => (TelegramTarget & {
21
- threadId: number;
22
- }) | undefined;
23
- route(input: {
24
- sourceTarget?: TelegramTarget;
25
- sourceThreadName?: string;
26
- message: TelegramBusAgentMessage;
27
- }): Promise<void>;
28
- };
@@ -1,90 +0,0 @@
1
- /**
2
- * Cross-instance Telegram agent message resolution and turn injection
3
- * Zones: multi-instance bus, inbound routing, operational delivery
4
- * Owns live target resolution, source attribution, same-target fencing, and synthetic turn construction; excludes Bot API delivery and bus transport.
5
- */
6
- import { TELEGRAM_INTERNAL_AGENT_MESSAGE } from "./updates.js";
7
- export function createTelegramAgentMessageRuntime(deps) {
8
- const getNowMs = deps.getNowMs ?? Date.now;
9
- const sameTarget = (left, right) => left?.chatId === right?.chatId && left?.threadId === right?.threadId;
10
- const listLiveTargets = () => {
11
- const targets = [];
12
- const leaderTarget = deps.getLeaderTarget();
13
- if (leaderTarget?.threadId) {
14
- targets.push({
15
- target: {
16
- chatId: leaderTarget.chatId,
17
- threadId: leaderTarget.threadId,
18
- },
19
- threadName: deps.getDisplayTitle?.(leaderTarget) ?? deps.getLeaderThreadName(),
20
- });
21
- }
22
- for (const follower of deps.followerRegistry.list()) {
23
- if (!follower.target?.threadId)
24
- continue;
25
- targets.push({
26
- target: {
27
- chatId: follower.target.chatId,
28
- threadId: follower.target.threadId,
29
- },
30
- threadName: deps.getDisplayTitle?.(follower.target) ?? follower.threadName,
31
- });
32
- }
33
- return targets;
34
- };
35
- const resolveTarget = (selector, sourceTarget) => {
36
- const allowedChatId = deps.getAllowedChatId();
37
- const chatId = selector.chatId ?? allowedChatId;
38
- if (chatId === undefined || chatId !== allowedChatId)
39
- return undefined;
40
- const matches = listLiveTargets().filter((candidate) => {
41
- if (candidate.target.chatId !== chatId)
42
- return false;
43
- if (selector.threadId !== undefined) {
44
- return candidate.target.threadId === selector.threadId;
45
- }
46
- const requestedName = selector.threadName?.trim().toLocaleLowerCase();
47
- return (requestedName !== undefined &&
48
- requestedName.length > 0 &&
49
- candidate.threadName?.trim().toLocaleLowerCase() === requestedName);
50
- });
51
- if (matches.length !== 1)
52
- return undefined;
53
- return sameTarget(matches[0].target, sourceTarget)
54
- ? undefined
55
- : matches[0].target;
56
- };
57
- return {
58
- resolveTarget,
59
- async route(input) {
60
- const target = resolveTarget({
61
- chatId: input.message.target.chatId,
62
- threadId: input.message.target.threadId,
63
- }, input.sourceTarget);
64
- if (!target)
65
- throw new Error("Telegram agent target is no longer live.");
66
- const allowedChatId = deps.getAllowedChatId();
67
- const ctx = deps.getContext();
68
- if (allowedChatId === undefined || !ctx) {
69
- throw new Error("Telegram agent turn routing is unavailable.");
70
- }
71
- const sourceTitle = (input.sourceTarget
72
- ? deps.getDisplayTitle?.(input.sourceTarget)
73
- : undefined) ?? input.sourceThreadName;
74
- const sourceLabel = sourceTitle
75
- ?.replace(/[\r\n\[\]]+/g, " ")
76
- .trim()
77
- .slice(0, 64) || "Pi";
78
- const message = {
79
- message_id: input.message.messageId,
80
- date: Math.floor(getNowMs() / 1000),
81
- chat: { id: target.chatId, type: "private" },
82
- from: { id: allowedChatId, is_bot: false, first_name: "Pi Agent" },
83
- message_thread_id: target.threadId,
84
- pi_telegram_agent_source_thread: sourceLabel,
85
- text: input.message.text,
86
- };
87
- await deps.handleUpdate({ message, [TELEGRAM_INTERNAL_AGENT_MESSAGE]: true }, ctx);
88
- },
89
- };
90
- }