ai-whisper 0.6.0 → 0.6.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/README.md +2 -0
- package/dist/bin/broker-daemon.js +1 -1
- package/dist/bin/companion-agent.js +1 -1
- package/dist/bin/relay-monitor.js +1 -1
- package/dist/bin/whisper.js +126 -37
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -61,6 +61,8 @@ You pair any two of three agents — `claude`, `codex`, and `ezio`. ai-whisper d
|
|
|
61
61
|
- **An LLM evaluator with credentials** — workflows are gated by it and refuse to start without it. See [Evaluator configuration](docs/evaluator-configuration.md).
|
|
62
62
|
- **tmux** *(optional)* — only for `whisper collab start`, which auto-arranges both agents into panes. The mount flow below does not need it.
|
|
63
63
|
|
|
64
|
+
> **Platform support:** ai-whisper is terminal-native and Unix-oriented — it drives interactive PTY sessions, so it runs on **macOS and Linux**. It is **not supported natively on Windows**: `whisper collab mount` / `reconnect` require a Unix tty-backed shell and will exit with an error pointing here. On Windows, run ai-whisper inside **[WSL2](https://learn.microsoft.com/windows/wsl/install)** — install Node, your agent CLI, and ai-whisper inside the WSL2 distro and run the commands there, where everything works as-is.
|
|
65
|
+
|
|
64
66
|
## Safety & permissions
|
|
65
67
|
|
|
66
68
|
ai-whisper launches each agent in **full-autonomy mode** so the relay can drive it unattended — `claude --dangerously-skip-permissions` and `codex --dangerously-bypass-approvals-and-sandbox`. Inside the mounted workspace the agents read, write, and run commands without prompting. Point it at code you're willing to let two agents change autonomously, watch the run on the dashboard, and remember you are the final gatekeeper — review the result before you ship it. The deeper rationale is in [Concepts](docs/concepts.md).
|
|
@@ -4386,7 +4386,7 @@ function enforceOneActiveCollabPerWorkspace(db, options = {}) {
|
|
|
4386
4386
|
|
|
4387
4387
|
// ../broker/dist/storage/clipboard-capture-lease.js
|
|
4388
4388
|
var LEASE_ID = 1;
|
|
4389
|
-
var DEFAULT_LEASE_TTL_MS =
|
|
4389
|
+
var DEFAULT_LEASE_TTL_MS = 25e3;
|
|
4390
4390
|
function defaultIsPidAlive2(pid) {
|
|
4391
4391
|
try {
|
|
4392
4392
|
process.kill(pid, 0);
|
|
@@ -4406,7 +4406,7 @@ function enforceOneActiveCollabPerWorkspace(db, options = {}) {
|
|
|
4406
4406
|
|
|
4407
4407
|
// ../broker/dist/storage/clipboard-capture-lease.js
|
|
4408
4408
|
var LEASE_ID = 1;
|
|
4409
|
-
var DEFAULT_LEASE_TTL_MS =
|
|
4409
|
+
var DEFAULT_LEASE_TTL_MS = 25e3;
|
|
4410
4410
|
function defaultIsPidAlive2(pid) {
|
|
4411
4411
|
try {
|
|
4412
4412
|
process.kill(pid, 0);
|
|
@@ -4376,7 +4376,7 @@ function enforceOneActiveCollabPerWorkspace(db, options = {}) {
|
|
|
4376
4376
|
|
|
4377
4377
|
// ../broker/dist/storage/clipboard-capture-lease.js
|
|
4378
4378
|
var LEASE_ID = 1;
|
|
4379
|
-
var DEFAULT_LEASE_TTL_MS =
|
|
4379
|
+
var DEFAULT_LEASE_TTL_MS = 25e3;
|
|
4380
4380
|
function defaultIsPidAlive2(pid) {
|
|
4381
4381
|
try {
|
|
4382
4382
|
process.kill(pid, 0);
|
package/dist/bin/whisper.js
CHANGED
|
@@ -4877,7 +4877,7 @@ function acquireCaptureLease(db, collabId, pid, options = {}) {
|
|
|
4877
4877
|
const tx = db.transaction(() => {
|
|
4878
4878
|
const row = db.prepare("SELECT holder_collab_id, holder_pid, acquired_at FROM clipboard_capture_lease WHERE id = ?").get(LEASE_ID);
|
|
4879
4879
|
if (row && !isStale(row, opts))
|
|
4880
|
-
return
|
|
4880
|
+
return null;
|
|
4881
4881
|
const acquiredAt = new Date(opts.now()).toISOString();
|
|
4882
4882
|
db.prepare(`INSERT INTO clipboard_capture_lease (id, holder_collab_id, holder_pid, acquired_at)
|
|
4883
4883
|
VALUES (?, ?, ?, ?)
|
|
@@ -4885,13 +4885,19 @@ function acquireCaptureLease(db, collabId, pid, options = {}) {
|
|
|
4885
4885
|
holder_collab_id = excluded.holder_collab_id,
|
|
4886
4886
|
holder_pid = excluded.holder_pid,
|
|
4887
4887
|
acquired_at = excluded.acquired_at`).run(LEASE_ID, collabId, pid, acquiredAt);
|
|
4888
|
-
return
|
|
4888
|
+
return acquiredAt;
|
|
4889
4889
|
});
|
|
4890
4890
|
return tx.immediate();
|
|
4891
4891
|
}
|
|
4892
|
-
function releaseCaptureLease(db, collabId) {
|
|
4892
|
+
function releaseCaptureLease(db, collabId, token) {
|
|
4893
|
+
const tx = db.transaction(() => {
|
|
4894
|
+
db.prepare("UPDATE clipboard_capture_lease SET holder_collab_id = NULL, holder_pid = NULL, acquired_at = NULL WHERE id = ? AND holder_collab_id = ? AND acquired_at = ?").run(LEASE_ID, collabId, token);
|
|
4895
|
+
});
|
|
4896
|
+
tx();
|
|
4897
|
+
}
|
|
4898
|
+
function releaseCaptureLeaseForHolderPid(db, collabId, pid) {
|
|
4893
4899
|
const tx = db.transaction(() => {
|
|
4894
|
-
db.prepare("UPDATE clipboard_capture_lease SET holder_collab_id = NULL, holder_pid = NULL, acquired_at = NULL WHERE id = ? AND holder_collab_id = ?").run(LEASE_ID, collabId);
|
|
4900
|
+
db.prepare("UPDATE clipboard_capture_lease SET holder_collab_id = NULL, holder_pid = NULL, acquired_at = NULL WHERE id = ? AND holder_collab_id = ? AND holder_pid = ?").run(LEASE_ID, collabId, pid);
|
|
4895
4901
|
});
|
|
4896
4902
|
tx();
|
|
4897
4903
|
}
|
|
@@ -4912,7 +4918,7 @@ var init_clipboard_capture_lease = __esm({
|
|
|
4912
4918
|
"../broker/dist/storage/clipboard-capture-lease.js"() {
|
|
4913
4919
|
"use strict";
|
|
4914
4920
|
LEASE_ID = 1;
|
|
4915
|
-
DEFAULT_LEASE_TTL_MS =
|
|
4921
|
+
DEFAULT_LEASE_TTL_MS = 25e3;
|
|
4916
4922
|
}
|
|
4917
4923
|
});
|
|
4918
4924
|
|
|
@@ -8006,6 +8012,11 @@ function lookupByCwd(db, cwd) {
|
|
|
8006
8012
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
8007
8013
|
import { existsSync as existsSync6 } from "node:fs";
|
|
8008
8014
|
function resolveCurrentTty() {
|
|
8015
|
+
if (process.platform === "win32") {
|
|
8016
|
+
throw new Error(
|
|
8017
|
+
"This command is not supported natively on Windows: it requires a Unix tty-backed shell to drive an interactive PTY session. Run ai-whisper inside WSL2 (Windows Subsystem for Linux) instead \u2014 install Node, your agent CLI, and ai-whisper inside your WSL2 distro and run the command there. Setup guide: https://learn.microsoft.com/windows/wsl/install"
|
|
8018
|
+
);
|
|
8019
|
+
}
|
|
8009
8020
|
const stdin = process.stdin;
|
|
8010
8021
|
const ttyPath = stdin.isTTY && typeof stdin.path === "string" ? stdin.path : execFileSync2("tty", [], {
|
|
8011
8022
|
encoding: "utf8",
|
|
@@ -12603,8 +12614,8 @@ function compareSemver(a, b) {
|
|
|
12603
12614
|
var EZIO_PROVENANCE = {
|
|
12604
12615
|
ezioCliVersion: "0.2.0-beta.5",
|
|
12605
12616
|
ezioGitSha: "a701ee7",
|
|
12606
|
-
builtAt: "2026-06-
|
|
12607
|
-
whisperVersion: "0.6.
|
|
12617
|
+
builtAt: "2026-06-21T14:21:01.635Z",
|
|
12618
|
+
whisperVersion: "0.6.1"
|
|
12608
12619
|
};
|
|
12609
12620
|
|
|
12610
12621
|
// src/ezio-provenance-types.ts
|
|
@@ -12904,6 +12915,22 @@ function isAutonomousHandoff(handoffId, broker) {
|
|
|
12904
12915
|
const chain = broker.control.getRelayChain?.(meta.chainId);
|
|
12905
12916
|
return wf?.status === "running" && chain?.status === "active";
|
|
12906
12917
|
}
|
|
12918
|
+
var CAPTURE_WATCHDOG_TIMEOUT = /* @__PURE__ */ Symbol("capture-watchdog-timeout");
|
|
12919
|
+
async function raceCaptureAgainstWatchdog(run, watchdogMs, schedule) {
|
|
12920
|
+
let cancel = () => {
|
|
12921
|
+
};
|
|
12922
|
+
const watchdog = new Promise((resolve5) => {
|
|
12923
|
+
cancel = schedule(watchdogMs, () => resolve5(CAPTURE_WATCHDOG_TIMEOUT));
|
|
12924
|
+
});
|
|
12925
|
+
try {
|
|
12926
|
+
return await Promise.race([
|
|
12927
|
+
Promise.resolve(run()).then((r) => r ?? null),
|
|
12928
|
+
watchdog
|
|
12929
|
+
]);
|
|
12930
|
+
} finally {
|
|
12931
|
+
cancel();
|
|
12932
|
+
}
|
|
12933
|
+
}
|
|
12907
12934
|
function createMountedTurnOwnedRelay(input) {
|
|
12908
12935
|
function resolveCols() {
|
|
12909
12936
|
const c = input.getTerminalCols?.() ?? process.stdout.columns;
|
|
@@ -12913,6 +12940,11 @@ function createMountedTurnOwnedRelay(input) {
|
|
|
12913
12940
|
const HAND_BACK_READY_AFTER_MS = 3e4;
|
|
12914
12941
|
const autoHandbackMaxAttempts = Math.max(1, input.autoHandbackMaxAttempts ?? 3);
|
|
12915
12942
|
const autoHandbackRetryMs = Math.max(0, input.autoHandbackRetryMs ?? 1e4);
|
|
12943
|
+
const captureWatchdogMs2 = Math.max(1, input.captureWatchdogMs ?? 2e4);
|
|
12944
|
+
const scheduleWatchdog = input.scheduleWatchdog ?? ((ms, onTimeout) => {
|
|
12945
|
+
const timer = setTimeout(onTimeout, ms);
|
|
12946
|
+
return () => clearTimeout(timer);
|
|
12947
|
+
});
|
|
12916
12948
|
const autoHandbackAttempts = /* @__PURE__ */ new Map();
|
|
12917
12949
|
let autoHandbackInFlight = false;
|
|
12918
12950
|
let disconnectHandled = false;
|
|
@@ -13533,15 +13565,30 @@ ${hint}`,
|
|
|
13533
13565
|
let leaseDegraded = false;
|
|
13534
13566
|
let interferenceDetected = false;
|
|
13535
13567
|
let cleanCaptureEmpty = false;
|
|
13568
|
+
let captureTimedOut = false;
|
|
13536
13569
|
try {
|
|
13537
|
-
const captureResult = await
|
|
13538
|
-
|
|
13570
|
+
const captureResult = await raceCaptureAgainstWatchdog(
|
|
13571
|
+
() => input.captureHandbackText?.(turnResult.text ?? ""),
|
|
13572
|
+
captureWatchdogMs2,
|
|
13573
|
+
scheduleWatchdog
|
|
13574
|
+
);
|
|
13575
|
+
if (captureResult === CAPTURE_WATCHDOG_TIMEOUT) {
|
|
13576
|
+
captureTimedOut = true;
|
|
13577
|
+
console.warn(
|
|
13578
|
+
`[ai-whisper] capture watchdog fired (${captureWatchdogMs2}ms): target=${input.currentAgent} handoff=${accepted.handoffId} \u2014 abandoning capture, will retry`
|
|
13579
|
+
);
|
|
13580
|
+
} else if (typeof captureResult === "string") {
|
|
13539
13581
|
clipboardText = captureResult;
|
|
13540
13582
|
} else if (captureResult !== null) {
|
|
13541
13583
|
interferenceDetected = captureResult.interferenceDetected;
|
|
13542
13584
|
if (captureResult.status === "captured") {
|
|
13543
13585
|
clipboardText = captureResult.text;
|
|
13544
13586
|
cleanCaptureEmpty = (captureResult.text ?? "").trim().length === 0;
|
|
13587
|
+
} else if (captureResult.status === "timed_out" || captureResult.status === "lease_unavailable") {
|
|
13588
|
+
captureTimedOut = true;
|
|
13589
|
+
console.warn(
|
|
13590
|
+
`[ai-whisper] capture ${captureResult.status}: target=${input.currentAgent} handoff=${accepted.handoffId} \u2014 will retry (no PTY fallback)`
|
|
13591
|
+
);
|
|
13545
13592
|
} else {
|
|
13546
13593
|
leaseDegraded = true;
|
|
13547
13594
|
console.warn(
|
|
@@ -13571,7 +13618,7 @@ ${msg}`
|
|
|
13571
13618
|
const capturedNothing = captureStatus === "no_response_captured";
|
|
13572
13619
|
const staleAgainstCurrentTurn = captureStatus === "no_response_captured_confidently" && classification.jaccardScore !== null;
|
|
13573
13620
|
const emptyClipConfidentMiss = captureStatus === "no_response_captured_confidently" && cleanCaptureEmpty;
|
|
13574
|
-
if ((capturedNothing || staleAgainstCurrentTurn || emptyClipConfidentMiss) && retryState.attempts + 1 < autoHandbackMaxAttempts) {
|
|
13621
|
+
if ((capturedNothing || staleAgainstCurrentTurn || emptyClipConfidentMiss || captureTimedOut) && retryState.attempts + 1 < autoHandbackMaxAttempts) {
|
|
13575
13622
|
retryState.attempts += 1;
|
|
13576
13623
|
retryState.nextEligibleAt = Date.now() + autoHandbackRetryMs;
|
|
13577
13624
|
autoHandbackAttempts.set(accepted.handoffId, retryState);
|
|
@@ -13872,25 +13919,40 @@ function createAssistantTurnCapture() {
|
|
|
13872
13919
|
|
|
13873
13920
|
// src/runtime/clipboard-handback-capture.ts
|
|
13874
13921
|
import { execFile as execFile2 } from "node:child_process";
|
|
13875
|
-
|
|
13922
|
+
var CaptureIoTimeoutError = class extends Error {
|
|
13923
|
+
constructor(command) {
|
|
13924
|
+
super(`clipboard I/O timed out: ${command}`);
|
|
13925
|
+
this.name = "CaptureIoTimeoutError";
|
|
13926
|
+
}
|
|
13927
|
+
};
|
|
13928
|
+
function execFileText(command, args = [], timeoutMs) {
|
|
13876
13929
|
return new Promise((resolve5, reject) => {
|
|
13877
|
-
execFile2(
|
|
13878
|
-
|
|
13879
|
-
|
|
13880
|
-
|
|
13881
|
-
|
|
13882
|
-
|
|
13930
|
+
execFile2(
|
|
13931
|
+
command,
|
|
13932
|
+
args,
|
|
13933
|
+
{ encoding: "utf8", timeout: timeoutMs ?? 0, killSignal: "SIGTERM" },
|
|
13934
|
+
(error, stdout) => {
|
|
13935
|
+
if (error) {
|
|
13936
|
+
if (error.killed) {
|
|
13937
|
+
reject(new CaptureIoTimeoutError(command));
|
|
13938
|
+
return;
|
|
13939
|
+
}
|
|
13940
|
+
reject(
|
|
13941
|
+
error instanceof Error ? error : new Error("Clipboard command failed")
|
|
13942
|
+
);
|
|
13943
|
+
return;
|
|
13944
|
+
}
|
|
13945
|
+
resolve5(stdout);
|
|
13883
13946
|
}
|
|
13884
|
-
|
|
13885
|
-
});
|
|
13947
|
+
);
|
|
13886
13948
|
});
|
|
13887
13949
|
}
|
|
13888
13950
|
async function captureClipboardHandback(input) {
|
|
13889
13951
|
const readClipboard = input.readClipboard ?? (() => {
|
|
13890
|
-
if (process.platform !== "darwin") {
|
|
13952
|
+
if ((input.platform ?? process.platform) !== "darwin") {
|
|
13891
13953
|
return Promise.resolve("");
|
|
13892
13954
|
}
|
|
13893
|
-
return execFileText("pbpaste");
|
|
13955
|
+
return execFileText("pbpaste", [], input.clipboardTimeoutMs);
|
|
13894
13956
|
});
|
|
13895
13957
|
const sleep3 = input.sleep ?? ((ms) => new Promise((resolve5) => setTimeout(resolve5, ms)));
|
|
13896
13958
|
const attempts = input.attempts ?? 10;
|
|
@@ -13936,7 +13998,7 @@ async function captureHandbackText(input) {
|
|
|
13936
13998
|
const recaptureAttempts = input.recaptureAttempts ?? 2;
|
|
13937
13999
|
const recaptureBackoffMs = input.recaptureBackoffMs ?? 50;
|
|
13938
14000
|
const sig = input.copySignature;
|
|
13939
|
-
let acquired =
|
|
14001
|
+
let acquired = null;
|
|
13940
14002
|
const deadline = Date.now() + acquireMaxWaitMs;
|
|
13941
14003
|
for (; ; ) {
|
|
13942
14004
|
try {
|
|
@@ -13947,21 +14009,30 @@ async function captureHandbackText(input) {
|
|
|
13947
14009
|
input.leaseOptions
|
|
13948
14010
|
);
|
|
13949
14011
|
} catch {
|
|
13950
|
-
acquired =
|
|
14012
|
+
acquired = null;
|
|
13951
14013
|
}
|
|
13952
14014
|
if (acquired) break;
|
|
13953
14015
|
if (Date.now() >= deadline) break;
|
|
13954
14016
|
await sleep3(acquireBackoffMs);
|
|
13955
14017
|
}
|
|
13956
14018
|
if (!acquired) {
|
|
13957
|
-
return { status: "
|
|
14019
|
+
return { status: "lease_unavailable", text: null, interferenceDetected: false };
|
|
13958
14020
|
}
|
|
14021
|
+
const leaseToken = acquired;
|
|
13959
14022
|
try {
|
|
13960
14023
|
let interferenceDetected = false;
|
|
13961
14024
|
for (let attempt = 0; attempt <= recaptureAttempts; attempt += 1) {
|
|
13962
14025
|
if (attempt > 0) await sleep3(recaptureBackoffMs);
|
|
13963
14026
|
const c0 = await input.readChangeCount();
|
|
13964
|
-
|
|
14027
|
+
let clip;
|
|
14028
|
+
try {
|
|
14029
|
+
clip = await input.runCapture();
|
|
14030
|
+
} catch (err) {
|
|
14031
|
+
if (err instanceof CaptureIoTimeoutError) {
|
|
14032
|
+
return { status: "timed_out", text: null, interferenceDetected };
|
|
14033
|
+
}
|
|
14034
|
+
throw err;
|
|
14035
|
+
}
|
|
13965
14036
|
const cn = await input.readChangeCount();
|
|
13966
14037
|
const checkAvailable = c0 !== null && cn !== null;
|
|
13967
14038
|
const delta = checkAvailable ? cn - c0 : null;
|
|
@@ -13991,7 +14062,7 @@ async function captureHandbackText(input) {
|
|
|
13991
14062
|
}
|
|
13992
14063
|
return { status: "degraded_pty_only", text: null, interferenceDetected: true };
|
|
13993
14064
|
} finally {
|
|
13994
|
-
releaseCaptureLease(input.db, input.collabId);
|
|
14065
|
+
releaseCaptureLease(input.db, input.collabId, leaseToken);
|
|
13995
14066
|
}
|
|
13996
14067
|
}
|
|
13997
14068
|
|
|
@@ -14001,15 +14072,20 @@ import { existsSync as existsSync11 } from "node:fs";
|
|
|
14001
14072
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
14002
14073
|
import { dirname as dirname6, join as join20 } from "node:path";
|
|
14003
14074
|
var HELPER_BIN_NAME = "clipboard-change-count";
|
|
14004
|
-
function execFileText2(command, args = []) {
|
|
14075
|
+
function execFileText2(command, args = [], timeoutMs) {
|
|
14005
14076
|
return new Promise((resolve5, reject) => {
|
|
14006
|
-
execFile3(
|
|
14007
|
-
|
|
14008
|
-
|
|
14009
|
-
|
|
14077
|
+
execFile3(
|
|
14078
|
+
command,
|
|
14079
|
+
args,
|
|
14080
|
+
{ encoding: "utf8", timeout: timeoutMs ?? 0, killSignal: "SIGTERM" },
|
|
14081
|
+
(error, stdout) => {
|
|
14082
|
+
if (error) {
|
|
14083
|
+
reject(error instanceof Error ? error : new Error("helper failed"));
|
|
14084
|
+
return;
|
|
14085
|
+
}
|
|
14086
|
+
resolve5(stdout);
|
|
14010
14087
|
}
|
|
14011
|
-
|
|
14012
|
-
});
|
|
14088
|
+
);
|
|
14013
14089
|
});
|
|
14014
14090
|
}
|
|
14015
14091
|
function makeChangeCountReader(deps) {
|
|
@@ -14018,7 +14094,7 @@ function makeChangeCountReader(deps) {
|
|
|
14018
14094
|
const here = dirname6(fileURLToPath3(import.meta.url));
|
|
14019
14095
|
const bin = join20(here, "..", "native", HELPER_BIN_NAME);
|
|
14020
14096
|
if (!existsSync11(bin)) throw new Error("changeCount helper not built");
|
|
14021
|
-
return execFileText2(bin);
|
|
14097
|
+
return execFileText2(bin, [], deps?.timeoutMs);
|
|
14022
14098
|
});
|
|
14023
14099
|
return async () => {
|
|
14024
14100
|
if (platform !== "darwin") return null;
|
|
@@ -14133,7 +14209,13 @@ function resolvePositiveIntEnv(name) {
|
|
|
14133
14209
|
const n = Number(process.env[name] ?? "");
|
|
14134
14210
|
return Number.isFinite(n) && n > 0 ? Math.floor(n) : void 0;
|
|
14135
14211
|
}
|
|
14136
|
-
var
|
|
14212
|
+
var clipboardIoTimeoutMs = resolvePositiveIntEnv("AI_WHISPER_CLIPBOARD_IO_TIMEOUT_MS") ?? 8e3;
|
|
14213
|
+
var captureWatchdogMs = resolvePositiveIntEnv("AI_WHISPER_CAPTURE_WATCHDOG_MS") ?? 2e4;
|
|
14214
|
+
var captureLeaseTtlMs = Math.max(
|
|
14215
|
+
captureWatchdogMs + 5e3,
|
|
14216
|
+
DEFAULT_LEASE_TTL_MS
|
|
14217
|
+
);
|
|
14218
|
+
var readChangeCount = makeChangeCountReader({ timeoutMs: clipboardIoTimeoutMs });
|
|
14137
14219
|
function safeReapSessions(input) {
|
|
14138
14220
|
const reap = input.reap ?? ((collabId, agentType, keepSessionId) => {
|
|
14139
14221
|
const db = openDatabase(getSharedSqlitePath());
|
|
@@ -14245,7 +14327,11 @@ function createMountSessionRuntime(input) {
|
|
|
14245
14327
|
agentType: input.target,
|
|
14246
14328
|
attachmentKind: "mounted"
|
|
14247
14329
|
});
|
|
14248
|
-
|
|
14330
|
+
releaseCaptureLeaseForHolderPid(
|
|
14331
|
+
db,
|
|
14332
|
+
resolvedClaim.collabId,
|
|
14333
|
+
process.pid
|
|
14334
|
+
);
|
|
14249
14335
|
} finally {
|
|
14250
14336
|
db.close();
|
|
14251
14337
|
}
|
|
@@ -14453,6 +14539,7 @@ function createMountSessionRuntime(input) {
|
|
|
14453
14539
|
turnText,
|
|
14454
14540
|
readChangeCount,
|
|
14455
14541
|
copySignature,
|
|
14542
|
+
leaseOptions: { ttlMs: captureLeaseTtlMs },
|
|
14456
14543
|
runCapture: () => captureClipboardHandback({
|
|
14457
14544
|
triggerCopy: () => submitInjectedInput2("/copy"),
|
|
14458
14545
|
// Some provider versions show a picker after /copy (e.g. Claude
|
|
@@ -14461,7 +14548,8 @@ function createMountSessionRuntime(input) {
|
|
|
14461
14548
|
confirmPicker: () => {
|
|
14462
14549
|
writeInjectedInput("mounted-submit-picker", "\r");
|
|
14463
14550
|
},
|
|
14464
|
-
triggerDelayMs: 300
|
|
14551
|
+
triggerDelayMs: 300,
|
|
14552
|
+
clipboardTimeoutMs: clipboardIoTimeoutMs
|
|
14465
14553
|
})
|
|
14466
14554
|
});
|
|
14467
14555
|
} finally {
|
|
@@ -14490,6 +14578,7 @@ function createMountSessionRuntime(input) {
|
|
|
14490
14578
|
autoHandbackRetryMs: resolvePositiveIntEnv(
|
|
14491
14579
|
"AI_WHISPER_AUTO_HANDBACK_RETRY_MS"
|
|
14492
14580
|
),
|
|
14581
|
+
captureWatchdogMs,
|
|
14493
14582
|
// Protocol-native providers (ai-ezio) expose onTurnFinished; for them
|
|
14494
14583
|
// the quiescence /copy auto-handback is skipped (handback comes from
|
|
14495
14584
|
// the explicit idle event). Event-enabled claude/codex ALSO suppress it
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-whisper",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Terminal-first relay for paired AI coding agents (Claude + Codex), driven by structured workflows.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -53,10 +53,10 @@
|
|
|
53
53
|
"ink-testing-library": "^4.0.0",
|
|
54
54
|
"@ai-whisper/adapter-ai-ezio": "0.0.0",
|
|
55
55
|
"@ai-whisper/adapter-claude": "0.0.0",
|
|
56
|
-
"@ai-whisper/
|
|
56
|
+
"@ai-whisper/adapter-codex": "0.0.0",
|
|
57
57
|
"@ai-whisper/broker": "0.0.0",
|
|
58
|
-
"@ai-whisper/
|
|
59
|
-
"@ai-whisper/
|
|
58
|
+
"@ai-whisper/companion-core": "0.0.0",
|
|
59
|
+
"@ai-whisper/shared": "0.0.0"
|
|
60
60
|
},
|
|
61
61
|
"files": [
|
|
62
62
|
"dist",
|