newmark-agent 0.5.7 → 0.5.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4281,6 +4281,234 @@ var require_jpeg_js = __commonJS({
4281
4281
  }
4282
4282
  });
4283
4283
 
4284
+ // src/core/flow.ts
4285
+ var fs6, path7, FlowEngine;
4286
+ var init_flow = __esm({
4287
+ "src/core/flow.ts"() {
4288
+ "use strict";
4289
+ fs6 = __toESM(require("fs"));
4290
+ path7 = __toESM(require("path"));
4291
+ FlowEngine = class _FlowEngine {
4292
+ static load(dir, name50) {
4293
+ const p = path7.join(dir, `${name50}.Flow.json`);
4294
+ try {
4295
+ return JSON.parse(fs6.readFileSync(p, "utf-8").replace(/^\uFEFF/, ""));
4296
+ } catch {
4297
+ return null;
4298
+ }
4299
+ }
4300
+ static save(dir, workflow) {
4301
+ const p = path7.join(dir, `${workflow.name}.Flow.json`);
4302
+ fs6.writeFileSync(p, JSON.stringify(workflow, null, 2), "utf-8");
4303
+ }
4304
+ static delete(dir, name50) {
4305
+ const p = path7.join(dir, `${name50}.Flow.json`);
4306
+ if (fs6.existsSync(p)) fs6.unlinkSync(p);
4307
+ }
4308
+ static listAll(dir) {
4309
+ try {
4310
+ return fs6.readdirSync(dir).filter((f3) => f3.endsWith(".Flow.json")).map((f3) => f3.replace(".Flow.json", "")).sort();
4311
+ } catch {
4312
+ return [];
4313
+ }
4314
+ }
4315
+ static describeWorkflow(wf) {
4316
+ const comps = [...wf.components].sort((a3, b2) => a3.id - b2.id);
4317
+ if (comps.length === 0) return "";
4318
+ const parts = [];
4319
+ for (const c3 of comps) {
4320
+ if (c3.type === "dialog") {
4321
+ parts.push(c3.mode.charAt(0).toUpperCase() + c3.mode.slice(1));
4322
+ } else {
4323
+ const label = c3.prompt.replace(/\{#prompt#\}/g, "<i>").replace(/\n/g, " ").slice(0, 22);
4324
+ parts.push(`?${label}?`);
4325
+ }
4326
+ }
4327
+ return parts.join(" \u2192 ");
4328
+ }
4329
+ static validate(wf) {
4330
+ const errors = [];
4331
+ if (!wf.components || wf.components.length === 0) {
4332
+ errors.push({ message: "No components defined." });
4333
+ return errors;
4334
+ }
4335
+ const ids = /* @__PURE__ */ new Set();
4336
+ for (const c3 of wf.components) {
4337
+ if (typeof c3.id === "number") ids.add(c3.id);
4338
+ }
4339
+ const seenIds = /* @__PURE__ */ new Set();
4340
+ for (const c3 of wf.components) {
4341
+ if (seenIds.has(c3.id)) {
4342
+ errors.push({ componentId: c3.id, message: `Duplicate component ID: ${c3.id}` });
4343
+ }
4344
+ seenIds.add(c3.id);
4345
+ if (c3.type === "dialog") {
4346
+ const mode = c3.mode.toLowerCase();
4347
+ if (!["build", "plan", "goal"].includes(mode)) {
4348
+ errors.push({ componentId: c3.id, message: `Invalid dialog mode '${c3.mode}' (must be build/plan/goal)` });
4349
+ }
4350
+ } else if (c3.type === "logic") {
4351
+ if (!ids.has(c3.goto_true)) {
4352
+ errors.push({ componentId: c3.id, message: `goto_true=${c3.goto_true} not found` });
4353
+ }
4354
+ if (!ids.has(c3.goto_false)) {
4355
+ errors.push({ componentId: c3.id, message: `goto_false=${c3.goto_false} not found` });
4356
+ }
4357
+ } else {
4358
+ errors.push({ componentId: c3.id, message: `Unknown component type '${c3.type}'` });
4359
+ }
4360
+ }
4361
+ return errors;
4362
+ }
4363
+ static detectCycles(wf) {
4364
+ const comps = [...wf.components].sort((a3, b2) => a3.id - b2.id);
4365
+ if (comps.length === 0) return [];
4366
+ const idToIdx = /* @__PURE__ */ new Map();
4367
+ comps.forEach((c3, i4) => idToIdx.set(c3.id, i4));
4368
+ const graph = /* @__PURE__ */ new Map();
4369
+ for (let index = 0; index < comps.length; index++) {
4370
+ const c3 = comps[index];
4371
+ graph.set(c3.id, []);
4372
+ if (c3.type === "dialog") {
4373
+ const next = comps[index + 1];
4374
+ if (next) graph.get(c3.id).push(next.id);
4375
+ } else if (c3.type === "logic") {
4376
+ if (idToIdx.has(c3.goto_true)) graph.get(c3.id).push(c3.goto_true);
4377
+ if (idToIdx.has(c3.goto_false)) graph.get(c3.id).push(c3.goto_false);
4378
+ }
4379
+ }
4380
+ const WHITE = 0, GRAY = 1, BLACK = 2;
4381
+ const color2 = /* @__PURE__ */ new Map();
4382
+ for (const c3 of comps) color2.set(c3.id, WHITE);
4383
+ const cycles = [];
4384
+ const dfsPath = [];
4385
+ function dfs(node) {
4386
+ color2.set(node, GRAY);
4387
+ dfsPath.push(node);
4388
+ for (const nb of graph.get(node) || []) {
4389
+ if (!color2.has(nb)) continue;
4390
+ if (color2.get(nb) === GRAY) {
4391
+ const start = dfsPath.indexOf(nb);
4392
+ cycles.push(dfsPath.slice(start));
4393
+ } else if (color2.get(nb) === WHITE) {
4394
+ dfs(nb);
4395
+ }
4396
+ }
4397
+ dfsPath.pop();
4398
+ color2.set(node, BLACK);
4399
+ }
4400
+ for (const c3 of comps) {
4401
+ if (color2.get(c3.id) === WHITE) dfs(c3.id);
4402
+ }
4403
+ const unique2 = [];
4404
+ const seen = /* @__PURE__ */ new Set();
4405
+ for (const cyc of cycles) {
4406
+ const key3 = [...cyc].sort((a3, b2) => a3 - b2).join(",");
4407
+ if (!seen.has(key3)) {
4408
+ seen.add(key3);
4409
+ unique2.push(cyc);
4410
+ }
4411
+ }
4412
+ return unique2;
4413
+ }
4414
+ static getCycleWarnings(wf) {
4415
+ const cycles = _FlowEngine.detectCycles(wf);
4416
+ return cycles.map(
4417
+ (cyc) => `[!] Potential logic cycle in '${wf.name}': components [${cyc.join(", ")}] can form a loop.`
4418
+ );
4419
+ }
4420
+ static findWorkflow(name50, dir) {
4421
+ const names = _FlowEngine.listAll(dir);
4422
+ if (names.length === 0) return null;
4423
+ if (names.includes(name50)) return name50;
4424
+ const nameLower = name50.toLowerCase();
4425
+ for (const n3 of names) {
4426
+ if (n3.toLowerCase() === nameLower) return n3;
4427
+ }
4428
+ for (const n3 of names) {
4429
+ if (n3.toLowerCase().includes(nameLower)) return n3;
4430
+ }
4431
+ return null;
4432
+ }
4433
+ static autoTrigger(text, dir) {
4434
+ const names = _FlowEngine.listAll(dir);
4435
+ const textLower = text.toLowerCase();
4436
+ const results = [];
4437
+ for (const n3 of names) {
4438
+ const nLower = n3.toLowerCase();
4439
+ if (nLower === textLower) {
4440
+ results.push({ name: n3, score: 1 });
4441
+ } else if (textLower.includes(nLower) || nLower.includes(textLower)) {
4442
+ const longer = nLower.length > textLower.length ? nLower : textLower;
4443
+ const shorter = nLower.length > textLower.length ? textLower : nLower;
4444
+ const ratio = shorter.length / Math.max(longer.length, 1);
4445
+ results.push({ name: n3, score: 0.5 + 0.4 * Math.min(ratio, 1) });
4446
+ } else {
4447
+ const words = nLower.split(/[\s_-]+/).filter((w) => w.length > 0);
4448
+ const matchCount = words.filter((w) => textLower.includes(w)).length;
4449
+ if (matchCount > 0) {
4450
+ results.push({ name: n3, score: 0.2 + 0.6 * (matchCount / Math.max(words.length, 1)) });
4451
+ }
4452
+ }
4453
+ }
4454
+ results.sort((a3, b2) => b2.score - a3.score);
4455
+ return results;
4456
+ }
4457
+ static buildDialogPrompt(component, userInput) {
4458
+ const raw = component.prompt;
4459
+ const ui = userInput || "";
4460
+ const hasPlaceholder = raw.includes("{#prompt#}");
4461
+ if (component.mode === "plan" && ui) {
4462
+ if (hasPlaceholder) {
4463
+ return raw.replace(/\{#prompt#\}/g, ui);
4464
+ } else {
4465
+ return `Plan: ${raw}
4466
+ User context: ${ui}`;
4467
+ }
4468
+ }
4469
+ return hasPlaceholder ? raw.replace(/\{#prompt#\}/g, ui) : raw;
4470
+ }
4471
+ static generateSequence(workflow, start, input) {
4472
+ const orderedComponents = [...workflow.components];
4473
+ const seq = [];
4474
+ let cur = start;
4475
+ let count = 0;
4476
+ const max2 = workflow.components.length + 10;
4477
+ while (count < max2) {
4478
+ count++;
4479
+ const comp = workflow.components.find((c3) => c3.id === cur);
4480
+ if (!comp) break;
4481
+ if (comp.type === "dialog") {
4482
+ const expanded = _FlowEngine.buildDialogPrompt(comp, input);
4483
+ seq.push({ id: comp.id, mode: comp.mode, prompt: expanded, isLogic: false });
4484
+ const index = orderedComponents.findIndex((item) => item.id === comp.id);
4485
+ if (index < 0 || index + 1 >= orderedComponents.length) break;
4486
+ cur = orderedComponents[index + 1].id;
4487
+ } else {
4488
+ seq.push({
4489
+ id: comp.id,
4490
+ prompt: comp.prompt.replace(/\{#prompt#\}/g, input),
4491
+ isLogic: true,
4492
+ gotoTrue: comp.goto_true,
4493
+ gotoFalse: comp.goto_false
4494
+ });
4495
+ break;
4496
+ }
4497
+ }
4498
+ return seq;
4499
+ }
4500
+ static resolveGoto(workflow, cur, cond) {
4501
+ const comp = workflow.components.find((c3) => c3.id === cur);
4502
+ if (comp?.type === "logic") {
4503
+ return cond ? comp.goto_true : comp.goto_false;
4504
+ }
4505
+ const index = workflow.components.findIndex((component) => component.id === cur);
4506
+ return index >= 0 && index + 1 < workflow.components.length ? workflow.components[index + 1].id : -1;
4507
+ }
4508
+ };
4509
+ }
4510
+ });
4511
+
4284
4512
  // node_modules/regenerator-runtime/runtime.js
4285
4513
  var require_runtime = __commonJS({
4286
4514
  "node_modules/regenerator-runtime/runtime.js"(exports2, module2) {
@@ -326027,234 +326255,6 @@ var require_readability = __commonJS({
326027
326255
  }
326028
326256
  });
326029
326257
 
326030
- // src/core/flow.ts
326031
- var fs16, path18, FlowEngine;
326032
- var init_flow = __esm({
326033
- "src/core/flow.ts"() {
326034
- "use strict";
326035
- fs16 = __toESM(require("fs"));
326036
- path18 = __toESM(require("path"));
326037
- FlowEngine = class _FlowEngine {
326038
- static load(dir, name50) {
326039
- const p = path18.join(dir, `${name50}.Flow.json`);
326040
- try {
326041
- return JSON.parse(fs16.readFileSync(p, "utf-8").replace(/^\uFEFF/, ""));
326042
- } catch {
326043
- return null;
326044
- }
326045
- }
326046
- static save(dir, workflow) {
326047
- const p = path18.join(dir, `${workflow.name}.Flow.json`);
326048
- fs16.writeFileSync(p, JSON.stringify(workflow, null, 2), "utf-8");
326049
- }
326050
- static delete(dir, name50) {
326051
- const p = path18.join(dir, `${name50}.Flow.json`);
326052
- if (fs16.existsSync(p)) fs16.unlinkSync(p);
326053
- }
326054
- static listAll(dir) {
326055
- try {
326056
- return fs16.readdirSync(dir).filter((f3) => f3.endsWith(".Flow.json")).map((f3) => f3.replace(".Flow.json", "")).sort();
326057
- } catch {
326058
- return [];
326059
- }
326060
- }
326061
- static describeWorkflow(wf) {
326062
- const comps = [...wf.components].sort((a3, b2) => a3.id - b2.id);
326063
- if (comps.length === 0) return "";
326064
- const parts = [];
326065
- for (const c3 of comps) {
326066
- if (c3.type === "dialog") {
326067
- parts.push(c3.mode.charAt(0).toUpperCase() + c3.mode.slice(1));
326068
- } else {
326069
- const label = c3.prompt.replace(/\{#prompt#\}/g, "<i>").replace(/\n/g, " ").slice(0, 22);
326070
- parts.push(`?${label}?`);
326071
- }
326072
- }
326073
- return parts.join(" \u2192 ");
326074
- }
326075
- static validate(wf) {
326076
- const errors = [];
326077
- if (!wf.components || wf.components.length === 0) {
326078
- errors.push({ message: "No components defined." });
326079
- return errors;
326080
- }
326081
- const ids = /* @__PURE__ */ new Set();
326082
- for (const c3 of wf.components) {
326083
- if (typeof c3.id === "number") ids.add(c3.id);
326084
- }
326085
- const seenIds = /* @__PURE__ */ new Set();
326086
- for (const c3 of wf.components) {
326087
- if (seenIds.has(c3.id)) {
326088
- errors.push({ componentId: c3.id, message: `Duplicate component ID: ${c3.id}` });
326089
- }
326090
- seenIds.add(c3.id);
326091
- if (c3.type === "dialog") {
326092
- const mode = c3.mode.toLowerCase();
326093
- if (!["build", "plan", "goal"].includes(mode)) {
326094
- errors.push({ componentId: c3.id, message: `Invalid dialog mode '${c3.mode}' (must be build/plan/goal)` });
326095
- }
326096
- } else if (c3.type === "logic") {
326097
- if (!ids.has(c3.goto_true)) {
326098
- errors.push({ componentId: c3.id, message: `goto_true=${c3.goto_true} not found` });
326099
- }
326100
- if (!ids.has(c3.goto_false)) {
326101
- errors.push({ componentId: c3.id, message: `goto_false=${c3.goto_false} not found` });
326102
- }
326103
- } else {
326104
- errors.push({ componentId: c3.id, message: `Unknown component type '${c3.type}'` });
326105
- }
326106
- }
326107
- return errors;
326108
- }
326109
- static detectCycles(wf) {
326110
- const comps = [...wf.components].sort((a3, b2) => a3.id - b2.id);
326111
- if (comps.length === 0) return [];
326112
- const idToIdx = /* @__PURE__ */ new Map();
326113
- comps.forEach((c3, i4) => idToIdx.set(c3.id, i4));
326114
- const graph = /* @__PURE__ */ new Map();
326115
- for (let index = 0; index < comps.length; index++) {
326116
- const c3 = comps[index];
326117
- graph.set(c3.id, []);
326118
- if (c3.type === "dialog") {
326119
- const next = comps[index + 1];
326120
- if (next) graph.get(c3.id).push(next.id);
326121
- } else if (c3.type === "logic") {
326122
- if (idToIdx.has(c3.goto_true)) graph.get(c3.id).push(c3.goto_true);
326123
- if (idToIdx.has(c3.goto_false)) graph.get(c3.id).push(c3.goto_false);
326124
- }
326125
- }
326126
- const WHITE = 0, GRAY = 1, BLACK = 2;
326127
- const color2 = /* @__PURE__ */ new Map();
326128
- for (const c3 of comps) color2.set(c3.id, WHITE);
326129
- const cycles = [];
326130
- const dfsPath = [];
326131
- function dfs(node) {
326132
- color2.set(node, GRAY);
326133
- dfsPath.push(node);
326134
- for (const nb of graph.get(node) || []) {
326135
- if (!color2.has(nb)) continue;
326136
- if (color2.get(nb) === GRAY) {
326137
- const start = dfsPath.indexOf(nb);
326138
- cycles.push(dfsPath.slice(start));
326139
- } else if (color2.get(nb) === WHITE) {
326140
- dfs(nb);
326141
- }
326142
- }
326143
- dfsPath.pop();
326144
- color2.set(node, BLACK);
326145
- }
326146
- for (const c3 of comps) {
326147
- if (color2.get(c3.id) === WHITE) dfs(c3.id);
326148
- }
326149
- const unique2 = [];
326150
- const seen = /* @__PURE__ */ new Set();
326151
- for (const cyc of cycles) {
326152
- const key3 = [...cyc].sort((a3, b2) => a3 - b2).join(",");
326153
- if (!seen.has(key3)) {
326154
- seen.add(key3);
326155
- unique2.push(cyc);
326156
- }
326157
- }
326158
- return unique2;
326159
- }
326160
- static getCycleWarnings(wf) {
326161
- const cycles = _FlowEngine.detectCycles(wf);
326162
- return cycles.map(
326163
- (cyc) => `[!] Potential logic cycle in '${wf.name}': components [${cyc.join(", ")}] can form a loop.`
326164
- );
326165
- }
326166
- static findWorkflow(name50, dir) {
326167
- const names = _FlowEngine.listAll(dir);
326168
- if (names.length === 0) return null;
326169
- if (names.includes(name50)) return name50;
326170
- const nameLower = name50.toLowerCase();
326171
- for (const n3 of names) {
326172
- if (n3.toLowerCase() === nameLower) return n3;
326173
- }
326174
- for (const n3 of names) {
326175
- if (n3.toLowerCase().includes(nameLower)) return n3;
326176
- }
326177
- return null;
326178
- }
326179
- static autoTrigger(text, dir) {
326180
- const names = _FlowEngine.listAll(dir);
326181
- const textLower = text.toLowerCase();
326182
- const results = [];
326183
- for (const n3 of names) {
326184
- const nLower = n3.toLowerCase();
326185
- if (nLower === textLower) {
326186
- results.push({ name: n3, score: 1 });
326187
- } else if (textLower.includes(nLower) || nLower.includes(textLower)) {
326188
- const longer = nLower.length > textLower.length ? nLower : textLower;
326189
- const shorter = nLower.length > textLower.length ? textLower : nLower;
326190
- const ratio = shorter.length / Math.max(longer.length, 1);
326191
- results.push({ name: n3, score: 0.5 + 0.4 * Math.min(ratio, 1) });
326192
- } else {
326193
- const words = nLower.split(/[\s_-]+/).filter((w) => w.length > 0);
326194
- const matchCount = words.filter((w) => textLower.includes(w)).length;
326195
- if (matchCount > 0) {
326196
- results.push({ name: n3, score: 0.2 + 0.6 * (matchCount / Math.max(words.length, 1)) });
326197
- }
326198
- }
326199
- }
326200
- results.sort((a3, b2) => b2.score - a3.score);
326201
- return results;
326202
- }
326203
- static buildDialogPrompt(component, userInput) {
326204
- const raw = component.prompt;
326205
- const ui = userInput || "";
326206
- const hasPlaceholder = raw.includes("{#prompt#}");
326207
- if (component.mode === "plan" && ui) {
326208
- if (hasPlaceholder) {
326209
- return raw.replace(/\{#prompt#\}/g, ui);
326210
- } else {
326211
- return `Plan: ${raw}
326212
- User context: ${ui}`;
326213
- }
326214
- }
326215
- return hasPlaceholder ? raw.replace(/\{#prompt#\}/g, ui) : raw;
326216
- }
326217
- static generateSequence(workflow, start, input) {
326218
- const orderedComponents = [...workflow.components];
326219
- const seq = [];
326220
- let cur = start;
326221
- let count = 0;
326222
- const max2 = workflow.components.length + 10;
326223
- while (count < max2) {
326224
- count++;
326225
- const comp = workflow.components.find((c3) => c3.id === cur);
326226
- if (!comp) break;
326227
- if (comp.type === "dialog") {
326228
- const expanded = _FlowEngine.buildDialogPrompt(comp, input);
326229
- seq.push({ id: comp.id, mode: comp.mode, prompt: expanded, isLogic: false });
326230
- const index = orderedComponents.findIndex((item) => item.id === comp.id);
326231
- if (index < 0 || index + 1 >= orderedComponents.length) break;
326232
- cur = orderedComponents[index + 1].id;
326233
- } else {
326234
- seq.push({
326235
- id: comp.id,
326236
- prompt: comp.prompt.replace(/\{#prompt#\}/g, input),
326237
- isLogic: true,
326238
- gotoTrue: comp.goto_true,
326239
- gotoFalse: comp.goto_false
326240
- });
326241
- break;
326242
- }
326243
- }
326244
- return seq;
326245
- }
326246
- static resolveGoto(workflow, cur, cond) {
326247
- const comp = workflow.components.find((c3) => c3.id === cur);
326248
- if (comp?.type === "logic") {
326249
- return cond ? comp.goto_true : comp.goto_false;
326250
- }
326251
- const index = workflow.components.findIndex((component) => component.id === cur);
326252
- return index >= 0 && index + 1 < workflow.components.length ? workflow.components[index + 1].id : -1;
326253
- }
326254
- };
326255
- }
326256
- });
326257
-
326258
326258
  // src/core/agentKernel/agent-loop.ts
326259
326259
  async function runAgentLoop(prompts, config, signal) {
326260
326260
  throwIfAborted3(signal);
@@ -328709,6 +328709,30 @@ function parseProviderSse(raw) {
328709
328709
  }
328710
328710
  return events;
328711
328711
  }
328712
+ function assembleCompatibleToolArguments(parts) {
328713
+ const nonEmpty = (parts || []).map(String).filter((part) => part && part !== "null");
328714
+ if (!nonEmpty.length) return "{}";
328715
+ const isJsonObject = (value) => {
328716
+ try {
328717
+ const parsed = JSON.parse(value);
328718
+ return !!parsed && typeof parsed === "object" && !Array.isArray(parsed);
328719
+ } catch {
328720
+ return false;
328721
+ }
328722
+ };
328723
+ const incremental = nonEmpty.join("");
328724
+ if (isJsonObject(incremental)) return incremental;
328725
+ let compatible = "";
328726
+ for (const incoming of nonEmpty) {
328727
+ if (!compatible) compatible = incoming;
328728
+ else if (incoming === compatible) continue;
328729
+ else if (incoming.startsWith(compatible)) compatible = incoming;
328730
+ else if (compatible.startsWith(incoming)) continue;
328731
+ else compatible += incoming;
328732
+ }
328733
+ if (isJsonObject(compatible)) return compatible;
328734
+ return [...nonEmpty].reverse().find(isJsonObject) || compatible;
328735
+ }
328712
328736
  function isContentPolicyBlocked(json) {
328713
328737
  const choices = Array.isArray(json.choices) ? json.choices : [];
328714
328738
  const choice = choices[0] || {};
@@ -328937,6 +328961,8 @@ var ChatCompletionsAdapter = class {
328937
328961
  let contentPolicyBlocked = false;
328938
328962
  let emittedContent = false;
328939
328963
  let emittedTool = false;
328964
+ let emittedReasoning = false;
328965
+ let explicitCompletion = false;
328940
328966
  try {
328941
328967
  while (true) {
328942
328968
  const { done, value } = await readProviderStreamChunk(reader, signal);
@@ -328948,7 +328974,10 @@ var ChatCompletionsAdapter = class {
328948
328974
  const trimmed = line.trim();
328949
328975
  if (!trimmed.startsWith("data: ")) continue;
328950
328976
  const data = trimmed.slice(6);
328951
- if (data === "[DONE]") continue;
328977
+ if (data === "[DONE]") {
328978
+ explicitCompletion = true;
328979
+ continue;
328980
+ }
328952
328981
  let json;
328953
328982
  try {
328954
328983
  json = JSON.parse(data);
@@ -328961,11 +328990,16 @@ var ChatCompletionsAdapter = class {
328961
328990
  }
328962
328991
  if (isContentPolicyBlocked(json)) contentPolicyBlocked = true;
328963
328992
  const choices = Array.isArray(json.choices) ? json.choices : [];
328964
- const delta = choices[0]?.delta;
328993
+ const choice = choices[0];
328994
+ if (choice?.finish_reason !== void 0 && choice.finish_reason !== null) explicitCompletion = true;
328995
+ const delta = choice?.delta;
328965
328996
  if (!delta) continue;
328966
328997
  if (delta.reasoning_content) {
328967
328998
  const reasoning = this.extractText(delta.reasoning_content);
328968
- if (reasoning) yield { type: "reasoning.summary.delta", delta: reasoning };
328999
+ if (reasoning) {
329000
+ emittedReasoning = true;
329001
+ yield { type: "reasoning.summary.delta", delta: reasoning };
329002
+ }
328969
329003
  }
328970
329004
  const textDelta = this.extractText(delta.content);
328971
329005
  if (textDelta) {
@@ -329010,13 +329044,17 @@ var ChatCompletionsAdapter = class {
329010
329044
  type: "tool_call.completed",
329011
329045
  id: currentToolCall.id,
329012
329046
  name: currentToolCall.name,
329013
- arguments: currentToolCall.argumentParts.join("")
329047
+ arguments: assembleCompatibleToolArguments(currentToolCall.argumentParts)
329014
329048
  };
329015
329049
  }
329016
329050
  } else if (!emittedContent && !emittedTool && contentPolicyBlocked) {
329017
329051
  yield { type: "response.failed", error: "[Error] Content policy refusal (content_filter)." };
329018
329052
  return;
329019
329053
  }
329054
+ if (!explicitCompletion && !emittedContent && !emittedTool && !emittedReasoning) {
329055
+ yield { type: "response.failed", error: "[LLM Error] Chat stream ended before an explicit completion." };
329056
+ return;
329057
+ }
329020
329058
  yield { type: "response.completed" };
329021
329059
  } finally {
329022
329060
  reader.releaseLock();
@@ -329213,6 +329251,7 @@ var ResponsesAdapter = class {
329213
329251
  const key3 = `${String(payload.item_id || "")}:${String(payload.summary_index || 0)}`;
329214
329252
  const delta = this.extractText(payload.delta);
329215
329253
  if (delta) {
329254
+ emittedContent = true;
329216
329255
  reasoningSummaries.set(key3, (reasoningSummaries.get(key3) || "") + delta);
329217
329256
  yield { type: "reasoning.summary.delta", delta };
329218
329257
  }
@@ -329243,7 +329282,7 @@ var ResponsesAdapter = class {
329243
329282
  calls.set(key3, {
329244
329283
  id: String(item.call_id || item.id || key3),
329245
329284
  name: String(item.name || ""),
329246
- arguments: String(item.arguments || ""),
329285
+ argumentParts: item.arguments ? [String(item.arguments)] : [],
329247
329286
  emitted: false
329248
329287
  });
329249
329288
  }
@@ -329251,9 +329290,9 @@ var ResponsesAdapter = class {
329251
329290
  }
329252
329291
  if (eventType === "response.function_call_arguments.delta") {
329253
329292
  const key3 = String(payload.item_id || payload.call_id || payload.output_index || "");
329254
- const call = calls.get(key3) || { id: String(payload.call_id || key3), name: String(payload.name || ""), arguments: "", emitted: false };
329293
+ const call = calls.get(key3) || { id: String(payload.call_id || key3), name: String(payload.name || ""), argumentParts: [], emitted: false };
329255
329294
  const delta = String(payload.delta || "");
329256
- call.arguments += delta;
329295
+ if (delta) call.argumentParts.push(delta);
329257
329296
  calls.set(key3, call);
329258
329297
  yield { type: "tool_call.arguments.delta", id: call.id, delta };
329259
329298
  continue;
@@ -329265,19 +329304,20 @@ var ResponsesAdapter = class {
329265
329304
  const call = calls.get(key3) || {
329266
329305
  id: String(item.call_id || item.id || key3),
329267
329306
  name: String(item.name || ""),
329268
- arguments: String(item.arguments || ""),
329307
+ argumentParts: item.arguments ? [String(item.arguments)] : [],
329269
329308
  emitted: false
329270
329309
  };
329271
329310
  call.id = String(item.call_id || call.id);
329272
329311
  call.name = String(item.name || call.name);
329273
- call.arguments = typeof item.arguments === "string" ? item.arguments : call.arguments;
329312
+ if (typeof item.arguments === "string" && item.arguments) call.argumentParts.push(item.arguments);
329274
329313
  if (!call.emitted) {
329275
329314
  call.emitted = true;
329315
+ const argumentsJson = assembleCompatibleToolArguments(call.argumentParts);
329276
329316
  yield { type: "tool_call.started", id: call.id, name: call.name };
329277
- if (call.arguments && call.arguments !== "{}") {
329278
- yield { type: "tool_call.arguments.delta", id: call.id, delta: call.arguments };
329317
+ if (argumentsJson !== "{}") {
329318
+ yield { type: "tool_call.arguments.delta", id: call.id, delta: argumentsJson };
329279
329319
  }
329280
- yield { type: "tool_call.completed", id: call.id, name: call.name, arguments: call.arguments };
329320
+ yield { type: "tool_call.completed", id: call.id, name: call.name, arguments: argumentsJson };
329281
329321
  }
329282
329322
  calls.set(key3, call);
329283
329323
  }
@@ -329302,8 +329342,14 @@ var ResponsesAdapter = class {
329302
329342
  } else if (!completed) {
329303
329343
  yield { type: "response.failed", error: "[LLM Error] Responses stream ended before response.completed." };
329304
329344
  } else if (!emittedContent && calls.size === 0) {
329305
- yield { type: "response.failed", error: "[Error] Empty Responses stream." };
329345
+ yield { type: "response.failed", error: "[Error] Provider returned an empty response." };
329306
329346
  } else {
329347
+ for (const call of calls.values()) {
329348
+ if (call.emitted) continue;
329349
+ const argumentsJson = assembleCompatibleToolArguments(call.argumentParts);
329350
+ yield { type: "tool_call.started", id: call.id, name: call.name };
329351
+ yield { type: "tool_call.completed", id: call.id, name: call.name, arguments: argumentsJson };
329352
+ }
329307
329353
  yield { type: "response.completed" };
329308
329354
  }
329309
329355
  } finally {
@@ -330182,7 +330228,7 @@ ${responsePath}
330182
330228
  * `provider_adapters_v2` context flag. Request serialization and SSE
330183
330229
  * normalization are delegated to the shared provider adapters while the
330184
330230
  * transport orchestration (loopback node-http, fetch -> node-http fallback,
330185
- * 120s/30s timeouts) and the 4xx Chat -> Responses downgrade stay here.
330231
+ * cancellation-only streaming and the 4xx Chat -> Responses downgrade) stay here.
330186
330232
  * The emitted request body and StreamToken stream are byte-equivalent to
330187
330233
  * the legacy inlined path.
330188
330234
  */
@@ -330319,9 +330365,9 @@ ${responsePath}
330319
330365
  }
330320
330366
  /**
330321
330367
  * Loopback-aware transport injected into adapter `execute`. Streaming
330322
- * requests retain the fetch-to-node fallback for transport failures, while
330323
- * a local deadline is returned directly so one request cannot become a
330324
- * second Windows fallback request.
330368
+ * requests retain the fetch-to-node fallback for transport failures. They
330369
+ * have no response deadline; only caller cancellation or a concrete
330370
+ * transport/provider failure may end the request.
330325
330371
  */
330326
330372
  buildProviderAdapterTransport() {
330327
330373
  return async (request, signal) => {
@@ -330331,7 +330377,7 @@ ${responsePath}
330331
330377
  const forwardAbort = () => abort.abort(signal?.reason);
330332
330378
  if (signal?.aborted) forwardAbort();
330333
330379
  else signal?.addEventListener("abort", forwardAbort, { once: true });
330334
- const effectiveTimeout = this.effectiveRequestTimeout(12e4);
330380
+ const effectiveTimeout = 0;
330335
330381
  const timer = effectiveTimeout > 0 ? setTimeout(() => abort.abort(providerTimeoutError(effectiveTimeout)), effectiveTimeout) : void 0;
330336
330382
  try {
330337
330383
  try {
@@ -330476,7 +330522,7 @@ ${responsePath}
330476
330522
  const forwardAbort = () => abort.abort(signal?.reason);
330477
330523
  if (signal?.aborted) forwardAbort();
330478
330524
  else signal?.addEventListener("abort", forwardAbort, { once: true });
330479
- const effectiveTimeout = this.effectiveRequestTimeout(12e4);
330525
+ const effectiveTimeout = 0;
330480
330526
  const timeout = effectiveTimeout > 0 ? setTimeout(() => abort.abort(providerTimeoutError(effectiveTimeout)), effectiveTimeout) : void 0;
330481
330527
  let reader = null;
330482
330528
  try {
@@ -330508,10 +330554,14 @@ ${responsePath}
330508
330554
  }
330509
330555
  const decoder = new TextDecoder();
330510
330556
  let buffer = "";
330511
- let currentToolCall = null;
330557
+ const toolCalls = /* @__PURE__ */ new Map();
330558
+ const toolCallOrder = [];
330559
+ let syntheticToolIndex = 0;
330560
+ let lastToolIndex = 0;
330512
330561
  let currentReasoningContent = "";
330513
330562
  let contentPolicyBlocked = false;
330514
330563
  let emittedContent = false;
330564
+ let explicitCompletion = false;
330515
330565
  const streamSignal = signal || new AbortController().signal;
330516
330566
  while (true) {
330517
330567
  const { done, value } = await readProviderStreamChunk(reader, streamSignal);
@@ -330523,7 +330573,10 @@ ${responsePath}
330523
330573
  const trimmed = line.trim();
330524
330574
  if (!trimmed.startsWith("data: ")) continue;
330525
330575
  const data = trimmed.slice(6);
330526
- if (data === "[DONE]") continue;
330576
+ if (data === "[DONE]") {
330577
+ explicitCompletion = true;
330578
+ continue;
330579
+ }
330527
330580
  try {
330528
330581
  const json = JSON.parse(data);
330529
330582
  if (json.usage) yield { type: "usage", text: "", usage: extractProviderUsage(json) };
@@ -330541,24 +330594,44 @@ ${responsePath}
330541
330594
  }
330542
330595
  if (delta.tool_calls) {
330543
330596
  for (const tc of delta.tool_calls) {
330544
- if (tc.id) {
330545
- if (currentToolCall) {
330546
- yield { type: "tool_call", text: "", toolCall: currentToolCall, reasoningContent: currentReasoningContent || void 0 };
330547
- }
330548
- currentToolCall = { id: tc.id, name: tc.function?.name || "", arguments: tc.function?.arguments || "" };
330549
- } else if (tc.function?.arguments && currentToolCall) {
330550
- currentToolCall.arguments += tc.function.arguments;
330597
+ const rawIndex = Number(tc.index);
330598
+ const index = Number.isInteger(rawIndex) && rawIndex >= 0 ? rawIndex : tc.id ? syntheticToolIndex++ : lastToolIndex;
330599
+ lastToolIndex = index;
330600
+ let call = toolCalls.get(index);
330601
+ if (!call && (tc.id || tc.function?.name)) {
330602
+ call = { id: tc.id || "", name: tc.function?.name || "", argumentParts: [] };
330603
+ toolCalls.set(index, call);
330604
+ toolCallOrder.push(index);
330551
330605
  }
330606
+ if (!call) continue;
330607
+ if (tc.id && !call.id) call.id = tc.id;
330608
+ if (tc.function?.name && !call.name) call.name = tc.function.name;
330609
+ if (tc.function?.arguments) call.argumentParts.push(tc.function.arguments);
330552
330610
  }
330553
330611
  }
330554
330612
  } catch {
330555
330613
  }
330556
330614
  }
330557
330615
  }
330558
- if (currentToolCall && currentToolCall.arguments) {
330559
- yield { type: "tool_call", text: "", toolCall: currentToolCall, reasoningContent: currentReasoningContent || void 0 };
330616
+ if (toolCallOrder.length) {
330617
+ for (const index of toolCallOrder) {
330618
+ const call = toolCalls.get(index);
330619
+ if (!call) continue;
330620
+ yield {
330621
+ type: "tool_call",
330622
+ text: "",
330623
+ toolCall: {
330624
+ id: call.id,
330625
+ name: call.name,
330626
+ arguments: assembleCompatibleToolArguments(call.argumentParts)
330627
+ },
330628
+ reasoningContent: currentReasoningContent || void 0
330629
+ };
330630
+ }
330560
330631
  } else if (!emittedContent && contentPolicyBlocked) {
330561
330632
  yield { type: "text", text: "[Error] Content policy refusal (content_filter)." };
330633
+ } else if (!explicitCompletion && !emittedContent && !currentReasoningContent) {
330634
+ yield { type: "text", text: "[LLM Error] GitHub Models stream ended before an explicit completion." };
330562
330635
  }
330563
330636
  } finally {
330564
330637
  reader?.releaseLock();
@@ -331245,8 +331318,8 @@ async function fuzzyDiscoverWithoutGuide(input, explicit, preferredModels = [])
331245
331318
  }
331246
331319
 
331247
331320
  // src/tools/index.ts
331248
- var fs14 = __toESM(require("fs"));
331249
- var path16 = __toESM(require("path"));
331321
+ var fs15 = __toESM(require("fs"));
331322
+ var path17 = __toESM(require("path"));
331250
331323
  var crypto8 = __toESM(require("crypto"));
331251
331324
  var import_url2 = require("url");
331252
331325
 
@@ -331590,7 +331663,8 @@ var MemoryLabManager = class {
331590
331663
  "Use memory_lab_read to inspect index.json before deciding what memory is relevant.",
331591
331664
  "Use memory_lab_query for bounded task-relevant retrieval; do not inject the complete index when a focused query is sufficient.",
331592
331665
  "Use memory_lab_read with component/name/slug to read a component core markdown file.",
331593
- "Use memory_lab_update only when the user asks to create or update durable memory, passing name, description, tags, optional tagPaths, content, and optional kind=file|folder.",
331666
+ "Use memory_lab_update only when the user asks to create or update durable memory. Create with name, tags, and content; patch an existing component with component plus only changed fields.",
331667
+ "For small body edits prefer contentAppend or oldText/newText over resending the complete content.",
331594
331668
  "For an existing component, pass expectedUpdatedAt from the latest read/query result. A stale update is rejected instead of overwriting newer memory.",
331595
331669
  "Use memory_lab_delete only when the user explicitly asks to forget/remove durable memory. Delete moves the prior revision to Memory Lab/archive and records a policy event.",
331596
331670
  "Every mutation should include a concise reason and source. ADD, UPDATE, and DELETE decisions are append-only in policy.jsonl and are recoverable from archive.",
@@ -331698,6 +331772,39 @@ var MemoryLabManager = class {
331698
331772
  source: String(input.source || "").trim()
331699
331773
  };
331700
331774
  }
331775
+ preparePatch(input) {
331776
+ const selector2 = String(input.component || "").trim();
331777
+ if (!selector2) throw new Error("Memory component is required for a patch.");
331778
+ const current = this.read(selector2);
331779
+ if (!current.ok || !current.component) throw new Error(current.error || `Memory component not found: ${selector2}`);
331780
+ const existing = current.component.meta;
331781
+ const oldContent = current.component.content;
331782
+ let content = input.content !== void 0 ? String(input.content) : oldContent;
331783
+ if (input.contentAppend !== void 0) content = `${oldContent}${String(input.contentAppend)}`;
331784
+ if (input.oldText !== void 0) {
331785
+ const oldText = String(input.oldText);
331786
+ if (!oldText) throw new Error("oldText must not be empty.");
331787
+ const matches = oldContent.split(oldText).length - 1;
331788
+ if (!matches) throw new Error("oldText was not found in the Memory Lab component.");
331789
+ if (matches > 1 && input.replaceAll !== true) throw new Error(`oldText matched ${matches} places; pass replaceAll=true or a unique fragment.`);
331790
+ content = input.replaceAll === true ? oldContent.split(oldText).join(String(input.newText || "")) : oldContent.replace(oldText, String(input.newText || ""));
331791
+ }
331792
+ const name50 = input.name === void 0 ? existing.name : String(input.name);
331793
+ if (this.slugify(name50) !== current.component.slug) {
331794
+ throw new Error("Renaming a Memory Lab component is not supported by incremental patch; create the new component then delete the old one.");
331795
+ }
331796
+ return this.prepareUpdate({
331797
+ name: name50,
331798
+ description: input.description === void 0 ? existing.description : String(input.description),
331799
+ tags: input.tags === void 0 ? existing.tags : input.tags,
331800
+ tagPaths: input.tagPaths === void 0 ? existing.tagPaths : input.tagPaths,
331801
+ content,
331802
+ kind: input.kind === void 0 ? existing.kind : input.kind,
331803
+ expectedUpdatedAt: String(input.expectedUpdatedAt || existing.updatedAt),
331804
+ reason: input.reason,
331805
+ source: input.source
331806
+ });
331807
+ }
331701
331808
  update(prepared) {
331702
331809
  this.ensure();
331703
331810
  const index = this.loadIndex();
@@ -332244,20 +332351,23 @@ ${JSON.stringify(payload, null, 2)}`;
332244
332351
  }
332245
332352
  };
332246
332353
 
332354
+ // src/tools/index.ts
332355
+ init_flow();
332356
+
332247
332357
  // src/core/compat.ts
332248
- var fs6 = __toESM(require("fs"));
332249
- var path7 = __toESM(require("path"));
332358
+ var fs7 = __toESM(require("fs"));
332359
+ var path8 = __toESM(require("path"));
332250
332360
  var os2 = __toESM(require("os"));
332251
332361
  function readJson(filePath) {
332252
332362
  try {
332253
- return JSON.parse(fs6.readFileSync(filePath, "utf-8").replace(/^\uFEFF/, ""));
332363
+ return JSON.parse(fs7.readFileSync(filePath, "utf-8").replace(/^\uFEFF/, ""));
332254
332364
  } catch {
332255
332365
  return null;
332256
332366
  }
332257
332367
  }
332258
332368
  function readJsonLoose(filePath) {
332259
332369
  try {
332260
- const withoutBom = fs6.readFileSync(filePath, "utf-8").replace(/^\uFEFF/, "");
332370
+ const withoutBom = fs7.readFileSync(filePath, "utf-8").replace(/^\uFEFF/, "");
332261
332371
  const withoutComments = withoutBom.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|\s)\/\/.*$/gm, "$1");
332262
332372
  return JSON.parse(withoutComments);
332263
332373
  } catch {
@@ -332338,7 +332448,7 @@ function normalizeToolResult(output, metadata) {
332338
332448
  return { ok: !error, output, error, metadata };
332339
332449
  }
332340
332450
  function componentPaths(root2, value) {
332341
- return asStringArray(value).map((item) => path7.resolve(root2, item));
332451
+ return asStringArray(value).map((item) => path8.resolve(root2, item));
332342
332452
  }
332343
332453
  function manifestComponentPaths(root2, manifest, ...keys) {
332344
332454
  for (const key3 of keys) {
@@ -332349,7 +332459,7 @@ function manifestComponentPaths(root2, manifest, ...keys) {
332349
332459
  function discoverComponentFiles(root2, relativeDirs, extension, maxDepth = 2) {
332350
332460
  const files = [];
332351
332461
  for (const dir of relativeDirs) {
332352
- files.push(...listFilesRecursive(path7.join(root2, dir), extension, maxDepth));
332462
+ files.push(...listFilesRecursive(path8.join(root2, dir), extension, maxDepth));
332353
332463
  }
332354
332464
  return Array.from(new Set(files)).sort();
332355
332465
  }
@@ -332374,12 +332484,12 @@ function collectMcpServers(...values) {
332374
332484
  function defaultComponentWarnings(kind, components) {
332375
332485
  const warnings = [];
332376
332486
  for (const item of components) {
332377
- if (path7.isAbsolute(item) && !fs6.existsSync(item)) warnings.push(`${kind} path does not exist: ${item}`);
332487
+ if (path8.isAbsolute(item) && !fs7.existsSync(item)) warnings.push(`${kind} path does not exist: ${item}`);
332378
332488
  }
332379
332489
  return warnings;
332380
332490
  }
332381
332491
  function normalizeCodexPlugin(root2, manifest) {
332382
- const name50 = asString(manifest.name) || path7.basename(root2);
332492
+ const name50 = asString(manifest.name) || path8.basename(root2);
332383
332493
  const components = {
332384
332494
  skills: manifestComponentPaths(root2, manifest, "skills"),
332385
332495
  agents: manifestComponentPaths(root2, manifest, "agents"),
@@ -332410,7 +332520,7 @@ function normalizeCodexPlugin(root2, manifest) {
332410
332520
  };
332411
332521
  }
332412
332522
  function normalizeClaudePlugin(root2, manifest) {
332413
- const name50 = asString(manifest.name) || path7.basename(root2);
332523
+ const name50 = asString(manifest.name) || path8.basename(root2);
332414
332524
  const experimental = nestedRecord(manifest.experimental);
332415
332525
  const components = {
332416
332526
  skills: manifestComponentPaths(root2, manifest, "skills"),
@@ -332448,7 +332558,7 @@ function normalizeClaudePlugin(root2, manifest) {
332448
332558
  };
332449
332559
  }
332450
332560
  function normalizeNewmarkPlugin(root2, manifest) {
332451
- const name50 = asString(manifest.name) || path7.basename(root2);
332561
+ const name50 = asString(manifest.name) || path8.basename(root2);
332452
332562
  return {
332453
332563
  id: `newmark:${name50}`,
332454
332564
  ecosystem: "newmark",
@@ -332477,7 +332587,7 @@ function findPluginRoots(root2, maxDepth = 5) {
332477
332587
  if (depth > maxDepth) return;
332478
332588
  let entries;
332479
332589
  try {
332480
- entries = fs6.readdirSync(dir, { withFileTypes: true });
332590
+ entries = fs7.readdirSync(dir, { withFileTypes: true });
332481
332591
  } catch {
332482
332592
  return;
332483
332593
  }
@@ -332486,7 +332596,7 @@ function findPluginRoots(root2, maxDepth = 5) {
332486
332596
  }
332487
332597
  for (const entry of entries) {
332488
332598
  if (!entry.isDirectory() || skip.has(entry.name) || entry.name.startsWith("release.locked-")) continue;
332489
- walk4(path7.join(dir, entry.name), depth + 1);
332599
+ walk4(path8.join(dir, entry.name), depth + 1);
332490
332600
  }
332491
332601
  };
332492
332602
  walk4(root2, 0);
@@ -332495,20 +332605,20 @@ function findPluginRoots(root2, maxDepth = 5) {
332495
332605
  function discoverPluginManifests(root2) {
332496
332606
  const manifests = [];
332497
332607
  for (const pluginRoot of findPluginRoots(root2)) {
332498
- const codexPath = path7.join(pluginRoot, ".codex-plugin", "plugin.json");
332499
- const claudePath = path7.join(pluginRoot, ".claude-plugin", "plugin.json");
332500
- const newmarkPath = path7.join(pluginRoot, ".newmark-plugin", "plugin.json");
332501
- const codex = fs6.existsSync(codexPath) ? readJson(codexPath) : null;
332502
- const claude = fs6.existsSync(claudePath) ? readJson(claudePath) : null;
332503
- const newmark = fs6.existsSync(newmarkPath) ? readJson(newmarkPath) : null;
332608
+ const codexPath = path8.join(pluginRoot, ".codex-plugin", "plugin.json");
332609
+ const claudePath = path8.join(pluginRoot, ".claude-plugin", "plugin.json");
332610
+ const newmarkPath = path8.join(pluginRoot, ".newmark-plugin", "plugin.json");
332611
+ const codex = fs7.existsSync(codexPath) ? readJson(codexPath) : null;
332612
+ const claude = fs7.existsSync(claudePath) ? readJson(claudePath) : null;
332613
+ const newmark = fs7.existsSync(newmarkPath) ? readJson(newmarkPath) : null;
332504
332614
  if (codex && typeof codex === "object") manifests.push(normalizeCodexPlugin(pluginRoot, codex));
332505
332615
  if (claude && typeof claude === "object") manifests.push(normalizeClaudePlugin(pluginRoot, claude));
332506
332616
  if (newmark && typeof newmark === "object") manifests.push(normalizeNewmarkPlugin(pluginRoot, newmark));
332507
332617
  }
332508
332618
  const projectOpencode = readOpenCodeManifest(root2, "project");
332509
332619
  if (projectOpencode) manifests.push(projectOpencode);
332510
- const userOpenCodeRoot = path7.join(os2.homedir(), ".config", "opencode");
332511
- if (path7.resolve(userOpenCodeRoot) !== path7.resolve(path7.join(root2, ".opencode"))) {
332620
+ const userOpenCodeRoot = path8.join(os2.homedir(), ".config", "opencode");
332621
+ if (path8.resolve(userOpenCodeRoot) !== path8.resolve(path8.join(root2, ".opencode"))) {
332512
332622
  const userOpenCode = readOpenCodeManifest(userOpenCodeRoot, "user");
332513
332623
  if (userOpenCode) manifests.push(userOpenCode);
332514
332624
  }
@@ -332516,20 +332626,20 @@ function discoverPluginManifests(root2) {
332516
332626
  }
332517
332627
  function readOpenCodeConfig(root2) {
332518
332628
  const candidates = [
332519
- path7.join(root2, "opencode.json"),
332520
- path7.join(root2, "opencode.jsonc"),
332521
- path7.join(root2, ".opencode", "opencode.json"),
332522
- path7.join(root2, ".opencode", "opencode.jsonc")
332629
+ path8.join(root2, "opencode.json"),
332630
+ path8.join(root2, "opencode.jsonc"),
332631
+ path8.join(root2, ".opencode", "opencode.json"),
332632
+ path8.join(root2, ".opencode", "opencode.jsonc")
332523
332633
  ];
332524
332634
  for (const filePath of candidates) {
332525
- if (fs6.existsSync(filePath)) return { path: filePath, value: readJsonLoose(filePath) };
332635
+ if (fs7.existsSync(filePath)) return { path: filePath, value: readJsonLoose(filePath) };
332526
332636
  }
332527
332637
  return null;
332528
332638
  }
332529
332639
  function readOpenCodeManifest(root2, scope) {
332530
- const localRoot = scope === "project" ? path7.join(root2, ".opencode") : root2;
332531
- const opencodeToolsDir = path7.join(localRoot, "tools");
332532
- const opencodePluginsDir = path7.join(localRoot, "plugins");
332640
+ const localRoot = scope === "project" ? path8.join(root2, ".opencode") : root2;
332641
+ const opencodeToolsDir = path8.join(localRoot, "tools");
332642
+ const opencodePluginsDir = path8.join(localRoot, "plugins");
332533
332643
  const tools = listCodeFiles(opencodeToolsDir);
332534
332644
  const pluginFiles = listCodeFiles(opencodePluginsDir);
332535
332645
  const config = readOpenCodeConfig(root2);
@@ -332570,18 +332680,18 @@ function readOpenCodeManifest(root2, scope) {
332570
332680
  }
332571
332681
  function discoverOpenCodeInstructionFiles(projectRoot, localRoot) {
332572
332682
  const candidates = [
332573
- path7.join(projectRoot, "AGENTS.md"),
332574
- path7.join(projectRoot, ".opencode", "instructions.md"),
332575
- path7.join(projectRoot, ".opencode", "AGENTS.md"),
332576
- path7.join(localRoot, "instructions.md"),
332577
- path7.join(localRoot, "AGENTS.md")
332683
+ path8.join(projectRoot, "AGENTS.md"),
332684
+ path8.join(projectRoot, ".opencode", "instructions.md"),
332685
+ path8.join(projectRoot, ".opencode", "AGENTS.md"),
332686
+ path8.join(localRoot, "instructions.md"),
332687
+ path8.join(localRoot, "AGENTS.md")
332578
332688
  ];
332579
- return Array.from(new Set(candidates.filter((filePath) => fs6.existsSync(filePath)))).sort();
332689
+ return Array.from(new Set(candidates.filter((filePath) => fs7.existsSync(filePath)))).sort();
332580
332690
  }
332581
332691
  function dedupeManifests(manifests) {
332582
332692
  const seen = /* @__PURE__ */ new Set();
332583
332693
  return manifests.filter((item) => {
332584
- const key3 = `${item.id}:${path7.resolve(item.root)}`;
332694
+ const key3 = `${item.id}:${path8.resolve(item.root)}`;
332585
332695
  if (seen.has(key3)) return false;
332586
332696
  seen.add(key3);
332587
332697
  return true;
@@ -332589,14 +332699,14 @@ function dedupeManifests(manifests) {
332589
332699
  }
332590
332700
  function listCodeFiles(dir) {
332591
332701
  try {
332592
- return fs6.readdirSync(dir, { withFileTypes: true }).filter((e3) => e3.isFile() && /\.(?:c?js|mjs|ts)$/.test(e3.name)).map((e3) => path7.join(dir, e3.name)).sort();
332702
+ return fs7.readdirSync(dir, { withFileTypes: true }).filter((e3) => e3.isFile() && /\.(?:c?js|mjs|ts)$/.test(e3.name)).map((e3) => path8.join(dir, e3.name)).sort();
332593
332703
  } catch {
332594
332704
  return [];
332595
332705
  }
332596
332706
  }
332597
332707
  function parseFrontmatterMarkdown(filePath) {
332598
332708
  try {
332599
- const content = fs6.readFileSync(filePath, "utf-8").replace(/^\uFEFF/, "");
332709
+ const content = fs7.readFileSync(filePath, "utf-8").replace(/^\uFEFF/, "");
332600
332710
  const match = content.match(/^---\s*([\s\S]*?)\s*---\s*/);
332601
332711
  if (!match) return { metadata: {}, body: content };
332602
332712
  const metadata = {};
@@ -332625,7 +332735,7 @@ function parseMetadataValue(raw) {
332625
332735
  function parseSimpleToml(filePath) {
332626
332736
  try {
332627
332737
  const metadata = {};
332628
- const content = fs6.readFileSync(filePath, "utf-8").replace(/^\uFEFF/, "");
332738
+ const content = fs7.readFileSync(filePath, "utf-8").replace(/^\uFEFF/, "");
332629
332739
  const multiline = null;
332630
332740
  if (multiline) return metadata;
332631
332741
  const lines = content.split(/\r?\n/);
@@ -332659,7 +332769,7 @@ function parseSimpleToml(filePath) {
332659
332769
  }
332660
332770
  }
332661
332771
  function agentPresetFromMetadata(filePath, ecosystem, metadata, body = "") {
332662
- const name50 = asString(metadata.name) || path7.basename(filePath).replace(/\.(?:toml|md)$/i, "");
332772
+ const name50 = asString(metadata.name) || path8.basename(filePath).replace(/\.(?:toml|md)$/i, "");
332663
332773
  const description = asString(metadata.description);
332664
332774
  const instructions = asString(metadata.developer_instructions || metadata.instructions || metadata.prompt) || body.trim();
332665
332775
  if (!name50 || !description) return null;
@@ -332687,12 +332797,12 @@ function listFilesRecursive(root2, extension, maxDepth = 4) {
332687
332797
  if (depth > maxDepth) return;
332688
332798
  let entries;
332689
332799
  try {
332690
- entries = fs6.readdirSync(dir, { withFileTypes: true });
332800
+ entries = fs7.readdirSync(dir, { withFileTypes: true });
332691
332801
  } catch {
332692
332802
  return;
332693
332803
  }
332694
332804
  for (const entry of entries) {
332695
- const full = path7.join(dir, entry.name);
332805
+ const full = path8.join(dir, entry.name);
332696
332806
  if (entry.isFile() && extension.test(entry.name)) results.push(full);
332697
332807
  if (entry.isDirectory() && !entry.name.startsWith(".git") && entry.name !== "node_modules") walk4(full, depth + 1);
332698
332808
  }
@@ -332703,10 +332813,10 @@ function listFilesRecursive(root2, extension, maxDepth = 4) {
332703
332813
  function discoverAgentPresets(root2) {
332704
332814
  const presets = [];
332705
332815
  const codexDirs = [
332706
- path7.join(root2, ".codex", "agents"),
332707
- path7.join(root2, ".agents", "agents"),
332708
- path7.join(os2.homedir(), ".codex", "agents"),
332709
- path7.join(os2.homedir(), ".agents", "agents")
332816
+ path8.join(root2, ".codex", "agents"),
332817
+ path8.join(root2, ".agents", "agents"),
332818
+ path8.join(os2.homedir(), ".codex", "agents"),
332819
+ path8.join(os2.homedir(), ".agents", "agents")
332710
332820
  ];
332711
332821
  for (const dir of codexDirs) {
332712
332822
  for (const filePath of listFilesRecursive(dir, /\.toml$/i, 1)) {
@@ -332715,14 +332825,14 @@ function discoverAgentPresets(root2) {
332715
332825
  }
332716
332826
  }
332717
332827
  const claudeDirs = [
332718
- path7.join(root2, ".claude", "agents"),
332719
- path7.join(os2.homedir(), ".claude", "agents"),
332720
- path7.join(os2.homedir(), ".config", "opencode", "agents")
332828
+ path8.join(root2, ".claude", "agents"),
332829
+ path8.join(os2.homedir(), ".claude", "agents"),
332830
+ path8.join(os2.homedir(), ".config", "opencode", "agents")
332721
332831
  ];
332722
332832
  for (const dir of claudeDirs) {
332723
332833
  for (const filePath of listFilesRecursive(dir, /\.md$/i, 1)) {
332724
332834
  const parsed = parseFrontmatterMarkdown(filePath);
332725
- const ecosystem = filePath.includes(`${path7.sep}.config${path7.sep}opencode${path7.sep}`) ? "opencode" : "claude-code";
332835
+ const ecosystem = filePath.includes(`${path8.sep}.config${path8.sep}opencode${path8.sep}`) ? "opencode" : "claude-code";
332726
332836
  const preset = agentPresetFromMetadata(filePath, ecosystem, parsed.metadata, parsed.body);
332727
332837
  if (preset) presets.push(preset);
332728
332838
  }
@@ -332759,15 +332869,15 @@ function findAgentPreset(root2, selector2) {
332759
332869
  preset.id,
332760
332870
  preset.name,
332761
332871
  `${preset.ecosystem}:${preset.name}`,
332762
- path7.basename(preset.path)
332872
+ path8.basename(preset.path)
332763
332873
  ].map((value) => String(value || "").toLowerCase());
332764
- return keys.includes(normalized) || path7.resolve(preset.path).toLowerCase() === path7.resolve(wanted).toLowerCase();
332874
+ return keys.includes(normalized) || path8.resolve(preset.path).toLowerCase() === path8.resolve(wanted).toLowerCase();
332765
332875
  }) || null;
332766
332876
  }
332767
332877
 
332768
332878
  // src/tools/terminalTakeover.ts
332769
- var fs7 = __toESM(require("fs"));
332770
- var path8 = __toESM(require("path"));
332879
+ var fs8 = __toESM(require("fs"));
332880
+ var path9 = __toESM(require("path"));
332771
332881
  var import_child_process2 = require("child_process");
332772
332882
  var import_crypto5 = require("crypto");
332773
332883
  var ROOT_TERMINAL_ACTOR_ID = "00000000-0000-4000-8000-000000000001";
@@ -332782,7 +332892,7 @@ function isoNow() {
332782
332892
  return (/* @__PURE__ */ new Date()).toISOString();
332783
332893
  }
332784
332894
  function canonicalPersistenceRoot(root2) {
332785
- const resolved = path8.resolve(root2 || process.cwd());
332895
+ const resolved = path9.resolve(root2 || process.cwd());
332786
332896
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
332787
332897
  }
332788
332898
  function portableWorkspacePath(input) {
@@ -332791,7 +332901,7 @@ function portableWorkspacePath(input) {
332791
332901
  if (wsl) return `${wsl[1].toLowerCase()}:/${String(wsl[2] || "").replace(/^\/+|\/+$/g, "")}`.replace(/\/$/, "");
332792
332902
  const drive = /^([a-zA-Z]):(?:\/(.*))?$/.exec(raw);
332793
332903
  if (drive) return `${drive[1].toLowerCase()}:/${String(drive[2] || "").replace(/^\/+|\/+$/g, "")}`.replace(/\/$/, "");
332794
- const resolved = path8.resolve(raw).replace(/\\/g, "/").replace(/\/+$/g, "");
332904
+ const resolved = path9.resolve(raw).replace(/\\/g, "/").replace(/\/+$/g, "");
332795
332905
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
332796
332906
  }
332797
332907
  function terminalTakeoverWorkspaceId(workspacePath) {
@@ -332886,12 +332996,12 @@ function nodePtyHasConptyDll() {
332886
332996
  if (process.platform !== "win32") return false;
332887
332997
  try {
332888
332998
  const packageJson = require.resolve("node-pty/package.json");
332889
- const packageRoot = path8.dirname(packageJson);
332999
+ const packageRoot = path9.dirname(packageJson);
332890
333000
  return [
332891
- path8.join(packageRoot, "build", "Release", "conpty", "conpty.dll"),
332892
- path8.join(packageRoot, "build", "Debug", "conpty", "conpty.dll"),
332893
- path8.join(packageRoot, "prebuilds", `${process.platform}-${process.arch}`, "conpty", "conpty.dll")
332894
- ].some((candidate) => fs7.existsSync(candidate));
333001
+ path9.join(packageRoot, "build", "Release", "conpty", "conpty.dll"),
333002
+ path9.join(packageRoot, "build", "Debug", "conpty", "conpty.dll"),
333003
+ path9.join(packageRoot, "prebuilds", `${process.platform}-${process.arch}`, "conpty", "conpty.dll")
333004
+ ].some((candidate) => fs8.existsSync(candidate));
332895
333005
  } catch {
332896
333006
  return false;
332897
333007
  }
@@ -333124,7 +333234,7 @@ function spawnTakeoverPty(shell, cwd, env, cols, rows) {
333124
333234
  };
333125
333235
  }
333126
333236
  function persistencePath(root2) {
333127
- return path8.join(root2, "Terminal", "Takeover.json");
333237
+ return path9.join(root2, "Terminal", "Takeover.json");
333128
333238
  }
333129
333239
  function validPersistedRecord(input) {
333130
333240
  if (!input || typeof input !== "object") return null;
@@ -333165,7 +333275,7 @@ function ensurePersistenceLoaded(rootRaw) {
333165
333275
  if (loaded) return loaded;
333166
333276
  const records = /* @__PURE__ */ new Map();
333167
333277
  try {
333168
- const parsed = JSON.parse(fs7.readFileSync(persistencePath(root2), "utf-8"));
333278
+ const parsed = JSON.parse(fs8.readFileSync(persistencePath(root2), "utf-8"));
333169
333279
  if (Array.isArray(parsed.records)) {
333170
333280
  for (const input of parsed.records) {
333171
333281
  const record = validPersistedRecord(input);
@@ -333183,19 +333293,19 @@ function persistEndedRecords(rootRaw) {
333183
333293
  const output = { version: 1, updatedAt: isoNow(), records };
333184
333294
  const filePath = persistencePath(root2);
333185
333295
  const tempPath = `${filePath}.tmp-${process.pid}-${(0, import_crypto5.randomUUID)()}`;
333186
- fs7.mkdirSync(path8.dirname(filePath), { recursive: true });
333187
- const fd = fs7.openSync(tempPath, "w");
333296
+ fs8.mkdirSync(path9.dirname(filePath), { recursive: true });
333297
+ const fd = fs8.openSync(tempPath, "w");
333188
333298
  try {
333189
- fs7.writeFileSync(fd, JSON.stringify(output, null, 2), "utf-8");
333190
- fs7.fsyncSync(fd);
333299
+ fs8.writeFileSync(fd, JSON.stringify(output, null, 2), "utf-8");
333300
+ fs8.fsyncSync(fd);
333191
333301
  } finally {
333192
- fs7.closeSync(fd);
333302
+ fs8.closeSync(fd);
333193
333303
  }
333194
333304
  try {
333195
- fs7.renameSync(tempPath, filePath);
333305
+ fs8.renameSync(tempPath, filePath);
333196
333306
  } catch (error) {
333197
333307
  try {
333198
- fs7.rmSync(tempPath, { force: true });
333308
+ fs8.rmSync(tempPath, { force: true });
333199
333309
  } catch {
333200
333310
  }
333201
333311
  throw error;
@@ -333424,8 +333534,8 @@ function runTerminalTakeover(input) {
333424
333534
  }
333425
333535
 
333426
333536
  // src/tools/computerUse.ts
333427
- var fs8 = __toESM(require("fs"));
333428
- var path9 = __toESM(require("path"));
333537
+ var fs9 = __toESM(require("fs"));
333538
+ var path10 = __toESM(require("path"));
333429
333539
  var crypto6 = __toESM(require("crypto"));
333430
333540
  var os3 = __toESM(require("os"));
333431
333541
 
@@ -333633,8 +333743,8 @@ async function runPowerShell(script, timeout = 3e4, lane = "action") {
333633
333743
  return await runPersistentPowerShell(script, timeout, lane);
333634
333744
  }
333635
333745
  function tempScreenshotDir() {
333636
- const dir = path9.join(os3.tmpdir(), "newmark-computer-use");
333637
- fs8.mkdirSync(dir, { recursive: true });
333746
+ const dir = path10.join(os3.tmpdir(), "newmark-computer-use");
333747
+ fs9.mkdirSync(dir, { recursive: true });
333638
333748
  const now2 = Date.now();
333639
333749
  if (now2 - lastScreenshotCleanupAt >= SCREENSHOT_CLEANUP_INTERVAL_MS) {
333640
333750
  lastScreenshotCleanupAt = now2;
@@ -333656,7 +333766,7 @@ function ephemeralScreenshotPath(kind, directory = tempScreenshotDir(), createdA
333656
333766
  const pid = Math.max(1, Math.floor(Number(ownerPid) || process.pid));
333657
333767
  const timestamp = Math.max(0, Math.floor(Number(createdAt) || Date.now()));
333658
333768
  const nonce = /^[a-f0-9]{8}$/i.test(String(suffix)) ? String(suffix).toLowerCase() : crypto6.randomBytes(4).toString("hex");
333659
- return path9.join(directory, `${kind}-p${pid}-t${timestamp}-${nonce}.jpg`);
333769
+ return path10.join(directory, `${kind}-p${pid}-t${timestamp}-${nonce}.jpg`);
333660
333770
  }
333661
333771
  function isProcessAlive(pid) {
333662
333772
  if (!Number.isSafeInteger(pid) || pid <= 0) return false;
@@ -333669,14 +333779,14 @@ function isProcessAlive(pid) {
333669
333779
  }
333670
333780
  }
333671
333781
  function cleanupStaleScreenshots(options = {}) {
333672
- const directory = options.directory || path9.join(os3.tmpdir(), "newmark-computer-use");
333782
+ const directory = options.directory || path10.join(os3.tmpdir(), "newmark-computer-use");
333673
333783
  const now2 = Number.isFinite(Number(options.now)) ? Number(options.now) : Date.now();
333674
333784
  const processAlive = options.isProcessAlive || isProcessAlive;
333675
333785
  const ownedPattern = /^(?:observe|app)-p([1-9]\d*)-t(\d{10,16})-[a-f0-9]{8}\.jpg$/i;
333676
333786
  const legacyPattern = /^(?:observe|app)-\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-\d{3}Z-[a-f0-9]{8}\.jpg$/i;
333677
333787
  let names = [];
333678
333788
  try {
333679
- names = fs8.readdirSync(directory);
333789
+ names = fs9.readdirSync(directory);
333680
333790
  } catch {
333681
333791
  return { removed: 0 };
333682
333792
  }
@@ -333685,10 +333795,10 @@ function cleanupStaleScreenshots(options = {}) {
333685
333795
  const owned = ownedPattern.exec(name50);
333686
333796
  const isLegacy = !owned && legacyPattern.test(name50);
333687
333797
  if (!owned && !isLegacy) continue;
333688
- const filePath = path9.join(directory, name50);
333798
+ const filePath = path10.join(directory, name50);
333689
333799
  let stats;
333690
333800
  try {
333691
- stats = fs8.lstatSync(filePath);
333801
+ stats = fs9.lstatSync(filePath);
333692
333802
  if (!stats.isFile() || stats.isSymbolicLink()) continue;
333693
333803
  } catch {
333694
333804
  continue;
@@ -333712,7 +333822,7 @@ function cleanupStaleScreenshots(options = {}) {
333712
333822
  }
333713
333823
  if (!shouldRemove) continue;
333714
333824
  try {
333715
- fs8.unlinkSync(filePath);
333825
+ fs9.unlinkSync(filePath);
333716
333826
  removed += 1;
333717
333827
  } catch {
333718
333828
  }
@@ -333735,7 +333845,7 @@ function captureBounds(maxWidth, maxHeight) {
333735
333845
  }
333736
333846
  function removeEphemeralScreenshot(outPath) {
333737
333847
  try {
333738
- fs8.unlinkSync(outPath);
333848
+ fs9.unlinkSync(outPath);
333739
333849
  } catch {
333740
333850
  }
333741
333851
  }
@@ -333811,7 +333921,7 @@ async function startTakeoverOverlay(durationMs = 0, input = {}) {
333811
333921
  const width = 2;
333812
333922
  const speedSeconds = 3;
333813
333923
  const ownerPid = Math.max(0, Math.floor(Number(input.ownerPid ?? process.pid) || 0));
333814
- const scriptPath = path9.join(tempScreenshotDir(), `takeover-overlay-${timestampName()}-${crypto6.randomBytes(4).toString("hex")}.ps1`);
333924
+ const scriptPath = path10.join(tempScreenshotDir(), `takeover-overlay-${timestampName()}-${crypto6.randomBytes(4).toString("hex")}.ps1`);
333815
333925
  const script = [
333816
333926
  "Add-Type -AssemblyName System.Windows.Forms",
333817
333927
  "Add-Type -AssemblyName System.Drawing",
@@ -333950,7 +334060,7 @@ async function startTakeoverOverlay(durationMs = 0, input = {}) {
333950
334060
  "[System.Windows.Forms.Application]::Run()",
333951
334061
  "try { Remove-Item -LiteralPath $PSCommandPath -Force -ErrorAction SilentlyContinue } catch {}"
333952
334062
  ].filter(Boolean).join("\r\n");
333953
- fs8.writeFileSync(scriptPath, `\uFEFF${script}`, "utf8");
334063
+ fs9.writeFileSync(scriptPath, `\uFEFF${script}`, "utf8");
333954
334064
  const createCommand = [
333955
334065
  `$cmd = 'powershell.exe -NoProfile -ExecutionPolicy Bypass -File ' + ${psQuote(`"${scriptPath}"`)}`,
333956
334066
  `$startup = ([wmiclass]'Win32_ProcessStartup').CreateInstance()`,
@@ -333963,7 +334073,7 @@ async function startTakeoverOverlay(durationMs = 0, input = {}) {
333963
334073
  const pid = Number(String(result.output || "").trim().split(/\r?\n/).pop() || 0);
333964
334074
  if (!Number.isFinite(pid) || pid <= 0 || !result.ok) {
333965
334075
  try {
333966
- fs8.unlinkSync(scriptPath);
334076
+ fs9.unlinkSync(scriptPath);
333967
334077
  } catch {
333968
334078
  }
333969
334079
  return { ok: false, action: "takeover_start", error: result.output || "Overlay failed to start." };
@@ -333990,7 +334100,7 @@ function parseJsonArray(text) {
333990
334100
  return [];
333991
334101
  }
333992
334102
  function observationKey(workspacePath, ownerId) {
333993
- return `${path9.resolve(workspacePath || process.cwd()).toLowerCase()}::${String(ownerId || "direct")}`;
334103
+ return `${path10.resolve(workspacePath || process.cwd()).toLowerCase()}::${String(ownerId || "direct")}`;
333994
334104
  }
333995
334105
  function sceneGeneration(apps, elements) {
333996
334106
  const seed = [
@@ -334206,7 +334316,7 @@ async function cropScreenshot(workspacePath, ownerId, allowEphemeralVisionImage,
334206
334316
  ...parsed
334207
334317
  };
334208
334318
  if (includeRawUi) payload.perception.elements = elements;
334209
- const imageAvailable = parsed.image_available === true && fs8.existsSync(outPath);
334319
+ const imageAvailable = parsed.image_available === true && fs9.existsSync(outPath);
334210
334320
  if (allowEphemeralVisionImage && imageAvailable) {
334211
334321
  payload.vision_image_path = outPath;
334212
334322
  retainedForVision = true;
@@ -334469,7 +334579,7 @@ async function screenshot(workspacePath, ownerId, allowEphemeralVisionImage, inc
334469
334579
  ...parsed
334470
334580
  };
334471
334581
  if (includeRawUi) payload.perception.elements = ui.elements;
334472
- const imageAvailable = parsed.image_available === true && fs8.existsSync(outPath);
334582
+ const imageAvailable = parsed.image_available === true && fs9.existsSync(outPath);
334473
334583
  if (allowEphemeralVisionImage && imageAvailable) {
334474
334584
  payload.vision_image_path = outPath;
334475
334585
  retainedForVision = true;
@@ -334793,13 +334903,13 @@ async function runComputerUse(options) {
334793
334903
  }
334794
334904
 
334795
334905
  // src/core/ssh.ts
334796
- var fs10 = __toESM(require("fs"));
334797
- var path11 = __toESM(require("path"));
334906
+ var fs11 = __toESM(require("fs"));
334907
+ var path12 = __toESM(require("path"));
334798
334908
 
334799
334909
  // src/core/asyncProcess.ts
334800
334910
  var import_child_process4 = require("child_process");
334801
- var fs9 = __toESM(require("fs/promises"));
334802
- var path10 = __toESM(require("path"));
334911
+ var fs10 = __toESM(require("fs/promises"));
334912
+ var path11 = __toESM(require("path"));
334803
334913
  var STOP_SETTLEMENT_WATCHDOG_MS = 500;
334804
334914
  function signalMessage(signal) {
334805
334915
  const reason = signal?.reason;
@@ -334809,7 +334919,7 @@ function signalMessage(signal) {
334809
334919
  }
334810
334920
  function trustedWindowsTaskkillPath() {
334811
334921
  const windowsRoot = String(process.env.SystemRoot || process.env.WINDIR || "C:\\Windows");
334812
- return path10.join(windowsRoot, "System32", "taskkill.exe");
334922
+ return path11.join(windowsRoot, "System32", "taskkill.exe");
334813
334923
  }
334814
334924
  function stopProcessTree(child) {
334815
334925
  const pid = child.pid;
@@ -334978,7 +335088,7 @@ async function runAsyncWindowsBatch(command, args, options = {}) {
334978
335088
  }
334979
335089
  async function accessible(filePath) {
334980
335090
  try {
334981
- await fs9.access(filePath);
335091
+ await fs10.access(filePath);
334982
335092
  return true;
334983
335093
  } catch {
334984
335094
  return false;
@@ -334987,14 +335097,14 @@ async function accessible(filePath) {
334987
335097
  async function resolveWindowsLauncher(command) {
334988
335098
  const clean = String(command || "").trim();
334989
335099
  if (!clean) return "";
334990
- if (path10.isAbsolute(clean) || /[\\/]/.test(clean)) {
334991
- const absolute = path10.resolve(clean);
335100
+ if (path11.isAbsolute(clean) || /[\\/]/.test(clean)) {
335101
+ const absolute = path11.resolve(clean);
334992
335102
  return await accessible(absolute) ? absolute : "";
334993
335103
  }
334994
- for (const entry of String(process.env.PATH || "").split(path10.delimiter)) {
335104
+ for (const entry of String(process.env.PATH || "").split(path11.delimiter)) {
334995
335105
  const directory = entry.trim().replace(/^"|"$/g, "");
334996
335106
  if (!directory) continue;
334997
- const candidate = path10.join(directory, clean);
335107
+ const candidate = path11.join(directory, clean);
334998
335108
  if (await accessible(candidate)) return candidate;
334999
335109
  }
335000
335110
  return "";
@@ -335002,11 +335112,11 @@ async function resolveWindowsLauncher(command) {
335002
335112
  async function resolveNpmBatchTarget(batchPath) {
335003
335113
  let source = "";
335004
335114
  try {
335005
- source = await fs9.readFile(batchPath, "utf8");
335115
+ source = await fs10.readFile(batchPath, "utf8");
335006
335116
  } catch {
335007
335117
  return null;
335008
335118
  }
335009
- const directory = path10.dirname(batchPath);
335119
+ const directory = path11.dirname(batchPath);
335010
335120
  let relativeScript = "";
335011
335121
  const direct = /(?:%~dp0|%dp0%)\\?([^"\r\n]+)"\s+%\*/i.exec(source);
335012
335122
  if (direct) relativeScript = direct[1];
@@ -335020,10 +335130,10 @@ async function resolveNpmBatchTarget(batchPath) {
335020
335130
  }
335021
335131
  }
335022
335132
  if (!relativeScript) return null;
335023
- const scriptPath = path10.resolve(directory, relativeScript.replace(/\\/g, path10.sep));
335024
- const directoryPrefix = `${path10.resolve(directory).toLowerCase()}${path10.sep}`;
335133
+ const scriptPath = path11.resolve(directory, relativeScript.replace(/\\/g, path11.sep));
335134
+ const directoryPrefix = `${path11.resolve(directory).toLowerCase()}${path11.sep}`;
335025
335135
  if (!scriptPath.toLowerCase().startsWith(directoryPrefix) || !await accessible(scriptPath)) return null;
335026
- const siblingNode = path10.join(directory, "node.exe");
335136
+ const siblingNode = path11.join(directory, "node.exe");
335027
335137
  const nodePath = await accessible(siblingNode) ? siblingNode : await resolveWindowsLauncher("node.exe") || await resolveWindowsLauncher("node");
335028
335138
  return nodePath ? { nodePath, scriptPath } : null;
335029
335139
  }
@@ -335066,19 +335176,19 @@ var SshManager = class {
335066
335176
  rootPath;
335067
335177
  runner;
335068
335178
  storePath() {
335069
- return path11.join(this.rootPath, "Work", "SSH.json");
335179
+ return path12.join(this.rootPath, "Work", "SSH.json");
335070
335180
  }
335071
335181
  ensureStore() {
335072
335182
  try {
335073
- fs10.mkdirSync(path11.join(this.rootPath, "Work"), { recursive: true });
335074
- if (!fs10.existsSync(this.storePath())) fs10.writeFileSync(this.storePath(), "[]", "utf-8");
335183
+ fs11.mkdirSync(path12.join(this.rootPath, "Work"), { recursive: true });
335184
+ if (!fs11.existsSync(this.storePath())) fs11.writeFileSync(this.storePath(), "[]", "utf-8");
335075
335185
  } catch {
335076
335186
  }
335077
335187
  }
335078
335188
  readRaw() {
335079
335189
  this.ensureStore();
335080
335190
  try {
335081
- const parsed = JSON.parse(fs10.readFileSync(this.storePath(), "utf-8").replace(/^\uFEFF/, ""));
335191
+ const parsed = JSON.parse(fs11.readFileSync(this.storePath(), "utf-8").replace(/^\uFEFF/, ""));
335082
335192
  if (!Array.isArray(parsed)) return [];
335083
335193
  return parsed.map((item) => this.normalize(item)).filter((item) => !!item);
335084
335194
  } catch {
@@ -335087,7 +335197,7 @@ var SshManager = class {
335087
335197
  }
335088
335198
  writeRaw(items) {
335089
335199
  this.ensureStore();
335090
- fs10.writeFileSync(this.storePath(), JSON.stringify(items, null, 2), "utf-8");
335200
+ fs11.writeFileSync(this.storePath(), JSON.stringify(items, null, 2), "utf-8");
335091
335201
  }
335092
335202
  normalize(raw) {
335093
335203
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
@@ -335267,8 +335377,8 @@ var SshManager = class {
335267
335377
  };
335268
335378
 
335269
335379
  // src/core/workspace.ts
335270
- var fs11 = __toESM(require("fs"));
335271
- var path12 = __toESM(require("path"));
335380
+ var fs12 = __toESM(require("fs"));
335381
+ var path13 = __toESM(require("path"));
335272
335382
  var crypto7 = __toESM(require("crypto"));
335273
335383
  function lastEmbeddedWindowsPath(input) {
335274
335384
  const matcher = /[A-Za-z]:[\\/]/g;
@@ -335287,22 +335397,22 @@ function normalizeHostWorkspacePath(input, platform = process.platform) {
335287
335397
  const raw = String(input || "").trim();
335288
335398
  const embeddedWindowsPath = lastEmbeddedWindowsPath(raw);
335289
335399
  if (platform === "win32") {
335290
- if (embeddedWindowsPath) return path12.win32.normalize(embeddedWindowsPath.replace(/\//g, "\\"));
335400
+ if (embeddedWindowsPath) return path13.win32.normalize(embeddedWindowsPath.replace(/\//g, "\\"));
335291
335401
  const wsl = /^\/mnt\/([a-zA-Z])(?:\/(.*))?$/.exec(raw.replace(/\\/g, "/"));
335292
- if (wsl) return path12.win32.normalize(`${wsl[1].toUpperCase()}:\\${String(wsl[2] || "").replace(/\//g, "\\")}`);
335293
- return path12.win32.resolve(raw || ".");
335402
+ if (wsl) return path13.win32.normalize(`${wsl[1].toUpperCase()}:\\${String(wsl[2] || "").replace(/\//g, "\\")}`);
335403
+ return path13.win32.resolve(raw || ".");
335294
335404
  }
335295
335405
  if (platform === "linux" && embeddedWindowsPath) {
335296
335406
  const drive = embeddedWindowsPath[0].toLowerCase();
335297
335407
  const rest = embeddedWindowsPath.slice(3).replace(/\\/g, "/").replace(/^\/+/, "");
335298
- return path12.posix.resolve(`/mnt/${drive}/${rest}`);
335408
+ return path13.posix.resolve(`/mnt/${drive}/${rest}`);
335299
335409
  }
335300
- return path12.posix.resolve(raw || ".");
335410
+ return path13.posix.resolve(raw || ".");
335301
335411
  }
335302
335412
  function isPathInside(parent, child) {
335303
335413
  try {
335304
- const relative6 = path12.relative(path12.resolve(parent), path12.resolve(child));
335305
- return relative6 === "" || !!relative6 && !relative6.startsWith("..") && !path12.isAbsolute(relative6);
335414
+ const relative6 = path13.relative(path13.resolve(parent), path13.resolve(child));
335415
+ return relative6 === "" || !!relative6 && !relative6.startsWith("..") && !path13.isAbsolute(relative6);
335306
335416
  } catch {
335307
335417
  return false;
335308
335418
  }
@@ -335310,7 +335420,7 @@ function isPathInside(parent, child) {
335310
335420
  function isProtectedInstallWorkspacePath(candidate) {
335311
335421
  const value = String(candidate || "").trim();
335312
335422
  if (!value) return false;
335313
- const roots = [path12.dirname(process.execPath)];
335423
+ const roots = [path13.dirname(process.execPath)];
335314
335424
  if (process.platform === "win32") {
335315
335425
  roots.push(
335316
335426
  process.env.ProgramFiles || "",
@@ -335318,7 +335428,7 @@ function isProtectedInstallWorkspacePath(candidate) {
335318
335428
  process.env.ProgramW6432 || ""
335319
335429
  );
335320
335430
  }
335321
- const resolved = path12.resolve(value);
335431
+ const resolved = path13.resolve(value);
335322
335432
  return roots.filter(Boolean).some((root2) => isPathInside(root2, resolved));
335323
335433
  }
335324
335434
  var WorkspaceManager = class {
@@ -335328,16 +335438,16 @@ var WorkspaceManager = class {
335328
335438
  this.detached = options.detached === true;
335329
335439
  this.pcHash = this.loadPcHash();
335330
335440
  if (this.detached) return;
335331
- const workDir = path12.join(rootPath, "Work");
335441
+ const workDir = path13.join(rootPath, "Work");
335332
335442
  try {
335333
- fs11.mkdirSync(workDir, { recursive: true });
335443
+ fs12.mkdirSync(workDir, { recursive: true });
335334
335444
  } catch {
335335
335445
  }
335336
335446
  for (const fn of ["Local.json", "External.json"]) {
335337
- const p = path12.join(workDir, fn);
335338
- if (!fs11.existsSync(p)) {
335447
+ const p = path13.join(workDir, fn);
335448
+ if (!fs12.existsSync(p)) {
335339
335449
  try {
335340
- fs11.writeFileSync(p, "[]", "utf-8");
335450
+ fs12.writeFileSync(p, "[]", "utf-8");
335341
335451
  } catch {
335342
335452
  }
335343
335453
  }
@@ -335358,7 +335468,7 @@ var WorkspaceManager = class {
335358
335468
  detached;
335359
335469
  loadPcHash() {
335360
335470
  try {
335361
- const h2 = fs11.readFileSync(path12.join(this.rootPath, "PC_Hash.config"), "utf-8");
335471
+ const h2 = fs12.readFileSync(path13.join(this.rootPath, "PC_Hash.config"), "utf-8");
335362
335472
  return h2.trim();
335363
335473
  } catch {
335364
335474
  return "";
@@ -335373,19 +335483,19 @@ var WorkspaceManager = class {
335373
335483
  if (this.external.length !== before) this.saveExternal();
335374
335484
  }
335375
335485
  scan() {
335376
- const w = path12.join(this.rootPath, "Work");
335377
- if (!fs11.existsSync(w)) return;
335486
+ const w = path13.join(this.rootPath, "Work");
335487
+ if (!fs12.existsSync(w)) return;
335378
335488
  let internalChanged = false;
335379
335489
  let externalChanged = false;
335380
335490
  try {
335381
- const local = JSON.parse(fs11.readFileSync(path12.join(w, "Local.json"), "utf-8"));
335491
+ const local = JSON.parse(fs12.readFileSync(path13.join(w, "Local.json"), "utf-8"));
335382
335492
  this.internal = Array.isArray(local) ? local.map((item) => this.normalizeInternalWorkspace(item, (changed) => {
335383
335493
  internalChanged = internalChanged || changed;
335384
335494
  })) : [];
335385
335495
  } catch {
335386
335496
  }
335387
335497
  try {
335388
- const ext = JSON.parse(fs11.readFileSync(path12.join(w, "External.json"), "utf-8"));
335498
+ const ext = JSON.parse(fs12.readFileSync(path13.join(w, "External.json"), "utf-8"));
335389
335499
  const normalized = Array.isArray(ext) ? ext.map((item) => this.normalizeExternalWorkspace(item, (changed) => {
335390
335500
  externalChanged = externalChanged || changed;
335391
335501
  })) : [];
@@ -335396,13 +335506,13 @@ var WorkspaceManager = class {
335396
335506
  });
335397
335507
  } catch {
335398
335508
  }
335399
- for (const entry of fs11.readdirSync(w, { withFileTypes: true })) {
335509
+ for (const entry of fs12.readdirSync(w, { withFileTypes: true })) {
335400
335510
  if (entry.isDirectory() && !["Local.json", "External.json", ".ssh"].includes(entry.name)) {
335401
335511
  if (!this.internal.find((wi) => wi.name === entry.name)) {
335402
335512
  this.internal.push({
335403
- id: this.stableWorkspaceId("local", path12.join(w, entry.name)),
335513
+ id: this.stableWorkspaceId("local", path13.join(w, entry.name)),
335404
335514
  name: entry.name,
335405
- path: path12.join(w, entry.name),
335515
+ path: path13.join(w, entry.name),
335406
335516
  isInternal: true,
335407
335517
  hostBinding: "",
335408
335518
  icon: entry.name.charAt(0).toUpperCase()
@@ -335415,9 +335525,9 @@ var WorkspaceManager = class {
335415
335525
  if (externalChanged) this.saveExternal();
335416
335526
  }
335417
335527
  normalizeInternalWorkspace(input, markChanged) {
335418
- const rawName = String(input?.name || path12.basename(String(input?.path || "")) || "").trim();
335528
+ const rawName = String(input?.name || path13.basename(String(input?.path || "")) || "").trim();
335419
335529
  const name50 = rawName || (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").replace("T", "_").slice(0, 15);
335420
- const expectedPath = path12.join(this.rootPath, "Work", name50);
335530
+ const expectedPath = path13.join(this.rootPath, "Work", name50);
335421
335531
  const id = this.stableWorkspaceId("local", expectedPath);
335422
335532
  if (normalizeHostWorkspacePath(String(input?.path || "")) !== normalizeHostWorkspacePath(expectedPath) || input?.isInternal !== true || input?.id !== id) markChanged(true);
335423
335533
  return {
@@ -335438,11 +335548,11 @@ var WorkspaceManager = class {
335438
335548
  return {
335439
335549
  ...input,
335440
335550
  id,
335441
- name: String(input?.name || path12.basename(workspacePath) || id),
335551
+ name: String(input?.name || path13.basename(workspacePath) || id),
335442
335552
  path: workspacePath,
335443
335553
  isInternal: false,
335444
335554
  hostBinding: String(input?.hostBinding || ""),
335445
- icon: String(input?.icon || path12.basename(workspacePath).charAt(0).toUpperCase()),
335555
+ icon: String(input?.icon || path13.basename(workspacePath).charAt(0).toUpperCase()),
335446
335556
  kind
335447
335557
  };
335448
335558
  }
@@ -335463,11 +335573,11 @@ var WorkspaceManager = class {
335463
335573
  const resolved = normalizeHostWorkspacePath(target);
335464
335574
  let real = resolved;
335465
335575
  try {
335466
- real = fs11.existsSync(resolved) ? fs11.realpathSync.native(resolved) : resolved;
335576
+ real = fs12.existsSync(resolved) ? fs12.realpathSync.native(resolved) : resolved;
335467
335577
  } catch {
335468
335578
  real = resolved;
335469
335579
  }
335470
- const normalized = path12.normalize(real).replace(/[\\/]+$/, "");
335580
+ const normalized = path13.normalize(real).replace(/[\\/]+$/, "");
335471
335581
  return process.platform === "win32" ? normalized.toLowerCase() : normalized;
335472
335582
  }
335473
335583
  stableWorkspaceId(kind, workspacePath) {
@@ -335493,8 +335603,8 @@ var WorkspaceManager = class {
335493
335603
  isInsideRoot(target) {
335494
335604
  const root2 = this.canonicalWorkspacePath(this.rootPath);
335495
335605
  const candidate = this.canonicalWorkspacePath(target);
335496
- const rel = path12.relative(root2, candidate);
335497
- return rel === "" || !!rel && !rel.startsWith("..") && !path12.isAbsolute(rel);
335606
+ const rel = path13.relative(root2, candidate);
335607
+ return rel === "" || !!rel && !rel.startsWith("..") && !path13.isAbsolute(rel);
335498
335608
  }
335499
335609
  canonicalRemotePath(target) {
335500
335610
  let cleaned = String(target || "").trim().replace(/\\/g, "/").replace(/\/+$/g, "");
@@ -335523,13 +335633,13 @@ var WorkspaceManager = class {
335523
335633
  return deduped;
335524
335634
  }
335525
335635
  statePath() {
335526
- return path12.join(this.rootPath, "Work", "State.json");
335636
+ return path13.join(this.rootPath, "Work", "State.json");
335527
335637
  }
335528
335638
  readState() {
335529
335639
  const p = this.statePath();
335530
- if (!fs11.existsSync(p)) return {};
335640
+ if (!fs12.existsSync(p)) return {};
335531
335641
  try {
335532
- const raw = fs11.readFileSync(p, "utf-8").replace(/^\uFEFF/, "");
335642
+ const raw = fs12.readFileSync(p, "utf-8").replace(/^\uFEFF/, "");
335533
335643
  const parsed = JSON.parse(raw);
335534
335644
  if (parsed && typeof parsed === "object") return parsed;
335535
335645
  } catch {
@@ -335550,8 +335660,8 @@ var WorkspaceManager = class {
335550
335660
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
335551
335661
  };
335552
335662
  try {
335553
- fs11.mkdirSync(path12.dirname(p), { recursive: true });
335554
- fs11.writeFileSync(p, JSON.stringify(state, null, 2), "utf-8");
335663
+ fs12.mkdirSync(path13.dirname(p), { recursive: true });
335664
+ fs12.writeFileSync(p, JSON.stringify(state, null, 2), "utf-8");
335555
335665
  } catch {
335556
335666
  }
335557
335667
  }
@@ -335610,17 +335720,17 @@ var WorkspaceManager = class {
335610
335720
  }
335611
335721
  saveInternal() {
335612
335722
  if (this.detached) return;
335613
- const p = path12.join(this.rootPath, "Work", "Local.json");
335723
+ const p = path13.join(this.rootPath, "Work", "Local.json");
335614
335724
  this.internal = this.dedupeByPath(this.internal);
335615
335725
  this.sortWorkspaces();
335616
- fs11.writeFileSync(p, JSON.stringify(this.internal, null, 2), "utf-8");
335726
+ fs12.writeFileSync(p, JSON.stringify(this.internal, null, 2), "utf-8");
335617
335727
  }
335618
335728
  saveExternal() {
335619
335729
  if (this.detached) return;
335620
- const p = path12.join(this.rootPath, "Work", "External.json");
335730
+ const p = path13.join(this.rootPath, "Work", "External.json");
335621
335731
  this.external = this.dedupeByPath(this.external);
335622
335732
  this.sortWorkspaces();
335623
- fs11.writeFileSync(p, JSON.stringify(this.external, null, 2), "utf-8");
335733
+ fs12.writeFileSync(p, JSON.stringify(this.external, null, 2), "utf-8");
335624
335734
  }
335625
335735
  sleepSync(ms) {
335626
335736
  if (ms <= 0) return;
@@ -335628,48 +335738,48 @@ var WorkspaceManager = class {
335628
335738
  Atomics.wait(new Int32Array(buffer), 0, 0, ms);
335629
335739
  }
335630
335740
  isInternalWorkspacePath(target) {
335631
- const workRoot = path12.resolve(this.rootPath, "Work");
335632
- const resolved = path12.resolve(target);
335633
- const rel = path12.relative(workRoot, resolved);
335634
- return !!rel && !rel.startsWith("..") && !path12.isAbsolute(rel);
335741
+ const workRoot = path13.resolve(this.rootPath, "Work");
335742
+ const resolved = path13.resolve(target);
335743
+ const rel = path13.relative(workRoot, resolved);
335744
+ return !!rel && !rel.startsWith("..") && !path13.isAbsolute(rel);
335635
335745
  }
335636
335746
  clearReadOnlyRecursive(target) {
335637
- if (!fs11.existsSync(target)) return;
335638
- const stat = fs11.lstatSync(target);
335747
+ if (!fs12.existsSync(target)) return;
335748
+ const stat = fs12.lstatSync(target);
335639
335749
  try {
335640
- fs11.chmodSync(target, stat.mode | 448);
335750
+ fs12.chmodSync(target, stat.mode | 448);
335641
335751
  } catch {
335642
335752
  }
335643
335753
  if (!stat.isDirectory()) return;
335644
- for (const entry of fs11.readdirSync(target)) {
335645
- this.clearReadOnlyRecursive(path12.join(target, entry));
335754
+ for (const entry of fs12.readdirSync(target)) {
335755
+ this.clearReadOnlyRecursive(path13.join(target, entry));
335646
335756
  }
335647
335757
  }
335648
335758
  removeInternalDirectory(target) {
335649
- const resolved = path12.resolve(target);
335759
+ const resolved = path13.resolve(target);
335650
335760
  if (!this.isInternalWorkspacePath(resolved)) return false;
335651
- if (!fs11.existsSync(resolved)) return true;
335761
+ if (!fs12.existsSync(resolved)) return true;
335652
335762
  const delays = [0, 50, 100, 200, 400, 800, 1200];
335653
335763
  for (const delay of delays) {
335654
335764
  this.sleepSync(delay);
335655
335765
  try {
335656
- fs11.rmSync(resolved, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
335766
+ fs12.rmSync(resolved, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
335657
335767
  } catch {
335658
335768
  }
335659
- if (!fs11.existsSync(resolved)) return true;
335769
+ if (!fs12.existsSync(resolved)) return true;
335660
335770
  try {
335661
335771
  this.clearReadOnlyRecursive(resolved);
335662
- fs11.rmSync(resolved, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
335772
+ fs12.rmSync(resolved, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
335663
335773
  } catch {
335664
335774
  }
335665
- if (!fs11.existsSync(resolved)) return true;
335775
+ if (!fs12.existsSync(resolved)) return true;
335666
335776
  }
335667
335777
  return false;
335668
335778
  }
335669
335779
  createInternal(name50) {
335670
335780
  const n3 = name50 || (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").replace("T", "_").slice(0, 15);
335671
- const d3 = path12.join(this.rootPath, "Work", n3);
335672
- fs11.mkdirSync(d3, { recursive: true });
335781
+ const d3 = path13.join(this.rootPath, "Work", n3);
335782
+ fs12.mkdirSync(d3, { recursive: true });
335673
335783
  const existing = this.findWorkspaceByPath(d3);
335674
335784
  if (existing) {
335675
335785
  this.current = existing;
@@ -335691,15 +335801,15 @@ var WorkspaceManager = class {
335691
335801
  return ws;
335692
335802
  }
335693
335803
  addExternal(p) {
335694
- const resolved = path12.resolve(p);
335695
- if (!fs11.existsSync(resolved) || this.isInsideRoot(resolved)) return null;
335804
+ const resolved = path13.resolve(p);
335805
+ if (!fs12.existsSync(resolved) || this.isInsideRoot(resolved)) return null;
335696
335806
  const existing = this.findWorkspaceByPath(resolved);
335697
335807
  if (existing) {
335698
335808
  this.current = existing;
335699
335809
  this.saveState();
335700
335810
  return existing;
335701
335811
  }
335702
- const name50 = path12.basename(resolved);
335812
+ const name50 = path13.basename(resolved);
335703
335813
  const ws = {
335704
335814
  id: this.stableWorkspaceId("local", resolved),
335705
335815
  name: name50,
@@ -335717,10 +335827,10 @@ var WorkspaceManager = class {
335717
335827
  addSshExternal(input) {
335718
335828
  if (!input.sshConnectionId || !input.remotePath || !input.remotePcHash) return null;
335719
335829
  const remotePath = this.canonicalRemotePath(input.remotePath);
335720
- const baseName = (input.name || path12.basename(remotePath.replace(/[\\/]+$/, "")) || input.sshConnectionId || "ssh-workspace").trim();
335830
+ const baseName = (input.name || path13.basename(remotePath.replace(/[\\/]+$/, "")) || input.sshConnectionId || "ssh-workspace").trim();
335721
335831
  const safeName = baseName.replace(/[<>:"/\\|?*\x00-\x1F]/g, "-").replace(/\s+/g, " ").trim() || "ssh-workspace";
335722
- const shadowRoot = input.localPath ? path12.resolve(input.localPath) : path12.join(this.rootPath, "Work", ".ssh", `${input.sshConnectionId}-${crypto7.createHash("sha256").update(remotePath).digest("hex").slice(0, 16)}`);
335723
- fs11.mkdirSync(shadowRoot, { recursive: true });
335832
+ const shadowRoot = input.localPath ? path13.resolve(input.localPath) : path13.join(this.rootPath, "Work", ".ssh", `${input.sshConnectionId}-${crypto7.createHash("sha256").update(remotePath).digest("hex").slice(0, 16)}`);
335833
+ fs12.mkdirSync(shadowRoot, { recursive: true });
335724
335834
  const existing = this.findSshWorkspaceByRemotePath(input.sshConnectionId, remotePath);
335725
335835
  const ws = {
335726
335836
  ...existing || {},
@@ -335802,7 +335912,7 @@ var WorkspaceManager = class {
335802
335912
  currentAgentPrompt() {
335803
335913
  if (!this.current) return null;
335804
335914
  try {
335805
- return fs11.readFileSync(path12.join(this.current.path, "agent.md"), "utf-8");
335915
+ return fs12.readFileSync(path13.join(this.current.path, "agent.md"), "utf-8");
335806
335916
  } catch {
335807
335917
  return null;
335808
335918
  }
@@ -335811,8 +335921,8 @@ var WorkspaceManager = class {
335811
335921
  const perm = this.config.getStr("workspace", "access_permission");
335812
335922
  if (perm === "full_access") return true;
335813
335923
  if (!this.current) return perm !== "no_outside_access";
335814
- const rel = path12.relative(path12.resolve(this.current.path), path12.resolve(target));
335815
- const inside2 = rel === "" || !!rel && !rel.startsWith("..") && !path12.isAbsolute(rel);
335924
+ const rel = path13.relative(path13.resolve(this.current.path), path13.resolve(target));
335925
+ const inside2 = rel === "" || !!rel && !rel.startsWith("..") && !path13.isAbsolute(rel);
335816
335926
  if (inside2) return true;
335817
335927
  return perm !== "no_outside_access";
335818
335928
  }
@@ -335895,6 +336005,7 @@ var PLAN_COMPUTER_USE_ACTIONS = ["observe", "app_list", "app_observe"];
335895
336005
  var PLAN_BROWSER_USE_ACTIONS = ["observe", "navigate", "wait", "extract"];
335896
336006
  var PLAN_COMPUTER_USE_ACTION_SET = new Set(PLAN_COMPUTER_USE_ACTIONS);
335897
336007
  var PLAN_BROWSER_USE_ACTION_SET = new Set(PLAN_BROWSER_USE_ACTIONS);
336008
+ var CHAT_WEB_TOOLS = /* @__PURE__ */ new Set(["web_search", "web_fetch"]);
335898
336009
  var CONCURRENCY_SAFE_TOOLS = /* @__PURE__ */ new Set([
335899
336010
  "pwd",
335900
336011
  "read",
@@ -335928,6 +336039,13 @@ function evaluateToolPolicy(request) {
335928
336039
  const availability = toolAvailability(name50);
335929
336040
  const base2 = { availability, settingsVisible: availability === "configurable" };
335930
336041
  if (!name50) return { ...base2, allowed: false, reason: "[permission] Tool name is required." };
336042
+ if (request.mode === "chat" && !CHAT_WEB_TOOLS.has(name50)) {
336043
+ return {
336044
+ ...base2,
336045
+ allowed: false,
336046
+ reason: `[permission] Chat mode only allows web_search and web_fetch. It has no workspace, host, application, memory, task, or other write access. Blocked: ${name50}`
336047
+ };
336048
+ }
335931
336049
  if (request.mode === "plan") {
335932
336050
  if (name50 === "computer_use") {
335933
336051
  const action = String(request.args?.action || "").trim();
@@ -335969,6 +336087,13 @@ function planModePolicyPrompt() {
335969
336087
  "Runtime policy rejects stale or hidden mutating tool calls even if a prompt asks for them."
335970
336088
  ].join(" ");
335971
336089
  }
336090
+ function chatModePolicyPrompt() {
336091
+ return [
336092
+ "Chat mode is a narrow web-evidence mode.",
336093
+ "Only web_search and web_fetch are available; every workspace, host, application, memory, task, browser-control, and write capability is denied at runtime.",
336094
+ "Search the web for relevant evidence, fetch primary or authoritative sources when useful, then summarize and answer promptly instead of expanding into a long-running task."
336095
+ ].join(" ");
336096
+ }
335972
336097
  var DELETE_VERB_SOURCE = "(?:remove-item|rmdir|unlink|erase|del|rm|rd|ri)";
335973
336098
  var DELETE_VERB_BOUNDARY = new RegExp(`(?:^|[\\s;&|()\\n])${DELETE_VERB_SOURCE}(?:\\s|$)`, "i");
335974
336099
  function hasDeletionVerb(text) {
@@ -336250,7 +336375,7 @@ function rejectPendingUtilityHostTools(reason) {
336250
336375
  }
336251
336376
 
336252
336377
  // src/core/nativeBash.ts
336253
- var path13 = __toESM(require("path"));
336378
+ var path14 = __toESM(require("path"));
336254
336379
  var import_module = require("module");
336255
336380
  var MAX_OUTPUT_BYTES = 1024 * 1024;
336256
336381
  var DEFAULT_TIMEOUT_MS = 3e4;
@@ -336338,11 +336463,11 @@ function normalizedTimeout(timeoutMs) {
336338
336463
  }
336339
336464
  function virtualCwd(workspaceRoot, requestedCwd) {
336340
336465
  if (!requestedCwd) return "/";
336341
- const root2 = path13.resolve(workspaceRoot);
336342
- const cwd = path13.resolve(requestedCwd);
336343
- const relative6 = path13.relative(root2, cwd);
336344
- if (relative6.startsWith("..") || path13.isAbsolute(relative6)) return "/";
336345
- return relative6 ? `/${relative6.split(path13.sep).join("/")}` : "/";
336466
+ const root2 = path14.resolve(workspaceRoot);
336467
+ const cwd = path14.resolve(requestedCwd);
336468
+ const relative6 = path14.relative(root2, cwd);
336469
+ if (relative6.startsWith("..") || path14.isAbsolute(relative6)) return "/";
336470
+ return relative6 ? `/${relative6.split(path14.sep).join("/")}` : "/";
336346
336471
  }
336347
336472
  function combineAbortSignals(signal, timeoutMs) {
336348
336473
  const controller = new AbortController();
@@ -336368,7 +336493,7 @@ function createBash(workspaceRoot, timeoutMs) {
336368
336493
  const justBash = loadJustBash();
336369
336494
  if (!justBash) throw new Error("Native Bash runtime unavailable");
336370
336495
  const fs26 = new justBash.ReadWriteFs({
336371
- root: path13.resolve(workspaceRoot),
336496
+ root: path14.resolve(workspaceRoot),
336372
336497
  maxFileReadSize: MAX_OUTPUT_BYTES * 8,
336373
336498
  allowSymlinks: false
336374
336499
  });
@@ -336457,12 +336582,12 @@ async function executeWorkspaceBash(script, workspaceRoot, options = {}) {
336457
336582
  }
336458
336583
 
336459
336584
  // src/core/toolArgumentValidator.ts
336460
- var fs12 = require("fs");
336461
- var path14 = require("path");
336585
+ var fs13 = require("fs");
336586
+ var path15 = require("path");
336462
336587
  var typeBoxCompilerPath = [
336463
- path14.join(__dirname, "..", "typebox-compile.bundle.cjs"),
336464
- path14.join(__dirname, "typebox-compile.bundle.cjs")
336465
- ].find((candidate) => fs12.existsSync(candidate));
336588
+ path15.join(__dirname, "..", "typebox-compile.bundle.cjs"),
336589
+ path15.join(__dirname, "typebox-compile.bundle.cjs")
336590
+ ].find((candidate) => fs13.existsSync(candidate));
336466
336591
  if (!typeBoxCompilerPath) throw new Error("Bundled TypeBox compiler is missing from the Newmark runtime.");
336467
336592
  var { Compile } = require(typeBoxCompilerPath);
336468
336593
  function closeToolArgumentSchema(input) {
@@ -336552,8 +336677,8 @@ function formatValidationErrors(name50, errors) {
336552
336677
  }
336553
336678
 
336554
336679
  // src/core/localOcr.ts
336555
- var fs13 = __toESM(require("fs"));
336556
- var path15 = __toESM(require("path"));
336680
+ var fs14 = __toESM(require("fs"));
336681
+ var path16 = __toESM(require("path"));
336557
336682
  var AGENT_REPAIR_PROMPT = [
336558
336683
  "The local OCR output is approximate Chinese/English fallback evidence.",
336559
336684
  "Repair likely OCR substitutions, spacing, and line breaks using the visible UI/PDF context and the user task.",
@@ -336586,12 +336711,12 @@ var LocalOcrEngine = class {
336586
336711
  return await this.recognize(dataUrlBuffer(dataUrl), signal, profile);
336587
336712
  }
336588
336713
  async recognizeFile(filePath, signal) {
336589
- const absolute = path15.resolve(filePath);
336590
- const extension = path15.extname(absolute).toLowerCase();
336714
+ const absolute = path16.resolve(filePath);
336715
+ const extension = path16.extname(absolute).toLowerCase();
336591
336716
  if (![".png", ".jpg", ".jpeg", ".bmp"].includes(extension)) {
336592
336717
  throw new Error("Local OCR only accepts PNG, JPEG, or BMP images.");
336593
336718
  }
336594
- const stat = fs13.statSync(absolute);
336719
+ const stat = fs14.statSync(absolute);
336595
336720
  if (!stat.isFile() || stat.size <= 0 || stat.size > 12 * 1024 * 1024) {
336596
336721
  throw new Error("Local OCR image must be a regular file no larger than 12 MB.");
336597
336722
  }
@@ -336645,7 +336770,7 @@ var LocalOcrEngine = class {
336645
336770
  const tesseract = require_src();
336646
336771
  const worker = await tesseract.createWorker("chi_sim+eng", tesseract.OEM.LSTM_ONLY, {
336647
336772
  langPath: tessdataPath,
336648
- cachePath: path15.join(this.rootPath, "cache", "ocr-runtime"),
336773
+ cachePath: path16.join(this.rootPath, "cache", "ocr-runtime"),
336649
336774
  cacheMethod: "none",
336650
336775
  gzip: true,
336651
336776
  logger: () => void 0
@@ -336653,14 +336778,14 @@ var LocalOcrEngine = class {
336653
336778
  return worker;
336654
336779
  }
336655
336780
  prepareLanguageCache() {
336656
- const target = path15.join(this.rootPath, "cache", "ocr-tessdata");
336657
- fs13.mkdirSync(target, { recursive: true });
336781
+ const target = path16.join(this.rootPath, "cache", "ocr-tessdata");
336782
+ fs14.mkdirSync(target, { recursive: true });
336658
336783
  for (const language of ["eng", "chi_sim"]) {
336659
- const destination = path15.join(target, `${language}.traineddata.gz`);
336660
- if (fs13.existsSync(destination) && fs13.statSync(destination).size > 0) continue;
336661
- const packageRoot = path15.dirname(require.resolve(`@tesseract.js-data/${language}/package.json`));
336662
- const source = path15.join(packageRoot, "4.0.0_best_int", `${language}.traineddata.gz`);
336663
- fs13.copyFileSync(source, destination);
336784
+ const destination = path16.join(target, `${language}.traineddata.gz`);
336785
+ if (fs14.existsSync(destination) && fs14.statSync(destination).size > 0) continue;
336786
+ const packageRoot = path16.dirname(require.resolve(`@tesseract.js-data/${language}/package.json`));
336787
+ const source = path16.join(packageRoot, "4.0.0_best_int", `${language}.traineddata.gz`);
336788
+ fs14.copyFileSync(source, destination);
336664
336789
  }
336665
336790
  return target;
336666
336791
  }
@@ -336799,8 +336924,8 @@ function normalizeCrossEnvPath(value, wsPath) {
336799
336924
  const posix3 = windowsDrivePathToPosix(raw);
336800
336925
  if (posix3) return posix3;
336801
336926
  }
336802
- if (path16.isAbsolute(raw)) return raw;
336803
- return path16.join(wsPath, raw);
336927
+ if (path17.isAbsolute(raw)) return raw;
336928
+ return path17.join(wsPath, raw);
336804
336929
  }
336805
336930
  function translateWindowsPathsForWslBash(script) {
336806
336931
  if (!process.env.NEWMARK_WSL_DISTRO) return script;
@@ -336814,7 +336939,7 @@ function translateWindowsPathsForWslBash(script) {
336814
336939
  function computerUseOwner(context, wsPath) {
336815
336940
  const conversationId = String(context.conversationId || "").trim();
336816
336941
  if (conversationId) return `conversation:${conversationId}`;
336817
- const resolved = path16.resolve(context.workspacePath || wsPath || process.cwd());
336942
+ const resolved = path17.resolve(context.workspacePath || wsPath || process.cwd());
336818
336943
  const workspaceHash = crypto8.createHash("sha256").update(resolved).digest("hex").slice(0, 12);
336819
336944
  return `direct:${workspaceHash}`;
336820
336945
  }
@@ -336896,7 +337021,7 @@ function computerUseSessionScope(context, wsPath, owner) {
336896
337021
  return {
336897
337022
  runtimeKey: browserUseScope(context, wsPath).runtimeKey,
336898
337023
  ownerLabel: owner,
336899
- workspacePath: path16.resolve(wsPath || process.cwd())
337024
+ workspacePath: path17.resolve(wsPath || process.cwd())
336900
337025
  };
336901
337026
  }
336902
337027
  function acquireComputerUseLock(action, owner, wsPath, context = {}, dryRun = false) {
@@ -337091,7 +337216,7 @@ var ToolExecutor = class {
337091
337216
  t3("subagent_send", "Persist a mailbox message to a same-conversation peer agent. Target by exact id (preferred) or name.", { id: { type: "string", description: "Exact peer id from subagent_list." }, name: { type: "string", description: "Convenience peer name." }, message: { type: "string" }, prompt: { type: "string", description: "Legacy alias for message." }, kind: { type: "string", enum: ["directive", "question", "result", "handoff"] }, reply_to: { type: "string" }, correlation_id: { type: "string" } }, []),
337092
337217
  t3("subagent_result", "Return the persisted transcript, mailbox summary, status, and latest result for a peer agent. Target by exact id (preferred) or name.", { id: { type: "string", description: "Exact peer id from subagent_list." }, name: { type: "string", description: "Convenience peer name." } }, []),
337093
337218
  t3("subagent_close", "Close a same-conversation peer. Root can close any peer; a peer can close only itself. Target by exact id (preferred) or name.", { id: { type: "string", description: "Exact peer id from subagent_list." }, name: { type: "string", description: "Convenience peer name." } }, []),
337094
- t3("linked_plan", "Read or update the current conversation linked Markdown plan. Update requires the current expected_revision.", { action: { type: "string", enum: ["get", "update"] }, markdown: { type: "string" }, expected_revision: { type: "number" } }, ["action"]),
337219
+ t3("linked_plan", "Read or incrementally update the current conversation linked Markdown plan. Update requires expected_revision. Prefer append or old_text/new_text for local changes; markdown remains the legacy full replacement path.", { action: { type: "string", enum: ["get", "update"] }, markdown: { type: "string" }, append: { type: "string" }, old_text: { type: "string" }, new_text: { type: "string" }, replace_all: { type: "boolean" }, expected_revision: { type: "number" } }, ["action"]),
337095
337220
  t3("build_history_query", "Read the concrete public work details (tool calls, results, file changes, guides) of one historical Build Block. Call it proactively when the current task continues, fixes, verifies, or depends on earlier work: reuse the returned activity instead of re-investigating from scratch. Do not call it merely to answer completion status already exposed by the prompt. Select by newest-to-oldest history_index, or by run_id returned from an earlier query. Every activity/guide content is bounded to max_chars (default 2000) to keep the read lean and cache-friendly.", { history_index: { type: "number", minimum: 1, description: "1-based historical Build Block index from the request ledger; 1 is the newest previous task." }, run_id: { type: "string", description: "Exact run id returned by an earlier build_history_query result." }, max_events: { type: "number", minimum: 1, maximum: 200, description: "Maximum trailing public work events; defaults to 80." }, max_chars: { type: "number", minimum: 100, maximum: 4e3, description: "Per-event/per-guide content character bound; defaults to 2000." } }, []),
337096
337221
  t3("context_compress", "Actively compress the LLM context history for this conversation. This collapses older history entries into a concise summary while preserving the recent tail, which reduces context tokens and cost. IMPORTANT: it affects only the LLM context (what the model sees); the displayed conversation history shown to the user is never altered. Call this when the conversation is long, token pressure is high, or you judge that older turns are no longer needed in full. Idempotent and safe: repeated calls produce incremental summaries.", { keep_recent: { type: "number", minimum: 2, maximum: 60, description: "Recent message count to keep uncompressed at the tail. Defaults to the configured keep_recent_messages." }, force: { type: "boolean", description: "Compress even if the context is not yet over the automatic threshold. Defaults to false." } }, []),
337097
337222
  t3("context_history_manage", "Manage the LLM context history for this conversation without affecting the displayed conversation history. This is the active context-management surface. The hot cache stays bounded; evicted folded segments remain in a conversation-isolated append-only cold archive and are loaded only by explicit search/read/restore calls. Actions: list returns a bounded index of current context entries; remove declares one long-term entry for unload (see below); summarize folds a contiguous current range; restore reinserts a folded segment when its summary marker is still present; search finds matching hot or archived segments; read returns one bounded segment without injecting the whole archive; status reports budgets, hot cache, cold archive, the protected recent zone, and pending removals. The recent context tail and last user message are protected from remove/summarize unless dangerous is true. For cache-optimization, remove ONLY targets long-term history (never the protected recent tail or last user message) and does NOT unload immediately: the declared entry stays in context for the rest of the current Build Block so the provider prefix cache stays stable, then is physically removed when the Block ends \u2014 applying to subsequent Blocks only.", {
@@ -337121,11 +337246,11 @@ var ToolExecutor = class {
337121
337246
  t3("skill_download", "Download a skill", { name: { type: "string" }, source: { type: "string" } }, ["name", "source"]),
337122
337247
  t3("skill", "Search enabled skill metadata or load one exact skill body on demand. Use query when unsure, then name to load the selected skill.", { query: { type: "string", maxLength: 200 }, name: { type: "string", maxLength: 200 } }, []),
337123
337248
  t3("flow_list", "List available Newmark Flow workflows from the Flow folder so the agent can choose one.", {}, []),
337124
- t3("flow_save", "Design or update a Newmark Flow workflow. Components must be an array of dialog/logic objects compatible with *.Flow.json.", { name: { type: "string" }, components: { type: "array" } }, ["name", "components"]),
337249
+ t3("flow_save", "Create or incrementally update a Newmark Flow workflow. Use action=upsert with one component or action=delete with component_id and confirm=true for local edits. action=replace plus components remains the legacy full replacement path.", { name: { type: "string" }, action: { type: "string", enum: ["replace", "upsert", "delete"] }, components: { type: "array" }, component: { type: "object" }, component_id: { type: "number" }, confirm: { type: "boolean" } }, ["name"]),
337125
337250
  t3("flow_run", "Trigger an existing Newmark Flow workflow by name with optional input and start component.", { name: { type: "string" }, input: { type: "string" }, start: { type: "number" } }, ["name"]),
337126
337251
  t3("memory_lab_read", "Read Memory Lab index.json, its path, and usage instructions. Optionally pass component/name/slug to read a memory component core markdown.", { component: { type: "string" }, name: { type: "string" }, slug: { type: "string" } }, []),
337127
337252
  t3("memory_lab_query", "Retrieve a bounded task-relevant Memory Lab set with deterministic scoring and adaptive early stopping. Prefer this over loading the complete index when a focused query is sufficient.", { query: { type: "string", minLength: 1 }, limit: { type: "number", minimum: 1, maximum: 12 }, max_chars: { type: "number", minimum: 1e3, maximum: 48e3 } }, ["query"]),
337128
- t3("memory_lab_update", "ADD or UPDATE a Memory Lab component. Existing memory should include expectedUpdatedAt from the latest read/query so stale writes fail closed. Prior revisions are archived and the Policy decision is logged.", { name: { type: "string" }, description: { type: "string" }, tags: { type: "array", items: { type: "string" } }, tagPaths: { type: "array", items: { type: "array", items: { type: "string" } } }, content: { type: "string" }, kind: { type: "string", enum: ["file", "folder"] }, expectedUpdatedAt: { type: "string" }, reason: { type: "string" }, source: { type: "string" } }, ["name", "tags", "content"]),
337253
+ t3("memory_lab_update", "Create or incrementally patch a Memory Lab component. Create with name/tags/content. For an existing component pass component plus expectedUpdatedAt and only changed fields; prefer contentAppend or oldText/newText for small body edits. Prior revisions are archived and stale writes fail closed.", { component: { type: "string" }, name: { type: "string" }, description: { type: "string" }, tags: { type: "array", items: { type: "string" } }, tagPaths: { type: "array", items: { type: "array", items: { type: "string" } } }, content: { type: "string" }, contentAppend: { type: "string" }, oldText: { type: "string" }, newText: { type: "string" }, replaceAll: { type: "boolean" }, kind: { type: "string", enum: ["file", "folder"] }, expectedUpdatedAt: { type: "string" }, reason: { type: "string" }, source: { type: "string" } }, []),
337129
337254
  t3("memory_lab_delete", "DELETE obsolete durable memory only when the user explicitly asks to forget/remove it. The prior revision is moved to Memory Lab/archive and the Policy decision is logged.", { component: { type: "string" }, name: { type: "string" }, slug: { type: "string" }, expectedUpdatedAt: { type: "string" }, reason: { type: "string" }, source: { type: "string" } }, []),
337130
337255
  t3("memory_lab_reindex", "Rebuild and organize Memory Lab index links. Routed through Agent runtime when invoked by the model.", {}, []),
337131
337256
  t3("automation_list", "List persisted Newmark automations so the agent can inspect scheduled work.", {}, []),
@@ -337447,13 +337572,13 @@ var ToolExecutor = class {
337447
337572
  }
337448
337573
  case "pdf_read": {
337449
337574
  const pdfPath = resolve16(g2("path"));
337450
- if (path16.extname(pdfPath).toLowerCase() !== ".pdf") return "[pdf_read error] path must end in .pdf.";
337451
- const stat = fs14.statSync(pdfPath);
337575
+ if (path17.extname(pdfPath).toLowerCase() !== ".pdf") return "[pdf_read error] path must end in .pdf.";
337576
+ const stat = fs15.statSync(pdfPath);
337452
337577
  if (!stat.isFile() || stat.size <= 0 || stat.size > 250 * 1024 * 1024) {
337453
337578
  return "[pdf_read error] PDF must be a regular file no larger than 250 MB.";
337454
337579
  }
337455
337580
  const maxChars = Math.max(500, Math.min(1e5, Number(args.max_chars || 5e4)));
337456
- const textLayer = extractPdfTextLayer(fs14.readFileSync(pdfPath)).slice(0, maxChars);
337581
+ const textLayer = extractPdfTextLayer(fs15.readFileSync(pdfPath)).slice(0, maxChars);
337457
337582
  const readableCount = (textLayer.match(/[A-Za-z0-9\u3400-\u9fff]/g) || []).length;
337458
337583
  if (readableCount >= 20) {
337459
337584
  return JSON.stringify({
@@ -337672,7 +337797,7 @@ var ToolExecutor = class {
337672
337797
  case "flow_list":
337673
337798
  return this.flowList();
337674
337799
  case "flow_save":
337675
- return this.flowSave(g2("name"), args.components);
337800
+ return this.flowSave(g2("name"), args);
337676
337801
  case "flow_run":
337677
337802
  return `[flow_run] Routed to Agent runtime: ${g2("name")}`;
337678
337803
  case "memory_lab_read":
@@ -337729,8 +337854,8 @@ var ToolExecutor = class {
337729
337854
  }
337730
337855
  }
337731
337856
  isInside(parent, child) {
337732
- const rel = path16.relative(path16.resolve(parent), path16.resolve(child));
337733
- return rel === "" || !!rel && !rel.startsWith("..") && !path16.isAbsolute(rel);
337857
+ const rel = path17.relative(path17.resolve(parent), path17.resolve(child));
337858
+ return rel === "" || !!rel && !rel.startsWith("..") && !path17.isAbsolute(rel);
337734
337859
  }
337735
337860
  hostSupportsTool(name50) {
337736
337861
  if (name50.startsWith("browser_") && !this.hostProfile.electronBrowser) return false;
@@ -337842,7 +337967,7 @@ var ToolExecutor = class {
337842
337967
  if (!token || /^https?:\/\//i.test(token) || token.startsWith("-")) continue;
337843
337968
  if (!this.looksLikePath(token)) continue;
337844
337969
  const withoutWildcard = token.replace(/[\\/][*?][^\\/]*$/g, "");
337845
- refs.push(path16.resolve(normalizeCrossEnvPath(withoutWildcard, wsPath)));
337970
+ refs.push(path17.resolve(normalizeCrossEnvPath(withoutWildcard, wsPath)));
337846
337971
  }
337847
337972
  return Array.from(new Set(refs));
337848
337973
  }
@@ -337893,7 +338018,7 @@ var ToolExecutor = class {
337893
338018
  }
337894
338019
  fread(p) {
337895
338020
  try {
337896
- const c3 = fs14.readFileSync(p, "utf-8");
338021
+ const c3 = fs15.readFileSync(p, "utf-8");
337897
338022
  return c3.length > 3e4 ? c3.slice(0, 3e4) + "...\n[truncated]" : c3;
337898
338023
  } catch (e3) {
337899
338024
  return `[read] ${e3}`;
@@ -337901,8 +338026,8 @@ var ToolExecutor = class {
337901
338026
  }
337902
338027
  fwrite(p, content) {
337903
338028
  try {
337904
- fs14.mkdirSync(path16.dirname(p), { recursive: true });
337905
- fs14.writeFileSync(p, content, "utf-8");
338029
+ fs15.mkdirSync(path17.dirname(p), { recursive: true });
338030
+ fs15.writeFileSync(p, content, "utf-8");
337906
338031
  return `[write] OK: ${p}`;
337907
338032
  } catch (e3) {
337908
338033
  return `[write] ${e3}`;
@@ -337910,10 +338035,10 @@ var ToolExecutor = class {
337910
338035
  }
337911
338036
  fedit(p, oldStr, newStr) {
337912
338037
  try {
337913
- const c3 = fs14.readFileSync(p, "utf-8");
338038
+ const c3 = fs15.readFileSync(p, "utf-8");
337914
338039
  if (!c3.includes(oldStr)) return `[edit] String not found in ${p}.`;
337915
338040
  const updated = c3.replace(oldStr, newStr);
337916
- fs14.writeFileSync(p, updated, "utf-8");
338041
+ fs15.writeFileSync(p, updated, "utf-8");
337917
338042
  return `[edit] OK: ${p}`;
337918
338043
  } catch (e3) {
337919
338044
  return `[edit] ${e3}`;
@@ -337922,12 +338047,12 @@ var ToolExecutor = class {
337922
338047
  fdelete(p) {
337923
338048
  try {
337924
338049
  if (/[*?]/.test(p)) return "[delete_file] Refused: wildcard paths are not allowed. Delete one file per call.";
337925
- const resolved = path16.resolve(p);
337926
- const stat = fs14.lstatSync(resolved);
338050
+ const resolved = path17.resolve(p);
338051
+ const stat = fs15.lstatSync(resolved);
337927
338052
  if (stat.isDirectory()) {
337928
338053
  return "[delete_file] Refused: deleting a directory is not allowed. Delete files one by one under Agent supervision.";
337929
338054
  }
337930
- fs14.unlinkSync(resolved);
338055
+ fs15.unlinkSync(resolved);
337931
338056
  return `[delete_file] OK: ${resolved}`;
337932
338057
  } catch (e3) {
337933
338058
  return `[delete_file] ${e3 instanceof Error ? e3.message : String(e3)}`;
@@ -337951,11 +338076,11 @@ var ToolExecutor = class {
337951
338076
  const results = [];
337952
338077
  const walk4 = (d3, depth) => {
337953
338078
  if (depth > 5 || results.length >= 80) return;
337954
- for (const entry of fs14.readdirSync(d3, { withFileTypes: true })) {
337955
- const full = path16.join(d3, entry.name);
338079
+ for (const entry of fs15.readdirSync(d3, { withFileTypes: true })) {
338080
+ const full = path17.join(d3, entry.name);
337956
338081
  if (entry.isFile()) {
337957
338082
  try {
337958
- const content = fs14.readFileSync(full, "utf-8");
338083
+ const content = fs15.readFileSync(full, "utf-8");
337959
338084
  for (const [i4, line] of content.split("\n").entries()) {
337960
338085
  if (re.test(line)) {
337961
338086
  results.push(`${entry.name}:${i4 + 1}:${line.trim()}`);
@@ -338171,29 +338296,53 @@ ${String(result.data)}`);
338171
338296
  try {
338172
338297
  const resp = await this.proxyFetch(src, { signal });
338173
338298
  const content = await resp.text();
338174
- const dir = path16.join(this.root, "skills", name50);
338175
- fs14.mkdirSync(dir, { recursive: true });
338176
- fs14.writeFileSync(path16.join(dir, "SKILL.md"), content, "utf-8");
338299
+ const dir = path17.join(this.root, "skills", name50);
338300
+ fs15.mkdirSync(dir, { recursive: true });
338301
+ fs15.writeFileSync(path17.join(dir, "SKILL.md"), content, "utf-8");
338177
338302
  return `[skill] Downloaded '${name50}'`;
338178
338303
  } catch (e3) {
338179
338304
  return `[skill] ${e3}`;
338180
338305
  }
338181
338306
  }
338182
338307
  flowList() {
338183
- const dir = path16.join(this.root, "Flow");
338308
+ const dir = path17.join(this.root, "Flow");
338184
338309
  try {
338185
- const files = fs14.readdirSync(dir).filter((f3) => f3.endsWith(".Flow.json")).sort();
338310
+ const files = fs15.readdirSync(dir).filter((f3) => f3.endsWith(".Flow.json")).sort();
338186
338311
  if (!files.length) return "[flow_list] No workflows found.";
338187
338312
  return files.map((f3) => f3.replace(/\.Flow\.json$/, "")).join("\n");
338188
338313
  } catch (e3) {
338189
338314
  return `[flow_list] ${e3}`;
338190
338315
  }
338191
338316
  }
338192
- flowSave(name50, componentsRaw) {
338317
+ flowSave(name50, input) {
338193
338318
  const cleanName = (name50 || "").replace(/[<>:"/\\|?*]/g, "-").trim();
338194
338319
  if (!cleanName) return "[flow_save] Workflow name is required.";
338195
- if (!Array.isArray(componentsRaw)) return "[flow_save] components must be an array.";
338196
- const components = componentsRaw.map((raw, idx) => {
338320
+ const dir = path17.join(this.root, "Flow");
338321
+ const target = path17.join(dir, `${cleanName}.Flow.json`);
338322
+ const action = String(input.action || (Array.isArray(input.components) ? "replace" : "upsert")).toLowerCase();
338323
+ let componentsRaw = input.components;
338324
+ if (action === "upsert") {
338325
+ if (!input.component || typeof input.component !== "object") return "[flow_save] component is required for action=upsert.";
338326
+ const existing = FlowEngine.load(dir, cleanName)?.components || [];
338327
+ const component = input.component;
338328
+ const requestedId = Number(component.id);
338329
+ if (!Number.isFinite(requestedId)) return "[flow_save] component.id is required for action=upsert.";
338330
+ componentsRaw = [...existing.filter((item) => item.id !== requestedId), component].sort((a3, b2) => Number(a3.id) - Number(b2.id));
338331
+ } else if (action === "delete") {
338332
+ if (input.confirm !== true) return "[flow_save] action=delete requires confirm=true.";
338333
+ const componentId = Number(input.component_id);
338334
+ if (!Number.isFinite(componentId)) return "[flow_save] component_id is required for action=delete.";
338335
+ const existing = FlowEngine.load(dir, cleanName);
338336
+ if (!existing) return `[flow_save] Workflow not found: ${cleanName}`;
338337
+ const remaining = existing.components.filter((item) => item.id !== componentId);
338338
+ if (remaining.length === existing.components.length) return `[flow_save] Component not found: ${componentId}`;
338339
+ componentsRaw = remaining;
338340
+ } else if (action !== "replace") {
338341
+ return `[flow_save] Unknown action: ${action}`;
338342
+ }
338343
+ if (!Array.isArray(componentsRaw)) return "[flow_save] components must be an array for action=replace.";
338344
+ const componentInputs = componentsRaw;
338345
+ const components = componentInputs.map((raw, idx) => {
338197
338346
  const c3 = raw;
338198
338347
  const type = c3.type === "logic" ? "logic" : "dialog";
338199
338348
  if (type === "logic") {
@@ -338214,10 +338363,9 @@ ${String(result.data)}`);
338214
338363
  };
338215
338364
  });
338216
338365
  const workflow = { name: cleanName, components };
338217
- const dir = path16.join(this.root, "Flow");
338218
- fs14.mkdirSync(dir, { recursive: true });
338219
- fs14.writeFileSync(path16.join(dir, `${cleanName}.Flow.json`), JSON.stringify(workflow, null, 2), "utf-8");
338220
- return `[flow_save] OK: ${cleanName}.Flow.json`;
338366
+ fs15.mkdirSync(dir, { recursive: true });
338367
+ fs15.writeFileSync(target, JSON.stringify(workflow, null, 2), "utf-8");
338368
+ return `[flow_save] OK (${action}): ${cleanName}.Flow.json`;
338221
338369
  }
338222
338370
  memoryLabRead(selector2) {
338223
338371
  const lab2 = new MemoryLabManager(this.root);
@@ -338277,10 +338425,10 @@ ${String(result.data)}`);
338277
338425
  return this.gh(args, ws, signal);
338278
338426
  }
338279
338427
  async fileAudit(target, ws, includeRemote, baseRef, signal) {
338280
- const resolvedTarget = path16.resolve(target || ws);
338281
- const exists = fs14.existsSync(resolvedTarget);
338282
- const stat = exists ? fs14.statSync(resolvedTarget) : null;
338283
- const repoRoot = await this.findGitRoot(exists && stat?.isDirectory() ? resolvedTarget : path16.dirname(resolvedTarget), ws, signal);
338428
+ const resolvedTarget = path17.resolve(target || ws);
338429
+ const exists = fs15.existsSync(resolvedTarget);
338430
+ const stat = exists ? fs15.statSync(resolvedTarget) : null;
338431
+ const repoRoot = await this.findGitRoot(exists && stat?.isDirectory() ? resolvedTarget : path17.dirname(resolvedTarget), ws, signal);
338284
338432
  const audit = {
338285
338433
  ok: true,
338286
338434
  target: resolvedTarget,
@@ -338307,11 +338455,11 @@ ${String(result.data)}`);
338307
338455
  };
338308
338456
  if (stat.isFile()) {
338309
338457
  const hash = crypto8.createHash("sha256");
338310
- hash.update(fs14.readFileSync(target));
338458
+ hash.update(fs15.readFileSync(target));
338311
338459
  base2.sha256 = hash.digest("hex").toUpperCase();
338312
338460
  }
338313
338461
  if (stat.isDirectory()) {
338314
- base2.entries = fs14.readdirSync(target).slice(0, 200).sort();
338462
+ base2.entries = fs15.readdirSync(target).slice(0, 200).sort();
338315
338463
  }
338316
338464
  return base2;
338317
338465
  }
@@ -338320,14 +338468,14 @@ ${String(result.data)}`);
338320
338468
  const out = await this.gitExecAt(candidate, ["rev-parse", "--show-toplevel"], signal);
338321
338469
  if (!out.startsWith("[git]") && !out.includes("not a git repository")) {
338322
338470
  const root2 = out.split(/\r?\n/)[0].trim();
338323
- if (root2 && fs14.existsSync(root2)) return path16.resolve(root2);
338471
+ if (root2 && fs15.existsSync(root2)) return path17.resolve(root2);
338324
338472
  }
338325
338473
  }
338326
338474
  return null;
338327
338475
  }
338328
338476
  async gitFileAudit(repoRoot, target, baseRef, signal) {
338329
- const rel = path16.relative(repoRoot, target).replace(/\\/g, "/");
338330
- const inside2 = rel === "" || !!rel && !rel.startsWith("..") && !path16.isAbsolute(rel);
338477
+ const rel = path17.relative(repoRoot, target).replace(/\\/g, "/");
338478
+ const inside2 = rel === "" || !!rel && !rel.startsWith("..") && !path17.isAbsolute(rel);
338331
338479
  if (!inside2) return { repository: repoRoot, tracked: false, note: "Path is outside the detected repository." };
338332
338480
  const branch = await this.gitExecAt(repoRoot, ["branch", "--show-current"], signal);
338333
338481
  const status = rel === "" ? await this.gitExecAt(repoRoot, ["status", "--short"], signal) : await this.gitExecAt(repoRoot, ["status", "--short", "--", rel], signal);
@@ -338385,7 +338533,7 @@ ${String(result.data)}`);
338385
338533
  }
338386
338534
  async githubFileAudit(repoRoot, target, remote, signal) {
338387
338535
  const repo = `${remote.owner}/${remote.name}`;
338388
- const rel = path16.relative(repoRoot, target).replace(/\\/g, "/");
338536
+ const rel = path17.relative(repoRoot, target).replace(/\\/g, "/");
338389
338537
  const branch = (await this.gitExecAt(repoRoot, ["branch", "--show-current"], signal)).trim();
338390
338538
  const encodedPath = rel && rel !== "." ? rel.split("/").map((part) => encodeURIComponent(part)).join("/") : "";
338391
338539
  const repoInfo = await this.ghJson(["api", `repos/${repo}`, "--jq", "{name: .full_name, private: .private, default_branch: .default_branch, fork: .fork, html_url: .html_url}"], repoRoot, signal);
@@ -338409,8 +338557,8 @@ ${String(result.data)}`);
338409
338557
  };
338410
338558
  }
338411
338559
  async repoSecurityAudit(target, ws, baseRef, signal) {
338412
- const resolvedTarget = path16.resolve(target || ws);
338413
- const repoRoot = await this.findGitRoot(fs14.existsSync(resolvedTarget) && fs14.statSync(resolvedTarget).isDirectory() ? resolvedTarget : path16.dirname(resolvedTarget), ws, signal);
338560
+ const resolvedTarget = path17.resolve(target || ws);
338561
+ const repoRoot = await this.findGitRoot(fs15.existsSync(resolvedTarget) && fs15.statSync(resolvedTarget).isDirectory() ? resolvedTarget : path17.dirname(resolvedTarget), ws, signal);
338414
338562
  if (!repoRoot) {
338415
338563
  return JSON.stringify({
338416
338564
  ok: true,
@@ -338508,12 +338656,12 @@ ${String(result.data)}`);
338508
338656
  const findings = [];
338509
338657
  for (const rel of Array.from(files).sort()) {
338510
338658
  if (findings.length >= 40) break;
338511
- const full = path16.join(repoRoot, rel);
338512
- if (!fs14.existsSync(full) || !fs14.statSync(full).isFile()) continue;
338513
- if (fs14.statSync(full).size > 512 * 1024) continue;
338659
+ const full = path17.join(repoRoot, rel);
338660
+ if (!fs15.existsSync(full) || !fs15.statSync(full).isFile()) continue;
338661
+ if (fs15.statSync(full).size > 512 * 1024) continue;
338514
338662
  let text = "";
338515
338663
  try {
338516
- text = fs14.readFileSync(full, "utf-8");
338664
+ text = fs15.readFileSync(full, "utf-8");
338517
338665
  } catch {
338518
338666
  continue;
338519
338667
  }
@@ -338543,12 +338691,12 @@ ${String(result.data)}`);
338543
338691
  const findings = [];
338544
338692
  for (const rel of Array.from(files).sort()) {
338545
338693
  if (findings.length >= 40) break;
338546
- const full = path16.join(repoRoot, rel);
338547
- if (!fs14.existsSync(full) || !fs14.statSync(full).isFile()) continue;
338548
- if (fs14.statSync(full).size > 512 * 1024) continue;
338694
+ const full = path17.join(repoRoot, rel);
338695
+ if (!fs15.existsSync(full) || !fs15.statSync(full).isFile()) continue;
338696
+ if (fs15.statSync(full).size > 512 * 1024) continue;
338549
338697
  let text = "";
338550
338698
  try {
338551
- text = fs14.readFileSync(full, "utf-8");
338699
+ text = fs15.readFileSync(full, "utf-8");
338552
338700
  } catch {
338553
338701
  continue;
338554
338702
  }
@@ -338565,7 +338713,7 @@ ${String(result.data)}`);
338565
338713
  releaseExcludedPathFindings(repoRoot, ignoredFilesRaw) {
338566
338714
  const sensitive = /^(config\.json|agent\.md|PC_Hash\.config|Work\/|archive\/|skills\/|Memory Lab\/|Design\.md|release\/|_local\/|_ref\/|vendor\/)/i;
338567
338715
  const fromIgnored = String(ignoredFilesRaw || "").split(/\r?\n/).map((line) => line.trim().replace(/\\/g, "/")).filter((line) => line && sensitive.test(line));
338568
- const direct = ["config.json", "agent.md", "PC_Hash.config", "Work", "archive", "skills", "Memory Lab", "Design.md", "release", "_local", "_ref", "vendor"].filter((rel) => fs14.existsSync(path16.join(repoRoot, rel))).map((rel) => rel.replace(/\\/g, "/"));
338716
+ const direct = ["config.json", "agent.md", "PC_Hash.config", "Work", "archive", "skills", "Memory Lab", "Design.md", "release", "_local", "_ref", "vendor"].filter((rel) => fs15.existsSync(path17.join(repoRoot, rel))).map((rel) => rel.replace(/\\/g, "/"));
338569
338717
  return Array.from(/* @__PURE__ */ new Set([...fromIgnored, ...direct])).slice(0, 80);
338570
338718
  }
338571
338719
  async ghJson(args, ws, signal) {
@@ -339369,8 +339517,8 @@ function sharedSubagentManager(key3, options) {
339369
339517
  }
339370
339518
 
339371
339519
  // src/core/skills.ts
339372
- var fs15 = __toESM(require("fs"));
339373
- var path17 = __toESM(require("path"));
339520
+ var fs16 = __toESM(require("fs"));
339521
+ var path18 = __toESM(require("path"));
339374
339522
  var os4 = __toESM(require("os"));
339375
339523
  var import_crypto9 = require("crypto");
339376
339524
  var SkillsManager = class {
@@ -339379,13 +339527,13 @@ var SkillsManager = class {
339379
339527
  marketSourcesPath;
339380
339528
  metadataCache = /* @__PURE__ */ new Map();
339381
339529
  constructor(root2) {
339382
- this.skillsDir = path17.join(root2, "skills");
339383
- this.metaPath = path17.join(this.skillsDir, ".skills.json");
339384
- this.marketSourcesPath = path17.join(this.skillsDir, ".market-sources.json");
339385
- fs15.mkdirSync(this.skillsDir, { recursive: true });
339530
+ this.skillsDir = path18.join(root2, "skills");
339531
+ this.metaPath = path18.join(this.skillsDir, ".skills.json");
339532
+ this.marketSourcesPath = path18.join(this.skillsDir, ".market-sources.json");
339533
+ fs16.mkdirSync(this.skillsDir, { recursive: true });
339386
339534
  }
339387
339535
  list() {
339388
- return fs15.readdirSync(this.skillsDir, { withFileTypes: true }).filter((e3) => e3.isDirectory() && !e3.name.startsWith(".")).map((e3) => e3.name);
339536
+ return fs16.readdirSync(this.skillsDir, { withFileTypes: true }).filter((e3) => e3.isDirectory() && !e3.name.startsWith(".")).map((e3) => e3.name);
339389
339537
  }
339390
339538
  listDetailed() {
339391
339539
  return this.list().map((name50) => this.infoFor(name50, this.getPath(name50), "project", true));
@@ -339406,48 +339554,48 @@ var SkillsManager = class {
339406
339554
  }
339407
339555
  load(name50) {
339408
339556
  const reference = String(name50 || "").trim().toLowerCase();
339409
- const skill = this.active().find((item) => item.name.toLowerCase() === reference || path17.basename(item.path).toLowerCase() === reference);
339557
+ const skill = this.active().find((item) => item.name.toLowerCase() === reference || path18.basename(item.path).toLowerCase() === reference);
339410
339558
  if (!skill) return null;
339411
- const skillPath = path17.join(skill.path, "SKILL.md");
339412
- const content = fs15.readFileSync(skillPath, "utf-8");
339559
+ const skillPath = path18.join(skill.path, "SKILL.md");
339560
+ const content = fs16.readFileSync(skillPath, "utf-8");
339413
339561
  const files = this.sampleSkillFiles(skill.path, 10);
339414
339562
  return { skill, content, files };
339415
339563
  }
339416
339564
  has(name50) {
339417
- return fs15.existsSync(path17.join(this.skillsDir, name50, "SKILL.md"));
339565
+ return fs16.existsSync(path18.join(this.skillsDir, name50, "SKILL.md"));
339418
339566
  }
339419
339567
  getPath(name50) {
339420
- return path17.join(this.skillsDir, name50);
339568
+ return path18.join(this.skillsDir, name50);
339421
339569
  }
339422
339570
  async download(name50, url) {
339423
- const dir = path17.join(this.skillsDir, name50);
339424
- fs15.mkdirSync(dir, { recursive: true });
339571
+ const dir = path18.join(this.skillsDir, name50);
339572
+ fs16.mkdirSync(dir, { recursive: true });
339425
339573
  if (!url.startsWith("http")) return `[skill] Not a URL: ${url}`;
339426
339574
  try {
339427
339575
  const resp = await fetch(url);
339428
339576
  const content = await resp.text();
339429
- fs15.writeFileSync(path17.join(dir, "SKILL.md"), content, "utf-8");
339577
+ fs16.writeFileSync(path18.join(dir, "SKILL.md"), content, "utf-8");
339430
339578
  return `[skill] Downloaded '${name50}'`;
339431
339579
  } catch (e3) {
339432
339580
  return `[skill] ${e3}`;
339433
339581
  }
339434
339582
  }
339435
339583
  installFromLocal(sourceDir, targetName) {
339436
- const skillPath = path17.join(sourceDir, "SKILL.md");
339437
- if (!fs15.existsSync(skillPath)) return false;
339584
+ const skillPath = path18.join(sourceDir, "SKILL.md");
339585
+ if (!fs16.existsSync(skillPath)) return false;
339438
339586
  const info = this.parseSkillInfo(sourceDir);
339439
- const cleanName = this.cleanName(targetName || info.name || path17.basename(sourceDir));
339587
+ const cleanName = this.cleanName(targetName || info.name || path18.basename(sourceDir));
339440
339588
  if (!cleanName) return false;
339441
- const dest = path17.join(this.skillsDir, cleanName);
339442
- fs15.rmSync(dest, { recursive: true, force: true });
339443
- fs15.cpSync(sourceDir, dest, { recursive: true });
339589
+ const dest = path18.join(this.skillsDir, cleanName);
339590
+ fs16.rmSync(dest, { recursive: true, force: true });
339591
+ fs16.cpSync(sourceDir, dest, { recursive: true });
339444
339592
  this.setEnabled(cleanName, true);
339445
339593
  return true;
339446
339594
  }
339447
339595
  remove(name50) {
339448
- const dir = path17.join(this.skillsDir, name50);
339449
- if (fs15.existsSync(dir)) {
339450
- fs15.rmSync(dir, { recursive: true, force: true });
339596
+ const dir = path18.join(this.skillsDir, name50);
339597
+ if (fs16.existsSync(dir)) {
339598
+ fs16.rmSync(dir, { recursive: true, force: true });
339451
339599
  const meta = this.loadMeta();
339452
339600
  meta.disabled = meta.disabled.filter((n3) => n3 !== name50);
339453
339601
  this.saveMeta(meta);
@@ -339496,7 +339644,7 @@ var SkillsManager = class {
339496
339644
  type,
339497
339645
  enabled: input.enabled !== false,
339498
339646
  url: url || void 0,
339499
- path: sourcePath ? path17.resolve(sourcePath) : void 0,
339647
+ path: sourcePath ? path18.resolve(sourcePath) : void 0,
339500
339648
  addedAt: existing?.addedAt || now2,
339501
339649
  updatedAt: now2
339502
339650
  };
@@ -339534,17 +339682,17 @@ var SkillsManager = class {
339534
339682
  const items = [];
339535
339683
  for (const info of this.listDetailed()) items.push(info);
339536
339684
  const roots = [
339537
- { root: path17.join(this.skillsDir, "..", ".agents", "skills"), source: "codex" },
339538
- { root: path17.join(this.skillsDir, "..", ".claude", "skills"), source: "claude" },
339539
- { root: path17.join(os4.homedir(), ".agents", "skills"), source: "user" },
339540
- { root: path17.join(os4.homedir(), ".codex", "skills"), source: "codex" },
339541
- { root: path17.join(os4.homedir(), ".claude", "skills"), source: "claude" },
339542
- { root: path17.join(os4.homedir(), ".config", "opencode", "skills"), source: "opencode" }
339685
+ { root: path18.join(this.skillsDir, "..", ".agents", "skills"), source: "codex" },
339686
+ { root: path18.join(this.skillsDir, "..", ".claude", "skills"), source: "claude" },
339687
+ { root: path18.join(os4.homedir(), ".agents", "skills"), source: "user" },
339688
+ { root: path18.join(os4.homedir(), ".codex", "skills"), source: "codex" },
339689
+ { root: path18.join(os4.homedir(), ".claude", "skills"), source: "claude" },
339690
+ { root: path18.join(os4.homedir(), ".config", "opencode", "skills"), source: "opencode" }
339543
339691
  ];
339544
339692
  for (const entry of roots) {
339545
339693
  for (const dir of this.findSkillDirs(entry.root, 4, 240)) {
339546
339694
  const parsed = this.parseSkillInfo(dir);
339547
- const name50 = this.cleanName(parsed.name || path17.basename(dir));
339695
+ const name50 = this.cleanName(parsed.name || path18.basename(dir));
339548
339696
  if (!name50 || items.some((i4) => i4.name === name50 && i4.source !== "remote")) continue;
339549
339697
  items.push({
339550
339698
  name: name50,
@@ -339561,9 +339709,9 @@ var SkillsManager = class {
339561
339709
  });
339562
339710
  }
339563
339711
  }
339564
- for (const dir of this.findPluginSkillDirs(path17.join(this.skillsDir, ".."), 5, 240)) {
339712
+ for (const dir of this.findPluginSkillDirs(path18.join(this.skillsDir, ".."), 5, 240)) {
339565
339713
  const parsed = this.parseSkillInfo(dir);
339566
- const name50 = this.cleanName(parsed.name || path17.basename(dir));
339714
+ const name50 = this.cleanName(parsed.name || path18.basename(dir));
339567
339715
  if (!name50 || items.some((i4) => i4.name === name50 && i4.source !== "remote")) continue;
339568
339716
  items.push({
339569
339717
  name: name50,
@@ -339620,8 +339768,8 @@ var SkillsManager = class {
339620
339768
  }
339621
339769
  loadMeta() {
339622
339770
  try {
339623
- if (fs15.existsSync(this.metaPath)) {
339624
- const raw = JSON.parse(fs15.readFileSync(this.metaPath, "utf-8"));
339771
+ if (fs16.existsSync(this.metaPath)) {
339772
+ const raw = JSON.parse(fs16.readFileSync(this.metaPath, "utf-8"));
339625
339773
  return { disabled: Array.isArray(raw.disabled) ? raw.disabled.map(String) : [] };
339626
339774
  }
339627
339775
  } catch {
@@ -339629,7 +339777,7 @@ var SkillsManager = class {
339629
339777
  return { disabled: [] };
339630
339778
  }
339631
339779
  saveMeta(meta) {
339632
- fs15.writeFileSync(this.metaPath, JSON.stringify({ disabled: meta.disabled }, null, 2), "utf-8");
339780
+ fs16.writeFileSync(this.metaPath, JSON.stringify({ disabled: meta.disabled }, null, 2), "utf-8");
339633
339781
  }
339634
339782
  builtinMarketSources() {
339635
339783
  return [{
@@ -339643,8 +339791,8 @@ var SkillsManager = class {
339643
339791
  }
339644
339792
  loadMarketSources() {
339645
339793
  try {
339646
- if (!fs15.existsSync(this.marketSourcesPath)) return [];
339647
- const raw = JSON.parse(fs15.readFileSync(this.marketSourcesPath, "utf-8"));
339794
+ if (!fs16.existsSync(this.marketSourcesPath)) return [];
339795
+ const raw = JSON.parse(fs16.readFileSync(this.marketSourcesPath, "utf-8"));
339648
339796
  if (!Array.isArray(raw.sources)) return [];
339649
339797
  return raw.sources.map((source) => this.normalizeMarketSource(source)).filter((source) => !!source);
339650
339798
  } catch {
@@ -339653,7 +339801,7 @@ var SkillsManager = class {
339653
339801
  }
339654
339802
  saveMarketSources(sources) {
339655
339803
  const normalized = sources.filter((s3) => !s3.builtin).map((s3) => this.normalizeMarketSource(s3)).filter((source) => !!source);
339656
- fs15.writeFileSync(this.marketSourcesPath, JSON.stringify({ sources: normalized }, null, 2), "utf-8");
339804
+ fs16.writeFileSync(this.marketSourcesPath, JSON.stringify({ sources: normalized }, null, 2), "utf-8");
339657
339805
  }
339658
339806
  normalizeMarketSource(raw) {
339659
339807
  if (!raw || typeof raw !== "object") return null;
@@ -339673,7 +339821,7 @@ var SkillsManager = class {
339673
339821
  type,
339674
339822
  enabled: source.enabled !== false,
339675
339823
  url: url || void 0,
339676
- path: sourcePath ? path17.resolve(sourcePath) : void 0,
339824
+ path: sourcePath ? path18.resolve(sourcePath) : void 0,
339677
339825
  builtin: source.builtin === true,
339678
339826
  addedAt: source.addedAt ? String(source.addedAt) : void 0,
339679
339827
  updatedAt: source.updatedAt ? String(source.updatedAt) : void 0
@@ -339724,11 +339872,11 @@ var SkillsManager = class {
339724
339872
  return rawItems.slice(0, 1e3).map((entry) => this.marketInfoFromCatalogEntry(entry, source, installed)).filter((item) => !!item);
339725
339873
  }
339726
339874
  readCatalogText(source) {
339727
- const catalogPath = source.path ? path17.resolve(source.path) : "";
339728
- if (catalogPath && fs15.existsSync(catalogPath)) return fs15.readFileSync(catalogPath, "utf-8");
339875
+ const catalogPath = source.path ? path18.resolve(source.path) : "";
339876
+ if (catalogPath && fs16.existsSync(catalogPath)) return fs16.readFileSync(catalogPath, "utf-8");
339729
339877
  const url = source.url || "";
339730
- if (url.startsWith("file://")) return fs15.readFileSync(new URL(url), "utf-8");
339731
- if (url && !url.startsWith("http")) return fs15.readFileSync(path17.resolve(url), "utf-8");
339878
+ if (url.startsWith("file://")) return fs16.readFileSync(new URL(url), "utf-8");
339879
+ if (url && !url.startsWith("http")) return fs16.readFileSync(path18.resolve(url), "utf-8");
339732
339880
  return "";
339733
339881
  }
339734
339882
  async discoverJsonMarketSourceAsync(source, installed) {
@@ -339788,7 +339936,7 @@ var SkillsManager = class {
339788
339936
  }
339789
339937
  marketInfoFromLocalDir(dir, source, installed) {
339790
339938
  const parsed = this.parseSkillInfo(dir);
339791
- const name50 = this.cleanName(parsed.name || path17.basename(dir));
339939
+ const name50 = this.cleanName(parsed.name || path18.basename(dir));
339792
339940
  if (!name50) return null;
339793
339941
  return {
339794
339942
  name: name50,
@@ -339813,9 +339961,9 @@ var SkillsManager = class {
339813
339961
  }
339814
339962
  parseSkillInfo(dir) {
339815
339963
  try {
339816
- const skillPath = path17.join(dir, "SKILL.md");
339817
- const stat = fs15.statSync(skillPath);
339818
- const content = fs15.readFileSync(skillPath, "utf-8");
339964
+ const skillPath = path18.join(dir, "SKILL.md");
339965
+ const stat = fs16.statSync(skillPath);
339966
+ const content = fs16.readFileSync(skillPath, "utf-8");
339819
339967
  const digest = (0, import_crypto9.createHash)("sha256").update(content).digest("hex");
339820
339968
  const fingerprint2 = `${stat.mtimeMs}:${stat.size}:${digest}`;
339821
339969
  const cached = this.metadataCache.get(skillPath);
@@ -339848,7 +339996,7 @@ var SkillsManager = class {
339848
339996
  };
339849
339997
  this.metadataCache.set(skillPath, {
339850
339998
  fingerprint: fingerprint2,
339851
- info: { ...parsed, path: dir, enabled: this.isEnabled(path17.basename(dir)), installed: true, source: "project" }
339999
+ info: { ...parsed, path: dir, enabled: this.isEnabled(path18.basename(dir)), installed: true, source: "project" }
339852
340000
  });
339853
340001
  return parsed;
339854
340002
  } catch {
@@ -339865,13 +340013,13 @@ var SkillsManager = class {
339865
340013
  if (files.length >= limit || depth > 2) return;
339866
340014
  let entries = [];
339867
340015
  try {
339868
- entries = fs15.readdirSync(dir, { withFileTypes: true });
340016
+ entries = fs16.readdirSync(dir, { withFileTypes: true });
339869
340017
  } catch {
339870
340018
  return;
339871
340019
  }
339872
340020
  for (const entry of entries) {
339873
340021
  if (files.length >= limit || entry.name === "SKILL.md" || entry.name.startsWith(".")) continue;
339874
- const target = path17.join(dir, entry.name);
340022
+ const target = path18.join(dir, entry.name);
339875
340023
  if (entry.isDirectory()) walk4(target, depth + 1);
339876
340024
  else if (entry.isFile()) files.push(target);
339877
340025
  }
@@ -339885,7 +340033,7 @@ var SkillsManager = class {
339885
340033
  if (results.length >= maxItems || depth > maxDepth) return;
339886
340034
  let entries;
339887
340035
  try {
339888
- entries = fs15.readdirSync(dir, { withFileTypes: true });
340036
+ entries = fs16.readdirSync(dir, { withFileTypes: true });
339889
340037
  } catch {
339890
340038
  return;
339891
340039
  }
@@ -339895,7 +340043,7 @@ var SkillsManager = class {
339895
340043
  }
339896
340044
  for (const e3 of entries) {
339897
340045
  if (!e3.isDirectory() || e3.name.startsWith(".git") || e3.name === "node_modules") continue;
339898
- walk4(path17.join(dir, e3.name), depth + 1);
340046
+ walk4(path18.join(dir, e3.name), depth + 1);
339899
340047
  }
339900
340048
  };
339901
340049
  walk4(root2, 0);
@@ -339907,19 +340055,19 @@ var SkillsManager = class {
339907
340055
  if (results.length >= maxItems || depth > maxDepth) return;
339908
340056
  let entries;
339909
340057
  try {
339910
- entries = fs15.readdirSync(dir, { withFileTypes: true });
340058
+ entries = fs16.readdirSync(dir, { withFileTypes: true });
339911
340059
  } catch {
339912
340060
  return;
339913
340061
  }
339914
340062
  const hasPluginManifest = entries.some((e3) => e3.isDirectory() && (e3.name === ".codex-plugin" || e3.name === ".claude-plugin"));
339915
340063
  if (hasPluginManifest) {
339916
340064
  for (const skillsDir of ["skills", "Skills"]) {
339917
- results.push(...this.findSkillDirs(path17.join(dir, skillsDir), 3, maxItems - results.length));
340065
+ results.push(...this.findSkillDirs(path18.join(dir, skillsDir), 3, maxItems - results.length));
339918
340066
  }
339919
340067
  }
339920
340068
  for (const e3 of entries) {
339921
340069
  if (!e3.isDirectory() || e3.name.startsWith(".git") || e3.name === "node_modules" || e3.name === "release" || e3.name.startsWith("release.locked-")) continue;
339922
- walk4(path17.join(dir, e3.name), depth + 1);
340070
+ walk4(path18.join(dir, e3.name), depth + 1);
339923
340071
  }
339924
340072
  };
339925
340073
  walk4(root2, 0);
@@ -339947,25 +340095,25 @@ var SkillsManager = class {
339947
340095
  if (!description) warnings.push("Missing required frontmatter field: description.");
339948
340096
  if (name50 && !/^[A-Za-z0-9][A-Za-z0-9_.:-]{0,119}$/.test(name50)) warnings.push("Skill name contains characters outside the portable Agent Skills subset.");
339949
340097
  if (description && description.length > 1e3) warnings.push("Description is longer than recommended for skill discovery.");
339950
- const folderName = path17.basename(dir);
340098
+ const folderName = path18.basename(dir);
339951
340099
  if (name50 && folderName && this.cleanName(name50) !== this.cleanName(folderName)) warnings.push("Skill name does not match containing folder name.");
339952
340100
  return warnings;
339953
340101
  }
339954
340102
  pluginIdForSkill(dir) {
339955
- let current = path17.resolve(dir);
340103
+ let current = path18.resolve(dir);
339956
340104
  for (let i4 = 0; i4 < 6; i4++) {
339957
- const codex = path17.join(current, ".codex-plugin", "plugin.json");
339958
- const claude = path17.join(current, ".claude-plugin", "plugin.json");
340105
+ const codex = path18.join(current, ".codex-plugin", "plugin.json");
340106
+ const claude = path18.join(current, ".claude-plugin", "plugin.json");
339959
340107
  for (const filePath of [codex, claude]) {
339960
340108
  try {
339961
- if (fs15.existsSync(filePath)) {
339962
- const raw = JSON.parse(fs15.readFileSync(filePath, "utf-8"));
340109
+ if (fs16.existsSync(filePath)) {
340110
+ const raw = JSON.parse(fs16.readFileSync(filePath, "utf-8"));
339963
340111
  if (raw?.name) return String(raw.name);
339964
340112
  }
339965
340113
  } catch {
339966
340114
  }
339967
340115
  }
339968
- const parent = path17.dirname(current);
340116
+ const parent = path18.dirname(current);
339969
340117
  if (parent === current) break;
339970
340118
  current = parent;
339971
340119
  }
@@ -340511,6 +340659,22 @@ function createToolchainCore() {
340511
340659
  return { registry: new ToolRegistry(), catalog: new CapabilityCatalog() };
340512
340660
  }
340513
340661
 
340662
+ // src/core/emptyResponseRetry.ts
340663
+ var EMPTY_RESPONSE_RETRY_DELAYS_MS = [200, 800, 2e3, 1e4, 6e4];
340664
+ var MAX_EMPTY_RESPONSE_RETRIES = EMPTY_RESPONSE_RETRY_DELAYS_MS.length;
340665
+ var MAX_CONSECUTIVE_EMPTY_RESPONSES = MAX_EMPTY_RESPONSE_RETRIES + 1;
340666
+ function emptyResponseRetryDelayMs(consecutiveEmptyResponses) {
340667
+ return EMPTY_RESPONSE_RETRY_DELAYS_MS[Math.max(0, consecutiveEmptyResponses - 1)] ?? 0;
340668
+ }
340669
+ function observeEmptyResponseOutcome(consecutiveEmptyResponses, emptyResponse) {
340670
+ const nextCount = emptyResponse ? consecutiveEmptyResponses + 1 : 0;
340671
+ return {
340672
+ consecutiveEmptyResponses: nextCount,
340673
+ retry: emptyResponse && nextCount <= MAX_EMPTY_RESPONSE_RETRIES,
340674
+ terminate: emptyResponse && nextCount > MAX_EMPTY_RESPONSE_RETRIES
340675
+ };
340676
+ }
340677
+
340514
340678
  // src/core/agentKernelRunner.ts
340515
340679
  var publicStreamFilters = /* @__PURE__ */ new WeakMap();
340516
340680
  var brokerOnlyAssistantBuffers = /* @__PURE__ */ new WeakMap();
@@ -340662,7 +340826,7 @@ function kernelTurnFailed(agent, turn) {
340662
340826
  return turn.stopReason === "error" || agent.isLlmErrorText(turn.text);
340663
340827
  }
340664
340828
  function providerTurnIsEmpty(turn) {
340665
- return /provider returned an empty response/i.test(`${turn.errorMessage}
340829
+ return !turn.activity && /provider returned an empty response/i.test(`${turn.errorMessage}
340666
340830
  ${turn.text}`);
340667
340831
  }
340668
340832
  function removeTrailingFailedAssistant(agent, messages) {
@@ -340671,6 +340835,12 @@ function removeTrailingFailedAssistant(agent, messages) {
340671
340835
  const text = KernelMessageText(last);
340672
340836
  if (last.stopReason === "error" || agent.isLlmErrorText(text)) messages.pop();
340673
340837
  }
340838
+ function removeTrailingThoughtOnlyAssistant(messages) {
340839
+ const last = messages[messages.length - 1];
340840
+ if (last?.role !== "assistant") return;
340841
+ const hasToolCall = last.content.some((content) => content.type === "toolCall");
340842
+ if (!KernelMessageText(last).trim() && !hasToolCall) messages.pop();
340843
+ }
340674
340844
  function normalizePublicProviderError(error, secrets = []) {
340675
340845
  let raw = "";
340676
340846
  if (error instanceof Error) {
@@ -340796,8 +340966,17 @@ async function runAgentKernel(agent) {
340796
340966
  const tokens = [];
340797
340967
  const runOnce = async (promptMessages, appendPromptToAgentHistory) => {
340798
340968
  let lastAssistant = null;
340969
+ let observedActivity = false;
340970
+ let observedThought = false;
340799
340971
  const unsubscribe = kernel2.subscribe(async (event) => {
340800
340972
  await handleKernelEvent(agent, event, tokens);
340973
+ if (event.type === "message_update") {
340974
+ const delta = event.assistantMessageEvent;
340975
+ const deltaText = typeof delta.delta === "string" ? delta.delta : "";
340976
+ const thoughtDelta = delta.type === "thinking_delta" && !!deltaText.trim();
340977
+ observedThought = observedThought || thoughtDelta;
340978
+ observedActivity = observedActivity || thoughtDelta || delta.type === "text_delta" && !!deltaText.trim() || delta.type === "toolcall_end";
340979
+ }
340801
340980
  if (event.type === "message_end" && event.message.role === "assistant") {
340802
340981
  lastAssistant = event.message;
340803
340982
  }
@@ -340815,11 +340994,13 @@ async function runAgentKernel(agent) {
340815
340994
  const assistant = lastAssistant;
340816
340995
  const text = assistant ? KernelMessageText(assistant) : "";
340817
340996
  const hasToolCall = !!assistant?.content?.some((content) => content.type === "toolCall");
340818
- const emptyResponse = !assistant || !text.trim() && !hasToolCall && String(assistant?.stopReason || "") !== "aborted";
340997
+ const emptyResponse = !assistant || !text.trim() && !hasToolCall && !observedActivity && String(assistant?.stopReason || "") !== "aborted";
340819
340998
  return {
340820
340999
  text: emptyResponse ? "[Error] Provider returned an empty response." : text,
340821
341000
  stopReason: String(assistant?.stopReason || ""),
340822
- errorMessage: String(assistant?.errorMessage || (emptyResponse ? "Provider returned an empty response." : ""))
341001
+ errorMessage: String(assistant?.errorMessage || (emptyResponse ? "Provider returned an empty response." : "")),
341002
+ activity: observedActivity || !!text.trim() || hasToolCall,
341003
+ thoughtOnly: observedThought && !text.trim() && !hasToolCall && !["error", "aborted"].includes(String(assistant?.stopReason || ""))
340823
341004
  };
340824
341005
  } finally {
340825
341006
  unsubscribe();
@@ -340865,14 +341046,22 @@ async function runAgentKernel(agent) {
340865
341046
  fallback: { from: modelBeforeKernelRun, to: agent.model, providerId: agent.activeDeployment()?.providerId }
340866
341047
  });
340867
341048
  }
340868
- let emptyResponseRetries = 0;
340869
- while (providerTurnIsEmpty(lastTurn) && emptyResponseRetries < 2) {
341049
+ let consecutiveEmptyResponses = 0;
341050
+ for (; ; ) {
341051
+ const emptyResponseState = observeEmptyResponseOutcome(consecutiveEmptyResponses, providerTurnIsEmpty(lastTurn));
341052
+ consecutiveEmptyResponses = emptyResponseState.consecutiveEmptyResponses;
341053
+ if (lastTurn.thoughtOnly) {
341054
+ removeTrailingThoughtOnlyAssistant(kernel2.state.messages);
341055
+ lastTurn = await runWithCompressionResume([], false);
341056
+ continue;
341057
+ }
341058
+ if (!emptyResponseState.retry) break;
340870
341059
  removeTrailingFailedAssistant(agent, kernel2.state.messages);
340871
- emptyResponseRetries += 1;
340872
- const notice = `[Model retry] Provider returned an empty response; retrying the same deployment (${emptyResponseRetries}/2).`;
341060
+ const retryNumber = consecutiveEmptyResponses;
341061
+ const notice = `[Model retry] Provider returned an empty response; retrying the same deployment (${retryNumber}/${MAX_EMPTY_RESPONSE_RETRIES}) after ${emptyResponseRetryDelayMs(consecutiveEmptyResponses)}ms.`;
340873
341062
  tokens.push({ type: "text", text: notice });
340874
341063
  agent.recordWorkStatus(notice);
340875
- await agent.waitForPlannedRouteRetry();
341064
+ await agent.waitForPlannedRouteRetry(emptyResponseRetryDelayMs(consecutiveEmptyResponses));
340876
341065
  lastTurn = await runWithCompressionResume([], false);
340877
341066
  }
340878
341067
  let routeRetries = 0;
@@ -341068,7 +341257,7 @@ async function runAgentKernel(agent) {
341068
341257
  return;
341069
341258
  }
341070
341259
  if (textStarted) finalContent.push({ type: "text", text });
341071
- if (!finalContent.length) {
341260
+ if (!finalContent.length && !thinking.trim()) {
341072
341261
  text = "[Error] Provider returned an empty response.";
341073
341262
  finalContent.push({ type: "text", text });
341074
341263
  }
@@ -344952,7 +345141,7 @@ var Agent4 = class _Agent {
344952
345141
  this.config.set("skills", "auto_download", "disabled");
344953
345142
  }
344954
345143
  const modeStr = this.config.getStr("agent", "default_mode");
344955
- this.mode = ["plan", "goal", "flow"].includes(modeStr) ? modeStr : "build";
345144
+ this.mode = ["plan", "chat", "goal", "flow"].includes(modeStr) ? modeStr : "build";
344956
345145
  const inputStr = this.config.getStr("general", "default_input");
344957
345146
  this.inputMode = inputStr === "next" ? "next" : "guide";
344958
345147
  const configuredModel = this.config.getStr("models", "default_model");
@@ -345120,6 +345309,7 @@ var Agent4 = class _Agent {
345120
345309
  return this.toolchainCore;
345121
345310
  }
345122
345311
  setMode(m2) {
345312
+ if (!["build", "plan", "chat", "goal", "flow"].includes(m2)) m2 = "build";
345123
345313
  if (m2 === "goal" && !this.goal) {
345124
345314
  this.goal = new GoalStateImpl("Set your objective");
345125
345315
  }
@@ -345652,7 +345842,12 @@ var Agent4 = class _Agent {
345652
345842
  beginRouteAttempt() {
345653
345843
  this.routeAttemptStartedAt = Date.now();
345654
345844
  }
345655
- async waitForPlannedRouteRetry() {
345845
+ async waitForPlannedRouteRetry(explicitDelayMs) {
345846
+ if (explicitDelayMs !== void 0) {
345847
+ if (explicitDelayMs <= 0) return;
345848
+ await new Promise((resolve16) => setTimeout(resolve16, explicitDelayMs));
345849
+ return;
345850
+ }
345656
345851
  const waitBudgetMs = Math.max(0, Math.min(15e3, this.lastRouteDecision?.retryBudgetMs ?? 5e3));
345657
345852
  const delay = Math.max(0, Math.min(waitBudgetMs, this.lastRouteRetryDelayMs));
345658
345853
  this.lastRouteRetryDelayMs = 0;
@@ -349482,7 +349677,20 @@ ${summary}`, segment, "local-summarize", true);
349482
349677
  if (action === "get") return JSON.stringify({ ok: true, linkedPlan: this.getLinkedPlan() }, null, 2);
349483
349678
  if (action !== "update") return JSON.stringify({ ok: false, error: `Unknown linked_plan action: ${action}` });
349484
349679
  const expectedRevision = Number(input.expected_revision ?? input.expectedRevision);
349485
- return JSON.stringify({ ok: true, linkedPlan: this.updateLinkedPlan(String(input.markdown || ""), expectedRevision) }, null, 2);
349680
+ const current = this.getLinkedPlan();
349681
+ let markdown = input.markdown === void 0 ? current.markdown : String(input.markdown);
349682
+ if (input.append !== void 0) markdown = `${current.markdown}${String(input.append)}`;
349683
+ if (input.old_text !== void 0 || input.oldText !== void 0) {
349684
+ const oldText = String(input.old_text ?? input.oldText ?? "");
349685
+ if (!oldText) throw new Error("linked_plan old_text must not be empty.");
349686
+ const matches = current.markdown.split(oldText).length - 1;
349687
+ if (!matches) throw new Error("linked_plan old_text was not found.");
349688
+ const replaceAll = input.replace_all === true || input.replaceAll === true;
349689
+ if (matches > 1 && !replaceAll) throw new Error(`linked_plan old_text matched ${matches} places; pass replace_all=true or a unique fragment.`);
349690
+ const newText = String(input.new_text ?? input.newText ?? "");
349691
+ markdown = replaceAll ? current.markdown.split(oldText).join(newText) : current.markdown.replace(oldText, newText);
349692
+ }
349693
+ return JSON.stringify({ ok: true, linkedPlan: this.updateLinkedPlan(markdown, expectedRevision) }, null, 2);
349486
349694
  } catch (error) {
349487
349695
  return JSON.stringify({ ok: false, error: error instanceof Error ? error.message : String(error) });
349488
349696
  }
@@ -351784,7 +351992,24 @@ ${this.formatAutomation(item)}` : `[automation_toggle] Not found: ${id}`;
351784
351992
  }));
351785
351993
  }
351786
351994
  case "memory_lab_update": {
351787
- const result = await this.updateMemoryLab({
351995
+ const selector2 = String(params.component || params.slug || "").trim();
351996
+ const prepared = selector2 ? this.memoryLab.preparePatch({
351997
+ component: selector2,
351998
+ name: params.name === void 0 ? void 0 : String(params.name),
351999
+ description: params.description === void 0 ? void 0 : String(params.description),
352000
+ tags: params.tags === void 0 ? void 0 : Array.isArray(params.tags) ? params.tags.map(String) : String(params.tags).split(/[,,\n]+/),
352001
+ tagPaths: params.tagPaths === void 0 ? void 0 : Array.isArray(params.tagPaths) ? params.tagPaths.filter(Array.isArray).map((pathValue) => pathValue.map(String)) : [],
352002
+ content: params.content === void 0 ? void 0 : String(params.content),
352003
+ contentAppend: params.contentAppend === void 0 && params.content_append === void 0 ? void 0 : String(params.contentAppend ?? params.content_append),
352004
+ oldText: params.oldText === void 0 && params.old_text === void 0 ? void 0 : String(params.oldText ?? params.old_text),
352005
+ newText: String(params.newText ?? params.new_text ?? ""),
352006
+ replaceAll: params.replaceAll === true || params.replace_all === true,
352007
+ kind: params.kind === void 0 ? void 0 : params.kind === "folder" ? "folder" : "file",
352008
+ expectedUpdatedAt: String(params.expectedUpdatedAt || params.expected_updated_at || ""),
352009
+ reason: String(params.reason || ""),
352010
+ source: String(params.source || "")
352011
+ }) : void 0;
352012
+ const result = await this.updateMemoryLab(prepared || {
351788
352013
  name: String(params.name || ""),
351789
352014
  description: String(params.description || ""),
351790
352015
  tags: Array.isArray(params.tags) ? params.tags.map(String) : String(params.tags || "").split(/[,,\n]+/),
@@ -352787,6 +353012,8 @@ When using file tools (read, write, edit, glob), use ABSOLUTE paths rooted at th
352787
353012
  parts.push(this.buildFeatureDisclosurePrompt());
352788
353013
  if (this.mode === "plan") parts.push(`[Plan Tool Policy]
352789
353014
  ${planModePolicyPrompt()}`);
353015
+ if (this.mode === "chat") parts.push(`[Chat Tool Policy]
353016
+ ${chatModePolicyPrompt()}`);
352790
353017
  const pm = this.config.getStr("workspace", "prompt_mode") || "both";
352791
353018
  const injectedPrompts = /* @__PURE__ */ new Set();
352792
353019
  if ((pm === "global_only" || pm === "both") && globalPrompt) {
@@ -352908,7 +353135,7 @@ ${custom}`);
352908
353135
  `- Language policy: general.language=${language}; the UI can switch this at runtime and each turn must obey the current value. auto follows the user's dominant input language, en replies in English, zh replies in Simplified Chinese. Keep code, commands, file paths, JSON keys, model/provider names, tool names, quoted source text, and user-provided literals exactly as required by their source language.`,
352909
353136
  `- Workspace permissions: access_permission=${permission}; file tools are checked before execution and blocked when they exceed the configured workspace boundary.`,
352910
353137
  `- Remote repository safety: when the active workspace or any target path is inside a GitHub/remote-backed repository, proactively use repo_security_audit and file_audit before git_push, gh_pr_create, release packaging, public reporting, or cloud-side audit. Treat public remotes as public disclosure surfaces and keep private URLs, secrets, privacy addresses (credential URLs, private network addresses, local user paths), local runtime state, archives, Memory Lab, Work, config, and release outputs out of commits and summaries. git_push/gh_pr_create hard-block on detected high-risk findings until a second review resolves them and the action is retried with security_review_confirmed=true.`,
352911
- `- Mode engine: current mode=${this.modeName()}; Build works autonomously, Plan is fully read-only with no file modifications, Goal continues until completion unless paused, Flow follows saved workflow components.`,
353138
+ `- Mode engine: current mode=${this.modeName()}; Build works autonomously, Plan is fully read-only, Chat only performs web search/fetch evidence gathering and prompt synthesis, Goal continues until completion unless paused, Flow follows saved workflow components.`,
352912
353139
  `- Input mode: ${input}; Guide injects immediately, Next queues user intent for the following build turn.`,
352913
353140
  `- Option feedback: ${this.buildQuestionPolicyPrompt(optionFeedback)}`,
352914
353141
  `- Model policy: current model=${this.model || "(unset)"}, intelligence=${this.intelligence}, auto-switch=${modelSwitch}.`,
@@ -352974,6 +353201,14 @@ ${custom}`);
352974
353201
  'Only after the durable linked plan has actually been updated and the plan is complete, expose the fixed mode handoff asking whether execution should begin. Offer exactly these two choices in the user language: "\u662F\uFF0C\u6267\u884C\u6B64\u8BA1\u5212" / "\u5426\uFF0C\u8BF7\u8865\u5145____" (or "Yes, execute this plan" / "No, please supplement _____"). This fixed handoff remains required when discretionary questions are disabled.',
352975
353202
  "A positive choice starts a new Build-mode input. A negative choice remains in Plan mode so the user can supply the missing details."
352976
353203
  ]).join("\n");
353204
+ case "chat":
353205
+ return withLanguage([
353206
+ "CHAT MODE.",
353207
+ "Use only web_search and web_fetch. You have no workspace, host, application, memory, task, browser-control, or write permissions.",
353208
+ "Perform an online search to gather evidence before answering. Fetch primary or authoritative pages when the search snippets are insufficient.",
353209
+ "After sufficient evidence is collected, summarize and answer the user as soon as possible. Stay concise and do not turn the request into a long-running Build, Plan, Goal, or Flow task.",
353210
+ "Distinguish sourced facts from uncertainty and include useful source links in the final answer."
353211
+ ]).join("\n");
352977
353212
  case "goal": {
352978
353213
  const g2 = this.goal?.history() || "";
352979
353214
  const paused = this.goal?.paused ? "\n[GOAL PAUSED by user. Wait for resume.]" : "\n[Continue working until the goal is achieved.]";