pi-harness-runtime 1.1.375 → 1.1.376

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 (116) hide show
  1. package/README.md +68 -39
  2. package/package.json +2 -1
  3. package/packages/a2a-adapter/package.json +1 -1
  4. package/packages/agent-policy/package.json +1 -1
  5. package/packages/architecture-generator/package.json +1 -1
  6. package/packages/auth/package.json +1 -1
  7. package/packages/autonomous-refactor/package.json +1 -1
  8. package/packages/autonomous-runtime/package.json +1 -1
  9. package/packages/cache-strategy/package.json +1 -1
  10. package/packages/capability-registry/package.json +1 -1
  11. package/packages/checkpoint/package.json +1 -1
  12. package/packages/cli-plugin-sdk/package.json +1 -1
  13. package/packages/clipboard/package.json +1 -1
  14. package/packages/clipboard-plugin/package.json +1 -1
  15. package/packages/code-generation/package.json +1 -1
  16. package/packages/code-review/package.json +1 -1
  17. package/packages/codex-adapter/package.json +1 -1
  18. package/packages/codex-watcher/package.json +1 -1
  19. package/packages/config-capture/package.json +1 -1
  20. package/packages/connection-runtime/package.json +1 -1
  21. package/packages/context-compiler/package.json +1 -1
  22. package/packages/context-discovery/package.json +1 -1
  23. package/packages/context-manager/package.json +1 -1
  24. package/packages/contracts/package.json +1 -1
  25. package/packages/cookie-sanitizer/package.json +1 -1
  26. package/packages/cost-optimizer/package.json +1 -1
  27. package/packages/dependency-analyzer/package.json +1 -1
  28. package/packages/django-plugin/package.json +1 -1
  29. package/packages/doc-generator/package.json +1 -1
  30. package/packages/durable-review/package.json +1 -1
  31. package/packages/e2e-tester/package.json +1 -1
  32. package/packages/evaluation-engine/package.json +1 -1
  33. package/packages/evaluation-runner/package.json +1 -1
  34. package/packages/event-bus/package.json +1 -1
  35. package/packages/event-sink/package.json +1 -1
  36. package/packages/event-store/package.json +1 -1
  37. package/packages/experience-replay/package.json +1 -1
  38. package/packages/feedback-collector/package.json +1 -1
  39. package/packages/file-copy-helper/package.json +1 -1
  40. package/packages/framework-detector/package.json +1 -1
  41. package/packages/framework-plugin-sdk/package.json +1 -1
  42. package/packages/frappe-plugin/package.json +1 -1
  43. package/packages/generic-web-plugin/package.json +1 -1
  44. package/packages/health-monitor/package.json +1 -1
  45. package/packages/intent-analyzer/package.json +1 -1
  46. package/packages/jev-judge/package.json +1 -1
  47. package/packages/knowledge-graph/package.json +1 -1
  48. package/packages/knowledge-retrieval/package.json +1 -1
  49. package/packages/laravel-plugin/package.json +1 -1
  50. package/packages/learning-engine/package.json +1 -1
  51. package/packages/mcp-adapter/package.json +1 -1
  52. package/packages/memory-engine/package.json +1 -1
  53. package/packages/milestone-manager/package.json +1 -1
  54. package/packages/model-registry/package.json +1 -1
  55. package/packages/nextjs-plugin/package.json +1 -1
  56. package/packages/notification/package.json +1 -1
  57. package/packages/observability/package.json +1 -1
  58. package/packages/okf-indexer/package.json +1 -1
  59. package/packages/paperclip-integration/package.json +1 -1
  60. package/packages/performance-optimizer/package.json +1 -1
  61. package/packages/permission-mode/package.json +1 -1
  62. package/packages/privilege-broker/package.json +1 -1
  63. package/packages/project-analyzer/package.json +1 -1
  64. package/packages/project-bootstrap/package.json +1 -1
  65. package/packages/projection-engine/package.json +1 -1
  66. package/packages/prompt-compiler/package.json +1 -1
  67. package/packages/prompt-versioning/package.json +1 -1
  68. package/packages/provider-adapter-sdk/package.json +1 -1
  69. package/packages/provider-router/package.json +1 -1
  70. package/packages/provider-selector/package.json +1 -1
  71. package/packages/providers/package.json +1 -1
  72. package/packages/qdrant-skills/package.json +1 -1
  73. package/packages/quota-manager/package.json +1 -1
  74. package/packages/rate-limiter/package.json +1 -1
  75. package/packages/react-vite-plugin/package.json +1 -1
  76. package/packages/relay/dist/ws-server.d.ts +48 -0
  77. package/packages/relay/dist/ws-server.js +130 -0
  78. package/packages/relay/package.json +1 -1
  79. package/packages/relay/src/ws-server.ts +165 -0
  80. package/packages/release-manager/package.json +1 -1
  81. package/packages/requirement-compiler/package.json +1 -1
  82. package/packages/runtime/package.json +1 -1
  83. package/packages/scheduler/package.json +1 -1
  84. package/packages/scheduler-adapter/package.json +1 -1
  85. package/packages/session/package.json +1 -1
  86. package/packages/session-api/package.json +1 -1
  87. package/packages/session-export/package.json +1 -1
  88. package/packages/shared-context/package.json +1 -1
  89. package/packages/skill-mcp-client/package.json +1 -1
  90. package/packages/skill-registry/package.json +1 -1
  91. package/packages/skills/package.json +1 -1
  92. package/packages/sprint-planner/package.json +1 -1
  93. package/packages/subscription-engine/package.json +1 -1
  94. package/packages/task-compiler/package.json +1 -1
  95. package/packages/telegram/package.json +1 -1
  96. package/packages/tencentdb-memory/package.json +1 -1
  97. package/packages/tencentdb-sync/package.json +1 -1
  98. package/packages/test-data-generator/package.json +1 -1
  99. package/packages/test-generator/package.json +1 -1
  100. package/packages/todo-bd-sync/package.json +1 -1
  101. package/packages/token-estimation/package.json +1 -1
  102. package/packages/token-optimizer/package.json +1 -1
  103. package/packages/trajectory/package.json +1 -1
  104. package/packages/tui/package.json +1 -1
  105. package/packages/types/package.json +1 -1
  106. package/packages/voice-api/package.json +1 -1
  107. package/packages/webmcp-tester/package.json +1 -1
  108. package/packages/wiki/package.json +1 -1
  109. package/packages/workflow-events/package.json +1 -1
  110. package/packages/workspace-scanner/package.json +1 -1
  111. package/packages/worktree/package.json +1 -1
  112. package/packages/write-review/package.json +1 -1
  113. package/scripts/relay-client.ts +175 -0
  114. package/scripts/sync-callback-queue.ts +57 -0
  115. package/scripts/telegram-callback-processor.ts +16 -0
  116. package/scripts/unified-webhook-server.ts +142 -129
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/skill-registry",
3
- "version": "1.1.375",
3
+ "version": "1.1.376",
4
4
  "description": "Skill Registry for pi-harness-runtime — dynamic skill discovery and invocation",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/skills",
