ai-whisper 0.5.8 → 0.5.9
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/companion-agent.js +422 -8
- package/dist/bin/whisper.js +468 -29
- package/package.json +5 -5
|
@@ -5951,7 +5951,7 @@ function createLiveSessionRuntime(input) {
|
|
|
5951
5951
|
input.interactiveSession.sendLocalMessage(CLEAR_LINE2);
|
|
5952
5952
|
}
|
|
5953
5953
|
let inputLineBuffer = "";
|
|
5954
|
-
function feedLineBufferedInput(data) {
|
|
5954
|
+
async function feedLineBufferedInput(data) {
|
|
5955
5955
|
for (const char of data) {
|
|
5956
5956
|
if (char === "") {
|
|
5957
5957
|
if (inputLineBuffer.length > 0) {
|
|
@@ -5970,7 +5970,7 @@ function createLiveSessionRuntime(input) {
|
|
|
5970
5970
|
const completed = inputLineBuffer;
|
|
5971
5971
|
inputLineBuffer = "";
|
|
5972
5972
|
const session = input.interactiveSession;
|
|
5973
|
-
if (completed.length > 0
|
|
5973
|
+
if (completed.length > 0) {
|
|
5974
5974
|
const cols = ttyCols(input.stdout);
|
|
5975
5975
|
const len = Array.from(completed).length;
|
|
5976
5976
|
const plainRows = Math.max(1, Math.ceil((STRIPE_COLS + len) / cols));
|
|
@@ -5978,13 +5978,18 @@ function createLiveSessionRuntime(input) {
|
|
|
5978
5978
|
if (plainRows > 1) erase += `\x1B[${plainRows - 1}A`;
|
|
5979
5979
|
erase += "\x1B[J";
|
|
5980
5980
|
input.stdout.write(erase);
|
|
5981
|
-
session.
|
|
5981
|
+
if (await session.tryConsumeLocalCommand?.(completed)) {
|
|
5982
|
+
continue;
|
|
5983
|
+
}
|
|
5984
|
+
if (session.echoUserInput) {
|
|
5985
|
+
session.echoUserInput(completed, cols);
|
|
5986
|
+
} else {
|
|
5987
|
+
input.stdout.write("\n");
|
|
5988
|
+
}
|
|
5989
|
+
input.interactiveSession.writeUserInput(completed);
|
|
5982
5990
|
} else {
|
|
5983
5991
|
input.stdout.write("\n");
|
|
5984
5992
|
}
|
|
5985
|
-
if (completed.length > 0) {
|
|
5986
|
-
input.interactiveSession.writeUserInput(completed);
|
|
5987
|
-
}
|
|
5988
5993
|
continue;
|
|
5989
5994
|
}
|
|
5990
5995
|
if (char === "\b" || char === "\x7F") {
|
|
@@ -6054,7 +6059,7 @@ function createLiveSessionRuntime(input) {
|
|
|
6054
6059
|
clearRelayPreview();
|
|
6055
6060
|
if (decision.kind === "passthrough") {
|
|
6056
6061
|
if (input.lineBufferedInput) {
|
|
6057
|
-
feedLineBufferedInput(decision.data);
|
|
6062
|
+
await feedLineBufferedInput(decision.data);
|
|
6058
6063
|
} else {
|
|
6059
6064
|
input.interactiveSession.writeUserInput(decision.data);
|
|
6060
6065
|
}
|
|
@@ -8596,6 +8601,360 @@ ${RED}\u258C ${event.message}${RESET2}`);
|
|
|
8596
8601
|
};
|
|
8597
8602
|
}
|
|
8598
8603
|
|
|
8604
|
+
// ../../node_modules/.pnpm/@ai-ezio+surface@file+..+ai-ezio+packages+surface/node_modules/@ai-ezio/surface/dist/skills.js
|
|
8605
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync3 } from "node:fs";
|
|
8606
|
+
import { join as join12 } from "node:path";
|
|
8607
|
+
function configBase(env) {
|
|
8608
|
+
return env.xdgConfigHome && env.xdgConfigHome !== "" ? env.xdgConfigHome : join12(env.home, ".config");
|
|
8609
|
+
}
|
|
8610
|
+
function skillDirs(env) {
|
|
8611
|
+
const base = configBase(env);
|
|
8612
|
+
return [
|
|
8613
|
+
{ source: "project", path: join12(env.cwd, ".agents", "skills"), engineVisible: true },
|
|
8614
|
+
{
|
|
8615
|
+
source: "ai-ezio-global",
|
|
8616
|
+
path: join12(base, "ai-ezio", "skills"),
|
|
8617
|
+
// Engine-visible as of M4: ai-ezio sets HAX_EXTRA_SKILLS_DIR to this dir
|
|
8618
|
+
// on launch, so hax injects these skills into the model prompt.
|
|
8619
|
+
engineVisible: true
|
|
8620
|
+
},
|
|
8621
|
+
{ source: "hax-global", path: join12(base, "hax", "skills"), engineVisible: true }
|
|
8622
|
+
];
|
|
8623
|
+
}
|
|
8624
|
+
function parseSkillDescription(md) {
|
|
8625
|
+
const text = md.replace(/^/, "");
|
|
8626
|
+
if (!/^---\r?\n/.test(text))
|
|
8627
|
+
return null;
|
|
8628
|
+
const end = text.indexOf("\n---", 4);
|
|
8629
|
+
if (end === -1)
|
|
8630
|
+
return null;
|
|
8631
|
+
const front = text.slice(text.indexOf("\n") + 1, end);
|
|
8632
|
+
for (const raw of front.split(/\r?\n/)) {
|
|
8633
|
+
const m = raw.match(/^description:\s*(.*)$/);
|
|
8634
|
+
if (m) {
|
|
8635
|
+
let value = (m[1] ?? "").trim();
|
|
8636
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
8637
|
+
value = value.slice(1, -1);
|
|
8638
|
+
}
|
|
8639
|
+
return value.length > 0 ? value : null;
|
|
8640
|
+
}
|
|
8641
|
+
}
|
|
8642
|
+
return null;
|
|
8643
|
+
}
|
|
8644
|
+
function discoverSkills(env, fs2) {
|
|
8645
|
+
const byName = /* @__PURE__ */ new Map();
|
|
8646
|
+
for (const dir of skillDirs(env)) {
|
|
8647
|
+
if (!fs2.isDirectory(dir.path))
|
|
8648
|
+
continue;
|
|
8649
|
+
for (const name of fs2.listDirs(dir.path)) {
|
|
8650
|
+
if (byName.has(name))
|
|
8651
|
+
continue;
|
|
8652
|
+
const skillMdPath = join12(dir.path, name, "SKILL.md");
|
|
8653
|
+
const md = fs2.readFile(skillMdPath);
|
|
8654
|
+
if (md === null)
|
|
8655
|
+
continue;
|
|
8656
|
+
byName.set(name, {
|
|
8657
|
+
name,
|
|
8658
|
+
description: parseSkillDescription(md),
|
|
8659
|
+
skillMdPath,
|
|
8660
|
+
source: dir.source,
|
|
8661
|
+
engineVisible: dir.engineVisible
|
|
8662
|
+
});
|
|
8663
|
+
}
|
|
8664
|
+
}
|
|
8665
|
+
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
8666
|
+
}
|
|
8667
|
+
function nodeSkillFs() {
|
|
8668
|
+
return {
|
|
8669
|
+
isDirectory: (path) => {
|
|
8670
|
+
try {
|
|
8671
|
+
return statSync3(path).isDirectory();
|
|
8672
|
+
} catch {
|
|
8673
|
+
return false;
|
|
8674
|
+
}
|
|
8675
|
+
},
|
|
8676
|
+
listDirs: (path) => {
|
|
8677
|
+
try {
|
|
8678
|
+
return readdirSync2(path, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
8679
|
+
} catch {
|
|
8680
|
+
return [];
|
|
8681
|
+
}
|
|
8682
|
+
},
|
|
8683
|
+
readFile: (path) => {
|
|
8684
|
+
try {
|
|
8685
|
+
return readFileSync4(path, "utf8");
|
|
8686
|
+
} catch {
|
|
8687
|
+
return null;
|
|
8688
|
+
}
|
|
8689
|
+
}
|
|
8690
|
+
};
|
|
8691
|
+
}
|
|
8692
|
+
|
|
8693
|
+
// ../../node_modules/.pnpm/@ai-ezio+surface@file+..+ai-ezio+packages+surface/node_modules/@ai-ezio/surface/dist/clipboard.js
|
|
8694
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
8695
|
+
function tryCopy(argv, text, spawnFn) {
|
|
8696
|
+
return new Promise((resolve2, reject) => {
|
|
8697
|
+
const child = spawnFn(argv[0], argv.slice(1), { stdio: ["pipe", "ignore", "ignore"] });
|
|
8698
|
+
child.on("error", reject);
|
|
8699
|
+
child.on("close", (code) => code === 0 ? resolve2() : reject(new Error(`${argv[0]} exited ${code}`)));
|
|
8700
|
+
child.stdin?.end(text);
|
|
8701
|
+
});
|
|
8702
|
+
}
|
|
8703
|
+
function makeClipboard(platform, spawnFn = spawn6) {
|
|
8704
|
+
const candidates = platform === "darwin" ? [["pbcopy"]] : [["wl-copy"], ["xclip", "-selection", "clipboard"]];
|
|
8705
|
+
return async (text) => {
|
|
8706
|
+
let lastErr = new Error("no clipboard tool available");
|
|
8707
|
+
for (const argv of candidates) {
|
|
8708
|
+
try {
|
|
8709
|
+
await tryCopy(argv, text, spawnFn);
|
|
8710
|
+
return;
|
|
8711
|
+
} catch (e) {
|
|
8712
|
+
lastErr = e;
|
|
8713
|
+
}
|
|
8714
|
+
}
|
|
8715
|
+
throw lastErr;
|
|
8716
|
+
};
|
|
8717
|
+
}
|
|
8718
|
+
|
|
8719
|
+
// ../../node_modules/.pnpm/@ai-ezio+surface@file+..+ai-ezio+packages+surface/node_modules/@ai-ezio/surface/dist/transcript-view.js
|
|
8720
|
+
import { join as join13 } from "node:path";
|
|
8721
|
+
async function showTranscript(deps) {
|
|
8722
|
+
const text = deps.path ? deps.readText(deps.path) : void 0;
|
|
8723
|
+
if (text === void 0 || text === "") {
|
|
8724
|
+
deps.write("\x1B[2m\u2500 no transcript yet \u2500\x1B[0m\n");
|
|
8725
|
+
return;
|
|
8726
|
+
}
|
|
8727
|
+
if (!deps.interactive) {
|
|
8728
|
+
deps.write(text.endsWith("\n") ? text : `${text}
|
|
8729
|
+
`);
|
|
8730
|
+
return;
|
|
8731
|
+
}
|
|
8732
|
+
deps.suspendRaw();
|
|
8733
|
+
try {
|
|
8734
|
+
await deps.spawnPager(deps.path);
|
|
8735
|
+
} catch {
|
|
8736
|
+
deps.write(text.endsWith("\n") ? text : `${text}
|
|
8737
|
+
`);
|
|
8738
|
+
} finally {
|
|
8739
|
+
deps.restoreRaw();
|
|
8740
|
+
}
|
|
8741
|
+
}
|
|
8742
|
+
|
|
8743
|
+
// ../../node_modules/.pnpm/@ai-ezio+surface@file+..+ai-ezio+packages+surface/node_modules/@ai-ezio/surface/dist/slash.js
|
|
8744
|
+
var NAME_RE = /^[a-zA-Z][\w-]*$/;
|
|
8745
|
+
function classifyLine(line, known) {
|
|
8746
|
+
if (!line.startsWith("/"))
|
|
8747
|
+
return { kind: "submit" };
|
|
8748
|
+
if (line.includes("\n"))
|
|
8749
|
+
return { kind: "submit" };
|
|
8750
|
+
const body = line.slice(1);
|
|
8751
|
+
const ws = body.search(/\s/);
|
|
8752
|
+
const rawName = ws === -1 ? body : body.slice(0, ws);
|
|
8753
|
+
const args = ws === -1 ? "" : body.slice(ws).trim();
|
|
8754
|
+
if (rawName === "")
|
|
8755
|
+
return { kind: "submit" };
|
|
8756
|
+
if (!NAME_RE.test(rawName))
|
|
8757
|
+
return { kind: "submit" };
|
|
8758
|
+
const name = rawName.toLowerCase();
|
|
8759
|
+
return known.has(name) ? { kind: "command", name, args } : { kind: "unknown", name };
|
|
8760
|
+
}
|
|
8761
|
+
function renderHelp(ctx, cmds) {
|
|
8762
|
+
for (const c of cmds)
|
|
8763
|
+
ctx.write(` /${c.name} ${c.summary}
|
|
8764
|
+
`);
|
|
8765
|
+
ctx.write("\nshortcuts: Enter submit \xB7 Alt+Enter newline \xB7 paste multiline \xB7 Ctrl-C interrupt \xB7 Ctrl-D exit\n");
|
|
8766
|
+
}
|
|
8767
|
+
function formatUsage(u) {
|
|
8768
|
+
if (!u)
|
|
8769
|
+
return null;
|
|
8770
|
+
const parts = [];
|
|
8771
|
+
if (u.contextTokens !== void 0)
|
|
8772
|
+
parts.push(`context ${u.contextTokens}`);
|
|
8773
|
+
if (u.outputTokens !== void 0)
|
|
8774
|
+
parts.push(`output ${u.outputTokens}`);
|
|
8775
|
+
if (u.cachedTokens !== void 0)
|
|
8776
|
+
parts.push(`cached ${u.cachedTokens}`);
|
|
8777
|
+
if (u.contextLimit !== void 0)
|
|
8778
|
+
parts.push(`limit ${u.contextLimit}`);
|
|
8779
|
+
if (u.contextTokens !== void 0 && u.contextLimit !== void 0 && u.contextLimit > 0) {
|
|
8780
|
+
parts.push(`${Math.round(u.contextTokens / u.contextLimit * 100)}%`);
|
|
8781
|
+
}
|
|
8782
|
+
return parts.length ? parts.join(" \xB7 ") : null;
|
|
8783
|
+
}
|
|
8784
|
+
function builtinCommands(listCommands) {
|
|
8785
|
+
return [
|
|
8786
|
+
{
|
|
8787
|
+
name: "help",
|
|
8788
|
+
summary: "list commands and keyboard shortcuts",
|
|
8789
|
+
run: (ctx) => renderHelp(ctx, listCommands())
|
|
8790
|
+
},
|
|
8791
|
+
{
|
|
8792
|
+
name: "new",
|
|
8793
|
+
aliases: ["clear"],
|
|
8794
|
+
summary: "start a new conversation",
|
|
8795
|
+
run: async (ctx) => {
|
|
8796
|
+
ctx.recorder?.noteNewConversation();
|
|
8797
|
+
await ctx.session.newConversation();
|
|
8798
|
+
ctx.write("\u2014 new conversation \u2014\n");
|
|
8799
|
+
}
|
|
8800
|
+
},
|
|
8801
|
+
{
|
|
8802
|
+
name: "status",
|
|
8803
|
+
summary: "show provider, model, and effort",
|
|
8804
|
+
run: async (ctx) => {
|
|
8805
|
+
const s = await ctx.session.status();
|
|
8806
|
+
const effort = s.effort ? ` \xB7 ${s.effort}` : "";
|
|
8807
|
+
ctx.write(`${s.provider} \xB7 ${s.model}${effort}
|
|
8808
|
+
`);
|
|
8809
|
+
}
|
|
8810
|
+
},
|
|
8811
|
+
{
|
|
8812
|
+
name: "skills",
|
|
8813
|
+
summary: "list discovered skills",
|
|
8814
|
+
run: (ctx) => {
|
|
8815
|
+
const skills = ctx.skills();
|
|
8816
|
+
if (skills.length === 0) {
|
|
8817
|
+
ctx.write("(no skills found)\n");
|
|
8818
|
+
return;
|
|
8819
|
+
}
|
|
8820
|
+
for (const s of skills)
|
|
8821
|
+
ctx.write(` ${s.name} \xB7 ${s.source}
|
|
8822
|
+
`);
|
|
8823
|
+
}
|
|
8824
|
+
},
|
|
8825
|
+
{
|
|
8826
|
+
name: "copy",
|
|
8827
|
+
summary: "copy the last response to the clipboard",
|
|
8828
|
+
run: async (ctx) => {
|
|
8829
|
+
const text = ctx.lastContent();
|
|
8830
|
+
if (text === "") {
|
|
8831
|
+
ctx.write("no response to copy\n");
|
|
8832
|
+
return;
|
|
8833
|
+
}
|
|
8834
|
+
try {
|
|
8835
|
+
await ctx.clipboard(text);
|
|
8836
|
+
ctx.write(`copied ${Buffer.byteLength(text, "utf8")} bytes
|
|
8837
|
+
`);
|
|
8838
|
+
} catch (e) {
|
|
8839
|
+
ctx.write(`clipboard unavailable: ${e.message}
|
|
8840
|
+
`);
|
|
8841
|
+
}
|
|
8842
|
+
}
|
|
8843
|
+
},
|
|
8844
|
+
{
|
|
8845
|
+
name: "usage",
|
|
8846
|
+
summary: "show the last turn's token usage",
|
|
8847
|
+
run: (ctx) => {
|
|
8848
|
+
const formatted = formatUsage(ctx.lastUsage());
|
|
8849
|
+
ctx.write(formatted ? `${formatted}
|
|
8850
|
+
` : "no usage yet\n");
|
|
8851
|
+
}
|
|
8852
|
+
},
|
|
8853
|
+
{
|
|
8854
|
+
name: "transcript",
|
|
8855
|
+
summary: "view the model-perspective transcript (same as Ctrl+T)",
|
|
8856
|
+
run: async (ctx) => {
|
|
8857
|
+
if (!ctx.showTranscript) {
|
|
8858
|
+
ctx.write("transcript unavailable\n");
|
|
8859
|
+
return;
|
|
8860
|
+
}
|
|
8861
|
+
await ctx.showTranscript();
|
|
8862
|
+
}
|
|
8863
|
+
},
|
|
8864
|
+
{
|
|
8865
|
+
name: "compact",
|
|
8866
|
+
summary: "summarize old history and free context",
|
|
8867
|
+
run: async (ctx) => {
|
|
8868
|
+
if (!ctx.compactor) {
|
|
8869
|
+
ctx.write("compaction unavailable\n");
|
|
8870
|
+
return;
|
|
8871
|
+
}
|
|
8872
|
+
const out = await ctx.compactor.compactNow();
|
|
8873
|
+
if (out.kind === "skipped" && out.reason === "in-progress") {
|
|
8874
|
+
ctx.write("compaction already in progress\n");
|
|
8875
|
+
}
|
|
8876
|
+
}
|
|
8877
|
+
},
|
|
8878
|
+
{
|
|
8879
|
+
name: "quit",
|
|
8880
|
+
aliases: ["exit"],
|
|
8881
|
+
summary: "exit ezio",
|
|
8882
|
+
run: () => {
|
|
8883
|
+
}
|
|
8884
|
+
// the controller maps /quit to the exit outcome before run()
|
|
8885
|
+
}
|
|
8886
|
+
];
|
|
8887
|
+
}
|
|
8888
|
+
var SlashController = class {
|
|
8889
|
+
ctx;
|
|
8890
|
+
/** name OR alias → command (so classifyLine's `known` set is keys()). */
|
|
8891
|
+
byKey = /* @__PURE__ */ new Map();
|
|
8892
|
+
constructor(ctx, opts) {
|
|
8893
|
+
this.ctx = ctx;
|
|
8894
|
+
const exclude = new Set(opts?.excludeCommands ?? []);
|
|
8895
|
+
for (const cmd of builtinCommands(() => this.summaries())) {
|
|
8896
|
+
if (exclude.has(cmd.name))
|
|
8897
|
+
continue;
|
|
8898
|
+
this.register(cmd);
|
|
8899
|
+
}
|
|
8900
|
+
}
|
|
8901
|
+
/** Register (or override) a command and its aliases. Last registration wins
|
|
8902
|
+
* per key: any command that already owns this command's NAME is fully evicted
|
|
8903
|
+
* (all of its keys removed) so an override replaces it cleanly; an alias key
|
|
8904
|
+
* collision is resolved key-by-key (the alias now points at the new command,
|
|
8905
|
+
* but the prior owner keeps its other keys). */
|
|
8906
|
+
register(cmd) {
|
|
8907
|
+
const displaced = this.byKey.get(cmd.name);
|
|
8908
|
+
if (displaced) {
|
|
8909
|
+
for (const [k, v] of this.byKey)
|
|
8910
|
+
if (v === displaced)
|
|
8911
|
+
this.byKey.delete(k);
|
|
8912
|
+
}
|
|
8913
|
+
this.byKey.set(cmd.name, cmd);
|
|
8914
|
+
for (const a of cmd.aliases ?? [])
|
|
8915
|
+
this.byKey.set(a, cmd);
|
|
8916
|
+
}
|
|
8917
|
+
/** Deduped canonical command list for /help. A command is listed only if it
|
|
8918
|
+
* still OWNS its own name key — this filters out any command reachable only
|
|
8919
|
+
* through a stolen alias key (e.g. another command claimed its name), so
|
|
8920
|
+
* /help never shows a stale entry. */
|
|
8921
|
+
summaries() {
|
|
8922
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8923
|
+
const out = [];
|
|
8924
|
+
for (const cmd of this.byKey.values()) {
|
|
8925
|
+
if (seen.has(cmd))
|
|
8926
|
+
continue;
|
|
8927
|
+
seen.add(cmd);
|
|
8928
|
+
if (this.byKey.get(cmd.name) !== cmd)
|
|
8929
|
+
continue;
|
|
8930
|
+
out.push({ name: cmd.name, summary: cmd.summary });
|
|
8931
|
+
}
|
|
8932
|
+
return out;
|
|
8933
|
+
}
|
|
8934
|
+
async handle(line) {
|
|
8935
|
+
const c = classifyLine(line, new Set(this.byKey.keys()));
|
|
8936
|
+
if (c.kind === "submit")
|
|
8937
|
+
return { action: "submit", text: line };
|
|
8938
|
+
if (c.kind === "unknown") {
|
|
8939
|
+
this.ctx.write(`unknown command: /${c.name}. type /help for the list.
|
|
8940
|
+
`);
|
|
8941
|
+
return { action: "handled" };
|
|
8942
|
+
}
|
|
8943
|
+
const cmd = this.byKey.get(c.name);
|
|
8944
|
+
if (!cmd)
|
|
8945
|
+
return { action: "handled" };
|
|
8946
|
+
if (cmd.name === "quit")
|
|
8947
|
+
return { action: "exit" };
|
|
8948
|
+
try {
|
|
8949
|
+
await cmd.run(this.ctx, c.args);
|
|
8950
|
+
} catch (e) {
|
|
8951
|
+
this.ctx.write(`/${c.name} failed: ${e.message}
|
|
8952
|
+
`);
|
|
8953
|
+
}
|
|
8954
|
+
return { action: "handled" };
|
|
8955
|
+
}
|
|
8956
|
+
};
|
|
8957
|
+
|
|
8599
8958
|
// ../adapter-ai-ezio/dist/mid-composition-shape.js
|
|
8600
8959
|
var DRAFTING_PREFIXES = [
|
|
8601
8960
|
"let's draft",
|
|
@@ -8632,6 +8991,11 @@ function isMidCompositionShape(message) {
|
|
|
8632
8991
|
}
|
|
8633
8992
|
|
|
8634
8993
|
// ../adapter-ai-ezio/dist/create-ai-ezio-live-session.js
|
|
8994
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
8995
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
8996
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
8997
|
+
import { tmpdir } from "node:os";
|
|
8998
|
+
import { join as join14 } from "node:path";
|
|
8635
8999
|
var defaultBuildAutoCompact = ({ session, host, write }) => {
|
|
8636
9000
|
const { compaction } = loadConfig();
|
|
8637
9001
|
return createAutoCompactDriver({
|
|
@@ -8653,6 +9017,9 @@ function createAiEzioLiveSession(input) {
|
|
|
8653
9017
|
let driver = null;
|
|
8654
9018
|
let sawTurn = false;
|
|
8655
9019
|
let pendingContent = null;
|
|
9020
|
+
let lastContent = "";
|
|
9021
|
+
let lastUsage;
|
|
9022
|
+
let slash = null;
|
|
8656
9023
|
const outputHandlers = [];
|
|
8657
9024
|
const turnFinishedHandlers = [];
|
|
8658
9025
|
const fidelityDecisionHandlers = [];
|
|
@@ -8669,6 +9036,8 @@ function createAiEzioLiveSession(input) {
|
|
|
8669
9036
|
break;
|
|
8670
9037
|
case "assistant_turn_finished":
|
|
8671
9038
|
sawTurn = true;
|
|
9039
|
+
lastContent = event.content;
|
|
9040
|
+
lastUsage = event.usage;
|
|
8672
9041
|
if (pendingContent !== null) {
|
|
8673
9042
|
const superseded = pendingContent;
|
|
8674
9043
|
for (const h of fidelityDecisionHandlers)
|
|
@@ -8722,8 +9091,47 @@ function createAiEzioLiveSession(input) {
|
|
|
8722
9091
|
for (const h of exitHandlers)
|
|
8723
9092
|
h();
|
|
8724
9093
|
});
|
|
8725
|
-
|
|
9094
|
+
const transcriptPath = join14(tmpdir(), `ezio-mounted-${randomUUID3()}.txt`);
|
|
9095
|
+
await session.start({ transcriptPath });
|
|
8726
9096
|
await host.start(session);
|
|
9097
|
+
const skillEnv = {
|
|
9098
|
+
cwd: process.cwd(),
|
|
9099
|
+
home: process.env.HOME ?? "",
|
|
9100
|
+
xdgConfigHome: process.env.XDG_CONFIG_HOME
|
|
9101
|
+
};
|
|
9102
|
+
const skillFs = nodeSkillFs();
|
|
9103
|
+
const sessionFacet = session;
|
|
9104
|
+
const slashCtx = {
|
|
9105
|
+
write: (s) => input.stdout.write(s),
|
|
9106
|
+
session: sessionFacet,
|
|
9107
|
+
...driver ? { compactor: { compactNow: () => driver.compactNow() } } : {},
|
|
9108
|
+
lastContent: () => lastContent,
|
|
9109
|
+
lastUsage: () => lastUsage,
|
|
9110
|
+
skills: () => discoverSkills(skillEnv, skillFs).map((s) => ({
|
|
9111
|
+
name: s.name,
|
|
9112
|
+
source: s.source,
|
|
9113
|
+
description: s.description
|
|
9114
|
+
})),
|
|
9115
|
+
clipboard: input.clipboard ?? makeClipboard(process.platform, spawn7),
|
|
9116
|
+
showTranscript: () => showTranscript({
|
|
9117
|
+
path: sessionFacet.transcriptPath ?? "",
|
|
9118
|
+
readText: (p) => {
|
|
9119
|
+
try {
|
|
9120
|
+
return readFileSync5(p, "utf8");
|
|
9121
|
+
} catch {
|
|
9122
|
+
return void 0;
|
|
9123
|
+
}
|
|
9124
|
+
},
|
|
9125
|
+
interactive: false,
|
|
9126
|
+
spawnPager: () => Promise.resolve(),
|
|
9127
|
+
suspendRaw: () => {
|
|
9128
|
+
},
|
|
9129
|
+
restoreRaw: () => {
|
|
9130
|
+
},
|
|
9131
|
+
write: (s) => input.stdout.write(s)
|
|
9132
|
+
})
|
|
9133
|
+
};
|
|
9134
|
+
slash = new SlashController(slashCtx, { excludeCommands: ["quit"] });
|
|
8727
9135
|
},
|
|
8728
9136
|
async stop() {
|
|
8729
9137
|
await host.stop();
|
|
@@ -8736,6 +9144,12 @@ function createAiEzioLiveSession(input) {
|
|
|
8736
9144
|
interrupt() {
|
|
8737
9145
|
session?.interrupt();
|
|
8738
9146
|
},
|
|
9147
|
+
async tryConsumeLocalCommand(line) {
|
|
9148
|
+
if (!slash)
|
|
9149
|
+
return false;
|
|
9150
|
+
const outcome = await slash.handle(line);
|
|
9151
|
+
return outcome.action !== "submit";
|
|
9152
|
+
},
|
|
8739
9153
|
echoUserInput(text, cols) {
|
|
8740
9154
|
renderer.echoUserInput(text, cols);
|
|
8741
9155
|
},
|
package/dist/bin/whisper.js
CHANGED
|
@@ -8047,11 +8047,25 @@ function codexNotifyArgs(input) {
|
|
|
8047
8047
|
]);
|
|
8048
8048
|
return ["-c", `notify=${notify}`];
|
|
8049
8049
|
}
|
|
8050
|
+
var RECOGNIZED_TURN_EVENTS_TOKENS = /* @__PURE__ */ new Set(["claude", "codex", "off", "none"]);
|
|
8051
|
+
function parseTurnEventsTokens(raw) {
|
|
8052
|
+
return raw.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
|
|
8053
|
+
}
|
|
8050
8054
|
function resolveTurnEvents(flag) {
|
|
8051
|
-
const
|
|
8052
|
-
|
|
8055
|
+
const raw = flag ?? process.env["AI_WHISPER_TURN_EVENTS"];
|
|
8056
|
+
if (raw === void 0) return { claude: true, codex: true };
|
|
8057
|
+
const set = new Set(parseTurnEventsTokens(raw));
|
|
8058
|
+
if (set.has("off") || set.has("none")) return { claude: false, codex: false };
|
|
8053
8059
|
return { claude: set.has("claude"), codex: set.has("codex") };
|
|
8054
8060
|
}
|
|
8061
|
+
function unrecognizedTurnEventsTokens(flag) {
|
|
8062
|
+
const raw = flag ?? process.env["AI_WHISPER_TURN_EVENTS"];
|
|
8063
|
+
if (raw === void 0) return [];
|
|
8064
|
+
const unknown = parseTurnEventsTokens(raw).filter(
|
|
8065
|
+
(t) => !RECOGNIZED_TURN_EVENTS_TOKENS.has(t)
|
|
8066
|
+
);
|
|
8067
|
+
return [...new Set(unknown)];
|
|
8068
|
+
}
|
|
8055
8069
|
function formatTurnEventsStartupLine(e) {
|
|
8056
8070
|
const on = (b) => b ? "ON" : "off";
|
|
8057
8071
|
return `[ai-whisper] turn-events: claude=${on(e.claude)} codex=${on(e.codex)} (codex notify-chaining: off)`;
|
|
@@ -8605,7 +8619,7 @@ function createLiveSessionRuntime(input) {
|
|
|
8605
8619
|
input.interactiveSession.sendLocalMessage(CLEAR_LINE2);
|
|
8606
8620
|
}
|
|
8607
8621
|
let inputLineBuffer = "";
|
|
8608
|
-
function feedLineBufferedInput(data) {
|
|
8622
|
+
async function feedLineBufferedInput(data) {
|
|
8609
8623
|
for (const char of data) {
|
|
8610
8624
|
if (char === "") {
|
|
8611
8625
|
if (inputLineBuffer.length > 0) {
|
|
@@ -8624,7 +8638,7 @@ function createLiveSessionRuntime(input) {
|
|
|
8624
8638
|
const completed = inputLineBuffer;
|
|
8625
8639
|
inputLineBuffer = "";
|
|
8626
8640
|
const session = input.interactiveSession;
|
|
8627
|
-
if (completed.length > 0
|
|
8641
|
+
if (completed.length > 0) {
|
|
8628
8642
|
const cols = ttyCols(input.stdout);
|
|
8629
8643
|
const len = Array.from(completed).length;
|
|
8630
8644
|
const plainRows = Math.max(1, Math.ceil((STRIPE_COLS + len) / cols));
|
|
@@ -8632,13 +8646,18 @@ function createLiveSessionRuntime(input) {
|
|
|
8632
8646
|
if (plainRows > 1) erase += `\x1B[${plainRows - 1}A`;
|
|
8633
8647
|
erase += "\x1B[J";
|
|
8634
8648
|
input.stdout.write(erase);
|
|
8635
|
-
session.
|
|
8649
|
+
if (await session.tryConsumeLocalCommand?.(completed)) {
|
|
8650
|
+
continue;
|
|
8651
|
+
}
|
|
8652
|
+
if (session.echoUserInput) {
|
|
8653
|
+
session.echoUserInput(completed, cols);
|
|
8654
|
+
} else {
|
|
8655
|
+
input.stdout.write("\n");
|
|
8656
|
+
}
|
|
8657
|
+
input.interactiveSession.writeUserInput(completed);
|
|
8636
8658
|
} else {
|
|
8637
8659
|
input.stdout.write("\n");
|
|
8638
8660
|
}
|
|
8639
|
-
if (completed.length > 0) {
|
|
8640
|
-
input.interactiveSession.writeUserInput(completed);
|
|
8641
|
-
}
|
|
8642
8661
|
continue;
|
|
8643
8662
|
}
|
|
8644
8663
|
if (char === "\b" || char === "\x7F") {
|
|
@@ -8708,7 +8727,7 @@ function createLiveSessionRuntime(input) {
|
|
|
8708
8727
|
clearRelayPreview();
|
|
8709
8728
|
if (decision.kind === "passthrough") {
|
|
8710
8729
|
if (input.lineBufferedInput) {
|
|
8711
|
-
feedLineBufferedInput(decision.data);
|
|
8730
|
+
await feedLineBufferedInput(decision.data);
|
|
8712
8731
|
} else {
|
|
8713
8732
|
input.interactiveSession.writeUserInput(decision.data);
|
|
8714
8733
|
}
|
|
@@ -8803,6 +8822,11 @@ function createLiveSessionRuntime(input) {
|
|
|
8803
8822
|
};
|
|
8804
8823
|
}
|
|
8805
8824
|
|
|
8825
|
+
// src/runtime/injected-input.ts
|
|
8826
|
+
function injectedWrite(session, value) {
|
|
8827
|
+
session.writeUserInput(value);
|
|
8828
|
+
}
|
|
8829
|
+
|
|
8806
8830
|
// ../companion-core/dist/create-companion-runtime.js
|
|
8807
8831
|
function createCompanionRuntime(input) {
|
|
8808
8832
|
let sessionSecret = null;
|
|
@@ -11264,6 +11288,360 @@ ${RED}\u258C ${event.message}${RESET2}`);
|
|
|
11264
11288
|
};
|
|
11265
11289
|
}
|
|
11266
11290
|
|
|
11291
|
+
// ../../node_modules/.pnpm/@ai-ezio+surface@file+..+ai-ezio+packages+surface/node_modules/@ai-ezio/surface/dist/skills.js
|
|
11292
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync5, statSync as statSync3 } from "node:fs";
|
|
11293
|
+
import { join as join15 } from "node:path";
|
|
11294
|
+
function configBase(env) {
|
|
11295
|
+
return env.xdgConfigHome && env.xdgConfigHome !== "" ? env.xdgConfigHome : join15(env.home, ".config");
|
|
11296
|
+
}
|
|
11297
|
+
function skillDirs(env) {
|
|
11298
|
+
const base = configBase(env);
|
|
11299
|
+
return [
|
|
11300
|
+
{ source: "project", path: join15(env.cwd, ".agents", "skills"), engineVisible: true },
|
|
11301
|
+
{
|
|
11302
|
+
source: "ai-ezio-global",
|
|
11303
|
+
path: join15(base, "ai-ezio", "skills"),
|
|
11304
|
+
// Engine-visible as of M4: ai-ezio sets HAX_EXTRA_SKILLS_DIR to this dir
|
|
11305
|
+
// on launch, so hax injects these skills into the model prompt.
|
|
11306
|
+
engineVisible: true
|
|
11307
|
+
},
|
|
11308
|
+
{ source: "hax-global", path: join15(base, "hax", "skills"), engineVisible: true }
|
|
11309
|
+
];
|
|
11310
|
+
}
|
|
11311
|
+
function parseSkillDescription(md) {
|
|
11312
|
+
const text = md.replace(/^/, "");
|
|
11313
|
+
if (!/^---\r?\n/.test(text))
|
|
11314
|
+
return null;
|
|
11315
|
+
const end = text.indexOf("\n---", 4);
|
|
11316
|
+
if (end === -1)
|
|
11317
|
+
return null;
|
|
11318
|
+
const front = text.slice(text.indexOf("\n") + 1, end);
|
|
11319
|
+
for (const raw of front.split(/\r?\n/)) {
|
|
11320
|
+
const m = raw.match(/^description:\s*(.*)$/);
|
|
11321
|
+
if (m) {
|
|
11322
|
+
let value = (m[1] ?? "").trim();
|
|
11323
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
11324
|
+
value = value.slice(1, -1);
|
|
11325
|
+
}
|
|
11326
|
+
return value.length > 0 ? value : null;
|
|
11327
|
+
}
|
|
11328
|
+
}
|
|
11329
|
+
return null;
|
|
11330
|
+
}
|
|
11331
|
+
function discoverSkills(env, fs2) {
|
|
11332
|
+
const byName = /* @__PURE__ */ new Map();
|
|
11333
|
+
for (const dir of skillDirs(env)) {
|
|
11334
|
+
if (!fs2.isDirectory(dir.path))
|
|
11335
|
+
continue;
|
|
11336
|
+
for (const name of fs2.listDirs(dir.path)) {
|
|
11337
|
+
if (byName.has(name))
|
|
11338
|
+
continue;
|
|
11339
|
+
const skillMdPath = join15(dir.path, name, "SKILL.md");
|
|
11340
|
+
const md = fs2.readFile(skillMdPath);
|
|
11341
|
+
if (md === null)
|
|
11342
|
+
continue;
|
|
11343
|
+
byName.set(name, {
|
|
11344
|
+
name,
|
|
11345
|
+
description: parseSkillDescription(md),
|
|
11346
|
+
skillMdPath,
|
|
11347
|
+
source: dir.source,
|
|
11348
|
+
engineVisible: dir.engineVisible
|
|
11349
|
+
});
|
|
11350
|
+
}
|
|
11351
|
+
}
|
|
11352
|
+
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
11353
|
+
}
|
|
11354
|
+
function nodeSkillFs() {
|
|
11355
|
+
return {
|
|
11356
|
+
isDirectory: (path4) => {
|
|
11357
|
+
try {
|
|
11358
|
+
return statSync3(path4).isDirectory();
|
|
11359
|
+
} catch {
|
|
11360
|
+
return false;
|
|
11361
|
+
}
|
|
11362
|
+
},
|
|
11363
|
+
listDirs: (path4) => {
|
|
11364
|
+
try {
|
|
11365
|
+
return readdirSync3(path4, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
11366
|
+
} catch {
|
|
11367
|
+
return [];
|
|
11368
|
+
}
|
|
11369
|
+
},
|
|
11370
|
+
readFile: (path4) => {
|
|
11371
|
+
try {
|
|
11372
|
+
return readFileSync5(path4, "utf8");
|
|
11373
|
+
} catch {
|
|
11374
|
+
return null;
|
|
11375
|
+
}
|
|
11376
|
+
}
|
|
11377
|
+
};
|
|
11378
|
+
}
|
|
11379
|
+
|
|
11380
|
+
// ../../node_modules/.pnpm/@ai-ezio+surface@file+..+ai-ezio+packages+surface/node_modules/@ai-ezio/surface/dist/clipboard.js
|
|
11381
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
11382
|
+
function tryCopy(argv2, text, spawnFn) {
|
|
11383
|
+
return new Promise((resolve5, reject) => {
|
|
11384
|
+
const child = spawnFn(argv2[0], argv2.slice(1), { stdio: ["pipe", "ignore", "ignore"] });
|
|
11385
|
+
child.on("error", reject);
|
|
11386
|
+
child.on("close", (code) => code === 0 ? resolve5() : reject(new Error(`${argv2[0]} exited ${code}`)));
|
|
11387
|
+
child.stdin?.end(text);
|
|
11388
|
+
});
|
|
11389
|
+
}
|
|
11390
|
+
function makeClipboard(platform, spawnFn = spawn7) {
|
|
11391
|
+
const candidates = platform === "darwin" ? [["pbcopy"]] : [["wl-copy"], ["xclip", "-selection", "clipboard"]];
|
|
11392
|
+
return async (text) => {
|
|
11393
|
+
let lastErr = new Error("no clipboard tool available");
|
|
11394
|
+
for (const argv2 of candidates) {
|
|
11395
|
+
try {
|
|
11396
|
+
await tryCopy(argv2, text, spawnFn);
|
|
11397
|
+
return;
|
|
11398
|
+
} catch (e) {
|
|
11399
|
+
lastErr = e;
|
|
11400
|
+
}
|
|
11401
|
+
}
|
|
11402
|
+
throw lastErr;
|
|
11403
|
+
};
|
|
11404
|
+
}
|
|
11405
|
+
|
|
11406
|
+
// ../../node_modules/.pnpm/@ai-ezio+surface@file+..+ai-ezio+packages+surface/node_modules/@ai-ezio/surface/dist/transcript-view.js
|
|
11407
|
+
import { join as join16 } from "node:path";
|
|
11408
|
+
async function showTranscript(deps) {
|
|
11409
|
+
const text = deps.path ? deps.readText(deps.path) : void 0;
|
|
11410
|
+
if (text === void 0 || text === "") {
|
|
11411
|
+
deps.write("\x1B[2m\u2500 no transcript yet \u2500\x1B[0m\n");
|
|
11412
|
+
return;
|
|
11413
|
+
}
|
|
11414
|
+
if (!deps.interactive) {
|
|
11415
|
+
deps.write(text.endsWith("\n") ? text : `${text}
|
|
11416
|
+
`);
|
|
11417
|
+
return;
|
|
11418
|
+
}
|
|
11419
|
+
deps.suspendRaw();
|
|
11420
|
+
try {
|
|
11421
|
+
await deps.spawnPager(deps.path);
|
|
11422
|
+
} catch {
|
|
11423
|
+
deps.write(text.endsWith("\n") ? text : `${text}
|
|
11424
|
+
`);
|
|
11425
|
+
} finally {
|
|
11426
|
+
deps.restoreRaw();
|
|
11427
|
+
}
|
|
11428
|
+
}
|
|
11429
|
+
|
|
11430
|
+
// ../../node_modules/.pnpm/@ai-ezio+surface@file+..+ai-ezio+packages+surface/node_modules/@ai-ezio/surface/dist/slash.js
|
|
11431
|
+
var NAME_RE = /^[a-zA-Z][\w-]*$/;
|
|
11432
|
+
function classifyLine(line, known) {
|
|
11433
|
+
if (!line.startsWith("/"))
|
|
11434
|
+
return { kind: "submit" };
|
|
11435
|
+
if (line.includes("\n"))
|
|
11436
|
+
return { kind: "submit" };
|
|
11437
|
+
const body = line.slice(1);
|
|
11438
|
+
const ws = body.search(/\s/);
|
|
11439
|
+
const rawName = ws === -1 ? body : body.slice(0, ws);
|
|
11440
|
+
const args = ws === -1 ? "" : body.slice(ws).trim();
|
|
11441
|
+
if (rawName === "")
|
|
11442
|
+
return { kind: "submit" };
|
|
11443
|
+
if (!NAME_RE.test(rawName))
|
|
11444
|
+
return { kind: "submit" };
|
|
11445
|
+
const name = rawName.toLowerCase();
|
|
11446
|
+
return known.has(name) ? { kind: "command", name, args } : { kind: "unknown", name };
|
|
11447
|
+
}
|
|
11448
|
+
function renderHelp(ctx, cmds) {
|
|
11449
|
+
for (const c of cmds)
|
|
11450
|
+
ctx.write(` /${c.name} ${c.summary}
|
|
11451
|
+
`);
|
|
11452
|
+
ctx.write("\nshortcuts: Enter submit \xB7 Alt+Enter newline \xB7 paste multiline \xB7 Ctrl-C interrupt \xB7 Ctrl-D exit\n");
|
|
11453
|
+
}
|
|
11454
|
+
function formatUsage(u) {
|
|
11455
|
+
if (!u)
|
|
11456
|
+
return null;
|
|
11457
|
+
const parts = [];
|
|
11458
|
+
if (u.contextTokens !== void 0)
|
|
11459
|
+
parts.push(`context ${u.contextTokens}`);
|
|
11460
|
+
if (u.outputTokens !== void 0)
|
|
11461
|
+
parts.push(`output ${u.outputTokens}`);
|
|
11462
|
+
if (u.cachedTokens !== void 0)
|
|
11463
|
+
parts.push(`cached ${u.cachedTokens}`);
|
|
11464
|
+
if (u.contextLimit !== void 0)
|
|
11465
|
+
parts.push(`limit ${u.contextLimit}`);
|
|
11466
|
+
if (u.contextTokens !== void 0 && u.contextLimit !== void 0 && u.contextLimit > 0) {
|
|
11467
|
+
parts.push(`${Math.round(u.contextTokens / u.contextLimit * 100)}%`);
|
|
11468
|
+
}
|
|
11469
|
+
return parts.length ? parts.join(" \xB7 ") : null;
|
|
11470
|
+
}
|
|
11471
|
+
function builtinCommands(listCommands) {
|
|
11472
|
+
return [
|
|
11473
|
+
{
|
|
11474
|
+
name: "help",
|
|
11475
|
+
summary: "list commands and keyboard shortcuts",
|
|
11476
|
+
run: (ctx) => renderHelp(ctx, listCommands())
|
|
11477
|
+
},
|
|
11478
|
+
{
|
|
11479
|
+
name: "new",
|
|
11480
|
+
aliases: ["clear"],
|
|
11481
|
+
summary: "start a new conversation",
|
|
11482
|
+
run: async (ctx) => {
|
|
11483
|
+
ctx.recorder?.noteNewConversation();
|
|
11484
|
+
await ctx.session.newConversation();
|
|
11485
|
+
ctx.write("\u2014 new conversation \u2014\n");
|
|
11486
|
+
}
|
|
11487
|
+
},
|
|
11488
|
+
{
|
|
11489
|
+
name: "status",
|
|
11490
|
+
summary: "show provider, model, and effort",
|
|
11491
|
+
run: async (ctx) => {
|
|
11492
|
+
const s = await ctx.session.status();
|
|
11493
|
+
const effort = s.effort ? ` \xB7 ${s.effort}` : "";
|
|
11494
|
+
ctx.write(`${s.provider} \xB7 ${s.model}${effort}
|
|
11495
|
+
`);
|
|
11496
|
+
}
|
|
11497
|
+
},
|
|
11498
|
+
{
|
|
11499
|
+
name: "skills",
|
|
11500
|
+
summary: "list discovered skills",
|
|
11501
|
+
run: (ctx) => {
|
|
11502
|
+
const skills = ctx.skills();
|
|
11503
|
+
if (skills.length === 0) {
|
|
11504
|
+
ctx.write("(no skills found)\n");
|
|
11505
|
+
return;
|
|
11506
|
+
}
|
|
11507
|
+
for (const s of skills)
|
|
11508
|
+
ctx.write(` ${s.name} \xB7 ${s.source}
|
|
11509
|
+
`);
|
|
11510
|
+
}
|
|
11511
|
+
},
|
|
11512
|
+
{
|
|
11513
|
+
name: "copy",
|
|
11514
|
+
summary: "copy the last response to the clipboard",
|
|
11515
|
+
run: async (ctx) => {
|
|
11516
|
+
const text = ctx.lastContent();
|
|
11517
|
+
if (text === "") {
|
|
11518
|
+
ctx.write("no response to copy\n");
|
|
11519
|
+
return;
|
|
11520
|
+
}
|
|
11521
|
+
try {
|
|
11522
|
+
await ctx.clipboard(text);
|
|
11523
|
+
ctx.write(`copied ${Buffer.byteLength(text, "utf8")} bytes
|
|
11524
|
+
`);
|
|
11525
|
+
} catch (e) {
|
|
11526
|
+
ctx.write(`clipboard unavailable: ${e.message}
|
|
11527
|
+
`);
|
|
11528
|
+
}
|
|
11529
|
+
}
|
|
11530
|
+
},
|
|
11531
|
+
{
|
|
11532
|
+
name: "usage",
|
|
11533
|
+
summary: "show the last turn's token usage",
|
|
11534
|
+
run: (ctx) => {
|
|
11535
|
+
const formatted = formatUsage(ctx.lastUsage());
|
|
11536
|
+
ctx.write(formatted ? `${formatted}
|
|
11537
|
+
` : "no usage yet\n");
|
|
11538
|
+
}
|
|
11539
|
+
},
|
|
11540
|
+
{
|
|
11541
|
+
name: "transcript",
|
|
11542
|
+
summary: "view the model-perspective transcript (same as Ctrl+T)",
|
|
11543
|
+
run: async (ctx) => {
|
|
11544
|
+
if (!ctx.showTranscript) {
|
|
11545
|
+
ctx.write("transcript unavailable\n");
|
|
11546
|
+
return;
|
|
11547
|
+
}
|
|
11548
|
+
await ctx.showTranscript();
|
|
11549
|
+
}
|
|
11550
|
+
},
|
|
11551
|
+
{
|
|
11552
|
+
name: "compact",
|
|
11553
|
+
summary: "summarize old history and free context",
|
|
11554
|
+
run: async (ctx) => {
|
|
11555
|
+
if (!ctx.compactor) {
|
|
11556
|
+
ctx.write("compaction unavailable\n");
|
|
11557
|
+
return;
|
|
11558
|
+
}
|
|
11559
|
+
const out = await ctx.compactor.compactNow();
|
|
11560
|
+
if (out.kind === "skipped" && out.reason === "in-progress") {
|
|
11561
|
+
ctx.write("compaction already in progress\n");
|
|
11562
|
+
}
|
|
11563
|
+
}
|
|
11564
|
+
},
|
|
11565
|
+
{
|
|
11566
|
+
name: "quit",
|
|
11567
|
+
aliases: ["exit"],
|
|
11568
|
+
summary: "exit ezio",
|
|
11569
|
+
run: () => {
|
|
11570
|
+
}
|
|
11571
|
+
// the controller maps /quit to the exit outcome before run()
|
|
11572
|
+
}
|
|
11573
|
+
];
|
|
11574
|
+
}
|
|
11575
|
+
var SlashController = class {
|
|
11576
|
+
ctx;
|
|
11577
|
+
/** name OR alias → command (so classifyLine's `known` set is keys()). */
|
|
11578
|
+
byKey = /* @__PURE__ */ new Map();
|
|
11579
|
+
constructor(ctx, opts) {
|
|
11580
|
+
this.ctx = ctx;
|
|
11581
|
+
const exclude = new Set(opts?.excludeCommands ?? []);
|
|
11582
|
+
for (const cmd of builtinCommands(() => this.summaries())) {
|
|
11583
|
+
if (exclude.has(cmd.name))
|
|
11584
|
+
continue;
|
|
11585
|
+
this.register(cmd);
|
|
11586
|
+
}
|
|
11587
|
+
}
|
|
11588
|
+
/** Register (or override) a command and its aliases. Last registration wins
|
|
11589
|
+
* per key: any command that already owns this command's NAME is fully evicted
|
|
11590
|
+
* (all of its keys removed) so an override replaces it cleanly; an alias key
|
|
11591
|
+
* collision is resolved key-by-key (the alias now points at the new command,
|
|
11592
|
+
* but the prior owner keeps its other keys). */
|
|
11593
|
+
register(cmd) {
|
|
11594
|
+
const displaced = this.byKey.get(cmd.name);
|
|
11595
|
+
if (displaced) {
|
|
11596
|
+
for (const [k, v] of this.byKey)
|
|
11597
|
+
if (v === displaced)
|
|
11598
|
+
this.byKey.delete(k);
|
|
11599
|
+
}
|
|
11600
|
+
this.byKey.set(cmd.name, cmd);
|
|
11601
|
+
for (const a of cmd.aliases ?? [])
|
|
11602
|
+
this.byKey.set(a, cmd);
|
|
11603
|
+
}
|
|
11604
|
+
/** Deduped canonical command list for /help. A command is listed only if it
|
|
11605
|
+
* still OWNS its own name key — this filters out any command reachable only
|
|
11606
|
+
* through a stolen alias key (e.g. another command claimed its name), so
|
|
11607
|
+
* /help never shows a stale entry. */
|
|
11608
|
+
summaries() {
|
|
11609
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11610
|
+
const out = [];
|
|
11611
|
+
for (const cmd of this.byKey.values()) {
|
|
11612
|
+
if (seen.has(cmd))
|
|
11613
|
+
continue;
|
|
11614
|
+
seen.add(cmd);
|
|
11615
|
+
if (this.byKey.get(cmd.name) !== cmd)
|
|
11616
|
+
continue;
|
|
11617
|
+
out.push({ name: cmd.name, summary: cmd.summary });
|
|
11618
|
+
}
|
|
11619
|
+
return out;
|
|
11620
|
+
}
|
|
11621
|
+
async handle(line) {
|
|
11622
|
+
const c = classifyLine(line, new Set(this.byKey.keys()));
|
|
11623
|
+
if (c.kind === "submit")
|
|
11624
|
+
return { action: "submit", text: line };
|
|
11625
|
+
if (c.kind === "unknown") {
|
|
11626
|
+
this.ctx.write(`unknown command: /${c.name}. type /help for the list.
|
|
11627
|
+
`);
|
|
11628
|
+
return { action: "handled" };
|
|
11629
|
+
}
|
|
11630
|
+
const cmd = this.byKey.get(c.name);
|
|
11631
|
+
if (!cmd)
|
|
11632
|
+
return { action: "handled" };
|
|
11633
|
+
if (cmd.name === "quit")
|
|
11634
|
+
return { action: "exit" };
|
|
11635
|
+
try {
|
|
11636
|
+
await cmd.run(this.ctx, c.args);
|
|
11637
|
+
} catch (e) {
|
|
11638
|
+
this.ctx.write(`/${c.name} failed: ${e.message}
|
|
11639
|
+
`);
|
|
11640
|
+
}
|
|
11641
|
+
return { action: "handled" };
|
|
11642
|
+
}
|
|
11643
|
+
};
|
|
11644
|
+
|
|
11267
11645
|
// ../adapter-ai-ezio/dist/mid-composition-shape.js
|
|
11268
11646
|
var DRAFTING_PREFIXES = [
|
|
11269
11647
|
"let's draft",
|
|
@@ -11300,6 +11678,11 @@ function isMidCompositionShape(message) {
|
|
|
11300
11678
|
}
|
|
11301
11679
|
|
|
11302
11680
|
// ../adapter-ai-ezio/dist/create-ai-ezio-live-session.js
|
|
11681
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
11682
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
11683
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
11684
|
+
import { tmpdir } from "node:os";
|
|
11685
|
+
import { join as join17 } from "node:path";
|
|
11303
11686
|
var defaultBuildAutoCompact = ({ session, host, write }) => {
|
|
11304
11687
|
const { compaction } = loadConfig();
|
|
11305
11688
|
return createAutoCompactDriver({
|
|
@@ -11321,6 +11704,9 @@ function createAiEzioLiveSession(input) {
|
|
|
11321
11704
|
let driver = null;
|
|
11322
11705
|
let sawTurn = false;
|
|
11323
11706
|
let pendingContent = null;
|
|
11707
|
+
let lastContent = "";
|
|
11708
|
+
let lastUsage;
|
|
11709
|
+
let slash = null;
|
|
11324
11710
|
const outputHandlers = [];
|
|
11325
11711
|
const turnFinishedHandlers = [];
|
|
11326
11712
|
const fidelityDecisionHandlers = [];
|
|
@@ -11337,6 +11723,8 @@ function createAiEzioLiveSession(input) {
|
|
|
11337
11723
|
break;
|
|
11338
11724
|
case "assistant_turn_finished":
|
|
11339
11725
|
sawTurn = true;
|
|
11726
|
+
lastContent = event.content;
|
|
11727
|
+
lastUsage = event.usage;
|
|
11340
11728
|
if (pendingContent !== null) {
|
|
11341
11729
|
const superseded = pendingContent;
|
|
11342
11730
|
for (const h of fidelityDecisionHandlers)
|
|
@@ -11390,8 +11778,47 @@ function createAiEzioLiveSession(input) {
|
|
|
11390
11778
|
for (const h of exitHandlers)
|
|
11391
11779
|
h();
|
|
11392
11780
|
});
|
|
11393
|
-
|
|
11781
|
+
const transcriptPath = join17(tmpdir(), `ezio-mounted-${randomUUID3()}.txt`);
|
|
11782
|
+
await session.start({ transcriptPath });
|
|
11394
11783
|
await host.start(session);
|
|
11784
|
+
const skillEnv = {
|
|
11785
|
+
cwd: process.cwd(),
|
|
11786
|
+
home: process.env.HOME ?? "",
|
|
11787
|
+
xdgConfigHome: process.env.XDG_CONFIG_HOME
|
|
11788
|
+
};
|
|
11789
|
+
const skillFs = nodeSkillFs();
|
|
11790
|
+
const sessionFacet = session;
|
|
11791
|
+
const slashCtx = {
|
|
11792
|
+
write: (s) => input.stdout.write(s),
|
|
11793
|
+
session: sessionFacet,
|
|
11794
|
+
...driver ? { compactor: { compactNow: () => driver.compactNow() } } : {},
|
|
11795
|
+
lastContent: () => lastContent,
|
|
11796
|
+
lastUsage: () => lastUsage,
|
|
11797
|
+
skills: () => discoverSkills(skillEnv, skillFs).map((s) => ({
|
|
11798
|
+
name: s.name,
|
|
11799
|
+
source: s.source,
|
|
11800
|
+
description: s.description
|
|
11801
|
+
})),
|
|
11802
|
+
clipboard: input.clipboard ?? makeClipboard(process.platform, spawn8),
|
|
11803
|
+
showTranscript: () => showTranscript({
|
|
11804
|
+
path: sessionFacet.transcriptPath ?? "",
|
|
11805
|
+
readText: (p) => {
|
|
11806
|
+
try {
|
|
11807
|
+
return readFileSync6(p, "utf8");
|
|
11808
|
+
} catch {
|
|
11809
|
+
return void 0;
|
|
11810
|
+
}
|
|
11811
|
+
},
|
|
11812
|
+
interactive: false,
|
|
11813
|
+
spawnPager: () => Promise.resolve(),
|
|
11814
|
+
suspendRaw: () => {
|
|
11815
|
+
},
|
|
11816
|
+
restoreRaw: () => {
|
|
11817
|
+
},
|
|
11818
|
+
write: (s) => input.stdout.write(s)
|
|
11819
|
+
})
|
|
11820
|
+
};
|
|
11821
|
+
slash = new SlashController(slashCtx, { excludeCommands: ["quit"] });
|
|
11395
11822
|
},
|
|
11396
11823
|
async stop() {
|
|
11397
11824
|
await host.stop();
|
|
@@ -11404,6 +11831,12 @@ function createAiEzioLiveSession(input) {
|
|
|
11404
11831
|
interrupt() {
|
|
11405
11832
|
session?.interrupt();
|
|
11406
11833
|
},
|
|
11834
|
+
async tryConsumeLocalCommand(line) {
|
|
11835
|
+
if (!slash)
|
|
11836
|
+
return false;
|
|
11837
|
+
const outcome = await slash.handle(line);
|
|
11838
|
+
return outcome.action !== "submit";
|
|
11839
|
+
},
|
|
11407
11840
|
echoUserInput(text, cols) {
|
|
11408
11841
|
renderer.echoUserInput(text, cols);
|
|
11409
11842
|
},
|
|
@@ -11586,9 +12019,9 @@ function createCliWorkItemId(now) {
|
|
|
11586
12019
|
|
|
11587
12020
|
// src/ezio-staleness-check.ts
|
|
11588
12021
|
import { createRequire as createRequire4 } from "node:module";
|
|
11589
|
-
import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as
|
|
12022
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
|
|
11590
12023
|
import { homedir as homedir2 } from "node:os";
|
|
11591
|
-
import { join as
|
|
12024
|
+
import { join as join18 } from "node:path";
|
|
11592
12025
|
|
|
11593
12026
|
// src/semver-compare.ts
|
|
11594
12027
|
function parse(version) {
|
|
@@ -11634,10 +12067,10 @@ function compareSemver(a, b) {
|
|
|
11634
12067
|
|
|
11635
12068
|
// src/generated/ezio-provenance.ts
|
|
11636
12069
|
var EZIO_PROVENANCE = {
|
|
11637
|
-
ezioCliVersion: "0.2.0-beta.
|
|
11638
|
-
ezioGitSha: "
|
|
11639
|
-
builtAt: "2026-06-
|
|
11640
|
-
whisperVersion: "0.5.
|
|
12070
|
+
ezioCliVersion: "0.2.0-beta.4",
|
|
12071
|
+
ezioGitSha: "64f64ed",
|
|
12072
|
+
builtAt: "2026-06-14T04:05:27.620Z",
|
|
12073
|
+
whisperVersion: "0.5.9"
|
|
11641
12074
|
};
|
|
11642
12075
|
|
|
11643
12076
|
// src/ezio-provenance-types.ts
|
|
@@ -11661,18 +12094,18 @@ function parseCache(raw) {
|
|
|
11661
12094
|
}
|
|
11662
12095
|
}
|
|
11663
12096
|
function cacheFile() {
|
|
11664
|
-
return
|
|
12097
|
+
return join18(homedir2(), ".ai-whisper", "update-check.json");
|
|
11665
12098
|
}
|
|
11666
12099
|
function defaultReadCache() {
|
|
11667
12100
|
try {
|
|
11668
|
-
return parseCache(
|
|
12101
|
+
return parseCache(readFileSync7(cacheFile(), "utf8"));
|
|
11669
12102
|
} catch {
|
|
11670
12103
|
return null;
|
|
11671
12104
|
}
|
|
11672
12105
|
}
|
|
11673
12106
|
function defaultWriteCache(cache) {
|
|
11674
12107
|
try {
|
|
11675
|
-
mkdirSync6(
|
|
12108
|
+
mkdirSync6(join18(homedir2(), ".ai-whisper"), { recursive: true });
|
|
11676
12109
|
writeFileSync4(cacheFile(), JSON.stringify(cache), "utf8");
|
|
11677
12110
|
} catch {
|
|
11678
12111
|
}
|
|
@@ -11697,9 +12130,9 @@ function defaultResolveInstalledEzioVersion() {
|
|
|
11697
12130
|
const require4 = createRequire4(import.meta.url);
|
|
11698
12131
|
const paths = require4.resolve.paths("@ai-creed/ai-ezio") ?? [];
|
|
11699
12132
|
for (const base of paths) {
|
|
11700
|
-
const pkgPath =
|
|
12133
|
+
const pkgPath = join18(base, "@ai-creed", "ai-ezio", "package.json");
|
|
11701
12134
|
if (existsSync10(pkgPath)) {
|
|
11702
|
-
const pkg = JSON.parse(
|
|
12135
|
+
const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
|
|
11703
12136
|
return pkg.version ?? null;
|
|
11704
12137
|
}
|
|
11705
12138
|
}
|
|
@@ -13032,7 +13465,7 @@ async function captureHandbackText(input) {
|
|
|
13032
13465
|
import { execFile as execFile3 } from "node:child_process";
|
|
13033
13466
|
import { existsSync as existsSync11 } from "node:fs";
|
|
13034
13467
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
13035
|
-
import { dirname as dirname5, join as
|
|
13468
|
+
import { dirname as dirname5, join as join19 } from "node:path";
|
|
13036
13469
|
var HELPER_BIN_NAME = "clipboard-change-count";
|
|
13037
13470
|
function execFileText2(command, args = []) {
|
|
13038
13471
|
return new Promise((resolve5, reject) => {
|
|
@@ -13049,7 +13482,7 @@ function makeChangeCountReader(deps) {
|
|
|
13049
13482
|
const platform = deps?.platform ?? process.platform;
|
|
13050
13483
|
const runHelper = deps?.runHelper ?? (async () => {
|
|
13051
13484
|
const here = dirname5(fileURLToPath3(import.meta.url));
|
|
13052
|
-
const bin =
|
|
13485
|
+
const bin = join19(here, "..", "native", HELPER_BIN_NAME);
|
|
13053
13486
|
if (!existsSync11(bin)) throw new Error("changeCount helper not built");
|
|
13054
13487
|
return execFileText2(bin);
|
|
13055
13488
|
});
|
|
@@ -13313,7 +13746,7 @@ function createMountSessionRuntime(input) {
|
|
|
13313
13746
|
channel,
|
|
13314
13747
|
data: value
|
|
13315
13748
|
});
|
|
13316
|
-
interactiveSession
|
|
13749
|
+
injectedWrite(interactiveSession, value);
|
|
13317
13750
|
};
|
|
13318
13751
|
const submitInjectedInput2 = async (text) => {
|
|
13319
13752
|
debugLog({
|
|
@@ -14094,6 +14527,12 @@ async function runCollabMount(input) {
|
|
|
14094
14527
|
mkdirSync8(getStateSocketsDir(), { recursive: true });
|
|
14095
14528
|
mkdirSync8(getStateLogsDir(), { recursive: true });
|
|
14096
14529
|
console.error(formatTurnEventsStartupLine(enablement));
|
|
14530
|
+
const unknownTurnEventsTokens = unrecognizedTurnEventsTokens(input.turnEventsFlag);
|
|
14531
|
+
if (unknownTurnEventsTokens.length > 0) {
|
|
14532
|
+
console.error(
|
|
14533
|
+
`[ai-whisper] turn-events: ignoring unrecognized token(s) ${unknownTurnEventsTokens.map((t) => `"${t}"`).join(", ")} \u2014 expected claude, codex, off, or none`
|
|
14534
|
+
);
|
|
14535
|
+
}
|
|
14097
14536
|
const tryResolve = () => {
|
|
14098
14537
|
const db = openDatabase(getSharedSqlitePath());
|
|
14099
14538
|
try {
|
|
@@ -15091,8 +15530,8 @@ import Database2 from "better-sqlite3";
|
|
|
15091
15530
|
import { existsSync as existsSync12 } from "node:fs";
|
|
15092
15531
|
|
|
15093
15532
|
// src/runtime/evaluator-config.ts
|
|
15094
|
-
import { readFileSync as
|
|
15095
|
-
import { join as
|
|
15533
|
+
import { readFileSync as readFileSync8, statSync as statSync4 } from "node:fs";
|
|
15534
|
+
import { join as join20 } from "node:path";
|
|
15096
15535
|
function isEvaluatorReady(status) {
|
|
15097
15536
|
return status === "ready" || status === "unknown";
|
|
15098
15537
|
}
|
|
@@ -15778,7 +16217,7 @@ async function runSkillInstall(input) {
|
|
|
15778
16217
|
init_dist2();
|
|
15779
16218
|
|
|
15780
16219
|
// src/runtime/cli-package-info.ts
|
|
15781
|
-
import { readFileSync as
|
|
16220
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
15782
16221
|
import { dirname as dirname7 } from "node:path";
|
|
15783
16222
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
15784
16223
|
var PKG_CANDIDATES = ["../../package.json", "../package.json"];
|
|
@@ -15786,7 +16225,7 @@ function readWhisperPackage() {
|
|
|
15786
16225
|
for (const rel of PKG_CANDIDATES) {
|
|
15787
16226
|
try {
|
|
15788
16227
|
const url = new URL(rel, import.meta.url);
|
|
15789
|
-
const pkg = JSON.parse(
|
|
16228
|
+
const pkg = JSON.parse(readFileSync9(url, "utf8"));
|
|
15790
16229
|
if (pkg.name === "ai-whisper" && typeof pkg.version === "string") {
|
|
15791
16230
|
return { url, version: pkg.version };
|
|
15792
16231
|
}
|
|
@@ -16007,7 +16446,7 @@ Not attached (stdin/stdout is not a TTY). To view the tmux session, run:
|
|
|
16007
16446
|
"Args forwarded after `--` to the agent binary spawn (e.g. `mount codex -- --full-auto`)"
|
|
16008
16447
|
).option("--workspace <path>", "Workspace root", process.cwd()).option("--collab <id>", "Target a specific collab id (defaults to the active collab for cwd)").option(
|
|
16009
16448
|
"--turn-events <providers>",
|
|
16010
|
-
"
|
|
16449
|
+
"Scope the push turn-completion event path to the given providers (comma-separated: claude,codex), or disable it with `off`/`none` to revert to pure clipboard capture. Overrides AI_WHISPER_TURN_EVENTS. Default: on (claude,codex)."
|
|
16011
16450
|
).action(
|
|
16012
16451
|
async (target, passthroughArgs, opts) => {
|
|
16013
16452
|
await runCollabMount({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-whisper",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.9",
|
|
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": {
|
|
@@ -51,12 +51,12 @@
|
|
|
51
51
|
"@types/better-sqlite3": "^7.6.12",
|
|
52
52
|
"@types/react": "^19.2.14",
|
|
53
53
|
"ink-testing-library": "^4.0.0",
|
|
54
|
-
"@ai-whisper/adapter-
|
|
55
|
-
"@ai-whisper/adapter-ai-ezio": "0.0.0",
|
|
56
|
-
"@ai-whisper/shared": "0.0.0",
|
|
54
|
+
"@ai-whisper/adapter-claude": "0.0.0",
|
|
57
55
|
"@ai-whisper/broker": "0.0.0",
|
|
56
|
+
"@ai-whisper/adapter-ai-ezio": "0.0.0",
|
|
58
57
|
"@ai-whisper/companion-core": "0.0.0",
|
|
59
|
-
"@ai-whisper/adapter-
|
|
58
|
+
"@ai-whisper/adapter-codex": "0.0.0",
|
|
59
|
+
"@ai-whisper/shared": "0.0.0"
|
|
60
60
|
},
|
|
61
61
|
"files": [
|
|
62
62
|
"dist",
|