ai-whisper 0.5.7 → 0.5.8

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.
@@ -2478,6 +2478,25 @@ function derivePlanPath(specPath, dateIso) {
2478
2478
  return `docs/superpowers/plans/${date}-${slug}.md`;
2479
2479
  }
2480
2480
 
2481
+ // ../broker/dist/storage/repositories/workflow-event-outbox-repository.js
2482
+ function appendWorkflowEvent(db, input) {
2483
+ db.prepare(`INSERT INTO workflow_event_outbox (collab_id, event_name, payload_json, created_at)
2484
+ VALUES (?, ?, ?, ?)`).run(input.collabId, input.eventName, JSON.stringify(input.payload), input.now);
2485
+ }
2486
+ function getMaxWorkflowEventId(db, collabId2) {
2487
+ const row = db.prepare("SELECT MAX(id) AS maxId FROM workflow_event_outbox WHERE collab_id = ?").get(collabId2);
2488
+ return row.maxId ?? 0;
2489
+ }
2490
+ function listWorkflowEventsAfter(db, input) {
2491
+ const rows = db.prepare(`SELECT id, event_name, payload_json FROM workflow_event_outbox
2492
+ WHERE collab_id = ? AND id > ? ORDER BY id ASC`).all(input.collabId, input.afterId);
2493
+ return rows.map((r) => ({
2494
+ id: r.id,
2495
+ eventName: r.event_name,
2496
+ payload: JSON.parse(r.payload_json)
2497
+ }));
2498
+ }
2499
+
2481
2500
  // ../broker/dist/control/workflow-control.js
2482
2501
  var SHA_REGEX = /^[0-9a-f]{7,40}$/;
2483
2502
  function liveReviewCommitRange(ctx) {
@@ -2514,6 +2533,10 @@ function composeResumeNotice(input) {
2514
2533
  }
2515
2534
  function createWorkflowControl(deps) {
2516
2535
  const { db, events } = deps;
2536
+ function emitAndRecord(collabId2, name, payload, now) {
2537
+ events.emit(name, payload);
2538
+ appendWorkflowEvent(db, { collabId: collabId2, eventName: name, payload, now });
2539
+ }
2517
2540
  function createWorkflow(input) {
2518
2541
  const collab2 = getCollab(db, input.collabId);
2519
2542
  if (!collab2) {
@@ -2552,7 +2575,7 @@ function createWorkflowControl(deps) {
2552
2575
  });
2553
2576
  });
2554
2577
  tx.immediate();
2555
- events.emit("workflow.created", { workflowId });
2578
+ emitAndRecord(input.collabId, "workflow.created", { workflowId }, input.now);
2556
2579
  return { workflowId };
2557
2580
  }
2558
2581
  function getWorkflow(workflowId) {
@@ -3295,7 +3318,7 @@ ${findingsText}`;
3295
3318
  });
3296
3319
  });
3297
3320
  tx.immediate();
3298
- events.emit("workflow.paused", { workflowId: input.workflowId });
3321
+ emitAndRecord(workflow.collabId, "workflow.paused", { workflowId: input.workflowId }, input.now);
3299
3322
  maybeCaptureQuiesceSnapshot({ workflowId: input.workflowId, now: input.now });
3300
3323
  }
3301
3324
  function resumeHaltedWorkflow(workflow, now) {
@@ -3312,10 +3335,7 @@ ${findingsText}`;
3312
3335
  });
3313
3336
  });
3314
3337
  tx.immediate();
3315
- events.emit("workflow.resumed", {
3316
- workflowId: workflow.workflowId,
3317
- phaseIndex: workflow.currentPhaseIndex
3318
- });
3338
+ emitAndRecord(workflow.collabId, "workflow.resumed", { workflowId: workflow.workflowId, phaseIndex: workflow.currentPhaseIndex }, now);
3319
3339
  }
