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, input2) {
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, input2);
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, input2),
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, input2) {
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, input2);
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, input2),
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);
@@ -328713,6 +328713,30 @@ function parseProviderSse(raw) {
328713
328713
  }
328714
328714
  return events;
328715
328715
  }
328716
+ function assembleCompatibleToolArguments(parts) {
328717
+ const nonEmpty = (parts || []).map(String).filter((part) => part && part !== "null");
328718
+ if (!nonEmpty.length) return "{}";
328719
+ const isJsonObject = (value) => {
328720
+ try {
328721
+ const parsed = JSON.parse(value);
328722
+ return !!parsed && typeof parsed === "object" && !Array.isArray(parsed);
328723
+ } catch {
328724
+ return false;
328725
+ }
328726
+ };
328727
+ const incremental = nonEmpty.join("");
328728
+ if (isJsonObject(incremental)) return incremental;
328729
+ let compatible = "";
328730
+ for (const incoming of nonEmpty) {
328731
+ if (!compatible) compatible = incoming;
328732
+ else if (incoming === compatible) continue;
328733
+ else if (incoming.startsWith(compatible)) compatible = incoming;
328734
+ else if (compatible.startsWith(incoming)) continue;
328735
+ else compatible += incoming;
328736
+ }
328737
+ if (isJsonObject(compatible)) return compatible;
328738
+ return [...nonEmpty].reverse().find(isJsonObject) || compatible;
328739
+ }
328716
328740
  function isContentPolicyBlocked(json) {
328717
328741
  const choices = Array.isArray(json.choices) ? json.choices : [];
328718
328742
  const choice = choices[0] || {};
@@ -328941,6 +328965,8 @@ var ChatCompletionsAdapter = class {
328941
328965
  let contentPolicyBlocked = false;
328942
328966
  let emittedContent = false;
328943
328967
  let emittedTool = false;
328968
+ let emittedReasoning = false;
328969
+ let explicitCompletion = false;
328944
328970
  try {
328945
328971
  while (true) {
328946
328972
  const { done, value } = await readProviderStreamChunk(reader, signal);
@@ -328952,7 +328978,10 @@ var ChatCompletionsAdapter = class {
328952
328978
  const trimmed = line.trim();
328953
328979
  if (!trimmed.startsWith("data: ")) continue;
328954
328980
  const data = trimmed.slice(6);
328955
- if (data === "[DONE]") continue;
328981
+ if (data === "[DONE]") {
328982
+ explicitCompletion = true;
328983
+ continue;
328984
+ }
328956
328985
  let json;
328957
328986
  try {
328958
328987
  json = JSON.parse(data);
@@ -328965,11 +328994,16 @@ var ChatCompletionsAdapter = class {
328965
328994
  }
328966
328995
  if (isContentPolicyBlocked(json)) contentPolicyBlocked = true;
328967
328996
  const choices = Array.isArray(json.choices) ? json.choices : [];
328968
- const delta = choices[0]?.delta;
328997
+ const choice = choices[0];
328998
+ if (choice?.finish_reason !== void 0 && choice.finish_reason !== null) explicitCompletion = true;
328999
+ const delta = choice?.delta;
328969
329000
  if (!delta) continue;
328970
329001
  if (delta.reasoning_content) {
328971
329002
  const reasoning = this.extractText(delta.reasoning_content);
328972
- if (reasoning) yield { type: "reasoning.summary.delta", delta: reasoning };
329003
+ if (reasoning) {
329004
+ emittedReasoning = true;
329005
+ yield { type: "reasoning.summary.delta", delta: reasoning };
329006
+ }
328973
329007
  }
328974
329008
  const textDelta = this.extractText(delta.content);
328975
329009
  if (textDelta) {
@@ -329014,13 +329048,17 @@ var ChatCompletionsAdapter = class {
329014
329048
  type: "tool_call.completed",
329015
329049
  id: currentToolCall.id,
329016
329050
  name: currentToolCall.name,
329017
- arguments: currentToolCall.argumentParts.join("")
329051
+ arguments: assembleCompatibleToolArguments(currentToolCall.argumentParts)
329018
329052
  };
329019
329053
  }
329020
329054
  } else if (!emittedContent && !emittedTool && contentPolicyBlocked) {
329021
329055
  yield { type: "response.failed", error: "[Error] Content policy refusal (content_filter)." };
329022
329056
  return;
329023
329057
  }
329058
+ if (!explicitCompletion && !emittedContent && !emittedTool && !emittedReasoning) {
329059
+ yield { type: "response.failed", error: "[LLM Error] Chat stream ended before an explicit completion." };
329060
+ return;
329061
+ }
329024
329062
  yield { type: "response.completed" };
329025
329063
  } finally {
329026
329064
  reader.releaseLock();
@@ -329217,6 +329255,7 @@ var ResponsesAdapter = class {
329217
329255
  const key3 = `${String(payload.item_id || "")}:${String(payload.summary_index || 0)}`;
329218
329256
  const delta = this.extractText(payload.delta);
329219
329257
  if (delta) {
329258
+ emittedContent = true;
329220
329259
  reasoningSummaries.set(key3, (reasoningSummaries.get(key3) || "") + delta);
329221
329260
  yield { type: "reasoning.summary.delta", delta };
329222
329261
  }
@@ -329247,7 +329286,7 @@ var ResponsesAdapter = class {
329247
329286
  calls.set(key3, {
329248
329287
  id: String(item.call_id || item.id || key3),
329249
329288
  name: String(item.name || ""),
329250
- arguments: String(item.arguments || ""),
329289
+ argumentParts: item.arguments ? [String(item.arguments)] : [],
329251
329290
  emitted: false
329252
329291
  });
329253
329292
  }
@@ -329255,9 +329294,9 @@ var ResponsesAdapter = class {
329255
329294
  }
329256
329295
  if (eventType === "response.function_call_arguments.delta") {
329257
329296
  const key3 = String(payload.item_id || payload.call_id || payload.output_index || "");
329258
- const call = calls.get(key3) || { id: String(payload.call_id || key3), name: String(payload.name || ""), arguments: "", emitted: false };
329297
+ const call = calls.get(key3) || { id: String(payload.call_id || key3), name: String(payload.name || ""), argumentParts: [], emitted: false };
329259
329298
  const delta = String(payload.delta || "");
329260
- call.arguments += delta;
329299
+ if (delta) call.argumentParts.push(delta);
329261
329300
  calls.set(key3, call);
329262
329301
  yield { type: "tool_call.arguments.delta", id: call.id, delta };
329263
329302
  continue;
@@ -329269,19 +329308,20 @@ var ResponsesAdapter = class {
329269
329308
  const call = calls.get(key3) || {
329270
329309
  id: String(item.call_id || item.id || key3),
329271
329310
  name: String(item.name || ""),
329272
- arguments: String(item.arguments || ""),
329311
+ argumentParts: item.arguments ? [String(item.arguments)] : [],
329273
329312
  emitted: false
329274
329313
  };
329275
329314
  call.id = String(item.call_id || call.id);
329276
329315
  call.name = String(item.name || call.name);
329277
- call.arguments = typeof item.arguments === "string" ? item.arguments : call.arguments;
329316
+ if (typeof item.arguments === "string" && item.arguments) call.argumentParts.push(item.arguments);
329278
329317
  if (!call.emitted) {
329279
329318
  call.emitted = true;
329319
+ const argumentsJson = assembleCompatibleToolArguments(call.argumentParts);
329280
329320
  yield { type: "tool_call.started", id: call.id, name: call.name };
329281
- if (call.arguments && call.arguments !== "{}") {
329282
- yield { type: "tool_call.arguments.delta", id: call.id, delta: call.arguments };
329321
+ if (argumentsJson !== "{}") {
329322
+ yield { type: "tool_call.arguments.delta", id: call.id, delta: argumentsJson };
329283
329323
  }
329284
- yield { type: "tool_call.completed", id: call.id, name: call.name, arguments: call.arguments };
329324
+ yield { type: "tool_call.completed", id: call.id, name: call.name, arguments: argumentsJson };
329285
329325
  }
329286
329326
  calls.set(key3, call);
329287
329327
  }
@@ -329306,8 +329346,14 @@ var ResponsesAdapter = class {
329306
329346
  } else if (!completed) {
329307
329347
  yield { type: "response.failed", error: "[LLM Error] Responses stream ended before response.completed." };
329308
329348
  } else if (!emittedContent && calls.size === 0) {
329309
- yield { type: "response.failed", error: "[Error] Empty Responses stream." };
329349
+ yield { type: "response.failed", error: "[Error] Provider returned an empty response." };
329310
329350
  } else {
329351
+ for (const call of calls.values()) {
329352
+ if (call.emitted) continue;
329353
+ const argumentsJson = assembleCompatibleToolArguments(call.argumentParts);
329354
+ yield { type: "tool_call.started", id: call.id, name: call.name };
329355
+ yield { type: "tool_call.completed", id: call.id, name: call.name, arguments: argumentsJson };
329356
+ }
329311
329357
  yield { type: "response.completed" };
329312
329358
  }
329313
329359
  } finally {
@@ -330186,7 +330232,7 @@ ${responsePath}
330186
330232
  * `provider_adapters_v2` context flag. Request serialization and SSE
330187
330233
  * normalization are delegated to the shared provider adapters while the
330188
330234
  * transport orchestration (loopback node-http, fetch -> node-http fallback,
330189
- * 120s/30s timeouts) and the 4xx Chat -> Responses downgrade stay here.
330235
+ * cancellation-only streaming and the 4xx Chat -> Responses downgrade) stay here.
330190
330236
  * The emitted request body and StreamToken stream are byte-equivalent to
330191
330237
  * the legacy inlined path.
330192
330238
  */
@@ -330323,9 +330369,9 @@ ${responsePath}
330323
330369
  }
330324
330370
  /**
330325
330371
  * Loopback-aware transport injected into adapter `execute`. Streaming
330326
- * requests retain the fetch-to-node fallback for transport failures, while
330327
- * a local deadline is returned directly so one request cannot become a
330328
- * second Windows fallback request.
330372
+ * requests retain the fetch-to-node fallback for transport failures. They
330373
+ * have no response deadline; only caller cancellation or a concrete
330374
+ * transport/provider failure may end the request.
330329
330375
  */
330330
330376
  buildProviderAdapterTransport() {
330331
330377
  return async (request, signal) => {
@@ -330335,7 +330381,7 @@ ${responsePath}
330335
330381
  const forwardAbort = () => abort.abort(signal?.reason);
330336
330382
  if (signal?.aborted) forwardAbort();
330337
330383
  else signal?.addEventListener("abort", forwardAbort, { once: true });
330338
- const effectiveTimeout = this.effectiveRequestTimeout(12e4);
330384
+ const effectiveTimeout = 0;
330339
330385
  const timer = effectiveTimeout > 0 ? setTimeout(() => abort.abort(providerTimeoutError(effectiveTimeout)), effectiveTimeout) : void 0;
330340
330386
  try {
330341
330387
  try {
@@ -330480,7 +330526,7 @@ ${responsePath}
330480
330526
  const forwardAbort = () => abort.abort(signal?.reason);
330481
330527
  if (signal?.aborted) forwardAbort();
330482
330528
  else signal?.addEventListener("abort", forwardAbort, { once: true });
330483
- const effectiveTimeout = this.effectiveRequestTimeout(12e4);
330529
+ const effectiveTimeout = 0;
330484
330530
  const timeout = effectiveTimeout > 0 ? setTimeout(() => abort.abort(providerTimeoutError(effectiveTimeout)), effectiveTimeout) : void 0;
330485
330531
  let reader = null;
330486
330532
  try {
@@ -330512,10 +330558,14 @@ ${responsePath}
330512
330558
  }
330513
330559
  const decoder = new TextDecoder();
330514
330560
  let buffer = "";
330515
- let currentToolCall = null;
330561
+ const toolCalls = /* @__PURE__ */ new Map();
330562
+ const toolCallOrder = [];
330563
+ let syntheticToolIndex = 0;
330564
+ let lastToolIndex = 0;
330516
330565
  let currentReasoningContent = "";
330517
330566
  let contentPolicyBlocked = false;
330518
330567
  let emittedContent = false;
330568
+ let explicitCompletion = false;
330519
330569
  const streamSignal = signal || new AbortController().signal;
330520
330570
  while (true) {
330521
330571
  const { done, value } = await readProviderStreamChunk(reader, streamSignal);
@@ -330527,7 +330577,10 @@ ${responsePath}
330527
330577
  const trimmed = line.trim();
330528
330578
  if (!trimmed.startsWith("data: ")) continue;
330529
330579
  const data = trimmed.slice(6);
330530
- if (data === "[DONE]") continue;
330580
+ if (data === "[DONE]") {
330581
+ explicitCompletion = true;
330582
+ continue;
330583
+ }
330531
330584
  try {
330532
330585
  const json = JSON.parse(data);
330533
330586
  if (json.usage) yield { type: "usage", text: "", usage: extractProviderUsage(json) };
@@ -330545,24 +330598,44 @@ ${responsePath}
330545
330598
  }
330546
330599
  if (delta.tool_calls) {
330547
330600
  for (const tc of delta.tool_calls) {
330548
- if (tc.id) {
330549
- if (currentToolCall) {
330550
- yield { type: "tool_call", text: "", toolCall: currentToolCall, reasoningContent: currentReasoningContent || void 0 };
330551
- }
330552
- currentToolCall = { id: tc.id, name: tc.function?.name || "", arguments: tc.function?.arguments || "" };
330553
- } else if (tc.function?.arguments && currentToolCall) {
330554
- currentToolCall.arguments += tc.function.arguments;
330601
+ const rawIndex = Number(tc.index);
330602
+ const index = Number.isInteger(rawIndex) && rawIndex >= 0 ? rawIndex : tc.id ? syntheticToolIndex++ : lastToolIndex;
330603
+ lastToolIndex = index;
330604
+ let call = toolCalls.get(index);
330605
+ if (!call && (tc.id || tc.function?.name)) {
330606
+ call = { id: tc.id || "", name: tc.function?.name || "", argumentParts: [] };
330607
+ toolCalls.set(index, call);
330608
+ toolCallOrder.push(index);
330555
330609
  }
330610
+ if (!call) continue;
330611
+ if (tc.id && !call.id) call.id = tc.id;
330612
+ if (tc.function?.name && !call.name) call.name = tc.function.name;
330613
+ if (tc.function?.arguments) call.argumentParts.push(tc.function.arguments);
330556
330614
  }
330557
330615
  }
330558
330616
  } catch {
330559
330617
  }
330560
330618
  }
330561
330619
  }
330562
- if (currentToolCall && currentToolCall.arguments) {
330563
- yield { type: "tool_call", text: "", toolCall: currentToolCall, reasoningContent: currentReasoningContent || void 0 };
330620
+ if (toolCallOrder.length) {
330621
+ for (const index of toolCallOrder) {
330622
+ const call = toolCalls.get(index);
330623
+ if (!call) continue;
330624
+ yield {
330625
+ type: "tool_call",
330626
+ text: "",
330627
+ toolCall: {
330628
+ id: call.id,
330629
+ name: call.name,
330630
+ arguments: assembleCompatibleToolArguments(call.argumentParts)
330631
+ },
330632
+ reasoningContent: currentReasoningContent || void 0
330633
+ };
330634
+ }
330564
330635
  } else if (!emittedContent && contentPolicyBlocked) {
330565
330636
  yield { type: "text", text: "[Error] Content policy refusal (content_filter)." };
330637
+ } else if (!explicitCompletion && !emittedContent && !currentReasoningContent) {
330638
+ yield { type: "text", text: "[LLM Error] GitHub Models stream ended before an explicit completion." };
330566
330639
  }
330567
330640
  } finally {
330568
330641
  reader?.releaseLock();
@@ -331249,8 +331322,8 @@ async function fuzzyDiscoverWithoutGuide(input2, explicit, preferredModels = [])
331249
331322
  }
331250
331323
 
331251
331324
  // src/tools/index.ts
331252
- var fs14 = __toESM(require("fs"));
331253
- var path16 = __toESM(require("path"));
331325
+ var fs15 = __toESM(require("fs"));
331326
+ var path17 = __toESM(require("path"));
331254
331327
  var crypto8 = __toESM(require("crypto"));
331255
331328
  var import_url2 = require("url");
331256
331329
 
@@ -331594,7 +331667,8 @@ var MemoryLabManager = class {
331594
331667
  "Use memory_lab_read to inspect index.json before deciding what memory is relevant.",
331595
331668
  "Use memory_lab_query for bounded task-relevant retrieval; do not inject the complete index when a focused query is sufficient.",
331596
331669
  "Use memory_lab_read with component/name/slug to read a component core markdown file.",
331597
- "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.",
331670
+ "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.",
331671
+ "For small body edits prefer contentAppend or oldText/newText over resending the complete content.",
331598
331672
  "For an existing component, pass expectedUpdatedAt from the latest read/query result. A stale update is rejected instead of overwriting newer memory.",
331599
331673
  "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.",
331600
331674
  "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.",
@@ -331702,6 +331776,39 @@ var MemoryLabManager = class {
331702
331776
  source: String(input2.source || "").trim()
331703
331777
  };
331704
331778
  }
331779
+ preparePatch(input2) {
331780
+ const selector2 = String(input2.component || "").trim();
331781
+ if (!selector2) throw new Error("Memory component is required for a patch.");
331782
+ const current = this.read(selector2);
331783
+ if (!current.ok || !current.component) throw new Error(current.error || `Memory component not found: ${selector2}`);
331784
+ const existing = current.component.meta;
331785
+ const oldContent = current.component.content;
331786
+ let content = input2.content !== void 0 ? String(input2.content) : oldContent;
331787
+ if (input2.contentAppend !== void 0) content = `${oldContent}${String(input2.contentAppend)}`;
331788
+ if (input2.oldText !== void 0) {
331789
+ const oldText = String(input2.oldText);
331790
+ if (!oldText) throw new Error("oldText must not be empty.");
331791
+ const matches = oldContent.split(oldText).length - 1;
331792
+ if (!matches) throw new Error("oldText was not found in the Memory Lab component.");
331793
+ if (matches > 1 && input2.replaceAll !== true) throw new Error(`oldText matched ${matches} places; pass replaceAll=true or a unique fragment.`);
331794
+ content = input2.replaceAll === true ? oldContent.split(oldText).join(String(input2.newText || "")) : oldContent.replace(oldText, String(input2.newText || ""));
331795
+ }
331796
+ const name50 = input2.name === void 0 ? existing.name : String(input2.name);
331797
+ if (this.slugify(name50) !== current.component.slug) {
331798
+ throw new Error("Renaming a Memory Lab component is not supported by incremental patch; create the new component then delete the old one.");
331799
+ }
331800
+ return this.prepareUpdate({
331801
+ name: name50,
331802
+ description: input2.description === void 0 ? existing.description : String(input2.description),
331803
+ tags: input2.tags === void 0 ? existing.tags : input2.tags,
331804
+ tagPaths: input2.tagPaths === void 0 ? existing.tagPaths : input2.tagPaths,
331805
+ content,
331806
+ kind: input2.kind === void 0 ? existing.kind : input2.kind,
331807
+ expectedUpdatedAt: String(input2.expectedUpdatedAt || existing.updatedAt),
331808
+ reason: input2.reason,
331809
+ source: input2.source
331810
+ });
331811
+ }
331705
331812
  update(prepared) {
331706
331813
  this.ensure();
331707
331814
  const index = this.loadIndex();
@@ -332248,20 +332355,23 @@ ${JSON.stringify(payload, null, 2)}`;
332248
332355
  }
332249
332356
  };
332250
332357
 
332358
+ // src/tools/index.ts
332359
+ init_flow();
332360
+
332251
332361
  // src/core/compat.ts
332252
- var fs6 = __toESM(require("fs"));
332253
- var path7 = __toESM(require("path"));
332362
+ var fs7 = __toESM(require("fs"));
332363
+ var path8 = __toESM(require("path"));
332254
332364
  var os2 = __toESM(require("os"));
332255
332365
  function readJson(filePath) {
332256
332366
  try {
332257
- return JSON.parse(fs6.readFileSync(filePath, "utf-8").replace(/^\uFEFF/, ""));
332367
+ return JSON.parse(fs7.readFileSync(filePath, "utf-8").replace(/^\uFEFF/, ""));
332258
332368
  } catch {
332259
332369
  return null;
332260
332370
  }
332261
332371
  }
332262
332372
  function readJsonLoose(filePath) {
332263
332373
  try {
332264
- const withoutBom = fs6.readFileSync(filePath, "utf-8").replace(/^\uFEFF/, "");
332374
+ const withoutBom = fs7.readFileSync(filePath, "utf-8").replace(/^\uFEFF/, "");
332265
332375
  const withoutComments = withoutBom.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|\s)\/\/.*$/gm, "$1");
332266
332376
  return JSON.parse(withoutComments);
332267
332377
  } catch {
@@ -332342,7 +332452,7 @@ function normalizeToolResult(output, metadata) {
332342
332452
  return { ok: !error, output, error, metadata };
332343
332453
  }
332344
332454
  function componentPaths(root2, value) {
332345
- return asStringArray(value).map((item) => path7.resolve(root2, item));
332455
+ return asStringArray(value).map((item) => path8.resolve(root2, item));
332346
332456
  }
332347
332457
  function manifestComponentPaths(root2, manifest, ...keys) {
332348
332458
  for (const key3 of keys) {
@@ -332353,7 +332463,7 @@ function manifestComponentPaths(root2, manifest, ...keys) {
332353
332463
  function discoverComponentFiles(root2, relativeDirs, extension, maxDepth = 2) {
332354
332464
  const files = [];
332355
332465
  for (const dir of relativeDirs) {
332356
- files.push(...listFilesRecursive(path7.join(root2, dir), extension, maxDepth));
332466
+ files.push(...listFilesRecursive(path8.join(root2, dir), extension, maxDepth));
332357
332467
  }
332358
332468
  return Array.from(new Set(files)).sort();
332359
332469
  }
@@ -332378,12 +332488,12 @@ function collectMcpServers(...values) {
332378
332488
  function defaultComponentWarnings(kind, components) {
332379
332489
  const warnings = [];
332380
332490
  for (const item of components) {
332381
- if (path7.isAbsolute(item) && !fs6.existsSync(item)) warnings.push(`${kind} path does not exist: ${item}`);
332491
+ if (path8.isAbsolute(item) && !fs7.existsSync(item)) warnings.push(`${kind} path does not exist: ${item}`);
332382
332492
  }
332383
332493
  return warnings;
332384
332494
  }
332385
332495
  function normalizeCodexPlugin(root2, manifest) {
332386
- const name50 = asString(manifest.name) || path7.basename(root2);
332496
+ const name50 = asString(manifest.name) || path8.basename(root2);
332387
332497
  const components = {
332388
332498
  skills: manifestComponentPaths(root2, manifest, "skills"),
332389
332499
  agents: manifestComponentPaths(root2, manifest, "agents"),
@@ -332414,7 +332524,7 @@ function normalizeCodexPlugin(root2, manifest) {
332414
332524
  };
332415
332525
  }
332416
332526
  function normalizeClaudePlugin(root2, manifest) {
332417
- const name50 = asString(manifest.name) || path7.basename(root2);
332527
+ const name50 = asString(manifest.name) || path8.basename(root2);
332418
332528
  const experimental = nestedRecord(manifest.experimental);
332419
332529
  const components = {
332420
332530
  skills: manifestComponentPaths(root2, manifest, "skills"),
@@ -332452,7 +332562,7 @@ function normalizeClaudePlugin(root2, manifest) {
332452
332562
  };
332453
332563
  }
332454
332564
  function normalizeNewmarkPlugin(root2, manifest) {
332455
- const name50 = asString(manifest.name) || path7.basename(root2);
332565
+ const name50 = asString(manifest.name) || path8.basename(root2);
332456
332566
  return {
332457
332567
  id: `newmark:${name50}`,
332458
332568
  ecosystem: "newmark",
@@ -332481,7 +332591,7 @@ function findPluginRoots(root2, maxDepth = 5) {
332481
332591
  if (depth > maxDepth) return;
332482
332592
  let entries;
332483
332593
  try {
332484
- entries = fs6.readdirSync(dir, { withFileTypes: true });
332594
+ entries = fs7.readdirSync(dir, { withFileTypes: true });
332485
332595
  } catch {
332486
332596
  return;
332487
332597
  }
@@ -332490,7 +332600,7 @@ function findPluginRoots(root2, maxDepth = 5) {
332490
332600
  }
332491
332601
  for (const entry of entries) {
332492
332602
  if (!entry.isDirectory() || skip.has(entry.name) || entry.name.startsWith("release.locked-")) continue;
332493
- walk4(path7.join(dir, entry.name), depth + 1);
332603
+ walk4(path8.join(dir, entry.name), depth + 1);
332494
332604
  }
332495
332605
  };
332496
332606
  walk4(root2, 0);
@@ -332499,20 +332609,20 @@ function findPluginRoots(root2, maxDepth = 5) {
332499
332609
  function discoverPluginManifests(root2) {
332500
332610
  const manifests = [];
332501
332611
  for (const pluginRoot of findPluginRoots(root2)) {
332502
- const codexPath = path7.join(pluginRoot, ".codex-plugin", "plugin.json");
332503
- const claudePath = path7.join(pluginRoot, ".claude-plugin", "plugin.json");
332504
- const newmarkPath = path7.join(pluginRoot, ".newmark-plugin", "plugin.json");
332505
- const codex = fs6.existsSync(codexPath) ? readJson(codexPath) : null;
332506
- const claude = fs6.existsSync(claudePath) ? readJson(claudePath) : null;
332507
- const newmark = fs6.existsSync(newmarkPath) ? readJson(newmarkPath) : null;
332612
+ const codexPath = path8.join(pluginRoot, ".codex-plugin", "plugin.json");
332613
+ const claudePath = path8.join(pluginRoot, ".claude-plugin", "plugin.json");
332614
+ const newmarkPath = path8.join(pluginRoot, ".newmark-plugin", "plugin.json");
332615
+ const codex = fs7.existsSync(codexPath) ? readJson(codexPath) : null;
332616
+ const claude = fs7.existsSync(claudePath) ? readJson(claudePath) : null;
332617
+ const newmark = fs7.existsSync(newmarkPath) ? readJson(newmarkPath) : null;
332508
332618
  if (codex && typeof codex === "object") manifests.push(normalizeCodexPlugin(pluginRoot, codex));
332509
332619
  if (claude && typeof claude === "object") manifests.push(normalizeClaudePlugin(pluginRoot, claude));
332510
332620
  if (newmark && typeof newmark === "object") manifests.push(normalizeNewmarkPlugin(pluginRoot, newmark));
332511
332621
  }
332512
332622
  const projectOpencode = readOpenCodeManifest(root2, "project");
332513
332623
  if (projectOpencode) manifests.push(projectOpencode);
332514
- const userOpenCodeRoot = path7.join(os2.homedir(), ".config", "opencode");
332515
- if (path7.resolve(userOpenCodeRoot) !== path7.resolve(path7.join(root2, ".opencode"))) {
332624
+ const userOpenCodeRoot = path8.join(os2.homedir(), ".config", "opencode");
332625
+ if (path8.resolve(userOpenCodeRoot) !== path8.resolve(path8.join(root2, ".opencode"))) {
332516
332626
  const userOpenCode = readOpenCodeManifest(userOpenCodeRoot, "user");
332517
332627
  if (userOpenCode) manifests.push(userOpenCode);
332518
332628
  }
@@ -332520,20 +332630,20 @@ function discoverPluginManifests(root2) {
332520
332630
  }
332521
332631
  function readOpenCodeConfig(root2) {
332522
332632
  const candidates = [
332523
- path7.join(root2, "opencode.json"),
332524
- path7.join(root2, "opencode.jsonc"),
332525
- path7.join(root2, ".opencode", "opencode.json"),
332526
- path7.join(root2, ".opencode", "opencode.jsonc")
332633
+ path8.join(root2, "opencode.json"),
332634
+ path8.join(root2, "opencode.jsonc"),
332635
+ path8.join(root2, ".opencode", "opencode.json"),
332636
+ path8.join(root2, ".opencode", "opencode.jsonc")
332527
332637
  ];
332528
332638
  for (const filePath of candidates) {
332529
- if (fs6.existsSync(filePath)) return { path: filePath, value: readJsonLoose(filePath) };
332639
+ if (fs7.existsSync(filePath)) return { path: filePath, value: readJsonLoose(filePath) };
332530
332640
  }
332531
332641
  return null;
332532
332642
  }
332533
332643
  function readOpenCodeManifest(root2, scope) {
332534
- const localRoot = scope === "project" ? path7.join(root2, ".opencode") : root2;
332535
- const opencodeToolsDir = path7.join(localRoot, "tools");
332536
- const opencodePluginsDir = path7.join(localRoot, "plugins");
332644
+ const localRoot = scope === "project" ? path8.join(root2, ".opencode") : root2;
332645
+ const opencodeToolsDir = path8.join(localRoot, "tools");
332646
+ const opencodePluginsDir = path8.join(localRoot, "plugins");
332537
332647
  const tools = listCodeFiles(opencodeToolsDir);
332538
332648
  const pluginFiles = listCodeFiles(opencodePluginsDir);
332539
332649
  const config = readOpenCodeConfig(root2);
@@ -332574,18 +332684,18 @@ function readOpenCodeManifest(root2, scope) {
332574
332684
  }
332575
332685
  function discoverOpenCodeInstructionFiles(projectRoot, localRoot) {
332576
332686
  const candidates = [
332577
- path7.join(projectRoot, "AGENTS.md"),
332578
- path7.join(projectRoot, ".opencode", "instructions.md"),
332579
- path7.join(projectRoot, ".opencode", "AGENTS.md"),
332580
- path7.join(localRoot, "instructions.md"),
332581
- path7.join(localRoot, "AGENTS.md")
332687
+ path8.join(projectRoot, "AGENTS.md"),
332688
+ path8.join(projectRoot, ".opencode", "instructions.md"),
332689
+ path8.join(projectRoot, ".opencode", "AGENTS.md"),
332690
+ path8.join(localRoot, "instructions.md"),
332691
+ path8.join(localRoot, "AGENTS.md")
332582
332692
  ];
332583
- return Array.from(new Set(candidates.filter((filePath) => fs6.existsSync(filePath)))).sort();
332693
+ return Array.from(new Set(candidates.filter((filePath) => fs7.existsSync(filePath)))).sort();
332584
332694
  }
332585
332695
  function dedupeManifests(manifests) {
332586
332696
  const seen = /* @__PURE__ */ new Set();
332587
332697
  return manifests.filter((item) => {
332588
- const key3 = `${item.id}:${path7.resolve(item.root)}`;
332698
+ const key3 = `${item.id}:${path8.resolve(item.root)}`;
332589
332699
  if (seen.has(key3)) return false;
332590
332700
  seen.add(key3);
332591
332701
  return true;
@@ -332593,14 +332703,14 @@ function dedupeManifests(manifests) {
332593
332703
  }
332594
332704
  function listCodeFiles(dir) {
332595
332705
  try {
332596
- 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();
332706
+ 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();
332597
332707
  } catch {
332598
332708
  return [];
332599
332709
  }
332600
332710
  }
332601
332711
  function parseFrontmatterMarkdown(filePath) {
332602
332712
  try {
332603
- const content = fs6.readFileSync(filePath, "utf-8").replace(/^\uFEFF/, "");
332713
+ const content = fs7.readFileSync(filePath, "utf-8").replace(/^\uFEFF/, "");
332604
332714
  const match = content.match(/^---\s*([\s\S]*?)\s*---\s*/);
332605
332715
  if (!match) return { metadata: {}, body: content };
332606
332716
  const metadata = {};
@@ -332629,7 +332739,7 @@ function parseMetadataValue(raw) {
332629
332739
  function parseSimpleToml(filePath) {
332630
332740
  try {
332631
332741
  const metadata = {};
332632
- const content = fs6.readFileSync(filePath, "utf-8").replace(/^\uFEFF/, "");
332742
+ const content = fs7.readFileSync(filePath, "utf-8").replace(/^\uFEFF/, "");
332633
332743
  const multiline = null;
332634
332744
  if (multiline) return metadata;
332635
332745
  const lines = content.split(/\r?\n/);
@@ -332663,7 +332773,7 @@ function parseSimpleToml(filePath) {
332663
332773
  }
332664
332774
  }
332665
332775
  function agentPresetFromMetadata(filePath, ecosystem, metadata, body = "") {
332666
- const name50 = asString(metadata.name) || path7.basename(filePath).replace(/\.(?:toml|md)$/i, "");
332776
+ const name50 = asString(metadata.name) || path8.basename(filePath).replace(/\.(?:toml|md)$/i, "");
332667
332777
  const description = asString(metadata.description);
332668
332778
  const instructions = asString(metadata.developer_instructions || metadata.instructions || metadata.prompt) || body.trim();
332669
332779
  if (!name50 || !description) return null;
@@ -332691,12 +332801,12 @@ function listFilesRecursive(root2, extension, maxDepth = 4) {
332691
332801
  if (depth > maxDepth) return;
332692
332802
  let entries;
332693
332803
  try {
332694
- entries = fs6.readdirSync(dir, { withFileTypes: true });
332804
+ entries = fs7.readdirSync(dir, { withFileTypes: true });
332695
332805
  } catch {
332696
332806
  return;
332697
332807
  }
332698
332808
  for (const entry of entries) {
332699
- const full = path7.join(dir, entry.name);
332809
+ const full = path8.join(dir, entry.name);
332700
332810
  if (entry.isFile() && extension.test(entry.name)) results.push(full);
332701
332811
  if (entry.isDirectory() && !entry.name.startsWith(".git") && entry.name !== "node_modules") walk4(full, depth + 1);
332702
332812
  }
@@ -332707,10 +332817,10 @@ function listFilesRecursive(root2, extension, maxDepth = 4) {
332707
332817
  function discoverAgentPresets(root2) {
332708
332818
  const presets = [];
332709
332819
  const codexDirs = [
332710
- path7.join(root2, ".codex", "agents"),
332711
- path7.join(root2, ".agents", "agents"),
332712
- path7.join(os2.homedir(), ".codex", "agents"),
332713
- path7.join(os2.homedir(), ".agents", "agents")
332820
+ path8.join(root2, ".codex", "agents"),
332821
+ path8.join(root2, ".agents", "agents"),
332822
+ path8.join(os2.homedir(), ".codex", "agents"),
332823
+ path8.join(os2.homedir(), ".agents", "agents")
332714
332824
  ];
332715
332825
  for (const dir of codexDirs) {
332716
332826
  for (const filePath of listFilesRecursive(dir, /\.toml$/i, 1)) {
@@ -332719,14 +332829,14 @@ function discoverAgentPresets(root2) {
332719
332829
  }
332720
332830
  }
332721
332831
  const claudeDirs = [
332722
- path7.join(root2, ".claude", "agents"),
332723
- path7.join(os2.homedir(), ".claude", "agents"),
332724
- path7.join(os2.homedir(), ".config", "opencode", "agents")
332832
+ path8.join(root2, ".claude", "agents"),
332833
+ path8.join(os2.homedir(), ".claude", "agents"),
332834
+ path8.join(os2.homedir(), ".config", "opencode", "agents")
332725
332835
  ];
332726
332836
  for (const dir of claudeDirs) {
332727
332837
  for (const filePath of listFilesRecursive(dir, /\.md$/i, 1)) {
332728
332838
  const parsed = parseFrontmatterMarkdown(filePath);
332729
- const ecosystem = filePath.includes(`${path7.sep}.config${path7.sep}opencode${path7.sep}`) ? "opencode" : "claude-code";
332839
+ const ecosystem = filePath.includes(`${path8.sep}.config${path8.sep}opencode${path8.sep}`) ? "opencode" : "claude-code";
332730
332840
  const preset = agentPresetFromMetadata(filePath, ecosystem, parsed.metadata, parsed.body);
332731
332841
  if (preset) presets.push(preset);
332732
332842
  }
@@ -332763,15 +332873,15 @@ function findAgentPreset(root2, selector2) {
332763
332873
  preset.id,
332764
332874
  preset.name,
332765
332875
  `${preset.ecosystem}:${preset.name}`,
332766
- path7.basename(preset.path)
332876
+ path8.basename(preset.path)
332767
332877
  ].map((value) => String(value || "").toLowerCase());
332768
- return keys.includes(normalized) || path7.resolve(preset.path).toLowerCase() === path7.resolve(wanted).toLowerCase();
332878
+ return keys.includes(normalized) || path8.resolve(preset.path).toLowerCase() === path8.resolve(wanted).toLowerCase();
332769
332879
  }) || null;
332770
332880
  }
332771
332881
 
332772
332882
  // src/tools/terminalTakeover.ts
332773
- var fs7 = __toESM(require("fs"));
332774
- var path8 = __toESM(require("path"));
332883
+ var fs8 = __toESM(require("fs"));
332884
+ var path9 = __toESM(require("path"));
332775
332885
  var import_child_process2 = require("child_process");
332776
332886
  var import_crypto5 = require("crypto");
332777
332887
  var ROOT_TERMINAL_ACTOR_ID = "00000000-0000-4000-8000-000000000001";
@@ -332786,7 +332896,7 @@ function isoNow() {
332786
332896
  return (/* @__PURE__ */ new Date()).toISOString();
332787
332897
  }
332788
332898
  function canonicalPersistenceRoot(root2) {
332789
- const resolved = path8.resolve(root2 || process.cwd());
332899
+ const resolved = path9.resolve(root2 || process.cwd());
332790
332900
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
332791
332901
  }
332792
332902
  function portableWorkspacePath(input2) {
@@ -332795,7 +332905,7 @@ function portableWorkspacePath(input2) {
332795
332905
  if (wsl) return `${wsl[1].toLowerCase()}:/${String(wsl[2] || "").replace(/^\/+|\/+$/g, "")}`.replace(/\/$/, "");
332796
332906
  const drive = /^([a-zA-Z]):(?:\/(.*))?$/.exec(raw);
332797
332907
  if (drive) return `${drive[1].toLowerCase()}:/${String(drive[2] || "").replace(/^\/+|\/+$/g, "")}`.replace(/\/$/, "");
332798
- const resolved = path8.resolve(raw).replace(/\\/g, "/").replace(/\/+$/g, "");
332908
+ const resolved = path9.resolve(raw).replace(/\\/g, "/").replace(/\/+$/g, "");
332799
332909
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
332800
332910
  }
332801
332911
  function terminalTakeoverWorkspaceId(workspacePath) {
@@ -332890,12 +333000,12 @@ function nodePtyHasConptyDll() {
332890
333000
  if (process.platform !== "win32") return false;
332891
333001
  try {
332892
333002
  const packageJson = require.resolve("node-pty/package.json");
332893
- const packageRoot = path8.dirname(packageJson);
333003
+ const packageRoot = path9.dirname(packageJson);
332894
333004
  return [
332895
- path8.join(packageRoot, "build", "Release", "conpty", "conpty.dll"),
332896
- path8.join(packageRoot, "build", "Debug", "conpty", "conpty.dll"),
332897
- path8.join(packageRoot, "prebuilds", `${process.platform}-${process.arch}`, "conpty", "conpty.dll")
332898
- ].some((candidate) => fs7.existsSync(candidate));
333005
+ path9.join(packageRoot, "build", "Release", "conpty", "conpty.dll"),
333006
+ path9.join(packageRoot, "build", "Debug", "conpty", "conpty.dll"),
333007
+ path9.join(packageRoot, "prebuilds", `${process.platform}-${process.arch}`, "conpty", "conpty.dll")
333008
+ ].some((candidate) => fs8.existsSync(candidate));
332899
333009
  } catch {
332900
333010
  return false;
332901
333011
  }
@@ -333128,7 +333238,7 @@ function spawnTakeoverPty(shell, cwd, env, cols, rows) {
333128
333238
  };
333129
333239
  }
333130
333240
  function persistencePath(root2) {
333131
- return path8.join(root2, "Terminal", "Takeover.json");
333241
+ return path9.join(root2, "Terminal", "Takeover.json");
333132
333242
  }
333133
333243
  function validPersistedRecord(input2) {
333134
333244
  if (!input2 || typeof input2 !== "object") return null;
@@ -333169,7 +333279,7 @@ function ensurePersistenceLoaded(rootRaw) {
333169
333279
  if (loaded) return loaded;
333170
333280
  const records = /* @__PURE__ */ new Map();
333171
333281
  try {
333172
- const parsed = JSON.parse(fs7.readFileSync(persistencePath(root2), "utf-8"));
333282
+ const parsed = JSON.parse(fs8.readFileSync(persistencePath(root2), "utf-8"));
333173
333283
  if (Array.isArray(parsed.records)) {
333174
333284
  for (const input2 of parsed.records) {
333175
333285
  const record = validPersistedRecord(input2);
@@ -333187,19 +333297,19 @@ function persistEndedRecords(rootRaw) {
333187
333297
  const output = { version: 1, updatedAt: isoNow(), records };
333188
333298
  const filePath = persistencePath(root2);
333189
333299
  const tempPath = `${filePath}.tmp-${process.pid}-${(0, import_crypto5.randomUUID)()}`;
333190
- fs7.mkdirSync(path8.dirname(filePath), { recursive: true });
333191
- const fd = fs7.openSync(tempPath, "w");
333300
+ fs8.mkdirSync(path9.dirname(filePath), { recursive: true });
333301
+ const fd = fs8.openSync(tempPath, "w");
333192
333302
  try {
333193
- fs7.writeFileSync(fd, JSON.stringify(output, null, 2), "utf-8");
333194
- fs7.fsyncSync(fd);
333303
+ fs8.writeFileSync(fd, JSON.stringify(output, null, 2), "utf-8");
333304
+ fs8.fsyncSync(fd);
333195
333305
  } finally {
333196
- fs7.closeSync(fd);
333306
+ fs8.closeSync(fd);
333197
333307
  }
333198
333308
  try {
333199
- fs7.renameSync(tempPath, filePath);
333309
+ fs8.renameSync(tempPath, filePath);
333200
333310
  } catch (error) {
333201
333311
  try {
333202
- fs7.rmSync(tempPath, { force: true });
333312
+ fs8.rmSync(tempPath, { force: true });
333203
333313
  } catch {
333204
333314
  }
333205
333315
  throw error;
@@ -333432,8 +333542,8 @@ function runTerminalTakeover(input2) {
333432
333542
  }
333433
333543
 
333434
333544
  // src/tools/computerUse.ts
333435
- var fs8 = __toESM(require("fs"));
333436
- var path9 = __toESM(require("path"));
333545
+ var fs9 = __toESM(require("fs"));
333546
+ var path10 = __toESM(require("path"));
333437
333547
  var crypto6 = __toESM(require("crypto"));
333438
333548
  var os3 = __toESM(require("os"));
333439
333549
 
@@ -333641,8 +333751,8 @@ async function runPowerShell(script, timeout = 3e4, lane = "action") {
333641
333751
  return await runPersistentPowerShell(script, timeout, lane);
333642
333752
  }
333643
333753
  function tempScreenshotDir() {
333644
- const dir = path9.join(os3.tmpdir(), "newmark-computer-use");
333645
- fs8.mkdirSync(dir, { recursive: true });
333754
+ const dir = path10.join(os3.tmpdir(), "newmark-computer-use");
333755
+ fs9.mkdirSync(dir, { recursive: true });
333646
333756
  const now2 = Date.now();
333647
333757
  if (now2 - lastScreenshotCleanupAt >= SCREENSHOT_CLEANUP_INTERVAL_MS) {
333648
333758
  lastScreenshotCleanupAt = now2;
@@ -333664,7 +333774,7 @@ function ephemeralScreenshotPath(kind, directory = tempScreenshotDir(), createdA
333664
333774
  const pid = Math.max(1, Math.floor(Number(ownerPid) || process.pid));
333665
333775
  const timestamp = Math.max(0, Math.floor(Number(createdAt) || Date.now()));
333666
333776
  const nonce = /^[a-f0-9]{8}$/i.test(String(suffix)) ? String(suffix).toLowerCase() : crypto6.randomBytes(4).toString("hex");
333667
- return path9.join(directory, `${kind}-p${pid}-t${timestamp}-${nonce}.jpg`);
333777
+ return path10.join(directory, `${kind}-p${pid}-t${timestamp}-${nonce}.jpg`);
333668
333778
  }
333669
333779
  function isProcessAlive(pid) {
333670
333780
  if (!Number.isSafeInteger(pid) || pid <= 0) return false;
@@ -333677,14 +333787,14 @@ function isProcessAlive(pid) {
333677
333787
  }
333678
333788
  }
333679
333789
  function cleanupStaleScreenshots(options = {}) {
333680
- const directory = options.directory || path9.join(os3.tmpdir(), "newmark-computer-use");
333790
+ const directory = options.directory || path10.join(os3.tmpdir(), "newmark-computer-use");
333681
333791
  const now2 = Number.isFinite(Number(options.now)) ? Number(options.now) : Date.now();
333682
333792
  const processAlive = options.isProcessAlive || isProcessAlive;
333683
333793
  const ownedPattern = /^(?:observe|app)-p([1-9]\d*)-t(\d{10,16})-[a-f0-9]{8}\.jpg$/i;
333684
333794
  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;
333685
333795
  let names = [];
333686
333796
  try {
333687
- names = fs8.readdirSync(directory);
333797
+ names = fs9.readdirSync(directory);
333688
333798
  } catch {
333689
333799
  return { removed: 0 };
333690
333800
  }
@@ -333693,10 +333803,10 @@ function cleanupStaleScreenshots(options = {}) {
333693
333803
  const owned = ownedPattern.exec(name50);
333694
333804
  const isLegacy = !owned && legacyPattern.test(name50);
333695
333805
  if (!owned && !isLegacy) continue;
333696
- const filePath = path9.join(directory, name50);
333806
+ const filePath = path10.join(directory, name50);
333697
333807
  let stats;
333698
333808
  try {
333699
- stats = fs8.lstatSync(filePath);
333809
+ stats = fs9.lstatSync(filePath);
333700
333810
  if (!stats.isFile() || stats.isSymbolicLink()) continue;
333701
333811
  } catch {
333702
333812
  continue;
@@ -333720,7 +333830,7 @@ function cleanupStaleScreenshots(options = {}) {
333720
333830
  }
333721
333831
  if (!shouldRemove) continue;
333722
333832
  try {
333723
- fs8.unlinkSync(filePath);
333833
+ fs9.unlinkSync(filePath);
333724
333834
  removed += 1;
333725
333835
  } catch {
333726
333836
  }
@@ -333743,7 +333853,7 @@ function captureBounds(maxWidth, maxHeight) {
333743
333853
  }
333744
333854
  function removeEphemeralScreenshot(outPath) {
333745
333855
  try {
333746
- fs8.unlinkSync(outPath);
333856
+ fs9.unlinkSync(outPath);
333747
333857
  } catch {
333748
333858
  }
333749
333859
  }
@@ -333819,7 +333929,7 @@ async function startTakeoverOverlay(durationMs = 0, input2 = {}) {
333819
333929
  const width = 2;
333820
333930
  const speedSeconds = 3;
333821
333931
  const ownerPid = Math.max(0, Math.floor(Number(input2.ownerPid ?? process.pid) || 0));
333822
- const scriptPath = path9.join(tempScreenshotDir(), `takeover-overlay-${timestampName()}-${crypto6.randomBytes(4).toString("hex")}.ps1`);
333932
+ const scriptPath = path10.join(tempScreenshotDir(), `takeover-overlay-${timestampName()}-${crypto6.randomBytes(4).toString("hex")}.ps1`);
333823
333933
  const script = [
333824
333934
  "Add-Type -AssemblyName System.Windows.Forms",
333825
333935
  "Add-Type -AssemblyName System.Drawing",
@@ -333958,7 +334068,7 @@ async function startTakeoverOverlay(durationMs = 0, input2 = {}) {
333958
334068
  "[System.Windows.Forms.Application]::Run()",
333959
334069
  "try { Remove-Item -LiteralPath $PSCommandPath -Force -ErrorAction SilentlyContinue } catch {}"
333960
334070
  ].filter(Boolean).join("\r\n");
333961
- fs8.writeFileSync(scriptPath, `\uFEFF${script}`, "utf8");
334071
+ fs9.writeFileSync(scriptPath, `\uFEFF${script}`, "utf8");
333962
334072
  const createCommand = [
333963
334073
  `$cmd = 'powershell.exe -NoProfile -ExecutionPolicy Bypass -File ' + ${psQuote(`"${scriptPath}"`)}`,
333964
334074
  `$startup = ([wmiclass]'Win32_ProcessStartup').CreateInstance()`,
@@ -333971,7 +334081,7 @@ async function startTakeoverOverlay(durationMs = 0, input2 = {}) {
333971
334081
  const pid = Number(String(result.output || "").trim().split(/\r?\n/).pop() || 0);
333972
334082
  if (!Number.isFinite(pid) || pid <= 0 || !result.ok) {
333973
334083
  try {
333974
- fs8.unlinkSync(scriptPath);
334084
+ fs9.unlinkSync(scriptPath);
333975
334085
  } catch {
333976
334086
  }
333977
334087
  return { ok: false, action: "takeover_start", error: result.output || "Overlay failed to start." };
@@ -333998,7 +334108,7 @@ function parseJsonArray(text) {
333998
334108
  return [];
333999
334109
  }
334000
334110
  function observationKey(workspacePath, ownerId) {
334001
- return `${path9.resolve(workspacePath || process.cwd()).toLowerCase()}::${String(ownerId || "direct")}`;
334111
+ return `${path10.resolve(workspacePath || process.cwd()).toLowerCase()}::${String(ownerId || "direct")}`;
334002
334112
  }
334003
334113
  function sceneGeneration(apps, elements) {
334004
334114
  const seed = [
@@ -334214,7 +334324,7 @@ async function cropScreenshot(workspacePath, ownerId, allowEphemeralVisionImage,
334214
334324
  ...parsed
334215
334325
  };
334216
334326
  if (includeRawUi) payload.perception.elements = elements;
334217
- const imageAvailable = parsed.image_available === true && fs8.existsSync(outPath);
334327
+ const imageAvailable = parsed.image_available === true && fs9.existsSync(outPath);
334218
334328
  if (allowEphemeralVisionImage && imageAvailable) {
334219
334329
  payload.vision_image_path = outPath;
334220
334330
  retainedForVision = true;
@@ -334477,7 +334587,7 @@ async function screenshot(workspacePath, ownerId, allowEphemeralVisionImage, inc
334477
334587
  ...parsed
334478
334588
  };
334479
334589
  if (includeRawUi) payload.perception.elements = ui.elements;
334480
- const imageAvailable = parsed.image_available === true && fs8.existsSync(outPath);
334590
+ const imageAvailable = parsed.image_available === true && fs9.existsSync(outPath);
334481
334591
  if (allowEphemeralVisionImage && imageAvailable) {
334482
334592
  payload.vision_image_path = outPath;
334483
334593
  retainedForVision = true;
@@ -334801,13 +334911,13 @@ async function runComputerUse(options) {
334801
334911
  }
334802
334912
 
334803
334913
  // src/core/ssh.ts
334804
- var fs10 = __toESM(require("fs"));
334805
- var path11 = __toESM(require("path"));
334914
+ var fs11 = __toESM(require("fs"));
334915
+ var path12 = __toESM(require("path"));
334806
334916
 
334807
334917
  // src/core/asyncProcess.ts
334808
334918
  var import_child_process4 = require("child_process");
334809
- var fs9 = __toESM(require("fs/promises"));
334810
- var path10 = __toESM(require("path"));
334919
+ var fs10 = __toESM(require("fs/promises"));
334920
+ var path11 = __toESM(require("path"));
334811
334921
  var STOP_SETTLEMENT_WATCHDOG_MS = 500;
334812
334922
  function signalMessage(signal) {
334813
334923
  const reason = signal?.reason;
@@ -334817,7 +334927,7 @@ function signalMessage(signal) {
334817
334927
  }
334818
334928
  function trustedWindowsTaskkillPath() {
334819
334929
  const windowsRoot = String(process.env.SystemRoot || process.env.WINDIR || "C:\\Windows");
334820
- return path10.join(windowsRoot, "System32", "taskkill.exe");
334930
+ return path11.join(windowsRoot, "System32", "taskkill.exe");
334821
334931
  }
334822
334932
  function stopProcessTree(child) {
334823
334933
  const pid = child.pid;
@@ -334986,7 +335096,7 @@ async function runAsyncWindowsBatch(command, args, options = {}) {
334986
335096
  }
334987
335097
  async function accessible(filePath) {
334988
335098
  try {
334989
- await fs9.access(filePath);
335099
+ await fs10.access(filePath);
334990
335100
  return true;
334991
335101
  } catch {
334992
335102
  return false;
@@ -334995,14 +335105,14 @@ async function accessible(filePath) {
334995
335105
  async function resolveWindowsLauncher(command) {
334996
335106
  const clean = String(command || "").trim();
334997
335107
  if (!clean) return "";
334998
- if (path10.isAbsolute(clean) || /[\\/]/.test(clean)) {
334999
- const absolute = path10.resolve(clean);
335108
+ if (path11.isAbsolute(clean) || /[\\/]/.test(clean)) {
335109
+ const absolute = path11.resolve(clean);
335000
335110
  return await accessible(absolute) ? absolute : "";
335001
335111
  }
335002
- for (const entry of String(process.env.PATH || "").split(path10.delimiter)) {
335112
+ for (const entry of String(process.env.PATH || "").split(path11.delimiter)) {
335003
335113
  const directory = entry.trim().replace(/^"|"$/g, "");
335004
335114
  if (!directory) continue;
335005
- const candidate = path10.join(directory, clean);
335115
+ const candidate = path11.join(directory, clean);
335006
335116
  if (await accessible(candidate)) return candidate;
335007
335117
  }
335008
335118
  return "";
@@ -335010,11 +335120,11 @@ async function resolveWindowsLauncher(command) {
335010
335120
  async function resolveNpmBatchTarget(batchPath) {
335011
335121
  let source = "";
335012
335122
  try {
335013
- source = await fs9.readFile(batchPath, "utf8");
335123
+ source = await fs10.readFile(batchPath, "utf8");
335014
335124
  } catch {
335015
335125
  return null;
335016
335126
  }
335017
- const directory = path10.dirname(batchPath);
335127
+ const directory = path11.dirname(batchPath);
335018
335128
  let relativeScript = "";
335019
335129
  const direct = /(?:%~dp0|%dp0%)\\?([^"\r\n]+)"\s+%\*/i.exec(source);
335020
335130
  if (direct) relativeScript = direct[1];
@@ -335028,10 +335138,10 @@ async function resolveNpmBatchTarget(batchPath) {
335028
335138
  }
335029
335139
  }
335030
335140
  if (!relativeScript) return null;
335031
- const scriptPath = path10.resolve(directory, relativeScript.replace(/\\/g, path10.sep));
335032
- const directoryPrefix = `${path10.resolve(directory).toLowerCase()}${path10.sep}`;
335141
+ const scriptPath = path11.resolve(directory, relativeScript.replace(/\\/g, path11.sep));
335142
+ const directoryPrefix = `${path11.resolve(directory).toLowerCase()}${path11.sep}`;
335033
335143
  if (!scriptPath.toLowerCase().startsWith(directoryPrefix) || !await accessible(scriptPath)) return null;
335034
- const siblingNode = path10.join(directory, "node.exe");
335144
+ const siblingNode = path11.join(directory, "node.exe");
335035
335145
  const nodePath = await accessible(siblingNode) ? siblingNode : await resolveWindowsLauncher("node.exe") || await resolveWindowsLauncher("node");
335036
335146
  return nodePath ? { nodePath, scriptPath } : null;
335037
335147
  }
@@ -335074,19 +335184,19 @@ var SshManager = class {
335074
335184
  rootPath;
335075
335185
  runner;
335076
335186
  storePath() {
335077
- return path11.join(this.rootPath, "Work", "SSH.json");
335187
+ return path12.join(this.rootPath, "Work", "SSH.json");
335078
335188
  }
335079
335189
  ensureStore() {
335080
335190
  try {
335081
- fs10.mkdirSync(path11.join(this.rootPath, "Work"), { recursive: true });
335082
- if (!fs10.existsSync(this.storePath())) fs10.writeFileSync(this.storePath(), "[]", "utf-8");
335191
+ fs11.mkdirSync(path12.join(this.rootPath, "Work"), { recursive: true });
335192
+ if (!fs11.existsSync(this.storePath())) fs11.writeFileSync(this.storePath(), "[]", "utf-8");
335083
335193
  } catch {
335084
335194
  }
335085
335195
  }
335086
335196
  readRaw() {
335087
335197
  this.ensureStore();
335088
335198
  try {
335089
- const parsed = JSON.parse(fs10.readFileSync(this.storePath(), "utf-8").replace(/^\uFEFF/, ""));
335199
+ const parsed = JSON.parse(fs11.readFileSync(this.storePath(), "utf-8").replace(/^\uFEFF/, ""));
335090
335200
  if (!Array.isArray(parsed)) return [];
335091
335201
  return parsed.map((item) => this.normalize(item)).filter((item) => !!item);
335092
335202
  } catch {
@@ -335095,7 +335205,7 @@ var SshManager = class {
335095
335205
  }
335096
335206
  writeRaw(items) {
335097
335207
  this.ensureStore();
335098
- fs10.writeFileSync(this.storePath(), JSON.stringify(items, null, 2), "utf-8");
335208
+ fs11.writeFileSync(this.storePath(), JSON.stringify(items, null, 2), "utf-8");
335099
335209
  }
335100
335210
  normalize(raw) {
335101
335211
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
@@ -335275,8 +335385,8 @@ var SshManager = class {
335275
335385
  };
335276
335386
 
335277
335387
  // src/core/workspace.ts
335278
- var fs11 = __toESM(require("fs"));
335279
- var path12 = __toESM(require("path"));
335388
+ var fs12 = __toESM(require("fs"));
335389
+ var path13 = __toESM(require("path"));
335280
335390
  var crypto7 = __toESM(require("crypto"));
335281
335391
  function lastEmbeddedWindowsPath(input2) {
335282
335392
  const matcher = /[A-Za-z]:[\\/]/g;
@@ -335295,22 +335405,22 @@ function normalizeHostWorkspacePath(input2, platform = process.platform) {
335295
335405
  const raw = String(input2 || "").trim();
335296
335406
  const embeddedWindowsPath = lastEmbeddedWindowsPath(raw);
335297
335407
  if (platform === "win32") {
335298
- if (embeddedWindowsPath) return path12.win32.normalize(embeddedWindowsPath.replace(/\//g, "\\"));
335408
+ if (embeddedWindowsPath) return path13.win32.normalize(embeddedWindowsPath.replace(/\//g, "\\"));
335299
335409
  const wsl = /^\/mnt\/([a-zA-Z])(?:\/(.*))?$/.exec(raw.replace(/\\/g, "/"));
335300
- if (wsl) return path12.win32.normalize(`${wsl[1].toUpperCase()}:\\${String(wsl[2] || "").replace(/\//g, "\\")}`);
335301
- return path12.win32.resolve(raw || ".");
335410
+ if (wsl) return path13.win32.normalize(`${wsl[1].toUpperCase()}:\\${String(wsl[2] || "").replace(/\//g, "\\")}`);
335411
+ return path13.win32.resolve(raw || ".");
335302
335412
  }
335303
335413
  if (platform === "linux" && embeddedWindowsPath) {
335304
335414
  const drive = embeddedWindowsPath[0].toLowerCase();
335305
335415
  const rest = embeddedWindowsPath.slice(3).replace(/\\/g, "/").replace(/^\/+/, "");
335306
- return path12.posix.resolve(`/mnt/${drive}/${rest}`);
335416
+ return path13.posix.resolve(`/mnt/${drive}/${rest}`);
335307
335417
  }
335308
- return path12.posix.resolve(raw || ".");
335418
+ return path13.posix.resolve(raw || ".");
335309
335419
  }
335310
335420
  function isPathInside(parent, child) {
335311
335421
  try {
335312
- const relative6 = path12.relative(path12.resolve(parent), path12.resolve(child));
335313
- return relative6 === "" || !!relative6 && !relative6.startsWith("..") && !path12.isAbsolute(relative6);
335422
+ const relative6 = path13.relative(path13.resolve(parent), path13.resolve(child));
335423
+ return relative6 === "" || !!relative6 && !relative6.startsWith("..") && !path13.isAbsolute(relative6);
335314
335424
  } catch {
335315
335425
  return false;
335316
335426
  }
@@ -335318,7 +335428,7 @@ function isPathInside(parent, child) {
335318
335428
  function isProtectedInstallWorkspacePath(candidate) {
335319
335429
  const value = String(candidate || "").trim();
335320
335430
  if (!value) return false;
335321
- const roots = [path12.dirname(process.execPath)];
335431
+ const roots = [path13.dirname(process.execPath)];
335322
335432
  if (process.platform === "win32") {
335323
335433
  roots.push(
335324
335434
  process.env.ProgramFiles || "",
@@ -335326,7 +335436,7 @@ function isProtectedInstallWorkspacePath(candidate) {
335326
335436
  process.env.ProgramW6432 || ""
335327
335437
  );
335328
335438
  }
335329
- const resolved = path12.resolve(value);
335439
+ const resolved = path13.resolve(value);
335330
335440
  return roots.filter(Boolean).some((root2) => isPathInside(root2, resolved));
335331
335441
  }
335332
335442
  var WorkspaceManager = class {
@@ -335336,16 +335446,16 @@ var WorkspaceManager = class {
335336
335446
  this.detached = options.detached === true;
335337
335447
  this.pcHash = this.loadPcHash();
335338
335448
  if (this.detached) return;
335339
- const workDir = path12.join(rootPath, "Work");
335449
+ const workDir = path13.join(rootPath, "Work");
335340
335450
  try {
335341
- fs11.mkdirSync(workDir, { recursive: true });
335451
+ fs12.mkdirSync(workDir, { recursive: true });
335342
335452
  } catch {
335343
335453
  }
335344
335454
  for (const fn of ["Local.json", "External.json"]) {
335345
- const p = path12.join(workDir, fn);
335346
- if (!fs11.existsSync(p)) {
335455
+ const p = path13.join(workDir, fn);
335456
+ if (!fs12.existsSync(p)) {
335347
335457
  try {
335348
- fs11.writeFileSync(p, "[]", "utf-8");
335458
+ fs12.writeFileSync(p, "[]", "utf-8");
335349
335459
  } catch {
335350
335460
  }
335351
335461
  }
@@ -335366,7 +335476,7 @@ var WorkspaceManager = class {
335366
335476
  detached;
335367
335477
  loadPcHash() {
335368
335478
  try {
335369
- const h2 = fs11.readFileSync(path12.join(this.rootPath, "PC_Hash.config"), "utf-8");
335479
+ const h2 = fs12.readFileSync(path13.join(this.rootPath, "PC_Hash.config"), "utf-8");
335370
335480
  return h2.trim();
335371
335481
  } catch {
335372
335482
  return "";
@@ -335381,19 +335491,19 @@ var WorkspaceManager = class {
335381
335491
  if (this.external.length !== before) this.saveExternal();
335382
335492
  }
335383
335493
  scan() {
335384
- const w = path12.join(this.rootPath, "Work");
335385
- if (!fs11.existsSync(w)) return;
335494
+ const w = path13.join(this.rootPath, "Work");
335495
+ if (!fs12.existsSync(w)) return;
335386
335496
  let internalChanged = false;
335387
335497
  let externalChanged = false;
335388
335498
  try {
335389
- const local = JSON.parse(fs11.readFileSync(path12.join(w, "Local.json"), "utf-8"));
335499
+ const local = JSON.parse(fs12.readFileSync(path13.join(w, "Local.json"), "utf-8"));
335390
335500
  this.internal = Array.isArray(local) ? local.map((item) => this.normalizeInternalWorkspace(item, (changed) => {
335391
335501
  internalChanged = internalChanged || changed;
335392
335502
  })) : [];
335393
335503
  } catch {
335394
335504
  }
335395
335505
  try {
335396
- const ext = JSON.parse(fs11.readFileSync(path12.join(w, "External.json"), "utf-8"));
335506
+ const ext = JSON.parse(fs12.readFileSync(path13.join(w, "External.json"), "utf-8"));
335397
335507
  const normalized = Array.isArray(ext) ? ext.map((item) => this.normalizeExternalWorkspace(item, (changed) => {
335398
335508
  externalChanged = externalChanged || changed;
335399
335509
  })) : [];
@@ -335404,13 +335514,13 @@ var WorkspaceManager = class {
335404
335514
  });
335405
335515
  } catch {
335406
335516
  }
335407
- for (const entry of fs11.readdirSync(w, { withFileTypes: true })) {
335517
+ for (const entry of fs12.readdirSync(w, { withFileTypes: true })) {
335408
335518
  if (entry.isDirectory() && !["Local.json", "External.json", ".ssh"].includes(entry.name)) {
335409
335519
  if (!this.internal.find((wi) => wi.name === entry.name)) {
335410
335520
  this.internal.push({
335411
- id: this.stableWorkspaceId("local", path12.join(w, entry.name)),
335521
+ id: this.stableWorkspaceId("local", path13.join(w, entry.name)),
335412
335522
  name: entry.name,
335413
- path: path12.join(w, entry.name),
335523
+ path: path13.join(w, entry.name),
335414
335524
  isInternal: true,
335415
335525
  hostBinding: "",
335416
335526
  icon: entry.name.charAt(0).toUpperCase()
@@ -335423,9 +335533,9 @@ var WorkspaceManager = class {
335423
335533
  if (externalChanged) this.saveExternal();
335424
335534
  }
335425
335535
  normalizeInternalWorkspace(input2, markChanged) {
335426
- const rawName = String(input2?.name || path12.basename(String(input2?.path || "")) || "").trim();
335536
+ const rawName = String(input2?.name || path13.basename(String(input2?.path || "")) || "").trim();
335427
335537
  const name50 = rawName || (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").replace("T", "_").slice(0, 15);
335428
- const expectedPath = path12.join(this.rootPath, "Work", name50);
335538
+ const expectedPath = path13.join(this.rootPath, "Work", name50);
335429
335539
  const id = this.stableWorkspaceId("local", expectedPath);
335430
335540
  if (normalizeHostWorkspacePath(String(input2?.path || "")) !== normalizeHostWorkspacePath(expectedPath) || input2?.isInternal !== true || input2?.id !== id) markChanged(true);
335431
335541
  return {
@@ -335446,11 +335556,11 @@ var WorkspaceManager = class {
335446
335556
  return {
335447
335557
  ...input2,
335448
335558
  id,
335449
- name: String(input2?.name || path12.basename(workspacePath) || id),
335559
+ name: String(input2?.name || path13.basename(workspacePath) || id),
335450
335560
  path: workspacePath,
335451
335561
  isInternal: false,
335452
335562
  hostBinding: String(input2?.hostBinding || ""),
335453
- icon: String(input2?.icon || path12.basename(workspacePath).charAt(0).toUpperCase()),
335563
+ icon: String(input2?.icon || path13.basename(workspacePath).charAt(0).toUpperCase()),
335454
335564
  kind
335455
335565
  };
335456
335566
  }
@@ -335471,11 +335581,11 @@ var WorkspaceManager = class {
335471
335581
  const resolved = normalizeHostWorkspacePath(target);
335472
335582
  let real = resolved;
335473
335583
  try {
335474
- real = fs11.existsSync(resolved) ? fs11.realpathSync.native(resolved) : resolved;
335584
+ real = fs12.existsSync(resolved) ? fs12.realpathSync.native(resolved) : resolved;
335475
335585
  } catch {
335476
335586
  real = resolved;
335477
335587
  }
335478
- const normalized = path12.normalize(real).replace(/[\\/]+$/, "");
335588
+ const normalized = path13.normalize(real).replace(/[\\/]+$/, "");
335479
335589
  return process.platform === "win32" ? normalized.toLowerCase() : normalized;
335480
335590
  }
335481
335591
  stableWorkspaceId(kind, workspacePath) {
@@ -335501,8 +335611,8 @@ var WorkspaceManager = class {
335501
335611
  isInsideRoot(target) {
335502
335612
  const root2 = this.canonicalWorkspacePath(this.rootPath);
335503
335613
  const candidate = this.canonicalWorkspacePath(target);
335504
- const rel = path12.relative(root2, candidate);
335505
- return rel === "" || !!rel && !rel.startsWith("..") && !path12.isAbsolute(rel);
335614
+ const rel = path13.relative(root2, candidate);
335615
+ return rel === "" || !!rel && !rel.startsWith("..") && !path13.isAbsolute(rel);
335506
335616
  }
335507
335617
  canonicalRemotePath(target) {
335508
335618
  let cleaned = String(target || "").trim().replace(/\\/g, "/").replace(/\/+$/g, "");
@@ -335531,13 +335641,13 @@ var WorkspaceManager = class {
335531
335641
  return deduped;
335532
335642
  }
335533
335643
  statePath() {
335534
- return path12.join(this.rootPath, "Work", "State.json");
335644
+ return path13.join(this.rootPath, "Work", "State.json");
335535
335645
  }
335536
335646
  readState() {
335537
335647
  const p = this.statePath();
335538
- if (!fs11.existsSync(p)) return {};
335648
+ if (!fs12.existsSync(p)) return {};
335539
335649
  try {
335540
- const raw = fs11.readFileSync(p, "utf-8").replace(/^\uFEFF/, "");
335650
+ const raw = fs12.readFileSync(p, "utf-8").replace(/^\uFEFF/, "");
335541
335651
  const parsed = JSON.parse(raw);
335542
335652
  if (parsed && typeof parsed === "object") return parsed;
335543
335653
  } catch {
@@ -335558,8 +335668,8 @@ var WorkspaceManager = class {
335558
335668
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
335559
335669
  };
335560
335670
  try {
335561
- fs11.mkdirSync(path12.dirname(p), { recursive: true });
335562
- fs11.writeFileSync(p, JSON.stringify(state, null, 2), "utf-8");
335671
+ fs12.mkdirSync(path13.dirname(p), { recursive: true });
335672
+ fs12.writeFileSync(p, JSON.stringify(state, null, 2), "utf-8");
335563
335673
  } catch {
335564
335674
  }
335565
335675
  }
@@ -335618,17 +335728,17 @@ var WorkspaceManager = class {
335618
335728
  }
335619
335729
  saveInternal() {
335620
335730
  if (this.detached) return;
335621
- const p = path12.join(this.rootPath, "Work", "Local.json");
335731
+ const p = path13.join(this.rootPath, "Work", "Local.json");
335622
335732
  this.internal = this.dedupeByPath(this.internal);
335623
335733
  this.sortWorkspaces();
335624
- fs11.writeFileSync(p, JSON.stringify(this.internal, null, 2), "utf-8");
335734
+ fs12.writeFileSync(p, JSON.stringify(this.internal, null, 2), "utf-8");
335625
335735
  }
335626
335736
  saveExternal() {
335627
335737
  if (this.detached) return;
335628
- const p = path12.join(this.rootPath, "Work", "External.json");
335738
+ const p = path13.join(this.rootPath, "Work", "External.json");
335629
335739
  this.external = this.dedupeByPath(this.external);
335630
335740
  this.sortWorkspaces();
335631
- fs11.writeFileSync(p, JSON.stringify(this.external, null, 2), "utf-8");
335741
+ fs12.writeFileSync(p, JSON.stringify(this.external, null, 2), "utf-8");
335632
335742
  }
335633
335743
  sleepSync(ms) {
335634
335744
  if (ms <= 0) return;
@@ -335636,48 +335746,48 @@ var WorkspaceManager = class {
335636
335746
  Atomics.wait(new Int32Array(buffer), 0, 0, ms);
335637
335747
  }
335638
335748
  isInternalWorkspacePath(target) {
335639
- const workRoot = path12.resolve(this.rootPath, "Work");
335640
- const resolved = path12.resolve(target);
335641
- const rel = path12.relative(workRoot, resolved);
335642
- return !!rel && !rel.startsWith("..") && !path12.isAbsolute(rel);
335749
+ const workRoot = path13.resolve(this.rootPath, "Work");
335750
+ const resolved = path13.resolve(target);
335751
+ const rel = path13.relative(workRoot, resolved);
335752
+ return !!rel && !rel.startsWith("..") && !path13.isAbsolute(rel);
335643
335753
  }
335644
335754
  clearReadOnlyRecursive(target) {
335645
- if (!fs11.existsSync(target)) return;
335646
- const stat = fs11.lstatSync(target);
335755
+ if (!fs12.existsSync(target)) return;
335756
+ const stat = fs12.lstatSync(target);
335647
335757
  try {
335648
- fs11.chmodSync(target, stat.mode | 448);
335758
+ fs12.chmodSync(target, stat.mode | 448);
335649
335759
  } catch {
335650
335760
  }
335651
335761
  if (!stat.isDirectory()) return;
335652
- for (const entry of fs11.readdirSync(target)) {
335653
- this.clearReadOnlyRecursive(path12.join(target, entry));
335762
+ for (const entry of fs12.readdirSync(target)) {
335763
+ this.clearReadOnlyRecursive(path13.join(target, entry));
335654
335764
  }
335655
335765
  }
335656
335766
  removeInternalDirectory(target) {
335657
- const resolved = path12.resolve(target);
335767
+ const resolved = path13.resolve(target);
335658
335768
  if (!this.isInternalWorkspacePath(resolved)) return false;
335659
- if (!fs11.existsSync(resolved)) return true;
335769
+ if (!fs12.existsSync(resolved)) return true;
335660
335770
  const delays = [0, 50, 100, 200, 400, 800, 1200];
335661
335771
  for (const delay of delays) {
335662
335772
  this.sleepSync(delay);
335663
335773
  try {
335664
- fs11.rmSync(resolved, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
335774
+ fs12.rmSync(resolved, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
335665
335775
  } catch {
335666
335776
  }
335667
- if (!fs11.existsSync(resolved)) return true;
335777
+ if (!fs12.existsSync(resolved)) return true;
335668
335778
  try {
335669
335779
  this.clearReadOnlyRecursive(resolved);
335670
- fs11.rmSync(resolved, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
335780
+ fs12.rmSync(resolved, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
335671
335781
  } catch {
335672
335782
  }
335673
- if (!fs11.existsSync(resolved)) return true;
335783
+ if (!fs12.existsSync(resolved)) return true;
335674
335784
  }
335675
335785
  return false;
335676
335786
  }
335677
335787
  createInternal(name50) {
335678
335788
  const n3 = name50 || (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").replace("T", "_").slice(0, 15);
335679
- const d3 = path12.join(this.rootPath, "Work", n3);
335680
- fs11.mkdirSync(d3, { recursive: true });
335789
+ const d3 = path13.join(this.rootPath, "Work", n3);
335790
+ fs12.mkdirSync(d3, { recursive: true });
335681
335791
  const existing = this.findWorkspaceByPath(d3);
335682
335792
  if (existing) {
335683
335793
  this.current = existing;
@@ -335699,15 +335809,15 @@ var WorkspaceManager = class {
335699
335809
  return ws;
335700
335810
  }
335701
335811
  addExternal(p) {
335702
- const resolved = path12.resolve(p);
335703
- if (!fs11.existsSync(resolved) || this.isInsideRoot(resolved)) return null;
335812
+ const resolved = path13.resolve(p);
335813
+ if (!fs12.existsSync(resolved) || this.isInsideRoot(resolved)) return null;
335704
335814
  const existing = this.findWorkspaceByPath(resolved);
335705
335815
  if (existing) {
335706
335816
  this.current = existing;
335707
335817
  this.saveState();
335708
335818
  return existing;
335709
335819
  }
335710
- const name50 = path12.basename(resolved);
335820
+ const name50 = path13.basename(resolved);
335711
335821
  const ws = {
335712
335822
  id: this.stableWorkspaceId("local", resolved),
335713
335823
  name: name50,
@@ -335725,10 +335835,10 @@ var WorkspaceManager = class {
335725
335835
  addSshExternal(input2) {
335726
335836
  if (!input2.sshConnectionId || !input2.remotePath || !input2.remotePcHash) return null;
335727
335837
  const remotePath = this.canonicalRemotePath(input2.remotePath);
335728
- const baseName = (input2.name || path12.basename(remotePath.replace(/[\\/]+$/, "")) || input2.sshConnectionId || "ssh-workspace").trim();
335838
+ const baseName = (input2.name || path13.basename(remotePath.replace(/[\\/]+$/, "")) || input2.sshConnectionId || "ssh-workspace").trim();
335729
335839
  const safeName = baseName.replace(/[<>:"/\\|?*\x00-\x1F]/g, "-").replace(/\s+/g, " ").trim() || "ssh-workspace";
335730
- const shadowRoot = input2.localPath ? path12.resolve(input2.localPath) : path12.join(this.rootPath, "Work", ".ssh", `${input2.sshConnectionId}-${crypto7.createHash("sha256").update(remotePath).digest("hex").slice(0, 16)}`);
335731
- fs11.mkdirSync(shadowRoot, { recursive: true });
335840
+ const shadowRoot = input2.localPath ? path13.resolve(input2.localPath) : path13.join(this.rootPath, "Work", ".ssh", `${input2.sshConnectionId}-${crypto7.createHash("sha256").update(remotePath).digest("hex").slice(0, 16)}`);
335841
+ fs12.mkdirSync(shadowRoot, { recursive: true });
335732
335842
  const existing = this.findSshWorkspaceByRemotePath(input2.sshConnectionId, remotePath);
335733
335843
  const ws = {
335734
335844
  ...existing || {},
@@ -335810,7 +335920,7 @@ var WorkspaceManager = class {
335810
335920
  currentAgentPrompt() {
335811
335921
  if (!this.current) return null;
335812
335922
  try {
335813
- return fs11.readFileSync(path12.join(this.current.path, "agent.md"), "utf-8");
335923
+ return fs12.readFileSync(path13.join(this.current.path, "agent.md"), "utf-8");
335814
335924
  } catch {
335815
335925
  return null;
335816
335926
  }
@@ -335819,8 +335929,8 @@ var WorkspaceManager = class {
335819
335929
  const perm = this.config.getStr("workspace", "access_permission");
335820
335930
  if (perm === "full_access") return true;
335821
335931
  if (!this.current) return perm !== "no_outside_access";
335822
- const rel = path12.relative(path12.resolve(this.current.path), path12.resolve(target));
335823
- const inside2 = rel === "" || !!rel && !rel.startsWith("..") && !path12.isAbsolute(rel);
335932
+ const rel = path13.relative(path13.resolve(this.current.path), path13.resolve(target));
335933
+ const inside2 = rel === "" || !!rel && !rel.startsWith("..") && !path13.isAbsolute(rel);
335824
335934
  if (inside2) return true;
335825
335935
  return perm !== "no_outside_access";
335826
335936
  }
@@ -335903,6 +336013,7 @@ var PLAN_COMPUTER_USE_ACTIONS = ["observe", "app_list", "app_observe"];
335903
336013
  var PLAN_BROWSER_USE_ACTIONS = ["observe", "navigate", "wait", "extract"];
335904
336014
  var PLAN_COMPUTER_USE_ACTION_SET = new Set(PLAN_COMPUTER_USE_ACTIONS);
335905
336015
  var PLAN_BROWSER_USE_ACTION_SET = new Set(PLAN_BROWSER_USE_ACTIONS);
336016
+ var CHAT_WEB_TOOLS = /* @__PURE__ */ new Set(["web_search", "web_fetch"]);
335906
336017
  var CONCURRENCY_SAFE_TOOLS = /* @__PURE__ */ new Set([
335907
336018
  "pwd",
335908
336019
  "read",
@@ -335936,6 +336047,13 @@ function evaluateToolPolicy(request) {
335936
336047
  const availability = toolAvailability(name50);
335937
336048
  const base2 = { availability, settingsVisible: availability === "configurable" };
335938
336049
  if (!name50) return { ...base2, allowed: false, reason: "[permission] Tool name is required." };
336050
+ if (request.mode === "chat" && !CHAT_WEB_TOOLS.has(name50)) {
336051
+ return {
336052
+ ...base2,
336053
+ allowed: false,
336054
+ 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}`
336055
+ };
336056
+ }
335939
336057
  if (request.mode === "plan") {
335940
336058
  if (name50 === "computer_use") {
335941
336059
  const action = String(request.args?.action || "").trim();
@@ -335977,6 +336095,13 @@ function planModePolicyPrompt() {
335977
336095
  "Runtime policy rejects stale or hidden mutating tool calls even if a prompt asks for them."
335978
336096
  ].join(" ");
335979
336097
  }
336098
+ function chatModePolicyPrompt() {
336099
+ return [
336100
+ "Chat mode is a narrow web-evidence mode.",
336101
+ "Only web_search and web_fetch are available; every workspace, host, application, memory, task, browser-control, and write capability is denied at runtime.",
336102
+ "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."
336103
+ ].join(" ");
336104
+ }
335980
336105
  var DELETE_VERB_SOURCE = "(?:remove-item|rmdir|unlink|erase|del|rm|rd|ri)";
335981
336106
  var DELETE_VERB_BOUNDARY = new RegExp(`(?:^|[\\s;&|()\\n])${DELETE_VERB_SOURCE}(?:\\s|$)`, "i");
335982
336107
  function hasDeletionVerb(text) {
@@ -336254,7 +336379,7 @@ function requestUtilityHostTool(tool, args, context, timeoutMs = 12e4, signal) {
336254
336379
  }
336255
336380
 
336256
336381
  // src/core/nativeBash.ts
336257
- var path13 = __toESM(require("path"));
336382
+ var path14 = __toESM(require("path"));
336258
336383
  var import_module = require("module");
336259
336384
  var MAX_OUTPUT_BYTES = 1024 * 1024;
336260
336385
  var DEFAULT_TIMEOUT_MS = 3e4;
@@ -336342,11 +336467,11 @@ function normalizedTimeout(timeoutMs) {
336342
336467
  }
336343
336468
  function virtualCwd(workspaceRoot, requestedCwd) {
336344
336469
  if (!requestedCwd) return "/";
336345
- const root2 = path13.resolve(workspaceRoot);
336346
- const cwd = path13.resolve(requestedCwd);
336347
- const relative6 = path13.relative(root2, cwd);
336348
- if (relative6.startsWith("..") || path13.isAbsolute(relative6)) return "/";
336349
- return relative6 ? `/${relative6.split(path13.sep).join("/")}` : "/";
336470
+ const root2 = path14.resolve(workspaceRoot);
336471
+ const cwd = path14.resolve(requestedCwd);
336472
+ const relative6 = path14.relative(root2, cwd);
336473
+ if (relative6.startsWith("..") || path14.isAbsolute(relative6)) return "/";
336474
+ return relative6 ? `/${relative6.split(path14.sep).join("/")}` : "/";
336350
336475
  }
336351
336476
  function combineAbortSignals(signal, timeoutMs) {
336352
336477
  const controller = new AbortController();
@@ -336372,7 +336497,7 @@ function createBash(workspaceRoot, timeoutMs) {
336372
336497
  const justBash = loadJustBash();
336373
336498
  if (!justBash) throw new Error("Native Bash runtime unavailable");
336374
336499
  const fs27 = new justBash.ReadWriteFs({
336375
- root: path13.resolve(workspaceRoot),
336500
+ root: path14.resolve(workspaceRoot),
336376
336501
  maxFileReadSize: MAX_OUTPUT_BYTES * 8,
336377
336502
  allowSymlinks: false
336378
336503
  });
@@ -336461,12 +336586,12 @@ async function executeWorkspaceBash(script, workspaceRoot, options = {}) {
336461
336586
  }
336462
336587
 
336463
336588
  // src/core/toolArgumentValidator.ts
336464
- var fs12 = require("fs");
336465
- var path14 = require("path");
336589
+ var fs13 = require("fs");
336590
+ var path15 = require("path");
336466
336591
  var typeBoxCompilerPath = [
336467
- path14.join(__dirname, "..", "typebox-compile.bundle.cjs"),
336468
- path14.join(__dirname, "typebox-compile.bundle.cjs")
336469
- ].find((candidate) => fs12.existsSync(candidate));
336592
+ path15.join(__dirname, "..", "typebox-compile.bundle.cjs"),
336593
+ path15.join(__dirname, "typebox-compile.bundle.cjs")
336594
+ ].find((candidate) => fs13.existsSync(candidate));
336470
336595
  if (!typeBoxCompilerPath) throw new Error("Bundled TypeBox compiler is missing from the Newmark runtime.");
336471
336596
  var { Compile } = require(typeBoxCompilerPath);
336472
336597
  function closeToolArgumentSchema(input2) {
@@ -336556,8 +336681,8 @@ function formatValidationErrors(name50, errors) {
336556
336681
  }
336557
336682
 
336558
336683
  // src/core/localOcr.ts
336559
- var fs13 = __toESM(require("fs"));
336560
- var path15 = __toESM(require("path"));
336684
+ var fs14 = __toESM(require("fs"));
336685
+ var path16 = __toESM(require("path"));
336561
336686
  var AGENT_REPAIR_PROMPT = [
336562
336687
  "The local OCR output is approximate Chinese/English fallback evidence.",
336563
336688
  "Repair likely OCR substitutions, spacing, and line breaks using the visible UI/PDF context and the user task.",
@@ -336590,12 +336715,12 @@ var LocalOcrEngine = class {
336590
336715
  return await this.recognize(dataUrlBuffer(dataUrl), signal, profile);
336591
336716
  }
336592
336717
  async recognizeFile(filePath, signal) {
336593
- const absolute = path15.resolve(filePath);
336594
- const extension = path15.extname(absolute).toLowerCase();
336718
+ const absolute = path16.resolve(filePath);
336719
+ const extension = path16.extname(absolute).toLowerCase();
336595
336720
  if (![".png", ".jpg", ".jpeg", ".bmp"].includes(extension)) {
336596
336721
  throw new Error("Local OCR only accepts PNG, JPEG, or BMP images.");
336597
336722
  }
336598
- const stat = fs13.statSync(absolute);
336723
+ const stat = fs14.statSync(absolute);
336599
336724
  if (!stat.isFile() || stat.size <= 0 || stat.size > 12 * 1024 * 1024) {
336600
336725
  throw new Error("Local OCR image must be a regular file no larger than 12 MB.");
336601
336726
  }
@@ -336649,7 +336774,7 @@ var LocalOcrEngine = class {
336649
336774
  const tesseract = require_src();
336650
336775
  const worker = await tesseract.createWorker("chi_sim+eng", tesseract.OEM.LSTM_ONLY, {
336651
336776
  langPath: tessdataPath,
336652
- cachePath: path15.join(this.rootPath, "cache", "ocr-runtime"),
336777
+ cachePath: path16.join(this.rootPath, "cache", "ocr-runtime"),
336653
336778
  cacheMethod: "none",
336654
336779
  gzip: true,
336655
336780
  logger: () => void 0
@@ -336657,14 +336782,14 @@ var LocalOcrEngine = class {
336657
336782
  return worker;
336658
336783
  }
336659
336784
  prepareLanguageCache() {
336660
- const target = path15.join(this.rootPath, "cache", "ocr-tessdata");
336661
- fs13.mkdirSync(target, { recursive: true });
336785
+ const target = path16.join(this.rootPath, "cache", "ocr-tessdata");
336786
+ fs14.mkdirSync(target, { recursive: true });
336662
336787
  for (const language of ["eng", "chi_sim"]) {
336663
- const destination = path15.join(target, `${language}.traineddata.gz`);
336664
- if (fs13.existsSync(destination) && fs13.statSync(destination).size > 0) continue;
336665
- const packageRoot = path15.dirname(require.resolve(`@tesseract.js-data/${language}/package.json`));
336666
- const source = path15.join(packageRoot, "4.0.0_best_int", `${language}.traineddata.gz`);
336667
- fs13.copyFileSync(source, destination);
336788
+ const destination = path16.join(target, `${language}.traineddata.gz`);
336789
+ if (fs14.existsSync(destination) && fs14.statSync(destination).size > 0) continue;
336790
+ const packageRoot = path16.dirname(require.resolve(`@tesseract.js-data/${language}/package.json`));
336791
+ const source = path16.join(packageRoot, "4.0.0_best_int", `${language}.traineddata.gz`);
336792
+ fs14.copyFileSync(source, destination);
336668
336793
  }
336669
336794
  return target;
336670
336795
  }
@@ -336803,8 +336928,8 @@ function normalizeCrossEnvPath(value, wsPath) {
336803
336928
  const posix3 = windowsDrivePathToPosix(raw);
336804
336929
  if (posix3) return posix3;
336805
336930
  }
336806
- if (path16.isAbsolute(raw)) return raw;
336807
- return path16.join(wsPath, raw);
336931
+ if (path17.isAbsolute(raw)) return raw;
336932
+ return path17.join(wsPath, raw);
336808
336933
  }
336809
336934
  function translateWindowsPathsForWslBash(script) {
336810
336935
  if (!process.env.NEWMARK_WSL_DISTRO) return script;
@@ -336818,7 +336943,7 @@ function translateWindowsPathsForWslBash(script) {
336818
336943
  function computerUseOwner(context, wsPath) {
336819
336944
  const conversationId = String(context.conversationId || "").trim();
336820
336945
  if (conversationId) return `conversation:${conversationId}`;
336821
- const resolved = path16.resolve(context.workspacePath || wsPath || process.cwd());
336946
+ const resolved = path17.resolve(context.workspacePath || wsPath || process.cwd());
336822
336947
  const workspaceHash = crypto8.createHash("sha256").update(resolved).digest("hex").slice(0, 12);
336823
336948
  return `direct:${workspaceHash}`;
336824
336949
  }
@@ -336900,7 +337025,7 @@ function computerUseSessionScope(context, wsPath, owner) {
336900
337025
  return {
336901
337026
  runtimeKey: browserUseScope(context, wsPath).runtimeKey,
336902
337027
  ownerLabel: owner,
336903
- workspacePath: path16.resolve(wsPath || process.cwd())
337028
+ workspacePath: path17.resolve(wsPath || process.cwd())
336904
337029
  };
336905
337030
  }
336906
337031
  function acquireComputerUseLock(action, owner, wsPath, context = {}, dryRun = false) {
@@ -337095,7 +337220,7 @@ var ToolExecutor = class {
337095
337220
  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" } }, []),
337096
337221
  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." } }, []),
337097
337222
  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." } }, []),
337098
- 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"]),
337223
+ 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"]),
337099
337224
  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." } }, []),
337100
337225
  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." } }, []),
337101
337226
  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.", {
@@ -337125,11 +337250,11 @@ var ToolExecutor = class {
337125
337250
  t3("skill_download", "Download a skill", { name: { type: "string" }, source: { type: "string" } }, ["name", "source"]),
337126
337251
  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 } }, []),
337127
337252
  t3("flow_list", "List available Newmark Flow workflows from the Flow folder so the agent can choose one.", {}, []),
337128
- 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"]),
337253
+ 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"]),
337129
337254
  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"]),
337130
337255
  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" } }, []),
337131
337256
  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"]),
337132
- 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"]),
337257
+ 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" } }, []),
337133
337258
  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" } }, []),
337134
337259
  t3("memory_lab_reindex", "Rebuild and organize Memory Lab index links. Routed through Agent runtime when invoked by the model.", {}, []),
337135
337260
  t3("automation_list", "List persisted Newmark automations so the agent can inspect scheduled work.", {}, []),
@@ -337451,13 +337576,13 @@ var ToolExecutor = class {
337451
337576
  }
337452
337577
  case "pdf_read": {
337453
337578
  const pdfPath = resolve16(g2("path"));
337454
- if (path16.extname(pdfPath).toLowerCase() !== ".pdf") return "[pdf_read error] path must end in .pdf.";
337455
- const stat = fs14.statSync(pdfPath);
337579
+ if (path17.extname(pdfPath).toLowerCase() !== ".pdf") return "[pdf_read error] path must end in .pdf.";
337580
+ const stat = fs15.statSync(pdfPath);
337456
337581
  if (!stat.isFile() || stat.size <= 0 || stat.size > 250 * 1024 * 1024) {
337457
337582
  return "[pdf_read error] PDF must be a regular file no larger than 250 MB.";
337458
337583
  }
337459
337584
  const maxChars = Math.max(500, Math.min(1e5, Number(args.max_chars || 5e4)));
337460
- const textLayer = extractPdfTextLayer(fs14.readFileSync(pdfPath)).slice(0, maxChars);
337585
+ const textLayer = extractPdfTextLayer(fs15.readFileSync(pdfPath)).slice(0, maxChars);
337461
337586
  const readableCount = (textLayer.match(/[A-Za-z0-9\u3400-\u9fff]/g) || []).length;
337462
337587
  if (readableCount >= 20) {
337463
337588
  return JSON.stringify({
@@ -337676,7 +337801,7 @@ var ToolExecutor = class {
337676
337801
  case "flow_list":
337677
337802
  return this.flowList();
337678
337803
  case "flow_save":
337679
- return this.flowSave(g2("name"), args.components);
337804
+ return this.flowSave(g2("name"), args);
337680
337805
  case "flow_run":
337681
337806
  return `[flow_run] Routed to Agent runtime: ${g2("name")}`;
337682
337807
  case "memory_lab_read":
@@ -337733,8 +337858,8 @@ var ToolExecutor = class {
337733
337858
  }
337734
337859
  }
337735
337860
  isInside(parent, child) {
337736
- const rel = path16.relative(path16.resolve(parent), path16.resolve(child));
337737
- return rel === "" || !!rel && !rel.startsWith("..") && !path16.isAbsolute(rel);
337861
+ const rel = path17.relative(path17.resolve(parent), path17.resolve(child));
337862
+ return rel === "" || !!rel && !rel.startsWith("..") && !path17.isAbsolute(rel);
337738
337863
  }
337739
337864
  hostSupportsTool(name50) {
337740
337865
  if (name50.startsWith("browser_") && !this.hostProfile.electronBrowser) return false;
@@ -337846,7 +337971,7 @@ var ToolExecutor = class {
337846
337971
  if (!token || /^https?:\/\//i.test(token) || token.startsWith("-")) continue;
337847
337972
  if (!this.looksLikePath(token)) continue;
337848
337973
  const withoutWildcard = token.replace(/[\\/][*?][^\\/]*$/g, "");
337849
- refs.push(path16.resolve(normalizeCrossEnvPath(withoutWildcard, wsPath)));
337974
+ refs.push(path17.resolve(normalizeCrossEnvPath(withoutWildcard, wsPath)));
337850
337975
  }
337851
337976
  return Array.from(new Set(refs));
337852
337977
  }
@@ -337897,7 +338022,7 @@ var ToolExecutor = class {
337897
338022
  }
337898
338023
  fread(p) {
337899
338024
  try {
337900
- const c3 = fs14.readFileSync(p, "utf-8");
338025
+ const c3 = fs15.readFileSync(p, "utf-8");
337901
338026
  return c3.length > 3e4 ? c3.slice(0, 3e4) + "...\n[truncated]" : c3;
337902
338027
  } catch (e3) {
337903
338028
  return `[read] ${e3}`;
@@ -337905,8 +338030,8 @@ var ToolExecutor = class {
337905
338030
  }
337906
338031
  fwrite(p, content) {
337907
338032
  try {
337908
- fs14.mkdirSync(path16.dirname(p), { recursive: true });
337909
- fs14.writeFileSync(p, content, "utf-8");
338033
+ fs15.mkdirSync(path17.dirname(p), { recursive: true });
338034
+ fs15.writeFileSync(p, content, "utf-8");
337910
338035
  return `[write] OK: ${p}`;
337911
338036
  } catch (e3) {
337912
338037
  return `[write] ${e3}`;
@@ -337914,10 +338039,10 @@ var ToolExecutor = class {
337914
338039
  }
337915
338040
  fedit(p, oldStr, newStr) {
337916
338041
  try {
337917
- const c3 = fs14.readFileSync(p, "utf-8");
338042
+ const c3 = fs15.readFileSync(p, "utf-8");
337918
338043
  if (!c3.includes(oldStr)) return `[edit] String not found in ${p}.`;
337919
338044
  const updated = c3.replace(oldStr, newStr);
337920
- fs14.writeFileSync(p, updated, "utf-8");
338045
+ fs15.writeFileSync(p, updated, "utf-8");
337921
338046
  return `[edit] OK: ${p}`;
337922
338047
  } catch (e3) {
337923
338048
  return `[edit] ${e3}`;
@@ -337926,12 +338051,12 @@ var ToolExecutor = class {
337926
338051
  fdelete(p) {
337927
338052
  try {
337928
338053
  if (/[*?]/.test(p)) return "[delete_file] Refused: wildcard paths are not allowed. Delete one file per call.";
337929
- const resolved = path16.resolve(p);
337930
- const stat = fs14.lstatSync(resolved);
338054
+ const resolved = path17.resolve(p);
338055
+ const stat = fs15.lstatSync(resolved);
337931
338056
  if (stat.isDirectory()) {
337932
338057
  return "[delete_file] Refused: deleting a directory is not allowed. Delete files one by one under Agent supervision.";
337933
338058
  }
337934
- fs14.unlinkSync(resolved);
338059
+ fs15.unlinkSync(resolved);
337935
338060
  return `[delete_file] OK: ${resolved}`;
337936
338061
  } catch (e3) {
337937
338062
  return `[delete_file] ${e3 instanceof Error ? e3.message : String(e3)}`;
@@ -337955,11 +338080,11 @@ var ToolExecutor = class {
337955
338080
  const results = [];
337956
338081
  const walk4 = (d3, depth) => {
337957
338082
  if (depth > 5 || results.length >= 80) return;
337958
- for (const entry of fs14.readdirSync(d3, { withFileTypes: true })) {
337959
- const full = path16.join(d3, entry.name);
338083
+ for (const entry of fs15.readdirSync(d3, { withFileTypes: true })) {
338084
+ const full = path17.join(d3, entry.name);
337960
338085
  if (entry.isFile()) {
337961
338086
  try {
337962
- const content = fs14.readFileSync(full, "utf-8");
338087
+ const content = fs15.readFileSync(full, "utf-8");
337963
338088
  for (const [i4, line] of content.split("\n").entries()) {
337964
338089
  if (re.test(line)) {
337965
338090
  results.push(`${entry.name}:${i4 + 1}:${line.trim()}`);
@@ -338175,29 +338300,53 @@ ${String(result.data)}`);
338175
338300
  try {
338176
338301
  const resp = await this.proxyFetch(src, { signal });
338177
338302
  const content = await resp.text();
338178
- const dir = path16.join(this.root, "skills", name50);
338179
- fs14.mkdirSync(dir, { recursive: true });
338180
- fs14.writeFileSync(path16.join(dir, "SKILL.md"), content, "utf-8");
338303
+ const dir = path17.join(this.root, "skills", name50);
338304
+ fs15.mkdirSync(dir, { recursive: true });
338305
+ fs15.writeFileSync(path17.join(dir, "SKILL.md"), content, "utf-8");
338181
338306
  return `[skill] Downloaded '${name50}'`;
338182
338307
  } catch (e3) {
338183
338308
  return `[skill] ${e3}`;
338184
338309
  }
338185
338310
  }
338186
338311
  flowList() {
338187
- const dir = path16.join(this.root, "Flow");
338312
+ const dir = path17.join(this.root, "Flow");
338188
338313
  try {
338189
- const files = fs14.readdirSync(dir).filter((f3) => f3.endsWith(".Flow.json")).sort();
338314
+ const files = fs15.readdirSync(dir).filter((f3) => f3.endsWith(".Flow.json")).sort();
338190
338315
  if (!files.length) return "[flow_list] No workflows found.";
338191
338316
  return files.map((f3) => f3.replace(/\.Flow\.json$/, "")).join("\n");
338192
338317
  } catch (e3) {
338193
338318
  return `[flow_list] ${e3}`;
338194
338319
  }
338195
338320
  }
338196
- flowSave(name50, componentsRaw) {
338321
+ flowSave(name50, input2) {
338197
338322
  const cleanName = (name50 || "").replace(/[<>:"/\\|?*]/g, "-").trim();
338198
338323
  if (!cleanName) return "[flow_save] Workflow name is required.";
338199
- if (!Array.isArray(componentsRaw)) return "[flow_save] components must be an array.";
338200
- const components = componentsRaw.map((raw, idx) => {
338324
+ const dir = path17.join(this.root, "Flow");
338325
+ const target = path17.join(dir, `${cleanName}.Flow.json`);
338326
+ const action = String(input2.action || (Array.isArray(input2.components) ? "replace" : "upsert")).toLowerCase();
338327
+ let componentsRaw = input2.components;
338328
+ if (action === "upsert") {
338329
+ if (!input2.component || typeof input2.component !== "object") return "[flow_save] component is required for action=upsert.";
338330
+ const existing = FlowEngine.load(dir, cleanName)?.components || [];
338331
+ const component = input2.component;
338332
+ const requestedId = Number(component.id);
338333
+ if (!Number.isFinite(requestedId)) return "[flow_save] component.id is required for action=upsert.";
338334
+ componentsRaw = [...existing.filter((item) => item.id !== requestedId), component].sort((a3, b2) => Number(a3.id) - Number(b2.id));
338335
+ } else if (action === "delete") {
338336
+ if (input2.confirm !== true) return "[flow_save] action=delete requires confirm=true.";
338337
+ const componentId = Number(input2.component_id);
338338
+ if (!Number.isFinite(componentId)) return "[flow_save] component_id is required for action=delete.";
338339
+ const existing = FlowEngine.load(dir, cleanName);
338340
+ if (!existing) return `[flow_save] Workflow not found: ${cleanName}`;
338341
+ const remaining = existing.components.filter((item) => item.id !== componentId);
338342
+ if (remaining.length === existing.components.length) return `[flow_save] Component not found: ${componentId}`;
338343
+ componentsRaw = remaining;
338344
+ } else if (action !== "replace") {
338345
+ return `[flow_save] Unknown action: ${action}`;
338346
+ }
338347
+ if (!Array.isArray(componentsRaw)) return "[flow_save] components must be an array for action=replace.";
338348
+ const componentInputs = componentsRaw;
338349
+ const components = componentInputs.map((raw, idx) => {
338201
338350
  const c3 = raw;
338202
338351
  const type = c3.type === "logic" ? "logic" : "dialog";
338203
338352
  if (type === "logic") {
@@ -338218,10 +338367,9 @@ ${String(result.data)}`);
338218
338367
  };
338219
338368
  });
338220
338369
  const workflow = { name: cleanName, components };
338221
- const dir = path16.join(this.root, "Flow");
338222
- fs14.mkdirSync(dir, { recursive: true });
338223
- fs14.writeFileSync(path16.join(dir, `${cleanName}.Flow.json`), JSON.stringify(workflow, null, 2), "utf-8");
338224
- return `[flow_save] OK: ${cleanName}.Flow.json`;
338370
+ fs15.mkdirSync(dir, { recursive: true });
338371
+ fs15.writeFileSync(target, JSON.stringify(workflow, null, 2), "utf-8");
338372
+ return `[flow_save] OK (${action}): ${cleanName}.Flow.json`;
338225
338373
  }
338226
338374
  memoryLabRead(selector2) {
338227
338375
  const lab2 = new MemoryLabManager(this.root);
@@ -338281,10 +338429,10 @@ ${String(result.data)}`);
338281
338429
  return this.gh(args, ws, signal);
338282
338430
  }
338283
338431
  async fileAudit(target, ws, includeRemote, baseRef, signal) {
338284
- const resolvedTarget = path16.resolve(target || ws);
338285
- const exists = fs14.existsSync(resolvedTarget);
338286
- const stat = exists ? fs14.statSync(resolvedTarget) : null;
338287
- const repoRoot = await this.findGitRoot(exists && stat?.isDirectory() ? resolvedTarget : path16.dirname(resolvedTarget), ws, signal);
338432
+ const resolvedTarget = path17.resolve(target || ws);
338433
+ const exists = fs15.existsSync(resolvedTarget);
338434
+ const stat = exists ? fs15.statSync(resolvedTarget) : null;
338435
+ const repoRoot = await this.findGitRoot(exists && stat?.isDirectory() ? resolvedTarget : path17.dirname(resolvedTarget), ws, signal);
338288
338436
  const audit = {
338289
338437
  ok: true,
338290
338438
  target: resolvedTarget,
@@ -338311,11 +338459,11 @@ ${String(result.data)}`);
338311
338459
  };
338312
338460
  if (stat.isFile()) {
338313
338461
  const hash = crypto8.createHash("sha256");
338314
- hash.update(fs14.readFileSync(target));
338462
+ hash.update(fs15.readFileSync(target));
338315
338463
  base2.sha256 = hash.digest("hex").toUpperCase();
338316
338464
  }
338317
338465
  if (stat.isDirectory()) {
338318
- base2.entries = fs14.readdirSync(target).slice(0, 200).sort();
338466
+ base2.entries = fs15.readdirSync(target).slice(0, 200).sort();
338319
338467
  }
338320
338468
  return base2;
338321
338469
  }
@@ -338324,14 +338472,14 @@ ${String(result.data)}`);
338324
338472
  const out = await this.gitExecAt(candidate, ["rev-parse", "--show-toplevel"], signal);
338325
338473
  if (!out.startsWith("[git]") && !out.includes("not a git repository")) {
338326
338474
  const root2 = out.split(/\r?\n/)[0].trim();
338327
- if (root2 && fs14.existsSync(root2)) return path16.resolve(root2);
338475
+ if (root2 && fs15.existsSync(root2)) return path17.resolve(root2);
338328
338476
  }
338329
338477
  }
338330
338478
  return null;
338331
338479
  }
338332
338480
  async gitFileAudit(repoRoot, target, baseRef, signal) {
338333
- const rel = path16.relative(repoRoot, target).replace(/\\/g, "/");
338334
- const inside2 = rel === "" || !!rel && !rel.startsWith("..") && !path16.isAbsolute(rel);
338481
+ const rel = path17.relative(repoRoot, target).replace(/\\/g, "/");
338482
+ const inside2 = rel === "" || !!rel && !rel.startsWith("..") && !path17.isAbsolute(rel);
338335
338483
  if (!inside2) return { repository: repoRoot, tracked: false, note: "Path is outside the detected repository." };
338336
338484
  const branch = await this.gitExecAt(repoRoot, ["branch", "--show-current"], signal);
338337
338485
  const status = rel === "" ? await this.gitExecAt(repoRoot, ["status", "--short"], signal) : await this.gitExecAt(repoRoot, ["status", "--short", "--", rel], signal);
@@ -338389,7 +338537,7 @@ ${String(result.data)}`);
338389
338537
  }
338390
338538
  async githubFileAudit(repoRoot, target, remote, signal) {
338391
338539
  const repo = `${remote.owner}/${remote.name}`;
338392
- const rel = path16.relative(repoRoot, target).replace(/\\/g, "/");
338540
+ const rel = path17.relative(repoRoot, target).replace(/\\/g, "/");
338393
338541
  const branch = (await this.gitExecAt(repoRoot, ["branch", "--show-current"], signal)).trim();
338394
338542
  const encodedPath = rel && rel !== "." ? rel.split("/").map((part) => encodeURIComponent(part)).join("/") : "";
338395
338543
  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);
@@ -338413,8 +338561,8 @@ ${String(result.data)}`);
338413
338561
  };
338414
338562
  }
338415
338563
  async repoSecurityAudit(target, ws, baseRef, signal) {
338416
- const resolvedTarget = path16.resolve(target || ws);
338417
- const repoRoot = await this.findGitRoot(fs14.existsSync(resolvedTarget) && fs14.statSync(resolvedTarget).isDirectory() ? resolvedTarget : path16.dirname(resolvedTarget), ws, signal);
338564
+ const resolvedTarget = path17.resolve(target || ws);
338565
+ const repoRoot = await this.findGitRoot(fs15.existsSync(resolvedTarget) && fs15.statSync(resolvedTarget).isDirectory() ? resolvedTarget : path17.dirname(resolvedTarget), ws, signal);
338418
338566
  if (!repoRoot) {
338419
338567
  return JSON.stringify({
338420
338568
  ok: true,
@@ -338512,12 +338660,12 @@ ${String(result.data)}`);
338512
338660
  const findings = [];
338513
338661
  for (const rel of Array.from(files).sort()) {
338514
338662
  if (findings.length >= 40) break;
338515
- const full = path16.join(repoRoot, rel);
338516
- if (!fs14.existsSync(full) || !fs14.statSync(full).isFile()) continue;
338517
- if (fs14.statSync(full).size > 512 * 1024) continue;
338663
+ const full = path17.join(repoRoot, rel);
338664
+ if (!fs15.existsSync(full) || !fs15.statSync(full).isFile()) continue;
338665
+ if (fs15.statSync(full).size > 512 * 1024) continue;
338518
338666
  let text = "";
338519
338667
  try {
338520
- text = fs14.readFileSync(full, "utf-8");
338668
+ text = fs15.readFileSync(full, "utf-8");
338521
338669
  } catch {
338522
338670
  continue;
338523
338671
  }
@@ -338547,12 +338695,12 @@ ${String(result.data)}`);
338547
338695
  const findings = [];
338548
338696
  for (const rel of Array.from(files).sort()) {
338549
338697
  if (findings.length >= 40) break;
338550
- const full = path16.join(repoRoot, rel);
338551
- if (!fs14.existsSync(full) || !fs14.statSync(full).isFile()) continue;
338552
- if (fs14.statSync(full).size > 512 * 1024) continue;
338698
+ const full = path17.join(repoRoot, rel);
338699
+ if (!fs15.existsSync(full) || !fs15.statSync(full).isFile()) continue;
338700
+ if (fs15.statSync(full).size > 512 * 1024) continue;
338553
338701
  let text = "";
338554
338702
  try {
338555
- text = fs14.readFileSync(full, "utf-8");
338703
+ text = fs15.readFileSync(full, "utf-8");
338556
338704
  } catch {
338557
338705
  continue;
338558
338706
  }
@@ -338569,7 +338717,7 @@ ${String(result.data)}`);
338569
338717
  releaseExcludedPathFindings(repoRoot, ignoredFilesRaw) {
338570
338718
  const sensitive = /^(config\.json|agent\.md|PC_Hash\.config|Work\/|archive\/|skills\/|Memory Lab\/|Design\.md|release\/|_local\/|_ref\/|vendor\/)/i;
338571
338719
  const fromIgnored = String(ignoredFilesRaw || "").split(/\r?\n/).map((line) => line.trim().replace(/\\/g, "/")).filter((line) => line && sensitive.test(line));
338572
- 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, "/"));
338720
+ 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, "/"));
338573
338721
  return Array.from(/* @__PURE__ */ new Set([...fromIgnored, ...direct])).slice(0, 80);
338574
338722
  }
338575
338723
  async ghJson(args, ws, signal) {
@@ -339373,8 +339521,8 @@ function sharedSubagentManager(key3, options) {
339373
339521
  }
339374
339522
 
339375
339523
  // src/core/skills.ts
339376
- var fs15 = __toESM(require("fs"));
339377
- var path17 = __toESM(require("path"));
339524
+ var fs16 = __toESM(require("fs"));
339525
+ var path18 = __toESM(require("path"));
339378
339526
  var os4 = __toESM(require("os"));
339379
339527
  var import_crypto9 = require("crypto");
339380
339528
  var SkillsManager = class {
@@ -339383,13 +339531,13 @@ var SkillsManager = class {
339383
339531
  marketSourcesPath;
339384
339532
  metadataCache = /* @__PURE__ */ new Map();
339385
339533
  constructor(root2) {
339386
- this.skillsDir = path17.join(root2, "skills");
339387
- this.metaPath = path17.join(this.skillsDir, ".skills.json");
339388
- this.marketSourcesPath = path17.join(this.skillsDir, ".market-sources.json");
339389
- fs15.mkdirSync(this.skillsDir, { recursive: true });
339534
+ this.skillsDir = path18.join(root2, "skills");
339535
+ this.metaPath = path18.join(this.skillsDir, ".skills.json");
339536
+ this.marketSourcesPath = path18.join(this.skillsDir, ".market-sources.json");
339537
+ fs16.mkdirSync(this.skillsDir, { recursive: true });
339390
339538
  }
339391
339539
  list() {
339392
- return fs15.readdirSync(this.skillsDir, { withFileTypes: true }).filter((e3) => e3.isDirectory() && !e3.name.startsWith(".")).map((e3) => e3.name);
339540
+ return fs16.readdirSync(this.skillsDir, { withFileTypes: true }).filter((e3) => e3.isDirectory() && !e3.name.startsWith(".")).map((e3) => e3.name);
339393
339541
  }
339394
339542
  listDetailed() {
339395
339543
  return this.list().map((name50) => this.infoFor(name50, this.getPath(name50), "project", true));
@@ -339410,48 +339558,48 @@ var SkillsManager = class {
339410
339558
  }
339411
339559
  load(name50) {
339412
339560
  const reference = String(name50 || "").trim().toLowerCase();
339413
- const skill = this.active().find((item) => item.name.toLowerCase() === reference || path17.basename(item.path).toLowerCase() === reference);
339561
+ const skill = this.active().find((item) => item.name.toLowerCase() === reference || path18.basename(item.path).toLowerCase() === reference);
339414
339562
  if (!skill) return null;
339415
- const skillPath = path17.join(skill.path, "SKILL.md");
339416
- const content = fs15.readFileSync(skillPath, "utf-8");
339563
+ const skillPath = path18.join(skill.path, "SKILL.md");
339564
+ const content = fs16.readFileSync(skillPath, "utf-8");
339417
339565
  const files = this.sampleSkillFiles(skill.path, 10);
339418
339566
  return { skill, content, files };
339419
339567
  }
339420
339568
  has(name50) {
339421
- return fs15.existsSync(path17.join(this.skillsDir, name50, "SKILL.md"));
339569
+ return fs16.existsSync(path18.join(this.skillsDir, name50, "SKILL.md"));
339422
339570
  }
339423
339571
  getPath(name50) {
339424
- return path17.join(this.skillsDir, name50);
339572
+ return path18.join(this.skillsDir, name50);
339425
339573
  }
339426
339574
  async download(name50, url) {
339427
- const dir = path17.join(this.skillsDir, name50);
339428
- fs15.mkdirSync(dir, { recursive: true });
339575
+ const dir = path18.join(this.skillsDir, name50);
339576
+ fs16.mkdirSync(dir, { recursive: true });
339429
339577
  if (!url.startsWith("http")) return `[skill] Not a URL: ${url}`;
339430
339578
  try {
339431
339579
  const resp = await fetch(url);
339432
339580
  const content = await resp.text();
339433
- fs15.writeFileSync(path17.join(dir, "SKILL.md"), content, "utf-8");
339581
+ fs16.writeFileSync(path18.join(dir, "SKILL.md"), content, "utf-8");
339434
339582
  return `[skill] Downloaded '${name50}'`;
339435
339583
  } catch (e3) {
339436
339584
  return `[skill] ${e3}`;
339437
339585
  }
339438
339586
  }
339439
339587
  installFromLocal(sourceDir, targetName) {
339440
- const skillPath = path17.join(sourceDir, "SKILL.md");
339441
- if (!fs15.existsSync(skillPath)) return false;
339588
+ const skillPath = path18.join(sourceDir, "SKILL.md");
339589
+ if (!fs16.existsSync(skillPath)) return false;
339442
339590
  const info = this.parseSkillInfo(sourceDir);
339443
- const cleanName = this.cleanName(targetName || info.name || path17.basename(sourceDir));
339591
+ const cleanName = this.cleanName(targetName || info.name || path18.basename(sourceDir));
339444
339592
  if (!cleanName) return false;
339445
- const dest = path17.join(this.skillsDir, cleanName);
339446
- fs15.rmSync(dest, { recursive: true, force: true });
339447
- fs15.cpSync(sourceDir, dest, { recursive: true });
339593
+ const dest = path18.join(this.skillsDir, cleanName);
339594
+ fs16.rmSync(dest, { recursive: true, force: true });
339595
+ fs16.cpSync(sourceDir, dest, { recursive: true });
339448
339596
  this.setEnabled(cleanName, true);
339449
339597
  return true;
339450
339598
  }
339451
339599
  remove(name50) {
339452
- const dir = path17.join(this.skillsDir, name50);
339453
- if (fs15.existsSync(dir)) {
339454
- fs15.rmSync(dir, { recursive: true, force: true });
339600
+ const dir = path18.join(this.skillsDir, name50);
339601
+ if (fs16.existsSync(dir)) {
339602
+ fs16.rmSync(dir, { recursive: true, force: true });
339455
339603
  const meta = this.loadMeta();
339456
339604
  meta.disabled = meta.disabled.filter((n3) => n3 !== name50);
339457
339605
  this.saveMeta(meta);
@@ -339500,7 +339648,7 @@ var SkillsManager = class {
339500
339648
  type,
339501
339649
  enabled: input2.enabled !== false,
339502
339650
  url: url || void 0,
339503
- path: sourcePath ? path17.resolve(sourcePath) : void 0,
339651
+ path: sourcePath ? path18.resolve(sourcePath) : void 0,
339504
339652
  addedAt: existing?.addedAt || now2,
339505
339653
  updatedAt: now2
339506
339654
  };
@@ -339538,17 +339686,17 @@ var SkillsManager = class {
339538
339686
  const items = [];
339539
339687
  for (const info of this.listDetailed()) items.push(info);
339540
339688
  const roots = [
339541
- { root: path17.join(this.skillsDir, "..", ".agents", "skills"), source: "codex" },
339542
- { root: path17.join(this.skillsDir, "..", ".claude", "skills"), source: "claude" },
339543
- { root: path17.join(os4.homedir(), ".agents", "skills"), source: "user" },
339544
- { root: path17.join(os4.homedir(), ".codex", "skills"), source: "codex" },
339545
- { root: path17.join(os4.homedir(), ".claude", "skills"), source: "claude" },
339546
- { root: path17.join(os4.homedir(), ".config", "opencode", "skills"), source: "opencode" }
339689
+ { root: path18.join(this.skillsDir, "..", ".agents", "skills"), source: "codex" },
339690
+ { root: path18.join(this.skillsDir, "..", ".claude", "skills"), source: "claude" },
339691
+ { root: path18.join(os4.homedir(), ".agents", "skills"), source: "user" },
339692
+ { root: path18.join(os4.homedir(), ".codex", "skills"), source: "codex" },
339693
+ { root: path18.join(os4.homedir(), ".claude", "skills"), source: "claude" },
339694
+ { root: path18.join(os4.homedir(), ".config", "opencode", "skills"), source: "opencode" }
339547
339695
  ];
339548
339696
  for (const entry of roots) {
339549
339697
  for (const dir of this.findSkillDirs(entry.root, 4, 240)) {
339550
339698
  const parsed = this.parseSkillInfo(dir);
339551
- const name50 = this.cleanName(parsed.name || path17.basename(dir));
339699
+ const name50 = this.cleanName(parsed.name || path18.basename(dir));
339552
339700
  if (!name50 || items.some((i4) => i4.name === name50 && i4.source !== "remote")) continue;
339553
339701
  items.push({
339554
339702
  name: name50,
@@ -339565,9 +339713,9 @@ var SkillsManager = class {
339565
339713
  });
339566
339714
  }
339567
339715
  }
339568
- for (const dir of this.findPluginSkillDirs(path17.join(this.skillsDir, ".."), 5, 240)) {
339716
+ for (const dir of this.findPluginSkillDirs(path18.join(this.skillsDir, ".."), 5, 240)) {
339569
339717
  const parsed = this.parseSkillInfo(dir);
339570
- const name50 = this.cleanName(parsed.name || path17.basename(dir));
339718
+ const name50 = this.cleanName(parsed.name || path18.basename(dir));
339571
339719
  if (!name50 || items.some((i4) => i4.name === name50 && i4.source !== "remote")) continue;
339572
339720
  items.push({
339573
339721
  name: name50,
@@ -339624,8 +339772,8 @@ var SkillsManager = class {
339624
339772
  }
339625
339773
  loadMeta() {
339626
339774
  try {
339627
- if (fs15.existsSync(this.metaPath)) {
339628
- const raw = JSON.parse(fs15.readFileSync(this.metaPath, "utf-8"));
339775
+ if (fs16.existsSync(this.metaPath)) {
339776
+ const raw = JSON.parse(fs16.readFileSync(this.metaPath, "utf-8"));
339629
339777
  return { disabled: Array.isArray(raw.disabled) ? raw.disabled.map(String) : [] };
339630
339778
  }
339631
339779
  } catch {
@@ -339633,7 +339781,7 @@ var SkillsManager = class {
339633
339781
  return { disabled: [] };
339634
339782
  }
339635
339783
  saveMeta(meta) {
339636
- fs15.writeFileSync(this.metaPath, JSON.stringify({ disabled: meta.disabled }, null, 2), "utf-8");
339784
+ fs16.writeFileSync(this.metaPath, JSON.stringify({ disabled: meta.disabled }, null, 2), "utf-8");
339637
339785
  }
339638
339786
  builtinMarketSources() {
339639
339787
  return [{
@@ -339647,8 +339795,8 @@ var SkillsManager = class {
339647
339795
  }
339648
339796
  loadMarketSources() {
339649
339797
  try {
339650
- if (!fs15.existsSync(this.marketSourcesPath)) return [];
339651
- const raw = JSON.parse(fs15.readFileSync(this.marketSourcesPath, "utf-8"));
339798
+ if (!fs16.existsSync(this.marketSourcesPath)) return [];
339799
+ const raw = JSON.parse(fs16.readFileSync(this.marketSourcesPath, "utf-8"));
339652
339800
  if (!Array.isArray(raw.sources)) return [];
339653
339801
  return raw.sources.map((source) => this.normalizeMarketSource(source)).filter((source) => !!source);
339654
339802
  } catch {
@@ -339657,7 +339805,7 @@ var SkillsManager = class {
339657
339805
  }
339658
339806
  saveMarketSources(sources) {
339659
339807
  const normalized = sources.filter((s3) => !s3.builtin).map((s3) => this.normalizeMarketSource(s3)).filter((source) => !!source);
339660
- fs15.writeFileSync(this.marketSourcesPath, JSON.stringify({ sources: normalized }, null, 2), "utf-8");
339808
+ fs16.writeFileSync(this.marketSourcesPath, JSON.stringify({ sources: normalized }, null, 2), "utf-8");
339661
339809
  }
339662
339810
  normalizeMarketSource(raw) {
339663
339811
  if (!raw || typeof raw !== "object") return null;
@@ -339677,7 +339825,7 @@ var SkillsManager = class {
339677
339825
  type,
339678
339826
  enabled: source.enabled !== false,
339679
339827
  url: url || void 0,
339680
- path: sourcePath ? path17.resolve(sourcePath) : void 0,
339828
+ path: sourcePath ? path18.resolve(sourcePath) : void 0,
339681
339829
  builtin: source.builtin === true,
339682
339830
  addedAt: source.addedAt ? String(source.addedAt) : void 0,
339683
339831
  updatedAt: source.updatedAt ? String(source.updatedAt) : void 0
@@ -339728,11 +339876,11 @@ var SkillsManager = class {
339728
339876
  return rawItems.slice(0, 1e3).map((entry) => this.marketInfoFromCatalogEntry(entry, source, installed)).filter((item) => !!item);
339729
339877
  }
339730
339878
  readCatalogText(source) {
339731
- const catalogPath = source.path ? path17.resolve(source.path) : "";
339732
- if (catalogPath && fs15.existsSync(catalogPath)) return fs15.readFileSync(catalogPath, "utf-8");
339879
+ const catalogPath = source.path ? path18.resolve(source.path) : "";
339880
+ if (catalogPath && fs16.existsSync(catalogPath)) return fs16.readFileSync(catalogPath, "utf-8");
339733
339881
  const url = source.url || "";
339734
- if (url.startsWith("file://")) return fs15.readFileSync(new URL(url), "utf-8");
339735
- if (url && !url.startsWith("http")) return fs15.readFileSync(path17.resolve(url), "utf-8");
339882
+ if (url.startsWith("file://")) return fs16.readFileSync(new URL(url), "utf-8");
339883
+ if (url && !url.startsWith("http")) return fs16.readFileSync(path18.resolve(url), "utf-8");
339736
339884
  return "";
339737
339885
  }
339738
339886
  async discoverJsonMarketSourceAsync(source, installed) {
@@ -339792,7 +339940,7 @@ var SkillsManager = class {
339792
339940
  }
339793
339941
  marketInfoFromLocalDir(dir, source, installed) {
339794
339942
  const parsed = this.parseSkillInfo(dir);
339795
- const name50 = this.cleanName(parsed.name || path17.basename(dir));
339943
+ const name50 = this.cleanName(parsed.name || path18.basename(dir));
339796
339944
  if (!name50) return null;
339797
339945
  return {
339798
339946
  name: name50,
@@ -339817,9 +339965,9 @@ var SkillsManager = class {
339817
339965
  }
339818
339966
  parseSkillInfo(dir) {
339819
339967
  try {
339820
- const skillPath = path17.join(dir, "SKILL.md");
339821
- const stat = fs15.statSync(skillPath);
339822
- const content = fs15.readFileSync(skillPath, "utf-8");
339968
+ const skillPath = path18.join(dir, "SKILL.md");
339969
+ const stat = fs16.statSync(skillPath);
339970
+ const content = fs16.readFileSync(skillPath, "utf-8");
339823
339971
  const digest = (0, import_crypto9.createHash)("sha256").update(content).digest("hex");
339824
339972
  const fingerprint2 = `${stat.mtimeMs}:${stat.size}:${digest}`;
339825
339973
  const cached = this.metadataCache.get(skillPath);
@@ -339852,7 +340000,7 @@ var SkillsManager = class {
339852
340000
  };
339853
340001
  this.metadataCache.set(skillPath, {
339854
340002
  fingerprint: fingerprint2,
339855
- info: { ...parsed, path: dir, enabled: this.isEnabled(path17.basename(dir)), installed: true, source: "project" }
340003
+ info: { ...parsed, path: dir, enabled: this.isEnabled(path18.basename(dir)), installed: true, source: "project" }
339856
340004
  });
339857
340005
  return parsed;
339858
340006
  } catch {
@@ -339869,13 +340017,13 @@ var SkillsManager = class {
339869
340017
  if (files.length >= limit || depth > 2) return;
339870
340018
  let entries = [];
339871
340019
  try {
339872
- entries = fs15.readdirSync(dir, { withFileTypes: true });
340020
+ entries = fs16.readdirSync(dir, { withFileTypes: true });
339873
340021
  } catch {
339874
340022
  return;
339875
340023
  }
339876
340024
  for (const entry of entries) {
339877
340025
  if (files.length >= limit || entry.name === "SKILL.md" || entry.name.startsWith(".")) continue;
339878
- const target = path17.join(dir, entry.name);
340026
+ const target = path18.join(dir, entry.name);
339879
340027
  if (entry.isDirectory()) walk4(target, depth + 1);
339880
340028
  else if (entry.isFile()) files.push(target);
339881
340029
  }
@@ -339889,7 +340037,7 @@ var SkillsManager = class {
339889
340037
  if (results.length >= maxItems || depth > maxDepth) return;
339890
340038
  let entries;
339891
340039
  try {
339892
- entries = fs15.readdirSync(dir, { withFileTypes: true });
340040
+ entries = fs16.readdirSync(dir, { withFileTypes: true });
339893
340041
  } catch {
339894
340042
  return;
339895
340043
  }
@@ -339899,7 +340047,7 @@ var SkillsManager = class {
339899
340047
  }
339900
340048
  for (const e3 of entries) {
339901
340049
  if (!e3.isDirectory() || e3.name.startsWith(".git") || e3.name === "node_modules") continue;
339902
- walk4(path17.join(dir, e3.name), depth + 1);
340050
+ walk4(path18.join(dir, e3.name), depth + 1);
339903
340051
  }
339904
340052
  };
339905
340053
  walk4(root2, 0);
@@ -339911,19 +340059,19 @@ var SkillsManager = class {
339911
340059
  if (results.length >= maxItems || depth > maxDepth) return;
339912
340060
  let entries;
339913
340061
  try {
339914
- entries = fs15.readdirSync(dir, { withFileTypes: true });
340062
+ entries = fs16.readdirSync(dir, { withFileTypes: true });
339915
340063
  } catch {
339916
340064
  return;
339917
340065
  }
339918
340066
  const hasPluginManifest = entries.some((e3) => e3.isDirectory() && (e3.name === ".codex-plugin" || e3.name === ".claude-plugin"));
339919
340067
  if (hasPluginManifest) {
339920
340068
  for (const skillsDir of ["skills", "Skills"]) {
339921
- results.push(...this.findSkillDirs(path17.join(dir, skillsDir), 3, maxItems - results.length));
340069
+ results.push(...this.findSkillDirs(path18.join(dir, skillsDir), 3, maxItems - results.length));
339922
340070
  }
339923
340071
  }
339924
340072
  for (const e3 of entries) {
339925
340073
  if (!e3.isDirectory() || e3.name.startsWith(".git") || e3.name === "node_modules" || e3.name === "release" || e3.name.startsWith("release.locked-")) continue;
339926
- walk4(path17.join(dir, e3.name), depth + 1);
340074
+ walk4(path18.join(dir, e3.name), depth + 1);
339927
340075
  }
339928
340076
  };
339929
340077
  walk4(root2, 0);
@@ -339951,25 +340099,25 @@ var SkillsManager = class {
339951
340099
  if (!description) warnings.push("Missing required frontmatter field: description.");
339952
340100
  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.");
339953
340101
  if (description && description.length > 1e3) warnings.push("Description is longer than recommended for skill discovery.");
339954
- const folderName = path17.basename(dir);
340102
+ const folderName = path18.basename(dir);
339955
340103
  if (name50 && folderName && this.cleanName(name50) !== this.cleanName(folderName)) warnings.push("Skill name does not match containing folder name.");
339956
340104
  return warnings;
339957
340105
  }
339958
340106
  pluginIdForSkill(dir) {
339959
- let current = path17.resolve(dir);
340107
+ let current = path18.resolve(dir);
339960
340108
  for (let i4 = 0; i4 < 6; i4++) {
339961
- const codex = path17.join(current, ".codex-plugin", "plugin.json");
339962
- const claude = path17.join(current, ".claude-plugin", "plugin.json");
340109
+ const codex = path18.join(current, ".codex-plugin", "plugin.json");
340110
+ const claude = path18.join(current, ".claude-plugin", "plugin.json");
339963
340111
  for (const filePath of [codex, claude]) {
339964
340112
  try {
339965
- if (fs15.existsSync(filePath)) {
339966
- const raw = JSON.parse(fs15.readFileSync(filePath, "utf-8"));
340113
+ if (fs16.existsSync(filePath)) {
340114
+ const raw = JSON.parse(fs16.readFileSync(filePath, "utf-8"));
339967
340115
  if (raw?.name) return String(raw.name);
339968
340116
  }
339969
340117
  } catch {
339970
340118
  }
339971
340119
  }
339972
- const parent = path17.dirname(current);
340120
+ const parent = path18.dirname(current);
339973
340121
  if (parent === current) break;
339974
340122
  current = parent;
339975
340123
  }
@@ -340515,6 +340663,22 @@ function createToolchainCore() {
340515
340663
  return { registry: new ToolRegistry(), catalog: new CapabilityCatalog() };
340516
340664
  }
340517
340665
 
340666
+ // src/core/emptyResponseRetry.ts
340667
+ var EMPTY_RESPONSE_RETRY_DELAYS_MS = [200, 800, 2e3, 1e4, 6e4];
340668
+ var MAX_EMPTY_RESPONSE_RETRIES = EMPTY_RESPONSE_RETRY_DELAYS_MS.length;
340669
+ var MAX_CONSECUTIVE_EMPTY_RESPONSES = MAX_EMPTY_RESPONSE_RETRIES + 1;
340670
+ function emptyResponseRetryDelayMs(consecutiveEmptyResponses) {
340671
+ return EMPTY_RESPONSE_RETRY_DELAYS_MS[Math.max(0, consecutiveEmptyResponses - 1)] ?? 0;
340672
+ }
340673
+ function observeEmptyResponseOutcome(consecutiveEmptyResponses, emptyResponse) {
340674
+ const nextCount = emptyResponse ? consecutiveEmptyResponses + 1 : 0;
340675
+ return {
340676
+ consecutiveEmptyResponses: nextCount,
340677
+ retry: emptyResponse && nextCount <= MAX_EMPTY_RESPONSE_RETRIES,
340678
+ terminate: emptyResponse && nextCount > MAX_EMPTY_RESPONSE_RETRIES
340679
+ };
340680
+ }
340681
+
340518
340682
  // src/core/agentKernelRunner.ts
340519
340683
  var publicStreamFilters = /* @__PURE__ */ new WeakMap();
340520
340684
  var brokerOnlyAssistantBuffers = /* @__PURE__ */ new WeakMap();
@@ -340666,7 +340830,7 @@ function kernelTurnFailed(agent, turn) {
340666
340830
  return turn.stopReason === "error" || agent.isLlmErrorText(turn.text);
340667
340831
  }
340668
340832
  function providerTurnIsEmpty(turn) {
340669
- return /provider returned an empty response/i.test(`${turn.errorMessage}
340833
+ return !turn.activity && /provider returned an empty response/i.test(`${turn.errorMessage}
340670
340834
  ${turn.text}`);
340671
340835
  }
340672
340836
  function removeTrailingFailedAssistant(agent, messages) {
@@ -340675,6 +340839,12 @@ function removeTrailingFailedAssistant(agent, messages) {
340675
340839
  const text = KernelMessageText(last);
340676
340840
  if (last.stopReason === "error" || agent.isLlmErrorText(text)) messages.pop();
340677
340841
  }
340842
+ function removeTrailingThoughtOnlyAssistant(messages) {
340843
+ const last = messages[messages.length - 1];
340844
+ if (last?.role !== "assistant") return;
340845
+ const hasToolCall = last.content.some((content) => content.type === "toolCall");
340846
+ if (!KernelMessageText(last).trim() && !hasToolCall) messages.pop();
340847
+ }
340678
340848
  function normalizePublicProviderError(error, secrets = []) {
340679
340849
  let raw = "";
340680
340850
  if (error instanceof Error) {
@@ -340800,8 +340970,17 @@ async function runAgentKernel(agent) {
340800
340970
  const tokens = [];
340801
340971
  const runOnce = async (promptMessages, appendPromptToAgentHistory) => {
340802
340972
  let lastAssistant = null;
340973
+ let observedActivity = false;
340974
+ let observedThought = false;
340803
340975
  const unsubscribe = kernel2.subscribe(async (event) => {
340804
340976
  await handleKernelEvent(agent, event, tokens);
340977
+ if (event.type === "message_update") {
340978
+ const delta = event.assistantMessageEvent;
340979
+ const deltaText = typeof delta.delta === "string" ? delta.delta : "";
340980
+ const thoughtDelta = delta.type === "thinking_delta" && !!deltaText.trim();
340981
+ observedThought = observedThought || thoughtDelta;
340982
+ observedActivity = observedActivity || thoughtDelta || delta.type === "text_delta" && !!deltaText.trim() || delta.type === "toolcall_end";
340983
+ }
340805
340984
  if (event.type === "message_end" && event.message.role === "assistant") {
340806
340985
  lastAssistant = event.message;
340807
340986
  }
@@ -340819,11 +340998,13 @@ async function runAgentKernel(agent) {
340819
340998
  const assistant = lastAssistant;
340820
340999
  const text = assistant ? KernelMessageText(assistant) : "";
340821
341000
  const hasToolCall = !!assistant?.content?.some((content) => content.type === "toolCall");
340822
- const emptyResponse = !assistant || !text.trim() && !hasToolCall && String(assistant?.stopReason || "") !== "aborted";
341001
+ const emptyResponse = !assistant || !text.trim() && !hasToolCall && !observedActivity && String(assistant?.stopReason || "") !== "aborted";
340823
341002
  return {
340824
341003
  text: emptyResponse ? "[Error] Provider returned an empty response." : text,
340825
341004
  stopReason: String(assistant?.stopReason || ""),
340826
- errorMessage: String(assistant?.errorMessage || (emptyResponse ? "Provider returned an empty response." : ""))
341005
+ errorMessage: String(assistant?.errorMessage || (emptyResponse ? "Provider returned an empty response." : "")),
341006
+ activity: observedActivity || !!text.trim() || hasToolCall,
341007
+ thoughtOnly: observedThought && !text.trim() && !hasToolCall && !["error", "aborted"].includes(String(assistant?.stopReason || ""))
340827
341008
  };
340828
341009
  } finally {
340829
341010
  unsubscribe();
@@ -340869,14 +341050,22 @@ async function runAgentKernel(agent) {
340869
341050
  fallback: { from: modelBeforeKernelRun, to: agent.model, providerId: agent.activeDeployment()?.providerId }
340870
341051
  });
340871
341052
  }
340872
- let emptyResponseRetries = 0;
340873
- while (providerTurnIsEmpty(lastTurn) && emptyResponseRetries < 2) {
341053
+ let consecutiveEmptyResponses = 0;
341054
+ for (; ; ) {
341055
+ const emptyResponseState = observeEmptyResponseOutcome(consecutiveEmptyResponses, providerTurnIsEmpty(lastTurn));
341056
+ consecutiveEmptyResponses = emptyResponseState.consecutiveEmptyResponses;
341057
+ if (lastTurn.thoughtOnly) {
341058
+ removeTrailingThoughtOnlyAssistant(kernel2.state.messages);
341059
+ lastTurn = await runWithCompressionResume([], false);
341060
+ continue;
341061
+ }
341062
+ if (!emptyResponseState.retry) break;
340874
341063
  removeTrailingFailedAssistant(agent, kernel2.state.messages);
340875
- emptyResponseRetries += 1;
340876
- const notice = `[Model retry] Provider returned an empty response; retrying the same deployment (${emptyResponseRetries}/2).`;
341064
+ const retryNumber = consecutiveEmptyResponses;
341065
+ const notice = `[Model retry] Provider returned an empty response; retrying the same deployment (${retryNumber}/${MAX_EMPTY_RESPONSE_RETRIES}) after ${emptyResponseRetryDelayMs(consecutiveEmptyResponses)}ms.`;
340877
341066
  tokens.push({ type: "text", text: notice });
340878
341067
  agent.recordWorkStatus(notice);
340879
- await agent.waitForPlannedRouteRetry();
341068
+ await agent.waitForPlannedRouteRetry(emptyResponseRetryDelayMs(consecutiveEmptyResponses));
340880
341069
  lastTurn = await runWithCompressionResume([], false);
340881
341070
  }
340882
341071
  let routeRetries = 0;
@@ -341072,7 +341261,7 @@ async function runAgentKernel(agent) {
341072
341261
  return;
341073
341262
  }
341074
341263
  if (textStarted) finalContent.push({ type: "text", text });
341075
- if (!finalContent.length) {
341264
+ if (!finalContent.length && !thinking.trim()) {
341076
341265
  text = "[Error] Provider returned an empty response.";
341077
341266
  finalContent.push({ type: "text", text });
341078
341267
  }
@@ -344956,7 +345145,7 @@ var Agent4 = class _Agent {
344956
345145
  this.config.set("skills", "auto_download", "disabled");
344957
345146
  }
344958
345147
  const modeStr = this.config.getStr("agent", "default_mode");
344959
- this.mode = ["plan", "goal", "flow"].includes(modeStr) ? modeStr : "build";
345148
+ this.mode = ["plan", "chat", "goal", "flow"].includes(modeStr) ? modeStr : "build";
344960
345149
  const inputStr = this.config.getStr("general", "default_input");
344961
345150
  this.inputMode = inputStr === "next" ? "next" : "guide";
344962
345151
  const configuredModel = this.config.getStr("models", "default_model");
@@ -345124,6 +345313,7 @@ var Agent4 = class _Agent {
345124
345313
  return this.toolchainCore;
345125
345314
  }
345126
345315
  setMode(m2) {
345316
+ if (!["build", "plan", "chat", "goal", "flow"].includes(m2)) m2 = "build";
345127
345317
  if (m2 === "goal" && !this.goal) {
345128
345318
  this.goal = new GoalStateImpl("Set your objective");
345129
345319
  }
@@ -345656,7 +345846,12 @@ var Agent4 = class _Agent {
345656
345846
  beginRouteAttempt() {
345657
345847
  this.routeAttemptStartedAt = Date.now();
345658
345848
  }
345659
- async waitForPlannedRouteRetry() {
345849
+ async waitForPlannedRouteRetry(explicitDelayMs) {
345850
+ if (explicitDelayMs !== void 0) {
345851
+ if (explicitDelayMs <= 0) return;
345852
+ await new Promise((resolve16) => setTimeout(resolve16, explicitDelayMs));
345853
+ return;
345854
+ }
345660
345855
  const waitBudgetMs = Math.max(0, Math.min(15e3, this.lastRouteDecision?.retryBudgetMs ?? 5e3));
345661
345856
  const delay = Math.max(0, Math.min(waitBudgetMs, this.lastRouteRetryDelayMs));
345662
345857
  this.lastRouteRetryDelayMs = 0;
@@ -349486,7 +349681,20 @@ ${summary}`, segment, "local-summarize", true);
349486
349681
  if (action === "get") return JSON.stringify({ ok: true, linkedPlan: this.getLinkedPlan() }, null, 2);
349487
349682
  if (action !== "update") return JSON.stringify({ ok: false, error: `Unknown linked_plan action: ${action}` });
349488
349683
  const expectedRevision = Number(input2.expected_revision ?? input2.expectedRevision);
349489
- return JSON.stringify({ ok: true, linkedPlan: this.updateLinkedPlan(String(input2.markdown || ""), expectedRevision) }, null, 2);
349684
+ const current = this.getLinkedPlan();
349685
+ let markdown = input2.markdown === void 0 ? current.markdown : String(input2.markdown);
349686
+ if (input2.append !== void 0) markdown = `${current.markdown}${String(input2.append)}`;
349687
+ if (input2.old_text !== void 0 || input2.oldText !== void 0) {
349688
+ const oldText = String(input2.old_text ?? input2.oldText ?? "");
349689
+ if (!oldText) throw new Error("linked_plan old_text must not be empty.");
349690
+ const matches = current.markdown.split(oldText).length - 1;
349691
+ if (!matches) throw new Error("linked_plan old_text was not found.");
349692
+ const replaceAll = input2.replace_all === true || input2.replaceAll === true;
349693
+ if (matches > 1 && !replaceAll) throw new Error(`linked_plan old_text matched ${matches} places; pass replace_all=true or a unique fragment.`);
349694
+ const newText = String(input2.new_text ?? input2.newText ?? "");
349695
+ markdown = replaceAll ? current.markdown.split(oldText).join(newText) : current.markdown.replace(oldText, newText);
349696
+ }
349697
+ return JSON.stringify({ ok: true, linkedPlan: this.updateLinkedPlan(markdown, expectedRevision) }, null, 2);
349490
349698
  } catch (error) {
349491
349699
  return JSON.stringify({ ok: false, error: error instanceof Error ? error.message : String(error) });
349492
349700
  }
@@ -351788,7 +351996,24 @@ ${this.formatAutomation(item)}` : `[automation_toggle] Not found: ${id}`;
351788
351996
  }));
351789
351997
  }
351790
351998
  case "memory_lab_update": {
351791
- const result = await this.updateMemoryLab({
351999
+ const selector2 = String(params.component || params.slug || "").trim();
352000
+ const prepared = selector2 ? this.memoryLab.preparePatch({
352001
+ component: selector2,
352002
+ name: params.name === void 0 ? void 0 : String(params.name),
352003
+ description: params.description === void 0 ? void 0 : String(params.description),
352004
+ tags: params.tags === void 0 ? void 0 : Array.isArray(params.tags) ? params.tags.map(String) : String(params.tags).split(/[,,\n]+/),
352005
+ tagPaths: params.tagPaths === void 0 ? void 0 : Array.isArray(params.tagPaths) ? params.tagPaths.filter(Array.isArray).map((pathValue) => pathValue.map(String)) : [],
352006
+ content: params.content === void 0 ? void 0 : String(params.content),
352007
+ contentAppend: params.contentAppend === void 0 && params.content_append === void 0 ? void 0 : String(params.contentAppend ?? params.content_append),
352008
+ oldText: params.oldText === void 0 && params.old_text === void 0 ? void 0 : String(params.oldText ?? params.old_text),
352009
+ newText: String(params.newText ?? params.new_text ?? ""),
352010
+ replaceAll: params.replaceAll === true || params.replace_all === true,
352011
+ kind: params.kind === void 0 ? void 0 : params.kind === "folder" ? "folder" : "file",
352012
+ expectedUpdatedAt: String(params.expectedUpdatedAt || params.expected_updated_at || ""),
352013
+ reason: String(params.reason || ""),
352014
+ source: String(params.source || "")
352015
+ }) : void 0;
352016
+ const result = await this.updateMemoryLab(prepared || {
351792
352017
  name: String(params.name || ""),
351793
352018
  description: String(params.description || ""),
351794
352019
  tags: Array.isArray(params.tags) ? params.tags.map(String) : String(params.tags || "").split(/[,,\n]+/),
@@ -352791,6 +353016,8 @@ When using file tools (read, write, edit, glob), use ABSOLUTE paths rooted at th
352791
353016
  parts.push(this.buildFeatureDisclosurePrompt());
352792
353017
  if (this.mode === "plan") parts.push(`[Plan Tool Policy]
352793
353018
  ${planModePolicyPrompt()}`);
353019
+ if (this.mode === "chat") parts.push(`[Chat Tool Policy]
353020
+ ${chatModePolicyPrompt()}`);
352794
353021
  const pm = this.config.getStr("workspace", "prompt_mode") || "both";
352795
353022
  const injectedPrompts = /* @__PURE__ */ new Set();
352796
353023
  if ((pm === "global_only" || pm === "both") && globalPrompt) {
@@ -352912,7 +353139,7 @@ ${custom}`);
352912
353139
  `- 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.`,
352913
353140
  `- Workspace permissions: access_permission=${permission}; file tools are checked before execution and blocked when they exceed the configured workspace boundary.`,
352914
353141
  `- 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.`,
352915
- `- 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.`,
353142
+ `- 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.`,
352916
353143
  `- Input mode: ${input2}; Guide injects immediately, Next queues user intent for the following build turn.`,
352917
353144
  `- Option feedback: ${this.buildQuestionPolicyPrompt(optionFeedback)}`,
352918
353145
  `- Model policy: current model=${this.model || "(unset)"}, intelligence=${this.intelligence}, auto-switch=${modelSwitch}.`,
@@ -352978,6 +353205,14 @@ ${custom}`);
352978
353205
  '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.',
352979
353206
  "A positive choice starts a new Build-mode input. A negative choice remains in Plan mode so the user can supply the missing details."
352980
353207
  ]).join("\n");
353208
+ case "chat":
353209
+ return withLanguage([
353210
+ "CHAT MODE.",
353211
+ "Use only web_search and web_fetch. You have no workspace, host, application, memory, task, browser-control, or write permissions.",
353212
+ "Perform an online search to gather evidence before answering. Fetch primary or authoritative pages when the search snippets are insufficient.",
353213
+ "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.",
353214
+ "Distinguish sourced facts from uncertainty and include useful source links in the final answer."
353215
+ ]).join("\n");
352981
353216
  case "goal": {
352982
353217
  const g2 = this.goal?.history() || "";
352983
353218
  const paused = this.goal?.paused ? "\n[GOAL PAUSED by user. Wait for resume.]" : "\n[Continue working until the goal is achieved.]";