tesseract-agent 0.4.20 → 0.4.21
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 +503 -231
- package/package.json +1 -1
package/dist-bundle/cli.js
CHANGED
|
@@ -19450,6 +19450,10 @@ var init_ws_protocol = __esm({
|
|
|
19450
19450
|
// co-viewers see images the moment the agent generates them
|
|
19451
19451
|
"agent:canvas_ready",
|
|
19452
19452
|
// co-viewers dock the HTML canvas the moment the agent renders it
|
|
19453
|
+
"agent:report_ready",
|
|
19454
|
+
// co-viewers see the mission report (and its in-place updates)
|
|
19455
|
+
"agent:mission_progress",
|
|
19456
|
+
// co-viewers see the Mission card's live stage progress
|
|
19453
19457
|
"agent:elicitation_complete",
|
|
19454
19458
|
// M5 — co-viewers clear a resolved url-mode card
|
|
19455
19459
|
"chat:echoed",
|
|
@@ -19487,6 +19491,7 @@ var init_plans = __esm({
|
|
|
19487
19491
|
advancedBrowser: false,
|
|
19488
19492
|
hosting: false,
|
|
19489
19493
|
plugins: false,
|
|
19494
|
+
missions: false,
|
|
19490
19495
|
scheduling: false,
|
|
19491
19496
|
api: false,
|
|
19492
19497
|
sso: false,
|
|
@@ -19523,6 +19528,7 @@ var init_plans = __esm({
|
|
|
19523
19528
|
"Tesseract cloud server \u2014 a managed always-on agent in the cloud",
|
|
19524
19529
|
"Deploy to the cloud \u2014 your app on a *.tesseract.dev URL",
|
|
19525
19530
|
"Includes ~1 always-on service + 20 GB storage + 100 GB bandwidth (overage metered)",
|
|
19531
|
+
"Premium missions \u2014 GDPR, security, UX, performance audits that fix & verify",
|
|
19526
19532
|
"Sync settings across your devices",
|
|
19527
19533
|
"Email support"
|
|
19528
19534
|
],
|
|
@@ -19536,6 +19542,7 @@ var init_plans = __esm({
|
|
|
19536
19542
|
voice: true,
|
|
19537
19543
|
hosting: true,
|
|
19538
19544
|
plugins: true,
|
|
19545
|
+
missions: true,
|
|
19539
19546
|
scheduling: true,
|
|
19540
19547
|
maxMachines: 0
|
|
19541
19548
|
},
|
|
@@ -19570,6 +19577,7 @@ var init_plans = __esm({
|
|
|
19570
19577
|
advancedBrowser: true,
|
|
19571
19578
|
hosting: true,
|
|
19572
19579
|
plugins: true,
|
|
19580
|
+
missions: true,
|
|
19573
19581
|
scheduling: true,
|
|
19574
19582
|
api: true,
|
|
19575
19583
|
sso: true,
|
|
@@ -20407,19 +20415,19 @@ function tokensToTailwindTheme(input) {
|
|
|
20407
20415
|
const tokens = normalizeTokens(input);
|
|
20408
20416
|
const extend2 = {};
|
|
20409
20417
|
for (const { path: path5, token } of walkTokens(tokens)) {
|
|
20410
|
-
const
|
|
20411
|
-
if (!
|
|
20418
|
+
const bucket2 = TW_BUCKETS.find((b) => b.match.length <= path5.length && b.match.every((seg, i) => path5[i] === seg));
|
|
20419
|
+
if (!bucket2)
|
|
20412
20420
|
continue;
|
|
20413
|
-
const rest = path5.slice(
|
|
20421
|
+
const rest = path5.slice(bucket2.match.length).map(kebab).filter(Boolean);
|
|
20414
20422
|
if (rest.length === 0)
|
|
20415
20423
|
continue;
|
|
20416
20424
|
const ref = `var(${tokenCssVarName(path5, token)})`;
|
|
20417
|
-
if (!extend2[
|
|
20418
|
-
extend2[
|
|
20419
|
-
if (
|
|
20420
|
-
extend2[
|
|
20425
|
+
if (!extend2[bucket2.key])
|
|
20426
|
+
extend2[bucket2.key] = {};
|
|
20427
|
+
if (bucket2.flat)
|
|
20428
|
+
extend2[bucket2.key][rest.join("-")] = ref;
|
|
20421
20429
|
else
|
|
20422
|
-
setNested(extend2[
|
|
20430
|
+
setNested(extend2[bucket2.key], rest, ref);
|
|
20423
20431
|
}
|
|
20424
20432
|
return { extend: extend2 };
|
|
20425
20433
|
}
|
|
@@ -21440,7 +21448,7 @@ function slugify3(s) {
|
|
|
21440
21448
|
}
|
|
21441
21449
|
function buildCanvasTools(deps) {
|
|
21442
21450
|
return [
|
|
21443
|
-
tool4("render_canvas", "Render an HTML document as a live, sandboxed Canvas docked beside the chat (like OpenAI Canvas), for INTERACTIVE or multi-part PROJECTS (apps, tools, dashboards, anything the user will interact with at size). For a SMALL self-contained snippet or animation, do NOT use this \u2014 instead just include it as a fenced ```html (or ```svg) code block in your reply; it renders live inline in the message. To TRANSFORM an existing canvas, call again with the SAME id and the full updated HTML \u2014 it re-renders in place. Returns the canvas file path.", {
|
|
21451
|
+
tool4("render_canvas", "Render an HTML document as a live, sandboxed Canvas docked beside the chat (like OpenAI Canvas), for INTERACTIVE or multi-part PROJECTS (apps, tools, dashboards, anything the user will interact with at size). For a SMALL self-contained snippet or animation, do NOT use this \u2014 instead just include it as a fenced ```html (or ```svg) code block in your reply; it renders live inline in the message. To TRANSFORM an existing canvas, call again with the SAME id and the full updated HTML \u2014 it re-renders in place. Before composing the HTML, consult the canvas-design skill for design fundamentals (and chart-design / diagram-design when the canvas is a chart or a diagram) so the result looks deliberately designed. Returns the canvas file path.", {
|
|
21444
21452
|
html: external_exports.string().describe("The full HTML document to render (include <html>/<body>; put CSS/JS inline)."),
|
|
21445
21453
|
title: external_exports.string().optional().describe("Short title for the canvas tab."),
|
|
21446
21454
|
id: external_exports.string().optional().describe("Stable id to UPDATE an existing canvas; omit to create a new one.")
|
|
@@ -21462,6 +21470,132 @@ var init_canvas_tools = __esm({
|
|
|
21462
21470
|
}
|
|
21463
21471
|
});
|
|
21464
21472
|
|
|
21473
|
+
// ../../packages/agent-core/dist/report-tools.js
|
|
21474
|
+
import { tool as tool5 } from "@anthropic-ai/claude-agent-sdk";
|
|
21475
|
+
function slug(s) {
|
|
21476
|
+
return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "mission";
|
|
21477
|
+
}
|
|
21478
|
+
function buildReportTools(deps) {
|
|
21479
|
+
return [
|
|
21480
|
+
tool5("emit_report", `Deliver the structured findings of a mission playbook run as a Report card in the chat (this is the deliverable \u2014 do NOT just write findings as prose). Call it ONCE near the end with all findings grouped into the playbook's sections. To UPDATE after fixing/verifying a finding (Fix / Re-run), call again with the SAME id and finding ids, changing each finding's status \u2014 it re-renders in place. For "legal" bucket reports, never claim the site is compliant/certified; describe risks and route the verdict to a human.`, {
|
|
21481
|
+
id: external_exports.string().optional().describe("Stable report id to UPDATE an existing report; omit on the first call to create one."),
|
|
21482
|
+
playbookId: external_exports.string().describe("The playbook id this report belongs to."),
|
|
21483
|
+
title: external_exports.string().describe("Report title (usually the playbook name)."),
|
|
21484
|
+
summary: external_exports.string().describe("Plain-language overview of what was found."),
|
|
21485
|
+
bucket: bucket.describe("Overall verifiability bucket for the report."),
|
|
21486
|
+
sections: external_exports.array(external_exports.object({ id: external_exports.string(), title: external_exports.string(), description: external_exports.string().optional() })).describe("Ordered layout sections; findings reference these by id."),
|
|
21487
|
+
findings: external_exports.array(finding)
|
|
21488
|
+
}, async (args) => {
|
|
21489
|
+
const id = args.id ? slug(args.id) : `report-${slug(args.playbookId)}-${Date.now()}`;
|
|
21490
|
+
const report = {
|
|
21491
|
+
id,
|
|
21492
|
+
playbookId: args.playbookId,
|
|
21493
|
+
title: args.title,
|
|
21494
|
+
summary: args.summary,
|
|
21495
|
+
bucket: args.bucket,
|
|
21496
|
+
sections: args.sections,
|
|
21497
|
+
findings: args.findings,
|
|
21498
|
+
ts: Date.now()
|
|
21499
|
+
};
|
|
21500
|
+
deps.emitReport(report);
|
|
21501
|
+
return {
|
|
21502
|
+
content: [
|
|
21503
|
+
{
|
|
21504
|
+
type: "text",
|
|
21505
|
+
text: `Report "${args.title}" delivered (${args.findings.length} finding(s)). To update a finding's status after fixing it, call emit_report again with id "${id}" and the same finding ids.`
|
|
21506
|
+
}
|
|
21507
|
+
]
|
|
21508
|
+
};
|
|
21509
|
+
})
|
|
21510
|
+
];
|
|
21511
|
+
}
|
|
21512
|
+
var bucket, finding;
|
|
21513
|
+
var init_report_tools = __esm({
|
|
21514
|
+
"../../packages/agent-core/dist/report-tools.js"() {
|
|
21515
|
+
"use strict";
|
|
21516
|
+
init_zod();
|
|
21517
|
+
bucket = external_exports.enum(["deterministic", "heuristic", "legal"]);
|
|
21518
|
+
finding = external_exports.object({
|
|
21519
|
+
id: external_exports.string().describe("Stable id for this finding \u2014 REUSE it across updates so status changes land in place."),
|
|
21520
|
+
severity: external_exports.enum(["high", "medium", "low", "info"]),
|
|
21521
|
+
title: external_exports.string().describe("Short headline."),
|
|
21522
|
+
description: external_exports.string().describe("Plain-language explanation a non-technical user understands."),
|
|
21523
|
+
section: external_exports.string().optional().describe("Id of the report section this belongs under (matches a sections[].id)."),
|
|
21524
|
+
location: external_exports.object({ selector: external_exports.string().optional(), xpath: external_exports.string().optional(), file: external_exports.string().optional(), line: external_exports.number().optional() }).optional().describe("Where it is \u2014 a CSS selector/xpath on the page, or a source file+line."),
|
|
21525
|
+
suggestedFix: external_exports.string().optional().describe("Plain-language suggested fix."),
|
|
21526
|
+
bucket: bucket.describe("Verifiability: deterministic (auto-fixable+verifiable), heuristic (advisory), or legal (readiness only \u2014 never certify)."),
|
|
21527
|
+
status: external_exports.enum(["open", "fixed", "wontfix"]).describe('Current status; start as "open".')
|
|
21528
|
+
});
|
|
21529
|
+
}
|
|
21530
|
+
});
|
|
21531
|
+
|
|
21532
|
+
// ../../packages/agent-core/dist/interactive-ui-tools.js
|
|
21533
|
+
import { tool as tool6 } from "@anthropic-ai/claude-agent-sdk";
|
|
21534
|
+
function buildInteractiveUiTools(deps) {
|
|
21535
|
+
return [
|
|
21536
|
+
tool6("ask_form", "Show the user a FORM in the chat and wait for them to fill it in. Use this to collect structured input mid-task (settings, scope, details) instead of asking in prose. Returns the submitted values (or that the user cancelled). Prefer this over free-text questions when the answer is structured.", {
|
|
21537
|
+
title: external_exports.string().describe("Form heading (plain language)."),
|
|
21538
|
+
description: external_exports.string().optional().describe("Short explanation shown above the fields."),
|
|
21539
|
+
submitLabel: external_exports.string().optional().describe('Submit button label (default "Submit").'),
|
|
21540
|
+
fields: external_exports.array(formField).min(1),
|
|
21541
|
+
frozen: external_exports.boolean().optional().describe("Pin this card to the top of the chat so it does not scroll away while you keep working; it unpins once answered. Use true for input you NEED answered.")
|
|
21542
|
+
}, async (args) => {
|
|
21543
|
+
const spec = { component: "form", title: args.title, description: args.description, submitLabel: args.submitLabel, fields: args.fields, frozen: args.frozen };
|
|
21544
|
+
const res = await deps.raiseUiRequest(spec);
|
|
21545
|
+
if (res.cancelled)
|
|
21546
|
+
return { content: [{ type: "text", text: "The user cancelled the form (no values submitted)." }] };
|
|
21547
|
+
return { content: [{ type: "text", text: `The user submitted the form:
|
|
21548
|
+
${JSON.stringify(res.values ?? {}, null, 2)}` }] };
|
|
21549
|
+
}),
|
|
21550
|
+
tool6("present_choices", "Show the user a CHOICE picker in the chat and wait for them to choose. Use this to let the user make a decision or pick from options (single or multi-select). Returns the selected value(s) (or cancellation).", {
|
|
21551
|
+
title: external_exports.string().describe("The question / heading."),
|
|
21552
|
+
description: external_exports.string().optional(),
|
|
21553
|
+
options: external_exports.array(choiceOption).min(1),
|
|
21554
|
+
multiSelect: external_exports.boolean().optional().describe("Allow selecting more than one."),
|
|
21555
|
+
frozen: external_exports.boolean().optional().describe("Pin this card to the top of the chat so it does not scroll away while you keep working; it unpins once answered.")
|
|
21556
|
+
}, async (args) => {
|
|
21557
|
+
const spec = { component: "choices", title: args.title, description: args.description, options: args.options, multiSelect: args.multiSelect, frozen: args.frozen };
|
|
21558
|
+
const res = await deps.raiseUiRequest(spec);
|
|
21559
|
+
if (res.cancelled)
|
|
21560
|
+
return { content: [{ type: "text", text: "The user cancelled the choice (nothing selected)." }] };
|
|
21561
|
+
return { content: [{ type: "text", text: `The user selected: ${JSON.stringify(res.selected ?? [])}` }] };
|
|
21562
|
+
}),
|
|
21563
|
+
tool6("update_progress", `Update the live Mission card so the user sees ongoing status. Call this whenever you move between stages (and once at the start). Pass the FULL ordered stage list each time with each stage's current status, plus a short "what I'm doing now" note. This is a one-way feedback channel \u2014 it returns immediately, does not wait for the user.`, {
|
|
21564
|
+
stages: external_exports.array(external_exports.object({
|
|
21565
|
+
label: external_exports.string(),
|
|
21566
|
+
status: external_exports.enum(["pending", "active", "done"])
|
|
21567
|
+
})).min(1).describe("The full ordered list of stages with each one's current status."),
|
|
21568
|
+
note: external_exports.string().optional().describe("A short present-tense line about what is happening right now.")
|
|
21569
|
+
}, async (args) => {
|
|
21570
|
+
deps.emitProgress(args.stages, args.note);
|
|
21571
|
+
return { content: [{ type: "text", text: "Progress updated." }] };
|
|
21572
|
+
})
|
|
21573
|
+
];
|
|
21574
|
+
}
|
|
21575
|
+
var formField, choiceOption;
|
|
21576
|
+
var init_interactive_ui_tools = __esm({
|
|
21577
|
+
"../../packages/agent-core/dist/interactive-ui-tools.js"() {
|
|
21578
|
+
"use strict";
|
|
21579
|
+
init_zod();
|
|
21580
|
+
formField = external_exports.object({
|
|
21581
|
+
key: external_exports.string().describe("Stable key the answer is returned under."),
|
|
21582
|
+
label: external_exports.string(),
|
|
21583
|
+
help: external_exports.string().optional().describe("Help text shown under the field."),
|
|
21584
|
+
kind: external_exports.enum(["text", "textarea", "select", "multiselect", "toggle", "number"]),
|
|
21585
|
+
options: external_exports.array(external_exports.object({ value: external_exports.string(), label: external_exports.string() })).optional().describe("For select / multiselect."),
|
|
21586
|
+
default: external_exports.union([external_exports.string(), external_exports.array(external_exports.string()), external_exports.boolean(), external_exports.number()]).optional(),
|
|
21587
|
+
required: external_exports.boolean().optional(),
|
|
21588
|
+
placeholder: external_exports.string().optional()
|
|
21589
|
+
});
|
|
21590
|
+
choiceOption = external_exports.object({
|
|
21591
|
+
value: external_exports.string(),
|
|
21592
|
+
label: external_exports.string(),
|
|
21593
|
+
description: external_exports.string().optional(),
|
|
21594
|
+
preview: external_exports.string().optional().describe("Optional markdown preview shown when this option is focused.")
|
|
21595
|
+
});
|
|
21596
|
+
}
|
|
21597
|
+
});
|
|
21598
|
+
|
|
21465
21599
|
// ../../packages/agent-core/dist/session-transfer.js
|
|
21466
21600
|
import { homedir as homedir2 } from "node:os";
|
|
21467
21601
|
import { join as join6, dirname as dirname3, sep, resolve as resolve3 } from "node:path";
|
|
@@ -21634,6 +21768,21 @@ async function appendCanvasArtifact(sessionId, projectDir, a) {
|
|
|
21634
21768
|
await writeManifest(dir, list);
|
|
21635
21769
|
});
|
|
21636
21770
|
}
|
|
21771
|
+
async function appendReportArtifact(sessionId, projectDir, report) {
|
|
21772
|
+
const dir = await sessionSidecarDir(sessionId, projectDir);
|
|
21773
|
+
if (!dir)
|
|
21774
|
+
return;
|
|
21775
|
+
await withManifestLock(dir, async () => {
|
|
21776
|
+
const list = await readManifest(dir);
|
|
21777
|
+
const entry = { id: `report-${report.id}`, kind: "report", ts: report.ts, title: report.title, report };
|
|
21778
|
+
const at = list.findIndex((x) => x.kind === "report" && x.report?.id === report.id);
|
|
21779
|
+
if (at >= 0)
|
|
21780
|
+
list[at] = entry;
|
|
21781
|
+
else
|
|
21782
|
+
list.push(entry);
|
|
21783
|
+
await writeManifest(dir, list);
|
|
21784
|
+
});
|
|
21785
|
+
}
|
|
21637
21786
|
async function readSessionArtifacts(sessionId, projectDir) {
|
|
21638
21787
|
const dir = await sessionSidecarDir(sessionId, projectDir);
|
|
21639
21788
|
if (!dir)
|
|
@@ -21649,7 +21798,8 @@ async function readSessionArtifacts(sessionId, projectDir) {
|
|
|
21649
21798
|
images: a.images?.filter((im) => inDir(im.rel)).map((im) => ({ path: toAbs(dir, im.rel), mime: im.mime })),
|
|
21650
21799
|
canvasId: a.canvasId,
|
|
21651
21800
|
title: a.title,
|
|
21652
|
-
path: a.file && inDir(a.file) ? toAbs(dir, a.file) : void 0
|
|
21801
|
+
path: a.file && inDir(a.file) ? toAbs(dir, a.file) : void 0,
|
|
21802
|
+
report: a.report
|
|
21653
21803
|
})).sort((x, y) => x.ts - y.ts);
|
|
21654
21804
|
}
|
|
21655
21805
|
var MANIFEST, toRel, toAbs, manifestLocks;
|
|
@@ -22566,6 +22716,8 @@ var init_agent_manager = __esm({
|
|
|
22566
22716
|
init_design_tools();
|
|
22567
22717
|
init_imagegen_tools();
|
|
22568
22718
|
init_canvas_tools();
|
|
22719
|
+
init_report_tools();
|
|
22720
|
+
init_interactive_ui_tools();
|
|
22569
22721
|
init_session_artifacts();
|
|
22570
22722
|
init_design_tokens2();
|
|
22571
22723
|
init_narrator();
|
|
@@ -22620,7 +22772,7 @@ var init_agent_manager = __esm({
|
|
|
22620
22772
|
};
|
|
22621
22773
|
}
|
|
22622
22774
|
};
|
|
22623
|
-
IDLE_REAP_MS =
|
|
22775
|
+
IDLE_REAP_MS = 15 * 60 * 1e3;
|
|
22624
22776
|
MODERATE_IDLE_MS = 60 * 1e3;
|
|
22625
22777
|
STEER_COALESCE_MS = 500;
|
|
22626
22778
|
STOP_HARD_KILL_AFTER_MS = 1200;
|
|
@@ -22635,6 +22787,17 @@ var init_agent_manager = __esm({
|
|
|
22635
22787
|
liveSessions = /* @__PURE__ */ new Map();
|
|
22636
22788
|
/** Sessions whose canonical sessionId is not yet known (first frame pending) */
|
|
22637
22789
|
pendingLiveSessions = /* @__PURE__ */ new Set();
|
|
22790
|
+
/** PRE-WARM (tab-driven): a single standby NEW-session instance — its cli.js process
|
|
22791
|
+
* is already spawned + booted and idling on an empty pump, so the next NEW conversation
|
|
22792
|
+
* whose construction-config matches `warmEmptyKey` is adopted instantly (skips the
|
|
22793
|
+
* ~1.6–3.4s spawn/boot). system/init only fires on the first push, so an un-adopted
|
|
22794
|
+
* warm instance stays silent and writes no transcript. */
|
|
22795
|
+
warmEmpty = null;
|
|
22796
|
+
warmEmptyKey = null;
|
|
22797
|
+
/** The session backing the currently-FOCUSED tab — pinned against idle reap via the
|
|
22798
|
+
* 'tab' keep-alive holder. Switching tabs releases the previous one (arming its 15-min
|
|
22799
|
+
* idle clock) and pins the new one (see setActiveSession). */
|
|
22800
|
+
activeTabSession = null;
|
|
22638
22801
|
/** requestId → the originating browser-cli socket, for unicast responses.
|
|
22639
22802
|
* browser-cli is one-shot, so the entry is deleted on the first write. */
|
|
22640
22803
|
pendingResponses = /* @__PURE__ */ new Map();
|
|
@@ -22650,6 +22813,11 @@ var init_agent_manager = __esm({
|
|
|
22650
22813
|
/** M5 H6 — parked MCP elicitation prompts, resolved when the client replies via
|
|
22651
22814
|
* chat:elicitation (→ respondToElicitation) or denied on abort. */
|
|
22652
22815
|
pendingElicitations = /* @__PURE__ */ new Map();
|
|
22816
|
+
/** Skill-driven interactive UI requests (ask_form / present_choices), resolved when
|
|
22817
|
+
* the client replies via chat:ui_response (→ respondToUiRequest) or the owning
|
|
22818
|
+
* session is torn down (→ cancelled). Keyed by requestId; `sessionId` lets a
|
|
22819
|
+
* teardown sweep cancel a session's parked cards. */
|
|
22820
|
+
pendingUiRequests = /* @__PURE__ */ new Map();
|
|
22653
22821
|
browserCliPath;
|
|
22654
22822
|
/** Plugin-contributed agent tools (Epic G). The host loads enabled plugins and sets
|
|
22655
22823
|
* these; at session start they're aggregated behind ONE in-process MCP server and
|
|
@@ -22665,6 +22833,9 @@ var init_agent_manager = __esm({
|
|
|
22665
22833
|
/** Local plugin bundling the enabled ClawHub skills (Epic H), loaded via query()'s
|
|
22666
22834
|
* `plugins` option so the SDK discovers their SKILL.md files. */
|
|
22667
22835
|
skillsPlugin = null;
|
|
22836
|
+
/** Bundled first-party mission-skills plugin (Part B). Attached per-session only
|
|
22837
|
+
* when a turn carries missionSkillId. Set once by the host from app resources. */
|
|
22838
|
+
missionSkillsPlugin = null;
|
|
22668
22839
|
/** Host-injected image-generation backend (keys + provider call + disk save live
|
|
22669
22840
|
* in the host; secrets never reach agent-core). Non-null ⇒ NEW conversations get
|
|
22670
22841
|
* the in-process 'tesseract-imagegen' tools. */
|
|
@@ -22688,6 +22859,12 @@ var init_agent_manager = __esm({
|
|
|
22688
22859
|
setSkillsPlugin(config2) {
|
|
22689
22860
|
this.skillsPlugin = config2;
|
|
22690
22861
|
}
|
|
22862
|
+
/** Set (or clear) the bundled MISSION-skills local-plugin (first-party mission
|
|
22863
|
+
* procedures). Attached only to sessions launched with a missionSkillId, so it
|
|
22864
|
+
* doesn't bloat normal chats and stays out of the user's Skills list. */
|
|
22865
|
+
setMissionSkillsPlugin(config2) {
|
|
22866
|
+
this.missionSkillsPlugin = config2;
|
|
22867
|
+
}
|
|
22691
22868
|
/** Inject (or clear) the host's image-generation backend. When set, NEW
|
|
22692
22869
|
* conversations get the in-process 'tesseract-imagegen' tools. The runner is
|
|
22693
22870
|
* read fresh per call, so a key the user adds later applies without a restart. */
|
|
@@ -22937,6 +23114,41 @@ ${full.slice(-8e3)}`;
|
|
|
22937
23114
|
entry.cleanup();
|
|
22938
23115
|
entry.resolve({ action: decision.action, ...decision.action === "accept" && decision.content ? { content: decision.content } : {} });
|
|
22939
23116
|
}
|
|
23117
|
+
/** Skill UI: park a promise, raise an interactive card (agent:ui_request), and
|
|
23118
|
+
* resolve it from chat:ui_response (respondToUiRequest). A session teardown
|
|
23119
|
+
* cancels any still-parked cards (cancelSessionUiRequests). Mirrors onElicitation. */
|
|
23120
|
+
raiseUiRequest(live, spec) {
|
|
23121
|
+
return new Promise((resolve10) => {
|
|
23122
|
+
const requestId = randomUUID2();
|
|
23123
|
+
this.pendingUiRequests.set(requestId, { resolve: resolve10, sessionId: live.sessionId, origin: live.origin });
|
|
23124
|
+
const evt = { type: "agent:ui_request", sessionId: live.sessionId, requestId, spec, ts: Date.now() };
|
|
23125
|
+
if (!live.sessionId && live.clientRequestId)
|
|
23126
|
+
this.send({ ...evt, clientRequestId: live.clientRequestId }, live.origin);
|
|
23127
|
+
else
|
|
23128
|
+
this.send(evt, live.origin);
|
|
23129
|
+
});
|
|
23130
|
+
}
|
|
23131
|
+
/** Resolve a parked skill-UI request (client replied via chat:ui_response).
|
|
23132
|
+
* Driver-only, same origin guard as respondToElicitation. */
|
|
23133
|
+
respondToUiRequest(requestId, response, callerOrigin) {
|
|
23134
|
+
const entry = this.pendingUiRequests.get(requestId);
|
|
23135
|
+
if (!entry)
|
|
23136
|
+
return;
|
|
23137
|
+
if (entry.origin && callerOrigin && entry.origin !== callerOrigin)
|
|
23138
|
+
return;
|
|
23139
|
+
this.pendingUiRequests.delete(requestId);
|
|
23140
|
+
entry.resolve(response);
|
|
23141
|
+
}
|
|
23142
|
+
/** Cancel (resolve as cancelled) any interactive cards still parked for a session
|
|
23143
|
+
* that's being torn down / aborted, so the awaiting tool calls unblock. */
|
|
23144
|
+
cancelSessionUiRequests(sessionId) {
|
|
23145
|
+
for (const [requestId, entry] of this.pendingUiRequests) {
|
|
23146
|
+
if (entry.sessionId !== sessionId)
|
|
23147
|
+
continue;
|
|
23148
|
+
this.pendingUiRequests.delete(requestId);
|
|
23149
|
+
entry.resolve({ cancelled: true });
|
|
23150
|
+
}
|
|
23151
|
+
}
|
|
22940
23152
|
/** M5 — per-connector live status from the running Query. Returns [] if the
|
|
22941
23153
|
* session isn't resident or the SDK call throws (idle/closed). */
|
|
22942
23154
|
async mcpServerStatus(sessionId) {
|
|
@@ -23086,6 +23298,135 @@ ${t}`)
|
|
|
23086
23298
|
setConfig(projectDir) {
|
|
23087
23299
|
this.projectDir = projectDir;
|
|
23088
23300
|
}
|
|
23301
|
+
// ── Tab-driven pre-warming ──────────────────────────────────────────────────
|
|
23302
|
+
// Warming = spawn the cli.js process EARLY (the SDK spawns eagerly at query()) so its
|
|
23303
|
+
// ~1.6–3.4s node+cli.js boot overlaps idle time; the first user message is pushed only
|
|
23304
|
+
// when the user actually sends, by which point boot is done → first token is fast. The
|
|
23305
|
+
// warm process emits nothing (system/init is gated on the first push) so warming is
|
|
23306
|
+
// fire-and-forget and never worse than a cold start.
|
|
23307
|
+
/** Stable key over the CONSTRUCTION-immutable config a NEW conversation bakes into its
|
|
23308
|
+
* query(). A warm empty instance may be adopted only by a send whose key matches —
|
|
23309
|
+
* model/permissionMode/sources are reconciled live at adoption; `url` is excluded
|
|
23310
|
+
* (warmed with '' so browser navigation doesn't invalidate the standby). */
|
|
23311
|
+
warmKey(p) {
|
|
23312
|
+
const cwd = p.projectDir ?? this.projectDir ?? p.sources?.[0] ?? "";
|
|
23313
|
+
return JSON.stringify({
|
|
23314
|
+
cwd,
|
|
23315
|
+
effort: p.effort ?? "",
|
|
23316
|
+
mcp: Object.keys(p.mcpServers ?? {}).sort(),
|
|
23317
|
+
agents: p.agents ? Object.keys(p.agents).sort() : [],
|
|
23318
|
+
settings: p.settings ?? null,
|
|
23319
|
+
settingSources: [...p.settingSources ?? []].sort(),
|
|
23320
|
+
sandbox: p.sandbox ?? null,
|
|
23321
|
+
env: p.env ?? null,
|
|
23322
|
+
thinking: p.thinking ?? null,
|
|
23323
|
+
engineEnv: p.engineEnv ?? null,
|
|
23324
|
+
maxTurns: p.maxTurns ?? null,
|
|
23325
|
+
maxBudgetUsd: p.maxBudgetUsd ?? null,
|
|
23326
|
+
outputStyle: p.outputStyle ?? null
|
|
23327
|
+
});
|
|
23328
|
+
}
|
|
23329
|
+
/** Pre-warm a session (no user message pushed). Two kinds:
|
|
23330
|
+
* • RESUME (params.sessionId): the resumed session becomes resident, so a later
|
|
23331
|
+
* chat:send for it just pushes — no spawn/boot wait. Idempotent if already resident.
|
|
23332
|
+
* • EMPTY (no sessionId): the single standby new-session instance (warmEmpty), adopted
|
|
23333
|
+
* by the next matching NEW conversation. Replaced if the config key changed.
|
|
23334
|
+
* Best-effort: no open project ⇒ no-op; a spawn failure is swallowed (warm=true keeps
|
|
23335
|
+
* createLiveSession from emitting a client chat:error). */
|
|
23336
|
+
async warmSession(params) {
|
|
23337
|
+
const cwd = params.projectDir ?? this.projectDir ?? params.sources?.[0];
|
|
23338
|
+
if (!cwd)
|
|
23339
|
+
return;
|
|
23340
|
+
if (params.sessionId) {
|
|
23341
|
+
if (this.liveSessions.has(params.sessionId))
|
|
23342
|
+
return;
|
|
23343
|
+
} else {
|
|
23344
|
+
const key2 = this.warmKey(params);
|
|
23345
|
+
if (this.warmEmpty && this.warmEmptyKey === key2)
|
|
23346
|
+
return;
|
|
23347
|
+
if (this.warmEmpty)
|
|
23348
|
+
this.discardWarmEmpty();
|
|
23349
|
+
}
|
|
23350
|
+
let live;
|
|
23351
|
+
try {
|
|
23352
|
+
live = await this.createLiveSession({ ...params, message: "", clientRequestId: void 0, origin: void 0, url: "", warm: true });
|
|
23353
|
+
} catch (err) {
|
|
23354
|
+
log("warmSession failed (best-effort):", err instanceof Error ? err.message : String(err));
|
|
23355
|
+
return;
|
|
23356
|
+
}
|
|
23357
|
+
if (params.sessionId) {
|
|
23358
|
+
if (this.activeTabSession !== params.sessionId)
|
|
23359
|
+
this.scheduleIdleReap(live);
|
|
23360
|
+
} else {
|
|
23361
|
+
this.warmEmpty = live;
|
|
23362
|
+
this.warmEmptyKey = this.warmKey(params);
|
|
23363
|
+
}
|
|
23364
|
+
}
|
|
23365
|
+
/** A tab gained focus (sessionId set) or a NEW-conversation tab is focused (sessionId
|
|
23366
|
+
* omitted). Pins the focused session against idle reap and releases the previously
|
|
23367
|
+
* focused one so its 15-min idle clock starts ("time since last switched-to"). A
|
|
23368
|
+
* focused session that isn't resident (e.g. switched back to a reaped tab) is
|
|
23369
|
+
* warm-resumed so it's ready before the user types; a focused new-conversation tab
|
|
23370
|
+
* ensures the standby empty instance exists. */
|
|
23371
|
+
async setActiveSession(sessionId, params) {
|
|
23372
|
+
const prev = this.activeTabSession;
|
|
23373
|
+
if (prev && prev !== sessionId)
|
|
23374
|
+
this.setSessionKeepAlive(prev, false, "tab");
|
|
23375
|
+
this.activeTabSession = sessionId ?? null;
|
|
23376
|
+
if (sessionId) {
|
|
23377
|
+
if (!this.liveSessions.has(sessionId))
|
|
23378
|
+
await this.warmSession({ ...params, sessionId });
|
|
23379
|
+
this.setSessionKeepAlive(sessionId, true, "tab");
|
|
23380
|
+
} else {
|
|
23381
|
+
await this.warmSession({ ...params, sessionId: void 0 });
|
|
23382
|
+
}
|
|
23383
|
+
}
|
|
23384
|
+
/** Bind the standby empty instance to a brand-new conversation: set its routing/title,
|
|
23385
|
+
* reconcile the live-settable config the warm query couldn't bake, and re-seed a fresh
|
|
23386
|
+
* standby. The caller (sendMessage) then pushes the user message as usual. */
|
|
23387
|
+
adoptWarmEmpty(params) {
|
|
23388
|
+
const live = this.warmEmpty;
|
|
23389
|
+
this.warmEmpty = null;
|
|
23390
|
+
this.warmEmptyKey = null;
|
|
23391
|
+
live.clientRequestId = params.clientRequestId;
|
|
23392
|
+
live.origin = params.origin;
|
|
23393
|
+
if (params.browserId !== void 0)
|
|
23394
|
+
live.browserId = params.browserId;
|
|
23395
|
+
if (params.windowId !== void 0)
|
|
23396
|
+
live.windowId = params.windowId;
|
|
23397
|
+
live.pendingTitle = params.message.trim().slice(0, 80);
|
|
23398
|
+
if (params.model)
|
|
23399
|
+
void live.query.setModel(params.model || void 0).catch(() => {
|
|
23400
|
+
});
|
|
23401
|
+
if (params.permissionMode)
|
|
23402
|
+
void live.query.setPermissionMode(params.permissionMode).catch(() => {
|
|
23403
|
+
});
|
|
23404
|
+
const nextSources = params.sources ?? [];
|
|
23405
|
+
if (!sourcesEqual(live.sources, nextSources)) {
|
|
23406
|
+
void live.query.applyFlagSettings({ permissions: { additionalDirectories: nextSources } }).then(() => {
|
|
23407
|
+
live.sources = nextSources;
|
|
23408
|
+
}).catch((err) => log("adoptWarmEmpty applyFlagSettings failed:", err instanceof Error ? err.message : String(err)));
|
|
23409
|
+
}
|
|
23410
|
+
void this.warmSession({ ...params, sessionId: void 0, clientRequestId: void 0 });
|
|
23411
|
+
return live;
|
|
23412
|
+
}
|
|
23413
|
+
/** Tear down the standby empty instance (no sessionId ⇒ not in liveSessions / never
|
|
23414
|
+
* idle-reaped). Used when its config key goes stale or on dispose. */
|
|
23415
|
+
discardWarmEmpty() {
|
|
23416
|
+
const live = this.warmEmpty;
|
|
23417
|
+
this.warmEmpty = null;
|
|
23418
|
+
this.warmEmptyKey = null;
|
|
23419
|
+
if (!live || live.reaped)
|
|
23420
|
+
return;
|
|
23421
|
+
live.reaped = true;
|
|
23422
|
+
this.pendingLiveSessions.delete(live);
|
|
23423
|
+
try {
|
|
23424
|
+
live.abort.abort();
|
|
23425
|
+
} catch {
|
|
23426
|
+
}
|
|
23427
|
+
killProcessTree(live.child, "SIGKILL");
|
|
23428
|
+
live.pump.end();
|
|
23429
|
+
}
|
|
23089
23430
|
buildSystemPrompt(url2) {
|
|
23090
23431
|
const cli = `ELECTRON_RUN_AS_NODE=1 "${process.execPath}" "${this.browserCliPath}"`;
|
|
23091
23432
|
const pageInfo = url2 ? `
|
|
@@ -23192,7 +23533,11 @@ CRITICAL RULES:
|
|
|
23192
23533
|
return;
|
|
23193
23534
|
}
|
|
23194
23535
|
if (!live) {
|
|
23195
|
-
|
|
23536
|
+
if (!params.sessionId && this.warmEmpty && this.warmEmptyKey === this.warmKey(params)) {
|
|
23537
|
+
live = this.adoptWarmEmpty(params);
|
|
23538
|
+
} else {
|
|
23539
|
+
live = await this.createLiveSession(params);
|
|
23540
|
+
}
|
|
23196
23541
|
} else {
|
|
23197
23542
|
live.streaming = true;
|
|
23198
23543
|
live.lastActivity = Date.now();
|
|
@@ -23387,11 +23732,33 @@ CRITICAL RULES:
|
|
|
23387
23732
|
});
|
|
23388
23733
|
mcp["tesseract-canvas"] = createSdkMcpServer({ name: "tesseract-canvas", version: "0.1.0", tools: canvasTools });
|
|
23389
23734
|
}
|
|
23735
|
+
{
|
|
23736
|
+
const projectDir = this.projectDir;
|
|
23737
|
+
const reportTools = buildReportTools({
|
|
23738
|
+
emitReport: (report) => {
|
|
23739
|
+
const ts = Date.now();
|
|
23740
|
+
this.send({ type: "agent:report_ready", sessionId: live.sessionId, report, ts }, live.origin);
|
|
23741
|
+
void appendReportArtifact(live.sessionId, projectDir, report).catch(() => {
|
|
23742
|
+
});
|
|
23743
|
+
}
|
|
23744
|
+
});
|
|
23745
|
+
mcp["tesseract-report"] = createSdkMcpServer({ name: "tesseract-report", version: "0.1.0", tools: reportTools });
|
|
23746
|
+
}
|
|
23747
|
+
{
|
|
23748
|
+
const uiTools = buildInteractiveUiTools({
|
|
23749
|
+
raiseUiRequest: (spec) => this.raiseUiRequest(live, spec),
|
|
23750
|
+
emitProgress: (stages, note) => this.send({ type: "agent:mission_progress", sessionId: live.sessionId, stages, note, ts: Date.now() }, live.origin)
|
|
23751
|
+
});
|
|
23752
|
+
mcp["tesseract-interactive-ui"] = createSdkMcpServer({ name: "tesseract-interactive-ui", version: "0.1.0", tools: uiTools });
|
|
23753
|
+
}
|
|
23390
23754
|
return Object.keys(mcp).length > 0 ? { mcpServers: mcp } : {};
|
|
23391
23755
|
})(),
|
|
23392
23756
|
// Skills (Epic H): a local plugin bundling the enabled ClawHub skills, so the SDK
|
|
23393
23757
|
// discovers their SKILL.md files (isolated from ~/.claude + the edited project).
|
|
23394
|
-
...
|
|
23758
|
+
...(() => {
|
|
23759
|
+
const plugins = [this.skillsPlugin, params.missionSkillId ? this.missionSkillsPlugin : null].filter(Boolean);
|
|
23760
|
+
return plugins.length > 0 ? { plugins } : {};
|
|
23761
|
+
})(),
|
|
23395
23762
|
...params.maxTurns && params.maxTurns > 0 ? { maxTurns: params.maxTurns } : {},
|
|
23396
23763
|
...params.maxBudgetUsd && params.maxBudgetUsd > 0 ? { maxBudgetUsd: params.maxBudgetUsd } : {},
|
|
23397
23764
|
// Surface every gated tool call to the client for approval. The SDK only
|
|
@@ -23528,7 +23895,8 @@ CRITICAL RULES:
|
|
|
23528
23895
|
const nodeExe2 = resolveNodeExecutable();
|
|
23529
23896
|
const exe = nodeExe2 ?? process.execPath;
|
|
23530
23897
|
const runAsNode = exe === process.execPath && !!process.versions.electron;
|
|
23531
|
-
const
|
|
23898
|
+
const compileCache = process.env.NODE_COMPILE_CACHE || join9(process.env.CCB_TESSERACT_DIR ?? join9(homedir4(), ".tesseract"), "node-compile-cache");
|
|
23899
|
+
const spawnEnv = { NODE_COMPILE_CACHE: compileCache, ...options.env, ...params.env ?? {}, ...params.engineEnv ?? {}, CCB_IPC_PORT: String(live.ipcPort), ...runAsNode ? { ELECTRON_RUN_AS_NODE: "1" } : {} };
|
|
23532
23900
|
const child = spawn2(exe, [cliJs, ...options.args], {
|
|
23533
23901
|
cwd: options.cwd,
|
|
23534
23902
|
env: spawnEnv,
|
|
@@ -23547,7 +23915,9 @@ CRITICAL RULES:
|
|
|
23547
23915
|
// restores the SDK's dynamic command list, gitStatus/date context,
|
|
23548
23916
|
// env-info and memory/CLAUDE.md preamble that a bare custom string
|
|
23549
23917
|
// prompt suppresses — while `append` keeps the Tesseract browser tools.
|
|
23550
|
-
systemPrompt: { type: "preset", preset: "claude_code", append: this.buildSystemPrompt(params.url)
|
|
23918
|
+
systemPrompt: { type: "preset", preset: "claude_code", append: this.buildSystemPrompt(params.url) + (params.appendSystemPrompt ? `
|
|
23919
|
+
|
|
23920
|
+
${params.appendSystemPrompt}` : "") },
|
|
23551
23921
|
includePartialMessages: true,
|
|
23552
23922
|
// Opt into AskUserQuestion option previews. Without a previewFormat the
|
|
23553
23923
|
// SDK omits the preview-field guidance from the tool prompt, so the
|
|
@@ -23585,10 +23955,12 @@ CRITICAL RULES:
|
|
|
23585
23955
|
live.ipcServer?.close();
|
|
23586
23956
|
} catch {
|
|
23587
23957
|
}
|
|
23588
|
-
|
|
23589
|
-
|
|
23590
|
-
|
|
23591
|
-
|
|
23958
|
+
if (!params.warm) {
|
|
23959
|
+
const sid = live.sessionId || "";
|
|
23960
|
+
const crid = live.sessionId ? void 0 : live.clientRequestId;
|
|
23961
|
+
this.send({ type: "chat:error", sessionId: sid, clientRequestId: crid, error: err instanceof Error ? err.message : "Failed to start the agent session." }, params.origin);
|
|
23962
|
+
this.send({ type: "agent:status", sessionId: sid, clientRequestId: crid, status: "error" }, params.origin);
|
|
23963
|
+
}
|
|
23592
23964
|
throw err;
|
|
23593
23965
|
}
|
|
23594
23966
|
if (params.sessionId)
|
|
@@ -24038,6 +24410,8 @@ ${err.stack}` : ""}` : String(err);
|
|
|
24038
24410
|
live.steerFlushTimer = void 0;
|
|
24039
24411
|
}
|
|
24040
24412
|
this.clearStopWatchdog(live);
|
|
24413
|
+
if (live.sessionId)
|
|
24414
|
+
this.cancelSessionUiRequests(live.sessionId);
|
|
24041
24415
|
if (live.sessionId)
|
|
24042
24416
|
this.liveSessions.delete(live.sessionId);
|
|
24043
24417
|
this.pendingLiveSessions.delete(live);
|
|
@@ -24298,6 +24672,9 @@ ${err.stack}` : ""}` : String(err);
|
|
|
24298
24672
|
this.cronToolsDeps = null;
|
|
24299
24673
|
this.cronTaskSessions.clear();
|
|
24300
24674
|
this.cronRequestTask.clear();
|
|
24675
|
+
this.warmEmpty = null;
|
|
24676
|
+
this.warmEmptyKey = null;
|
|
24677
|
+
this.activeTabSession = null;
|
|
24301
24678
|
for (const live of [...this.liveSessions.values(), ...this.pendingLiveSessions]) {
|
|
24302
24679
|
if (live.reaped)
|
|
24303
24680
|
continue;
|
|
@@ -24311,6 +24688,8 @@ ${err.stack}` : ""}` : String(err);
|
|
|
24311
24688
|
live.steerFlushTimer = void 0;
|
|
24312
24689
|
}
|
|
24313
24690
|
this.clearStopWatchdog(live);
|
|
24691
|
+
if (live.sessionId)
|
|
24692
|
+
this.cancelSessionUiRequests(live.sessionId);
|
|
24314
24693
|
if (live.sessionId)
|
|
24315
24694
|
this.liveSessions.delete(live.sessionId);
|
|
24316
24695
|
this.pendingLiveSessions.delete(live);
|
|
@@ -24324,6 +24703,8 @@ ${err.stack}` : ""}` : String(err);
|
|
|
24324
24703
|
}
|
|
24325
24704
|
/** Memory-pressure entrypoint: moderate trims long-idle sessions; critical reaps all idle. */
|
|
24326
24705
|
reclaim(level) {
|
|
24706
|
+
if (level === "critical")
|
|
24707
|
+
this.discardWarmEmpty();
|
|
24327
24708
|
const n = level === "critical" ? this.reapAllIdle() : this.reapIdleSessions(MODERATE_IDLE_MS);
|
|
24328
24709
|
if (n > 0)
|
|
24329
24710
|
log(`Memory reclaim (${level}): reaped ${n} idle session(s)`);
|
|
@@ -42755,7 +43136,7 @@ var require_selfsigned = __commonJS({
|
|
|
42755
43136
|
import tls from "node:tls";
|
|
42756
43137
|
import { X509Certificate, timingSafeEqual } from "node:crypto";
|
|
42757
43138
|
import { readFileSync as readFileSync9, writeFileSync as writeFileSync9, existsSync as existsSync10, mkdirSync as mkdirSync8 } from "node:fs";
|
|
42758
|
-
import { dirname as
|
|
43139
|
+
import { dirname as dirname9 } from "node:path";
|
|
42759
43140
|
function fingerprintOf(certPem) {
|
|
42760
43141
|
return new X509Certificate(certPem).fingerprint256;
|
|
42761
43142
|
}
|
|
@@ -42775,7 +43156,7 @@ async function loadOrCreateIdentity(certPath, keyPath) {
|
|
|
42775
43156
|
const tenYears = new Date(Date.now() + 10 * 365 * 24 * 60 * 60 * 1e3);
|
|
42776
43157
|
const pems = await (0, import_selfsigned.generate)([{ name: "commonName", value: "tesseract-peer" }], { keySize: 2048, notAfterDate: tenYears, algorithm: "sha256" });
|
|
42777
43158
|
try {
|
|
42778
|
-
mkdirSync8(
|
|
43159
|
+
mkdirSync8(dirname9(certPath), { recursive: true });
|
|
42779
43160
|
} catch {
|
|
42780
43161
|
}
|
|
42781
43162
|
try {
|
|
@@ -44301,12 +44682,12 @@ import { createHash as createHash5 } from "node:crypto";
|
|
|
44301
44682
|
import { spawn as spawn8 } from "node:child_process";
|
|
44302
44683
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
44303
44684
|
import { homedir as homedir16 } from "node:os";
|
|
44304
|
-
import { dirname as
|
|
44685
|
+
import { dirname as dirname17, join as join38, resolve as resolve9 } from "node:path";
|
|
44305
44686
|
import { createInterface } from "node:readline/promises";
|
|
44306
44687
|
|
|
44307
44688
|
// ../../packages/host-core/dist/control-link.js
|
|
44308
44689
|
import { hostname as hostname3 } from "node:os";
|
|
44309
|
-
import { join as join30, basename as
|
|
44690
|
+
import { join as join30, basename as basename6 } from "node:path";
|
|
44310
44691
|
import { mkdirSync as mkdirSync11 } from "node:fs";
|
|
44311
44692
|
|
|
44312
44693
|
// ../../node_modules/ws/wrapper.mjs
|
|
@@ -44733,7 +45114,7 @@ import { join as join18 } from "node:path";
|
|
|
44733
45114
|
|
|
44734
45115
|
// ../../packages/host-core/dist/agent-backend.js
|
|
44735
45116
|
async function createClaudeCodeBackend(send2, onBrowserRequest, loadPluginRuntime2) {
|
|
44736
|
-
const { AgentManager: AgentManager2, SessionStore: SessionStore2,
|
|
45117
|
+
const { AgentManager: AgentManager2, SessionStore: SessionStore2, readSessionArtifacts: readSessionArtifacts2 } = await Promise.resolve().then(() => (init_dist4(), dist_exports));
|
|
44737
45118
|
const manager2 = new AgentManager2(send2, onBrowserRequest);
|
|
44738
45119
|
const store = new SessionStore2();
|
|
44739
45120
|
const refreshPlugins = async (projectDir) => {
|
|
@@ -44761,8 +45142,6 @@ async function createClaudeCodeBackend(send2, onBrowserRequest, loadPluginRuntim
|
|
|
44761
45142
|
getMessages: (sessionId) => store.getSessionMessages(sessionId),
|
|
44762
45143
|
getSessionArtifacts: (sessionId, projectDir) => readSessionArtifacts2(sessionId, projectDir),
|
|
44763
45144
|
summarizeSession: async (sessionId) => manager2.summarizeSession(await store.getSessionMessages(sessionId)),
|
|
44764
|
-
isSessionBusy: (sessionId) => manager2.isSessionBusy(sessionId),
|
|
44765
|
-
resolveSessionFile: (sessionId, projectDir) => resolveSessionFile2(sessionId, projectDir),
|
|
44766
45145
|
setConfig: (projectDir) => {
|
|
44767
45146
|
manager2.setConfig(projectDir);
|
|
44768
45147
|
void refreshPlugins(projectDir);
|
|
@@ -44773,9 +45152,12 @@ async function createClaudeCodeBackend(send2, onBrowserRequest, loadPluginRuntim
|
|
|
44773
45152
|
setImageGen: (runner) => manager2.setImageGen(runner),
|
|
44774
45153
|
setRunMode: (sessionId, opts) => manager2.setRunMode(sessionId, opts),
|
|
44775
45154
|
setSessionKeepAlive: (sessionId, keep, holder) => manager2.setSessionKeepAlive(sessionId, keep, holder),
|
|
45155
|
+
warmSession: (params) => manager2.warmSession(params),
|
|
45156
|
+
setActiveSession: (sessionId, params) => manager2.setActiveSession(sessionId, params),
|
|
44776
45157
|
clearSessionOrigin: (sessionId) => manager2.clearSessionOrigin(sessionId),
|
|
44777
45158
|
stopTask: (sessionId, taskId, callerOrigin) => manager2.stopTask(sessionId, taskId, callerOrigin),
|
|
44778
45159
|
respondToElicitation: (requestId, decision, callerOrigin) => manager2.respondToElicitation(requestId, decision, callerOrigin),
|
|
45160
|
+
respondToUiRequest: (requestId, response, callerOrigin) => manager2.respondToUiRequest(requestId, response, callerOrigin),
|
|
44779
45161
|
mcpServerStatus: (sessionId) => manager2.mcpServerStatus(sessionId),
|
|
44780
45162
|
reconnectMcpServer: (sessionId, name) => manager2.reconnectMcpServer(sessionId, name),
|
|
44781
45163
|
toggleMcpServer: (sessionId, name, enabled) => manager2.toggleMcpServer(sessionId, name, enabled),
|
|
@@ -45361,8 +45743,8 @@ function createOpenClawAdapter(pluginId, config2, secretResolver, registry2) {
|
|
|
45361
45743
|
pluginId,
|
|
45362
45744
|
config: config2,
|
|
45363
45745
|
secret: (envKey2) => secretResolver(pluginId, envKey2),
|
|
45364
|
-
registerTool: (
|
|
45365
|
-
const got = typeof
|
|
45746
|
+
registerTool: (tool7) => {
|
|
45747
|
+
const got = typeof tool7 === "function" ? tool7({}) : tool7;
|
|
45366
45748
|
const arr = Array.isArray(got) ? got : got ? [got] : [];
|
|
45367
45749
|
for (const t of arr) {
|
|
45368
45750
|
const c = convertTool(t);
|
|
@@ -45431,10 +45813,10 @@ var HostPluginApi = class {
|
|
|
45431
45813
|
secret(envKey2) {
|
|
45432
45814
|
return this.secretResolver(this.pluginId, envKey2);
|
|
45433
45815
|
}
|
|
45434
|
-
registerTool(
|
|
45816
|
+
registerTool(tool7) {
|
|
45435
45817
|
const prefix = this.pluginId.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
45436
|
-
const name =
|
|
45437
|
-
this.registry.tools.push({ ...
|
|
45818
|
+
const name = tool7.name.startsWith(`${prefix}_`) ? tool7.name : `${prefix}_${tool7.name}`;
|
|
45819
|
+
this.registry.tools.push({ ...tool7, name });
|
|
45438
45820
|
}
|
|
45439
45821
|
registerHook(reg) {
|
|
45440
45822
|
this.registry.hooks.push(reg);
|
|
@@ -46061,9 +46443,9 @@ function augmentedPath() {
|
|
|
46061
46443
|
return parts.join(sep8);
|
|
46062
46444
|
}
|
|
46063
46445
|
function which(bin, path5) {
|
|
46064
|
-
const
|
|
46446
|
+
const tool7 = process.platform === "win32" ? "where" : "which";
|
|
46065
46447
|
try {
|
|
46066
|
-
const out = execFileSync(
|
|
46448
|
+
const out = execFileSync(tool7, [bin], { env: { ...process.env, PATH: path5 }, timeout: 4e3, encoding: "utf-8" });
|
|
46067
46449
|
return out.split(/\r?\n/).map((l) => l.trim()).find(Boolean) || void 0;
|
|
46068
46450
|
} catch {
|
|
46069
46451
|
return void 0;
|
|
@@ -46179,12 +46561,12 @@ async function loadPluginRuntime(opts) {
|
|
|
46179
46561
|
function buildDaemonPluginLoader(pluginsDir) {
|
|
46180
46562
|
return async () => {
|
|
46181
46563
|
const skillsPlugin = daemonSkillsPluginConfig();
|
|
46182
|
-
const
|
|
46183
|
-
if (!existsSync7(
|
|
46564
|
+
const scan = join16(pluginsDir, "node_modules");
|
|
46565
|
+
if (!existsSync7(scan))
|
|
46184
46566
|
return { tools: [], hooks: [], engines: [], skillsPlugin };
|
|
46185
46567
|
writeOpenClawShim(pluginsDir);
|
|
46186
46568
|
const rt = await loadPluginRuntime({
|
|
46187
|
-
roots: [{ dir:
|
|
46569
|
+
roots: [{ dir: scan, trusted: true, relaxHardlink: true }],
|
|
46188
46570
|
secretResolver: (_pluginId, envKey2) => process.env[envKey2]
|
|
46189
46571
|
});
|
|
46190
46572
|
return { ...rt, skillsPlugin };
|
|
@@ -46196,7 +46578,7 @@ function daemonSkillsPluginConfig() {
|
|
|
46196
46578
|
return null;
|
|
46197
46579
|
const enabled = join16(root, "skills");
|
|
46198
46580
|
try {
|
|
46199
|
-
const hasSkill = readdirSync(enabled).some((
|
|
46581
|
+
const hasSkill = readdirSync(enabled).some((slug2) => existsSync7(join16(enabled, slug2, "SKILL.md")));
|
|
46200
46582
|
return hasSkill ? { type: "local", path: root } : null;
|
|
46201
46583
|
} catch {
|
|
46202
46584
|
return null;
|
|
@@ -47556,125 +47938,6 @@ function unwatchAllRemote() {
|
|
|
47556
47938
|
remoteWatches.clear();
|
|
47557
47939
|
}
|
|
47558
47940
|
|
|
47559
|
-
// ../../packages/host-core/dist/session-watch.js
|
|
47560
|
-
import { watch as watch2 } from "node:fs";
|
|
47561
|
-
import { dirname as dirname8, basename as basename3 } from "node:path";
|
|
47562
|
-
var DEBOUNCE_MS = 120;
|
|
47563
|
-
var BUSY_GRACE_MS = 2e3;
|
|
47564
|
-
var watches = /* @__PURE__ */ new Map();
|
|
47565
|
-
var sessionToFile = /* @__PURE__ */ new Map();
|
|
47566
|
-
async function watchSession(sessionId, key2, projectDir, deps) {
|
|
47567
|
-
let filePath = sessionToFile.get(sessionId);
|
|
47568
|
-
if (!filePath) {
|
|
47569
|
-
const resolved = await deps.resolveFile?.(sessionId, projectDir);
|
|
47570
|
-
if (!resolved)
|
|
47571
|
-
return;
|
|
47572
|
-
filePath = resolved;
|
|
47573
|
-
sessionToFile.set(sessionId, filePath);
|
|
47574
|
-
}
|
|
47575
|
-
const baseline = await deps.getMessages(sessionId).then((m) => m.length).catch(() => 0);
|
|
47576
|
-
const existing = watches.get(filePath);
|
|
47577
|
-
if (existing) {
|
|
47578
|
-
existing.listeners.set(key2, { emittedCount: baseline, deps });
|
|
47579
|
-
return;
|
|
47580
|
-
}
|
|
47581
|
-
const fileName = basename3(filePath);
|
|
47582
|
-
const fw = {
|
|
47583
|
-
watcher: void 0,
|
|
47584
|
-
sessionId,
|
|
47585
|
-
listeners: /* @__PURE__ */ new Map([[key2, { emittedCount: baseline, deps }]]),
|
|
47586
|
-
timer: null,
|
|
47587
|
-
lastBusyAt: 0
|
|
47588
|
-
};
|
|
47589
|
-
try {
|
|
47590
|
-
fw.watcher = watch2(dirname8(filePath), (_event, changed) => {
|
|
47591
|
-
if (changed && basename3(String(changed)) !== fileName)
|
|
47592
|
-
return;
|
|
47593
|
-
scheduleScan(filePath);
|
|
47594
|
-
});
|
|
47595
|
-
} catch {
|
|
47596
|
-
return;
|
|
47597
|
-
}
|
|
47598
|
-
watches.set(filePath, fw);
|
|
47599
|
-
}
|
|
47600
|
-
function unwatchSession(sessionId, key2) {
|
|
47601
|
-
const filePath = sessionToFile.get(sessionId);
|
|
47602
|
-
if (!filePath)
|
|
47603
|
-
return;
|
|
47604
|
-
const fw = watches.get(filePath);
|
|
47605
|
-
if (!fw)
|
|
47606
|
-
return;
|
|
47607
|
-
fw.listeners.delete(key2);
|
|
47608
|
-
if (fw.listeners.size === 0)
|
|
47609
|
-
closeWatch(filePath);
|
|
47610
|
-
}
|
|
47611
|
-
function unwatchSessionClient(key2) {
|
|
47612
|
-
for (const [filePath, fw] of [...watches]) {
|
|
47613
|
-
if (fw.listeners.delete(key2) && fw.listeners.size === 0)
|
|
47614
|
-
closeWatch(filePath);
|
|
47615
|
-
}
|
|
47616
|
-
}
|
|
47617
|
-
function unwatchAllSessions() {
|
|
47618
|
-
for (const [filePath] of [...watches])
|
|
47619
|
-
closeWatch(filePath);
|
|
47620
|
-
}
|
|
47621
|
-
function closeWatch(filePath) {
|
|
47622
|
-
const fw = watches.get(filePath);
|
|
47623
|
-
if (!fw)
|
|
47624
|
-
return;
|
|
47625
|
-
if (fw.timer)
|
|
47626
|
-
clearTimeout(fw.timer);
|
|
47627
|
-
try {
|
|
47628
|
-
fw.watcher.close();
|
|
47629
|
-
} catch {
|
|
47630
|
-
}
|
|
47631
|
-
watches.delete(filePath);
|
|
47632
|
-
sessionToFile.delete(fw.sessionId);
|
|
47633
|
-
}
|
|
47634
|
-
function scheduleScan(filePath) {
|
|
47635
|
-
const fw = watches.get(filePath);
|
|
47636
|
-
if (!fw)
|
|
47637
|
-
return;
|
|
47638
|
-
if (fw.timer)
|
|
47639
|
-
clearTimeout(fw.timer);
|
|
47640
|
-
fw.timer = setTimeout(() => {
|
|
47641
|
-
fw.timer = null;
|
|
47642
|
-
void scan(filePath);
|
|
47643
|
-
}, DEBOUNCE_MS);
|
|
47644
|
-
}
|
|
47645
|
-
async function scan(filePath) {
|
|
47646
|
-
const fw = watches.get(filePath);
|
|
47647
|
-
if (!fw)
|
|
47648
|
-
return;
|
|
47649
|
-
const { sessionId } = fw;
|
|
47650
|
-
const any2 = fw.listeners.values().next().value;
|
|
47651
|
-
if (!any2)
|
|
47652
|
-
return;
|
|
47653
|
-
const busy = any2.deps.isSessionBusy?.(sessionId) === true;
|
|
47654
|
-
const now = Date.now();
|
|
47655
|
-
if (busy)
|
|
47656
|
-
fw.lastBusyAt = now;
|
|
47657
|
-
const inGrace = busy || now - fw.lastBusyAt < BUSY_GRACE_MS;
|
|
47658
|
-
const messages = await any2.deps.getMessages(sessionId).catch(() => null);
|
|
47659
|
-
if (!messages)
|
|
47660
|
-
return;
|
|
47661
|
-
const len = messages.length;
|
|
47662
|
-
const willCompact = !inGrace && [...fw.listeners.values()].some((l) => len < l.emittedCount);
|
|
47663
|
-
const artifacts = willCompact ? await (any2.deps.getSessionArtifacts?.(sessionId).catch(() => []) ?? Promise.resolve([])) : [];
|
|
47664
|
-
for (const [key2, listener] of fw.listeners) {
|
|
47665
|
-
if (inGrace) {
|
|
47666
|
-
listener.emittedCount = len;
|
|
47667
|
-
continue;
|
|
47668
|
-
}
|
|
47669
|
-
if (len < listener.emittedCount) {
|
|
47670
|
-
listener.deps.emit(key2, { type: "session:messages", sessionId, messages, artifacts });
|
|
47671
|
-
} else if (len > listener.emittedCount) {
|
|
47672
|
-
listener.deps.emit(key2, { type: "session:appended", sessionId, messages: messages.slice(listener.emittedCount) });
|
|
47673
|
-
}
|
|
47674
|
-
listener.emittedCount = len;
|
|
47675
|
-
}
|
|
47676
|
-
}
|
|
47677
|
-
|
|
47678
47941
|
// ../../packages/host-core/dist/fs-datachannel-host.js
|
|
47679
47942
|
var SCTP_BUF = 16 * 1024 * 1024;
|
|
47680
47943
|
var ndcPromise = null;
|
|
@@ -47887,7 +48150,7 @@ function dynamicMetrics() {
|
|
|
47887
48150
|
// ../../packages/host-core/dist/fs-transfer.js
|
|
47888
48151
|
import { readdir as readdir4, lstat as lstat2, mkdir as mkdir6, chmod, rm as rm2, readlink, symlink, stat as stat3 } from "node:fs/promises";
|
|
47889
48152
|
import { openSync as openSync3, readSync as readSync2, closeSync as closeSync3, existsSync as existsSync9, createWriteStream } from "node:fs";
|
|
47890
|
-
import { dirname as
|
|
48153
|
+
import { dirname as dirname8, join as join22, relative as relative6, sep as sep6 } from "node:path";
|
|
47891
48154
|
|
|
47892
48155
|
// ../../packages/host-core/dist/active-link/paths.js
|
|
47893
48156
|
import { homedir as homedir11 } from "node:os";
|
|
@@ -48234,9 +48497,9 @@ async function writeInboundChunk(msg, send2) {
|
|
|
48234
48497
|
try {
|
|
48235
48498
|
if (msg.kind === "symlink") {
|
|
48236
48499
|
const target = Buffer.from(msg.base64, "base64").toString("utf-8");
|
|
48237
|
-
const resolved = join22(
|
|
48500
|
+
const resolved = join22(dirname8(abs), target);
|
|
48238
48501
|
if (isInsideRoot(resolved, s.destRoot)) {
|
|
48239
|
-
await mkdir6(
|
|
48502
|
+
await mkdir6(dirname8(abs), { recursive: true }).catch(() => {
|
|
48240
48503
|
});
|
|
48241
48504
|
await symlink(target, abs).catch(() => {
|
|
48242
48505
|
});
|
|
@@ -48249,7 +48512,7 @@ async function writeInboundChunk(msg, send2) {
|
|
|
48249
48512
|
if (s.curIx !== msg.fileIx || !s.curStream) {
|
|
48250
48513
|
if (s.curStream)
|
|
48251
48514
|
await new Promise((r) => s.curStream.end(r));
|
|
48252
|
-
await mkdir6(
|
|
48515
|
+
await mkdir6(dirname8(abs), { recursive: true }).catch(() => {
|
|
48253
48516
|
});
|
|
48254
48517
|
s.curStream = createWriteStream(abs);
|
|
48255
48518
|
s.curIx = msg.fileIx;
|
|
@@ -48766,13 +49029,13 @@ async function openPeerLink(peerHostId, ctx) {
|
|
|
48766
49029
|
// ../../packages/host-core/dist/active-link/engine.js
|
|
48767
49030
|
import { lstat as lstat7, readlink as readlink4, rm as rm3, mkdir as mkdir8, symlink as symlink2, rename as rename2, utimes as utimes2, writeFile as writeFile6 } from "node:fs/promises";
|
|
48768
49031
|
import { existsSync as existsSync12 } from "node:fs";
|
|
48769
|
-
import { dirname as
|
|
49032
|
+
import { dirname as dirname13, join as join28, resolve as resolve8 } from "node:path";
|
|
48770
49033
|
|
|
48771
49034
|
// ../../packages/host-core/dist/active-link/blocks.js
|
|
48772
49035
|
import { createHash as createHash3 } from "node:crypto";
|
|
48773
49036
|
import { createReadStream } from "node:fs";
|
|
48774
49037
|
import { open, mkdir as mkdir7, rename, chmod as chmod2, utimes, unlink } from "node:fs/promises";
|
|
48775
|
-
import { dirname as
|
|
49038
|
+
import { dirname as dirname10 } from "node:path";
|
|
48776
49039
|
var CHUNK3 = 1024 * 1024;
|
|
48777
49040
|
var MAX_INFLIGHT2 = 8 * CHUNK3;
|
|
48778
49041
|
var IDLE_MS2 = 6e4;
|
|
@@ -48900,7 +49163,7 @@ var StagingWriter = class {
|
|
|
48900
49163
|
}
|
|
48901
49164
|
async ensureOpen() {
|
|
48902
49165
|
if (!this.fh) {
|
|
48903
|
-
await mkdir7(
|
|
49166
|
+
await mkdir7(dirname10(this.stagingPath), { recursive: true });
|
|
48904
49167
|
this.fh = await open(this.stagingPath, "w");
|
|
48905
49168
|
}
|
|
48906
49169
|
return this.fh;
|
|
@@ -48930,7 +49193,7 @@ var StagingWriter = class {
|
|
|
48930
49193
|
});
|
|
48931
49194
|
await fh.close();
|
|
48932
49195
|
this.fh = null;
|
|
48933
|
-
await mkdir7(
|
|
49196
|
+
await mkdir7(dirname10(dest), { recursive: true });
|
|
48934
49197
|
await chmod2(this.stagingPath, mode & 511).catch(() => {
|
|
48935
49198
|
});
|
|
48936
49199
|
const secs = mtimeMs / 1e3;
|
|
@@ -48963,7 +49226,7 @@ var BlockAssembler = class {
|
|
|
48963
49226
|
this.stagingPath = stagingPath;
|
|
48964
49227
|
}
|
|
48965
49228
|
async open() {
|
|
48966
|
-
await mkdir7(
|
|
49229
|
+
await mkdir7(dirname10(this.stagingPath), { recursive: true });
|
|
48967
49230
|
this.fh = await open(this.stagingPath, "w");
|
|
48968
49231
|
}
|
|
48969
49232
|
/** Write a received block at its byte offset (index * CHUNK). */
|
|
@@ -49007,7 +49270,7 @@ var BlockAssembler = class {
|
|
|
49007
49270
|
});
|
|
49008
49271
|
return false;
|
|
49009
49272
|
}
|
|
49010
|
-
await mkdir7(
|
|
49273
|
+
await mkdir7(dirname10(dest), { recursive: true });
|
|
49011
49274
|
await chmod2(this.stagingPath, mode & 511).catch(() => {
|
|
49012
49275
|
});
|
|
49013
49276
|
const secs = mtimeMs / 1e3;
|
|
@@ -49593,10 +49856,10 @@ var ReaddirpStream = class extends Readable {
|
|
|
49593
49856
|
}
|
|
49594
49857
|
async _formatEntry(dirent, path5) {
|
|
49595
49858
|
let entry;
|
|
49596
|
-
const
|
|
49859
|
+
const basename7 = this._isDirent ? dirent.name : dirent;
|
|
49597
49860
|
try {
|
|
49598
|
-
const fullPath = presolve(pjoin(path5,
|
|
49599
|
-
entry = { path: prelative(this._root, fullPath), fullPath, basename:
|
|
49861
|
+
const fullPath = presolve(pjoin(path5, basename7));
|
|
49862
|
+
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename7 };
|
|
49600
49863
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
49601
49864
|
} catch (err) {
|
|
49602
49865
|
this._onError(err);
|
|
@@ -50135,9 +50398,9 @@ var NodeFsHandler = class {
|
|
|
50135
50398
|
_watchWithNodeFs(path5, listener) {
|
|
50136
50399
|
const opts = this.fsw.options;
|
|
50137
50400
|
const directory = sysPath.dirname(path5);
|
|
50138
|
-
const
|
|
50401
|
+
const basename7 = sysPath.basename(path5);
|
|
50139
50402
|
const parent = this.fsw._getWatchedDir(directory);
|
|
50140
|
-
parent.add(
|
|
50403
|
+
parent.add(basename7);
|
|
50141
50404
|
const absolutePath = sysPath.resolve(path5);
|
|
50142
50405
|
const options = {
|
|
50143
50406
|
persistent: opts.persistent
|
|
@@ -50147,7 +50410,7 @@ var NodeFsHandler = class {
|
|
|
50147
50410
|
let closer;
|
|
50148
50411
|
if (opts.usePolling) {
|
|
50149
50412
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
50150
|
-
options.interval = enableBin && isBinaryPath(
|
|
50413
|
+
options.interval = enableBin && isBinaryPath(basename7) ? opts.binaryInterval : opts.interval;
|
|
50151
50414
|
closer = setFsWatchFileListener(path5, absolutePath, options, {
|
|
50152
50415
|
listener,
|
|
50153
50416
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -50169,11 +50432,11 @@ var NodeFsHandler = class {
|
|
|
50169
50432
|
if (this.fsw.closed) {
|
|
50170
50433
|
return;
|
|
50171
50434
|
}
|
|
50172
|
-
const
|
|
50173
|
-
const
|
|
50174
|
-
const parent = this.fsw._getWatchedDir(
|
|
50435
|
+
const dirname18 = sysPath.dirname(file2);
|
|
50436
|
+
const basename7 = sysPath.basename(file2);
|
|
50437
|
+
const parent = this.fsw._getWatchedDir(dirname18);
|
|
50175
50438
|
let prevStats = stats;
|
|
50176
|
-
if (parent.has(
|
|
50439
|
+
if (parent.has(basename7))
|
|
50177
50440
|
return;
|
|
50178
50441
|
const listener = async (path5, newStats) => {
|
|
50179
50442
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file2, 5))
|
|
@@ -50198,9 +50461,9 @@ var NodeFsHandler = class {
|
|
|
50198
50461
|
prevStats = newStats2;
|
|
50199
50462
|
}
|
|
50200
50463
|
} catch (error51) {
|
|
50201
|
-
this.fsw._remove(
|
|
50464
|
+
this.fsw._remove(dirname18, basename7);
|
|
50202
50465
|
}
|
|
50203
|
-
} else if (parent.has(
|
|
50466
|
+
} else if (parent.has(basename7)) {
|
|
50204
50467
|
const at = newStats.atimeMs;
|
|
50205
50468
|
const mt = newStats.mtimeMs;
|
|
50206
50469
|
if (!at || at <= mt || mt !== prevStats.mtimeMs) {
|
|
@@ -51124,7 +51387,7 @@ var FSWatcher = class extends EventEmitter2 {
|
|
|
51124
51387
|
return stream;
|
|
51125
51388
|
}
|
|
51126
51389
|
};
|
|
51127
|
-
function
|
|
51390
|
+
function watch2(paths, options = {}) {
|
|
51128
51391
|
const watcher = new FSWatcher(options);
|
|
51129
51392
|
watcher.add(paths);
|
|
51130
51393
|
return watcher;
|
|
@@ -51142,7 +51405,7 @@ var LinkWatcher = class {
|
|
|
51142
51405
|
this.debounceMs = opts.debounceMs ?? 500;
|
|
51143
51406
|
}
|
|
51144
51407
|
async start() {
|
|
51145
|
-
this.fsw =
|
|
51408
|
+
this.fsw = watch2(this.opts.root, {
|
|
51146
51409
|
ignoreInitial: true,
|
|
51147
51410
|
// the engine does its own hashing scan on start
|
|
51148
51411
|
followSymlinks: false,
|
|
@@ -51643,13 +51906,13 @@ var ActiveLinkEngine = class {
|
|
|
51643
51906
|
* file so the path still exists — recorded as a FILE matching disk so the local
|
|
51644
51907
|
* watcher doesn't churn. The target is containment-checked, never dereferenced. */
|
|
51645
51908
|
async applySymlink(rel, dest, target, mtimeMs, mode) {
|
|
51646
|
-
const resolved = resolve8(
|
|
51909
|
+
const resolved = resolve8(dirname13(dest), target);
|
|
51647
51910
|
if (!isInsideRoot(resolved, this.opts.localRoot))
|
|
51648
51911
|
return;
|
|
51649
51912
|
const hash2 = hashString(target);
|
|
51650
51913
|
const secs = mtimeMs / 1e3;
|
|
51651
51914
|
try {
|
|
51652
|
-
await mkdir8(
|
|
51915
|
+
await mkdir8(dirname13(dest), { recursive: true });
|
|
51653
51916
|
await rm3(dest, { force: true, recursive: true });
|
|
51654
51917
|
try {
|
|
51655
51918
|
await symlink2(target, dest);
|
|
@@ -51701,7 +51964,7 @@ var ActiveLinkEngine = class {
|
|
|
51701
51964
|
return;
|
|
51702
51965
|
if (fromRec && fromRec.kind === "file") {
|
|
51703
51966
|
try {
|
|
51704
|
-
await mkdir8(
|
|
51967
|
+
await mkdir8(dirname13(toAbs2), { recursive: true });
|
|
51705
51968
|
await rename2(fromAbs, toAbs2);
|
|
51706
51969
|
const secs = mtimeMs / 1e3;
|
|
51707
51970
|
await utimes2(toAbs2, secs, secs).catch(() => {
|
|
@@ -51901,7 +52164,7 @@ var ActiveLinkEngine = class {
|
|
|
51901
52164
|
};
|
|
51902
52165
|
|
|
51903
52166
|
// ../../packages/host-core/dist/active-link/config.js
|
|
51904
|
-
import { readFileSync as readFileSync11, writeFileSync as writeFileSync11, mkdirSync as mkdirSync10, existsSync as existsSync13, renameSync as renameSync6, watch as
|
|
52167
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync11, mkdirSync as mkdirSync10, existsSync as existsSync13, renameSync as renameSync6, watch as watch3 } from "node:fs";
|
|
51905
52168
|
import { join as join29 } from "node:path";
|
|
51906
52169
|
function configPath2() {
|
|
51907
52170
|
return join29(activeLinkRoot(), "links.json");
|
|
@@ -51975,7 +52238,7 @@ function watchLinkConfigs(onChange) {
|
|
|
51975
52238
|
let timer = null;
|
|
51976
52239
|
let watcher = null;
|
|
51977
52240
|
try {
|
|
51978
|
-
watcher =
|
|
52241
|
+
watcher = watch3(dir, (_evt, file2) => {
|
|
51979
52242
|
if (file2 && file2 !== "links.json")
|
|
51980
52243
|
return;
|
|
51981
52244
|
if (timer)
|
|
@@ -52384,7 +52647,7 @@ function clampDim(v, dflt) {
|
|
|
52384
52647
|
init_dist2();
|
|
52385
52648
|
|
|
52386
52649
|
// ../../packages/host-core/dist/voice-control.js
|
|
52387
|
-
import { basename as
|
|
52650
|
+
import { basename as basename5 } from "node:path";
|
|
52388
52651
|
|
|
52389
52652
|
// ../../packages/voice-core/dist/providers/tool-adapters.js
|
|
52390
52653
|
function toELClientTool(t) {
|
|
@@ -53707,7 +53970,7 @@ var VoiceControl = class {
|
|
|
53707
53970
|
voice: msg.voice,
|
|
53708
53971
|
language: msg.language,
|
|
53709
53972
|
turnSensitivity: msg.turnSensitivity,
|
|
53710
|
-
instructions: buildGuidePrompt({ projectName: projectDir ?
|
|
53973
|
+
instructions: buildGuidePrompt({ projectName: projectDir ? basename5(projectDir) : void 0, sessions: msg.sessions }),
|
|
53711
53974
|
greeting: DEFAULT_GREETING
|
|
53712
53975
|
};
|
|
53713
53976
|
const orchestrator = new VoiceGuideOrchestrator({
|
|
@@ -54081,7 +54344,7 @@ function sendActiveLinkProvision(cfg2) {
|
|
|
54081
54344
|
type: "provision",
|
|
54082
54345
|
linkId: cfg2.linkId,
|
|
54083
54346
|
fromHost: ownHostId(),
|
|
54084
|
-
projectName: cfg2.remoteSubfolder ??
|
|
54347
|
+
projectName: cfg2.remoteSubfolder ?? basename6(cfg2.localRoot),
|
|
54085
54348
|
...cfg2.remoteParent ? { remoteParent: cfg2.remoteParent } : {},
|
|
54086
54349
|
...cfg2.remoteSubfolder ? { remoteSubfolder: cfg2.remoteSubfolder } : {},
|
|
54087
54350
|
...cfg2.workspaceLinkId ? { workspaceLinkId: cfg2.workspaceLinkId } : {}
|
|
@@ -54288,8 +54551,11 @@ async function handleInboundAgent(msg, driverClientId) {
|
|
|
54288
54551
|
...msg.permissionMode ? { permissionMode: msg.permissionMode } : {},
|
|
54289
54552
|
...msg.effort ? { effort: msg.effort } : {},
|
|
54290
54553
|
...msg.model ? { model: msg.model } : {},
|
|
54291
|
-
...msg.jsonSchema ? { jsonSchema: msg.jsonSchema } : {}
|
|
54554
|
+
...msg.jsonSchema ? { jsonSchema: msg.jsonSchema } : {},
|
|
54292
54555
|
// M7 structured output
|
|
54556
|
+
...msg.appendSystemPrompt ? { appendSystemPrompt: msg.appendSystemPrompt } : {},
|
|
54557
|
+
// mission hidden launch
|
|
54558
|
+
...msg.missionSkillId ? { missionSkillId: msg.missionSkillId } : {}
|
|
54293
54559
|
})).catch(() => {
|
|
54294
54560
|
});
|
|
54295
54561
|
break;
|
|
@@ -54316,21 +54582,6 @@ async function handleInboundAgent(msg, driverClientId) {
|
|
|
54316
54582
|
send("agent", { type: "session:summary", clientRequestId: msg.clientRequestId, ...res }, route2);
|
|
54317
54583
|
break;
|
|
54318
54584
|
}
|
|
54319
|
-
case "session:watch":
|
|
54320
|
-
if (driverClientId) {
|
|
54321
|
-
void watchSession(msg.sessionId, driverClientId, msg.projectDir, {
|
|
54322
|
-
getMessages: (sid) => b.getMessages(sid),
|
|
54323
|
-
isSessionBusy: (sid) => b.isSessionBusy?.(sid) ?? false,
|
|
54324
|
-
resolveFile: (sid, dir) => b.resolveSessionFile?.(sid, dir) ?? Promise.resolve(null),
|
|
54325
|
-
getSessionArtifacts: (sid) => b.getSessionArtifacts?.(sid) ?? Promise.resolve([]),
|
|
54326
|
-
emit: (key2, m) => send("agent", m, { clientId: key2 })
|
|
54327
|
-
});
|
|
54328
|
-
}
|
|
54329
|
-
break;
|
|
54330
|
-
case "session:unwatch":
|
|
54331
|
-
if (driverClientId)
|
|
54332
|
-
unwatchSession(msg.sessionId, driverClientId);
|
|
54333
|
-
break;
|
|
54334
54585
|
case "session:fork": {
|
|
54335
54586
|
const newId = await b.forkSession?.(msg.sessionId, { projectDir: msg.projectDir, upToMessageId: msg.upToMessageId, title: msg.title }).catch(() => null);
|
|
54336
54587
|
if (newId)
|
|
@@ -54378,6 +54629,9 @@ async function handleInboundAgent(msg, driverClientId) {
|
|
|
54378
54629
|
case "chat:elicitation":
|
|
54379
54630
|
b.respondToElicitation?.(msg.requestId, { action: msg.action, content: msg.content }, origin);
|
|
54380
54631
|
break;
|
|
54632
|
+
case "chat:ui_response":
|
|
54633
|
+
b.respondToUiRequest?.(msg.requestId, msg.response, origin);
|
|
54634
|
+
break;
|
|
54381
54635
|
case "agent:mcp_reconnect":
|
|
54382
54636
|
void b.reconnectMcpServer?.(msg.sessionId, msg.name);
|
|
54383
54637
|
break;
|
|
@@ -54466,7 +54720,6 @@ function releaseDriver(clientId) {
|
|
|
54466
54720
|
sealChains.delete(clientId);
|
|
54467
54721
|
voiceControls.get(clientId)?.stop("disconnected");
|
|
54468
54722
|
voiceControls.delete(clientId);
|
|
54469
|
-
unwatchSessionClient(clientId);
|
|
54470
54723
|
releaseClientPtys(clientId);
|
|
54471
54724
|
releaseDcClient(clientId);
|
|
54472
54725
|
for (const [sid, cid] of [...driverBySession]) {
|
|
@@ -54506,7 +54759,6 @@ function teardownHostRole() {
|
|
|
54506
54759
|
driverBySession.clear();
|
|
54507
54760
|
driverByRequest.clear();
|
|
54508
54761
|
unwatchAllRemote();
|
|
54509
|
-
unwatchAllSessions();
|
|
54510
54762
|
releaseRemotePtys();
|
|
54511
54763
|
try {
|
|
54512
54764
|
hostBackend?.reclaim("critical");
|
|
@@ -54992,6 +55244,7 @@ async function startLocalServer(opts) {
|
|
|
54992
55244
|
exit: (ptyId, exitCode, signal) => send2("term", { type: "term:exit", ptyId, exitCode, signal })
|
|
54993
55245
|
};
|
|
54994
55246
|
const heldSessions = /* @__PURE__ */ new Set();
|
|
55247
|
+
let focusedTab;
|
|
54995
55248
|
let voiceControl = null;
|
|
54996
55249
|
const unregisterSink = registerAgentSink((msg, origin2) => {
|
|
54997
55250
|
const isCron = origin2 === "cron" && (MIRRORED_AGENT_EVENTS.has(msg.type) || msg.type === "session:created");
|
|
@@ -55023,13 +55276,16 @@ async function startLocalServer(opts) {
|
|
|
55023
55276
|
unregisterSink();
|
|
55024
55277
|
voiceControl?.stop("disconnected");
|
|
55025
55278
|
voiceControl = null;
|
|
55026
|
-
unwatchSessionClient(loopbackOrigin);
|
|
55027
55279
|
clearBrowserOrigin(loopbackOrigin);
|
|
55028
|
-
if (heldSessions.size) {
|
|
55280
|
+
if (heldSessions.size || focusedTab) {
|
|
55029
55281
|
const b = peekSharedBackend();
|
|
55030
55282
|
for (const sid of heldSessions)
|
|
55031
55283
|
b?.setSessionKeepAlive?.(sid, false, loopbackOrigin);
|
|
55032
55284
|
heldSessions.clear();
|
|
55285
|
+
if (focusedTab) {
|
|
55286
|
+
b?.setSessionKeepAlive?.(focusedTab, false, "tab");
|
|
55287
|
+
focusedTab = void 0;
|
|
55288
|
+
}
|
|
55033
55289
|
}
|
|
55034
55290
|
releaseClientPtys(termClientId);
|
|
55035
55291
|
});
|
|
@@ -55062,8 +55318,11 @@ async function startLocalServer(opts) {
|
|
|
55062
55318
|
...msg.permissionMode ? { permissionMode: msg.permissionMode } : {},
|
|
55063
55319
|
...msg.effort ? { effort: msg.effort } : {},
|
|
55064
55320
|
...msg.model ? { model: msg.model } : {},
|
|
55065
|
-
...msg.jsonSchema ? { jsonSchema: msg.jsonSchema } : {}
|
|
55321
|
+
...msg.jsonSchema ? { jsonSchema: msg.jsonSchema } : {},
|
|
55066
55322
|
// M7 structured output
|
|
55323
|
+
...msg.appendSystemPrompt ? { appendSystemPrompt: msg.appendSystemPrompt } : {},
|
|
55324
|
+
// mission hidden launch
|
|
55325
|
+
...msg.missionSkillId ? { missionSkillId: msg.missionSkillId } : {}
|
|
55067
55326
|
})).catch(() => {
|
|
55068
55327
|
});
|
|
55069
55328
|
break;
|
|
@@ -55088,18 +55347,6 @@ async function startLocalServer(opts) {
|
|
|
55088
55347
|
send2("agent", { type: "session:summary", clientRequestId: msg.clientRequestId, ...res });
|
|
55089
55348
|
break;
|
|
55090
55349
|
}
|
|
55091
|
-
case "session:watch":
|
|
55092
|
-
void watchSession(msg.sessionId, loopbackOrigin, msg.projectDir, {
|
|
55093
|
-
getMessages: (sid) => b.getMessages(sid),
|
|
55094
|
-
isSessionBusy: (sid) => b.isSessionBusy?.(sid) ?? false,
|
|
55095
|
-
resolveFile: (sid, dir) => b.resolveSessionFile?.(sid, dir) ?? Promise.resolve(null),
|
|
55096
|
-
getSessionArtifacts: (sid) => b.getSessionArtifacts?.(sid) ?? Promise.resolve([]),
|
|
55097
|
-
emit: (_key, m) => send2("agent", m)
|
|
55098
|
-
});
|
|
55099
|
-
break;
|
|
55100
|
-
case "session:unwatch":
|
|
55101
|
-
unwatchSession(msg.sessionId, loopbackOrigin);
|
|
55102
|
-
break;
|
|
55103
55350
|
case "session:fork": {
|
|
55104
55351
|
const newId = await b.forkSession?.(msg.sessionId, { projectDir: msg.projectDir, upToMessageId: msg.upToMessageId, title: msg.title }).catch(() => null);
|
|
55105
55352
|
if (newId)
|
|
@@ -55146,6 +55393,9 @@ async function startLocalServer(opts) {
|
|
|
55146
55393
|
case "chat:elicitation":
|
|
55147
55394
|
b.respondToElicitation?.(msg.requestId, { action: msg.action, content: msg.content }, loopbackOrigin);
|
|
55148
55395
|
break;
|
|
55396
|
+
case "chat:ui_response":
|
|
55397
|
+
b.respondToUiRequest?.(msg.requestId, msg.response, loopbackOrigin);
|
|
55398
|
+
break;
|
|
55149
55399
|
case "agent:mcp_reconnect":
|
|
55150
55400
|
void b.reconnectMcpServer?.(msg.sessionId, msg.name);
|
|
55151
55401
|
break;
|
|
@@ -55162,6 +55412,28 @@ async function startLocalServer(opts) {
|
|
|
55162
55412
|
heldSessions.delete(msg.sessionId);
|
|
55163
55413
|
b.setSessionKeepAlive?.(msg.sessionId, msg.keep, loopbackOrigin);
|
|
55164
55414
|
break;
|
|
55415
|
+
case "session:focus": {
|
|
55416
|
+
const warmParams = mergeDaemonAgentSettings({
|
|
55417
|
+
message: "",
|
|
55418
|
+
url: msg.url ?? "",
|
|
55419
|
+
sessionId: msg.sessionId,
|
|
55420
|
+
projectDir: msg.projectDir ?? opts.hostRoot,
|
|
55421
|
+
sources: msg.sources,
|
|
55422
|
+
agents: msg.agents,
|
|
55423
|
+
browserId: msg.browserId ?? loopbackOrigin,
|
|
55424
|
+
origin: loopbackOrigin,
|
|
55425
|
+
...msg.permissionMode ? { permissionMode: msg.permissionMode } : {},
|
|
55426
|
+
...msg.effort ? { effort: msg.effort } : {},
|
|
55427
|
+
...msg.model ? { model: msg.model } : {}
|
|
55428
|
+
});
|
|
55429
|
+
if (msg.focused === false) {
|
|
55430
|
+
void b.warmSession?.(warmParams);
|
|
55431
|
+
} else {
|
|
55432
|
+
focusedTab = msg.sessionId;
|
|
55433
|
+
void b.setActiveSession?.(msg.sessionId, warmParams);
|
|
55434
|
+
}
|
|
55435
|
+
break;
|
|
55436
|
+
}
|
|
55165
55437
|
case "config:set":
|
|
55166
55438
|
b.setConfig(msg.projectDir);
|
|
55167
55439
|
refreshDaemonScheduling(b);
|
|
@@ -55560,7 +55832,7 @@ import { platform as platform2 } from "node:os";
|
|
|
55560
55832
|
|
|
55561
55833
|
// ../../packages/host-core/dist/service/launchd.js
|
|
55562
55834
|
import { writeFileSync as writeFileSync14, existsSync as existsSync17, mkdirSync as mkdirSync14, unlinkSync as unlinkSync3 } from "node:fs";
|
|
55563
|
-
import { dirname as
|
|
55835
|
+
import { dirname as dirname14, join as join33 } from "node:path";
|
|
55564
55836
|
import { homedir as homedir14, userInfo } from "node:os";
|
|
55565
55837
|
|
|
55566
55838
|
// ../../packages/host-core/dist/service/exec.js
|
|
@@ -55627,8 +55899,8 @@ ${env}
|
|
|
55627
55899
|
}
|
|
55628
55900
|
return {
|
|
55629
55901
|
async install(spec) {
|
|
55630
|
-
mkdirSync14(
|
|
55631
|
-
mkdirSync14(
|
|
55902
|
+
mkdirSync14(dirname14(plistPath()), { recursive: true });
|
|
55903
|
+
mkdirSync14(dirname14(spec.logPath), { recursive: true });
|
|
55632
55904
|
writeFileSync14(plistPath(), buildPlist(spec));
|
|
55633
55905
|
await run("launchctl", ["bootout", serviceTarget()]).catch(() => {
|
|
55634
55906
|
});
|
|
@@ -55680,7 +55952,7 @@ ${env}
|
|
|
55680
55952
|
|
|
55681
55953
|
// ../../packages/host-core/dist/service/systemd.js
|
|
55682
55954
|
import { writeFileSync as writeFileSync15, existsSync as existsSync18, mkdirSync as mkdirSync15, unlinkSync as unlinkSync4 } from "node:fs";
|
|
55683
|
-
import { dirname as
|
|
55955
|
+
import { dirname as dirname15, join as join34 } from "node:path";
|
|
55684
55956
|
import { homedir as homedir15, userInfo as userInfo2 } from "node:os";
|
|
55685
55957
|
function shq(s) {
|
|
55686
55958
|
return `"${s.replace(/(["\\$`])/g, "\\$1")}"`;
|
|
@@ -55714,8 +55986,8 @@ WantedBy=default.target
|
|
|
55714
55986
|
}
|
|
55715
55987
|
return {
|
|
55716
55988
|
async install(spec) {
|
|
55717
|
-
mkdirSync15(
|
|
55718
|
-
mkdirSync15(
|
|
55989
|
+
mkdirSync15(dirname15(unitPath()), { recursive: true });
|
|
55990
|
+
mkdirSync15(dirname15(spec.logPath), { recursive: true });
|
|
55719
55991
|
writeFileSync15(unitPath(), buildUnit(spec));
|
|
55720
55992
|
await run("loginctl", ["enable-linger", userInfo2().username]).catch(() => {
|
|
55721
55993
|
});
|
|
@@ -55760,7 +56032,7 @@ WantedBy=default.target
|
|
|
55760
56032
|
|
|
55761
56033
|
// ../../packages/host-core/dist/service/windows.js
|
|
55762
56034
|
import { writeFileSync as writeFileSync16, existsSync as existsSync19, mkdirSync as mkdirSync16, unlinkSync as unlinkSync5 } from "node:fs";
|
|
55763
|
-
import { dirname as
|
|
56035
|
+
import { dirname as dirname16, join as join35 } from "node:path";
|
|
55764
56036
|
function makeWindowsTask(instanceId) {
|
|
55765
56037
|
const task = `Fineguide\\TesseractHost${instanceId ? `-${instanceId}` : ""}`;
|
|
55766
56038
|
const wrapperPath = () => join35(daemonDir(), `run-host${instanceId ? `-${instanceId}` : ""}.cmd`);
|
|
@@ -55778,8 +56050,8 @@ start "" /b ${cmd} >> "${oneLine2(spec.logPath).replace(/"/g, "")}" 2>&1\r
|
|
|
55778
56050
|
}
|
|
55779
56051
|
return {
|
|
55780
56052
|
async install(spec) {
|
|
55781
|
-
mkdirSync16(
|
|
55782
|
-
mkdirSync16(
|
|
56053
|
+
mkdirSync16(dirname16(spec.logPath), { recursive: true });
|
|
56054
|
+
mkdirSync16(dirname16(wrapperPath()), { recursive: true });
|
|
55783
56055
|
writeFileSync16(wrapperPath(), buildWrapper(spec));
|
|
55784
56056
|
await run("schtasks", ["/Create", "/TN", task, "/SC", "ONLOGON", "/RL", "LIMITED", "/F", "/TR", `"${wrapperPath()}"`]);
|
|
55785
56057
|
await run("schtasks", ["/Run", "/TN", task]).catch(() => {
|
|
@@ -56272,7 +56544,7 @@ async function logs(instanceId, follow) {
|
|
|
56272
56544
|
// src/cli.ts
|
|
56273
56545
|
function pkgVersion() {
|
|
56274
56546
|
try {
|
|
56275
|
-
const p = join38(
|
|
56547
|
+
const p = join38(dirname17(fileURLToPath2(import.meta.url)), "..", "package.json");
|
|
56276
56548
|
return JSON.parse(readFileSync15(p, "utf8")).version ?? "0.0.0";
|
|
56277
56549
|
} catch {
|
|
56278
56550
|
return "0.0.0";
|