@tritard/waterbrother 0.16.100 → 0.16.101

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tritard/waterbrother",
3
- "version": "0.16.100",
3
+ "version": "0.16.101",
4
4
  "description": "Waterbrother: bring-your-own-model coding CLI with local tools, sessions, operator modes, and approval controls",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -3154,7 +3154,7 @@ async function dequeueTelegramBridgeRequest({ cwd }) {
3154
3154
  return next;
3155
3155
  }
3156
3156
 
3157
- async function deliverTelegramBridgeReply(request, { sessionId, content = "", error = "" }) {
3157
+ async function deliverTelegramBridgeReply(request, { sessionId, content = "", error = "", meta = {} }) {
3158
3158
  const bridge = await loadGatewayBridge(TELEGRAM_BRIDGE_SERVICE);
3159
3159
  const replies = Array.isArray(bridge.deliveredReplies) ? bridge.deliveredReplies : [];
3160
3160
  replies.push({
@@ -3163,6 +3163,7 @@ async function deliverTelegramBridgeReply(request, { sessionId, content = "", er
3163
3163
  sessionId: String(sessionId || "").trim(),
3164
3164
  content: String(content || "").trim(),
3165
3165
  error: String(error || "").trim(),
3166
+ meta: meta && typeof meta === "object" ? { ...meta } : {},
3166
3167
  completedAt: new Date().toISOString(),
3167
3168
  source: "tui"
3168
3169
  });
@@ -3187,6 +3188,92 @@ function formatTelegramRemoteActor(request = {}) {
3187
3188
  return username || userId || "telegram";
3188
3189
  }
3189
3190
 
3191
+ async function planTelegramVerificationCommands(cwd) {
3192
+ const packageJsonPath = path.join(cwd, "package.json");
3193
+ let scripts = {};
3194
+ try {
3195
+ const raw = await fs.readFile(packageJsonPath, "utf8");
3196
+ const parsed = JSON.parse(raw);
3197
+ scripts = parsed?.scripts && typeof parsed.scripts === "object" ? parsed.scripts : {};
3198
+ } catch {}
3199
+ const commands = [];
3200
+ if (scripts.check) commands.push(["npm", ["run", "check"], "npm run check"]);
3201
+ if (scripts.test) commands.push(["npm", ["test"], "npm test"]);
3202
+ if (scripts.build) commands.push(["npm", ["run", "build"], "npm run build"]);
3203
+ if (!commands.length) {
3204
+ throw new Error("No verification commands are available here. Expected package.json scripts like check, test, or build.");
3205
+ }
3206
+ return commands;
3207
+ }
3208
+
3209
+ function formatTelegramVerificationSummary(result = {}) {
3210
+ const lines = [
3211
+ "Verification result",
3212
+ `outcome: ${String(result.outcome || "failed").trim() || "failed"}`
3213
+ ];
3214
+ if (Array.isArray(result.commands) && result.commands.length) {
3215
+ lines.push("commands:");
3216
+ for (const command of result.commands) {
3217
+ lines.push(`• ${String(command || "").trim()}`);
3218
+ }
3219
+ }
3220
+ if (result.summary) {
3221
+ lines.push("Summary");
3222
+ lines.push(String(result.summary || "").trim());
3223
+ }
3224
+ if (Array.isArray(result.logs) && result.logs.length) {
3225
+ lines.push("Top errors");
3226
+ for (const line of result.logs.slice(0, 6)) {
3227
+ lines.push(`• ${String(line || "").trim()}`);
3228
+ }
3229
+ }
3230
+ return lines.filter(Boolean).join("\n");
3231
+ }
3232
+
3233
+ async function runTelegramLocalVerification(cwd) {
3234
+ const planned = await planTelegramVerificationCommands(cwd);
3235
+ const commands = [];
3236
+ const startedAt = new Date().toISOString();
3237
+ const logs = [];
3238
+ let passedCount = 0;
3239
+ for (const [bin, args, label] of planned) {
3240
+ commands.push(label);
3241
+ try {
3242
+ await execFileAsync(bin, args, {
3243
+ cwd,
3244
+ env: { ...process.env },
3245
+ maxBuffer: 8 * 1024 * 1024,
3246
+ timeout: 10 * 60 * 1000
3247
+ });
3248
+ passedCount += 1;
3249
+ } catch (error) {
3250
+ const combined = `${String(error?.stderr || "")}\n${String(error?.stdout || "")}`.trim();
3251
+ const extracted = combined
3252
+ .split("\n")
3253
+ .map((line) => String(line || "").trim())
3254
+ .filter(Boolean)
3255
+ .slice(0, 6);
3256
+ logs.push(...extracted);
3257
+ return {
3258
+ outcome: error?.killed || error?.signal === "SIGTERM" ? "timeout" : (passedCount > 0 ? "partial" : "failed"),
3259
+ summary: `${label} failed${passedCount > 0 ? ` after ${passedCount} passing check${passedCount === 1 ? "" : "s"}` : ""}.`,
3260
+ commands,
3261
+ startedAt,
3262
+ completedAt: new Date().toISOString(),
3263
+ logs
3264
+ };
3265
+ }
3266
+ }
3267
+ return {
3268
+ outcome: "passed",
3269
+ summary: `${commands.length} verification command${commands.length === 1 ? "" : "s"} passed.`,
3270
+ commands,
3271
+ startedAt,
3272
+ completedAt: new Date().toISOString(),
3273
+ logs
3274
+ };
3275
+ }
3276
+
3190
3277
  async function chooseFromInteractiveMenu({ title, options, defaultIndex = 0 }) {
3191
3278
  if (!process.stdin.isTTY || !process.stdout.isTTY || options.length === 0) {
3192
3279
  return options[Math.max(0, Math.min(defaultIndex, options.length - 1))] || null;
@@ -7714,6 +7801,16 @@ Be concrete about surfaces — name actual pages/flows. Choose the best stack fo
7714
7801
  }
7715
7802
  console.log(cyan(`telegram request from ${remoteActor}`));
7716
7803
  console.log(`${cyan("telegram>")} ${sanitizeTerminalText(remoteRequest.text || "")}`);
7804
+ if (String(remoteRequest.requestKind || "").trim() === "verification") {
7805
+ const verificationResult = await runTelegramLocalVerification(context.cwd);
7806
+ await deliverTelegramBridgeReply(remoteRequest, {
7807
+ sessionId: remoteSessionId,
7808
+ content: formatTelegramVerificationSummary(verificationResult),
7809
+ meta: { verificationResult }
7810
+ });
7811
+ console.log(green(`telegram verification sent -> ${remoteActor}`));
7812
+ continue;
7813
+ }
7717
7814
  const remoteManifest = await buildSelfAwarenessManifest({
7718
7815
  cwd: context.cwd,
7719
7816
  runtime: context.runtime,
@@ -53,6 +53,7 @@ function normalizeBridgeRequest(parsed = {}) {
53
53
  displayName: String(parsed?.displayName || "").trim(),
54
54
  sessionId: String(parsed?.sessionId || "").trim(),
55
55
  text: String(parsed?.text || "").trim(),
56
+ requestKind: String(parsed?.requestKind || "prompt").trim() || "prompt",
56
57
  explicitExecution: parsed?.explicitExecution === true,
57
58
  targetPid: Number.isFinite(Number(parsed?.targetPid)) ? Math.floor(Number(parsed.targetPid)) : 0,
58
59
  targetSessionId: String(parsed?.targetSessionId || "").trim(),
@@ -72,6 +73,7 @@ function normalizeBridgeReply(parsed = {}) {
72
73
  sessionId: String(parsed?.sessionId || "").trim(),
73
74
  content: String(parsed?.content || "").trim(),
74
75
  error: String(parsed?.error || "").trim(),
76
+ meta: parsed?.meta && typeof parsed.meta === "object" ? { ...parsed.meta } : {},
75
77
  completedAt: String(parsed?.completedAt || "").trim(),
76
78
  source: String(parsed?.source || "tui").trim() || "tui"
77
79
  };
package/src/gateway.js CHANGED
@@ -2448,6 +2448,34 @@ class TelegramGateway {
2448
2448
  if (!project?.enabled) {
2449
2449
  return "This project is not shared.";
2450
2450
  }
2451
+ const liveHosts = await this.getLiveBridgeHosts({ cwd });
2452
+ const verifierHost = verifier ? findLiveHostForAgent(liveHosts, verifier) : null;
2453
+ if (verifier && verifierHost?.pid) {
2454
+ const bridgeResult = await this.runPromptViaBridge(message, sessionId, "run verification", {
2455
+ explicitExecution: true,
2456
+ requestKind: "verification",
2457
+ targetHost: verifierHost,
2458
+ includeSource: true
2459
+ });
2460
+ const routedResult = bridgeResult?.replyMeta?.verificationResult && typeof bridgeResult.replyMeta.verificationResult === "object"
2461
+ ? bridgeResult.replyMeta.verificationResult
2462
+ : null;
2463
+ const handledBy = formatBridgeHostLabel(bridgeResult?.sourceHost || verifierHost) || verifierHost.sessionId || "live terminal";
2464
+ if (routedResult) {
2465
+ const nextProject = await addVerificationResult(cwd, routedResult, { actorId, actorName });
2466
+ const latest = getLatestVerificationResult(nextProject) || routedResult;
2467
+ return [
2468
+ `<b>Handled by</b> <code>${escapeTelegramHtml(handledBy)}</code>`,
2469
+ "",
2470
+ formatVerificationResultMarkup(latest, verifier)
2471
+ ].join("\n");
2472
+ }
2473
+ return [
2474
+ `<b>Handled by</b> <code>${escapeTelegramHtml(handledBy)}</code>`,
2475
+ "",
2476
+ String(bridgeResult?.content || "").trim() || "<b>Verification result</b>\n(no content)"
2477
+ ].join("\n");
2478
+ }
2451
2479
  const result = await this.runLocalVerification(cwd, verifier);
2452
2480
  const nextProject = await addVerificationResult(cwd, result, { actorId, actorName });
2453
2481
  const latest = getLatestVerificationResult(nextProject) || result;
@@ -3213,6 +3241,7 @@ class TelegramGateway {
3213
3241
  displayName: this.describeTelegramUser(message?.from || {}).displayName,
3214
3242
  sessionId: String(sessionId || "").trim(),
3215
3243
  text: String(promptText || "").trim(),
3244
+ requestKind: String(options.requestKind || "prompt").trim() || "prompt",
3216
3245
  explicitExecution: options.explicitExecution === true,
3217
3246
  targetPid: Number(host?.pid || 0),
3218
3247
  targetSessionId: String(host?.sessionId || "").trim(),
@@ -3243,7 +3272,7 @@ class TelegramGateway {
3243
3272
  }
3244
3273
  const content = String(reply.content || "").trim() || "(no content)";
3245
3274
  return options.includeSource
3246
- ? { content, sourceHost: host }
3275
+ ? { content, sourceHost: host, replyMeta: reply.meta && typeof reply.meta === "object" ? { ...reply.meta } : {} }
3247
3276
  : content;
3248
3277
  }
3249
3278