3
- "version": "1.1.375",
3
+ "version": "1.1.376",
4
4
  "description": "File-based skill system with pi.dev compatibility and Hermes self-improvement",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/sprint-planner",
3
- "version": "1.1.375",
3
+ "version": "1.1.376",
4
4
  "type": "module",
5
5
  "description": "Sprint planning engine for pi-harness",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/subscription-engine",
3
- "version": "1.1.375",
3
+ "version": "1.1.376",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/task-compiler",
3
- "version": "1.1.375",
3
+ "version": "1.1.376",
4
4
  "description": "Task Compiler for pi-harness-runtime — produces deterministic task DAG from CompiledRequirement",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/telegram",
3
- "version": "1.1.375",
3
+ "version": "1.1.376",
4
4
  "description": "pi-harness-runtime — Telegram inbound/outbound",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/tencentdb-memory",
3
- "version": "1.1.375",
3
+ "version": "1.1.376",
4
4
  "description": "TencentDB Agent Memory client - sync skills to centralized knowledge base",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/tencentdb-sync",
3
- "version": "1.1.375",
3
+ "version": "1.1.376",
4
4
  "description": "Sync SKILL.md skills to TencentDB-Agent-Memory server (RFC-0105/0106)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/test-data-generator",
3
- "version": "1.1.375",
3
+ "version": "1.1.376",
4
4
  "description": "Test data generation for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/test-generator",
