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