tesseract-agent 0.7.0 → 0.7.2
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-bundle/cli.js +695 -104
- package/package.json +1 -1
package/dist-bundle/cli.js
CHANGED
|
@@ -4475,14 +4475,14 @@ function promiseAllObject(promisesObj) {
|
|
|
4475
4475
|
}
|
|
4476
4476
|
function randomString(length = 10) {
|
|
4477
4477
|
const chars = "abcdefghijklmnopqrstuvwxyz";
|
|
4478
|
-
let
|
|
4478
|
+
let str2 = "";
|
|
4479
4479
|
for (let i = 0; i < length; i++) {
|
|
4480
|
-
|
|
4480
|
+
str2 += chars[Math.floor(Math.random() * chars.length)];
|
|
4481
4481
|
}
|
|
4482
|
-
return
|
|
4482
|
+
return str2;
|
|
4483
4483
|
}
|
|
4484
|
-
function esc(
|
|
4485
|
-
return JSON.stringify(
|
|
4484
|
+
function esc(str2) {
|
|
4485
|
+
return JSON.stringify(str2);
|
|
4486
4486
|
}
|
|
4487
4487
|
function slugify(input) {
|
|
4488
4488
|
return input.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -4526,8 +4526,8 @@ function numKeys(data) {
|
|
|
4526
4526
|
}
|
|
4527
4527
|
return keyCount;
|
|
4528
4528
|
}
|
|
4529
|
-
function escapeRegex(
|
|
4530
|
-
return
|
|
4529
|
+
function escapeRegex(str2) {
|
|
4530
|
+
return str2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4531
4531
|
}
|
|
4532
4532
|
function clone(inst, def, params) {
|
|
4533
4533
|
const cl = new inst._zod.constr(def ?? inst._zod.def);
|
|
@@ -19467,6 +19467,12 @@ var init_ws_protocol = __esm({
|
|
|
19467
19467
|
// co-viewers see the Mission card's live stage progress
|
|
19468
19468
|
"agent:elicitation_complete",
|
|
19469
19469
|
// M5 — co-viewers clear a resolved url-mode card
|
|
19470
|
+
"agent:framework_event",
|
|
19471
|
+
// full-visibility debug feed — co-viewers see the same framework events
|
|
19472
|
+
"agent:sources",
|
|
19473
|
+
// citation footnotes — co-viewers see the same sources under the reply
|
|
19474
|
+
"agent:task",
|
|
19475
|
+
// live subagent cards — co-viewers watch subagent progress too
|
|
19470
19476
|
"chat:echoed",
|
|
19471
19477
|
"chat:scheduled_fired"
|
|
19472
19478
|
// cron — co-viewers see the synthetic "Scheduled" user turn
|
|
@@ -22871,6 +22877,72 @@ var init_kill_tree = __esm({
|
|
|
22871
22877
|
}
|
|
22872
22878
|
});
|
|
22873
22879
|
|
|
22880
|
+
// ../../packages/agent-core/dist/sources.js
|
|
22881
|
+
function str(v) {
|
|
22882
|
+
return typeof v === "string" ? v : "";
|
|
22883
|
+
}
|
|
22884
|
+
function extractSources(content) {
|
|
22885
|
+
if (!Array.isArray(content))
|
|
22886
|
+
return [];
|
|
22887
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
22888
|
+
for (const block of content) {
|
|
22889
|
+
if (!block || typeof block !== "object")
|
|
22890
|
+
continue;
|
|
22891
|
+
const b = block;
|
|
22892
|
+
if (b.type !== "text" || !Array.isArray(b.citations))
|
|
22893
|
+
continue;
|
|
22894
|
+
for (const raw of b.citations) {
|
|
22895
|
+
if (!raw || typeof raw !== "object")
|
|
22896
|
+
continue;
|
|
22897
|
+
const c = raw;
|
|
22898
|
+
let url2;
|
|
22899
|
+
let title;
|
|
22900
|
+
let kind;
|
|
22901
|
+
switch (c.type) {
|
|
22902
|
+
case "web_search_result_location":
|
|
22903
|
+
url2 = str(c.url) || void 0;
|
|
22904
|
+
title = str(c.title) || url2 || "Web result";
|
|
22905
|
+
kind = "web";
|
|
22906
|
+
break;
|
|
22907
|
+
case "search_result_location": {
|
|
22908
|
+
const source = str(c.source);
|
|
22909
|
+
url2 = /^https?:\/\//.test(source) ? source : void 0;
|
|
22910
|
+
title = str(c.title) || source || "Search result";
|
|
22911
|
+
kind = "search";
|
|
22912
|
+
break;
|
|
22913
|
+
}
|
|
22914
|
+
case "char_location":
|
|
22915
|
+
case "page_location":
|
|
22916
|
+
case "content_block_location":
|
|
22917
|
+
title = str(c.document_title) || "Document";
|
|
22918
|
+
kind = "document";
|
|
22919
|
+
break;
|
|
22920
|
+
default:
|
|
22921
|
+
continue;
|
|
22922
|
+
}
|
|
22923
|
+
const key2 = (url2 || title).toLowerCase();
|
|
22924
|
+
const snippet = str(c.cited_text).replace(/\s+/g, " ").trim().slice(0, SNIPPET_MAX);
|
|
22925
|
+
const existing = byKey.get(key2);
|
|
22926
|
+
if (existing) {
|
|
22927
|
+
existing.count += 1;
|
|
22928
|
+
if (!existing.snippet && snippet)
|
|
22929
|
+
existing.snippet = snippet;
|
|
22930
|
+
} else if (byKey.size < SOURCES_MAX) {
|
|
22931
|
+
byKey.set(key2, { ...url2 ? { url: url2 } : {}, title, ...snippet ? { snippet } : {}, kind, count: 1 });
|
|
22932
|
+
}
|
|
22933
|
+
}
|
|
22934
|
+
}
|
|
22935
|
+
return [...byKey.values()];
|
|
22936
|
+
}
|
|
22937
|
+
var SNIPPET_MAX, SOURCES_MAX;
|
|
22938
|
+
var init_sources = __esm({
|
|
22939
|
+
"../../packages/agent-core/dist/sources.js"() {
|
|
22940
|
+
"use strict";
|
|
22941
|
+
SNIPPET_MAX = 220;
|
|
22942
|
+
SOURCES_MAX = 40;
|
|
22943
|
+
}
|
|
22944
|
+
});
|
|
22945
|
+
|
|
22874
22946
|
// ../../packages/agent-core/dist/agent-manager.js
|
|
22875
22947
|
import { query, createSdkMcpServer, startup } from "@anthropic-ai/claude-agent-sdk";
|
|
22876
22948
|
import { writeFile as writeFile4, mkdir as mkdir4, readFile as readFile6 } from "node:fs/promises";
|
|
@@ -22889,9 +22961,11 @@ function resolveClaudeCodeExecutable() {
|
|
|
22889
22961
|
const resourcesPath = process.resourcesPath;
|
|
22890
22962
|
if (resourcesPath) {
|
|
22891
22963
|
const platKey = process.platform === "darwin" ? "mac" : process.platform === "win32" ? "win" : "linux";
|
|
22892
|
-
const
|
|
22893
|
-
|
|
22894
|
-
|
|
22964
|
+
for (const dir2 of [`${platKey}-${process.arch}`, platKey]) {
|
|
22965
|
+
const bundled = join9(resourcesPath, "claude-cli", dir2, binName);
|
|
22966
|
+
if (existsSync2(bundled))
|
|
22967
|
+
return cachedClaudeExe = bundled;
|
|
22968
|
+
}
|
|
22895
22969
|
}
|
|
22896
22970
|
const require2 = createRequire(import.meta.url);
|
|
22897
22971
|
const keys = process.platform === "linux" ? [`linux-${process.arch}`, `linux-${process.arch}-musl`] : [`${process.platform}-${process.arch}`];
|
|
@@ -22990,8 +23064,176 @@ function extractHumanReadable(message) {
|
|
|
22990
23064
|
}
|
|
22991
23065
|
return parts.join("\n");
|
|
22992
23066
|
}
|
|
22993
|
-
function
|
|
22994
|
-
|
|
23067
|
+
function traceFrame(message) {
|
|
23068
|
+
const type = message.type || "unknown";
|
|
23069
|
+
if (type === "stream_event") {
|
|
23070
|
+
const ev = message.event ?? {};
|
|
23071
|
+
const et = ev.type ?? "?";
|
|
23072
|
+
if (et === "content_block_delta" || et === "ping")
|
|
23073
|
+
return;
|
|
23074
|
+
const cb = ev.content_block ? ` ${ev.content_block.type ?? ""}${ev.content_block.name ? `:${ev.content_block.name}` : ""}` : "";
|
|
23075
|
+
log(`frame stream_event/${et}${cb}`);
|
|
23076
|
+
return;
|
|
23077
|
+
}
|
|
23078
|
+
const sub = typeof message.subtype === "string" ? `/${message.subtype}` : "";
|
|
23079
|
+
const parent = message.parent_tool_use_id ? " (subagent)" : "";
|
|
23080
|
+
log(`frame ${type}${sub}${parent}`);
|
|
23081
|
+
}
|
|
23082
|
+
function rawFramePreview(message, limit = 2e3) {
|
|
23083
|
+
let s;
|
|
23084
|
+
try {
|
|
23085
|
+
s = JSON.stringify(message, null, 2);
|
|
23086
|
+
} catch {
|
|
23087
|
+
s = String(message);
|
|
23088
|
+
}
|
|
23089
|
+
return s.length > limit ? `${s.slice(0, limit)}
|
|
23090
|
+
\u2026 (${s.length} bytes total)` : s;
|
|
23091
|
+
}
|
|
23092
|
+
function humanizeName(name) {
|
|
23093
|
+
const s = name.replace(/[_-]+/g, " ").trim();
|
|
23094
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
23095
|
+
}
|
|
23096
|
+
function salientFields(m, max = 8) {
|
|
23097
|
+
const SKIP = /* @__PURE__ */ new Set(["type", "subtype", "uuid", "session_id", "parent_tool_use_id", "request_id"]);
|
|
23098
|
+
const out = [];
|
|
23099
|
+
for (const [k, v] of Object.entries(m)) {
|
|
23100
|
+
if (SKIP.has(k))
|
|
23101
|
+
continue;
|
|
23102
|
+
if (v === null || v === void 0)
|
|
23103
|
+
continue;
|
|
23104
|
+
let val;
|
|
23105
|
+
if (typeof v === "string")
|
|
23106
|
+
val = v.length > 200 ? `${v.slice(0, 200)}\u2026` : v;
|
|
23107
|
+
else if (typeof v === "number" || typeof v === "boolean")
|
|
23108
|
+
val = String(v);
|
|
23109
|
+
else if (Array.isArray(v))
|
|
23110
|
+
val = `[${v.length}]`;
|
|
23111
|
+
else
|
|
23112
|
+
continue;
|
|
23113
|
+
out.push({ label: humanizeName(k), value: val });
|
|
23114
|
+
if (out.length >= max)
|
|
23115
|
+
break;
|
|
23116
|
+
}
|
|
23117
|
+
return out;
|
|
23118
|
+
}
|
|
23119
|
+
function describeSystemSubtype(subtype, m) {
|
|
23120
|
+
if (subtype === "thinking_tokens") {
|
|
23121
|
+
const est = Number(m.estimated_tokens ?? 0);
|
|
23122
|
+
const delta = Number(m.estimated_tokens_delta ?? 0);
|
|
23123
|
+
return {
|
|
23124
|
+
category: "thinking",
|
|
23125
|
+
name: subtype,
|
|
23126
|
+
title: `Thinking \u2014 ~${est.toLocaleString("en-US")} tokens`,
|
|
23127
|
+
...delta ? { fields: [{ label: "this step", value: `+${delta.toLocaleString("en-US")}` }] } : {},
|
|
23128
|
+
updateKey: "thinking_tokens"
|
|
23129
|
+
// one self-updating card per turn, not one per tick
|
|
23130
|
+
};
|
|
23131
|
+
}
|
|
23132
|
+
const category = SYSTEM_EVENT_CATEGORY[subtype];
|
|
23133
|
+
if (category) {
|
|
23134
|
+
const updateKey = category === "hook" ? `hook:${String(m.hook_name ?? m.hook_event_name ?? subtype)}` : category === "task" ? `task:${String(m.task_id ?? subtype)}` : category === "telemetry" ? `sys:${subtype}` : void 0;
|
|
23135
|
+
return { category, name: subtype, title: humanizeName(subtype), fields: salientFields(m), ...updateKey ? { updateKey } : {} };
|
|
23136
|
+
}
|
|
23137
|
+
const text3 = extractHumanReadable(m);
|
|
23138
|
+
return { category: "unknown", name: subtype, title: `system/${subtype}`, ...text3 ? { detail: text3 } : {}, raw: rawFramePreview(m) };
|
|
23139
|
+
}
|
|
23140
|
+
function sstr(v) {
|
|
23141
|
+
return typeof v === "string" ? v : "";
|
|
23142
|
+
}
|
|
23143
|
+
function prettifyModel(id) {
|
|
23144
|
+
if (!id)
|
|
23145
|
+
return "the model";
|
|
23146
|
+
const known = {
|
|
23147
|
+
"claude-opus-4-8": "Opus 4.8",
|
|
23148
|
+
"claude-sonnet-4-6": "Sonnet 4.6",
|
|
23149
|
+
"claude-haiku-4-5-20251001": "Haiku 4.5",
|
|
23150
|
+
"claude-fable-5": "Fable 5",
|
|
23151
|
+
"claude-sonnet-5": "Sonnet 5"
|
|
23152
|
+
};
|
|
23153
|
+
return known[id] ?? id.replace(/^claude-/, "").replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
23154
|
+
}
|
|
23155
|
+
function buildNotice(subtype, m) {
|
|
23156
|
+
switch (subtype) {
|
|
23157
|
+
case "notification": {
|
|
23158
|
+
const text3 = sstr(m.text) || "Notification";
|
|
23159
|
+
const priority = ["low", "medium", "high", "immediate"].includes(sstr(m.priority)) ? m.priority : "medium";
|
|
23160
|
+
const level = priority === "immediate" ? "error" : priority === "high" ? "warning" : priority === "low" ? "info" : "notice";
|
|
23161
|
+
return { category: "notification", level, priority, title: text3, toast: true, inline: true, ...typeof m.timeout_ms === "number" ? { timeoutMs: m.timeout_ms } : {} };
|
|
23162
|
+
}
|
|
23163
|
+
case "informational": {
|
|
23164
|
+
const content = sstr(m.content).trim();
|
|
23165
|
+
if (!content)
|
|
23166
|
+
return null;
|
|
23167
|
+
const level = ["info", "notice", "suggestion", "warning"].includes(sstr(m.level)) ? m.level : "info";
|
|
23168
|
+
const nl = content.indexOf("\n");
|
|
23169
|
+
const firstLine = nl >= 0 ? content.slice(0, nl) : content;
|
|
23170
|
+
const remainder = nl >= 0 ? content.slice(nl + 1).trim() : "";
|
|
23171
|
+
const truncated = firstLine.length > 160;
|
|
23172
|
+
const title = truncated ? `${firstLine.slice(0, 159).trimEnd()}\u2026` : firstLine;
|
|
23173
|
+
const body = truncated ? content : remainder;
|
|
23174
|
+
return { category: "info", level, title, ...body ? { body } : {}, ...sstr(m.tool_use_id) ? { toolUseId: sstr(m.tool_use_id) } : {}, inline: true, toast: level === "warning" };
|
|
23175
|
+
}
|
|
23176
|
+
case "plugin_install": {
|
|
23177
|
+
const status2 = sstr(m.status);
|
|
23178
|
+
if (status2 !== "installed" && status2 !== "failed" && status2 !== "completed")
|
|
23179
|
+
return null;
|
|
23180
|
+
const name = sstr(m.name) || "plugin";
|
|
23181
|
+
const failed = status2 === "failed";
|
|
23182
|
+
return { category: "plugin", level: failed ? "error" : "success", title: failed ? `Plugin \u201C${name}\u201D failed to install` : `Installed plugin \u201C${name}\u201D`, ...failed && sstr(m.error) ? { body: sstr(m.error) } : {}, toast: true, inline: true };
|
|
23183
|
+
}
|
|
23184
|
+
case "memory_recall": {
|
|
23185
|
+
const memories = Array.isArray(m.memories) ? m.memories : [];
|
|
23186
|
+
if (!memories.length)
|
|
23187
|
+
return null;
|
|
23188
|
+
const names = memories.map((x) => sstr(x.path).split(/[/\\]/).pop() || sstr(x.path)).filter(Boolean);
|
|
23189
|
+
return { category: "memory", level: "info", title: `Recalled ${memories.length} ${memories.length === 1 ? "memory" : "memories"}`, ...names.length ? { body: names.join(" \xB7 ") } : {}, toast: false, inline: true };
|
|
23190
|
+
}
|
|
23191
|
+
case "permission_denied": {
|
|
23192
|
+
const tool9 = sstr(m.tool_name) || "a tool";
|
|
23193
|
+
const reason = sstr(m.decision_reason) || sstr(m.message);
|
|
23194
|
+
return { category: "permission", level: "warning", title: `Blocked: ${tool9}`, ...reason ? { body: reason } : {}, ...sstr(m.tool_use_id) ? { toolUseId: sstr(m.tool_use_id) } : {}, toast: true, inline: true };
|
|
23195
|
+
}
|
|
23196
|
+
case "model_refusal_fallback": {
|
|
23197
|
+
const orig = sstr(m.original_model);
|
|
23198
|
+
const fb = sstr(m.fallback_model);
|
|
23199
|
+
const cat = sstr(m.api_refusal_category);
|
|
23200
|
+
const expl = sstr(m.api_refusal_explanation) || sstr(m.content);
|
|
23201
|
+
const oL = prettifyModel(orig);
|
|
23202
|
+
const fL = prettifyModel(fb);
|
|
23203
|
+
const reverted = sstr(m.direction) === "revert";
|
|
23204
|
+
return {
|
|
23205
|
+
category: "model",
|
|
23206
|
+
level: "warning",
|
|
23207
|
+
priority: "high",
|
|
23208
|
+
title: reverted ? `${oL} declined this request${cat ? ` (${cat})` : ""} \u2014 retried on ${fL}` : `${oL} declined this request${cat ? ` (${cat})` : ""} \u2014 now on ${fL}`,
|
|
23209
|
+
...expl ? { body: expl } : {},
|
|
23210
|
+
...!reverted && fb ? { setModel: fb } : {},
|
|
23211
|
+
toast: true,
|
|
23212
|
+
inline: true,
|
|
23213
|
+
actions: reverted ? [{ id: "pick", label: "Choose model\u2026", kind: "pick_model" }] : [
|
|
23214
|
+
...orig ? [{ id: "back", label: `Switch back to ${oL}`, style: "primary", kind: "switch_model", data: { model: orig } }] : [],
|
|
23215
|
+
{ id: "stay", label: `Stay on ${fL}`, kind: "dismiss" },
|
|
23216
|
+
{ id: "pick", label: "Choose model\u2026", kind: "pick_model" }
|
|
23217
|
+
]
|
|
23218
|
+
};
|
|
23219
|
+
}
|
|
23220
|
+
case "model_refusal_no_fallback": {
|
|
23221
|
+
const orig = sstr(m.original_model);
|
|
23222
|
+
const cat = sstr(m.api_refusal_category);
|
|
23223
|
+
const expl = sstr(m.api_refusal_explanation) || sstr(m.content);
|
|
23224
|
+
return {
|
|
23225
|
+
category: "model",
|
|
23226
|
+
level: "error",
|
|
23227
|
+
title: `${prettifyModel(orig)} declined this request${cat ? ` (${cat})` : ""}`,
|
|
23228
|
+
...expl ? { body: expl } : {},
|
|
23229
|
+
toast: true,
|
|
23230
|
+
inline: true,
|
|
23231
|
+
actions: [{ id: "pick", label: "Try another model\u2026", style: "primary", kind: "pick_model" }]
|
|
23232
|
+
};
|
|
23233
|
+
}
|
|
23234
|
+
default:
|
|
23235
|
+
return null;
|
|
23236
|
+
}
|
|
22995
23237
|
}
|
|
22996
23238
|
function parseTodos(rawInput) {
|
|
22997
23239
|
try {
|
|
@@ -23200,7 +23442,7 @@ MOBILE (${fw}):
|
|
|
23200
23442
|
return "";
|
|
23201
23443
|
}
|
|
23202
23444
|
}
|
|
23203
|
-
var cachedClaudeExe, cachedNodeExe, HUMAN_READABLE_FIELDS, TOOL_RESULT_CAP, MAX_RESULT_IMAGES, MAX_IMAGE_BASE64, PERMISSION_MODES, EDIT_PREVIEW_CAP, AsyncMessagePump, IDLE_REAP_MS, MODERATE_IDLE_MS, STEER_COALESCE_MS, STOP_HARD_KILL_AFTER_MS, AgentManager;
|
|
23445
|
+
var cachedClaudeExe, cachedNodeExe, HUMAN_READABLE_FIELDS, THINKING_THROTTLE_MS, SYSTEM_EVENT_CATEGORY, TOOL_RESULT_CAP, MAX_RESULT_IMAGES, MAX_IMAGE_BASE64, PERMISSION_MODES, EDIT_PREVIEW_CAP, AsyncMessagePump, IDLE_REAP_MS, MODERATE_IDLE_MS, STEER_COALESCE_MS, STOP_HARD_KILL_AFTER_MS, AgentManager;
|
|
23204
23446
|
var init_agent_manager = __esm({
|
|
23205
23447
|
"../../packages/agent-core/dist/agent-manager.js"() {
|
|
23206
23448
|
"use strict";
|
|
@@ -23222,7 +23464,17 @@ var init_agent_manager = __esm({
|
|
|
23222
23464
|
init_session_usage();
|
|
23223
23465
|
init_kill_tree();
|
|
23224
23466
|
init_log();
|
|
23467
|
+
init_sources();
|
|
23225
23468
|
HUMAN_READABLE_FIELDS = ["message", "text", "content", "summary", "description", "output", "stdout", "error", "reason", "name"];
|
|
23469
|
+
THINKING_THROTTLE_MS = 2e3;
|
|
23470
|
+
SYSTEM_EVENT_CATEGORY = {
|
|
23471
|
+
hook_started: "hook",
|
|
23472
|
+
hook_progress: "hook",
|
|
23473
|
+
thinking_tokens: "thinking"
|
|
23474
|
+
// task_* / files_persisted / worker_shutting_down are handled explicitly now;
|
|
23475
|
+
// get_context_usage / get_session_cost / background_tasks are outbound control-requests
|
|
23476
|
+
// we never receive — both dropped from here rather than kept as dead entries.
|
|
23477
|
+
};
|
|
23226
23478
|
TOOL_RESULT_CAP = 16e3;
|
|
23227
23479
|
MAX_RESULT_IMAGES = 4;
|
|
23228
23480
|
MAX_IMAGE_BASE64 = 15e5;
|
|
@@ -23315,6 +23567,9 @@ var init_agent_manager = __esm({
|
|
|
23315
23567
|
/** M5 H6 — parked MCP elicitation prompts, resolved when the client replies via
|
|
23316
23568
|
* chat:elicitation (→ respondToElicitation) or denied on abort. */
|
|
23317
23569
|
pendingElicitations = /* @__PURE__ */ new Map();
|
|
23570
|
+
/** request_user_dialog prompts (model-fallback / usage-consent), resolved when the
|
|
23571
|
+
* client replies via chat:user_dialog (→ respondToUserDialog) or cancelled on abort. */
|
|
23572
|
+
pendingUserDialogs = /* @__PURE__ */ new Map();
|
|
23318
23573
|
/** Skill-driven interactive UI requests (ask_form / present_choices), resolved when
|
|
23319
23574
|
* the client replies via chat:ui_response (→ respondToUiRequest) or the owning
|
|
23320
23575
|
* session is torn down (→ cancelled). Keyed by requestId; `sessionId` lets a
|
|
@@ -23622,6 +23877,159 @@ ${full.slice(-8e3)}`;
|
|
|
23622
23877
|
entry.resolve({ behavior: "deny", message: decision.message || "Denied by user.", ...decision.interrupt ? { interrupt: true } : {} });
|
|
23623
23878
|
}
|
|
23624
23879
|
}
|
|
23880
|
+
/** AskUserQuestion: park a promise, raise the multiple-choice question card
|
|
23881
|
+
* (agent:permission_request, toolName='AskUserQuestion'), and resolve it from the
|
|
23882
|
+
* client's chat:permission reply — which lands in respondToPermission carrying
|
|
23883
|
+
* `updatedInput.answers`. Deliberately reuses pendingPermissions + the
|
|
23884
|
+
* PermissionResult shape so the existing answer round-trip (client card →
|
|
23885
|
+
* respondToPermission) is unchanged; the PreToolUse hook maps the resolved
|
|
23886
|
+
* PermissionResult into hookSpecificOutput. Driven from a PreToolUse hook rather
|
|
23887
|
+
* than canUseTool because hooks fire in EVERY permission mode — bypassPermissions
|
|
23888
|
+
* (the app default) shadows canUseTool but never hooks. Mirrors canUseTool's
|
|
23889
|
+
* abort/cleanup handling so an interrupted turn never leaks a parked card. */
|
|
23890
|
+
raiseQuestion(live, input, toolUseId, signal, agentId) {
|
|
23891
|
+
return new Promise((resolve10) => {
|
|
23892
|
+
const requestId = randomUUID2();
|
|
23893
|
+
const onAbort = () => {
|
|
23894
|
+
if (this.pendingPermissions.delete(requestId))
|
|
23895
|
+
resolve10({ behavior: "deny", message: "Interrupted.", interrupt: true });
|
|
23896
|
+
};
|
|
23897
|
+
const cleanup2 = () => signal.removeEventListener("abort", onAbort);
|
|
23898
|
+
if (signal.aborted) {
|
|
23899
|
+
resolve10({ behavior: "deny", message: "Interrupted.", interrupt: true });
|
|
23900
|
+
return;
|
|
23901
|
+
}
|
|
23902
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
23903
|
+
this.pendingPermissions.set(requestId, { resolve: resolve10, cleanup: cleanup2, origin: live.origin });
|
|
23904
|
+
const willEvaluate = !!this.cheapLlm && Array.isArray(input.questions) && input.questions.length > 0;
|
|
23905
|
+
const evt = {
|
|
23906
|
+
type: "agent:permission_request",
|
|
23907
|
+
sessionId: live.sessionId,
|
|
23908
|
+
requestId,
|
|
23909
|
+
toolName: "AskUserQuestion",
|
|
23910
|
+
input,
|
|
23911
|
+
...toolUseId ? { toolUseId } : {},
|
|
23912
|
+
...agentId ? { agentId } : {},
|
|
23913
|
+
...willEvaluate ? { recommendationsPending: true } : {}
|
|
23914
|
+
};
|
|
23915
|
+
if (!live.sessionId && live.clientRequestId)
|
|
23916
|
+
this.send({ ...evt, clientRequestId: live.clientRequestId }, live.origin);
|
|
23917
|
+
else
|
|
23918
|
+
this.send(evt, live.origin);
|
|
23919
|
+
if (willEvaluate)
|
|
23920
|
+
void this.evaluateOptions(live, requestId, input);
|
|
23921
|
+
});
|
|
23922
|
+
}
|
|
23923
|
+
/** Compile a compact context summary for the option-recommendation LLM: the project,
|
|
23924
|
+
* the user's current request, and what the assistant just said (why it's asking). */
|
|
23925
|
+
compileQuestionContext(live) {
|
|
23926
|
+
const parts = [];
|
|
23927
|
+
if (live.cwd)
|
|
23928
|
+
parts.push(`Project directory: ${live.cwd}`);
|
|
23929
|
+
if (live.lastPrompt)
|
|
23930
|
+
parts.push(`The user's current request:
|
|
23931
|
+
${live.lastPrompt.slice(0, 1400)}`);
|
|
23932
|
+
if (Array.isArray(live.lastAssistantContent)) {
|
|
23933
|
+
const text3 = live.lastAssistantContent.filter((b) => b?.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n").trim();
|
|
23934
|
+
if (text3)
|
|
23935
|
+
parts.push(`What the assistant just said (leading to this question):
|
|
23936
|
+
${text3.slice(0, 1600)}`);
|
|
23937
|
+
}
|
|
23938
|
+
return parts.join("\n\n") || "No additional context available beyond the question itself.";
|
|
23939
|
+
}
|
|
23940
|
+
/** Rate every option of an AskUserQuestion for the user's situation via the cheap LLM
|
|
23941
|
+
* (structured JSON, 5 levels), then emit agent:option_recommendations. Best-effort:
|
|
23942
|
+
* any failure (no model, bad JSON, card already answered) simply yields no badges. */
|
|
23943
|
+
async evaluateOptions(live, requestId, input) {
|
|
23944
|
+
const llm = this.cheapLlm;
|
|
23945
|
+
if (!llm)
|
|
23946
|
+
return;
|
|
23947
|
+
const questions = Array.isArray(input.questions) ? input.questions : [];
|
|
23948
|
+
if (!questions.length)
|
|
23949
|
+
return;
|
|
23950
|
+
try {
|
|
23951
|
+
const qText = questions.map((q, qi) => {
|
|
23952
|
+
const opts = Array.isArray(q.options) ? q.options : [];
|
|
23953
|
+
const lines = opts.map((o) => ` - "${String(o.label)}"${o.description ? `: ${String(o.description)}` : ""}`).join("\n");
|
|
23954
|
+
return `Question ${qi}: ${String(q.question)}
|
|
23955
|
+
Options:
|
|
23956
|
+
${lines}`;
|
|
23957
|
+
}).join("\n\n");
|
|
23958
|
+
const system = `You advise a user who is answering multiple-choice questions posed by a coding agent mid-task. For EVERY option of EVERY question, rate how advisable it is FOR THIS USER'S SPECIFIC SITUATION, using exactly one of these five levels: "highly_recommended", "recommended", "neutral", "caution", "discouraged". Be decisive and honest \u2014 most questions have one or two clearly better choices; do not rate everything neutral. Ground each rating in the provided context. Respond with ONLY a JSON object, no prose: {"recommendations":[{"questionIndex":<int>,"optionLabel":"<verbatim label>","level":"<level>","reason":"<max 12 words>"}]}. Include every option.`;
|
|
23959
|
+
const prompt = `## Context
|
|
23960
|
+
${this.compileQuestionContext(live)}
|
|
23961
|
+
|
|
23962
|
+
## Questions and options
|
|
23963
|
+
${qText}`;
|
|
23964
|
+
const out = await llm({ system, prompt, maxTokens: 1400 });
|
|
23965
|
+
if (!out)
|
|
23966
|
+
return;
|
|
23967
|
+
const parsed = JSON.parse(out.slice(out.indexOf("{"), out.lastIndexOf("}") + 1));
|
|
23968
|
+
const LEVELS = /* @__PURE__ */ new Set(["highly_recommended", "recommended", "neutral", "caution", "discouraged"]);
|
|
23969
|
+
const clean = (Array.isArray(parsed.recommendations) ? parsed.recommendations : []).map((r) => r).filter((r) => r && typeof r.optionLabel === "string" && typeof r.questionIndex === "number" && typeof r.level === "string" && LEVELS.has(r.level)).map((r) => ({ questionIndex: r.questionIndex, optionLabel: r.optionLabel, level: r.level, reason: typeof r.reason === "string" ? r.reason.slice(0, 140) : "" }));
|
|
23970
|
+
if (!clean.length || !this.pendingPermissions.has(requestId))
|
|
23971
|
+
return;
|
|
23972
|
+
const evt = { type: "agent:option_recommendations", sessionId: live.sessionId, requestId, recommendations: clean };
|
|
23973
|
+
if (!live.sessionId && live.clientRequestId)
|
|
23974
|
+
this.send({ ...evt, clientRequestId: live.clientRequestId }, live.origin);
|
|
23975
|
+
else
|
|
23976
|
+
this.send(evt, live.origin);
|
|
23977
|
+
} catch (err) {
|
|
23978
|
+
log("option recommendation eval failed:", err instanceof Error ? err.message : String(err));
|
|
23979
|
+
}
|
|
23980
|
+
}
|
|
23981
|
+
/** request_user_dialog: park a promise, raise the dialog card
|
|
23982
|
+
* (agent:user_dialog_request), and resolve it from the client's chat:user_dialog
|
|
23983
|
+
* reply (→ respondToUserDialog). Mirrors onElicitation: the CLI parks the flow until
|
|
23984
|
+
* we answer; an unanswered/aborted dialog resolves 'cancelled' so the CLI applies the
|
|
23985
|
+
* dialog's default behavior (e.g. the classic refusal message) rather than hanging. */
|
|
23986
|
+
raiseUserDialog(live, request, signal) {
|
|
23987
|
+
return new Promise((resolve10) => {
|
|
23988
|
+
const requestId = randomUUID2();
|
|
23989
|
+
log("user_dialog raised:", request.dialogKind, "requestId=" + requestId, "payloadKeys=" + Object.keys(request.payload ?? {}).join(","));
|
|
23990
|
+
const onAbort = () => {
|
|
23991
|
+
if (this.pendingUserDialogs.delete(requestId)) {
|
|
23992
|
+
log("user_dialog aborted (cancelled):", request.dialogKind, requestId);
|
|
23993
|
+
resolve10({ behavior: "cancelled" });
|
|
23994
|
+
}
|
|
23995
|
+
};
|
|
23996
|
+
const cleanup2 = () => signal.removeEventListener("abort", onAbort);
|
|
23997
|
+
if (signal.aborted) {
|
|
23998
|
+
log("user_dialog pre-aborted (cancelled):", request.dialogKind);
|
|
23999
|
+
resolve10({ behavior: "cancelled" });
|
|
24000
|
+
return;
|
|
24001
|
+
}
|
|
24002
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
24003
|
+
this.pendingUserDialogs.set(requestId, { resolve: resolve10, cleanup: cleanup2, origin: live.origin });
|
|
24004
|
+
const evt = {
|
|
24005
|
+
type: "agent:user_dialog_request",
|
|
24006
|
+
sessionId: live.sessionId,
|
|
24007
|
+
requestId,
|
|
24008
|
+
dialogKind: request.dialogKind,
|
|
24009
|
+
payload: request.payload ?? {},
|
|
24010
|
+
...request.toolUseID ? { toolUseId: request.toolUseID } : {}
|
|
24011
|
+
};
|
|
24012
|
+
if (!live.sessionId && live.clientRequestId)
|
|
24013
|
+
this.send({ ...evt, clientRequestId: live.clientRequestId }, live.origin);
|
|
24014
|
+
else
|
|
24015
|
+
this.send(evt, live.origin);
|
|
24016
|
+
});
|
|
24017
|
+
}
|
|
24018
|
+
/** Resolve a parked request_user_dialog (client replied via chat:user_dialog).
|
|
24019
|
+
* Driver-only, same origin guard as respondToPermission. */
|
|
24020
|
+
respondToUserDialog(requestId, decision, callerOrigin) {
|
|
24021
|
+
const entry = this.pendingUserDialogs.get(requestId);
|
|
24022
|
+
if (!entry) {
|
|
24023
|
+
log("user_dialog response for unknown/expired requestId:", requestId);
|
|
24024
|
+
return;
|
|
24025
|
+
}
|
|
24026
|
+
if (entry.origin && callerOrigin && entry.origin !== callerOrigin)
|
|
24027
|
+
return;
|
|
24028
|
+
this.pendingUserDialogs.delete(requestId);
|
|
24029
|
+
entry.cleanup();
|
|
24030
|
+
log("user_dialog answered:", requestId, decision.behavior === "completed" ? `result=${JSON.stringify(decision.result)}` : "cancelled");
|
|
24031
|
+
entry.resolve(decision.behavior === "completed" ? { behavior: "completed", result: decision.result } : { behavior: "cancelled" });
|
|
24032
|
+
}
|
|
23625
24033
|
/** M5 H6 — resolve a parked MCP elicitation (client replied via chat:elicitation).
|
|
23626
24034
|
* Driver-only, same origin guard as respondToPermission. */
|
|
23627
24035
|
respondToElicitation(requestId, decision, callerOrigin) {
|
|
@@ -24306,6 +24714,10 @@ CRITICAL RULES:
|
|
|
24306
24714
|
// resolve it when the client replies via chat:permission (respondToPermission),
|
|
24307
24715
|
// or deny on abort so an interrupted turn never leaks a pending decision.
|
|
24308
24716
|
canUseTool: (toolName, input, opts) => new Promise((resolve10) => {
|
|
24717
|
+
if (toolName === "AskUserQuestion") {
|
|
24718
|
+
resolve10({ behavior: "allow" });
|
|
24719
|
+
return;
|
|
24720
|
+
}
|
|
24309
24721
|
const requestId = randomUUID2();
|
|
24310
24722
|
const onAbort = () => {
|
|
24311
24723
|
if (this.pendingPermissions.delete(requestId)) {
|
|
@@ -24374,12 +24786,38 @@ CRITICAL RULES:
|
|
|
24374
24786
|
else
|
|
24375
24787
|
this.send(evt, live.origin);
|
|
24376
24788
|
}),
|
|
24789
|
+
// request_user_dialog — blocking model-fallback / usage-consent dialogs the CLI
|
|
24790
|
+
// asks the host to render. Without this the flow degrades to no-dialog (a model
|
|
24791
|
+
// refusal ends the turn with the classic error). We declare the kinds we render
|
|
24792
|
+
// (supportedDialogKinds); the CLI fails closed and never emits an undeclared kind.
|
|
24793
|
+
onUserDialog: (request, opts) => this.raiseUserDialog(live, request, opts.signal),
|
|
24794
|
+
supportedDialogKinds: ["refusal_fallback_prompt", "fable_overage_consent_prompt"],
|
|
24377
24795
|
// Capture file edits for the client's "Review changes" (see pendingEdits
|
|
24378
24796
|
// above). Bash-driven writes aren't visible here — only the editing tools.
|
|
24379
24797
|
// Subagent tool calls (agent_id set) are skipped: their tool blocks aren't
|
|
24380
24798
|
// in the visible stream, so an emitted edit would convert the wrong card.
|
|
24381
24799
|
hooks: {
|
|
24382
24800
|
PreToolUse: [{
|
|
24801
|
+
// AskUserQuestion is an INTERACTIVE tool with no server-side executor: its
|
|
24802
|
+
// "result" IS the user's selection. If it isn't surfaced and answered, the
|
|
24803
|
+
// tool_use dangles and the whole turn stalls (no result ever arrives).
|
|
24804
|
+
// canUseTool can't carry it: in bypassPermissions (the app default) the SDK
|
|
24805
|
+
// auto-approves every tool BEFORE canUseTool runs — it even logs
|
|
24806
|
+
// CLAUDE_SDK_CAN_USE_TOOL_SHADOWED — so the question card never shows. A
|
|
24807
|
+
// PreToolUse hook fires in EVERY permission mode and can return updatedInput,
|
|
24808
|
+
// so we raise the card here and bake the chosen answers back into the input.
|
|
24809
|
+
matcher: "AskUserQuestion",
|
|
24810
|
+
hooks: [async (input, _toolUseId, opts) => {
|
|
24811
|
+
if (input.hook_event_name !== "PreToolUse" || input.tool_name !== "AskUserQuestion")
|
|
24812
|
+
return {};
|
|
24813
|
+
const original = input.tool_input ?? {};
|
|
24814
|
+
const decision = await this.raiseQuestion(live, original, input.tool_use_id, opts.signal, input.agent_id);
|
|
24815
|
+
if (decision.behavior === "allow") {
|
|
24816
|
+
return { hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "allow", updatedInput: { ...original, ...decision.updatedInput ?? {} } } };
|
|
24817
|
+
}
|
|
24818
|
+
return { hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: decision.message || "The user dismissed the question without answering." } };
|
|
24819
|
+
}]
|
|
24820
|
+
}, {
|
|
24383
24821
|
matcher: "Edit|Write|MultiEdit|NotebookEdit",
|
|
24384
24822
|
hooks: [async (input) => {
|
|
24385
24823
|
if (input.hook_event_name === "PreToolUse" && !input.agent_id) {
|
|
@@ -24458,10 +24896,14 @@ CRITICAL RULES:
|
|
|
24458
24896
|
|
|
24459
24897
|
${params.appendSystemPrompt}` : "") },
|
|
24460
24898
|
includePartialMessages: true,
|
|
24461
|
-
//
|
|
24462
|
-
//
|
|
24463
|
-
|
|
24464
|
-
|
|
24899
|
+
// Emit a predicted follow-up prompt after each turn (git/context-aware) — rendered
|
|
24900
|
+
// as a clickable suggestion chip. Off by default in the SDK, so opt in here.
|
|
24901
|
+
promptSuggestions: true,
|
|
24902
|
+
// Opt into AskUserQuestion option previews as self-contained HTML fragments —
|
|
24903
|
+
// the card renders them as live, sandboxed mockups (an actual styled button,
|
|
24904
|
+
// not its source). Without a previewFormat the SDK omits the preview guidance
|
|
24905
|
+
// and the model never emits previews.
|
|
24906
|
+
toolConfig: { askUserQuestion: { previewFormat: "html" } },
|
|
24465
24907
|
// M7 — structured output: constrain the final result to a JSON Schema.
|
|
24466
24908
|
...params.jsonSchema ? { outputFormat: { type: "json_schema", schema: params.jsonSchema } } : {},
|
|
24467
24909
|
// Customize panel: inline-key parity surfaces. These keep the SDK
|
|
@@ -24529,6 +24971,36 @@ ${params.appendSystemPrompt}` : "") },
|
|
|
24529
24971
|
log("send callback threw (ignored):", err instanceof Error ? err.message : String(err));
|
|
24530
24972
|
}
|
|
24531
24973
|
};
|
|
24974
|
+
const sendEvent = (spec) => {
|
|
24975
|
+
emit({ type: "agent:framework_event", sessionId: live.sessionId, ...spec });
|
|
24976
|
+
};
|
|
24977
|
+
const flushPendingThinking = () => {
|
|
24978
|
+
if (live.pendingThinking) {
|
|
24979
|
+
sendEvent(live.pendingThinking);
|
|
24980
|
+
live.pendingThinking = void 0;
|
|
24981
|
+
live.lastThinkingEmitAt = Date.now();
|
|
24982
|
+
}
|
|
24983
|
+
};
|
|
24984
|
+
const emitNotice = (spec) => {
|
|
24985
|
+
flushPendingThinking();
|
|
24986
|
+
emit({ type: "agent:notice", sessionId: live.sessionId, noticeId: randomUUID2(), ...spec });
|
|
24987
|
+
};
|
|
24988
|
+
const emitEvent = (spec, parentId) => {
|
|
24989
|
+
const payload = { ...spec, ...parentId ? { parentToolUseId: parentId } : {} };
|
|
24990
|
+
if (spec.updateKey === "thinking_tokens") {
|
|
24991
|
+
const now2 = Date.now();
|
|
24992
|
+
if (now2 - (live.lastThinkingEmitAt ?? 0) >= THINKING_THROTTLE_MS) {
|
|
24993
|
+
live.lastThinkingEmitAt = now2;
|
|
24994
|
+
live.pendingThinking = void 0;
|
|
24995
|
+
sendEvent(payload);
|
|
24996
|
+
} else {
|
|
24997
|
+
live.pendingThinking = payload;
|
|
24998
|
+
}
|
|
24999
|
+
return;
|
|
25000
|
+
}
|
|
25001
|
+
flushPendingThinking();
|
|
25002
|
+
sendEvent(payload);
|
|
25003
|
+
};
|
|
24532
25004
|
const refreshSessionUsage = (sessionId) => {
|
|
24533
25005
|
void getSessionUsage(live.engineEnv).then((usage) => {
|
|
24534
25006
|
if (usage && (usage.session || usage.weekly)) {
|
|
@@ -24544,6 +25016,7 @@ ${params.appendSystemPrompt}` : "") },
|
|
|
24544
25016
|
live.trace = { turnId: crypto.randomUUID(), startedAt: Date.now(), frames: [] };
|
|
24545
25017
|
if (live.trace.frames.length < 2e3)
|
|
24546
25018
|
live.trace.frames.push(message);
|
|
25019
|
+
traceFrame(message);
|
|
24547
25020
|
if (!live.sessionId && "session_id" in message && message.session_id) {
|
|
24548
25021
|
const sid2 = message.session_id;
|
|
24549
25022
|
live.sessionId = sid2;
|
|
@@ -24566,6 +25039,7 @@ ${params.appendSystemPrompt}` : "") },
|
|
|
24566
25039
|
if (event.type === "content_block_start") {
|
|
24567
25040
|
const cbt = event.content_block?.type;
|
|
24568
25041
|
if ((cbt === "tool_use" || cbt === "server_tool_use") && event.content_block?.name) {
|
|
25042
|
+
flushPendingThinking();
|
|
24569
25043
|
const isServerTool = cbt === "server_tool_use";
|
|
24570
25044
|
const id = event.content_block.id ?? "";
|
|
24571
25045
|
blockAcc.set(key2, { name: event.content_block.name, input: "", isServerTool, id });
|
|
@@ -24659,6 +25133,7 @@ ${params.appendSystemPrompt}` : "") },
|
|
|
24659
25133
|
break;
|
|
24660
25134
|
}
|
|
24661
25135
|
case "result": {
|
|
25136
|
+
flushPendingThinking();
|
|
24662
25137
|
const r = message;
|
|
24663
25138
|
const turnSid = r.session_id || sid;
|
|
24664
25139
|
this.narrator.endTurn(turnSid);
|
|
@@ -24695,6 +25170,9 @@ ${params.appendSystemPrompt}` : "") },
|
|
|
24695
25170
|
}
|
|
24696
25171
|
}
|
|
24697
25172
|
}
|
|
25173
|
+
const sources = extractSources(live.lastAssistantContent);
|
|
25174
|
+
if (sources.length > 0)
|
|
25175
|
+
emit({ type: "agent:sources", sessionId: turnSid, messageId: turnMessageId, sources });
|
|
24698
25176
|
const usage = r.usage;
|
|
24699
25177
|
const continuable = r.stop_reason === "max_tokens";
|
|
24700
25178
|
emit({
|
|
@@ -24742,14 +25220,14 @@ ${params.appendSystemPrompt}` : "") },
|
|
|
24742
25220
|
}
|
|
24743
25221
|
case "auth_status": {
|
|
24744
25222
|
const m = message;
|
|
24745
|
-
|
|
24746
|
-
|
|
24747
|
-
|
|
24748
|
-
|
|
24749
|
-
|
|
24750
|
-
|
|
24751
|
-
|
|
24752
|
-
|
|
25223
|
+
emitEvent({
|
|
25224
|
+
category: "auth",
|
|
25225
|
+
name: "auth_status",
|
|
25226
|
+
title: m.error ? "Authentication error" : m.isAuthenticating ? "Authenticating\u2026" : "Auth status",
|
|
25227
|
+
...m.error ? { fields: [{ label: "Error", value: m.error }] } : {},
|
|
25228
|
+
...m.output?.length ? { detail: m.output.join("\n") } : {},
|
|
25229
|
+
updateKey: "auth_status"
|
|
25230
|
+
});
|
|
24753
25231
|
break;
|
|
24754
25232
|
}
|
|
24755
25233
|
case "rate_limit_event": {
|
|
@@ -24765,46 +25243,121 @@ ${params.appendSystemPrompt}` : "") },
|
|
|
24765
25243
|
const attempt = m.attempt ?? "?";
|
|
24766
25244
|
const max = m.max_retries ?? "?";
|
|
24767
25245
|
const errStatus = m.error_status ? ` (HTTP ${m.error_status})` : "";
|
|
24768
|
-
|
|
24769
|
-
|
|
24770
|
-
|
|
25246
|
+
emitEvent({
|
|
25247
|
+
category: "retry",
|
|
25248
|
+
name: "api_retry",
|
|
25249
|
+
title: `Retrying API call (${attempt}/${max})${errStatus}`,
|
|
25250
|
+
...m.error_status ? { fields: [{ label: "HTTP", value: String(m.error_status) }] } : {}
|
|
25251
|
+
});
|
|
24771
25252
|
break;
|
|
24772
25253
|
}
|
|
25254
|
+
// Subagent (Task tool) lifecycle → one live card per task_id (agent:task).
|
|
24773
25255
|
case "task_started": {
|
|
24774
|
-
const
|
|
24775
|
-
|
|
24776
|
-
|
|
24777
|
-
|
|
24778
|
-
emit({
|
|
25256
|
+
const taskId = sstr(m.task_id);
|
|
25257
|
+
if (!taskId)
|
|
25258
|
+
break;
|
|
25259
|
+
live.taskIds.add(taskId);
|
|
25260
|
+
emit({
|
|
25261
|
+
type: "agent:task",
|
|
25262
|
+
sessionId: sid,
|
|
25263
|
+
taskId,
|
|
25264
|
+
phase: "started",
|
|
25265
|
+
status: "running",
|
|
25266
|
+
...sstr(m.tool_use_id) ? { toolUseId: sstr(m.tool_use_id) } : {},
|
|
25267
|
+
...parentToolUseId ? { parentToolUseId } : {},
|
|
25268
|
+
description: sstr(m.description) || "subagent task",
|
|
25269
|
+
...sstr(m.subagent_type) ? { subagentType: sstr(m.subagent_type) } : {},
|
|
25270
|
+
...sstr(m.workflow_name) ? { workflowName: sstr(m.workflow_name) } : {}
|
|
25271
|
+
});
|
|
25272
|
+
break;
|
|
25273
|
+
}
|
|
25274
|
+
case "task_progress": {
|
|
25275
|
+
const taskId = sstr(m.task_id);
|
|
25276
|
+
if (!taskId)
|
|
25277
|
+
break;
|
|
25278
|
+
const u = m.usage;
|
|
25279
|
+
emit({
|
|
25280
|
+
type: "agent:task",
|
|
25281
|
+
sessionId: sid,
|
|
25282
|
+
taskId,
|
|
25283
|
+
phase: "progress",
|
|
25284
|
+
status: "running",
|
|
25285
|
+
...sstr(m.description) ? { description: sstr(m.description) } : {},
|
|
25286
|
+
...sstr(m.subagent_type) ? { subagentType: sstr(m.subagent_type) } : {},
|
|
25287
|
+
...sstr(m.last_tool_name) ? { lastToolName: sstr(m.last_tool_name) } : {},
|
|
25288
|
+
...sstr(m.summary) ? { summary: sstr(m.summary) } : {},
|
|
25289
|
+
...u ? { usage: { totalTokens: u.total_tokens ?? 0, toolUses: u.tool_uses ?? 0, durationMs: u.duration_ms ?? 0 } } : {}
|
|
25290
|
+
});
|
|
25291
|
+
break;
|
|
25292
|
+
}
|
|
25293
|
+
case "task_updated": {
|
|
25294
|
+
const taskId = sstr(m.task_id);
|
|
25295
|
+
if (!taskId)
|
|
25296
|
+
break;
|
|
25297
|
+
const patch = m.patch && typeof m.patch === "object" ? m.patch : {};
|
|
25298
|
+
const status2 = ["pending", "running", "completed", "failed", "killed", "paused"].includes(sstr(patch.status)) ? patch.status : void 0;
|
|
25299
|
+
emit({
|
|
25300
|
+
type: "agent:task",
|
|
25301
|
+
sessionId: sid,
|
|
25302
|
+
taskId,
|
|
25303
|
+
phase: "updated",
|
|
25304
|
+
...status2 ? { status: status2 } : {},
|
|
25305
|
+
...sstr(patch.description) ? { description: sstr(patch.description) } : {},
|
|
25306
|
+
...sstr(patch.error) ? { error: sstr(patch.error) } : {}
|
|
25307
|
+
});
|
|
25308
|
+
if (status2 === "completed" || status2 === "failed" || status2 === "killed")
|
|
25309
|
+
live.taskIds.delete(taskId);
|
|
24779
25310
|
break;
|
|
24780
25311
|
}
|
|
24781
25312
|
case "task_notification": {
|
|
24782
|
-
const
|
|
24783
|
-
|
|
24784
|
-
|
|
24785
|
-
|
|
24786
|
-
|
|
24787
|
-
|
|
25313
|
+
const taskId = sstr(m.task_id);
|
|
25314
|
+
if (!taskId)
|
|
25315
|
+
break;
|
|
25316
|
+
const status2 = ["completed", "failed", "stopped"].includes(sstr(m.status)) ? m.status : "completed";
|
|
25317
|
+
const u = m.usage;
|
|
25318
|
+
emit({
|
|
25319
|
+
type: "agent:task",
|
|
25320
|
+
sessionId: sid,
|
|
25321
|
+
taskId,
|
|
25322
|
+
phase: "done",
|
|
25323
|
+
status: status2,
|
|
25324
|
+
...sstr(m.summary) ? { summary: sstr(m.summary) } : {},
|
|
25325
|
+
...u ? { usage: { totalTokens: u.total_tokens ?? 0, toolUses: u.tool_uses ?? 0, durationMs: u.duration_ms ?? 0 } } : {}
|
|
25326
|
+
});
|
|
25327
|
+
live.taskIds.delete(taskId);
|
|
24788
25328
|
break;
|
|
24789
25329
|
}
|
|
25330
|
+
// Slash-command output is user-requested content — a visible inline notice
|
|
25331
|
+
// (NOT the geek-only debug feed).
|
|
24790
25332
|
case "local_command_output": {
|
|
24791
|
-
const content = m.content
|
|
24792
|
-
if (content
|
|
24793
|
-
|
|
24794
|
-
|
|
24795
|
-
|
|
25333
|
+
const content = sstr(m.content).trim();
|
|
25334
|
+
if (content)
|
|
25335
|
+
emitNotice({ category: "info", level: "info", title: "Command output", body: content, inline: true, toast: false });
|
|
25336
|
+
break;
|
|
25337
|
+
}
|
|
25338
|
+
// The session worker is stopping — surface it so an abrupt halt is explained.
|
|
25339
|
+
case "worker_shutting_down": {
|
|
25340
|
+
emitNotice({ category: "info", level: "warning", title: "Session worker is restarting", ...sstr(m.reason) ? { body: sstr(m.reason) } : {}, toast: true, inline: true });
|
|
25341
|
+
break;
|
|
25342
|
+
}
|
|
25343
|
+
// File persistence — only surface FAILURES (successes stay quiet in the log).
|
|
25344
|
+
case "files_persisted": {
|
|
25345
|
+
const failed = Array.isArray(m.failed) ? m.failed : [];
|
|
25346
|
+
if (failed.length) {
|
|
25347
|
+
emitNotice({ category: "info", level: "warning", title: `${failed.length} file${failed.length === 1 ? "" : "s"} failed to save`, body: failed.map((f) => `${sstr(f.filename)}: ${sstr(f.error)}`).join("\n"), toast: true, inline: true });
|
|
24796
25348
|
}
|
|
24797
25349
|
break;
|
|
24798
25350
|
}
|
|
24799
25351
|
case "hook_response": {
|
|
24800
|
-
const
|
|
24801
|
-
const
|
|
24802
|
-
const
|
|
24803
|
-
|
|
24804
|
-
|
|
24805
|
-
|
|
24806
|
-
|
|
24807
|
-
|
|
25352
|
+
const hookName = sstr(m.hook_name) || "hook";
|
|
25353
|
+
const out = (sstr(m.stderr) || sstr(m.stdout) || sstr(m.output)).trim();
|
|
25354
|
+
const exit = typeof m.exit_code === "number" ? m.exit_code : void 0;
|
|
25355
|
+
const failed = sstr(m.outcome) === "error" || exit !== void 0 && exit !== 0;
|
|
25356
|
+
if (failed) {
|
|
25357
|
+
const body = [sstr(m.hook_event) ? `Event: ${sstr(m.hook_event)}` : "", exit !== void 0 ? `Exit ${exit}` : "", out].filter(Boolean).join("\n").slice(0, 800);
|
|
25358
|
+
emitNotice({ category: "hook", level: "warning", title: `Hook failed: ${hookName}`, ...body ? { body } : {}, toast: true, inline: true });
|
|
25359
|
+
} else if (out) {
|
|
25360
|
+
emitEvent({ category: "hook", name: "hook_response", title: `Hook response \u2014 ${hookName}`, detail: out, updateKey: `hook:${hookName}` });
|
|
24808
25361
|
}
|
|
24809
25362
|
break;
|
|
24810
25363
|
}
|
|
@@ -24839,9 +25392,7 @@ ${out}` });
|
|
|
24839
25392
|
// M4 H11 — compaction boundary: clear the "compacting" indicator + a notice.
|
|
24840
25393
|
case "compact_boundary": {
|
|
24841
25394
|
emit({ type: "agent:session_state", sessionId: sid, state: "running" });
|
|
24842
|
-
|
|
24843
|
-
|
|
24844
|
-
Compacted earlier context.` });
|
|
25395
|
+
emitEvent({ category: "lifecycle", name: "compact_boundary", title: "Compacted earlier context", fields: salientFields(m) });
|
|
24845
25396
|
break;
|
|
24846
25397
|
}
|
|
24847
25398
|
// M4 H11 — authoritative idle/running/requires_action. Never drains
|
|
@@ -24856,20 +25407,37 @@ Compacted earlier context.` });
|
|
|
24856
25407
|
emit({ type: "agent:elicitation_complete", sessionId: sid, serverName: m.mcp_server_name ?? "", elicitationId: m.elicitation_id ?? "" });
|
|
24857
25408
|
break;
|
|
24858
25409
|
}
|
|
24859
|
-
//
|
|
24860
|
-
|
|
24861
|
-
case "
|
|
24862
|
-
|
|
24863
|
-
|
|
25410
|
+
// Slash-command set changed (plugin loaded, .claude/commands edited) — push
|
|
25411
|
+
// the fresh list so the command palette updates without a restart.
|
|
25412
|
+
case "commands_changed": {
|
|
25413
|
+
const commands = (Array.isArray(m.commands) ? m.commands : []).flatMap((c) => {
|
|
25414
|
+
if (!c || typeof c !== "object")
|
|
25415
|
+
return [];
|
|
25416
|
+
const o = c;
|
|
25417
|
+
if (typeof o.name !== "string")
|
|
25418
|
+
return [];
|
|
25419
|
+
return [{ name: o.name, description: typeof o.description === "string" ? o.description : "", ...typeof o.hint === "string" ? { hint: o.hint } : {} }];
|
|
25420
|
+
});
|
|
25421
|
+
this.send({ type: "commands:list", commands }, live.origin);
|
|
24864
25422
|
break;
|
|
25423
|
+
}
|
|
25424
|
+
// Subtypes that earn a real user-facing notice (toast + inline card + native
|
|
25425
|
+
// when unfocused) rather than the raw debug feed: model-fallback, blocked
|
|
25426
|
+
// tools, plugin installs, leveled info, memory recall.
|
|
25427
|
+
case "notification":
|
|
25428
|
+
case "informational":
|
|
25429
|
+
case "plugin_install":
|
|
25430
|
+
case "memory_recall":
|
|
25431
|
+
case "permission_denied":
|
|
25432
|
+
case "model_refusal_fallback":
|
|
25433
|
+
case "model_refusal_no_fallback": {
|
|
25434
|
+
const notice = buildNotice(subtype, m);
|
|
25435
|
+
if (notice)
|
|
25436
|
+
emitNotice(notice);
|
|
25437
|
+
break;
|
|
25438
|
+
}
|
|
24865
25439
|
default: {
|
|
24866
|
-
|
|
24867
|
-
const header = systemHeader("system", subtype);
|
|
24868
|
-
const body = text3 ? `${header}
|
|
24869
|
-
|
|
24870
|
-
${text3}` : header;
|
|
24871
|
-
emit({ type: "agent:tool_use", sessionId: sid, toolName: "_notice_", summary: body });
|
|
24872
|
-
log("best-effort surfaced unknown system subtype:", subtype ?? "?");
|
|
25440
|
+
emitEvent(describeSystemSubtype(subtype ?? "unknown", m), parentToolUseId);
|
|
24873
25441
|
break;
|
|
24874
25442
|
}
|
|
24875
25443
|
}
|
|
@@ -24924,24 +25492,40 @@ ${text3}` : header;
|
|
|
24924
25492
|
}
|
|
24925
25493
|
break;
|
|
24926
25494
|
}
|
|
24927
|
-
|
|
24928
|
-
|
|
25495
|
+
// A still-running tool's elapsed time → update its card's badge in place. Only
|
|
25496
|
+
// once it's been a few seconds (fast tools finish before the badge matters).
|
|
25497
|
+
case "tool_progress": {
|
|
25498
|
+
const tp = message;
|
|
25499
|
+
const elapsed = Math.round(Number(tp.elapsed_time_seconds ?? 0));
|
|
25500
|
+
if (tp.tool_use_id && elapsed >= 3)
|
|
25501
|
+
emit({ type: "agent:tool_use", sessionId: sid, toolName: "_progress_", summary: "", toolUseId: tp.tool_use_id, elapsedSeconds: elapsed, ...parentToolUseId ? { parentToolUseId } : {} });
|
|
25502
|
+
break;
|
|
25503
|
+
}
|
|
25504
|
+
// A predicted follow-up prompt → a clickable chip under the reply.
|
|
25505
|
+
case "prompt_suggestion": {
|
|
25506
|
+
const s = sstr(message.suggestion).trim();
|
|
25507
|
+
if (s)
|
|
25508
|
+
emit({ type: "agent:suggestion", sessionId: sid, suggestion: s });
|
|
24929
25509
|
break;
|
|
25510
|
+
}
|
|
24930
25511
|
default: {
|
|
24931
25512
|
const m = message;
|
|
24932
25513
|
const text3 = extractHumanReadable(m);
|
|
24933
|
-
|
|
24934
|
-
|
|
24935
|
-
|
|
24936
|
-
|
|
24937
|
-
|
|
24938
|
-
|
|
25514
|
+
emitEvent({
|
|
25515
|
+
category: "unknown",
|
|
25516
|
+
name: m.type || "unknown",
|
|
25517
|
+
title: m.type || "unknown frame",
|
|
25518
|
+
...text3 ? { detail: text3 } : {},
|
|
25519
|
+
raw: rawFramePreview(m)
|
|
25520
|
+
}, parentToolUseId);
|
|
25521
|
+
log("unknown SDK message type surfaced with raw data:", m.type || "?");
|
|
24939
25522
|
break;
|
|
24940
25523
|
}
|
|
24941
25524
|
}
|
|
24942
25525
|
}
|
|
24943
25526
|
} catch (err) {
|
|
24944
25527
|
const sid = live.sessionId;
|
|
25528
|
+
live.pendingThinking = void 0;
|
|
24945
25529
|
if (live.interrupted || live.abort.signal.aborted) {
|
|
24946
25530
|
live.interrupted = false;
|
|
24947
25531
|
this.clearStopWatchdog(live);
|
|
@@ -27132,26 +27716,26 @@ var require_build = __commonJS({
|
|
|
27132
27716
|
throw new Error(`Unknown type of encoding '${enc}'`);
|
|
27133
27717
|
}
|
|
27134
27718
|
}
|
|
27135
|
-
static FromString(
|
|
27136
|
-
if (!
|
|
27719
|
+
static FromString(str2, enc = "utf8") {
|
|
27720
|
+
if (!str2) {
|
|
27137
27721
|
return new ArrayBuffer(0);
|
|
27138
27722
|
}
|
|
27139
27723
|
switch (enc.toLowerCase()) {
|
|
27140
27724
|
case "utf8":
|
|
27141
|
-
return this.FromUtf8String(
|
|
27725
|
+
return this.FromUtf8String(str2);
|
|
27142
27726
|
case "binary":
|
|
27143
|
-
return this.FromBinary(
|
|
27727
|
+
return this.FromBinary(str2);
|
|
27144
27728
|
case "hex":
|
|
27145
|
-
return this.FromHex(
|
|
27729
|
+
return this.FromHex(str2);
|
|
27146
27730
|
case "base64":
|
|
27147
|
-
return this.FromBase64(
|
|
27731
|
+
return this.FromBase64(str2);
|
|
27148
27732
|
case "base64url":
|
|
27149
|
-
return this.FromBase64Url(
|
|
27733
|
+
return this.FromBase64Url(str2);
|
|
27150
27734
|
case "utf16le":
|
|
27151
|
-
return Utf16Converter.fromString(
|
|
27735
|
+
return Utf16Converter.fromString(str2, true);
|
|
27152
27736
|
case "utf16":
|
|
27153
27737
|
case "utf16be":
|
|
27154
|
-
return Utf16Converter.fromString(
|
|
27738
|
+
return Utf16Converter.fromString(str2);
|
|
27155
27739
|
default:
|
|
27156
27740
|
throw new Error(`Unknown type of encoding '${enc}'`);
|
|
27157
27741
|
}
|
|
@@ -27354,11 +27938,11 @@ var require_utils = __commonJS({
|
|
|
27354
27938
|
function bufferToHexCodes(inputBuffer, inputOffset = 0, inputLength = inputBuffer.byteLength - inputOffset, insertSpace = false) {
|
|
27355
27939
|
let result = "";
|
|
27356
27940
|
for (const item of new Uint8Array(inputBuffer, inputOffset, inputLength)) {
|
|
27357
|
-
const
|
|
27358
|
-
if (
|
|
27941
|
+
const str2 = item.toString(16).toUpperCase();
|
|
27942
|
+
if (str2.length === 1) {
|
|
27359
27943
|
result += "0";
|
|
27360
27944
|
}
|
|
27361
|
-
result +=
|
|
27945
|
+
result += str2;
|
|
27362
27946
|
if (insertSpace) {
|
|
27363
27947
|
result += " ";
|
|
27364
27948
|
}
|
|
@@ -27525,17 +28109,17 @@ var require_utils = __commonJS({
|
|
|
27525
28109
|
return true;
|
|
27526
28110
|
}
|
|
27527
28111
|
function padNumber(inputNumber, fullLength) {
|
|
27528
|
-
const
|
|
27529
|
-
if (fullLength <
|
|
28112
|
+
const str2 = inputNumber.toString(10);
|
|
28113
|
+
if (fullLength < str2.length) {
|
|
27530
28114
|
return "";
|
|
27531
28115
|
}
|
|
27532
|
-
const dif = fullLength -
|
|
28116
|
+
const dif = fullLength - str2.length;
|
|
27533
28117
|
const padding = new Array(dif);
|
|
27534
28118
|
for (let i = 0; i < dif; i++) {
|
|
27535
28119
|
padding[i] = "0";
|
|
27536
28120
|
}
|
|
27537
28121
|
const paddingString = padding.join("");
|
|
27538
|
-
return paddingString.concat(
|
|
28122
|
+
return paddingString.concat(str2);
|
|
27539
28123
|
}
|
|
27540
28124
|
var base64Template = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
|
27541
28125
|
var base64UrlTemplate = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=";
|
|
@@ -27647,12 +28231,12 @@ var require_utils = __commonJS({
|
|
|
27647
28231
|
}
|
|
27648
28232
|
return resultString;
|
|
27649
28233
|
}
|
|
27650
|
-
function stringToArrayBuffer(
|
|
27651
|
-
const stringLength =
|
|
28234
|
+
function stringToArrayBuffer(str2) {
|
|
28235
|
+
const stringLength = str2.length;
|
|
27652
28236
|
const resultBuffer = new ArrayBuffer(stringLength);
|
|
27653
28237
|
const resultView = new Uint8Array(resultBuffer);
|
|
27654
28238
|
for (let i = 0; i < stringLength; i++) {
|
|
27655
|
-
resultView[i] =
|
|
28239
|
+
resultView[i] = str2.charCodeAt(i);
|
|
27656
28240
|
}
|
|
27657
28241
|
return resultBuffer;
|
|
27658
28242
|
}
|
|
@@ -30090,11 +30674,11 @@ ${values.join("\n")}` : `${blockName} :`;
|
|
|
30090
30674
|
this.fromString(String.fromCharCode.apply(null, pvtsutils__namespace.BufferSourceConverter.toUint8Array(inputBuffer)));
|
|
30091
30675
|
}
|
|
30092
30676
|
toBuffer() {
|
|
30093
|
-
const
|
|
30094
|
-
const buffer = new ArrayBuffer(
|
|
30677
|
+
const str2 = this.toString();
|
|
30678
|
+
const buffer = new ArrayBuffer(str2.length);
|
|
30095
30679
|
const view = new Uint8Array(buffer);
|
|
30096
|
-
for (let i = 0; i <
|
|
30097
|
-
view[i] =
|
|
30680
|
+
for (let i = 0; i < str2.length; i++)
|
|
30681
|
+
view[i] = str2.charCodeAt(i);
|
|
30098
30682
|
return buffer;
|
|
30099
30683
|
}
|
|
30100
30684
|
fromDate(inputDate) {
|
|
@@ -45761,6 +46345,7 @@ async function createClaudeCodeBackend(send2, onBrowserRequest, loadPluginRuntim
|
|
|
45761
46345
|
stopTask: (sessionId, taskId, callerOrigin) => manager2.stopTask(sessionId, taskId, callerOrigin),
|
|
45762
46346
|
respondToElicitation: (requestId, decision, callerOrigin) => manager2.respondToElicitation(requestId, decision, callerOrigin),
|
|
45763
46347
|
respondToUiRequest: (requestId, response, callerOrigin) => manager2.respondToUiRequest(requestId, response, callerOrigin),
|
|
46348
|
+
respondToUserDialog: (requestId, decision, callerOrigin) => manager2.respondToUserDialog(requestId, decision, callerOrigin),
|
|
45764
46349
|
mcpServerStatus: (sessionId) => manager2.mcpServerStatus(sessionId),
|
|
45765
46350
|
reconnectMcpServer: (sessionId, name) => manager2.reconnectMcpServer(sessionId, name),
|
|
45766
46351
|
toggleMcpServer: (sessionId, name, enabled) => manager2.toggleMcpServer(sessionId, name, enabled),
|
|
@@ -53507,18 +54092,18 @@ var unifyPaths = (paths_) => {
|
|
|
53507
54092
|
return paths.map(normalizePathToUnix);
|
|
53508
54093
|
};
|
|
53509
54094
|
var toUnix = (string4) => {
|
|
53510
|
-
let
|
|
54095
|
+
let str2 = string4.replace(BACK_SLASH_RE, SLASH);
|
|
53511
54096
|
let prepend = false;
|
|
53512
|
-
if (
|
|
54097
|
+
if (str2.startsWith(SLASH_SLASH)) {
|
|
53513
54098
|
prepend = true;
|
|
53514
54099
|
}
|
|
53515
|
-
while (
|
|
53516
|
-
|
|
54100
|
+
while (str2.match(DOUBLE_SLASH_RE)) {
|
|
54101
|
+
str2 = str2.replace(DOUBLE_SLASH_RE, SLASH);
|
|
53517
54102
|
}
|
|
53518
54103
|
if (prepend) {
|
|
53519
|
-
|
|
54104
|
+
str2 = SLASH + str2;
|
|
53520
54105
|
}
|
|
53521
|
-
return
|
|
54106
|
+
return str2;
|
|
53522
54107
|
};
|
|
53523
54108
|
var normalizePathToUnix = (path5) => toUnix(sysPath2.normalize(toUnix(path5)));
|
|
53524
54109
|
var normalizeIgnored = (cwd = "") => (path5) => {
|
|
@@ -56069,6 +56654,9 @@ async function handleInboundAgent(msg, driverClientId) {
|
|
|
56069
56654
|
case "chat:ui_response":
|
|
56070
56655
|
b.respondToUiRequest?.(msg.requestId, msg.response, origin);
|
|
56071
56656
|
break;
|
|
56657
|
+
case "chat:user_dialog":
|
|
56658
|
+
b.respondToUserDialog?.(msg.requestId, { behavior: msg.behavior, result: msg.result }, origin);
|
|
56659
|
+
break;
|
|
56072
56660
|
case "agent:mcp_reconnect":
|
|
56073
56661
|
void b.reconnectMcpServer?.(msg.sessionId, msg.name);
|
|
56074
56662
|
break;
|
|
@@ -56836,6 +57424,9 @@ async function startLocalServer(opts) {
|
|
|
56836
57424
|
case "chat:ui_response":
|
|
56837
57425
|
b.respondToUiRequest?.(msg.requestId, msg.response, loopbackOrigin);
|
|
56838
57426
|
break;
|
|
57427
|
+
case "chat:user_dialog":
|
|
57428
|
+
b.respondToUserDialog?.(msg.requestId, { behavior: msg.behavior, result: msg.result }, loopbackOrigin);
|
|
57429
|
+
break;
|
|
56839
57430
|
case "agent:mcp_reconnect":
|
|
56840
57431
|
void b.reconnectMcpServer?.(msg.sessionId, msg.name);
|
|
56841
57432
|
break;
|