3
- "version": "1.1.375",
3
+ "version": "1.1.376",
4
4
  "description": "Auto-Test Generator — generate unit and E2E tests from source code (RFC-0013)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/todo-bd-sync",
3
- "version": "1.1.375",
3
+ "version": "1.1.376",
4
4
  "description": "Two-way sync between rpiv-todo overlay and bd issue tracker",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/token-estimation",
3
- "version": "1.1.375",
3
+ "version": "1.1.376",
4
4
  "description": "pi-harness-runtime — token-estimation",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/token-optimizer",
3
- "version": "1.1.375",
3
+ "version": "1.1.376",
4
4
  "description": "Token Budget Optimizer — maximize prompt value within token limits (RFC-0011)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/trajectory",
3
- "version": "1.1.375",
3
+ "version": "1.1.376",
4
4
  "description": "Trajectory capture for the write-review loop — persists cycle records for learning",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/tui",
3
- "version": "1.1.375",
3
+ "version": "1.1.376",
4
4
  "description": "pi-harness-runtime — tui",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/types",
3
- "version": "1.1.375",
3
+ "version": "1.1.376",
4
4
  "description": "Shared TypeScript types for pi-harness-runtime packages",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/voice-api",
3
- "version": "1.1.375",
3
+ "version": "1.1.376",
4
4
  "type": "module",
5
5
  "description": "Voice-to-text API using Whisper for Telegram and LINE integration",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/webmcp-tester",
3
- "version": "1.1.375",
3
+ "version": "1.1.376",
4
4
  "description": "WebMCP-powered E2E testing for pi-harness - natural language test scenarios",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/wiki",
3
- "version": "1.1.375",
3
+ "version": "1.1.376",
4
4
  "description": "LLM Wiki integration for pi-harness-runtime",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/workflow-events",
3
- "version": "1.1.375",
3
+ "version": "1.1.376",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi/workspace-scanner",
3
- "version": "1.1.375",
3
+ "version": "1.1.376",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/worktree",
3
- "version": "1.1.375",
3
+ "version": "1.1.376",
4
4
  "description": "pi-harness-runtime — worktree",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moocoding/write-review",
3
- "version": "1.1.375",
3
+ "version": "1.1.376",
4
4
  "description": "Two-agent write-review loop with blackboard coordination",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -0,0 +1,175 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Real-time Callback Relay Client
