ai-whisper 0.2.0 → 0.2.1
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/dist/bin/broker-daemon.js +153 -16
- package/dist/bin/companion-agent.js +153 -16
- package/dist/bin/relay-monitor.js +153 -16
- package/dist/bin/whisper.js +629 -302
- package/package.json +3 -3
|
@@ -1031,6 +1031,7 @@ function rowToRecord(row) {
|
|
|
1031
1031
|
clipSample: row.clip_sample,
|
|
1032
1032
|
turnSample: row.turn_sample,
|
|
1033
1033
|
abortedByRaceGuard: row.aborted_by_race_guard === 1,
|
|
1034
|
+
interferenceDetected: row.interference_detected === 1,
|
|
1034
1035
|
createdAt: row.created_at
|
|
1035
1036
|
};
|
|
1036
1037
|
}
|
|
@@ -1038,8 +1039,9 @@ function insertCaptureDiagnostic(db, input) {
|
|
|
1038
1039
|
db.prepare(`INSERT INTO relay_capture_diagnostics
|
|
1039
1040
|
(capture_id, handoff_id, collab_id, chain_id, workflow_id, target_provider,
|
|
1040
1041
|
capture_status, clip_len, turn_len, turn_confidence, jaccard_score,
|
|
1041
|
-
containment_score, clip_sample, turn_sample, aborted_by_race_guard,
|
|
1042
|
-
|
|
1042
|
+
containment_score, clip_sample, turn_sample, aborted_by_race_guard,
|
|
1043
|
+
interference_detected, created_at)
|
|
1044
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(input.captureId, input.handoffId, input.collabId, input.chainId, input.workflowId, input.targetProvider, input.captureStatus, input.clipLen, input.turnLen, input.turnConfidence, input.jaccardScore, input.containmentScore, input.clipSample, input.turnSample, input.abortedByRaceGuard ? 1 : 0, input.interferenceDetected ? 1 : 0, input.createdAt);
|
|
1043
1045
|
}
|
|
1044
1046
|
function listCaptureDiagnosticsByCollab(db, collabId2, limit, opts) {
|
|
1045
1047
|
const filter = opts?.workflowFilter;
|
|
@@ -3865,6 +3867,7 @@ function createControlService(db, events) {
|
|
|
3865
3867
|
clipSample: input.clipSample,
|
|
3866
3868
|
turnSample: input.turnSample,
|
|
3867
3869
|
abortedByRaceGuard: input.abortedByRaceGuard,
|
|
3870
|
+
interferenceDetected: input.interferenceDetected ?? false,
|
|
3868
3871
|
createdAt: input.now
|
|
3869
3872
|
});
|
|
3870
3873
|
return { captureId };
|
|
@@ -3941,8 +3944,130 @@ function createBrokerApp(input) {
|
|
|
3941
3944
|
return app;
|
|
3942
3945
|
}
|
|
3943
3946
|
|
|
3947
|
+
// ../broker/dist/storage/enforce-one-active-collab.js
|
|
3948
|
+
function defaultIsPidAlive(pid) {
|
|
3949
|
+
try {
|
|
3950
|
+
process.kill(pid, 0);
|
|
3951
|
+
return true;
|
|
3952
|
+
} catch (err) {
|
|
3953
|
+
return err.code === "EPERM";
|
|
3954
|
+
}
|
|
3955
|
+
}
|
|
3956
|
+
function dedupeActiveCollabs(db, opts) {
|
|
3957
|
+
const rows = db.prepare(`SELECT c.collab_id, c.workspace_id, c.created_at, d.pid AS pid,
|
|
3958
|
+
(SELECT COUNT(*) FROM workflows w
|
|
3959
|
+
WHERE w.collab_id = c.collab_id AND w.status = 'running') AS running_workflows
|
|
3960
|
+
FROM collab c
|
|
3961
|
+
LEFT JOIN broker_daemon d ON d.collab_id = c.collab_id
|
|
3962
|
+
WHERE c.status = 'active' AND c.workspace_id IS NOT NULL`).all();
|
|
3963
|
+
const byWorkspace = /* @__PURE__ */ new Map();
|
|
3964
|
+
for (const row of rows) {
|
|
3965
|
+
const key = row.workspace_id;
|
|
3966
|
+
const list = byWorkspace.get(key);
|
|
3967
|
+
if (list)
|
|
3968
|
+
list.push(row);
|
|
3969
|
+
else
|
|
3970
|
+
byWorkspace.set(key, [row]);
|
|
3971
|
+
}
|
|
3972
|
+
const conflicted = [];
|
|
3973
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3974
|
+
const stop = db.prepare("UPDATE collab SET status = 'stopped', stopped_at = ?, updated_at = ? WHERE collab_id = ?");
|
|
3975
|
+
for (const [workspaceId, group] of byWorkspace) {
|
|
3976
|
+
if (group.length < 2)
|
|
3977
|
+
continue;
|
|
3978
|
+
const workflowOwners = group.filter((r) => r.running_workflows > 0);
|
|
3979
|
+
if (workflowOwners.length >= 2) {
|
|
3980
|
+
conflicted.push(workspaceId);
|
|
3981
|
+
opts.warn(`Workspace ${workspaceId} has ${workflowOwners.length} active collabs that each own a running workflow: ${workflowOwners.map((r) => r.collab_id).join(", ")}. Refusing to auto-stop any (that would orphan a live run). Stop the extra collab(s) manually with \`whisper collab stop --collab <id>\`. The one-active-collab-per-workspace index will be created automatically once only one remains.`);
|
|
3982
|
+
continue;
|
|
3983
|
+
}
|
|
3984
|
+
let survivor;
|
|
3985
|
+
if (workflowOwners.length === 1) {
|
|
3986
|
+
survivor = workflowOwners[0];
|
|
3987
|
+
} else {
|
|
3988
|
+
const live = group.filter((r) => r.pid !== null && opts.isPidAlive(r.pid));
|
|
3989
|
+
if (live.length >= 1) {
|
|
3990
|
+
survivor = live.reduce((a, b) => a.created_at >= b.created_at ? a : b);
|
|
3991
|
+
} else {
|
|
3992
|
+
survivor = group.reduce((a, b) => a.created_at >= b.created_at ? a : b);
|
|
3993
|
+
}
|
|
3994
|
+
}
|
|
3995
|
+
for (const row of group) {
|
|
3996
|
+
if (row.collab_id === survivor.collab_id)
|
|
3997
|
+
continue;
|
|
3998
|
+
stop.run(now, now, row.collab_id);
|
|
3999
|
+
}
|
|
4000
|
+
}
|
|
4001
|
+
return conflicted;
|
|
4002
|
+
}
|
|
4003
|
+
var CREATE_INDEX_SQL = "CREATE UNIQUE INDEX IF NOT EXISTS idx_collab_one_active_per_workspace ON collab(workspace_id) WHERE status = 'active'";
|
|
4004
|
+
function residualDuplicateCount(db) {
|
|
4005
|
+
const row = db.prepare(`SELECT COUNT(*) AS n FROM (
|
|
4006
|
+
SELECT workspace_id FROM collab
|
|
4007
|
+
WHERE status = 'active' AND workspace_id IS NOT NULL
|
|
4008
|
+
GROUP BY workspace_id HAVING COUNT(*) > 1
|
|
4009
|
+
)`).get();
|
|
4010
|
+
return row.n;
|
|
4011
|
+
}
|
|
4012
|
+
function enforceOneActiveCollabPerWorkspace(db, options = {}) {
|
|
4013
|
+
const opts = {
|
|
4014
|
+
isPidAlive: options.isPidAlive ?? defaultIsPidAlive,
|
|
4015
|
+
warn: options.warn ?? ((m) => console.error(m))
|
|
4016
|
+
};
|
|
4017
|
+
const tx = db.transaction(() => {
|
|
4018
|
+
dedupeActiveCollabs(db, opts);
|
|
4019
|
+
if (residualDuplicateCount(db) === 0) {
|
|
4020
|
+
db.exec(CREATE_INDEX_SQL);
|
|
4021
|
+
} else {
|
|
4022
|
+
opts.warn("Skipping idx_collab_one_active_per_workspace: an irreducible duplicate active collab remains. The index will be created on a later startup once only one active collab per workspace exists.");
|
|
4023
|
+
}
|
|
4024
|
+
});
|
|
4025
|
+
tx();
|
|
4026
|
+
}
|
|
4027
|
+
|
|
4028
|
+
// ../broker/dist/storage/clipboard-capture-lease.js
|
|
4029
|
+
var LEASE_ID = 1;
|
|
4030
|
+
var DEFAULT_LEASE_TTL_MS = 5e3;
|
|
4031
|
+
function defaultIsPidAlive2(pid) {
|
|
4032
|
+
try {
|
|
4033
|
+
process.kill(pid, 0);
|
|
4034
|
+
return true;
|
|
4035
|
+
} catch (err) {
|
|
4036
|
+
return err.code === "EPERM";
|
|
4037
|
+
}
|
|
4038
|
+
}
|
|
4039
|
+
function resolveOptions(options) {
|
|
4040
|
+
return {
|
|
4041
|
+
isPidAlive: options.isPidAlive ?? defaultIsPidAlive2,
|
|
4042
|
+
ttlMs: options.ttlMs ?? DEFAULT_LEASE_TTL_MS,
|
|
4043
|
+
now: options.now ?? Date.now
|
|
4044
|
+
};
|
|
4045
|
+
}
|
|
4046
|
+
function isStale(row, opts) {
|
|
4047
|
+
if (row.holder_collab_id === null)
|
|
4048
|
+
return true;
|
|
4049
|
+
if (row.holder_pid === null || !opts.isPidAlive(row.holder_pid))
|
|
4050
|
+
return true;
|
|
4051
|
+
if (row.acquired_at === null)
|
|
4052
|
+
return true;
|
|
4053
|
+
const age = opts.now() - Date.parse(row.acquired_at);
|
|
4054
|
+
return age > opts.ttlMs;
|
|
4055
|
+
}
|
|
4056
|
+
function sweepStaleCaptureLease(db, options = {}) {
|
|
4057
|
+
const opts = resolveOptions(options);
|
|
4058
|
+
const tx = db.transaction(() => {
|
|
4059
|
+
const row = db.prepare("SELECT holder_collab_id, holder_pid, acquired_at FROM clipboard_capture_lease WHERE id = ?").get(LEASE_ID);
|
|
4060
|
+
if (!row || row.holder_collab_id === null)
|
|
4061
|
+
return;
|
|
4062
|
+
if (isStale(row, opts)) {
|
|
4063
|
+
db.prepare("UPDATE clipboard_capture_lease SET holder_collab_id = NULL, holder_pid = NULL, acquired_at = NULL WHERE id = ?").run(LEASE_ID);
|
|
4064
|
+
}
|
|
4065
|
+
});
|
|
4066
|
+
tx();
|
|
4067
|
+
}
|
|
4068
|
+
|
|
3944
4069
|
// ../broker/dist/storage/apply-migrations.js
|
|
3945
|
-
var CURRENT_SCHEMA_VERSION =
|
|
4070
|
+
var CURRENT_SCHEMA_VERSION = 5;
|
|
3946
4071
|
var initMigrationSql = `
|
|
3947
4072
|
CREATE TABLE IF NOT EXISTS broker_state (
|
|
3948
4073
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
@@ -4229,6 +4354,13 @@ CREATE TABLE IF NOT EXISTS recovery_state (
|
|
|
4229
4354
|
recovered_at TEXT
|
|
4230
4355
|
);
|
|
4231
4356
|
|
|
4357
|
+
CREATE TABLE IF NOT EXISTS clipboard_capture_lease (
|
|
4358
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
4359
|
+
holder_collab_id TEXT,
|
|
4360
|
+
holder_pid INTEGER,
|
|
4361
|
+
acquired_at TEXT
|
|
4362
|
+
);
|
|
4363
|
+
|
|
4232
4364
|
`;
|
|
4233
4365
|
function ensureBrokerStateRow(db) {
|
|
4234
4366
|
db.prepare(`INSERT INTO broker_state (id, schema_version, migrated)
|
|
@@ -4239,20 +4371,20 @@ function ensureBrokerStateRow(db) {
|
|
|
4239
4371
|
}
|
|
4240
4372
|
function applyMigrations(db) {
|
|
4241
4373
|
const current = db.pragma("user_version", { simple: true });
|
|
4242
|
-
if (current
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
} catch (err) {
|
|
4253
|
-
db.exec("ROLLBACK");
|
|
4254
|
-
throw err;
|
|
4374
|
+
if (current < CURRENT_SCHEMA_VERSION) {
|
|
4375
|
+
db.exec("BEGIN EXCLUSIVE");
|
|
4376
|
+
try {
|
|
4377
|
+
runMigrationBody(db);
|
|
4378
|
+
db.pragma(`user_version = ${CURRENT_SCHEMA_VERSION}`);
|
|
4379
|
+
db.exec("COMMIT");
|
|
4380
|
+
} catch (err) {
|
|
4381
|
+
db.exec("ROLLBACK");
|
|
4382
|
+
throw err;
|
|
4383
|
+
}
|
|
4255
4384
|
}
|
|
4385
|
+
ensureBrokerStateRow(db);
|
|
4386
|
+
enforceOneActiveCollabPerWorkspace(db);
|
|
4387
|
+
sweepStaleCaptureLease(db);
|
|
4256
4388
|
}
|
|
4257
4389
|
function runMigrationBody(db) {
|
|
4258
4390
|
db.exec(initMigrationSql);
|
|
@@ -4389,6 +4521,7 @@ function runMigrationBody(db) {
|
|
|
4389
4521
|
clip_sample TEXT,
|
|
4390
4522
|
turn_sample TEXT,
|
|
4391
4523
|
aborted_by_race_guard INTEGER NOT NULL DEFAULT 0,
|
|
4524
|
+
interference_detected INTEGER NOT NULL DEFAULT 0,
|
|
4392
4525
|
created_at TEXT NOT NULL
|
|
4393
4526
|
);
|
|
4394
4527
|
CREATE INDEX IF NOT EXISTS idx_relay_capture_diagnostics_collab_created
|
|
@@ -4400,6 +4533,10 @@ function runMigrationBody(db) {
|
|
|
4400
4533
|
CREATE INDEX IF NOT EXISTS idx_relay_capture_diagnostics_status
|
|
4401
4534
|
ON relay_capture_diagnostics (capture_status);
|
|
4402
4535
|
`);
|
|
4536
|
+
const captureDiagColumns = db.prepare("PRAGMA table_info(relay_capture_diagnostics)").all();
|
|
4537
|
+
if (!captureDiagColumns.some((column) => column.name === "interference_detected")) {
|
|
4538
|
+
db.exec("ALTER TABLE relay_capture_diagnostics ADD COLUMN interference_detected INTEGER NOT NULL DEFAULT 0");
|
|
4539
|
+
}
|
|
4403
4540
|
db.exec(`
|
|
4404
4541
|
CREATE TABLE IF NOT EXISTS relay_evaluator_diagnostics (
|
|
4405
4542
|
evaluator_id TEXT PRIMARY KEY,
|
|
@@ -1064,6 +1064,7 @@ function rowToRecord(row) {
|
|
|
1064
1064
|
clipSample: row.clip_sample,
|
|
1065
1065
|
turnSample: row.turn_sample,
|
|
1066
1066
|
abortedByRaceGuard: row.aborted_by_race_guard === 1,
|
|
1067
|
+
interferenceDetected: row.interference_detected === 1,
|
|
1067
1068
|
createdAt: row.created_at
|
|
1068
1069
|
};
|
|
1069
1070
|
}
|
|
@@ -1071,8 +1072,9 @@ function insertCaptureDiagnostic(db, input) {
|
|
|
1071
1072
|
db.prepare(`INSERT INTO relay_capture_diagnostics
|
|
1072
1073
|
(capture_id, handoff_id, collab_id, chain_id, workflow_id, target_provider,
|
|
1073
1074
|
capture_status, clip_len, turn_len, turn_confidence, jaccard_score,
|
|
1074
|
-
containment_score, clip_sample, turn_sample, aborted_by_race_guard,
|
|
1075
|
-
|
|
1075
|
+
containment_score, clip_sample, turn_sample, aborted_by_race_guard,
|
|
1076
|
+
interference_detected, created_at)
|
|
1077
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(input.captureId, input.handoffId, input.collabId, input.chainId, input.workflowId, input.targetProvider, input.captureStatus, input.clipLen, input.turnLen, input.turnConfidence, input.jaccardScore, input.containmentScore, input.clipSample, input.turnSample, input.abortedByRaceGuard ? 1 : 0, input.interferenceDetected ? 1 : 0, input.createdAt);
|
|
1076
1078
|
}
|
|
1077
1079
|
function listCaptureDiagnosticsByCollab(db, collabId, limit, opts) {
|
|
1078
1080
|
const filter = opts?.workflowFilter;
|
|
@@ -3898,6 +3900,7 @@ function createControlService(db, events) {
|
|
|
3898
3900
|
clipSample: input.clipSample,
|
|
3899
3901
|
turnSample: input.turnSample,
|
|
3900
3902
|
abortedByRaceGuard: input.abortedByRaceGuard,
|
|
3903
|
+
interferenceDetected: input.interferenceDetected ?? false,
|
|
3901
3904
|
createdAt: input.now
|
|
3902
3905
|
});
|
|
3903
3906
|
return { captureId };
|
|
@@ -3974,8 +3977,130 @@ function createBrokerApp(input) {
|
|
|
3974
3977
|
return app;
|
|
3975
3978
|
}
|
|
3976
3979
|
|
|
3980
|
+
// ../broker/dist/storage/enforce-one-active-collab.js
|
|
3981
|
+
function defaultIsPidAlive(pid) {
|
|
3982
|
+
try {
|
|
3983
|
+
process.kill(pid, 0);
|
|
3984
|
+
return true;
|
|
3985
|
+
} catch (err) {
|
|
3986
|
+
return err.code === "EPERM";
|
|
3987
|
+
}
|
|
3988
|
+
}
|
|
3989
|
+
function dedupeActiveCollabs(db, opts) {
|
|
3990
|
+
const rows = db.prepare(`SELECT c.collab_id, c.workspace_id, c.created_at, d.pid AS pid,
|
|
3991
|
+
(SELECT COUNT(*) FROM workflows w
|
|
3992
|
+
WHERE w.collab_id = c.collab_id AND w.status = 'running') AS running_workflows
|
|
3993
|
+
FROM collab c
|
|
3994
|
+
LEFT JOIN broker_daemon d ON d.collab_id = c.collab_id
|
|
3995
|
+
WHERE c.status = 'active' AND c.workspace_id IS NOT NULL`).all();
|
|
3996
|
+
const byWorkspace = /* @__PURE__ */ new Map();
|
|
3997
|
+
for (const row of rows) {
|
|
3998
|
+
const key = row.workspace_id;
|
|
3999
|
+
const list = byWorkspace.get(key);
|
|
4000
|
+
if (list)
|
|
4001
|
+
list.push(row);
|
|
4002
|
+
else
|
|
4003
|
+
byWorkspace.set(key, [row]);
|
|
4004
|
+
}
|
|
4005
|
+
const conflicted = [];
|
|
4006
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4007
|
+
const stop = db.prepare("UPDATE collab SET status = 'stopped', stopped_at = ?, updated_at = ? WHERE collab_id = ?");
|
|
4008
|
+
for (const [workspaceId, group] of byWorkspace) {
|
|
4009
|
+
if (group.length < 2)
|
|
4010
|
+
continue;
|
|
4011
|
+
const workflowOwners = group.filter((r) => r.running_workflows > 0);
|
|
4012
|
+
if (workflowOwners.length >= 2) {
|
|
4013
|
+
conflicted.push(workspaceId);
|
|
4014
|
+
opts.warn(`Workspace ${workspaceId} has ${workflowOwners.length} active collabs that each own a running workflow: ${workflowOwners.map((r) => r.collab_id).join(", ")}. Refusing to auto-stop any (that would orphan a live run). Stop the extra collab(s) manually with \`whisper collab stop --collab <id>\`. The one-active-collab-per-workspace index will be created automatically once only one remains.`);
|
|
4015
|
+
continue;
|
|
4016
|
+
}
|
|
4017
|
+
let survivor;
|
|
4018
|
+
if (workflowOwners.length === 1) {
|
|
4019
|
+
survivor = workflowOwners[0];
|
|
4020
|
+
} else {
|
|
4021
|
+
const live = group.filter((r) => r.pid !== null && opts.isPidAlive(r.pid));
|
|
4022
|
+
if (live.length >= 1) {
|
|
4023
|
+
survivor = live.reduce((a, b) => a.created_at >= b.created_at ? a : b);
|
|
4024
|
+
} else {
|
|
4025
|
+
survivor = group.reduce((a, b) => a.created_at >= b.created_at ? a : b);
|
|
4026
|
+
}
|
|
4027
|
+
}
|
|
4028
|
+
for (const row of group) {
|
|
4029
|
+
if (row.collab_id === survivor.collab_id)
|
|
4030
|
+
continue;
|
|
4031
|
+
stop.run(now, now, row.collab_id);
|
|
4032
|
+
}
|
|
4033
|
+
}
|
|
4034
|
+
return conflicted;
|
|
4035
|
+
}
|
|
4036
|
+
var CREATE_INDEX_SQL = "CREATE UNIQUE INDEX IF NOT EXISTS idx_collab_one_active_per_workspace ON collab(workspace_id) WHERE status = 'active'";
|
|
4037
|
+
function residualDuplicateCount(db) {
|
|
4038
|
+
const row = db.prepare(`SELECT COUNT(*) AS n FROM (
|
|
4039
|
+
SELECT workspace_id FROM collab
|
|
4040
|
+
WHERE status = 'active' AND workspace_id IS NOT NULL
|
|
4041
|
+
GROUP BY workspace_id HAVING COUNT(*) > 1
|
|
4042
|
+
)`).get();
|
|
4043
|
+
return row.n;
|
|
4044
|
+
}
|
|
4045
|
+
function enforceOneActiveCollabPerWorkspace(db, options = {}) {
|
|
4046
|
+
const opts = {
|
|
4047
|
+
isPidAlive: options.isPidAlive ?? defaultIsPidAlive,
|
|
4048
|
+
warn: options.warn ?? ((m) => console.error(m))
|
|
4049
|
+
};
|
|
4050
|
+
const tx = db.transaction(() => {
|
|
4051
|
+
dedupeActiveCollabs(db, opts);
|
|
4052
|
+
if (residualDuplicateCount(db) === 0) {
|
|
4053
|
+
db.exec(CREATE_INDEX_SQL);
|
|
4054
|
+
} else {
|
|
4055
|
+
opts.warn("Skipping idx_collab_one_active_per_workspace: an irreducible duplicate active collab remains. The index will be created on a later startup once only one active collab per workspace exists.");
|
|
4056
|
+
}
|
|
4057
|
+
});
|
|
4058
|
+
tx();
|
|
4059
|
+
}
|
|
4060
|
+
|
|
4061
|
+
// ../broker/dist/storage/clipboard-capture-lease.js
|
|
4062
|
+
var LEASE_ID = 1;
|
|
4063
|
+
var DEFAULT_LEASE_TTL_MS = 5e3;
|
|
4064
|
+
function defaultIsPidAlive2(pid) {
|
|
4065
|
+
try {
|
|
4066
|
+
process.kill(pid, 0);
|
|
4067
|
+
return true;
|
|
4068
|
+
} catch (err) {
|
|
4069
|
+
return err.code === "EPERM";
|
|
4070
|
+
}
|
|
4071
|
+
}
|
|
4072
|
+
function resolveOptions(options) {
|
|
4073
|
+
return {
|
|
4074
|
+
isPidAlive: options.isPidAlive ?? defaultIsPidAlive2,
|
|
4075
|
+
ttlMs: options.ttlMs ?? DEFAULT_LEASE_TTL_MS,
|
|
4076
|
+
now: options.now ?? Date.now
|
|
4077
|
+
};
|
|
4078
|
+
}
|
|
4079
|
+
function isStale(row, opts) {
|
|
4080
|
+
if (row.holder_collab_id === null)
|
|
4081
|
+
return true;
|
|
4082
|
+
if (row.holder_pid === null || !opts.isPidAlive(row.holder_pid))
|
|
4083
|
+
return true;
|
|
4084
|
+
if (row.acquired_at === null)
|
|
4085
|
+
return true;
|
|
4086
|
+
const age = opts.now() - Date.parse(row.acquired_at);
|
|
4087
|
+
return age > opts.ttlMs;
|
|
4088
|
+
}
|
|
4089
|
+
function sweepStaleCaptureLease(db, options = {}) {
|
|
4090
|
+
const opts = resolveOptions(options);
|
|
4091
|
+
const tx = db.transaction(() => {
|
|
4092
|
+
const row = db.prepare("SELECT holder_collab_id, holder_pid, acquired_at FROM clipboard_capture_lease WHERE id = ?").get(LEASE_ID);
|
|
4093
|
+
if (!row || row.holder_collab_id === null)
|
|
4094
|
+
return;
|
|
4095
|
+
if (isStale(row, opts)) {
|
|
4096
|
+
db.prepare("UPDATE clipboard_capture_lease SET holder_collab_id = NULL, holder_pid = NULL, acquired_at = NULL WHERE id = ?").run(LEASE_ID);
|
|
4097
|
+
}
|
|
4098
|
+
});
|
|
4099
|
+
tx();
|
|
4100
|
+
}
|
|
4101
|
+
|
|
3977
4102
|
// ../broker/dist/storage/apply-migrations.js
|
|
3978
|
-
var CURRENT_SCHEMA_VERSION =
|
|
4103
|
+
var CURRENT_SCHEMA_VERSION = 5;
|
|
3979
4104
|
var initMigrationSql = `
|
|
3980
4105
|
CREATE TABLE IF NOT EXISTS broker_state (
|
|
3981
4106
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
@@ -4262,6 +4387,13 @@ CREATE TABLE IF NOT EXISTS recovery_state (
|
|
|
4262
4387
|
recovered_at TEXT
|
|
4263
4388
|
);
|
|
4264
4389
|
|
|
4390
|
+
CREATE TABLE IF NOT EXISTS clipboard_capture_lease (
|
|
4391
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
4392
|
+
holder_collab_id TEXT,
|
|
4393
|
+
holder_pid INTEGER,
|
|
4394
|
+
acquired_at TEXT
|
|
4395
|
+
);
|
|
4396
|
+
|
|
4265
4397
|
`;
|
|
4266
4398
|
function ensureBrokerStateRow(db) {
|
|
4267
4399
|
db.prepare(`INSERT INTO broker_state (id, schema_version, migrated)
|
|
@@ -4272,20 +4404,20 @@ function ensureBrokerStateRow(db) {
|
|
|
4272
4404
|
}
|
|
4273
4405
|
function applyMigrations(db) {
|
|
4274
4406
|
const current = db.pragma("user_version", { simple: true });
|
|
4275
|
-
if (current
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
} catch (err) {
|
|
4286
|
-
db.exec("ROLLBACK");
|
|
4287
|
-
throw err;
|
|
4407
|
+
if (current < CURRENT_SCHEMA_VERSION) {
|
|
4408
|
+
db.exec("BEGIN EXCLUSIVE");
|
|
4409
|
+
try {
|
|
4410
|
+
runMigrationBody(db);
|
|
4411
|
+
db.pragma(`user_version = ${CURRENT_SCHEMA_VERSION}`);
|
|
4412
|
+
db.exec("COMMIT");
|
|
4413
|
+
} catch (err) {
|
|
4414
|
+
db.exec("ROLLBACK");
|
|
4415
|
+
throw err;
|
|
4416
|
+
}
|
|
4288
4417
|
}
|
|
4418
|
+
ensureBrokerStateRow(db);
|
|
4419
|
+
enforceOneActiveCollabPerWorkspace(db);
|
|
4420
|
+
sweepStaleCaptureLease(db);
|
|
4289
4421
|
}
|
|
4290
4422
|
function runMigrationBody(db) {
|
|
4291
4423
|
db.exec(initMigrationSql);
|
|
@@ -4422,6 +4554,7 @@ function runMigrationBody(db) {
|
|
|
4422
4554
|
clip_sample TEXT,
|
|
4423
4555
|
turn_sample TEXT,
|
|
4424
4556
|
aborted_by_race_guard INTEGER NOT NULL DEFAULT 0,
|
|
4557
|
+
interference_detected INTEGER NOT NULL DEFAULT 0,
|
|
4425
4558
|
created_at TEXT NOT NULL
|
|
4426
4559
|
);
|
|
4427
4560
|
CREATE INDEX IF NOT EXISTS idx_relay_capture_diagnostics_collab_created
|
|
@@ -4433,6 +4566,10 @@ function runMigrationBody(db) {
|
|
|
4433
4566
|
CREATE INDEX IF NOT EXISTS idx_relay_capture_diagnostics_status
|
|
4434
4567
|
ON relay_capture_diagnostics (capture_status);
|
|
4435
4568
|
`);
|
|
4569
|
+
const captureDiagColumns = db.prepare("PRAGMA table_info(relay_capture_diagnostics)").all();
|
|
4570
|
+
if (!captureDiagColumns.some((column) => column.name === "interference_detected")) {
|
|
4571
|
+
db.exec("ALTER TABLE relay_capture_diagnostics ADD COLUMN interference_detected INTEGER NOT NULL DEFAULT 0");
|
|
4572
|
+
}
|
|
4436
4573
|
db.exec(`
|
|
4437
4574
|
CREATE TABLE IF NOT EXISTS relay_evaluator_diagnostics (
|
|
4438
4575
|
evaluator_id TEXT PRIMARY KEY,
|
|
@@ -1034,6 +1034,7 @@ function rowToRecord(row) {
|
|
|
1034
1034
|
clipSample: row.clip_sample,
|
|
1035
1035
|
turnSample: row.turn_sample,
|
|
1036
1036
|
abortedByRaceGuard: row.aborted_by_race_guard === 1,
|
|
1037
|
+
interferenceDetected: row.interference_detected === 1,
|
|
1037
1038
|
createdAt: row.created_at
|
|
1038
1039
|
};
|
|
1039
1040
|
}
|
|
@@ -1041,8 +1042,9 @@ function insertCaptureDiagnostic(db, input) {
|
|
|
1041
1042
|
db.prepare(`INSERT INTO relay_capture_diagnostics
|
|
1042
1043
|
(capture_id, handoff_id, collab_id, chain_id, workflow_id, target_provider,
|
|
1043
1044
|
capture_status, clip_len, turn_len, turn_confidence, jaccard_score,
|
|
1044
|
-
containment_score, clip_sample, turn_sample, aborted_by_race_guard,
|
|
1045
|
-
|
|
1045
|
+
containment_score, clip_sample, turn_sample, aborted_by_race_guard,
|
|
1046
|
+
interference_detected, created_at)
|
|
1047
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(input.captureId, input.handoffId, input.collabId, input.chainId, input.workflowId, input.targetProvider, input.captureStatus, input.clipLen, input.turnLen, input.turnConfidence, input.jaccardScore, input.containmentScore, input.clipSample, input.turnSample, input.abortedByRaceGuard ? 1 : 0, input.interferenceDetected ? 1 : 0, input.createdAt);
|
|
1046
1048
|
}
|
|
1047
1049
|
function listCaptureDiagnosticsByCollab(db, collabId2, limit, opts) {
|
|
1048
1050
|
const filter = opts?.workflowFilter;
|
|
@@ -3868,6 +3870,7 @@ function createControlService(db, events) {
|
|
|
3868
3870
|
clipSample: input.clipSample,
|
|
3869
3871
|
turnSample: input.turnSample,
|
|
3870
3872
|
abortedByRaceGuard: input.abortedByRaceGuard,
|
|
3873
|
+
interferenceDetected: input.interferenceDetected ?? false,
|
|
3871
3874
|
createdAt: input.now
|
|
3872
3875
|
});
|
|
3873
3876
|
return { captureId };
|
|
@@ -3944,8 +3947,130 @@ function createBrokerApp(input) {
|
|
|
3944
3947
|
return app;
|
|
3945
3948
|
}
|
|
3946
3949
|
|
|
3950
|
+
// ../broker/dist/storage/enforce-one-active-collab.js
|
|
3951
|
+
function defaultIsPidAlive(pid) {
|
|
3952
|
+
try {
|
|
3953
|
+
process.kill(pid, 0);
|
|
3954
|
+
return true;
|
|
3955
|
+
} catch (err) {
|
|
3956
|
+
return err.code === "EPERM";
|
|
3957
|
+
}
|
|
3958
|
+
}
|
|
3959
|
+
function dedupeActiveCollabs(db, opts) {
|
|
3960
|
+
const rows = db.prepare(`SELECT c.collab_id, c.workspace_id, c.created_at, d.pid AS pid,
|
|
3961
|
+
(SELECT COUNT(*) FROM workflows w
|
|
3962
|
+
WHERE w.collab_id = c.collab_id AND w.status = 'running') AS running_workflows
|
|
3963
|
+
FROM collab c
|
|
3964
|
+
LEFT JOIN broker_daemon d ON d.collab_id = c.collab_id
|
|
3965
|
+
WHERE c.status = 'active' AND c.workspace_id IS NOT NULL`).all();
|
|
3966
|
+
const byWorkspace = /* @__PURE__ */ new Map();
|
|
3967
|
+
for (const row of rows) {
|
|
3968
|
+
const key = row.workspace_id;
|
|
3969
|
+
const list = byWorkspace.get(key);
|
|
3970
|
+
if (list)
|
|
3971
|
+
list.push(row);
|
|
3972
|
+
else
|
|
3973
|
+
byWorkspace.set(key, [row]);
|
|
3974
|
+
}
|
|
3975
|
+
const conflicted = [];
|
|
3976
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3977
|
+
const stop = db.prepare("UPDATE collab SET status = 'stopped', stopped_at = ?, updated_at = ? WHERE collab_id = ?");
|
|
3978
|
+
for (const [workspaceId, group] of byWorkspace) {
|
|
3979
|
+
if (group.length < 2)
|
|
3980
|
+
continue;
|
|
3981
|
+
const workflowOwners = group.filter((r) => r.running_workflows > 0);
|
|
3982
|
+
if (workflowOwners.length >= 2) {
|
|
3983
|
+
conflicted.push(workspaceId);
|
|
3984
|
+
opts.warn(`Workspace ${workspaceId} has ${workflowOwners.length} active collabs that each own a running workflow: ${workflowOwners.map((r) => r.collab_id).join(", ")}. Refusing to auto-stop any (that would orphan a live run). Stop the extra collab(s) manually with \`whisper collab stop --collab <id>\`. The one-active-collab-per-workspace index will be created automatically once only one remains.`);
|
|
3985
|
+
continue;
|
|
3986
|
+
}
|
|
3987
|
+
let survivor;
|
|
3988
|
+
if (workflowOwners.length === 1) {
|
|
3989
|
+
survivor = workflowOwners[0];
|
|
3990
|
+
} else {
|
|
3991
|
+
const live = group.filter((r) => r.pid !== null && opts.isPidAlive(r.pid));
|
|
3992
|
+
if (live.length >= 1) {
|
|
3993
|
+
survivor = live.reduce((a, b) => a.created_at >= b.created_at ? a : b);
|
|
3994
|
+
} else {
|
|
3995
|
+
survivor = group.reduce((a, b) => a.created_at >= b.created_at ? a : b);
|
|
3996
|
+
}
|
|
3997
|
+
}
|
|
3998
|
+
for (const row of group) {
|
|
3999
|
+
if (row.collab_id === survivor.collab_id)
|
|
4000
|
+
continue;
|
|
4001
|
+
stop.run(now, now, row.collab_id);
|
|
4002
|
+
}
|
|
4003
|
+
}
|
|
4004
|
+
return conflicted;
|
|
4005
|
+
}
|
|
4006
|
+
var CREATE_INDEX_SQL = "CREATE UNIQUE INDEX IF NOT EXISTS idx_collab_one_active_per_workspace ON collab(workspace_id) WHERE status = 'active'";
|
|
4007
|
+
function residualDuplicateCount(db) {
|
|
4008
|
+
const row = db.prepare(`SELECT COUNT(*) AS n FROM (
|
|
4009
|
+
SELECT workspace_id FROM collab
|
|
4010
|
+
WHERE status = 'active' AND workspace_id IS NOT NULL
|
|
4011
|
+
GROUP BY workspace_id HAVING COUNT(*) > 1
|
|
4012
|
+
)`).get();
|
|
4013
|
+
return row.n;
|
|
4014
|
+
}
|
|
4015
|
+
function enforceOneActiveCollabPerWorkspace(db, options = {}) {
|
|
4016
|
+
const opts = {
|
|
4017
|
+
isPidAlive: options.isPidAlive ?? defaultIsPidAlive,
|
|
4018
|
+
warn: options.warn ?? ((m) => console.error(m))
|
|
4019
|
+
};
|
|
4020
|
+
const tx = db.transaction(() => {
|
|
4021
|
+
dedupeActiveCollabs(db, opts);
|
|
4022
|
+
if (residualDuplicateCount(db) === 0) {
|
|
4023
|
+
db.exec(CREATE_INDEX_SQL);
|
|
4024
|
+
} else {
|
|
4025
|
+
opts.warn("Skipping idx_collab_one_active_per_workspace: an irreducible duplicate active collab remains. The index will be created on a later startup once only one active collab per workspace exists.");
|
|
4026
|
+
}
|
|
4027
|
+
});
|
|
4028
|
+
tx();
|
|
4029
|
+
}
|
|
4030
|
+
|
|
4031
|
+
// ../broker/dist/storage/clipboard-capture-lease.js
|
|
4032
|
+
var LEASE_ID = 1;
|
|
4033
|
+
var DEFAULT_LEASE_TTL_MS = 5e3;
|
|
4034
|
+
function defaultIsPidAlive2(pid) {
|
|
4035
|
+
try {
|
|
4036
|
+
process.kill(pid, 0);
|
|
4037
|
+
return true;
|
|
4038
|
+
} catch (err) {
|
|
4039
|
+
return err.code === "EPERM";
|
|
4040
|
+
}
|
|
4041
|
+
}
|
|
4042
|
+
function resolveOptions(options) {
|
|
4043
|
+
return {
|
|
4044
|
+
isPidAlive: options.isPidAlive ?? defaultIsPidAlive2,
|
|
4045
|
+
ttlMs: options.ttlMs ?? DEFAULT_LEASE_TTL_MS,
|
|
4046
|
+
now: options.now ?? Date.now
|
|
4047
|
+
};
|
|
4048
|
+
}
|
|
4049
|
+
function isStale(row, opts) {
|
|
4050
|
+
if (row.holder_collab_id === null)
|
|
4051
|
+
return true;
|
|
4052
|
+
if (row.holder_pid === null || !opts.isPidAlive(row.holder_pid))
|
|
4053
|
+
return true;
|
|
4054
|
+
if (row.acquired_at === null)
|
|
4055
|
+
return true;
|
|
4056
|
+
const age = opts.now() - Date.parse(row.acquired_at);
|
|
4057
|
+
return age > opts.ttlMs;
|
|
4058
|
+
}
|
|
4059
|
+
function sweepStaleCaptureLease(db, options = {}) {
|
|
4060
|
+
const opts = resolveOptions(options);
|
|
4061
|
+
const tx = db.transaction(() => {
|
|
4062
|
+
const row = db.prepare("SELECT holder_collab_id, holder_pid, acquired_at FROM clipboard_capture_lease WHERE id = ?").get(LEASE_ID);
|
|
4063
|
+
if (!row || row.holder_collab_id === null)
|
|
4064
|
+
return;
|
|
4065
|
+
if (isStale(row, opts)) {
|
|
4066
|
+
db.prepare("UPDATE clipboard_capture_lease SET holder_collab_id = NULL, holder_pid = NULL, acquired_at = NULL WHERE id = ?").run(LEASE_ID);
|
|
4067
|
+
}
|
|
4068
|
+
});
|
|
4069
|
+
tx();
|
|
4070
|
+
}
|
|
4071
|
+
|
|
3947
4072
|
// ../broker/dist/storage/apply-migrations.js
|
|
3948
|
-
var CURRENT_SCHEMA_VERSION =
|
|
4073
|
+
var CURRENT_SCHEMA_VERSION = 5;
|
|
3949
4074
|
var initMigrationSql = `
|
|
3950
4075
|
CREATE TABLE IF NOT EXISTS broker_state (
|
|
3951
4076
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
@@ -4232,6 +4357,13 @@ CREATE TABLE IF NOT EXISTS recovery_state (
|
|
|
4232
4357
|
recovered_at TEXT
|
|
4233
4358
|
);
|
|
4234
4359
|
|
|
4360
|
+
CREATE TABLE IF NOT EXISTS clipboard_capture_lease (
|
|
4361
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
4362
|
+
holder_collab_id TEXT,
|
|
4363
|
+
holder_pid INTEGER,
|
|
4364
|
+
acquired_at TEXT
|
|
4365
|
+
);
|
|
4366
|
+
|
|
4235
4367
|
`;
|
|
4236
4368
|
function ensureBrokerStateRow(db) {
|
|
4237
4369
|
db.prepare(`INSERT INTO broker_state (id, schema_version, migrated)
|
|
@@ -4242,20 +4374,20 @@ function ensureBrokerStateRow(db) {
|
|
|
4242
4374
|
}
|
|
4243
4375
|
function applyMigrations(db) {
|
|
4244
4376
|
const current = db.pragma("user_version", { simple: true });
|
|
4245
|
-
if (current
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
} catch (err) {
|
|
4256
|
-
db.exec("ROLLBACK");
|
|
4257
|
-
throw err;
|
|
4377
|
+
if (current < CURRENT_SCHEMA_VERSION) {
|
|
4378
|
+
db.exec("BEGIN EXCLUSIVE");
|
|
4379
|
+
try {
|
|
4380
|
+
runMigrationBody(db);
|
|
4381
|
+
db.pragma(`user_version = ${CURRENT_SCHEMA_VERSION}`);
|
|
4382
|
+
db.exec("COMMIT");
|
|
4383
|
+
} catch (err) {
|
|
4384
|
+
db.exec("ROLLBACK");
|
|
4385
|
+
throw err;
|
|
4386
|
+
}
|
|
4258
4387
|
}
|
|
4388
|
+
ensureBrokerStateRow(db);
|
|
4389
|
+
enforceOneActiveCollabPerWorkspace(db);
|
|
4390
|
+
sweepStaleCaptureLease(db);
|
|
4259
4391
|
}
|
|
4260
4392
|
function runMigrationBody(db) {
|
|
4261
4393
|
db.exec(initMigrationSql);
|
|
@@ -4392,6 +4524,7 @@ function runMigrationBody(db) {
|
|
|
4392
4524
|
clip_sample TEXT,
|
|
4393
4525
|
turn_sample TEXT,
|
|
4394
4526
|
aborted_by_race_guard INTEGER NOT NULL DEFAULT 0,
|
|
4527
|
+
interference_detected INTEGER NOT NULL DEFAULT 0,
|
|
4395
4528
|
created_at TEXT NOT NULL
|
|
4396
4529
|
);
|
|
4397
4530
|
CREATE INDEX IF NOT EXISTS idx_relay_capture_diagnostics_collab_created
|
|
@@ -4403,6 +4536,10 @@ function runMigrationBody(db) {
|
|
|
4403
4536
|
CREATE INDEX IF NOT EXISTS idx_relay_capture_diagnostics_status
|
|
4404
4537
|
ON relay_capture_diagnostics (capture_status);
|
|
4405
4538
|
`);
|
|
4539
|
+
const captureDiagColumns = db.prepare("PRAGMA table_info(relay_capture_diagnostics)").all();
|
|
4540
|
+
if (!captureDiagColumns.some((column) => column.name === "interference_detected")) {
|
|
4541
|
+
db.exec("ALTER TABLE relay_capture_diagnostics ADD COLUMN interference_detected INTEGER NOT NULL DEFAULT 0");
|
|
4542
|
+
}
|
|
4406
4543
|
db.exec(`
|
|
4407
4544
|
CREATE TABLE IF NOT EXISTS relay_evaluator_diagnostics (
|
|
4408
4545
|
evaluator_id TEXT PRIMARY KEY,
|