3320
3340
  function resumeWorkflow(input) {
3321
3341
  const workflow = getWorkflowById(db, input.workflowId);
@@ -3366,10 +3386,7 @@ ${findingsText}`;
3366
3386
  });
3367
3387
  });
3368
3388
  tx.immediate();
3369
- events.emit("workflow.resumed", {
3370
- workflowId: input.workflowId,
3371
- phaseIndex: workflow.currentPhaseIndex
3372
- });
3389
+ emitAndRecord(workflow.collabId, "workflow.resumed", { workflowId: input.workflowId, phaseIndex: workflow.currentPhaseIndex }, input.now);
3373
3390
  }
3374
3391
  function cancelWorkflow(input) {
3375
3392
  const workflow = getWorkflowById(db, input.workflowId);
@@ -3428,10 +3445,7 @@ ${findingsText}`;
3428
3445
  });
3429
3446
  });
3430
3447
  tx.immediate();
3431
- events.emit("workflow.canceled", {
3432
- workflowId: input.workflowId,
3433
- reason: "canceled by operator"
3434
- });
3448
+ emitAndRecord(workflow.collabId, "workflow.canceled", { workflowId: input.workflowId, reason: "canceled by operator" }, input.now);
3435
3449
  }
3436
3450
  function getHandoffWithWorkflowMeta(handoffId) {
3437
3451
  const meta = getHandoffWithWorkflowMetaById(db, handoffId);
@@ -4715,6 +4729,19 @@ function ensureBrokerStateRow(db) {
4715
4729
  schema_version = excluded.schema_version,
4716
4730
  migrated = 1`).run(CURRENT_SCHEMA_VERSION);
4717
4731
  }
4732
+ function ensureWorkflowEventOutbox(db) {
4733
+ db.exec(`
4734
+ CREATE TABLE IF NOT EXISTS workflow_event_outbox (
4735
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
4736
+ collab_id TEXT NOT NULL,
4737
+ event_name TEXT NOT NULL,
4738
+ payload_json TEXT NOT NULL,
4739
+ created_at TEXT NOT NULL
4740
+ );
4741
+ CREATE INDEX IF NOT EXISTS idx_workflow_event_outbox_collab_id
4742
+ ON workflow_event_outbox (collab_id, id);
4743
+ `);
4744
+ }
4718
4745
  function applyMigrations(db) {
4719
4746
  const current = db.pragma("user_version", { simple: true });
4720
4747
  if (current < CURRENT_SCHEMA_VERSION) {
@@ -4731,6 +4758,7 @@ function applyMigrations(db) {
4731
4758
  ensureBrokerStateRow(db);
4732
4759
  enforceOneActiveCollabPerWorkspace(db);
4733
4760
  sweepStaleCaptureLease(db);
4761
+ ensureWorkflowEventOutbox(db);
4734
4762
  }
4735
4763
  function runMigrationBody(db) {
4736
4764
  db.exec(initMigrationSql);
@@ -5503,6 +5531,140 @@ function createBrokerRuntime(input) {
5503
5531
  };
5504
5532
  }
5505
5533
 
5534
+ // ../broker/dist/runtime/event-protocol.js
5535
+ var EVENT_PROTOCOL_VERSION = "1";
5536
+
5537
+ // ../broker/dist/runtime/event-socket-server.js
5538
+ import { createServer } from "node:net";
5539
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2 } from "node:fs";
5540
+ import { dirname as dirname2 } from "node:path";
5541
+ var EVENT_NAME_TABLE = {
5542
+ "chain.resolved": true,
5543
+ "chain.escalated": true,
5544
+ "workflow.created": true,
5545
+ "workflow.phase-started": true,
5546
+ "workflow.round-started": true,
5547
+ "workflow.phase-done": true,
5548
+ "workflow.halted": true,
5549
+ "workflow.canceled": true,
5550
+ "workflow.done": true,
5551
+ "workflow.resumed": true,
5552
+ "workflow.paused": true
5553
+ };
5554
+ var ALL_BROKER_EVENT_NAMES = Object.keys(EVENT_NAME_TABLE);
5555
+ async function createEventSocketServer(input) {
5556
+ const now = input.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
5557
+ const clients = /* @__PURE__ */ new Set();
5558
+ mkdirSync3(dirname2(input.socketPath), { recursive: true });
5559
+ if (existsSync4(input.socketPath)) {
5560
+ try {
5561
+ unlinkSync2(input.socketPath);
5562
+ } catch {
5563
+ }
5564
+ }
5565
+ function dropClient(conn) {
5566
+ clients.delete(conn);
5567
+ try {
5568
+ conn.destroy();
5569
+ } catch {
5570
+ }
5571
+ }
5572
+ function writeLine(conn, line) {
5573
+ try {
5574
+ conn.write(line);
5575
+ } catch {
5576
+ dropClient(conn);
5577
+ }
5578
+ }
5579
+ const server = createServer((conn) => {
5580
+ clients.add(conn);
5581
+ conn.on("error", () => dropClient(conn));
5582
+ conn.on("close", () => clients.delete(conn));
5583
+ const hello = {
5584
+ type: "hello",
5585
+ engineVersion: input.engineVersion,
5586
+ protocolVersion: EVENT_PROTOCOL_VERSION
5587
+ };
5588
+ writeLine(conn, `${JSON.stringify(hello)}
5589
+ `);
5590
+ });
5591
+ const buses = Array.isArray(input.events) ? input.events : [input.events];
5592
+ const unsubscribes = buses.flatMap((bus) => ALL_BROKER_EVENT_NAMES.map((name) => bus.on(name, (payload) => {
5593
+ const frame = { type: "event", name, payload, ts: now() };
5594
+ const line = `${JSON.stringify(frame)}
5595
+ `;
5596
+ for (const conn of [...clients])
5597
+ writeLine(conn, line);
5598
+ })));
5599
+ await new Promise((resolve2, reject) => {
5600
+ server.once("error", reject);
5601
+ server.listen(input.socketPath, () => {
5602
+ server.removeListener("error", reject);
5603
+ resolve2();
5604
+ });
5605
+ });
5606
+ return {
5607
+ socketPath: input.socketPath,
5608
+ close: () => new Promise((resolve2) => {
5609
+ for (const unsub of unsubscribes)
5610
+ unsub();
5611
+ for (const conn of [...clients])
5612
+ dropClient(conn);
5613
+ server.close(() => {
5614
+ try {
5615
+ if (existsSync4(input.socketPath))
5616
+ unlinkSync2(input.socketPath);
5617
+ } catch {
5618
+ }
5619
+ resolve2();
5620
+ });
5621
+ })
5622
+ };
5623
+ }
5624
+
5625
+ // ../broker/dist/runtime/workflow-event-bridge.js
5626
+ function createWorkflowEventBridge(input) {
5627
+ let lastId = 0;
5628
+ let seeded = false;
5629
+ let timer = null;
5630
+ function tick() {
5631
+ if (!seeded) {
5632
+ lastId = getMaxWorkflowEventId(input.db, input.collabId);
5633
+ seeded = true;
5634
+ return;
5635
+ }
5636
+ const rows = listWorkflowEventsAfter(input.db, {
5637
+ collabId: input.collabId,
5638
+ afterId: lastId
5639
+ });
5640
+ for (const row of rows) {
5641
+ input.events.emit(row.eventName, row.payload);
5642
+ lastId = row.id;
5643
+ }
5644
+ }
5645
+ return {
5646
+ tick,
5647
+ start() {
5648
+ if (timer)
5649
+ return;
5650
+ tick();
5651
+ timer = setInterval(() => {
5652
+ try {
5653
+ tick();
5654
+ } catch {
5655
+ }
5656
+ }, input.intervalMs);
5657
+ timer.unref();
5658
+ },
5659
+ stop() {
5660
+ if (timer) {
5661
+ clearInterval(timer);
5662
+ timer = null;
5663
+ }
5664
+ }
5665
+ };
5666
+ }
5667
+
5506
5668
  // src/runtime/relay-orchestrator-evaluator.ts
5507
5669
  import * as crypto3 from "node:crypto";
5508
5670
  import Anthropic from "@anthropic-ai/sdk";
@@ -6283,6 +6445,9 @@ import path from "node:path";
6283
6445
  function getStateRoot() {
6284
6446
  return process.env.AI_WHISPER_STATE_ROOT ?? path.join(os.homedir(), ".ai-whisper");
6285
6447
  }
6448
+ function getStateSocketsDir() {
6449
+ return path.join(getStateRoot(), "sockets");
6450
+ }
6286
6451
 
6287
6452
  // src/runtime/evaluator-config.ts
6288
6453
  function parseDotEnv(text) {
@@ -6389,6 +6554,31 @@ function recordEvaluatorStatus(db, input) {
6389
6554
 
6390
6555
  // src/bin/broker-daemon.ts
6391
6556
  import { execFile as execFile2 } from "node:child_process";
6557
+ import { join as join6 } from "node:path";
6558
+
6559
+ // src/runtime/cli-package-info.ts
6560
+ import { readFileSync as readFileSync3 } from "node:fs";
6561
+ import { dirname as dirname3 } from "node:path";
6562
+ import { fileURLToPath } from "node:url";
6563
+ var PKG_CANDIDATES = ["../../package.json", "../package.json"];
6564
+ function readWhisperPackage() {
6565
+ for (const rel of PKG_CANDIDATES) {
6566
+ try {
6567
+ const url = new URL(rel, import.meta.url);
6568
+ const pkg = JSON.parse(readFileSync3(url, "utf8"));
6569
+ if (pkg.name === "ai-whisper" && typeof pkg.version === "string") {
6570
+ return { url, version: pkg.version };
6571
+ }
6572
+ } catch {
6573
+ }
6574
+ }
6575
+ return null;
6576
+ }
6577
+ function resolveCliVersion() {
6578
+ return readWhisperPackage()?.version ?? "0.0.0-dev";
6579
+ }
6580
+
6581
+ // src/bin/broker-daemon.ts
6392
6582
  var sqlitePath = process.env.AI_WHISPER_BROKER_SQLITE;
6393
6583
  var host = process.env.AI_WHISPER_BROKER_HOST ?? "127.0.0.1";
6394
6584
  var port = Number(process.env.AI_WHISPER_BROKER_PORT ?? "4311");
@@ -6401,6 +6591,31 @@ if (collabId) {
6401
6591
  });
6402
6592
  }
6403
6593
  await broker.start();
6594
+ var eventSocket = null;
6595
+ var workflowEventBridge = null;
6596
+ if (collabId) {
6597
+ try {
6598
+ const externalEvents = createBrokerEventBus();
6599
+ workflowEventBridge = createWorkflowEventBridge({
6600
+ db: broker.db,
6601
+ events: externalEvents,
6602
+ collabId,
6603
+ intervalMs: Number(process.env.AI_WHISPER_WORKFLOW_EVENT_BRIDGE_MS ?? 1e3)
6604
+ });
6605
+ workflowEventBridge.start();
6606
+ eventSocket = await createEventSocketServer({
6607
+ socketPath: join6(getStateSocketsDir(), `events-${collabId}.sock`),
6608
+ events: [broker.events, externalEvents],
6609
+ engineVersion: resolveCliVersion()
6610
+ });
6611
+ } catch (err) {
6612
+ workflowEventBridge?.stop();
6613
+ workflowEventBridge = null;
6614
+ console.error(
6615
+ `event-socket fanout unavailable: ${err instanceof Error ? err.message : String(err)}`
6616
+ );
6617
+ }
6618
+ }
6404
6619
  var resolved;
6405
6620
  var loaderError = null;
6406
6621
  try {
@@ -6462,6 +6677,8 @@ var orchestrator = collab?.orchestratorEnabled && evaluator ? createRelayOrchest
6462
6677
  orchestrator?.start();
6463
6678
  async function shutdown() {
6464
6679
  orchestrator?.stop();
6680
+ workflowEventBridge?.stop();
6681
+ await eventSocket?.close();
6465
6682
  await broker.stop();
6466
6683
  process.exit(0);
6467
6684
  }