4
+ *
5
+ * Connects to WebSocket server on VPS to receive callbacks instantly.
6
+ * No file queue sync needed!
7
+ *
8
+ * Usage:
9
+ * RELAY_WS_URL=wss://webhook.bunchee.online:8444 \
10
+ * RELAY_AUTH_TOKEN=change-me-in-production \
11
+ * bun run scripts/relay-client.ts
12
+ */
13
+
14
+ import { WebSocket } from "ws";
15
+ import { readFileSync, writeFileSync, existsSync } from "fs";
16
+ import { join } from "path";
17
+ import { homedir } from "os";
18
+
19
+ // Configuration
20
+ const RELAY_WS_URL = process.env.RELAY_WS_URL || "wss://webhook.bunchee.online:8444";
21
+ const RELAY_AUTH_TOKEN = process.env.RELAY_AUTH_TOKEN || "change-me-in-production";
22
+ const CALLBACK_QUEUE_FILE = join(homedir(), ".pi-harness-runtime", "callback-queue.jsonl");
23
+ const JOB_PIPE_FILE = join(homedir(), ".pi-harness-runtime", "job-commands.jsonl");
24
+
25
+ interface RelayMessage {
26
+ type: string;
27
+ payload: unknown;
28
+ }
29
+
30
+ interface CallbackPayload {
31
+ type: "telegram_callback" | "line_callback";
32
+ action: string;
33
+ targetId: string;
34
+ userId: number;
35
+ username?: string;
36
+ messageId?: number;
37
+ timestamp: string;
38
+ }
39
+
40
+ let ws: WebSocket | null = null;
41
+ let reconnectDelay = 1000;
42
+ let authenticated = false;
43
+
44
+ function loadKeys() {
45
+ const keysDir = join(homedir(), ".pi-harness-runtime", "keys");
46
+ return {
47
+ telegramBotToken: readFileSync(join(keysDir, "telegram-bot-token.txt"), "utf8").trim(),
48
+ telegramAllowedUsers: readFileSync(join(keysDir, "telegram-allowed-users.txt"), "utf8").trim(),
49
+ };
50
+ }
51
+
52
+ function writeToJobPipe(command: { command: string; jobId: string; userId: number; timestamp: string }): void {
53
+ const line = JSON.stringify(command) + "\n";
54
+ writeFileSync(JOB_PIPE_FILE, line, { flag: "a" });
55
+ console.log(`[RelayClient] Wrote to job pipe: ${command.command} ${command.jobId}`);
56
+ }
57
+
58
+ async function sendTelegramMessage(chatId: number, text: string): Promise<void> {
59
+ const keys = loadKeys();
60
+ await fetch(`https://api.telegram.org/bot${keys.telegramBotToken}/sendMessage`, {
61
+ method: "POST",
62
+ headers: { "Content-Type": "application/json" },
63
+ body: JSON.stringify({ chat_id: chatId, text }),
64
+ });
65
+ }
66
+
67
+ async function processCallback(callback: CallbackPayload): Promise<void> {
68
+ console.log(`[RelayClient] Processing: ${callback.type} - ${callback.action} ${callback.targetId}`);
69
+
70
+ switch (callback.action) {
71
+ case "approve":
72
+ writeToJobPipe({
73
+ command: "resume",
74
+ jobId: callback.targetId,
75
+ userId: callback.userId,
76
+ timestamp: new Date().toISOString(),
77
+ });
78
+ await sendTelegramMessage(callback.userId, `✅ Approved! Job ${callback.targetId} will resume.`);
79
+ break;
80
+
81
+ case "reject":
82
+ writeToJobPipe({
83
+ command: "cancel",
84
+ jobId: callback.targetId,
85
+ userId: callback.userId,
86
+ timestamp: new Date().toISOString(),
87
+ });
88
+ await sendTelegramMessage(callback.userId, `❌ Rejected. Job ${callback.targetId} cancelled.`);
89
+ break;
90
+
91
+ case "sayhi":
92
+ await sendTelegramMessage(
93
+ callback.userId,
94
+ "👋 Hi! Two-way communication working! (Real-time via WebSocket)"
95
+ );
96
+ break;
97
+
98
+ default:
99
+ console.log(`[RelayClient] Unknown action: ${callback.action}`);
100
+ }
101
+ }
102
+
103
+ function connect(): void {
104
+ console.log(`[RelayClient] Connecting to ${RELAY_WS_URL}...`);
105
+
106
+ ws = new WebSocket(RELAY_WS_URL);
107
+
108
+ ws.on("open", () => {
109
+ console.log("[RelayClient] Connected!");
110
+ reconnectDelay = 1000;
111
+
112
+ // Authenticate
113
+ ws!.send(JSON.stringify({ type: "auth", payload: RELAY_AUTH_TOKEN }));
114
+ });
115
+
116
+ ws.on("message", async (data) => {
117
+ try {
118
+ const msg = JSON.parse(data.toString()) as RelayMessage;
119
+
120
+ switch (msg.type) {
121
+ case "connected":
122
+ console.log("[RelayClient] Server acknowledged connection");
123
+ break;
124
+
125
+ case "auth":
126
+ const authResult = msg.payload as { success: boolean; error?: string };
127
+ if (authResult.success) {
128
+ authenticated = true;
129
+ console.log("[RelayClient] Authenticated!");
130
+ } else {
131
+ console.error("[RelayClient] Auth failed:", authResult.error);
132
+ ws?.close();
133
+ }
134
+ break;
135
+
136
+ case "callback":
137
+ if (!authenticated) break;
138
+ await processCallback(msg.payload as CallbackPayload);
139
+ break;
140
+
141
+ case "pong":
142
+ // Heartbeat response
143
+ break;
144
+ }
145
+ } catch (err) {
146
+ console.error("[RelayClient] Parse error:", err);
147
+ }
148
+ });
149
+
150
+ ws.on("close", () => {
151
+ authenticated = false;
152
+ console.log(`[RelayClient] Disconnected. Reconnecting in ${reconnectDelay}ms...`);
153
+ setTimeout(connect, reconnectDelay);
154
+ reconnectDelay = Math.min(reconnectDelay * 2, 30000); // Max 30s
155
+ });
156
+
157
+ ws.on("error", (err) => {
158
+ console.error("[RelayClient] Error:", err.message);
159
+ });
160
+ }
161
+
162
+ // Send ping every 30s to keep alive
163
+ setInterval(() => {
164
+ if (ws?.readyState === WebSocket.OPEN) {
165
+ ws.send(JSON.stringify({ type: "ping", payload: {} }));
166
+ }
167
+ }, 30000);
168
+
169
+ console.log("===========================================");
170
+ console.log("Real-time Callback Relay Client");
171
+ console.log("===========================================");
172
+ console.log(`Server: ${RELAY_WS_URL}`);
173
+ console.log("===========================================");
174
+
175
+ connect();
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Syncs callback queue from VPS to local for processing.
4
+ * Run this alongside telegram-callback-processor.ts
5
+ *
6
+ * Usage:
7
+ * bun run scripts/sync-callback-queue.ts
8
+ *
9
+ * Or set up a cron job:
10
+ * * * * * * bun /home/frappe/pi-harness-runtime/scripts/sync-callback-queue.ts
11
+ */
12
+
13
+ import { execSync } from "child_process";
14
+ import { readFileSync, writeFileSync, existsSync } from "fs";
15
+ import { join } from "path";
16
+
17
+ const VPS_HOST = process.env.VPS_HOST || "frappe@217.216.37.251";
18
+ const VPS_QUEUE = "/home/frappe/.pi-harness-runtime/callback-queue.jsonl";
19
+ const LOCAL_QUEUE = join(process.env.HOME || "/home/frappe", ".pi-harness-runtime", "callback-queue.jsonl");
20
+
21
+ function syncQueue(): number {
22
+ try {
23
+ // SSH to get VPS queue content
24
+ const vpsQueueContent = execSync(`ssh -o BatchMode=yes ${VPS_HOST} "cat ${VPS_QUEUE}"`, { encoding: "utf8" });
25
+
26
+ if (!vpsQueueContent.trim()) {
27
+ return 0; // Nothing to sync
28
+ }
29
+
30
+ // Append to local queue
31
+ const existing = existsSync(LOCAL_QUEUE) ? readFileSync(LOCAL_QUEUE, "utf8") : "";
32
+ const newQueue = existing + vpsQueueContent;
33
+ writeFileSync(LOCAL_QUEUE, newQueue);
34
+
35
+ // Clear VPS queue
36
+ execSync(`ssh -o BatchMode=yes ${VPS_HOST} "truncate -s 0 ${VPS_QUEUE}"`);
37
+
38
+ const count = vpsQueueContent.split("\n").filter(l => l.trim()).length;
39
+ if (count > 0) {
40
+ console.log(`[Sync] Synced ${count} callback(s) from VPS`);
41
+ }
42
+ return count;
43
+ } catch (err) {
44
+ console.error("[Sync] Error:", err);
45
+ return 0;
46
+ }
47
+ }
48
+
49
+ // Run once and exit (for cron) unless --watch is passed
50
+ const isWatch = process.argv.includes("--watch");
51
+
52
+ if (isWatch) {
53
+ console.log("[Sync] Watching VPS queue every 500ms...");
54
+ setInterval(syncQueue, 500);
55
+ } else {
56
+ syncQueue();
57
+ }
@@ -89,6 +89,22 @@ export async function processCallbacks(): Promise<number> {
89
89
  break;
90
90
  }
91
91
 
92
+ case "sayhi": {
93
+ console.log(`👋 SAYHI: ${item.targetId} from @${item.username || item.userId}`);
94
+ // Send greeting response via Telegram
95
+ const botToken = process.env.TELEGRAM_BOT_TOKEN || readFileSync(join(process.env.HOME || "", ".pi-harness-runtime", "keys", "telegram-bot-token.txt"), "utf-8").trim();
96
+ const chatId = item.userId;
97
+ await fetch(`https://api.telegram.org/bot${botToken}/sendMessage`, {
98
+ method: "POST",
99
+ headers: { "Content-Type": "application/json" },
100
+ body: JSON.stringify({
101
+ chat_id: chatId,
102
+ text: "👋 Hi! Two-way communication is working! Click buttons to interact."
103
+ })
104
+ });
105
+ break;
106
+ }
107
+
92
108
  default: {
93
109
  console.log(`❓ UNKNOWN: ${item.action}_${item.targetId}`);
94
110
  if (onUnknown) {