@markus-global/cli 0.4.22 → 0.4.24

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.
Files changed (83) hide show
  1. package/dist/commands/auth.js +1 -1
  2. package/dist/commands/auth.js.map +1 -1
  3. package/dist/commands/doctor.d.ts.map +1 -1
  4. package/dist/commands/doctor.js +9 -1
  5. package/dist/commands/doctor.js.map +1 -1
  6. package/dist/commands/init.d.ts.map +1 -1
  7. package/dist/commands/init.js +6 -2
  8. package/dist/commands/init.js.map +1 -1
  9. package/dist/commands/model.js +3 -3
  10. package/dist/commands/model.js.map +1 -1
  11. package/dist/commands/start.d.ts.map +1 -1
  12. package/dist/commands/start.js +35 -4
  13. package/dist/commands/start.js.map +1 -1
  14. package/dist/markus.mjs +1289 -418
  15. package/dist/web-ui/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  16. package/dist/web-ui/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  17. package/dist/web-ui/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  18. package/dist/web-ui/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  19. package/dist/web-ui/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  20. package/dist/web-ui/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  21. package/dist/web-ui/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  22. package/dist/web-ui/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  23. package/dist/web-ui/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  24. package/dist/web-ui/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  25. package/dist/web-ui/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  26. package/dist/web-ui/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  27. package/dist/web-ui/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  28. package/dist/web-ui/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  29. package/dist/web-ui/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  30. package/dist/web-ui/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  31. package/dist/web-ui/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  32. package/dist/web-ui/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  33. package/dist/web-ui/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  34. package/dist/web-ui/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  35. package/dist/web-ui/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  36. package/dist/web-ui/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  37. package/dist/web-ui/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  38. package/dist/web-ui/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  39. package/dist/web-ui/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  40. package/dist/web-ui/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  41. package/dist/web-ui/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  42. package/dist/web-ui/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  43. package/dist/web-ui/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  44. package/dist/web-ui/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  45. package/dist/web-ui/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  46. package/dist/web-ui/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  47. package/dist/web-ui/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  48. package/dist/web-ui/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  49. package/dist/web-ui/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  50. package/dist/web-ui/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  51. package/dist/web-ui/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  52. package/dist/web-ui/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  53. package/dist/web-ui/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  54. package/dist/web-ui/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  55. package/dist/web-ui/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  56. package/dist/web-ui/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  57. package/dist/web-ui/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  58. package/dist/web-ui/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  59. package/dist/web-ui/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  60. package/dist/web-ui/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  61. package/dist/web-ui/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  62. package/dist/web-ui/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  63. package/dist/web-ui/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  64. package/dist/web-ui/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  65. package/dist/web-ui/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  66. package/dist/web-ui/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  67. package/dist/web-ui/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  68. package/dist/web-ui/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  69. package/dist/web-ui/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  70. package/dist/web-ui/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  71. package/dist/web-ui/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  72. package/dist/web-ui/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  73. package/dist/web-ui/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  74. package/dist/web-ui/assets/index-CJqOXq2-.js +343 -0
  75. package/dist/web-ui/assets/index-vdyam0yR.css +1 -0
  76. package/dist/web-ui/index.html +2 -2
  77. package/package.json +1 -1
  78. package/templates/roles/org-manager/HEARTBEAT.md +8 -0
  79. package/templates/roles/secretary/HEARTBEAT.md +8 -0
  80. package/templates/skills/agent-building/SKILL.md +14 -5
  81. package/templates/skills/team-building/SKILL.md +22 -5
  82. package/dist/web-ui/assets/index-DEkXd9d7.css +0 -1
  83. package/dist/web-ui/assets/index-HAWGXtTM.js +0 -66
package/dist/markus.mjs CHANGED
@@ -4061,8 +4061,9 @@ var init_models = __esm({
4061
4061
  id: "deepseek",
4062
4062
  label: "DeepSeek",
4063
4063
  envKey: "DEEPSEEK_API_KEY",
4064
- defaultModel: "deepseek-chat",
4065
- models: ["deepseek-chat", "deepseek-coder", "deepseek-chat-v3"]
4064
+ baseUrl: "https://api.deepseek.com",
4065
+ defaultModel: "deepseek-v4-flash",
4066
+ models: ["deepseek-v4-flash", "deepseek-v4-pro", "deepseek-chat", "deepseek-reasoner"]
4066
4067
  }
4067
4068
  ];
4068
4069
  }
@@ -8324,8 +8325,16 @@ var init_tool_selector = __esm({
8324
8325
  result.push({ name: tool.name, description: tool.description, inputSchema: tool.inputSchema });
8325
8326
  }
8326
8327
  }
8328
+ const seen = new Set(result.map((t) => t.name));
8327
8329
  result.push(this.buildDiscoverTool(opts.allTools, selected, opts.skillCatalog));
8328
- result.push({
8330
+ seen.add("discover_tools");
8331
+ const pushUnique = (tool) => {
8332
+ if (!seen.has(tool.name)) {
8333
+ seen.add(tool.name);
8334
+ result.push(tool);
8335
+ }
8336
+ };
8337
+ pushUnique({
8329
8338
  name: "notify_user",
8330
8339
  description: "Send a message to the user. The message appears in the agent chat and as a notification. Write a comprehensive body \u2014 the user sees the full content and may reply. Use for status updates, reports, alerts, and findings.",
8331
8340
  inputSchema: {
@@ -8339,7 +8348,7 @@ var init_tool_selector = __esm({
8339
8348
  required: ["title", "body"]
8340
8349
  }
8341
8350
  });
8342
- result.push({
8351
+ pushUnique({
8343
8352
  name: "request_user_approval",
8344
8353
  description: "Request a decision or approval from the user. The tool BLOCKS until the user responds. Use when you need human approval, a choice between options, or any user decision/input. Default options: Approve / Reject (reject requires a reason). You can provide custom options and optionally allow freeform text input.",
8345
8354
  inputSchema: {
@@ -8367,7 +8376,7 @@ var init_tool_selector = __esm({
8367
8376
  required: ["title", "description"]
8368
8377
  }
8369
8378
  });
8370
- result.push({
8379
+ pushUnique({
8371
8380
  name: "recall_activity",
8372
8381
  description: 'Query your own execution history. Use "list" to see recent activities, or "get" with an activity_id to see detailed tool call logs for a specific activity.',
8373
8382
  inputSchema: {
@@ -8668,7 +8677,7 @@ function createShellTool(security, workspacePath, agentMeta, policy, onCommandAp
8668
8677
  }
8669
8678
  }
8670
8679
  const finalCommand = injectGitCommitMeta(command, agentMeta);
8671
- return new Promise((resolve21) => {
8680
+ return new Promise((resolve20) => {
8672
8681
  const child = spawn("sh", ["-c", finalCommand], {
8673
8682
  cwd: effectiveCwd ?? void 0,
8674
8683
  stdio: ["ignore", "pipe", "pipe"],
@@ -8723,7 +8732,7 @@ function createShellTool(security, workspacePath, agentMeta, policy, onCommandAp
8723
8732
  flushOutput();
8724
8733
  }
8725
8734
  if (killed) {
8726
- resolve21(JSON.stringify({
8735
+ resolve20(JSON.stringify({
8727
8736
  status: "error",
8728
8737
  error: `Command timed out after ${timeoutMs}ms`,
8729
8738
  exitCode: code,
@@ -8731,7 +8740,7 @@ function createShellTool(security, workspacePath, agentMeta, policy, onCommandAp
8731
8740
  stderr: stderr.slice(0, 4e3)
8732
8741
  }));
8733
8742
  } else if (code !== 0) {
8734
- resolve21(JSON.stringify({
8743
+ resolve20(JSON.stringify({
8735
8744
  status: "error",
8736
8745
  error: stderr.trim() || `Process exited with code ${code}`,
8737
8746
  exitCode: code,
@@ -8739,7 +8748,7 @@ function createShellTool(security, workspacePath, agentMeta, policy, onCommandAp
8739
8748
  stderr: stderr.slice(0, 4e3)
8740
8749
  }));
8741
8750
  } else {
8742
- resolve21(JSON.stringify({
8751
+ resolve20(JSON.stringify({
8743
8752
  status: "success",
8744
8753
  stdout: stdout.trim() || void 0,
8745
8754
  stderr: stderr.trim() || void 0
@@ -8752,7 +8761,7 @@ function createShellTool(security, workspacePath, agentMeta, policy, onCommandAp
8752
8761
  clearTimeout(flushTimer);
8753
8762
  flushOutput();
8754
8763
  }
8755
- resolve21(JSON.stringify({
8764
+ resolve20(JSON.stringify({
8756
8765
  status: "error",
8757
8766
  error: err.message,
8758
8767
  stdout: stdout.slice(0, 4e3),
@@ -14852,8 +14861,8 @@ var init_custom_element_registry = __esm({
14852
14861
  } : (element) => element.localName === localName;
14853
14862
  registry.set(localName, { Class, check: check2 });
14854
14863
  if (waiting.has(localName)) {
14855
- for (const resolve21 of waiting.get(localName))
14856
- resolve21(Class);
14864
+ for (const resolve20 of waiting.get(localName))
14865
+ resolve20(Class);
14857
14866
  waiting.delete(localName);
14858
14867
  }
14859
14868
  ownerDocument.querySelectorAll(
@@ -14893,13 +14902,13 @@ var init_custom_element_registry = __esm({
14893
14902
  */
14894
14903
  whenDefined(localName) {
14895
14904
  const { registry, waiting } = this;
14896
- return new Promise((resolve21) => {
14905
+ return new Promise((resolve20) => {
14897
14906
  if (registry.has(localName))
14898
- resolve21(registry.get(localName).Class);
14907
+ resolve20(registry.get(localName).Class);
14899
14908
  else {
14900
14909
  if (!waiting.has(localName))
14901
14910
  waiting.set(localName, []);
14902
- waiting.get(localName).push(resolve21);
14911
+ waiting.get(localName).push(resolve20);
14903
14912
  }
14904
14913
  });
14905
14914
  }
@@ -41155,13 +41164,41 @@ var init_web_fetch = __esm({
41155
41164
  });
41156
41165
 
41157
41166
  // ../core/dist/tools/web-search.js
41167
+ async function resolveProxyDispatcher() {
41168
+ if (_dispatcher !== false)
41169
+ return _dispatcher || void 0;
41170
+ const proxyUrl = process.env["HTTPS_PROXY"] || process.env["HTTP_PROXY"] || process.env["https_proxy"] || process.env["http_proxy"];
41171
+ if (!proxyUrl) {
41172
+ _dispatcher = void 0;
41173
+ return void 0;
41174
+ }
41175
+ try {
41176
+ const load = new Function("id", "return import(id)");
41177
+ const { ProxyAgent } = await load("undici");
41178
+ _dispatcher = new ProxyAgent(proxyUrl);
41179
+ } catch {
41180
+ _dispatcher = void 0;
41181
+ }
41182
+ return _dispatcher || void 0;
41183
+ }
41184
+ async function proxyFetch(url, init) {
41185
+ const dispatcher = await resolveProxyDispatcher();
41186
+ const signal = init?.signal ?? AbortSignal.timeout(SEARCH_TIMEOUT_MS);
41187
+ const opts = { ...init, signal };
41188
+ if (dispatcher)
41189
+ opts["dispatcher"] = dispatcher;
41190
+ return fetch(url, opts);
41191
+ }
41192
+ function hasProxy() {
41193
+ return !!(process.env["HTTPS_PROXY"] || process.env["HTTP_PROXY"] || process.env["https_proxy"] || process.env["http_proxy"]);
41194
+ }
41158
41195
  async function searchSerper(query2, maxResults) {
41159
41196
  const apiKey = process.env["SERPER_API_KEY"];
41160
41197
  if (!apiKey)
41161
41198
  throw new Error("SERPER_API_KEY not configured");
41162
41199
  let res;
41163
41200
  try {
41164
- res = await fetch("https://google.serper.dev/search", {
41201
+ res = await proxyFetch("https://google.serper.dev/search", {
41165
41202
  method: "POST",
41166
41203
  headers: {
41167
41204
  "X-API-KEY": apiKey,
@@ -41189,7 +41226,7 @@ async function searchBrave(query2, maxResults) {
41189
41226
  const params = new URLSearchParams({ q: query2, count: String(maxResults) });
41190
41227
  let res;
41191
41228
  try {
41192
- res = await fetch(`https://api.search.brave.com/res/v1/web/search?${params}`, {
41229
+ res = await proxyFetch(`https://api.search.brave.com/res/v1/web/search?${params}`, {
41193
41230
  headers: {
41194
41231
  "Accept": "application/json",
41195
41232
  "Accept-Encoding": "gzip",
@@ -41209,23 +41246,28 @@ async function searchBrave(query2, maxResults) {
41209
41246
  date: r.page_age
41210
41247
  }));
41211
41248
  }
41212
- async function searchDuckDuckGoLite(query2, maxResults) {
41249
+ async function searchDuckDuckGo(query2, maxResults) {
41213
41250
  const encoded = encodeURIComponent(query2);
41214
- let res;
41215
- try {
41216
- res = await fetch(`https://lite.duckduckgo.com/lite/?q=${encoded}`, {
41217
- headers: {
41218
- "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
41251
+ let lastError;
41252
+ for (const base of DDG_ENDPOINTS) {
41253
+ try {
41254
+ const res = await proxyFetch(`${base}?q=${encoded}`, {
41255
+ headers: { "User-Agent": DDG_UA }
41256
+ });
41257
+ if (!res.ok) {
41258
+ lastError = new Error(`HTTP ${res.status} ${res.statusText}`);
41259
+ continue;
41219
41260
  }
41220
- });
41221
- } catch (err) {
41222
- throw new Error(`Network error: ${err instanceof Error ? err.message : String(err)}`);
41223
- }
41224
- if (!res.ok) {
41225
- return searchDuckDuckGoHtml(query2, maxResults);
41261
+ const html = await res.text();
41262
+ const results = base.includes("lite") ? parseDDGLite(html, maxResults) : parseDDGHtml(html, maxResults);
41263
+ if (results.length > 0)
41264
+ return results;
41265
+ lastError = new Error(`Parsed 0 results from ${base}`);
41266
+ } catch (err) {
41267
+ lastError = err instanceof Error ? err : new Error(String(err));
41268
+ }
41226
41269
  }
41227
- const html = await res.text();
41228
- return parseDDGLite(html, maxResults);
41270
+ throw new Error(`Network error: ${lastError?.message ?? "all DuckDuckGo endpoints failed"}`);
41229
41271
  }
41230
41272
  function parseDDGLite(html, max) {
41231
41273
  const results = [];
@@ -41264,21 +41306,7 @@ function parseDDGLite(html, max) {
41264
41306
  }
41265
41307
  return results;
41266
41308
  }
41267
- async function searchDuckDuckGoHtml(query2, maxResults) {
41268
- const encoded = encodeURIComponent(query2);
41269
- let res;
41270
- try {
41271
- res = await fetch(`https://html.duckduckgo.com/html/?q=${encoded}`, {
41272
- headers: {
41273
- "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
41274
- }
41275
- });
41276
- } catch (err) {
41277
- throw new Error(`Network error: ${err instanceof Error ? err.message : String(err)}`);
41278
- }
41279
- if (!res.ok)
41280
- throw new Error(`HTTP ${res.status} ${res.statusText}`);
41281
- const html = await res.text();
41309
+ function parseDDGHtml(html, maxResults) {
41282
41310
  const results = [];
41283
41311
  const linkRegex = /<a[^>]+class="result__a"[^>]*href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/g;
41284
41312
  const snippetRegex = /<a[^>]+class="result__snippet"[^>]*>([\s\S]*?)<\/a>/g;
@@ -41306,10 +41334,12 @@ async function searchDuckDuckGoHtml(query2, maxResults) {
41306
41334
  function stripHtml(html) {
41307
41335
  return html.replace(/<[^>]*>/g, "").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#x27;/g, "'").replace(/&#39;/g, "'").replace(/&nbsp;/g, " ").replace(/\s+/g, " ").trim();
41308
41336
  }
41309
- var WebSearchTool;
41337
+ var SEARCH_TIMEOUT_MS, _dispatcher, WebSearchTool, DDG_UA, DDG_ENDPOINTS;
41310
41338
  var init_web_search = __esm({
41311
41339
  "../core/dist/tools/web-search.js"() {
41312
41340
  "use strict";
41341
+ SEARCH_TIMEOUT_MS = 15e3;
41342
+ _dispatcher = false;
41313
41343
  WebSearchTool = {
41314
41344
  name: "web_search",
41315
41345
  description: "Search the web for real-time information. Returns search result titles, snippets, and URLs. Use this when you need up-to-date information that might not be in your training data.",
@@ -41333,7 +41363,7 @@ var init_web_search = __esm({
41333
41363
  const backends = [
41334
41364
  { name: "Serper", fn: searchSerper },
41335
41365
  { name: "Brave", fn: searchBrave },
41336
- { name: "DuckDuckGo", fn: searchDuckDuckGoLite }
41366
+ { name: "DuckDuckGo", fn: searchDuckDuckGo }
41337
41367
  ];
41338
41368
  const errors = [];
41339
41369
  for (const { name, fn } of backends) {
@@ -41348,13 +41378,27 @@ var init_web_search = __esm({
41348
41378
  errors.push({ backend: name, error: message });
41349
41379
  }
41350
41380
  }
41381
+ const hasNetworkErr = errors.some((e) => e.error.includes("Network error") || e.error.includes("timed out"));
41382
+ const hints = [];
41383
+ if (hasNetworkErr && !hasProxy()) {
41384
+ hints.push("All network requests failed and no HTTP proxy is configured. If you are behind a firewall or in a restricted network, set HTTPS_PROXY (e.g. export HTTPS_PROXY=http://127.0.0.1:7890).");
41385
+ }
41386
+ if (hasNetworkErr && hasProxy()) {
41387
+ hints.push(`HTTP proxy is configured (${process.env["HTTPS_PROXY"] || process.env["HTTP_PROXY"] || process.env["https_proxy"] || process.env["http_proxy"]}), but network requests still failed. Verify the proxy is reachable and allows outbound HTTPS.`);
41388
+ }
41351
41389
  return JSON.stringify({
41352
41390
  status: "error",
41353
41391
  error: "All search backends failed.",
41354
- details: errors
41392
+ details: errors,
41393
+ ...hints.length > 0 ? { hints } : {}
41355
41394
  });
41356
41395
  }
41357
41396
  };
41397
+ DDG_UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36";
41398
+ DDG_ENDPOINTS = [
41399
+ "https://lite.duckduckgo.com/lite/",
41400
+ "https://html.duckduckgo.com/html/"
41401
+ ];
41358
41402
  }
41359
41403
  });
41360
41404
 
@@ -41766,7 +41810,7 @@ var init_search = __esm({
41766
41810
 
41767
41811
  // ../core/dist/tools/patch.js
41768
41812
  import { readFileSync as readFileSync9, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8, existsSync as existsSync13, unlinkSync } from "node:fs";
41769
- import { dirname as dirname4, resolve as resolve7 } from "node:path";
41813
+ import { dirname as dirname4 } from "node:path";
41770
41814
  function createPatchTool(security, workspacePath, policy) {
41771
41815
  const guard = security ?? defaultSecurityGuard;
41772
41816
  return {
@@ -41817,7 +41861,7 @@ function createPatchTool(security, workspacePath, policy) {
41817
41861
  if (!patches || !Array.isArray(patches) || patches.length === 0) {
41818
41862
  return JSON.stringify({ status: "error", error: "patches array is required and must not be empty" });
41819
41863
  }
41820
- const basePath = workspacePath ?? process.cwd();
41864
+ const resolvedPaths = [];
41821
41865
  const results = [];
41822
41866
  for (const patch of patches) {
41823
41867
  const { resolved: filePath, access } = resolveAndCheckAccess(patch.file, workspacePath, policy);
@@ -41858,6 +41902,7 @@ function createPatchTool(security, workspacePath, policy) {
41858
41902
  if (patch.action === "create" && !patch.content && patch.content !== "") {
41859
41903
  return JSON.stringify({ status: "error", error: `create action requires content for ${patch.file}` });
41860
41904
  }
41905
+ resolvedPaths.push(filePath);
41861
41906
  }
41862
41907
  if (dryRun) {
41863
41908
  return JSON.stringify({
@@ -41866,8 +41911,9 @@ function createPatchTool(security, workspacePath, policy) {
41866
41911
  patchCount: patches.length
41867
41912
  });
41868
41913
  }
41869
- for (const patch of patches) {
41870
- const filePath = resolve7(basePath, patch.file);
41914
+ for (let i = 0; i < patches.length; i++) {
41915
+ const patch = patches[i];
41916
+ const filePath = resolvedPaths[i];
41871
41917
  switch (patch.action) {
41872
41918
  case "edit": {
41873
41919
  let content = readFileSync9(filePath, "utf-8");
@@ -41923,7 +41969,7 @@ var init_patch = __esm({
41923
41969
 
41924
41970
  // ../core/dist/tools/process-manager.js
41925
41971
  import { spawn as spawn2 } from "node:child_process";
41926
- import { resolve as resolve8 } from "node:path";
41972
+ import { resolve as resolve7 } from "node:path";
41927
41973
  function onBackgroundCompletion(cb) {
41928
41974
  completionListeners.push(cb);
41929
41975
  return () => {
@@ -41985,8 +42031,8 @@ function createBackgroundExecTool(workspacePath) {
41985
42031
  const cwd = args["cwd"];
41986
42032
  const timeoutSec = args["timeout_seconds"] ?? 300;
41987
42033
  const basePath = workspacePath ?? process.cwd();
41988
- const effectiveCwd = cwd ? resolve8(basePath, cwd) : basePath;
41989
- if (workspacePath && !effectiveCwd.startsWith(resolve8(workspacePath))) {
42034
+ const effectiveCwd = cwd ? resolve7(basePath, cwd) : basePath;
42035
+ if (workspacePath && !effectiveCwd.startsWith(resolve7(workspacePath))) {
41990
42036
  return JSON.stringify({ status: "denied", error: "Working directory must be within workspace" });
41991
42037
  }
41992
42038
  const id = `bg_${++sessionCounter}_${Date.now()}`;
@@ -42250,7 +42296,7 @@ async function llmCallWithRetry(fn, label) {
42250
42296
  error: String(err).slice(0, SUBAGENT_ERROR_PREVIEW_CHARS),
42251
42297
  delay
42252
42298
  });
42253
- await new Promise((resolve21) => setTimeout(resolve21, delay));
42299
+ await new Promise((resolve20) => setTimeout(resolve20, delay));
42254
42300
  }
42255
42301
  }
42256
42302
  throw lastErr;
@@ -42323,7 +42369,7 @@ async function runSubagentLoop(ctx, task, opts) {
42323
42369
  metadata: { iteration: iterations, finishReason: response.finishReason }
42324
42370
  });
42325
42371
  if (response.finishReason === "max_tokens" && !response.toolCalls?.length) {
42326
- messages.push({ role: "assistant", content: response.content });
42372
+ messages.push({ role: "assistant", content: response.content, reasoningContent: response.reasoningContent });
42327
42373
  logEntries.push({ ts: (/* @__PURE__ */ new Date()).toISOString(), role: "assistant", content: response.content });
42328
42374
  messages.push({
42329
42375
  role: "user",
@@ -42333,7 +42379,8 @@ async function runSubagentLoop(ctx, task, opts) {
42333
42379
  messages.push({
42334
42380
  role: "assistant",
42335
42381
  content: response.content,
42336
- toolCalls: response.toolCalls
42382
+ toolCalls: response.toolCalls,
42383
+ reasoningContent: response.reasoningContent
42337
42384
  });
42338
42385
  logEntries.push({
42339
42386
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -42838,9 +42885,9 @@ ${typeLabel}${item.payload.content}`;
42838
42885
  */
42839
42886
  wakeIdleLoop() {
42840
42887
  if (this.idleResolve) {
42841
- const resolve21 = this.idleResolve;
42888
+ const resolve20 = this.idleResolve;
42842
42889
  this.idleResolve = void 0;
42843
- resolve21();
42890
+ resolve20();
42844
42891
  }
42845
42892
  }
42846
42893
  /**
@@ -42865,8 +42912,8 @@ ${typeLabel}${item.payload.content}`;
42865
42912
  const item = this.dequeue();
42866
42913
  if (item)
42867
42914
  return item;
42868
- await new Promise((resolve21) => {
42869
- this.idleResolve = resolve21;
42915
+ await new Promise((resolve20) => {
42916
+ this.idleResolve = resolve20;
42870
42917
  });
42871
42918
  const afterWake = this.dequeue();
42872
42919
  if (!afterWake) {
@@ -42917,6 +42964,18 @@ ${typeLabel}${item.payload.content}`;
42917
42964
  this.persistence?.updateStatus(item.id, "deferred", { deferredUntil: until });
42918
42965
  return item;
42919
42966
  }
42967
+ /**
42968
+ * Defer an item that has already been dequeued (not in the queue).
42969
+ * Used when processing is interrupted (preempted) and the item should
42970
+ * be resumed later. Only updates persistence — does NOT re-insert
42971
+ * into the in-memory queue (resurfaceDue handles that on the next idle cycle).
42972
+ */
42973
+ deferDequeued(item, until) {
42974
+ item.status = "deferred";
42975
+ item.deferredUntil = until;
42976
+ item.startedAt = void 0;
42977
+ this.persistence?.updateStatus(item.id, "deferred", { deferredUntil: until });
42978
+ }
42920
42979
  /**
42921
42980
  * Mark an item as merged into another item.
42922
42981
  */
@@ -43158,7 +43217,7 @@ function detectAbnormalCompletion(reply, item) {
43158
43217
  const registry = MAILBOX_TYPE_REGISTRY[item.sourceType];
43159
43218
  if (!registry?.invokesLLM)
43160
43219
  return void 0;
43161
- if (reply === "[preempted]")
43220
+ if (reply === "[preempted]" || reply === "[cancelled]")
43162
43221
  return void 0;
43163
43222
  const isUserFacing = item.sourceType === "human_chat" || item.sourceType === "a2a_message";
43164
43223
  if (isUserFacing)
@@ -43195,6 +43254,8 @@ var init_attention = __esm({
43195
43254
  triageToolHandlers;
43196
43255
  triageChatFn;
43197
43256
  lastTriageResult;
43257
+ /** Tracks whether the last yield-point decision was 'cancel' vs 'preempt'. */
43258
+ lastYieldDecision;
43198
43259
  unsubscribeNewItem;
43199
43260
  decisions = [];
43200
43261
  watchdogTimer;
@@ -43391,6 +43452,7 @@ var init_attention = __esm({
43391
43452
  this.interruptSignal = false;
43392
43453
  this.pendingInterruptItem = void 0;
43393
43454
  this.criticalInterruptResolve = void 0;
43455
+ this.lastYieldDecision = void 0;
43394
43456
  this.processingStartedAt = Date.now();
43395
43457
  this.delegate?.onFocusChanged(item);
43396
43458
  let reply = void 0;
@@ -43398,7 +43460,7 @@ var init_attention = __esm({
43398
43460
  try {
43399
43461
  const processing = this.delegate?.processMailboxItem(item);
43400
43462
  const backstopMs = this.waitingForHumanApproval ? APPROVAL_WAIT_TIMEOUT_MS : MAILBOX_PROCESSING_TIMEOUT_MS;
43401
- const backstop = new Promise((resolve21) => setTimeout(() => resolve21(void 0), backstopMs));
43463
+ const backstop = new Promise((resolve20) => setTimeout(() => resolve20(void 0), backstopMs));
43402
43464
  const result = await Promise.race([
43403
43465
  processing?.then((r) => ({ done: true, reply: r })),
43404
43466
  backstop.then(() => ({ done: false, reply: void 0 }))
@@ -43425,6 +43487,27 @@ var init_attention = __esm({
43425
43487
  this.processingStartedAt = void 0;
43426
43488
  if (timedOut) {
43427
43489
  this.mailbox.requeue(item);
43490
+ } else if (reply === "[cancelled]" || this.lastYieldDecision === "cancel") {
43491
+ log14.info("Item cancelled \u2014 dropping permanently", {
43492
+ agentId: this.agentId,
43493
+ itemId: item.id,
43494
+ type: item.sourceType
43495
+ });
43496
+ this.mailbox.complete(item.id);
43497
+ } else if (reply === "[preempted]" || this.lastYieldDecision === "preempt") {
43498
+ log14.info("Item preempted (paused) \u2014 deferring for later resumption", {
43499
+ agentId: this.agentId,
43500
+ itemId: item.id,
43501
+ type: item.sourceType
43502
+ });
43503
+ this.mailbox.deferDequeued(item);
43504
+ } else if (!this.running) {
43505
+ log14.info("Agent stopped during processing \u2014 requeueing item", {
43506
+ agentId: this.agentId,
43507
+ itemId: item.id,
43508
+ type: item.sourceType
43509
+ });
43510
+ this.mailbox.requeue(item);
43428
43511
  } else {
43429
43512
  const abnormalReason = detectAbnormalCompletion(reply, item);
43430
43513
  const retries = item.retryCount ?? 0;
@@ -43483,8 +43566,8 @@ var init_attention = __esm({
43483
43566
  * The promise is single-use; call again to get a fresh one.
43484
43567
  */
43485
43568
  waitForPreemptionSignal() {
43486
- return new Promise((resolve21) => {
43487
- this.criticalInterruptResolve = resolve21;
43569
+ return new Promise((resolve20) => {
43570
+ this.criticalInterruptResolve = resolve20;
43488
43571
  });
43489
43572
  }
43490
43573
  /**
@@ -43500,7 +43583,8 @@ var init_attention = __esm({
43500
43583
  *
43501
43584
  * Returns the decision type so the caller knows what to do:
43502
43585
  * - 'continue' → keep working on current task
43503
- * - 'preempt' → caller should save state and return; controller handles the switch
43586
+ * - 'preempt' → caller should save state and return '[preempted]'; item is deferred for later resumption
43587
+ * - 'cancel' → caller should abort and return '[cancelled]'; item is permanently dropped
43504
43588
  * - 'merge' → item was absorbed into current work (caller may inject into session)
43505
43589
  */
43506
43590
  async checkYieldPoint() {
@@ -43533,8 +43617,13 @@ var init_attention = __esm({
43533
43617
  return { decision: "defer", item: newItem, reasoning };
43534
43618
  }
43535
43619
  if (decisionType === "preempt") {
43620
+ this.lastYieldDecision = "preempt";
43536
43621
  return { decision: "preempt", item: newItem, reasoning };
43537
43622
  }
43623
+ if (decisionType === "cancel") {
43624
+ this.lastYieldDecision = "cancel";
43625
+ return { decision: "cancel", item: newItem, reasoning };
43626
+ }
43538
43627
  return { decision: "continue", reasoning };
43539
43628
  }
43540
43629
  /**
@@ -43617,7 +43706,7 @@ var init_attention = __esm({
43617
43706
  try {
43618
43707
  const prompt = this.buildJudgePrompt(currentItem, newItem);
43619
43708
  const llmDecision = await this.llmJudge(prompt);
43620
- const validDecisions = ["continue", "preempt", "merge", "defer"];
43709
+ const validDecisions = ["continue", "preempt", "cancel", "merge", "defer"];
43621
43710
  if (validDecisions.includes(llmDecision))
43622
43711
  return llmDecision;
43623
43712
  } catch (err) {
@@ -43632,17 +43721,29 @@ var init_attention = __esm({
43632
43721
  const recentDecs = this.decisions.slice(-5).map((d) => `- ${d.decisionType}: ${d.reasoning}`).join("\n");
43633
43722
  const queuedItems = this.mailbox.getQueuedItems();
43634
43723
  const queueSection = queuedItems.length > 0 ? queuedItems.map((i) => ` - [${i.sourceType}] p${i.priority}: "${i.payload.summary}"`).join("\n") : " (empty)";
43724
+ const currentContent = currentItem.payload.content?.slice(0, 500) ?? "";
43725
+ const newContent = newItem.payload.content?.slice(0, 800) ?? "";
43635
43726
  return [
43636
- "You are deciding how to handle a new incoming item in your mailbox.",
43637
- "CRITICAL RULE: Human messages (human_chat) and human comments ALWAYS take priority over all other work.",
43727
+ "You are deciding whether to INTERRUPT your current work to handle a new incoming message.",
43728
+ "",
43729
+ "RULES (in priority order):",
43730
+ "1. Human messages (human_chat) ALWAYS preempt non-human work \u2014 users are top priority.",
43731
+ '2. If the new message explicitly CANCELS or REVOKES the current work (e.g. "cancel that task",',
43732
+ ` "don't publish this anymore", "delete the draft"), choose cancel \u2014 current work is permanently abandoned.`,
43733
+ '3. If the new message asks to PAUSE or DELAY current work (e.g. "hold off for now",',
43734
+ ' "wait before deploying", "pause that"), choose preempt \u2014 current work is saved and can be resumed later.',
43735
+ "4. If the new message is about the SAME task/topic as current work and adds info, consider merge.",
43736
+ "5. If the new message is lower priority and unrelated, continue current work.",
43638
43737
  "",
43639
43738
  `CURRENT FOCUS: [${currentItem.sourceType}] priority=${currentItem.priority}`,
43640
- ` "${currentItem.payload.summary}"`,
43739
+ ` Summary: "${currentItem.payload.summary}"`,
43641
43740
  ` Task: ${currentItem.payload.taskId ?? "none"}`,
43741
+ currentContent ? ` Content preview: ${currentContent}` : "",
43642
43742
  "",
43643
43743
  `NEW ITEM: [${newItem.sourceType}] priority=${newItem.priority}`,
43644
- ` "${newItem.payload.summary}"`,
43744
+ ` Summary: "${newItem.payload.summary}"`,
43645
43745
  ` Task: ${newItem.payload.taskId ?? "none"}`,
43746
+ newContent ? ` Content: ${newContent}` : "",
43646
43747
  "",
43647
43748
  `FULL QUEUE (${this.mailbox.depth} items):`,
43648
43749
  queueSection,
@@ -43650,7 +43751,14 @@ var init_attention = __esm({
43650
43751
  "RECENT DECISIONS:",
43651
43752
  recentDecs || "(none)",
43652
43753
  "",
43653
- "Choose exactly one: continue | preempt | merge | defer",
43754
+ "DECISIONS:",
43755
+ "- preempt: PAUSE current work (session saved for later resumption) and handle the new item",
43756
+ "- cancel: PERMANENTLY STOP current work (session dropped, will NOT be resumed) and handle the new item",
43757
+ "- merge: Inject new item into current session as additional context (no interruption)",
43758
+ "- defer: Put the new item aside for later (no interruption)",
43759
+ "- continue: Ignore the new item for now, keep working",
43760
+ "",
43761
+ "Choose exactly one: continue | preempt | cancel | merge | defer",
43654
43762
  "Reply with ONLY the decision word, nothing else."
43655
43763
  ].join("\n");
43656
43764
  }
@@ -43706,7 +43814,7 @@ var init_attention = __esm({
43706
43814
  raw = response.content;
43707
43815
  break;
43708
43816
  }
43709
- messages.push({ role: "assistant", content: response.content, toolCalls: response.toolCalls });
43817
+ messages.push({ role: "assistant", content: response.content, toolCalls: response.toolCalls, reasoningContent: response.reasoningContent });
43710
43818
  for (const tc of response.toolCalls) {
43711
43819
  const handler4 = this.triageToolHandlers.get(tc.name);
43712
43820
  let result;
@@ -44206,7 +44314,7 @@ var init_task_queue = __esm({
44206
44314
  */
44207
44315
  async waitForAll() {
44208
44316
  while (this.runningTasks.size > 0 || this.queue.length > 0) {
44209
- await new Promise((resolve21) => setTimeout(resolve21, 100));
44317
+ await new Promise((resolve20) => setTimeout(resolve20, 100));
44210
44318
  }
44211
44319
  }
44212
44320
  };
@@ -44333,12 +44441,12 @@ var init_task_executor = __esm({
44333
44441
  * 等待任务完成
44334
44442
  */
44335
44443
  async waitForTaskCompletion(taskId2) {
44336
- return new Promise((resolve21) => {
44444
+ return new Promise((resolve20) => {
44337
44445
  const checkInterval = setInterval(() => {
44338
44446
  const task = this.taskQueue.getTaskStatus(taskId2);
44339
44447
  if (task && task.status !== TaskStatus.PENDING && task.status !== TaskStatus.RUNNING) {
44340
44448
  clearInterval(checkInterval);
44341
- resolve21(task);
44449
+ resolve20(task);
44342
44450
  }
44343
44451
  }, 100);
44344
44452
  });
@@ -44842,24 +44950,24 @@ function extensionForMime(mime) {
44842
44950
  async function checkMarkitdown() {
44843
44951
  if (markitdownAvailable !== null)
44844
44952
  return markitdownAvailable;
44845
- return new Promise((resolve21) => {
44953
+ return new Promise((resolve20) => {
44846
44954
  execFile2("markitdown", ["--help"], { timeout: 5e3 }, (err) => {
44847
44955
  markitdownAvailable = !err;
44848
44956
  if (!markitdownAvailable) {
44849
44957
  log16.info('markitdown CLI not found; file-to-text conversion will be limited. Install with: pip install "markitdown[all]"');
44850
44958
  }
44851
- resolve21(markitdownAvailable);
44959
+ resolve20(markitdownAvailable);
44852
44960
  });
44853
44961
  });
44854
44962
  }
44855
44963
  async function convertWithMarkitdown(filePath) {
44856
- return new Promise((resolve21, reject) => {
44964
+ return new Promise((resolve20, reject) => {
44857
44965
  execFile2("markitdown", [filePath], { timeout: 3e4, maxBuffer: 10 * 1024 * 1024 }, (err, stdout, stderr) => {
44858
44966
  if (err) {
44859
44967
  reject(new Error(`markitdown failed: ${stderr || err.message}`));
44860
44968
  return;
44861
44969
  }
44862
- resolve21(stdout);
44970
+ resolve20(stdout);
44863
44971
  });
44864
44972
  });
44865
44973
  }
@@ -45272,6 +45380,7 @@ ${notification.stdoutTail}`);
45272
45380
  log17.info(`Agent started: ${this.config.name}`);
45273
45381
  }
45274
45382
  async stop() {
45383
+ this.cancelActiveStream();
45275
45384
  this.attentionController.stop();
45276
45385
  this.heartbeat.stop();
45277
45386
  this._bgCompletionUnsub?.();
@@ -45317,14 +45426,14 @@ ${notification.stdoutTail}`);
45317
45426
  toolEventCollector: options?.toolEventCollector
45318
45427
  }
45319
45428
  };
45320
- return new Promise((resolve21, reject) => {
45429
+ return new Promise((resolve20, reject) => {
45321
45430
  this.mailbox.enqueue(sourceType, payload, {
45322
45431
  priority: options?.priority,
45323
45432
  metadata: {
45324
45433
  senderId,
45325
45434
  senderName: senderInfo?.name,
45326
45435
  senderRole: senderInfo?.role,
45327
- responsePromise: { resolve: resolve21, reject }
45436
+ responsePromise: { resolve: resolve20, reject }
45328
45437
  }
45329
45438
  });
45330
45439
  });
@@ -45346,14 +45455,14 @@ ${notification.stdoutTail}`);
45346
45455
  cancelToken
45347
45456
  }
45348
45457
  };
45349
- return new Promise((resolve21, reject) => {
45458
+ return new Promise((resolve20, reject) => {
45350
45459
  this.mailbox.enqueue("human_chat", payload, {
45351
45460
  priority: 0,
45352
45461
  metadata: {
45353
45462
  senderId,
45354
45463
  senderName: senderInfo?.name,
45355
45464
  senderRole: senderInfo?.role,
45356
- responsePromise: { resolve: resolve21, reject }
45465
+ responsePromise: { resolve: resolve20, reject }
45357
45466
  }
45358
45467
  });
45359
45468
  });
@@ -45381,12 +45490,12 @@ ${notification.stdoutTail}`);
45381
45490
  executionRound
45382
45491
  }
45383
45492
  };
45384
- return new Promise((resolve21, reject) => {
45493
+ return new Promise((resolve20, reject) => {
45385
45494
  this.mailbox.enqueue("task_status_update", payload, {
45386
45495
  priority: 1,
45387
45496
  metadata: {
45388
45497
  taskId: taskId2,
45389
- responsePromise: { resolve: resolve21, reject }
45498
+ responsePromise: { resolve: resolve20, reject }
45390
45499
  }
45391
45500
  });
45392
45501
  });
@@ -45404,13 +45513,13 @@ ${notification.stdoutTail}`);
45404
45513
  onLog
45405
45514
  }
45406
45515
  };
45407
- return new Promise((resolve21, reject) => {
45516
+ return new Promise((resolve20, reject) => {
45408
45517
  this.mailbox.enqueue("session_reply", payload, {
45409
45518
  metadata: {
45410
45519
  senderId,
45411
45520
  senderName: senderInfo?.name,
45412
45521
  senderRole: senderInfo?.role,
45413
- responsePromise: { resolve: resolve21, reject }
45522
+ responsePromise: { resolve: resolve20, reject }
45414
45523
  }
45415
45524
  });
45416
45525
  });
@@ -45473,7 +45582,7 @@ ${notification.stdoutTail}`);
45473
45582
  }
45474
45583
  },
45475
45584
  evaluateInterrupt: async (currentItem, newItem) => {
45476
- return this.attentionController.heuristicDecision(currentItem, newItem);
45585
+ return this.attentionController.evaluateWithLLMFallback(currentItem, newItem);
45477
45586
  },
45478
45587
  getTriageContext: async () => {
45479
45588
  const messages = this.currentSessionId ? this.memory.getRecentMessages(this.currentSessionId, TRIAGE_CONTEXT_MESSAGES_MAX * 2).filter((m) => m.role === "user" || m.role === "assistant").slice(-TRIAGE_CONTEXT_MESSAGES_MAX).map((m) => ({ role: m.role, content: typeof m.content === "string" ? m.content.slice(0, TRIAGE_CONTEXT_MSG_CHARS) : String(m.content).slice(0, TRIAGE_CONTEXT_MSG_CHARS) })) : [];
@@ -45569,7 +45678,7 @@ ${notification.stdoutTail}`);
45569
45678
  case "requirement_update": {
45570
45679
  if (extra.actionRequired) {
45571
45680
  const reqId = item.payload.requirementId ?? "unknown";
45572
- const reply = await this.handleMessage(item.payload.content + markerSuffix, item.metadata?.senderId, senderInfo, buildHandleOpts({ sessionId: `comment_${reqId}_${ts}`, scenario: "comment_response" }));
45681
+ const reply = await this.handleMessage(item.payload.content + markerSuffix, item.metadata?.senderId, senderInfo, buildHandleOpts({ sessionId: `requirement_${reqId}_${ts}`, scenario: "requirement_action" }));
45573
45682
  resolveResponse(reply);
45574
45683
  return reply;
45575
45684
  }
@@ -45803,6 +45912,7 @@ ${notification.stdoutTail}`);
45803
45912
  if (this.state.status === "offline")
45804
45913
  return;
45805
45914
  this.pauseReason = reason;
45915
+ this.cancelActiveStream();
45806
45916
  this.heartbeat.stop();
45807
45917
  this.attentionController.stop();
45808
45918
  if (this.memoryConsolidationTimer) {
@@ -46633,10 +46743,11 @@ ${report}`);
46633
46743
  if (!this.state.currentActivity) {
46634
46744
  let actType = "chat";
46635
46745
  let actLabel;
46746
+ const peerName = senderInfo?.name || senderId || void 0;
46636
46747
  switch (scenario) {
46637
46748
  case "a2a":
46638
46749
  actType = "a2a";
46639
- actLabel = `Chat from ${senderInfo?.name ?? senderId}`;
46750
+ actLabel = peerName ? `Chat from ${peerName}` : "Agent Message";
46640
46751
  break;
46641
46752
  case "heartbeat":
46642
46753
  actType = "internal";
@@ -46644,14 +46755,14 @@ ${report}`);
46644
46755
  break;
46645
46756
  case "comment_response":
46646
46757
  actType = "chat";
46647
- actLabel = `Comment reply to ${senderInfo?.name ?? senderId ?? "user"}`;
46758
+ actLabel = peerName ? `Comment reply to ${peerName}` : "Comment Reply";
46648
46759
  break;
46649
46760
  case "review":
46650
46761
  actType = "internal";
46651
- actLabel = `Reviewing task from ${senderInfo?.name ?? senderId ?? "worker"}`;
46762
+ actLabel = peerName ? `Reviewing task from ${peerName}` : "Task Review";
46652
46763
  break;
46653
46764
  default:
46654
- actLabel = `Chat with ${senderInfo?.name ?? senderId ?? "user"}`;
46765
+ actLabel = peerName ? `Chat with ${peerName}` : "Human Chat";
46655
46766
  break;
46656
46767
  }
46657
46768
  chatActivityId = this.startActivity(actType, actLabel);
@@ -46792,7 +46903,7 @@ ${block}
46792
46903
  break;
46793
46904
  }
46794
46905
  if (response.finishReason === "max_tokens" && !response.toolCalls?.length) {
46795
- this.memory.appendMessage(sessionId, { role: "assistant", content: response.content });
46906
+ this.memory.appendMessage(sessionId, { role: "assistant", content: response.content, reasoningContent: response.reasoningContent });
46796
46907
  const contMsg = {
46797
46908
  role: "user",
46798
46909
  content: "[Continue from where you left off. Do not repeat what you already said.]"
@@ -46802,7 +46913,8 @@ ${block}
46802
46913
  this.memory.appendMessage(sessionId, {
46803
46914
  role: "assistant",
46804
46915
  content: response.content,
46805
- toolCalls: response.toolCalls
46916
+ toolCalls: response.toolCalls,
46917
+ reasoningContent: response.reasoningContent
46806
46918
  });
46807
46919
  const currentActId = this.state.currentActivity?.id;
46808
46920
  const toolResults = await Promise.all(response.toolCalls.map(async (tc) => {
@@ -46894,13 +47006,14 @@ ${block}
46894
47006
  }
46895
47007
  }
46896
47008
  const chatYield = await this.checkAttentionYieldPoint();
46897
- if (chatYield.decision === "preempt" && isPreemptable) {
46898
- log17.info("handleMessage preempted by higher-priority item", {
47009
+ if ((chatYield.decision === "preempt" || chatYield.decision === "cancel") && isPreemptable) {
47010
+ const marker = chatYield.decision === "cancel" ? "[cancelled]" : "[preempted]";
47011
+ log17.info(`handleMessage ${chatYield.decision} by higher-priority item`, {
46899
47012
  agentId: this.id,
46900
47013
  scenario,
46901
47014
  preemptedBy: chatYield.item?.sourceType
46902
47015
  });
46903
- return "[preempted]";
47016
+ return marker;
46904
47017
  }
46905
47018
  if (chatYield.decision === "merge" && chatYield.item) {
46906
47019
  const mergeMsg = `[LIVE UPDATE] ${chatYield.item.payload.summary}
@@ -46943,10 +47056,10 @@ ${chatYield.item.payload.content}`;
46943
47056
  const outputCheck = await this.guardrails.checkOutput(displayReply, { agentId: this.id });
46944
47057
  if (!outputCheck.passed) {
46945
47058
  const filtered = `[Response filtered: ${outputCheck.reason}]`;
46946
- this.memory.appendMessage(sessionId, { role: "assistant", content: filtered });
47059
+ this.memory.appendMessage(sessionId, { role: "assistant", content: filtered, reasoningContent: response.reasoningContent });
46947
47060
  return filtered;
46948
47061
  }
46949
- this.memory.appendMessage(sessionId, { role: "assistant", content: displayReply });
47062
+ this.memory.appendMessage(sessionId, { role: "assistant", content: displayReply, reasoningContent: response.reasoningContent });
46950
47063
  if (!isLightweight && displayReply.length > 50 && senderId) {
46951
47064
  this.memory.writeDailyLog(this.id, `[Chat with ${senderInfo?.name ?? senderId}] Q: ${userMessage.slice(0, 150)}... A: ${displayReply.slice(0, 300)}`);
46952
47065
  }
@@ -47081,6 +47194,7 @@ ${chatYield.item.payload.content}`;
47081
47194
  this.checkDailyTokenBudget();
47082
47195
  const llmStart = Date.now();
47083
47196
  let response = await this.withNetworkRetry(() => this.llmRouter.chatStream({ messages, tools: llmTools.length > 0 ? llmTools : void 0, metadata: this.getLLMMetadata(this.currentSessionId), compaction: useCompaction }, wrappedOnEvent, this.getEffectiveProvider(), abortController.signal), "Stream LLM call");
47197
+ streamMarkerDelta.flush();
47084
47198
  const tokensThisCall = response.usage.inputTokens + response.usage.outputTokens;
47085
47199
  this.updateTokensUsed(tokensThisCall);
47086
47200
  this.calibrateTokenCounter(response.usage.inputTokens);
@@ -47118,7 +47232,8 @@ ${chatYield.item.payload.content}`;
47118
47232
  if (response.finishReason === "max_tokens" && !response.toolCalls?.length) {
47119
47233
  this.memory.appendMessage(this.currentSessionId, {
47120
47234
  role: "assistant",
47121
- content: response.content
47235
+ content: response.content,
47236
+ reasoningContent: response.reasoningContent
47122
47237
  });
47123
47238
  this.memory.appendMessage(this.currentSessionId, {
47124
47239
  role: "user",
@@ -47128,7 +47243,8 @@ ${chatYield.item.payload.content}`;
47128
47243
  this.memory.appendMessage(this.currentSessionId, {
47129
47244
  role: "assistant",
47130
47245
  content: response.content,
47131
- toolCalls: response.toolCalls
47246
+ toolCalls: response.toolCalls,
47247
+ reasoningContent: response.reasoningContent
47132
47248
  });
47133
47249
  const subagentProgressCb = (event) => {
47134
47250
  onEvent({
@@ -47231,6 +47347,7 @@ ${chatYield.item.payload.content}`;
47231
47347
  }
47232
47348
  const llmStart2 = Date.now();
47233
47349
  response = await this.withNetworkRetry(() => this.llmRouter.chatStream({ messages: updatedMessages, tools: llmTools.length > 0 ? llmTools : void 0, metadata: this.getLLMMetadata(this.currentSessionId), compaction: useCompaction }, wrappedOnEvent, this.getEffectiveProvider(), abortController.signal), "Stream LLM continuation");
47350
+ streamMarkerDelta.flush();
47234
47351
  const tokens2 = response.usage.inputTokens + response.usage.outputTokens;
47235
47352
  this.updateTokensUsed(tokens2);
47236
47353
  this.calibrateTokenCounter(response.usage.inputTokens);
@@ -47249,10 +47366,10 @@ ${chatYield.item.payload.content}`;
47249
47366
  const outputCheck = await this.guardrails.checkOutput(displayReply, { agentId: this.id });
47250
47367
  if (!outputCheck.passed) {
47251
47368
  const filtered = `[Response filtered: ${outputCheck.reason}]`;
47252
- this.memory.appendMessage(this.currentSessionId, { role: "assistant", content: filtered });
47369
+ this.memory.appendMessage(this.currentSessionId, { role: "assistant", content: filtered, reasoningContent: response.reasoningContent });
47253
47370
  return filtered;
47254
47371
  }
47255
- this.memory.appendMessage(this.currentSessionId, { role: "assistant", content: displayReply });
47372
+ this.memory.appendMessage(this.currentSessionId, { role: "assistant", content: displayReply, reasoningContent: response.reasoningContent });
47256
47373
  if (streamChatActivityId && thinkingBuffer.trim()) {
47257
47374
  this.emitActivityLog(streamChatActivityId, "text", thinkingBuffer, { isThinking: true });
47258
47375
  }
@@ -47535,7 +47652,7 @@ ${chatYield.item.payload.content}`;
47535
47652
  }
47536
47653
  flushText();
47537
47654
  if (response.finishReason === "max_tokens" && !response.toolCalls?.length) {
47538
- this.memory.appendMessage(sessionId, { role: "assistant", content: response.content });
47655
+ this.memory.appendMessage(sessionId, { role: "assistant", content: response.content, reasoningContent: response.reasoningContent });
47539
47656
  this.memory.appendMessage(sessionId, {
47540
47657
  role: "user",
47541
47658
  content: "[Continue from where you left off. Do not repeat what you already said.]"
@@ -47544,7 +47661,8 @@ ${chatYield.item.payload.content}`;
47544
47661
  this.memory.appendMessage(sessionId, {
47545
47662
  role: "assistant",
47546
47663
  content: response.content,
47547
- toolCalls: response.toolCalls
47664
+ toolCalls: response.toolCalls,
47665
+ reasoningContent: response.reasoningContent
47548
47666
  });
47549
47667
  let interruptedDuringTools = false;
47550
47668
  for (const tc of response.toolCalls) {
@@ -47637,14 +47755,16 @@ ${chatYield.item.payload.content}`;
47637
47755
  }
47638
47756
  this.flushPendingInjections(sessionId);
47639
47757
  const yieldResult = await this.checkAttentionYieldPoint();
47640
- if (yieldResult.decision === "preempt") {
47641
- emit("status", "preempted", {
47758
+ if (yieldResult.decision === "preempt" || yieldResult.decision === "cancel") {
47759
+ const statusLabel = yieldResult.decision === "cancel" ? "cancelled" : "preempted";
47760
+ emit("status", statusLabel, {
47642
47761
  reason: yieldResult.reasoning,
47643
47762
  preemptedBy: yieldResult.item?.sourceType
47644
47763
  });
47645
- log17.info("Task preempted by higher-priority mailbox item", {
47764
+ log17.info(`Task ${statusLabel} by higher-priority mailbox item`, {
47646
47765
  taskId: taskId2,
47647
47766
  agentId: this.id,
47767
+ decision: yieldResult.decision,
47648
47768
  preemptedBy: yieldResult.item?.sourceType,
47649
47769
  reasoning: yieldResult.reasoning?.slice(0, 120)
47650
47770
  });
@@ -47729,7 +47849,7 @@ ${yieldResult.item.payload.content}`
47729
47849
  if (!didSubmitReview && !cancelToken?.cancelled) {
47730
47850
  log17.warn("Task execution ending without task_submit_review \u2014 injecting final reminder", { taskId: taskId2, agentId: this.id });
47731
47851
  flushText();
47732
- this.memory.appendMessage(sessionId, { role: "assistant", content: response.content });
47852
+ this.memory.appendMessage(sessionId, { role: "assistant", content: response.content, reasoningContent: response.reasoningContent });
47733
47853
  this.memory.appendMessage(sessionId, {
47734
47854
  role: "user",
47735
47855
  content: [
@@ -47774,7 +47894,8 @@ ${yieldResult.item.payload.content}`
47774
47894
  this.memory.appendMessage(sessionId, {
47775
47895
  role: "assistant",
47776
47896
  content: response.content,
47777
- toolCalls: response.toolCalls
47897
+ toolCalls: response.toolCalls,
47898
+ reasoningContent: response.reasoningContent
47778
47899
  });
47779
47900
  for (const tc of response.toolCalls) {
47780
47901
  if (cancelToken?.cancelled)
@@ -47806,12 +47927,12 @@ ${yieldResult.item.payload.content}`
47806
47927
  } else {
47807
47928
  flushText();
47808
47929
  const finalReply = stripCompletionMarker(sanitizeLLMReply(response.content));
47809
- this.memory.appendMessage(sessionId, { role: "assistant", content: finalReply });
47930
+ this.memory.appendMessage(sessionId, { role: "assistant", content: finalReply, reasoningContent: response.reasoningContent });
47810
47931
  }
47811
47932
  } else {
47812
47933
  flushText();
47813
47934
  const finalReply = stripCompletionMarker(sanitizeLLMReply(response.content));
47814
- this.memory.appendMessage(sessionId, { role: "assistant", content: finalReply });
47935
+ this.memory.appendMessage(sessionId, { role: "assistant", content: finalReply, reasoningContent: response.reasoningContent });
47815
47936
  }
47816
47937
  emit("status", "execution_finished", {});
47817
47938
  this.metricsCollector.recordTaskCompletion(taskId2, "completed", Date.now() - taskStartMs);
@@ -47950,7 +48071,7 @@ ${yieldResult.item.payload.content}`
47950
48071
  break;
47951
48072
  flushText();
47952
48073
  if (response.finishReason === "max_tokens" && !response.toolCalls?.length) {
47953
- this.memory.appendMessage(sessionId, { role: "assistant", content: response.content });
48074
+ this.memory.appendMessage(sessionId, { role: "assistant", content: response.content, reasoningContent: response.reasoningContent });
47954
48075
  this.memory.appendMessage(sessionId, {
47955
48076
  role: "user",
47956
48077
  content: "[Continue from where you left off. Do not repeat what you already said.]"
@@ -47959,7 +48080,8 @@ ${yieldResult.item.payload.content}`
47959
48080
  this.memory.appendMessage(sessionId, {
47960
48081
  role: "assistant",
47961
48082
  content: response.content,
47962
- toolCalls: response.toolCalls
48083
+ toolCalls: response.toolCalls,
48084
+ reasoningContent: response.reasoningContent
47963
48085
  });
47964
48086
  for (const tc of response.toolCalls) {
47965
48087
  if (this.attentionController.hasInterruptPending()) {
@@ -48013,7 +48135,7 @@ ${yieldResult.item.payload.content}`
48013
48135
  flushText();
48014
48136
  const rawReply = sanitizeLLMReply(response.content);
48015
48137
  const displayReply = stripCompletionMarker(rawReply);
48016
- this.memory.appendMessage(sessionId, { role: "assistant", content: displayReply });
48138
+ this.memory.appendMessage(sessionId, { role: "assistant", content: displayReply, reasoningContent: response.reasoningContent });
48017
48139
  return rawReply;
48018
48140
  } catch (error) {
48019
48141
  if (textBuffer.trim()) {
@@ -48533,7 +48655,7 @@ ${escalationReason}`;
48533
48655
  error: String(error).slice(0, 200),
48534
48656
  delay
48535
48657
  });
48536
- await new Promise((resolve21) => setTimeout(resolve21, delay));
48658
+ await new Promise((resolve20) => setTimeout(resolve20, delay));
48537
48659
  }
48538
48660
  }
48539
48661
  throw lastError;
@@ -48809,7 +48931,7 @@ ${todayLog.slice(0, HEARTBEAT_DAILY_LOG_CHARS)}
48809
48931
  agentId: this.id,
48810
48932
  error: String(error).slice(0, 200)
48811
48933
  });
48812
- await new Promise((resolve21) => setTimeout(resolve21, delay));
48934
+ await new Promise((resolve20) => setTimeout(resolve20, delay));
48813
48935
  }
48814
48936
  }
48815
48937
  }
@@ -49103,7 +49225,7 @@ ${promo.content}` : promo.content;
49103
49225
 
49104
49226
  // ../core/dist/role-loader.js
49105
49227
  import { readFileSync as readFileSync11, existsSync as existsSync16, readdirSync as readdirSync3 } from "node:fs";
49106
- import { join as join11, resolve as resolve9 } from "node:path";
49228
+ import { join as join11, resolve as resolve8 } from "node:path";
49107
49229
  var RoleLoader;
49108
49230
  var init_role_loader = __esm({
49109
49231
  "../core/dist/role-loader.js"() {
@@ -49112,7 +49234,7 @@ var init_role_loader = __esm({
49112
49234
  RoleLoader = class {
49113
49235
  templateDirs;
49114
49236
  constructor(templateDirs) {
49115
- this.templateDirs = templateDirs ?? [resolve9(process.cwd(), "templates", "roles")];
49237
+ this.templateDirs = templateDirs ?? [resolve8(process.cwd(), "templates", "roles")];
49116
49238
  }
49117
49239
  getTemplateDirs() {
49118
49240
  return this.templateDirs;
@@ -49452,7 +49574,7 @@ var init_mcp_client = __esm({
49452
49574
  }
49453
49575
  }
49454
49576
  sendRequest(proc, method, params) {
49455
- return new Promise((resolve21, reject) => {
49577
+ return new Promise((resolve20, reject) => {
49456
49578
  const id = ++this.requestId;
49457
49579
  const message = JSON.stringify({ jsonrpc: "2.0", id, method, params }) + "\n";
49458
49580
  const timeoutMs = method === "tools/call" ? 12e4 : 3e4;
@@ -49460,7 +49582,7 @@ var init_mcp_client = __esm({
49460
49582
  this.pendingRequests.delete(id);
49461
49583
  reject(new Error(`MCP request timeout for ${method} (id=${id}, ${timeoutMs}ms)`));
49462
49584
  }, timeoutMs);
49463
- this.pendingRequests.set(id, { resolve: resolve21, reject, method, timer, proc });
49585
+ this.pendingRequests.set(id, { resolve: resolve20, reject, method, timer, proc });
49464
49586
  proc.stdin?.write(message);
49465
49587
  });
49466
49588
  }
@@ -50359,6 +50481,67 @@ function createA2ATools(ctx) {
50359
50481
  return JSON.stringify({ status: "error", error: String(err) });
50360
50482
  }
50361
50483
  }
50484
+ }] : [],
50485
+ ...ctx.getChannelMessages ? [{
50486
+ name: "recall_context",
50487
+ description: [
50488
+ "Recall historical context you may need to respond effectively.",
50489
+ "Use this BEFORE responding when the provided context is insufficient.",
50490
+ "",
50491
+ "Supported scopes:",
50492
+ '\u2022 "channel" \u2014 Read chat messages from a group chat or DM channel. Requires channel_key.',
50493
+ " Use when you joined a discussion late, were @mentioned, or need to understand prior conversation.",
50494
+ "",
50495
+ "For other context types, use these existing tools instead:",
50496
+ "\u2022 task_get \u2014 Full task details including all comments (scope=task context)",
50497
+ "\u2022 requirement_get \u2014 Full requirement details including all comments (scope=requirement context)",
50498
+ "\u2022 recall_activity \u2014 Your own execution history, activity logs, and past work (scope=your activities)",
50499
+ "\u2022 memory_search \u2014 Your saved memories and notes (scope=personal knowledge)"
50500
+ ].join("\n"),
50501
+ inputSchema: {
50502
+ type: "object",
50503
+ properties: {
50504
+ scope: {
50505
+ type: "string",
50506
+ enum: ["channel"],
50507
+ description: 'Type of context to recall. Currently: "channel" for chat/group messages.'
50508
+ },
50509
+ channel_key: {
50510
+ type: "string",
50511
+ description: 'Required when scope="channel". The channel key (e.g., "group:<teamId>" for team chats, "dm:<id1>_<id2>" for DMs).'
50512
+ },
50513
+ limit: { type: "number", description: "Number of items to fetch (default 30, max 50)." },
50514
+ before: { type: "string", description: "ISO timestamp \u2014 fetch items older than this for pagination. Omit for most recent." }
50515
+ },
50516
+ required: ["scope"]
50517
+ },
50518
+ async execute(args) {
50519
+ const scope = args["scope"];
50520
+ if (scope === "channel") {
50521
+ const channelKey = args["channel_key"];
50522
+ if (!channelKey) {
50523
+ return JSON.stringify({ status: "error", error: 'channel_key is required when scope="channel"' });
50524
+ }
50525
+ const limit = Math.min(args["limit"] ?? 30, 50);
50526
+ const before2 = args["before"];
50527
+ try {
50528
+ const result = await ctx.getChannelMessages(channelKey, limit, before2);
50529
+ const formatted = result.messages.map((m) => `[${m.createdAt}] ${m.senderType === "agent" ? `[agent] ${m.senderName}` : `[human] ${m.senderName}`}: ${m.text.slice(0, 500)}`);
50530
+ return JSON.stringify({
50531
+ messages: formatted,
50532
+ count: result.messages.length,
50533
+ hasMore: result.hasMore,
50534
+ oldestTimestamp: result.messages[0]?.createdAt
50535
+ });
50536
+ } catch (err) {
50537
+ return JSON.stringify({ status: "error", error: String(err) });
50538
+ }
50539
+ }
50540
+ return JSON.stringify({
50541
+ status: "error",
50542
+ error: `Unknown scope: "${scope}". Use "channel" for chat messages, or use task_get / requirement_get / recall_activity for other context.`
50543
+ });
50544
+ }
50362
50545
  }] : []
50363
50546
  ];
50364
50547
  }
@@ -52220,11 +52403,11 @@ function createSettingsTools(ctx) {
52220
52403
  },
52221
52404
  base_url: {
52222
52405
  type: "string",
52223
- description: 'Optional base URL for the API (e.g. "https://api.deepseek.com/v1")'
52406
+ description: 'Optional base URL for the API (e.g. "https://api.deepseek.com")'
52224
52407
  },
52225
52408
  model: {
52226
52409
  type: "string",
52227
- description: 'Default model ID (e.g. "deepseek-chat")'
52410
+ description: 'Default model ID (e.g. "deepseek-v4-flash")'
52228
52411
  }
52229
52412
  },
52230
52413
  required: ["name", "model"]
@@ -52349,7 +52532,7 @@ function createSettingsTools(ctx) {
52349
52532
  },
52350
52533
  id: {
52351
52534
  type: "string",
52352
- description: 'Model ID (e.g. "deepseek-chat-v3")'
52535
+ description: 'Model ID (e.g. "deepseek-v4-flash")'
52353
52536
  },
52354
52537
  name: {
52355
52538
  type: "string",
@@ -52875,7 +53058,7 @@ var init_dist3 = __esm({
52875
53058
 
52876
53059
  // ../core/dist/agent-manager.js
52877
53060
  import { join as join13 } from "node:path";
52878
- import { mkdirSync as mkdirSync12, readFileSync as readFileSync13, existsSync as existsSync18, copyFileSync, rmSync, readdirSync as readdirSync4 } from "node:fs";
53061
+ import { mkdirSync as mkdirSync12, readFileSync as readFileSync13, existsSync as existsSync18, copyFileSync, rmSync, readdirSync as readdirSync4, writeFileSync as writeFileSync11 } from "node:fs";
52879
53062
  import { homedir as homedir6 } from "node:os";
52880
53063
  function resolveCurrentTaskId(agentObj, ts, agentId2) {
52881
53064
  const activeTasks = agentObj?.getActiveTasks?.() ?? [];
@@ -53271,6 +53454,17 @@ You are ${request.name}.`,
53271
53454
  }
53272
53455
  }
53273
53456
  }
53457
+ const heartbeatPath = join13(agentRoleDir, "HEARTBEAT.md");
53458
+ if (!existsSync18(heartbeatPath)) {
53459
+ writeFileSync11(heartbeatPath, [
53460
+ "# Heartbeat Checklist",
53461
+ "",
53462
+ "- [ ] Check mailbox for new messages and respond to urgent items",
53463
+ "- [ ] Review assigned tasks \u2014 update progress, unblock if possible",
53464
+ "- [ ] Check team announcements for new information",
53465
+ "- [ ] Scan recent channel messages for anything requiring attention"
53466
+ ].join("\n"), "utf-8");
53467
+ }
53274
53468
  const config = {
53275
53469
  id,
53276
53470
  name: request.name,
@@ -53436,7 +53630,8 @@ You are ${request.name}.`,
53436
53630
  ...this.groupChatHandlers ? {
53437
53631
  sendGroupMessage: this.groupChatHandlers.sendGroupMessage,
53438
53632
  createGroupChat: (name, memberIds) => this.groupChatHandlers.createGroupChat(name, id, config.name, memberIds),
53439
- listGroupChats: this.groupChatHandlers.listGroupChats
53633
+ listGroupChats: this.groupChatHandlers.listGroupChats,
53634
+ getChannelMessages: this.groupChatHandlers.getChannelMessages
53440
53635
  } : {}
53441
53636
  };
53442
53637
  for (const tool of createA2ATools(a2aContext))
@@ -54009,7 +54204,8 @@ You are ${row.name}.`,
54009
54204
  ...this.groupChatHandlers ? {
54010
54205
  sendGroupMessage: this.groupChatHandlers.sendGroupMessage,
54011
54206
  createGroupChat: (name, memberIds) => this.groupChatHandlers.createGroupChat(name, id, config.name, memberIds),
54012
- listGroupChats: this.groupChatHandlers.listGroupChats
54207
+ listGroupChats: this.groupChatHandlers.listGroupChats,
54208
+ getChannelMessages: this.groupChatHandlers.getChannelMessages
54013
54209
  } : {}
54014
54210
  };
54015
54211
  for (const tool of createA2ATools(a2aCtx))
@@ -55299,7 +55495,7 @@ var init_openai = __esm({
55299
55495
  };
55300
55496
  }
55301
55497
  if (m.toolCalls?.length) {
55302
- return {
55498
+ const msg = {
55303
55499
  role: "assistant",
55304
55500
  content: getTextContent(m.content) || null,
55305
55501
  tool_calls: m.toolCalls.map((tc) => ({
@@ -55308,6 +55504,17 @@ var init_openai = __esm({
55308
55504
  function: { name: tc.name, arguments: JSON.stringify(tc.arguments) }
55309
55505
  }))
55310
55506
  };
55507
+ if (m.reasoningContent)
55508
+ msg.reasoning_content = m.reasoningContent;
55509
+ return msg;
55510
+ }
55511
+ if (m.role === "assistant" && m.reasoningContent) {
55512
+ const msg = {
55513
+ role: "assistant",
55514
+ content: typeof m.content === "string" ? m.content : getTextContent(m.content),
55515
+ reasoning_content: m.reasoningContent
55516
+ };
55517
+ return msg;
55311
55518
  }
55312
55519
  if (Array.isArray(m.content)) {
55313
55520
  return {
@@ -55319,14 +55526,22 @@ var init_openai = __esm({
55319
55526
  });
55320
55527
  }
55321
55528
  convertTools(tools) {
55322
- return tools.map((t) => ({
55323
- type: "function",
55324
- function: {
55325
- name: t.name,
55326
- description: t.description,
55327
- parameters: t.inputSchema
55328
- }
55329
- }));
55529
+ const seen = /* @__PURE__ */ new Set();
55530
+ const unique = [];
55531
+ for (const t of tools) {
55532
+ if (seen.has(t.name))
55533
+ continue;
55534
+ seen.add(t.name);
55535
+ unique.push({
55536
+ type: "function",
55537
+ function: {
55538
+ name: t.name,
55539
+ description: t.description,
55540
+ parameters: t.inputSchema
55541
+ }
55542
+ });
55543
+ }
55544
+ return unique;
55330
55545
  }
55331
55546
  async chatStream(request, onEvent, signal) {
55332
55547
  const messages = this.convertMessages(request.messages);
@@ -55373,6 +55588,7 @@ var init_openai = __esm({
55373
55588
  throw new Error(`OpenAI API error ${res.status}: ${errText}`);
55374
55589
  }
55375
55590
  let content = "";
55591
+ let reasoningContent = "";
55376
55592
  const toolCalls = /* @__PURE__ */ new Map();
55377
55593
  let finishReason = "end_turn";
55378
55594
  let promptTokens = 0;
@@ -55398,9 +55614,10 @@ var init_openai = __esm({
55398
55614
  try {
55399
55615
  const chunk = JSON.parse(trimmed.slice(6));
55400
55616
  const choice = chunk.choices?.[0];
55401
- const reasoningContent = choice?.delta?.reasoning_details ?? choice?.delta?.thinking;
55402
- if (reasoningContent) {
55403
- onEvent({ type: "thinking_delta", thinking: reasoningContent });
55617
+ const deltaReasoning = choice?.delta?.reasoning_content ?? choice?.delta?.reasoning_details ?? choice?.delta?.thinking;
55618
+ if (deltaReasoning) {
55619
+ reasoningContent += deltaReasoning;
55620
+ onEvent({ type: "thinking_delta", thinking: deltaReasoning });
55404
55621
  }
55405
55622
  if (choice?.delta?.content) {
55406
55623
  content += choice.delta.content;
@@ -55451,12 +55668,15 @@ var init_openai = __esm({
55451
55668
  });
55452
55669
  const usage = { inputTokens: promptTokens, outputTokens: completionTokens };
55453
55670
  onEvent({ type: "message_end", usage, finishReason });
55454
- return {
55671
+ const streamResult = {
55455
55672
  content,
55456
55673
  toolCalls: resultToolCalls.length ? resultToolCalls : void 0,
55457
55674
  usage,
55458
55675
  finishReason
55459
55676
  };
55677
+ if (reasoningContent)
55678
+ streamResult.reasoningContent = reasoningContent;
55679
+ return streamResult;
55460
55680
  }
55461
55681
  convertResponse(data) {
55462
55682
  const choice = data.choices[0];
@@ -55473,7 +55693,7 @@ var init_openai = __esm({
55473
55693
  tool_calls: "tool_use",
55474
55694
  length: "max_tokens"
55475
55695
  };
55476
- return {
55696
+ const result = {
55477
55697
  content: typeof msg.content === "string" ? msg.content : "",
55478
55698
  toolCalls: toolCalls?.length ? toolCalls : void 0,
55479
55699
  usage: {
@@ -55482,6 +55702,9 @@ var init_openai = __esm({
55482
55702
  },
55483
55703
  finishReason: finishMap[choice.finish_reason] ?? "end_turn"
55484
55704
  };
55705
+ if (msg.reasoning_content)
55706
+ result.reasoningContent = msg.reasoning_content;
55707
+ return result;
55485
55708
  }
55486
55709
  };
55487
55710
  }
@@ -55946,7 +56169,7 @@ var init_ollama = __esm({
55946
56169
  });
55947
56170
 
55948
56171
  // ../core/dist/llm/auth-profiles.js
55949
- import { readFileSync as readFileSync14, writeFileSync as writeFileSync11, mkdirSync as mkdirSync13, existsSync as existsSync19, unlinkSync as unlinkSync2 } from "node:fs";
56172
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync12, mkdirSync as mkdirSync13, existsSync as existsSync19, unlinkSync as unlinkSync2 } from "node:fs";
55950
56173
  import { join as join14 } from "node:path";
55951
56174
  import { homedir as homedir7 } from "node:os";
55952
56175
  var log34, AuthProfileStore;
@@ -55977,7 +56200,7 @@ var init_auth_profiles = __esm({
55977
56200
  }
55978
56201
  }
55979
56202
  write(data) {
55980
- writeFileSync11(this.filePath, JSON.stringify(data, null, 2) + "\n", { mode: 384 });
56203
+ writeFileSync12(this.filePath, JSON.stringify(data, null, 2) + "\n", { mode: 384 });
55981
56204
  }
55982
56205
  /**
55983
56206
  * Acquire a simple filesystem lock for atomic profile updates (token refresh).
@@ -55986,7 +56209,7 @@ var init_auth_profiles = __esm({
55986
56209
  acquireLock() {
55987
56210
  const lockContent = `${process.pid}-${Date.now()}`;
55988
56211
  try {
55989
- writeFileSync11(this.lockPath, lockContent, { flag: "wx" });
56212
+ writeFileSync12(this.lockPath, lockContent, { flag: "wx" });
55990
56213
  return () => {
55991
56214
  try {
55992
56215
  unlinkSync2(this.lockPath);
@@ -55998,7 +56221,7 @@ var init_auth_profiles = __esm({
55998
56221
  const existing = readFileSync14(this.lockPath, "utf-8");
55999
56222
  const ts = Number(existing.split("-").pop());
56000
56223
  if (Date.now() - ts > 3e4) {
56001
- writeFileSync11(this.lockPath, lockContent, { mode: 384 });
56224
+ writeFileSync12(this.lockPath, lockContent, { mode: 384 });
56002
56225
  return () => {
56003
56226
  try {
56004
56227
  unlinkSync2(this.lockPath);
@@ -56215,7 +56438,7 @@ var init_oauth_manager = __esm({
56215
56438
  const state = randomBytes2(16).toString("hex");
56216
56439
  const port = config.callbackPort ?? 1455;
56217
56440
  const redirectUri = `http://localhost:${port}/auth/callback`;
56218
- const promise = new Promise((resolve21, reject) => {
56441
+ const promise = new Promise((resolve20, reject) => {
56219
56442
  const pending = {
56220
56443
  provider,
56221
56444
  state,
@@ -56224,7 +56447,7 @@ var init_oauth_manager = __esm({
56224
56447
  redirectUri,
56225
56448
  resolve: (tokens) => {
56226
56449
  const profile = this.profileStore.createOAuthProfile(provider, tokens, `${provider} (${tokens.accountId ?? "OAuth"})`);
56227
- resolve21(profile);
56450
+ resolve20(profile);
56228
56451
  },
56229
56452
  reject
56230
56453
  };
@@ -56580,7 +56803,7 @@ var init_router = __esm({
56580
56803
  */
56581
56804
  static isNonRetryableError(error) {
56582
56805
  const msg = error instanceof Error ? error.message : String(error);
56583
- return /\b(402|403|401)\b/.test(msg) || /insufficient balance/i.test(msg) || /not available in your region/i.test(msg) || /invalid.*api.*key/i.test(msg) || /authentication/i.test(msg);
56806
+ return /\b(401|402|403)\b/.test(msg) || /insufficient balance/i.test(msg) || /not available in your region/i.test(msg) || /invalid.*api.*key/i.test(msg) || /authentication/i.test(msg) || /\b400\b.*invalid_request_error/i.test(msg) || /reasoning_content.*must be passed back/i.test(msg);
56584
56807
  }
56585
56808
  /** Detect rate-limit (429) errors which should use a shorter circuit breaker cooldown. */
56586
56809
  static isRateLimitError(error) {
@@ -56793,14 +57016,25 @@ var init_router = __esm({
56793
57016
  return "simple";
56794
57017
  }
56795
57018
  selectProvider(request, explicit) {
56796
- if (explicit)
57019
+ if (explicit && this.isAvailable(explicit))
56797
57020
  return explicit;
57021
+ if (explicit && this.disabledProviders.has(explicit)) {
57022
+ log36.warn(`Explicit provider ${explicit} is disabled \u2014 falling through to auto-select`);
57023
+ }
56798
57024
  if (!this.autoSelect || this.providerTiers.length === 0) {
56799
57025
  if (this.isAvailable(this.defaultProvider) && this.providers.has(this.defaultProvider)) {
56800
57026
  return this.defaultProvider;
56801
57027
  }
56802
57028
  const healthy2 = [...this.providers.keys()].find((n) => this.isAvailable(n));
56803
- return healthy2 ?? this.defaultProvider;
57029
+ if (healthy2)
57030
+ return healthy2;
57031
+ const enabledAny2 = [...this.providers.keys()].find((n) => !this.disabledProviders.has(n));
57032
+ if (enabledAny2) {
57033
+ log36.warn(`All providers degraded \u2014 using enabled provider ${enabledAny2} as last resort`);
57034
+ return enabledAny2;
57035
+ }
57036
+ log36.warn("All providers disabled or degraded \u2014 using default as last resort");
57037
+ return this.defaultProvider;
56804
57038
  }
56805
57039
  const complexity = _LLMRouter.assessComplexity(request);
56806
57040
  const match = this.providerTiers.find((t) => t.complexity.includes(complexity) && this.providers.has(t.name) && this.isAvailable(t.name));
@@ -56813,7 +57047,12 @@ var init_router = __esm({
56813
57047
  log36.warn(`All tiered providers degraded for complexity=${complexity}, falling back to: ${healthy}`);
56814
57048
  return healthy;
56815
57049
  }
56816
- log36.warn("All providers degraded \u2014 using default as last resort");
57050
+ const enabledAny = [...this.providers.keys()].find((n) => !this.disabledProviders.has(n));
57051
+ if (enabledAny) {
57052
+ log36.warn(`All providers degraded \u2014 using enabled provider ${enabledAny} as last resort`);
57053
+ return enabledAny;
57054
+ }
57055
+ log36.warn("All providers disabled or degraded \u2014 using default as last resort");
56817
57056
  return this.defaultProvider;
56818
57057
  }
56819
57058
  getFallbacks(primary) {
@@ -57102,7 +57341,7 @@ var init_router = __esm({
57102
57341
  for (const [name, p] of this.providers.entries()) {
57103
57342
  providers[name] = { model: p.model, configured: true };
57104
57343
  }
57105
- for (const name of ["anthropic", "openai", "openai-codex", "google", "ollama", "minimax", "siliconflow", "openrouter", "zai"]) {
57344
+ for (const name of ["anthropic", "openai", "openai-codex", "google", "ollama", "minimax", "siliconflow", "openrouter", "zai", "deepseek"]) {
57106
57345
  if (!providers[name]) {
57107
57346
  providers[name] = { model: "", configured: false };
57108
57347
  }
@@ -57134,7 +57373,7 @@ var init_router = __esm({
57134
57373
  oauthAccountId: oauthProfile?.oauth?.accountId
57135
57374
  };
57136
57375
  }
57137
- for (const name of ["anthropic", "openai", "openai-codex", "google", "ollama", "minimax", "siliconflow", "openrouter", "zai"]) {
57376
+ for (const name of ["anthropic", "openai", "openai-codex", "google", "ollama", "minimax", "siliconflow", "openrouter", "zai", "deepseek"]) {
57138
57377
  if (!providers[name]) {
57139
57378
  const oauthProfile = this._profileStore?.getDefaultProfile(name);
57140
57379
  const builtinModels = BUILTIN_MODEL_CATALOG.filter((m) => m.provider === name);
@@ -57189,6 +57428,13 @@ var init_router = __esm({
57189
57428
  this.disabledProviders.delete(providerName);
57190
57429
  } else {
57191
57430
  this.disabledProviders.add(providerName);
57431
+ if (this.defaultProvider === providerName) {
57432
+ const replacement = [...this.providers.keys()].find((n) => !this.disabledProviders.has(n));
57433
+ if (replacement) {
57434
+ log36.info(`Default provider ${providerName} disabled \u2014 switching default to ${replacement}`);
57435
+ this.defaultProvider = replacement;
57436
+ }
57437
+ }
57192
57438
  }
57193
57439
  log36.info(`Provider ${providerName} ${enabled ? "enabled" : "disabled"}`);
57194
57440
  }
@@ -57301,7 +57547,8 @@ var init_router = __esm({
57301
57547
  siliconflow: "SiliconFlow",
57302
57548
  minimax: "MiniMax",
57303
57549
  openrouter: "OpenRouter",
57304
- zai: "ZAI"
57550
+ zai: "ZAI",
57551
+ deepseek: "DeepSeek"
57305
57552
  };
57306
57553
  BUILTIN_MODEL_CATALOG = [
57307
57554
  // Anthropic — https://docs.anthropic.com/claude/reference/input-and-output-sizes
@@ -57325,7 +57572,12 @@ var init_router = __esm({
57325
57572
  { id: "xiaomi/mimo-v2-pro", name: "MiMo-V2-Pro", provider: "openrouter", contextWindow: 1048576, maxOutputTokens: 131072, cost: { input: 1, output: 3, cacheRead: 0.2 }, reasoning: true, inputTypes: ["text"] },
57326
57573
  { id: "anthropic/claude-opus-4-6", name: "Claude Opus 4.6 (via OpenRouter)", provider: "openrouter", contextWindow: 1e6, maxOutputTokens: 128e3, cost: { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 }, reasoning: true, inputTypes: ["text", "image"] },
57327
57574
  { id: "openai/gpt-5.4", name: "GPT-5.4 (via OpenRouter)", provider: "openrouter", contextWindow: 11e5, maxOutputTokens: 128e3, cost: { input: 2.5, output: 15, cacheRead: 0.25 }, reasoning: true, inputTypes: ["text", "image"] },
57328
- { id: "google/gemini-3-1-pro", name: "Gemini 3.1 Pro (via OpenRouter)", provider: "openrouter", contextWindow: 1e6, maxOutputTokens: 65536, cost: { input: 2, output: 12 }, reasoning: true, inputTypes: ["text", "image"] }
57575
+ { id: "google/gemini-3-1-pro", name: "Gemini 3.1 Pro (via OpenRouter)", provider: "openrouter", contextWindow: 1e6, maxOutputTokens: 65536, cost: { input: 2, output: 12 }, reasoning: true, inputTypes: ["text", "image"] },
57576
+ // DeepSeek — https://api-docs.deepseek.com/
57577
+ { id: "deepseek-v4-flash", name: "DeepSeek-V4-Flash", provider: "deepseek", contextWindow: 1e6, maxOutputTokens: 384e3, cost: { input: 0.14, output: 0.28, cacheRead: 0.028 }, reasoning: true, inputTypes: ["text"] },
57578
+ { id: "deepseek-v4-pro", name: "DeepSeek-V4-Pro", provider: "deepseek", contextWindow: 1e6, maxOutputTokens: 384e3, cost: { input: 1.67, output: 3.33, cacheRead: 0.14 }, reasoning: true, inputTypes: ["text"] },
57579
+ { id: "deepseek-chat", name: "DeepSeek-Chat (legacy)", provider: "deepseek", contextWindow: 65536, maxOutputTokens: 8192, cost: { input: 0.14, output: 0.28, cacheRead: 0.028 }, reasoning: false, inputTypes: ["text"] },
57580
+ { id: "deepseek-reasoner", name: "DeepSeek-Reasoner (legacy)", provider: "deepseek", contextWindow: 65536, maxOutputTokens: 8192, cost: { input: 0.55, output: 2.19, cacheRead: 0.14 }, reasoning: true, inputTypes: ["text"] }
57329
57581
  ];
57330
57582
  }
57331
57583
  });
@@ -58186,7 +58438,7 @@ var init_registry = __esm({
58186
58438
 
58187
58439
  // ../core/dist/skills/loader.js
58188
58440
  import { readFileSync as readFileSync15, readdirSync as readdirSync5, existsSync as existsSync20 } from "node:fs";
58189
- import { join as join16, resolve as resolve10 } from "node:path";
58441
+ import { join as join16, resolve as resolve9 } from "node:path";
58190
58442
  function resolveMcpServerPaths(servers, skillDir) {
58191
58443
  if (!servers)
58192
58444
  return void 0;
@@ -59152,7 +59404,7 @@ var init_composition = __esm({
59152
59404
 
59153
59405
  // ../core/dist/workflow/team-template.js
59154
59406
  import { readdirSync as readdirSync7, readFileSync as readFileSync17, existsSync as existsSync22 } from "node:fs";
59155
- import { join as join18, resolve as resolve11, dirname as dirname5 } from "node:path";
59407
+ import { join as join18, resolve as resolve10, dirname as dirname5 } from "node:path";
59156
59408
  import { fileURLToPath as fileURLToPath3 } from "node:url";
59157
59409
  function loadTeamTemplateFromDir(dirPath) {
59158
59410
  const fsHelper = { existsSync: existsSync22, readFileSync: (p, _enc) => readFileSync17(p, "utf-8"), join: join18 };
@@ -59193,16 +59445,16 @@ function createDefaultTeamTemplates() {
59193
59445
  const thisFile = fileURLToPath3(import.meta.url);
59194
59446
  const thisDir = dirname5(thisFile);
59195
59447
  const candidates = [
59196
- resolve11(thisDir, "..", "templates", "teams"),
59448
+ resolve10(thisDir, "..", "templates", "teams"),
59197
59449
  // npm global: dist/ → ../templates/teams
59198
- resolve11(thisDir, "..", "..", "..", "..", "templates", "teams"),
59450
+ resolve10(thisDir, "..", "..", "..", "..", "templates", "teams"),
59199
59451
  // monorepo: packages/core/dist/workflow/ → root
59200
- resolve11(process.cwd(), "templates", "teams")
59452
+ resolve10(process.cwd(), "templates", "teams")
59201
59453
  // cwd fallback
59202
59454
  ];
59203
59455
  templatesDir = candidates.find((d) => existsSync22(d)) ?? candidates[candidates.length - 1];
59204
59456
  } catch {
59205
- templatesDir = resolve11(process.cwd(), "templates", "teams");
59457
+ templatesDir = resolve10(process.cwd(), "templates", "teams");
59206
59458
  }
59207
59459
  if (!existsSync22(templatesDir)) {
59208
59460
  log47.warn(`Team templates directory not found: ${templatesDir}`);
@@ -59348,7 +59600,7 @@ var init_dist4 = __esm({
59348
59600
  });
59349
59601
 
59350
59602
  // ../org-manager/dist/org-service.js
59351
- import { mkdirSync as mkdirSync15, writeFileSync as writeFileSync12, existsSync as existsSync23, rmSync as rmSync2 } from "node:fs";
59603
+ import { mkdirSync as mkdirSync15, writeFileSync as writeFileSync13, existsSync as existsSync23, rmSync as rmSync2 } from "node:fs";
59352
59604
  import { join as join19 } from "node:path";
59353
59605
  import { homedir as homedir10 } from "node:os";
59354
59606
  var log50, OrganizationService;
@@ -59482,15 +59734,15 @@ var init_org_service = __esm({
59482
59734
  mkdirSync15(dir, { recursive: true });
59483
59735
  const annPath = join19(dir, "ANNOUNCEMENT.md");
59484
59736
  if (announcements) {
59485
- writeFileSync12(annPath, announcements, "utf-8");
59737
+ writeFileSync13(annPath, announcements, "utf-8");
59486
59738
  } else if (!existsSync23(annPath)) {
59487
- writeFileSync12(annPath, "", "utf-8");
59739
+ writeFileSync13(annPath, "", "utf-8");
59488
59740
  }
59489
59741
  const normsPath = join19(dir, "NORMS.md");
59490
59742
  if (norms) {
59491
- writeFileSync12(normsPath, norms, "utf-8");
59743
+ writeFileSync13(normsPath, norms, "utf-8");
59492
59744
  } else if (!existsSync23(normsPath)) {
59493
- writeFileSync12(normsPath, "", "utf-8");
59745
+ writeFileSync13(normsPath, "", "utf-8");
59494
59746
  }
59495
59747
  }
59496
59748
  async createTeam(orgId2, name, description) {
@@ -60226,8 +60478,8 @@ var init_org_service = __esm({
60226
60478
  });
60227
60479
 
60228
60480
  // ../org-manager/dist/task-service.js
60229
- import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync13, readFileSync as readFileSync18, existsSync as existsSync24, cpSync } from "node:fs";
60230
- import { join as join20, resolve as resolve12 } from "node:path";
60481
+ import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync14, readFileSync as readFileSync18, existsSync as existsSync24, cpSync } from "node:fs";
60482
+ import { join as join20, resolve as resolve11 } from "node:path";
60231
60483
  import { homedir as homedir11 } from "node:os";
60232
60484
  function formatLocalTimestamp(d = /* @__PURE__ */ new Date()) {
60233
60485
  const pad = (n) => String(n).padStart(2, "0");
@@ -60579,7 +60831,7 @@ var init_task_service = __esm({
60579
60831
  mkdirSync16(join20(dir, "knowledge"), { recursive: true });
60580
60832
  const userMdPath = join20(dir, "USER.md");
60581
60833
  if (!existsSync24(userMdPath)) {
60582
- writeFileSync13(userMdPath, [
60834
+ writeFileSync14(userMdPath, [
60583
60835
  "# About the Owner",
60584
60836
  "",
60585
60837
  "- Name:",
@@ -60628,6 +60880,10 @@ var init_task_service = __esm({
60628
60880
  const lower = errorContent.toLowerCase();
60629
60881
  return lower.includes("econnreset") || lower.includes("econnrefused") || lower.includes("etimedout") || lower.includes("fetch failed") || lower.includes("aborterror") || lower.includes("aborted") || lower.includes("socket hang up") || lower.includes("network");
60630
60882
  }
60883
+ static isNonRetryableError(errorContent) {
60884
+ const lower = errorContent.toLowerCase();
60885
+ return lower.includes("invalid_request_error") && /\b400\b/.test(errorContent) || lower.includes("reasoning_content") || lower.includes("invalid api key") || lower.includes("insufficient balance") || /\b(401|402|403)\b/.test(errorContent) && !lower.includes("rate");
60886
+ }
60631
60887
  shouldRetryTask(taskId2, errorContent, retryAttempt, cancelled) {
60632
60888
  if (cancelled)
60633
60889
  return { shouldRetry: false, reason: "cancelled" };
@@ -60638,6 +60894,9 @@ var init_task_service = __esm({
60638
60894
  if (retryAttempt >= _TaskService.MAX_IN_PROGRESS_RETRIES) {
60639
60895
  return { shouldRetry: false, reason: `exceeded max retries (${_TaskService.MAX_IN_PROGRESS_RETRIES})` };
60640
60896
  }
60897
+ if (_TaskService.isNonRetryableError(errorContent)) {
60898
+ return { shouldRetry: false, reason: "non-retryable error (auth/billing/request)" };
60899
+ }
60641
60900
  const normalizedError = errorContent.replace(/\d{4}-\d{2}-\d{2}T[\d:.]+Z?/g, "").trim().slice(0, 200);
60642
60901
  const tracker = this.taskRetryErrors.get(taskId2);
60643
60902
  if (tracker && tracker.lastError === normalizedError) {
@@ -61607,7 +61866,7 @@ ${c.content}`;
61607
61866
  });
61608
61867
  if (this.hitlService && request.createdBy && request.createdBy !== "default") {
61609
61868
  this.hitlService.notify({
61610
- targetUserId: "default",
61869
+ targetUserId: "all",
61611
61870
  type: "task_created",
61612
61871
  title: `Task created: ${task.title}`,
61613
61872
  body: `Agent created task "${task.title}"`,
@@ -61874,7 +62133,7 @@ Action: ${guidance}` : ""
61874
62133
  const notifType = to === "completed" ? "task_completed" : to === "review" ? "task_review" : "task_failed";
61875
62134
  const priority = to === "failed" ? "high" : "normal";
61876
62135
  this.hitlService.notify({
61877
- targetUserId: "default",
62136
+ targetUserId: "all",
61878
62137
  type: notifType,
61879
62138
  title: to === "review" ? `Task ready for review: ${task.title}` : to === "completed" ? `Task completed: ${task.title}` : `Task failed: ${task.title}`,
61880
62139
  body: `Task "${task.title}" status changed to ${to}`,
@@ -62645,10 +62904,11 @@ Action: ${guidance}` : ""
62645
62904
  testResults: d.testResults
62646
62905
  }))
62647
62906
  };
62648
- writeFileSync13(join20(taskSharedDir, "manifest.json"), JSON.stringify(manifest, null, 2));
62907
+ writeFileSync14(join20(taskSharedDir, "manifest.json"), JSON.stringify(manifest, null, 2));
62649
62908
  for (const d of deliverables) {
62909
+ let src;
62650
62910
  if (d.type === "file" && d.reference) {
62651
- const src = resolve12(d.reference);
62911
+ src = resolve11(d.reference);
62652
62912
  if (existsSync24(src)) {
62653
62913
  try {
62654
62914
  const destName = src.split("/").pop() ?? "deliverable";
@@ -62658,9 +62918,10 @@ Action: ${guidance}` : ""
62658
62918
  }
62659
62919
  }
62660
62920
  }
62661
- if (d.type === "file" && d.summary && !existsSync24(d.reference)) {
62662
- const safeName = d.reference.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
62663
- writeFileSync13(join20(taskSharedDir, `${safeName}.md`), d.summary);
62921
+ if (d.type === "file" && d.summary && src && !existsSync24(src)) {
62922
+ const baseName = d.reference.split("/").pop() ?? "deliverable";
62923
+ const safeName = baseName.replace(/[^a-zA-Z0-9_\u4e00-\u9fff.-]/g, "_").slice(0, 80);
62924
+ writeFileSync14(join20(taskSharedDir, `${safeName}.md`), d.summary);
62664
62925
  }
62665
62926
  }
62666
62927
  log51.info("Deliverables published to shared workspace", { taskId: task.id, dir: taskSharedDir });
@@ -63486,7 +63747,7 @@ ${task.description}`;
63486
63747
 
63487
63748
  // ../org-manager/dist/builder-service.js
63488
63749
  import { join as join21 } from "node:path";
63489
- import { readdirSync as readdirSync8, readFileSync as readFileSync19, existsSync as existsSync25, writeFileSync as writeFileSync14, mkdirSync as mkdirSync17, copyFileSync as copyFileSync2, statSync as statSync4, cpSync as cpSync2 } from "node:fs";
63750
+ import { readdirSync as readdirSync8, readFileSync as readFileSync19, existsSync as existsSync25, writeFileSync as writeFileSync15, mkdirSync as mkdirSync17, copyFileSync as copyFileSync2, statSync as statSync4, cpSync as cpSync2 } from "node:fs";
63490
63751
  import { homedir as homedir12 } from "node:os";
63491
63752
  var log52, FS_HELPER, BuilderService;
63492
63753
  var init_builder_service = __esm({
@@ -63592,7 +63853,7 @@ var init_builder_service = __esm({
63592
63853
  copyFileSync2(srcFile, join21(agentRoleDir, fname));
63593
63854
  }
63594
63855
  }
63595
- writeFileSync14(join21(agentRoleDir, ".role-origin.json"), JSON.stringify({ customRole: true, source: "builder-artifact", artifact: artifactName, artifactType: "agent" }));
63856
+ writeFileSync15(join21(agentRoleDir, ".role-origin.json"), JSON.stringify({ customRole: true, source: "builder-artifact", artifact: artifactName, artifactType: "agent" }));
63596
63857
  agent.reloadRole();
63597
63858
  await agentManager.startAgent(agent.id);
63598
63859
  return {
@@ -63654,7 +63915,7 @@ var init_builder_service = __esm({
63654
63915
  }
63655
63916
  }
63656
63917
  }
63657
- writeFileSync14(join21(agentRoleDir, ".role-origin.json"), JSON.stringify({ customRole: true, source: "builder-artifact", artifact: artifactName, artifactType: "team" }));
63918
+ writeFileSync15(join21(agentRoleDir, ".role-origin.json"), JSON.stringify({ customRole: true, source: "builder-artifact", artifact: artifactName, artifactType: "team" }));
63658
63919
  agent.reloadRole();
63659
63920
  if (memberRole === "manager") {
63660
63921
  await this.orgService.updateTeam(team.id, { managerId: agent.id, managerType: "agent" });
@@ -64369,23 +64630,6 @@ var init_sse_handler = __esm({
64369
64630
  this.sseBuffer.sendProgress(this.processedTokens, this.totalTokens, `\u5DE5\u5177\u6267\u884C\u5B8C\u6210: ${event.tool}`);
64370
64631
  }
64371
64632
  } else if (event.type === "message_end") {
64372
- const now2 = (/* @__PURE__ */ new Date()).toISOString();
64373
- let turnThinking;
64374
- if (this.thinkingBuf) {
64375
- this.sseBuffer.send({ type: "thinking_commit", thinking: this.thinkingBuf, createdAt: now2 });
64376
- turnThinking = this.thinkingBuf;
64377
- this.thinkingBuf = "";
64378
- }
64379
- if (this.textBuf) {
64380
- this.sseBuffer.send({ type: "text_commit", text: this.textBuf, createdAt: now2 });
64381
- const seg2 = { type: "text", content: this.textBuf, createdAt: now2 };
64382
- if (turnThinking)
64383
- seg2.thinking = turnThinking;
64384
- this.msgSegments.push(seg2);
64385
- this.textBuf = "";
64386
- } else if (turnThinking) {
64387
- this.msgSegments.push({ type: "text", content: "", thinking: turnThinking, createdAt: now2 });
64388
- }
64389
64633
  if (event.usage?.outputTokens) {
64390
64634
  this.totalTokens = Math.max(this.totalTokens, event.usage.outputTokens);
64391
64635
  this.processedTokens = event.usage.outputTokens;
@@ -64458,8 +64702,8 @@ var init_sse_handler = __esm({
64458
64702
  });
64459
64703
 
64460
64704
  // ../org-manager/dist/skill-service.js
64461
- import { join as join22, resolve as resolve13 } from "node:path";
64462
- import { existsSync as existsSync26, writeFileSync as writeFileSync15, mkdirSync as mkdirSync18, readFileSync as readFileSync20, readdirSync as readdirSync9, copyFileSync as copyFileSync3 } from "node:fs";
64705
+ import { join as join22, resolve as resolve12 } from "node:path";
64706
+ import { existsSync as existsSync26, writeFileSync as writeFileSync16, mkdirSync as mkdirSync18, readFileSync as readFileSync20, readdirSync as readdirSync9, copyFileSync as copyFileSync3 } from "node:fs";
64463
64707
  import { homedir as homedir13 } from "node:os";
64464
64708
  import { execSync as execSync2 } from "node:child_process";
64465
64709
  async function searchSkillHub(query2) {
@@ -64593,7 +64837,7 @@ async function installSkill(request, skillRegistry) {
64593
64837
  let installed = false;
64594
64838
  let installMethod = "metadata-only";
64595
64839
  if (source === "builtin") {
64596
- const builtinDir = resolve13(process.cwd(), "templates", "skills", safeName);
64840
+ const builtinDir = resolve12(process.cwd(), "templates", "skills", safeName);
64597
64841
  if (existsSync26(builtinDir)) {
64598
64842
  mkdirSync18(targetDir, { recursive: true });
64599
64843
  for (const file of readdirSync9(builtinDir)) {
@@ -64610,7 +64854,7 @@ async function installSkill(request, skillRegistry) {
64610
64854
  if (zipResp.ok) {
64611
64855
  const tmpZip = join22(skillsDir, `_tmp_${safeName}.zip`);
64612
64856
  const buffer = Buffer.from(await zipResp.arrayBuffer());
64613
- writeFileSync15(tmpZip, buffer);
64857
+ writeFileSync16(tmpZip, buffer);
64614
64858
  mkdirSync18(targetDir, { recursive: true });
64615
64859
  try {
64616
64860
  execSync2(`unzip -o "${tmpZip}" -d "${targetDir}"`, { timeout: 3e4 });
@@ -64636,7 +64880,7 @@ async function installSkill(request, skillRegistry) {
64636
64880
  const mdResp = await fetch(skillMdUrl, { signal: AbortSignal.timeout(15e3) });
64637
64881
  if (mdResp.ok) {
64638
64882
  mkdirSync18(targetDir, { recursive: true });
64639
- writeFileSync15(join22(targetDir, "SKILL.md"), await mdResp.text(), "utf-8");
64883
+ writeFileSync16(join22(targetDir, "SKILL.md"), await mdResp.text(), "utf-8");
64640
64884
  installed = true;
64641
64885
  installMethod = "github-skillmd";
64642
64886
  }
@@ -64650,7 +64894,7 @@ async function installSkill(request, skillRegistry) {
64650
64894
  const mdResp = await fetch(rootMd, { signal: AbortSignal.timeout(15e3) });
64651
64895
  if (mdResp.ok) {
64652
64896
  mkdirSync18(targetDir, { recursive: true });
64653
- writeFileSync15(join22(targetDir, "SKILL.md"), await mdResp.text(), "utf-8");
64897
+ writeFileSync16(join22(targetDir, "SKILL.md"), await mdResp.text(), "utf-8");
64654
64898
  installed = true;
64655
64899
  installMethod = "github-root-skillmd";
64656
64900
  }
@@ -64666,7 +64910,7 @@ async function installSkill(request, skillRegistry) {
64666
64910
  if (zipResp.ok) {
64667
64911
  const tmpZip = join22(skillsDir, `_tmp_${safeName}.zip`);
64668
64912
  const buffer = Buffer.from(await zipResp.arrayBuffer());
64669
- writeFileSync15(tmpZip, buffer);
64913
+ writeFileSync16(tmpZip, buffer);
64670
64914
  mkdirSync18(targetDir, { recursive: true });
64671
64915
  try {
64672
64916
  execSync2(`unzip -o "${tmpZip}" -d "${targetDir}"`, { timeout: 3e4 });
@@ -64698,13 +64942,13 @@ async function installSkill(request, skillRegistry) {
64698
64942
  };
64699
64943
  const manifest = buildManifest("skill", raw);
64700
64944
  manifest.source = skillSource;
64701
- writeFileSync15(skillMfPath, JSON.stringify(manifest, null, 2), "utf-8");
64945
+ writeFileSync16(skillMfPath, JSON.stringify(manifest, null, 2), "utf-8");
64702
64946
  } else {
64703
64947
  try {
64704
64948
  const mf = JSON.parse(readFileSync20(skillMfPath, "utf-8"));
64705
64949
  if (!mf.source) {
64706
64950
  mf.source = skillSource;
64707
- writeFileSync15(skillMfPath, JSON.stringify(mf, null, 2), "utf-8");
64951
+ writeFileSync16(skillMfPath, JSON.stringify(mf, null, 2), "utf-8");
64708
64952
  }
64709
64953
  } catch {
64710
64954
  }
@@ -64737,8 +64981,8 @@ var init_skill_service = __esm({
64737
64981
 
64738
64982
  // ../org-manager/dist/api-server.js
64739
64983
  import { createServer as createServer2 } from "node:http";
64740
- import { join as join23, resolve as resolve14, dirname as dirname6 } from "node:path";
64741
- import { readdirSync as readdirSync10, readFileSync as readFileSync21, existsSync as existsSync27, writeFileSync as writeFileSync16, mkdirSync as mkdirSync19, rmSync as rmSync3, statSync as statSync5 } from "node:fs";
64984
+ import { join as join23, resolve as resolve13, dirname as dirname6 } from "node:path";
64985
+ import { readdirSync as readdirSync10, readFileSync as readFileSync21, existsSync as existsSync27, writeFileSync as writeFileSync17, mkdirSync as mkdirSync19, rmSync as rmSync3, statSync as statSync5 } from "node:fs";
64742
64986
  import { homedir as homedir14 } from "node:os";
64743
64987
  import { execSync as execSync3 } from "node:child_process";
64744
64988
  async function signToken(payload, secret) {
@@ -64828,7 +65072,7 @@ var init_api_server = __esm({
64828
65072
  init_skill_service();
64829
65073
  log57 = createLogger("api-server");
64830
65074
  PBKDF2_ITERATIONS = 1e4;
64831
- APIServer = class {
65075
+ APIServer = class _APIServer {
64832
65076
  orgService;
64833
65077
  taskService;
64834
65078
  port;
@@ -64853,7 +65097,7 @@ var init_api_server = __esm({
64853
65097
  builderService;
64854
65098
  workflowEngine;
64855
65099
  teamTemplateRegistry;
64856
- customGroupChats = [];
65100
+ // Custom group chats are now persisted in SQLite via storage.groupChatRepo
64857
65101
  constructor(orgService, taskService, port = 8056) {
64858
65102
  this.orgService = orgService;
64859
65103
  this.taskService = taskService;
@@ -64868,15 +65112,18 @@ var init_api_server = __esm({
64868
65112
  am.setGroupChatHandlers({
64869
65113
  sendGroupMessage: async (channelKey, message, senderId, senderName) => {
64870
65114
  const cleanText = stripInternalBlocks(message);
65115
+ const orgId2 = "default";
65116
+ let persistedMsgId;
64871
65117
  if (this.storage) {
64872
- await this.storage.channelMessageRepo.append({
64873
- orgId: "default",
65118
+ const saved = await this.storage.channelMessageRepo.append({
65119
+ orgId: orgId2,
64874
65120
  channel: channelKey,
64875
65121
  senderId,
64876
65122
  senderType: "agent",
64877
65123
  senderName,
64878
65124
  text: cleanText
64879
65125
  });
65126
+ persistedMsgId = saved.id;
64880
65127
  }
64881
65128
  this.ws.broadcast({
64882
65129
  type: "chat:message",
@@ -64889,41 +65136,81 @@ var init_api_server = __esm({
64889
65136
  },
64890
65137
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
64891
65138
  });
64892
- const chat = this.customGroupChats.find((c) => c.id === channelKey);
64893
- if (chat) {
64894
- for (const memberId of chat.memberIds) {
64895
- if (memberId === senderId)
64896
- continue;
64897
- try {
64898
- const memberAgent = am.getAgent(memberId);
64899
- memberAgent.enqueueToMailbox("a2a_message", {
64900
- summary: `Group chat message from ${senderName}`,
64901
- content: `[Group chat: ${chat.name}] ${senderName}: ${cleanText}`,
64902
- extra: { senderId, senderName, channelKey }
64903
- });
64904
- } catch {
65139
+ let allAgentIds = [];
65140
+ if (channelKey.startsWith("group:custom:") && this.storage?.groupChatRepo) {
65141
+ allAgentIds = this.storage.groupChatRepo.getAgentMemberIds(channelKey);
65142
+ } else if (channelKey.startsWith("group:")) {
65143
+ const teamId = channelKey.replace(/^group:/, "");
65144
+ const team = this.orgService.getTeam(teamId);
65145
+ allAgentIds = team?.memberAgentIds ?? [];
65146
+ }
65147
+ const peerAgentIds = allAgentIds.filter((id) => id !== senderId);
65148
+ if (peerAgentIds.length > 0) {
65149
+ const agentManager = this.orgService.getAgentManager();
65150
+ const nameMap = this.buildAgentNameMap(allAgentIds, agentManager);
65151
+ const mentionedNames = this.parseAgentMentions(cleanText, [...nameMap.keys()]);
65152
+ const filteredMentions = mentionedNames.filter((n) => {
65153
+ const id = nameMap.get(n);
65154
+ return id && id !== senderId;
65155
+ });
65156
+ if (filteredMentions.length > 0) {
65157
+ let channelContext = [];
65158
+ if (this.storage) {
65159
+ try {
65160
+ const recent = await this.storage.channelMessageRepo.getMessages(channelKey, 20);
65161
+ channelContext = (recent.messages ?? []).map((m) => ({
65162
+ role: m.senderType === "agent" ? "assistant" : "user",
65163
+ content: m.senderType === "agent" ? stripInternalBlocks(m.text) : `[${m.senderName}]: ${m.text}`
65164
+ }));
65165
+ } catch {
65166
+ }
65167
+ }
65168
+ const roundId = `round_${Date.now()}`;
65169
+ const respondedAgents = /* @__PURE__ */ new Set([senderId]);
65170
+ void this.triggerAgentToAgentChain(filteredMentions, nameMap, senderName, senderId, cleanText, persistedMsgId, channelKey, orgId2, agentManager, allAgentIds.length, { roundId, depth: 1, respondedAgents, allAgentIds });
65171
+ } else {
65172
+ for (const peerId of peerAgentIds) {
65173
+ try {
65174
+ agentManager.getAgent(peerId).enqueueToMailbox("a2a_message", {
65175
+ summary: `Group chat message from ${senderName}`,
65176
+ content: `[Group chat message from ${senderName}]:
65177
+ ${cleanText}`,
65178
+ extra: { senderId, senderName, channelKey }
65179
+ }, {
65180
+ metadata: { senderId, senderName, senderRole: "agent" }
65181
+ });
65182
+ } catch {
65183
+ }
64905
65184
  }
64906
65185
  }
64907
65186
  }
64908
65187
  return "Message sent to group chat";
64909
65188
  },
64910
65189
  createGroupChat: async (name, creatorId, creatorName, memberIds) => {
64911
- const chatId = `group:custom:${Date.now().toString(36)}`;
64912
- this.customGroupChats.push({
64913
- id: chatId,
64914
- name,
64915
- orgId: "default",
64916
- creatorId,
64917
- creatorName,
64918
- memberIds,
64919
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
64920
- });
64921
- this.ws.broadcast({
64922
- type: "chat:group_created",
64923
- payload: { chatId, name, creatorId, creatorName },
64924
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
64925
- });
64926
- return { id: chatId, name };
65190
+ if (this.storage?.groupChatRepo) {
65191
+ const members = memberIds.map((id) => {
65192
+ try {
65193
+ const agentInfo = this.orgService.getAgentManager().getAgent(id);
65194
+ return { id, type: "agent", name: agentInfo?.config?.name ?? id };
65195
+ } catch {
65196
+ return { id, type: "agent", name: id };
65197
+ }
65198
+ });
65199
+ const gc = this.storage.groupChatRepo.create({
65200
+ orgId: "default",
65201
+ name,
65202
+ creatorId,
65203
+ creatorName,
65204
+ members
65205
+ });
65206
+ this.ws.broadcast({
65207
+ type: "chat:group_created",
65208
+ payload: { chatId: gc.channelKey, name, creatorId, creatorName },
65209
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
65210
+ });
65211
+ return { id: gc.channelKey, name };
65212
+ }
65213
+ return { id: `group:custom:${Date.now().toString(36)}`, name };
64927
65214
  },
64928
65215
  listGroupChats: async () => {
64929
65216
  const teams = this.orgService.listTeamsWithMembers("default");
@@ -64933,13 +65220,27 @@ var init_api_server = __esm({
64933
65220
  type: "team",
64934
65221
  channelKey: `group:${t.id}`
64935
65222
  }));
64936
- const customChats = this.customGroupChats.map((c) => ({
64937
- id: c.id,
65223
+ const customChats = this.storage?.groupChatRepo ? this.storage.groupChatRepo.list("default").map((c) => ({
65224
+ id: c.channelKey,
64938
65225
  name: c.name,
64939
65226
  type: "custom",
64940
- channelKey: c.id
64941
- }));
65227
+ channelKey: c.channelKey
65228
+ })) : [];
64942
65229
  return [...teamChats, ...customChats];
65230
+ },
65231
+ getChannelMessages: async (channelKey, limit, before2) => {
65232
+ if (!this.storage)
65233
+ return { messages: [], hasMore: false };
65234
+ const result = this.storage.channelMessageRepo.getMessages(channelKey, limit, before2);
65235
+ return {
65236
+ messages: result.messages.map((m) => ({
65237
+ senderName: m.senderName,
65238
+ senderType: m.senderType,
65239
+ text: stripInternalBlocks(m.text),
65240
+ createdAt: m.createdAt instanceof Date ? m.createdAt.toISOString() : String(m.createdAt)
65241
+ })),
65242
+ hasMore: result.hasMore
65243
+ };
64943
65244
  }
64944
65245
  });
64945
65246
  }
@@ -65005,10 +65306,10 @@ var init_api_server = __esm({
65005
65306
  for (const [fname, content] of Object.entries(data.files)) {
65006
65307
  const filePath = join23(artDir, fname);
65007
65308
  mkdirSync19(dirname6(filePath), { recursive: true });
65008
- writeFileSync16(filePath, content, "utf-8");
65309
+ writeFileSync17(filePath, content, "utf-8");
65009
65310
  }
65010
65311
  } else if (data.config) {
65011
- writeFileSync16(join23(artDir, manifestFilename(mode)), JSON.stringify(data.config, null, 2), "utf-8");
65312
+ writeFileSync17(join23(artDir, manifestFilename(mode)), JSON.stringify(data.config, null, 2), "utf-8");
65012
65313
  }
65013
65314
  return self.builderService.installArtifact(mode, slug);
65014
65315
  }
@@ -65239,48 +65540,167 @@ var init_api_server = __esm({
65239
65540
  log57.warn("Failed to persist chat turn", { error: String(err) });
65240
65541
  }
65241
65542
  }
65543
+ // ── Agent-to-agent reply storm prevention ───────────────────────────────────
65544
+ static A2A_MAX_DEPTH = 3;
65545
+ static A2A_COOLDOWN_MS = 3e4;
65546
+ /** Per-channel cooldown tracker: channel → agentId → last reply timestamp */
65547
+ a2aCooldowns = /* @__PURE__ */ new Map();
65548
+ /** Extract @mentions from agent reply text. Returns agent names found.
65549
+ * Handles multi-word names (e.g. "Ryan 莱恩") by checking known names after each @ sign.
65550
+ * Also matches partial names: "@Sofia" matches "Sofia 索菲亚" if "Sofia" is a token in the name. */
65551
+ parseAgentMentions(text, knownAgentNames) {
65552
+ const mentioned = /* @__PURE__ */ new Set();
65553
+ const sorted = [...knownAgentNames].sort((a, b) => b.length - a.length);
65554
+ const tokenToName = /* @__PURE__ */ new Map();
65555
+ for (const name of sorted) {
65556
+ for (const token of name.split(/\s+/)) {
65557
+ if (token.length >= 2) {
65558
+ const key2 = token.toLowerCase();
65559
+ if (!tokenToName.has(key2))
65560
+ tokenToName.set(key2, name);
65561
+ }
65562
+ }
65563
+ }
65564
+ let idx = 0;
65565
+ while (idx < text.length) {
65566
+ const atPos = text.indexOf("@", idx);
65567
+ if (atPos < 0)
65568
+ break;
65569
+ if (text[atPos + 1] === "[") {
65570
+ const close = text.indexOf("]", atPos + 2);
65571
+ if (close > atPos + 2) {
65572
+ const bracketed = text.slice(atPos + 2, close);
65573
+ const match = sorted.find((n) => n.toLowerCase() === bracketed.toLowerCase());
65574
+ if (match)
65575
+ mentioned.add(match);
65576
+ idx = close + 1;
65577
+ continue;
65578
+ }
65579
+ }
65580
+ const after2 = text.slice(atPos + 1);
65581
+ const afterLower = after2.toLowerCase();
65582
+ const fullMatch = sorted.find((n) => afterLower.startsWith(n.toLowerCase()));
65583
+ if (fullMatch) {
65584
+ mentioned.add(fullMatch);
65585
+ idx = atPos + 1 + fullMatch.length;
65586
+ continue;
65587
+ }
65588
+ const tokenMatch = after2.match(/^(\S+)/);
65589
+ if (tokenMatch) {
65590
+ const token = tokenMatch[1].toLowerCase().replace(/[,,。!?!?;;::、()()[\]【】]+$/, "");
65591
+ const resolved = tokenToName.get(token);
65592
+ if (resolved) {
65593
+ mentioned.add(resolved);
65594
+ idx = atPos + 1 + tokenMatch[1].length;
65595
+ continue;
65596
+ }
65597
+ }
65598
+ idx = atPos + 1;
65599
+ }
65600
+ return [...mentioned];
65601
+ }
65602
+ /** Build name→id lookup for agents in a channel */
65603
+ buildAgentNameMap(agentIds, agentManager) {
65604
+ const map = /* @__PURE__ */ new Map();
65605
+ for (const id of agentIds) {
65606
+ try {
65607
+ map.set(agentManager.getAgent(id).config.name, id);
65608
+ } catch {
65609
+ }
65610
+ }
65611
+ return map;
65612
+ }
65242
65613
  /**
65243
65614
  * Process a single agent's reply in a group chat broadcast.
65244
65615
  * Each agent decides independently whether to respond.
65245
65616
  * Replies are persisted and broadcast via WebSocket.
65617
+ * If the agent @mentions other agents, a chain reply is triggered (with storm prevention).
65246
65618
  */
65247
- async processGroupChatReply(agentId2, userMessage, senderId, senderInfo, channel, orgId2, channelContext, agentManager, teamSize) {
65619
+ async processGroupChatReply(agentId2, userMessage, senderId, senderInfo, channel, orgId2, channelContext, agentManager, teamSize, opts, chainCtx) {
65248
65620
  try {
65249
65621
  const agent = agentManager.getAgent(agentId2);
65250
65622
  const agentName = agent.config.name;
65251
- const groupChatPrefix = [
65252
- `[GROUP CHAT \u2014 ${teamSize} team members receiving this independently]`,
65253
- "",
65254
- "You are ONE of several agents in a group chat. Rules:",
65255
- "1. **Check channel history first** \u2014 if another agent already answered the question or addressed the topic, do NOT repeat their answer. Add only if you have a *different* perspective or expertise.",
65256
- '2. **Only respond if your role/expertise is specifically relevant.** Generic offers like "Let me know if you need help" add no value.',
65257
- "3. **Be concise** \u2014 this is a multi-party conversation. Short, actionable responses only.",
65258
- "4. **Do NOT create tasks** unless the user explicitly asks you to and you are the manager/coordinator.",
65259
- "5. **Prefer [NO_RESPONSE]** \u2014 when in doubt, say nothing. It is better to stay silent than to add noise.",
65260
- "",
65261
- "If you have nothing uniquely valuable to contribute, respond with exactly: [NO_RESPONSE]",
65262
- "---",
65623
+ const isA2A = !!chainCtx && chainCtx.depth > 0;
65624
+ const hasReplyTarget = !!opts?.replyToAgentName;
65625
+ const hasMentions = !!opts?.mentionedNames?.length;
65626
+ const isTargeted = hasReplyTarget || hasMentions;
65627
+ const targetNames = /* @__PURE__ */ new Set();
65628
+ if (opts?.replyToAgentName)
65629
+ targetNames.add(opts.replyToAgentName);
65630
+ if (opts?.mentionedNames)
65631
+ opts.mentionedNames.forEach((n) => targetNames.add(n));
65632
+ const thisAgentIsTarget = targetNames.has(agentName);
65633
+ const prefixLines = [
65634
+ `[GROUP CHAT \u2014 ${teamSize} team members | You are: ${agentName}]`,
65263
65635
  ""
65264
- ].join("\n");
65636
+ ];
65637
+ if (isA2A) {
65638
+ prefixLines.push(`[AGENT COLLABORATION] This message is from a fellow agent (${opts?.replyToAgentName ?? "teammate"}), not from the user.`);
65639
+ prefixLines.push("You were @mentioned because your expertise is needed. Respond concisely to the specific request.");
65640
+ prefixLines.push("You may @mention another agent if (and ONLY if) you genuinely need their specific expertise to answer.");
65641
+ prefixLines.push("Do NOT @mention agents just to be polite or to pass the conversation along.");
65642
+ prefixLines.push("");
65643
+ }
65644
+ if (isTargeted && !isA2A) {
65645
+ if (hasReplyTarget) {
65646
+ prefixLines.push(`[REPLY] The user is replying to ${opts.replyToAgentName}'s message: "${(opts.replyToText ?? "").slice(0, 200)}"`);
65647
+ }
65648
+ if (hasMentions) {
65649
+ prefixLines.push(`[MENTIONED] The user @mentioned: ${opts.mentionedNames.join(", ")}`);
65650
+ }
65651
+ prefixLines.push("");
65652
+ if (thisAgentIsTarget) {
65653
+ prefixLines.push(">>> You are the target of this message. You SHOULD respond. <<<");
65654
+ } else {
65655
+ prefixLines.push(">>> STOP. This message is NOT for you. The user is talking to " + [...targetNames].join(", ") + ", not you.");
65656
+ prefixLines.push("You MUST respond with exactly: [NO_RESPONSE]");
65657
+ prefixLines.push('The ONLY exception: you are directly contradicted by a factual error. Offering opinions, agreement, "me too", or generic help does NOT count. <<<');
65658
+ }
65659
+ } else if (!isA2A) {
65660
+ prefixLines.push("This is an open group message (no specific @mention or reply target).");
65661
+ }
65662
+ prefixLines.push("");
65663
+ prefixLines.push("Rules for ALL group chat messages:");
65664
+ prefixLines.push("1. Check channel history \u2014 if another agent already answered, do NOT repeat.");
65665
+ prefixLines.push("2. Only respond if your specific role/expertise is directly relevant.");
65666
+ prefixLines.push("3. Be concise \u2014 short, actionable responses only.");
65667
+ if (agent.config.agentRole === "manager") {
65668
+ prefixLines.push("4. When assigning work to agents, you MUST use `task_create` to formalize each assignment as a tracked task. Verbal delegation without a task is NOT allowed \u2014 oral promises are meaningless without task tracking. Every commitment must have a corresponding task.");
65669
+ } else {
65670
+ prefixLines.push("4. When you accept a work assignment, verify a task has been created for it (`task_list`). If the coordinator did not create one, remind them or create it yourself with the correct `assigned_agent_id`. Do NOT make promises without task tracking.");
65671
+ }
65672
+ prefixLines.push("5. DEFAULT IS SILENCE. When in doubt, respond with exactly: [NO_RESPONSE]");
65673
+ prefixLines.push('6. If the provided context is insufficient, use `recall_context` (scope="channel") to fetch earlier messages before responding. For task/requirement details use `task_get`/`requirement_get`. Do NOT guess about prior discussion.');
65674
+ if (isTargeted && !thisAgentIsTarget && !isA2A) {
65675
+ prefixLines.push("7. REMINDER: This message is directed at " + [...targetNames].join(", ") + ". You are " + agentName + ". Respond ONLY with [NO_RESPONSE].");
65676
+ }
65677
+ prefixLines.push("---", "");
65678
+ const groupChatPrefix = prefixLines.join("\n");
65265
65679
  const toolEvents = [];
65266
65680
  const reply = await agent.sendMessage(groupChatPrefix + userMessage, senderId, senderInfo, {
65267
- sourceType: "human_chat",
65681
+ sourceType: isA2A ? "a2a_message" : "human_chat",
65682
+ scenario: isA2A ? "a2a" : void 0,
65268
65683
  channelContext,
65269
65684
  toolEventCollector: toolEvents
65270
65685
  });
65271
- if (!reply || reply.trim() === "[NO_RESPONSE]" || !reply.trim()) {
65686
+ if (!reply || !reply.trim() || reply.includes("[NO_RESPONSE]")) {
65272
65687
  return;
65273
65688
  }
65274
- const { thinking, clean: cleanReply } = extractThinkBlocks(reply);
65275
- if (!cleanReply.trim() || cleanReply.trim() === "[NO_RESPONSE]")
65689
+ const { thinking, clean: rawClean } = extractThinkBlocks(reply);
65690
+ const cleanReply = rawClean.replace(/\[NO_RESPONSE\]/gi, "").trim();
65691
+ if (!cleanReply)
65276
65692
  return;
65277
65693
  const metadata = {};
65278
65694
  if (thinking.length > 0)
65279
65695
  metadata["thinking"] = thinking;
65280
65696
  if (toolEvents.length > 0)
65281
65697
  metadata["toolCalls"] = toolEvents;
65698
+ if (isA2A && opts?.replyToAgentName) {
65699
+ metadata["replyToAgent"] = opts.replyToAgentName;
65700
+ }
65701
+ let persistedMsgId;
65282
65702
  if (this.storage) {
65283
- await this.storage.channelMessageRepo.append({
65703
+ const saved = await this.storage.channelMessageRepo.append({
65284
65704
  orgId: orgId2,
65285
65705
  channel,
65286
65706
  senderId: agentId2,
@@ -65288,8 +65708,10 @@ var init_api_server = __esm({
65288
65708
  senderName: agentName,
65289
65709
  text: cleanReply,
65290
65710
  mentions: [],
65291
- metadata: Object.keys(metadata).length > 0 ? metadata : void 0
65711
+ metadata: Object.keys(metadata).length > 0 ? metadata : void 0,
65712
+ replyToId: isA2A ? opts?.replyToMsgId : void 0
65292
65713
  });
65714
+ persistedMsgId = saved.id;
65293
65715
  }
65294
65716
  this.ws.broadcast({
65295
65717
  type: "chat:message",
@@ -65299,10 +65721,29 @@ var init_api_server = __esm({
65299
65721
  senderType: "agent",
65300
65722
  senderName: agentName,
65301
65723
  text: cleanReply,
65302
- metadata: Object.keys(metadata).length > 0 ? metadata : void 0
65724
+ metadata: Object.keys(metadata).length > 0 ? metadata : void 0,
65725
+ ...isA2A ? {
65726
+ replyToId: opts?.replyToMsgId,
65727
+ replyToSender: opts?.replyToAgentName,
65728
+ replyToText: (opts?.replyToText ?? "").slice(0, 120)
65729
+ } : {}
65303
65730
  },
65304
65731
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
65305
65732
  });
65733
+ const allAgentIds = chainCtx?.allAgentIds ?? [];
65734
+ if (allAgentIds.length > 0) {
65735
+ const nameMap = this.buildAgentNameMap(allAgentIds, agentManager);
65736
+ const mentionedNames = this.parseAgentMentions(cleanReply, [...nameMap.keys()]);
65737
+ const filteredMentions = mentionedNames.filter((n) => n !== agentName);
65738
+ log57.info("A2A mention scan", { agentName, mentionedNames, filteredMentions, depth: chainCtx?.depth ?? 0 });
65739
+ if (filteredMentions.length > 0) {
65740
+ const depth = (chainCtx?.depth ?? 0) + 1;
65741
+ const roundId = chainCtx?.roundId ?? `round_${Date.now()}`;
65742
+ const responded = chainCtx?.respondedAgents ?? /* @__PURE__ */ new Set();
65743
+ responded.add(agentId2);
65744
+ void this.triggerAgentToAgentChain(filteredMentions, nameMap, agentName, agentId2, cleanReply, persistedMsgId, channel, orgId2, agentManager, teamSize, { roundId, depth, respondedAgents: responded, allAgentIds });
65745
+ }
65746
+ }
65306
65747
  } catch (err) {
65307
65748
  log57.warn("Group chat agent reply failed", { agentId: agentId2, error: String(err) });
65308
65749
  if (this.storage) {
@@ -65322,6 +65763,56 @@ var init_api_server = __esm({
65322
65763
  }
65323
65764
  }
65324
65765
  }
65766
+ /**
65767
+ * Trigger agent-to-agent chain replies with 3-layer storm prevention:
65768
+ * 1. Depth limit (max 3 hops)
65769
+ * 2. Per-round dedup (each agent responds at most once per conversation round)
65770
+ * 3. Cooldown window (same agent won't be triggered twice within 30s on same channel)
65771
+ */
65772
+ async triggerAgentToAgentChain(mentionedNames, nameMap, fromAgentName, fromAgentId, fromText, fromMsgId, channel, orgId2, agentManager, teamSize, chainCtx) {
65773
+ if (chainCtx.depth > _APIServer.A2A_MAX_DEPTH) {
65774
+ log57.info("A2A chain depth limit reached", { channel, depth: chainCtx.depth, roundId: chainCtx.roundId });
65775
+ return;
65776
+ }
65777
+ const now2 = Date.now();
65778
+ if (!this.a2aCooldowns.has(channel)) {
65779
+ this.a2aCooldowns.set(channel, /* @__PURE__ */ new Map());
65780
+ }
65781
+ const channelCooldowns = this.a2aCooldowns.get(channel);
65782
+ let channelContext = [];
65783
+ if (this.storage) {
65784
+ try {
65785
+ const recent = await this.storage.channelMessageRepo.getMessages(channel, 20);
65786
+ channelContext = (recent.messages ?? []).map((m) => ({
65787
+ role: m.senderType === "agent" ? "assistant" : "user",
65788
+ content: m.senderType === "agent" ? stripInternalBlocks(m.text) : `[${m.senderName}]: ${m.text}`
65789
+ }));
65790
+ } catch {
65791
+ }
65792
+ }
65793
+ for (const targetName of mentionedNames) {
65794
+ const targetId = nameMap.get(targetName);
65795
+ if (!targetId)
65796
+ continue;
65797
+ if (chainCtx.respondedAgents.has(targetId)) {
65798
+ log57.info("A2A skipping (already responded this round)", { targetName, roundId: chainCtx.roundId });
65799
+ continue;
65800
+ }
65801
+ const lastReply = channelCooldowns.get(targetId) ?? 0;
65802
+ if (now2 - lastReply < _APIServer.A2A_COOLDOWN_MS) {
65803
+ log57.info("A2A skipping (cooldown)", { targetName, channel, cooldownRemaining: _APIServer.A2A_COOLDOWN_MS - (now2 - lastReply) });
65804
+ continue;
65805
+ }
65806
+ channelCooldowns.set(targetId, now2);
65807
+ const a2aMessage = `[From @${fromAgentName}]: ${fromText}`;
65808
+ void this.processGroupChatReply(targetId, a2aMessage, fromAgentId, { name: fromAgentName, role: "agent" }, channel, orgId2, channelContext, agentManager, teamSize, {
65809
+ mentionedNames: [targetName],
65810
+ replyToAgentName: fromAgentName,
65811
+ replyToText: fromText.slice(0, 200),
65812
+ replyToMsgId: fromMsgId
65813
+ }, chainCtx);
65814
+ }
65815
+ }
65325
65816
  /** Persist the user message first (before LLM), returns session id for subsequent assistant persistence.
65326
65817
  * When sessionId is provided, appends to that session; when null/undefined, creates a new session. */
65327
65818
  async persistUserMessage(agentId2, userMessage, senderId, images, sessionId) {
@@ -65577,7 +66068,7 @@ var init_api_server = __esm({
65577
66068
  const avatarDir = join23(homedir14(), ".markus", "avatars");
65578
66069
  mkdirSync19(avatarDir, { recursive: true });
65579
66070
  const filename = `${targetType}_${targetId}.${ext}`;
65580
- writeFileSync16(join23(avatarDir, filename), buf);
66071
+ writeFileSync17(join23(avatarDir, filename), buf);
65581
66072
  const avatarUrl = `/api/avatars/${filename}`;
65582
66073
  if (targetType === "user" && this.storage) {
65583
66074
  this.storage.userRepo.updateAvatarUrl(targetId, avatarUrl);
@@ -65654,6 +66145,7 @@ var init_api_server = __esm({
65654
66145
  const senderName = body["senderName"] ?? "You";
65655
66146
  const mentions = body["mentions"] ?? [];
65656
66147
  const targetAgentId = body["targetAgentId"];
66148
+ const replyToId = body["replyToId"];
65657
66149
  const orgId2 = body["orgId"] ?? "default";
65658
66150
  let userMsg;
65659
66151
  if (this.storage) {
@@ -65664,7 +66156,8 @@ var init_api_server = __esm({
65664
66156
  senderType: "human",
65665
66157
  senderName,
65666
66158
  text,
65667
- mentions
66159
+ mentions,
66160
+ replyToId
65668
66161
  });
65669
66162
  }
65670
66163
  const humanOnly = body["humanOnly"] === true;
@@ -65682,21 +66175,50 @@ var init_api_server = __esm({
65682
66175
  return [];
65683
66176
  }
65684
66177
  };
65685
- if (!isHumanChannel && channel.startsWith("group:") && !targetAgentId) {
65686
- const teamId = channel.replace(/^group:/, "");
65687
- const team = this.orgService.getTeam(teamId);
65688
- const allAgentIds = team?.memberAgentIds ?? [];
66178
+ let replyToAgentName;
66179
+ let replyToText;
66180
+ if (replyToId && this.storage) {
66181
+ try {
66182
+ const original = this.storage.channelMessageRepo.getMessageById(replyToId);
66183
+ if (original) {
66184
+ replyToAgentName = original.senderName;
66185
+ replyToText = original.text.slice(0, 200);
66186
+ }
66187
+ } catch {
66188
+ }
66189
+ }
66190
+ const enrichedUserMsg = userMsg ? {
66191
+ ...userMsg,
66192
+ ...replyToAgentName ? { replyToSender: replyToAgentName, replyToText } : {}
66193
+ } : null;
66194
+ if (!isHumanChannel && channel.startsWith("group:")) {
66195
+ let allAgentIds;
66196
+ if (channel.startsWith("group:custom:")) {
66197
+ allAgentIds = this.storage?.groupChatRepo?.getAgentMemberIds(channel) ?? [];
66198
+ } else {
66199
+ const teamId = channel.replace(/^group:/, "");
66200
+ const team = this.orgService.getTeam(teamId);
66201
+ allAgentIds = team?.memberAgentIds ?? [];
66202
+ }
65689
66203
  if (allAgentIds.length === 0) {
65690
- this.json(res, 200, { userMessage: userMsg ?? null, agentMessage: null });
66204
+ this.json(res, 200, { userMessage: enrichedUserMsg, agentMessage: null });
65691
66205
  return;
65692
66206
  }
65693
66207
  const channelContext2 = await buildChannelContext();
65694
66208
  const senderInfo2 = this.orgService.resolveHumanIdentity(senderId);
65695
66209
  const agentManager = this.orgService.getAgentManager();
66210
+ const broadcastOpts = {
66211
+ mentionedNames: mentions.length > 0 ? mentions : void 0,
66212
+ replyToAgentName,
66213
+ replyToText
66214
+ };
66215
+ const roundId = `round_${Date.now()}`;
66216
+ const respondedAgents = /* @__PURE__ */ new Set();
66217
+ const chainCtx = { roundId, depth: 0, respondedAgents, allAgentIds };
65696
66218
  for (const agentId2 of allAgentIds) {
65697
- void this.processGroupChatReply(agentId2, text, senderId, senderInfo2, channel, orgId2, channelContext2, agentManager, allAgentIds.length);
66219
+ void this.processGroupChatReply(agentId2, text, senderId, senderInfo2, channel, orgId2, channelContext2, agentManager, allAgentIds.length, broadcastOpts, chainCtx);
65698
66220
  }
65699
- this.json(res, 200, { userMessage: userMsg ?? null, agentMessage: null });
66221
+ this.json(res, 200, { userMessage: enrichedUserMsg, agentMessage: null });
65700
66222
  return;
65701
66223
  }
65702
66224
  let routedAgentId = null;
@@ -65710,7 +66232,7 @@ var init_api_server = __esm({
65710
66232
  }
65711
66233
  }
65712
66234
  if (!routedAgentId) {
65713
- this.json(res, 200, { userMessage: userMsg ?? null, agentMessage: null });
66235
+ this.json(res, 200, { userMessage: enrichedUserMsg, agentMessage: null });
65714
66236
  return;
65715
66237
  }
65716
66238
  const agent = this.orgService.getAgentManager().getAgent(routedAgentId);
@@ -65753,7 +66275,7 @@ var init_api_server = __esm({
65753
66275
  }
65754
66276
  const statusCode = raw.includes("402") ? 402 : raw.includes("401") ? 401 : raw.includes("429") ? 429 : 502;
65755
66277
  this.json(res, statusCode, {
65756
- userMessage: userMsg ?? null,
66278
+ userMessage: enrichedUserMsg,
65757
66279
  agentMessage: errorMsg ?? null,
65758
66280
  error: detail2
65759
66281
  });
@@ -65780,7 +66302,7 @@ var init_api_server = __esm({
65780
66302
  void this.persistChatTurn(routedAgentId, text, reply, senderId);
65781
66303
  }
65782
66304
  this.json(res, 200, {
65783
- userMessage: userMsg ?? null,
66305
+ userMessage: enrichedUserMsg,
65784
66306
  agentMessage: agentMsg ?? {
65785
66307
  id: `tmp_${Date.now()}`,
65786
66308
  channel,
@@ -65980,7 +66502,7 @@ var init_api_server = __esm({
65980
66502
  if (path === "/api/group-chats" && req.method === "GET") {
65981
66503
  const orgId2 = url.searchParams.get("orgId") ?? "default";
65982
66504
  const teams = this.orgService.listTeamsWithMembers(orgId2);
65983
- const groupChats = teams.map((t) => ({
66505
+ const teamChats = teams.map((t) => ({
65984
66506
  id: `group:${t.id}`,
65985
66507
  name: t.name,
65986
66508
  type: "team",
@@ -65988,21 +66510,16 @@ var init_api_server = __esm({
65988
66510
  memberCount: t.members.length,
65989
66511
  channelKey: `group:${t.id}`
65990
66512
  }));
65991
- const customChats = this.customGroupChats.filter((c) => c.orgId === orgId2);
65992
- this.json(res, 200, {
65993
- chats: [
65994
- ...groupChats,
65995
- ...customChats.map((c) => ({
65996
- id: c.id,
65997
- name: c.name,
65998
- type: "custom",
65999
- creatorId: c.creatorId,
66000
- creatorName: c.creatorName,
66001
- memberCount: c.memberIds?.length ?? 0,
66002
- channelKey: c.id
66003
- }))
66004
- ]
66005
- });
66513
+ const customChats = this.storage?.groupChatRepo ? this.storage.groupChatRepo.list(orgId2).map((c) => ({
66514
+ id: c.id,
66515
+ name: c.name,
66516
+ type: "custom",
66517
+ creatorId: c.creatorId,
66518
+ creatorName: c.creatorName,
66519
+ memberCount: c.memberCount,
66520
+ channelKey: c.channelKey
66521
+ })) : [];
66522
+ this.json(res, 200, { chats: [...teamChats, ...customChats] });
66006
66523
  return;
66007
66524
  }
66008
66525
  if (path === "/api/group-chats" && req.method === "POST") {
@@ -66012,31 +66529,129 @@ var init_api_server = __esm({
66012
66529
  const creatorId = body["creatorId"];
66013
66530
  const creatorName = body["creatorName"];
66014
66531
  const memberIds = body["memberIds"];
66532
+ const memberTypes = body["memberTypes"];
66533
+ const memberNames = body["memberNames"];
66015
66534
  if (!name) {
66016
66535
  this.json(res, 400, { error: "name is required" });
66017
66536
  return;
66018
66537
  }
66019
- const chatId = `group:custom:${Date.now().toString(36)}`;
66020
- const chat = {
66021
- id: chatId,
66022
- name,
66023
- orgId: orgId2,
66024
- creatorId,
66025
- creatorName,
66026
- memberIds: memberIds ?? [],
66027
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
66028
- };
66029
- this.customGroupChats.push(chat);
66538
+ if (!this.storage?.groupChatRepo) {
66539
+ this.json(res, 500, { error: "Storage not initialized" });
66540
+ return;
66541
+ }
66542
+ const members = (memberIds ?? []).map((id) => {
66543
+ const mType = memberTypes?.[id] ?? "agent";
66544
+ let mName = memberNames?.[id] ?? id;
66545
+ if (mType === "agent") {
66546
+ try {
66547
+ mName = this.orgService.getAgentManager().getAgent(id)?.config?.name ?? mName;
66548
+ } catch {
66549
+ }
66550
+ }
66551
+ return { id, type: mType, name: mName };
66552
+ });
66553
+ const gc = this.storage.groupChatRepo.create({ orgId: orgId2, name, creatorId, creatorName, members });
66030
66554
  this.ws?.broadcast({
66031
66555
  type: "chat:group_created",
66032
- payload: { chatId, name, creatorId, creatorName },
66556
+ payload: { chatId: gc.channelKey, name, creatorId, creatorName },
66033
66557
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
66034
66558
  });
66035
66559
  this.json(res, 201, {
66036
- chat: { id: chatId, name, type: "custom", creatorId, creatorName, channelKey: chatId }
66560
+ chat: {
66561
+ id: gc.id,
66562
+ name: gc.name,
66563
+ type: "custom",
66564
+ creatorId: gc.creatorId,
66565
+ creatorName: gc.creatorName,
66566
+ channelKey: gc.channelKey,
66567
+ memberCount: gc.members.length,
66568
+ members: gc.members.map((m) => ({ id: m.memberId, name: m.memberName, type: m.memberType }))
66569
+ }
66037
66570
  });
66038
66571
  return;
66039
66572
  }
66573
+ if (path.match(/^\/api\/group-chats\/[^/]+$/) && req.method === "GET") {
66574
+ const gcId = path.split("/").pop();
66575
+ if (!this.storage?.groupChatRepo) {
66576
+ this.json(res, 500, { error: "Storage not initialized" });
66577
+ return;
66578
+ }
66579
+ const gc = this.storage.groupChatRepo.getById(gcId);
66580
+ if (!gc) {
66581
+ this.json(res, 404, { error: "Group chat not found" });
66582
+ return;
66583
+ }
66584
+ this.json(res, 200, {
66585
+ chat: {
66586
+ id: gc.id,
66587
+ name: gc.name,
66588
+ type: "custom",
66589
+ channelKey: gc.channelKey,
66590
+ creatorId: gc.creatorId,
66591
+ creatorName: gc.creatorName,
66592
+ members: gc.members.map((m) => ({ id: m.memberId, name: m.memberName, type: m.memberType }))
66593
+ }
66594
+ });
66595
+ return;
66596
+ }
66597
+ if (path.match(/^\/api\/group-chats\/[^/]+$/) && req.method === "PATCH") {
66598
+ const gcId = path.split("/").pop();
66599
+ if (!this.storage?.groupChatRepo) {
66600
+ this.json(res, 500, { error: "Storage not initialized" });
66601
+ return;
66602
+ }
66603
+ const body = await this.readBody(req);
66604
+ const newName = body["name"];
66605
+ if (newName)
66606
+ this.storage.groupChatRepo.updateName(gcId, newName);
66607
+ this.json(res, 200, { ok: true });
66608
+ return;
66609
+ }
66610
+ if (path.match(/^\/api\/group-chats\/[^/]+$/) && req.method === "DELETE") {
66611
+ const gcId = path.split("/").pop();
66612
+ if (!this.storage?.groupChatRepo) {
66613
+ this.json(res, 500, { error: "Storage not initialized" });
66614
+ return;
66615
+ }
66616
+ this.storage.groupChatRepo.delete(gcId);
66617
+ this.ws?.broadcast({ type: "chat:group_deleted", payload: { chatId: gcId }, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
66618
+ this.json(res, 200, { ok: true });
66619
+ return;
66620
+ }
66621
+ if (path.match(/^\/api\/group-chats\/[^/]+\/members$/) && req.method === "POST") {
66622
+ const gcId = path.split("/")[3];
66623
+ if (!this.storage?.groupChatRepo) {
66624
+ this.json(res, 500, { error: "Storage not initialized" });
66625
+ return;
66626
+ }
66627
+ const body = await this.readBody(req);
66628
+ const memberId = body["memberId"];
66629
+ const memberType = body["memberType"] ?? "agent";
66630
+ let memberName = body["memberName"] ?? memberId;
66631
+ if (memberType === "agent") {
66632
+ try {
66633
+ memberName = this.orgService.getAgentManager().getAgent(memberId)?.config?.name ?? memberName;
66634
+ } catch {
66635
+ }
66636
+ }
66637
+ this.storage.groupChatRepo.addMember(gcId, memberId, memberType, memberName);
66638
+ this.ws?.broadcast({ type: "chat:group_updated", payload: { chatId: gcId }, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
66639
+ this.json(res, 200, { ok: true });
66640
+ return;
66641
+ }
66642
+ if (path.match(/^\/api\/group-chats\/[^/]+\/members\/[^/]+$/) && req.method === "DELETE") {
66643
+ const parts = path.split("/");
66644
+ const gcId = parts[3];
66645
+ const memberId = parts[5];
66646
+ if (!this.storage?.groupChatRepo) {
66647
+ this.json(res, 500, { error: "Storage not initialized" });
66648
+ return;
66649
+ }
66650
+ this.storage.groupChatRepo.removeMember(gcId, memberId);
66651
+ this.ws?.broadcast({ type: "chat:group_updated", payload: { chatId: gcId }, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
66652
+ this.json(res, 200, { ok: true });
66653
+ return;
66654
+ }
66040
66655
  if (path === "/api/teams" && req.method === "GET") {
66041
66656
  const orgId2 = url.searchParams.get("orgId") ?? "default";
66042
66657
  const teams = this.orgService.listTeamsWithMembers(orgId2);
@@ -66271,7 +66886,7 @@ var init_api_server = __esm({
66271
66886
  const content = body["content"];
66272
66887
  const dir = this.orgService.getTeamDataDir(teamId);
66273
66888
  mkdirSync19(dir, { recursive: true });
66274
- writeFileSync16(join23(dir, filename), content ?? "", "utf-8");
66889
+ writeFileSync17(join23(dir, filename), content ?? "", "utf-8");
66275
66890
  this.json(res, 200, { ok: true });
66276
66891
  return;
66277
66892
  }
@@ -67116,7 +67731,7 @@ var init_api_server = __esm({
67116
67731
  }
67117
67732
  const body = await this.readBody(req);
67118
67733
  const content = body["content"] ?? "";
67119
- writeFileSync16(join23(roleDir, filename), content, "utf-8");
67734
+ writeFileSync17(join23(roleDir, filename), content, "utf-8");
67120
67735
  this.json(res, 200, { ok: true });
67121
67736
  } catch {
67122
67737
  this.json(res, 404, { error: `Agent not found: ${agentId2}` });
@@ -68155,7 +68770,7 @@ EXPLANATION_END`;
68155
68770
  return;
68156
68771
  }
68157
68772
  if (path === "/api/skills/builtin" && req.method === "GET") {
68158
- const builtinDir = resolve14(process.cwd(), "templates", "skills");
68773
+ const builtinDir = resolve13(process.cwd(), "templates", "skills");
68159
68774
  const found = discoverSkillsInDir(builtinDir);
68160
68775
  const installedSkills = new Map((this.skillRegistry?.list() ?? []).map((s) => [s.name, s]));
68161
68776
  const skills = found.map(({ manifest, path: p }) => {
@@ -68455,7 +69070,7 @@ EXPLANATION_END`;
68455
69070
  const mfName = manifestFilename(pkgType);
68456
69071
  const artDir = join23(homedir14(), ".markus", "builder-artifacts", typeDir, manifest.name);
68457
69072
  mkdirSync19(artDir, { recursive: true });
68458
- writeFileSync16(join23(artDir, mfName), JSON.stringify(manifest, null, 2), "utf-8");
69073
+ writeFileSync17(join23(artDir, mfName), JSON.stringify(manifest, null, 2), "utf-8");
68459
69074
  const artFiles = artifact.files;
68460
69075
  if (artFiles) {
68461
69076
  for (const [fn, c] of Object.entries(artFiles)) {
@@ -68463,18 +69078,18 @@ EXPLANATION_END`;
68463
69078
  continue;
68464
69079
  const filePath = join23(artDir, fn);
68465
69080
  mkdirSync19(dirname6(filePath), { recursive: true });
68466
- writeFileSync16(filePath, c, "utf-8");
69081
+ writeFileSync17(filePath, c, "utf-8");
68467
69082
  }
68468
69083
  }
68469
69084
  if (mode === "team") {
68470
69085
  const fileSet = new Set(artFiles ? Object.keys(artFiles) : []);
68471
69086
  const announcement = artifact.announcement;
68472
69087
  if (announcement && !fileSet.has("ANNOUNCEMENT.md")) {
68473
- writeFileSync16(join23(artDir, "ANNOUNCEMENT.md"), announcement, "utf-8");
69088
+ writeFileSync17(join23(artDir, "ANNOUNCEMENT.md"), announcement, "utf-8");
68474
69089
  }
68475
69090
  const norms = artifact.norms;
68476
69091
  if (norms && !fileSet.has("NORMS.md")) {
68477
- writeFileSync16(join23(artDir, "NORMS.md"), norms, "utf-8");
69092
+ writeFileSync17(join23(artDir, "NORMS.md"), norms, "utf-8");
68478
69093
  }
68479
69094
  const rawMembers = Array.isArray(artifact.team?.members) ? artifact.team.members : Array.isArray(artifact.members) ? artifact.members : [];
68480
69095
  for (const m of rawMembers) {
@@ -68486,15 +69101,15 @@ EXPLANATION_END`;
68486
69101
  const contextContent = m.contextContent || m.context_md;
68487
69102
  if (roleContent && !fileSet.has(`members/${slug}/ROLE.md`)) {
68488
69103
  mkdirSync19(memberDir, { recursive: true });
68489
- writeFileSync16(join23(memberDir, "ROLE.md"), roleContent, "utf-8");
69104
+ writeFileSync17(join23(memberDir, "ROLE.md"), roleContent, "utf-8");
68490
69105
  }
68491
69106
  if (policiesContent && !fileSet.has(`members/${slug}/POLICIES.md`)) {
68492
69107
  mkdirSync19(memberDir, { recursive: true });
68493
- writeFileSync16(join23(memberDir, "POLICIES.md"), policiesContent, "utf-8");
69108
+ writeFileSync17(join23(memberDir, "POLICIES.md"), policiesContent, "utf-8");
68494
69109
  }
68495
69110
  if (contextContent && !fileSet.has(`members/${slug}/CONTEXT.md`)) {
68496
69111
  mkdirSync19(memberDir, { recursive: true });
68497
- writeFileSync16(join23(memberDir, "CONTEXT.md"), contextContent, "utf-8");
69112
+ writeFileSync17(join23(memberDir, "CONTEXT.md"), contextContent, "utf-8");
68498
69113
  }
68499
69114
  }
68500
69115
  const memberFiles = artifact.memberFiles;
@@ -68503,7 +69118,7 @@ EXPLANATION_END`;
68503
69118
  const memberDir = join23(artDir, "members", slug);
68504
69119
  mkdirSync19(memberDir, { recursive: true });
68505
69120
  for (const [fn, c] of Object.entries(files))
68506
- writeFileSync16(join23(memberDir, fn), c, "utf-8");
69121
+ writeFileSync17(join23(memberDir, fn), c, "utf-8");
68507
69122
  }
68508
69123
  }
68509
69124
  }
@@ -68541,7 +69156,7 @@ EXPLANATION_END`;
68541
69156
  for (const [fn, content] of Object.entries(files)) {
68542
69157
  const filePath = join23(artDir, fn);
68543
69158
  mkdirSync19(dirname6(filePath), { recursive: true });
68544
- writeFileSync16(filePath, content, "utf-8");
69159
+ writeFileSync17(filePath, content, "utf-8");
68545
69160
  }
68546
69161
  if (source) {
68547
69162
  const mfName = manifestFilename(type);
@@ -68550,7 +69165,7 @@ EXPLANATION_END`;
68550
69165
  try {
68551
69166
  const mf = JSON.parse(readFileSync21(mfPath, "utf-8"));
68552
69167
  mf.source = source;
68553
- writeFileSync16(mfPath, JSON.stringify(mf, null, 2), "utf-8");
69168
+ writeFileSync17(mfPath, JSON.stringify(mf, null, 2), "utf-8");
68554
69169
  } catch {
68555
69170
  }
68556
69171
  }
@@ -69224,10 +69839,10 @@ EXPLANATION_END`;
69224
69839
  if (path === "/api/templates/teams" && req.method === "GET") {
69225
69840
  try {
69226
69841
  const { readdirSync: readdirSync13, readFileSync: readFileSync27 } = await import("node:fs");
69227
- const { resolve: resolve21 } = await import("node:path");
69228
- const teamsDir = resolve21(process.cwd(), "templates", "teams");
69842
+ const { resolve: resolve20 } = await import("node:path");
69843
+ const teamsDir = resolve20(process.cwd(), "templates", "teams");
69229
69844
  const files = readdirSync13(teamsDir).filter((f) => f.endsWith(".json"));
69230
- const teams = files.map((f) => JSON.parse(readFileSync27(resolve21(teamsDir, f), "utf-8")));
69845
+ const teams = files.map((f) => JSON.parse(readFileSync27(resolve20(teamsDir, f), "utf-8")));
69231
69846
  this.json(res, 200, { templates: teams });
69232
69847
  } catch {
69233
69848
  this.json(res, 200, { templates: [] });
@@ -69351,7 +69966,7 @@ EXPLANATION_END`;
69351
69966
  try {
69352
69967
  if (token) {
69353
69968
  mkdirSync19(join23(homedir14(), ".markus"), { recursive: true });
69354
- writeFileSync16(tokenPath, token, "utf-8");
69969
+ writeFileSync17(tokenPath, token, "utf-8");
69355
69970
  } else if (existsSync27(tokenPath)) {
69356
69971
  rmSync3(tokenPath);
69357
69972
  }
@@ -69778,7 +70393,9 @@ EXPLANATION_END`;
69778
70393
  return;
69779
70394
  }
69780
70395
  try {
70396
+ const prevDefault = this.llmRouter.getSettings().defaultProvider;
69781
70397
  this.llmRouter.setProviderEnabled(providerName, enabled);
70398
+ const newDefault = this.llmRouter.getSettings().defaultProvider;
69782
70399
  try {
69783
70400
  const { loadConfig: loadCfg } = await Promise.resolve().then(() => (init_dist(), dist_exports));
69784
70401
  const currentConfig = loadCfg(this.markusConfigPath);
@@ -69788,7 +70405,11 @@ EXPLANATION_END`;
69788
70405
  } else {
69789
70406
  providers[providerName] = { enabled };
69790
70407
  }
69791
- saveConfig({ llm: { providers } }, this.markusConfigPath);
70408
+ const configUpdates = { llm: { providers } };
70409
+ if (prevDefault !== newDefault) {
70410
+ configUpdates.llm.defaultProvider = newDefault;
70411
+ }
70412
+ saveConfig(configUpdates, this.markusConfigPath);
69792
70413
  } catch (e) {
69793
70414
  log57.warn("Failed to persist provider enabled state", { error: String(e) });
69794
70415
  }
@@ -69798,6 +70419,70 @@ EXPLANATION_END`;
69798
70419
  }
69799
70420
  return;
69800
70421
  }
70422
+ if (path.match(/^\/api\/settings\/llm\/providers\/[^/]+\/test$/) && req.method === "POST") {
70423
+ const auth = await this.requireAuth(req, res);
70424
+ if (!auth)
70425
+ return;
70426
+ if (!this.llmRouter) {
70427
+ this.json(res, 503, { error: "LLM router not available" });
70428
+ return;
70429
+ }
70430
+ const providerName = path.split("/")[5];
70431
+ const provider = this.llmRouter.getProvider(providerName);
70432
+ if (!provider) {
70433
+ this.json(res, 404, { ok: false, error: `Provider "${providerName}" not found or not configured` });
70434
+ return;
70435
+ }
70436
+ try {
70437
+ const startMs = Date.now();
70438
+ const response = await this.llmRouter.chat({
70439
+ messages: [{ role: "user", content: "Reply with exactly one word: hello" }],
70440
+ maxTokens: 32,
70441
+ temperature: 0
70442
+ }, providerName);
70443
+ const durationMs = Date.now() - startMs;
70444
+ const reply = (response.content ?? "").trim();
70445
+ if (!reply) {
70446
+ this.json(res, 200, {
70447
+ ok: false,
70448
+ error: "Model returned empty response \u2014 API key or model may be misconfigured",
70449
+ model: provider.model,
70450
+ durationMs
70451
+ });
70452
+ } else {
70453
+ this.json(res, 200, {
70454
+ ok: true,
70455
+ durationMs,
70456
+ model: provider.model,
70457
+ reply: reply.slice(0, 100),
70458
+ usage: response.usage
70459
+ });
70460
+ }
70461
+ } catch (err) {
70462
+ const raw = String(err);
70463
+ const statusMatch = raw.match(/(?:API error|status)\s+(\d{3})/i);
70464
+ const errorCode = statusMatch ? Number(statusMatch[1]) : void 0;
70465
+ let errorMsg = raw.replace(/^Error:\s*/, "");
70466
+ const jsonStart = errorMsg.indexOf("{");
70467
+ if (jsonStart >= 0) {
70468
+ try {
70469
+ const jsonStr = errorMsg.slice(jsonStart);
70470
+ const parsed = JSON.parse(jsonStr);
70471
+ if (parsed.error?.message) {
70472
+ errorMsg = `[${parsed.error.type ?? parsed.error.code ?? errorCode ?? "error"}] ${parsed.error.message}`;
70473
+ }
70474
+ } catch {
70475
+ }
70476
+ }
70477
+ this.json(res, 200, {
70478
+ ok: false,
70479
+ error: errorMsg.slice(0, 500),
70480
+ errorCode,
70481
+ model: provider.model
70482
+ });
70483
+ }
70484
+ return;
70485
+ }
69801
70486
  if (path === "/api/settings/env-models" && req.method === "GET") {
69802
70487
  const auth = await this.requireAuth(req, res);
69803
70488
  if (!auth)
@@ -69809,7 +70494,8 @@ EXPLANATION_END`;
69809
70494
  { provider: "siliconflow", displayName: "SiliconFlow", keyEnv: "SILICONFLOW_API_KEY", modelEnv: "SILICONFLOW_MODEL", baseUrlEnv: "SILICONFLOW_BASE_URL", defaultModel: "Qwen/Qwen3.5-35B-A3B", defaultBaseUrl: "https://api.siliconflow.cn/v1" },
69810
70495
  { provider: "minimax", displayName: "MiniMax", keyEnv: "MINIMAX_API_KEY", modelEnv: "MINIMAX_MODEL", baseUrlEnv: "MINIMAX_BASE_URL", defaultModel: "MiniMax-M2.7", defaultBaseUrl: "https://api.minimax.io/v1" },
69811
70496
  { provider: "openrouter", displayName: "OpenRouter", keyEnv: "OPENROUTER_API_KEY", modelEnv: "OPENROUTER_MODEL", baseUrlEnv: "OPENROUTER_BASE_URL", defaultModel: "xiaomi/mimo-v2-pro", defaultBaseUrl: "https://openrouter.ai/api/v1" },
69812
- { provider: "zai", displayName: "ZAI", keyEnv: "ZAI_API_KEY", modelEnv: "ZAI_MODEL", baseUrlEnv: "ZAI_BASE_URL", defaultModel: "glm-5.1", defaultBaseUrl: "https://api.z.ai/api/paas/v4" }
70497
+ { provider: "zai", displayName: "ZAI", keyEnv: "ZAI_API_KEY", modelEnv: "ZAI_MODEL", baseUrlEnv: "ZAI_BASE_URL", defaultModel: "glm-5.1", defaultBaseUrl: "https://api.z.ai/api/paas/v4" },
70498
+ { provider: "deepseek", displayName: "DeepSeek", keyEnv: "DEEPSEEK_API_KEY", modelEnv: "DEEPSEEK_MODEL", baseUrlEnv: "DEEPSEEK_BASE_URL", defaultModel: "deepseek-v4-flash", defaultBaseUrl: "https://api.deepseek.com" }
69813
70499
  ];
69814
70500
  const detected = [];
69815
70501
  for (const def of ENV_MODEL_MAP) {
@@ -69862,7 +70548,9 @@ EXPLANATION_END`;
69862
70548
  google: "GOOGLE_API_KEY",
69863
70549
  siliconflow: "SILICONFLOW_API_KEY",
69864
70550
  minimax: "MINIMAX_API_KEY",
69865
- openrouter: "OPENROUTER_API_KEY"
70551
+ openrouter: "OPENROUTER_API_KEY",
70552
+ zai: "ZAI_API_KEY",
70553
+ deepseek: "DEEPSEEK_API_KEY"
69866
70554
  };
69867
70555
  const apiKey = process.env[envKeyMap[pu.provider] ?? ""];
69868
70556
  if (!apiKey)
@@ -70481,14 +71169,14 @@ EXPLANATION_END`;
70481
71169
  return;
70482
71170
  }
70483
71171
  try {
70484
- const { resolve: resolve21, extname } = await import("node:path");
71172
+ const { resolve: resolve20, extname } = await import("node:path");
70485
71173
  const { existsSync: existsSync36, statSync: statSync6 } = await import("node:fs");
70486
71174
  const results = {};
70487
71175
  const mdExts = [".md", ".markdown"];
70488
71176
  const imageExts = [".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"];
70489
71177
  for (const p of paths.slice(0, 50)) {
70490
71178
  try {
70491
- const resolved = resolve21(p);
71179
+ const resolved = resolve20(p);
70492
71180
  if (!existsSync36(resolved)) {
70493
71181
  results[p] = { exists: false, isFile: false, type: "unknown" };
70494
71182
  continue;
@@ -70521,9 +71209,9 @@ EXPLANATION_END`;
70521
71209
  return;
70522
71210
  }
70523
71211
  try {
70524
- const { resolve: resolve21, extname } = await import("node:path");
71212
+ const { resolve: resolve20, extname } = await import("node:path");
70525
71213
  const { readFileSync: readFileSync27, existsSync: existsSync36, statSync: statSync6 } = await import("node:fs");
70526
- const resolved = resolve21(filePath);
71214
+ const resolved = resolve20(filePath);
70527
71215
  if (!existsSync36(resolved)) {
70528
71216
  this.json(res, 404, { error: "File not found" });
70529
71217
  return;
@@ -70571,10 +71259,10 @@ EXPLANATION_END`;
70571
71259
  return;
70572
71260
  }
70573
71261
  try {
70574
- const { resolve: resolve21, dirname: dirname11 } = await import("node:path");
71262
+ const { resolve: resolve20, dirname: dirname11 } = await import("node:path");
70575
71263
  const { existsSync: existsSync36, statSync: statSync6 } = await import("node:fs");
70576
71264
  const { exec: exec2 } = await import("node:child_process");
70577
- const resolved = resolve21(filePath);
71265
+ const resolved = resolve20(filePath);
70578
71266
  if (!existsSync36(resolved)) {
70579
71267
  this.json(res, 404, { error: "Path not found" });
70580
71268
  return;
@@ -71304,14 +71992,14 @@ EXPLANATION_END`;
71304
71992
  res.end(JSON.stringify(data));
71305
71993
  }
71306
71994
  readBody(req) {
71307
- return new Promise((resolve21, reject) => {
71995
+ return new Promise((resolve20, reject) => {
71308
71996
  const chunks = [];
71309
71997
  req.on("data", (chunk) => chunks.push(chunk));
71310
71998
  req.on("end", () => {
71311
71999
  try {
71312
- resolve21(JSON.parse(Buffer.concat(chunks).toString()));
72000
+ resolve20(JSON.parse(Buffer.concat(chunks).toString()));
71313
72001
  } catch {
71314
- resolve21({});
72002
+ resolve20({});
71315
72003
  }
71316
72004
  });
71317
72005
  req.on("error", reject);
@@ -71621,7 +72309,7 @@ var init_hitl_service = __esm({
71621
72309
  this.persistApproval(approval);
71622
72310
  log58.info(`Approval requested: ${id} by ${opts.agentName}`);
71623
72311
  this.notify({
71624
- targetUserId: opts.targetUserId ?? "default",
72312
+ targetUserId: opts.targetUserId ?? "all",
71625
72313
  type: "approval_request",
71626
72314
  title: `Approval needed: ${opts.title}`,
71627
72315
  body: opts.description,
@@ -71634,13 +72322,13 @@ var init_hitl_service = __esm({
71634
72322
  }
71635
72323
  async requestApprovalAndWait(opts) {
71636
72324
  const approval = this.requestApproval(opts);
71637
- return new Promise((resolve21) => {
71638
- this.pendingResolvers.set(approval.id, resolve21);
72325
+ return new Promise((resolve20) => {
72326
+ this.pendingResolvers.set(approval.id, resolve20);
71639
72327
  if (opts.expiresInMs) {
71640
72328
  setTimeout(() => {
71641
72329
  if (this.pendingResolvers.has(approval.id)) {
71642
72330
  this.pendingResolvers.delete(approval.id);
71643
- resolve21({ approved: false, comment: "Approval timed out" });
72331
+ resolve20({ approved: false, comment: "Approval timed out" });
71644
72332
  }
71645
72333
  }, opts.expiresInMs);
71646
72334
  }
@@ -71670,10 +72358,10 @@ var init_hitl_service = __esm({
71670
72358
  } catch {
71671
72359
  }
71672
72360
  }
71673
- const resolve21 = this.pendingResolvers.get(id);
71674
- if (resolve21) {
72361
+ const resolve20 = this.pendingResolvers.get(id);
72362
+ if (resolve20) {
71675
72363
  this.pendingResolvers.delete(id);
71676
- resolve21({ approved, comment, selectedOption });
72364
+ resolve20({ approved, comment, selectedOption });
71677
72365
  }
71678
72366
  return approval;
71679
72367
  }
@@ -72389,7 +73077,7 @@ var init_requirement_service = __esm({
72389
73077
  title: `Requirement approval: ${req.title}`,
72390
73078
  description: `Agent "${req.createdBy}" proposed requirement "${req.title}" (priority: ${req.priority}).`,
72391
73079
  details: { requirementId: req.id, priority: req.priority },
72392
- targetUserId: "default"
73080
+ targetUserId: "all"
72393
73081
  }).then((result) => {
72394
73082
  const current = this.requirements.get(req.id);
72395
73083
  if (!current || current.status !== "pending")
@@ -72522,7 +73210,7 @@ var init_requirement_service = __esm({
72522
73210
  title: `Requirement approval (resubmitted): ${req.title}`,
72523
73211
  description: `Agent "${req.createdBy}" resubmitted requirement "${req.title}" (priority: ${req.priority}).`,
72524
73212
  details: { requirementId: req.id, priority: req.priority },
72525
- targetUserId: "default"
73213
+ targetUserId: "all"
72526
73214
  }).then((result) => {
72527
73215
  const current = this.requirements.get(req.id);
72528
73216
  if (!current || current.status !== "pending")
@@ -72643,11 +73331,13 @@ var init_requirement_service = __esm({
72643
73331
  req.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
72644
73332
  }
72645
73333
  /**
72646
- * Check if all tasks for a requirement are done. If so, mark it as completed.
73334
+ * Check if all tasks for a requirement are done. Instead of auto-completing,
73335
+ * notify the creator agent so they can review results and decide whether to
73336
+ * complete the requirement or create additional tasks.
72647
73337
  */
72648
73338
  checkCompletion(requirementId2, taskStatuses) {
72649
73339
  const req = this.requirements.get(requirementId2);
72650
- if (!req || req.status === "completed")
73340
+ if (!req || req.status === "completed" || req.status === "cancelled")
72651
73341
  return false;
72652
73342
  if (req.taskIds.length === 0)
72653
73343
  return false;
@@ -72655,14 +73345,28 @@ var init_requirement_service = __esm({
72655
73345
  const status = taskStatuses.get(tid);
72656
73346
  return status === "completed" || status === "cancelled" || status === "archived";
72657
73347
  });
72658
- if (allDone) {
72659
- req.status = "completed";
72660
- req.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
72661
- if (this.requirementRepo) {
72662
- this.requirementRepo.updateStatus(requirementId2, "completed").catch((e) => log62.error("Failed to persist requirement completion", { id: requirementId2, error: String(e) }));
73348
+ if (allDone && req.createdBy && this.agentManager) {
73349
+ try {
73350
+ const agent = this.agentManager.getAgent(req.createdBy);
73351
+ if (agent) {
73352
+ agent.enqueueToMailbox("requirement_update", {
73353
+ summary: `All tasks for requirement "${req.title}" are done \u2014 review needed`,
73354
+ content: [
73355
+ `[REQUIREMENT REVIEW NEEDED] All linked tasks for "${req.title}" (ID: ${requirementId2}) have reached terminal state.`,
73356
+ "",
73357
+ "Please review the results and decide:",
73358
+ `1. If the requirement is fully satisfied, complete it: requirement_update_status(requirement_id="${requirementId2}", status="completed")`,
73359
+ `2. If more work is needed, create additional tasks with task_create(requirement_id="${requirementId2}", ...)`
73360
+ ].join("\n"),
73361
+ requirementId: requirementId2,
73362
+ extra: { actionRequired: true }
73363
+ }, {
73364
+ priority: 2,
73365
+ metadata: { senderName: "System", senderRole: "manager" }
73366
+ });
73367
+ }
73368
+ } catch {
72663
73369
  }
72664
- this.broadcast("requirement:completed", req);
72665
- log62.info("Requirement completed", { id: requirementId2 });
72666
73370
  return true;
72667
73371
  }
72668
73372
  return false;
@@ -72806,14 +73510,13 @@ var init_requirement_service = __esm({
72806
73510
  parts.push("2. Abandon this requirement if the feedback indicates it is not needed");
72807
73511
  }
72808
73512
  const agent = this.agentManager.getAgent(creatorId);
72809
- const isRejection = decision === "rejected";
72810
73513
  agent.enqueueToMailbox("requirement_update", {
72811
73514
  summary: `Requirement "${req.title}" ${decision}`,
72812
73515
  content: parts.join("\n"),
72813
73516
  requirementId: req.id,
72814
- extra: isRejection ? { actionRequired: true } : void 0
73517
+ extra: { actionRequired: true }
72815
73518
  }, {
72816
- priority: isRejection ? 1 : 3,
73519
+ priority: 1,
72817
73520
  metadata: { senderName: "System", senderRole: "manager" }
72818
73521
  });
72819
73522
  log62.info("Notified creator agent about requirement decision", {
@@ -72826,7 +73529,7 @@ var init_requirement_service = __esm({
72826
73529
  const title = decision === "approved" ? `Requirement approved: ${req.title}` : `Requirement rejected: ${req.title}`;
72827
73530
  const body = decision === "approved" ? `Requirement "${req.title}" has been approved and is now in progress.` : `Requirement "${req.title}" has been rejected.${reason ? ` Reason: ${reason}` : ""}`;
72828
73531
  this.hitlService.notify({
72829
- targetUserId: "default",
73532
+ targetUserId: "all",
72830
73533
  type: "requirement_decision",
72831
73534
  title,
72832
73535
  body,
@@ -73031,7 +73734,7 @@ var init_knowledge_service = __esm({
73031
73734
  });
73032
73735
 
73033
73736
  // ../org-manager/dist/file-knowledge-store.js
73034
- import { readFileSync as readFileSync22, writeFileSync as writeFileSync17, existsSync as existsSync28, mkdirSync as mkdirSync20, readdirSync as readdirSync11, unlinkSync as unlinkSync3 } from "node:fs";
73737
+ import { readFileSync as readFileSync22, writeFileSync as writeFileSync18, existsSync as existsSync28, mkdirSync as mkdirSync20, readdirSync as readdirSync11, unlinkSync as unlinkSync3 } from "node:fs";
73035
73738
  import { join as join24 } from "node:path";
73036
73739
  function readdirSafe(dir) {
73037
73740
  try {
@@ -73087,12 +73790,12 @@ var init_file_knowledge_store = __esm({
73087
73790
  saveEntry(entry) {
73088
73791
  const dir = this.scopeDir(entry.scope, entry.scopeId);
73089
73792
  mkdirSync20(dir, { recursive: true });
73090
- writeFileSync17(join24(dir, `${entry.id}.json`), JSON.stringify(entry, null, 2));
73793
+ writeFileSync18(join24(dir, `${entry.id}.json`), JSON.stringify(entry, null, 2));
73091
73794
  }
73092
73795
  saveIndex(scope, scopeId, entries2) {
73093
73796
  const dir = this.scopeDir(scope, scopeId);
73094
73797
  mkdirSync20(dir, { recursive: true });
73095
- writeFileSync17(join24(dir, "_index.json"), JSON.stringify(entries2, null, 2));
73798
+ writeFileSync18(join24(dir, "_index.json"), JSON.stringify(entries2, null, 2));
73096
73799
  }
73097
73800
  removeEntryFile(entry) {
73098
73801
  const p = join24(this.scopeDir(entry.scope, entry.scopeId), `${entry.id}.json`);
@@ -74130,7 +74833,8 @@ function openSqlite(dbPath) {
74130
74833
  { table: "chat_sessions", column: "is_main", sql: "ALTER TABLE chat_sessions ADD COLUMN is_main INTEGER NOT NULL DEFAULT 0" },
74131
74834
  { table: "mailbox_items", column: "retry_count", sql: "ALTER TABLE mailbox_items ADD COLUMN retry_count INTEGER NOT NULL DEFAULT 0" },
74132
74835
  { table: "users", column: "avatar_url", sql: "ALTER TABLE users ADD COLUMN avatar_url TEXT" },
74133
- { table: "agents", column: "avatar_url", sql: "ALTER TABLE agents ADD COLUMN avatar_url TEXT" }
74836
+ { table: "agents", column: "avatar_url", sql: "ALTER TABLE agents ADD COLUMN avatar_url TEXT" },
74837
+ { table: "channel_messages", column: "reply_to_id", sql: "ALTER TABLE channel_messages ADD COLUMN reply_to_id TEXT" }
74134
74838
  ];
74135
74839
  for (const m of migrations) {
74136
74840
  const cols = _db.prepare(`PRAGMA table_info(${m.table})`).all();
@@ -74187,7 +74891,7 @@ function migrateToExecutionStreamLogs(db) {
74187
74891
  log71.info(`Migration: copied ${actLogCount} agent_activity_logs to execution_stream_logs`);
74188
74892
  }
74189
74893
  }
74190
- var log71, SCHEMA_SQL, _db, SqliteOrgRepo, SqliteAgentRepo, SqliteTaskRepo, SqliteRequirementRepo, SqliteProjectRepo, SqliteTaskLogRepo, SqliteTaskCommentRepo, SqliteRequirementCommentRepo, SqliteMessageRepo, SqliteChatSessionRepo, SqliteChannelMessageRepo, SqliteUserRepo, SqliteTeamRepo, SqliteMarketplaceTemplateRepo, SqliteMarketplaceSkillRepo, SqliteMarketplaceRatingRepo, SqliteAgentKnowledgeRepo, SqliteExternalAgentRepo, SqliteDeliverableRepo, SqliteActivityRepo, SqliteExecutionStreamRepo, SqliteMailboxRepo, SqliteDecisionRepo, SqliteNotificationRepo, SqliteApprovalRepo;
74894
+ var log71, SCHEMA_SQL, _db, SqliteOrgRepo, SqliteAgentRepo, SqliteTaskRepo, SqliteRequirementRepo, SqliteProjectRepo, SqliteTaskLogRepo, SqliteTaskCommentRepo, SqliteRequirementCommentRepo, SqliteMessageRepo, SqliteChatSessionRepo, SqliteChannelMessageRepo, SqliteUserRepo, SqliteTeamRepo, SqliteMarketplaceTemplateRepo, SqliteMarketplaceSkillRepo, SqliteMarketplaceRatingRepo, SqliteAgentKnowledgeRepo, SqliteExternalAgentRepo, SqliteDeliverableRepo, SqliteActivityRepo, SqliteExecutionStreamRepo, SqliteMailboxRepo, SqliteDecisionRepo, SqliteNotificationRepo, SqliteApprovalRepo, SqliteGroupChatRepo;
74191
74895
  var init_sqlite_storage = __esm({
74192
74896
  "../storage/dist/sqlite-storage.js"() {
74193
74897
  "use strict";
@@ -74325,6 +75029,7 @@ CREATE TABLE IF NOT EXISTS channel_messages (
74325
75029
  text TEXT NOT NULL,
74326
75030
  mentions TEXT NOT NULL DEFAULT '[]',
74327
75031
  metadata TEXT DEFAULT '{}',
75032
+ reply_to_id TEXT,
74328
75033
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
74329
75034
  );
74330
75035
  CREATE INDEX IF NOT EXISTS idx_channel_messages_channel ON channel_messages(channel, created_at);
@@ -74685,6 +75390,26 @@ CREATE TABLE IF NOT EXISTS approvals (
74685
75390
  selected_option TEXT
74686
75391
  );
74687
75392
  CREATE INDEX IF NOT EXISTS idx_approvals_status ON approvals(status, requested_at DESC);
75393
+
75394
+ CREATE TABLE IF NOT EXISTS group_chats (
75395
+ id TEXT PRIMARY KEY,
75396
+ org_id TEXT NOT NULL DEFAULT 'default',
75397
+ name TEXT NOT NULL,
75398
+ channel_key TEXT NOT NULL UNIQUE,
75399
+ creator_id TEXT NOT NULL,
75400
+ creator_name TEXT NOT NULL,
75401
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
75402
+ );
75403
+
75404
+ CREATE TABLE IF NOT EXISTS group_chat_members (
75405
+ group_chat_id TEXT NOT NULL REFERENCES group_chats(id) ON DELETE CASCADE,
75406
+ member_id TEXT NOT NULL,
75407
+ member_type TEXT NOT NULL CHECK(member_type IN ('human', 'agent')),
75408
+ member_name TEXT NOT NULL,
75409
+ added_at TEXT NOT NULL DEFAULT (datetime('now')),
75410
+ PRIMARY KEY (group_chat_id, member_id)
75411
+ );
75412
+ CREATE INDEX IF NOT EXISTS idx_group_chat_members_group ON group_chat_members(group_chat_id);
74688
75413
  `;
74689
75414
  _db = null;
74690
75415
  SqliteOrgRepo = class {
@@ -75553,7 +76278,7 @@ CREATE INDEX IF NOT EXISTS idx_approvals_status ON approvals(status, requested_a
75553
76278
  async append(data) {
75554
76279
  const id = generateId2("chm");
75555
76280
  const ts = now();
75556
- this.db.prepare("INSERT INTO channel_messages (id, org_id, channel, sender_id, sender_type, sender_name, text, mentions, metadata, created_at) VALUES (?,?,?,?,?,?,?,?,?,?)").run(id, data.orgId, data.channel, data.senderId, data.senderType, data.senderName, data.text, toJson(data.mentions ?? []), toJson(data.metadata ?? {}), ts);
76281
+ this.db.prepare("INSERT INTO channel_messages (id, org_id, channel, sender_id, sender_type, sender_name, text, mentions, metadata, reply_to_id, created_at) VALUES (?,?,?,?,?,?,?,?,?,?,?)").run(id, data.orgId, data.channel, data.senderId, data.senderType, data.senderName, data.text, toJson(data.mentions ?? []), toJson(data.metadata ?? {}), data.replyToId ?? null, ts);
75557
76282
  return {
75558
76283
  id,
75559
76284
  orgId: data.orgId,
@@ -75564,17 +76289,20 @@ CREATE INDEX IF NOT EXISTS idx_approvals_status ON approvals(status, requested_a
75564
76289
  text: data.text,
75565
76290
  mentions: data.mentions ?? [],
75566
76291
  metadata: data.metadata ?? null,
76292
+ replyToId: data.replyToId,
75567
76293
  createdAt: new Date(ts)
75568
76294
  };
75569
76295
  }
75570
76296
  getMessages(channel, limit = 50, before2) {
75571
- let q = "SELECT * FROM channel_messages WHERE channel = ?";
76297
+ let q = `SELECT m.*, r.sender_name AS reply_to_sender, SUBSTR(r.text, 1, 120) AS reply_to_text
76298
+ FROM channel_messages m LEFT JOIN channel_messages r ON m.reply_to_id = r.id
76299
+ WHERE m.channel = ?`;
75572
76300
  const vals = [channel];
75573
76301
  if (before2) {
75574
- q += " AND created_at < ?";
76302
+ q += " AND m.created_at < ?";
75575
76303
  vals.push(before2);
75576
76304
  }
75577
- q += " ORDER BY created_at DESC LIMIT ?";
76305
+ q += " ORDER BY m.created_at DESC LIMIT ?";
75578
76306
  vals.push(limit + 1);
75579
76307
  const rows = this.db.prepare(q).all(...vals).map((r) => ({
75580
76308
  id: r["id"],
@@ -75586,11 +76314,26 @@ CREATE INDEX IF NOT EXISTS idx_approvals_status ON approvals(status, requested_a
75586
76314
  text: r["text"],
75587
76315
  mentions: fromJson(r["mentions"]),
75588
76316
  metadata: fromJson(r["metadata"]),
76317
+ replyToId: r["reply_to_id"] ?? void 0,
76318
+ replyToSender: r["reply_to_sender"] ?? void 0,
76319
+ replyToText: r["reply_to_text"] ?? void 0,
75589
76320
  createdAt: toDate(r["created_at"])
75590
76321
  }));
75591
76322
  const hasMore = rows.length > limit;
75592
76323
  return { messages: rows.slice(0, limit).reverse(), hasMore };
75593
76324
  }
76325
+ getMessageById(id) {
76326
+ const r = this.db.prepare("SELECT * FROM channel_messages WHERE id = ?").get(id);
76327
+ if (!r)
76328
+ return void 0;
76329
+ return {
76330
+ id: r["id"],
76331
+ senderName: r["sender_name"],
76332
+ senderType: r["sender_type"],
76333
+ senderId: r["sender_id"],
76334
+ text: r["text"]
76335
+ };
76336
+ }
75594
76337
  };
75595
76338
  SqliteUserRepo = class {
75596
76339
  db;
@@ -76610,8 +77353,8 @@ CREATE INDEX IF NOT EXISTS idx_approvals_status ON approvals(status, requested_a
76610
77353
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(n.id, n.userId, n.type, n.title, n.body, n.priority, n.read ? 1 : 0, n.actionType, n.actionTarget, n.metadata ? JSON.stringify(n.metadata) : null, n.createdAt);
76611
77354
  }
76612
77355
  list(userId, opts) {
76613
- const conditions = ["(user_id = ? OR user_id = ?)"];
76614
- const params = [userId, "all"];
77356
+ const conditions = ["(user_id = ? OR user_id = ? OR user_id = ?)"];
77357
+ const params = [userId, "all", "default"];
76615
77358
  if (opts?.unreadOnly) {
76616
77359
  conditions.push("read = 0");
76617
77360
  }
@@ -76626,8 +77369,8 @@ CREATE INDEX IF NOT EXISTS idx_approvals_status ON approvals(status, requested_a
76626
77369
  return this.db.prepare(sql).all(...params).map((r) => this.mapRow(r));
76627
77370
  }
76628
77371
  count(userId, unreadOnly = false) {
76629
- const conditions = ["(user_id = ? OR user_id = ?)"];
76630
- const params = [userId, "all"];
77372
+ const conditions = ["(user_id = ? OR user_id = ? OR user_id = ?)"];
77373
+ const params = [userId, "all", "default"];
76631
77374
  if (unreadOnly)
76632
77375
  conditions.push("read = 0");
76633
77376
  const row = this.db.prepare(`SELECT COUNT(*) as cnt FROM user_notifications WHERE ${conditions.join(" AND ")}`).get(...params);
@@ -76638,7 +77381,7 @@ CREATE INDEX IF NOT EXISTS idx_approvals_status ON approvals(status, requested_a
76638
77381
  return info4.changes > 0;
76639
77382
  }
76640
77383
  markAllRead(userId) {
76641
- const info4 = this.db.prepare("UPDATE user_notifications SET read = 1 WHERE (user_id = ? OR user_id = ?) AND read = 0").run(userId, "all");
77384
+ const info4 = this.db.prepare("UPDATE user_notifications SET read = 1 WHERE (user_id = ? OR user_id = ? OR user_id = ?) AND read = 0").run(userId, "all", "default");
76642
77385
  return Number(info4.changes);
76643
77386
  }
76644
77387
  mapRow(r) {
@@ -76708,6 +77451,96 @@ CREATE INDEX IF NOT EXISTS idx_approvals_status ON approvals(status, requested_a
76708
77451
  };
76709
77452
  }
76710
77453
  };
77454
+ SqliteGroupChatRepo = class {
77455
+ db;
77456
+ constructor(db) {
77457
+ this.db = db;
77458
+ }
77459
+ create(data) {
77460
+ const id = generateId2("gc");
77461
+ const channelKey = `group:custom:${id}`;
77462
+ const ts = now();
77463
+ this.db.prepare("INSERT INTO group_chats (id, org_id, name, channel_key, creator_id, creator_name, created_at) VALUES (?,?,?,?,?,?,?)").run(id, data.orgId, data.name, channelKey, data.creatorId, data.creatorName, ts);
77464
+ const insertMember = this.db.prepare("INSERT OR IGNORE INTO group_chat_members (group_chat_id, member_id, member_type, member_name, added_at) VALUES (?,?,?,?,?)");
77465
+ const members = [];
77466
+ for (const m of data.members) {
77467
+ insertMember.run(id, m.id, m.type, m.name, ts);
77468
+ members.push({ groupChatId: id, memberId: m.id, memberType: m.type, memberName: m.name, addedAt: new Date(ts) });
77469
+ }
77470
+ return {
77471
+ id,
77472
+ orgId: data.orgId,
77473
+ name: data.name,
77474
+ channelKey,
77475
+ creatorId: data.creatorId,
77476
+ creatorName: data.creatorName,
77477
+ createdAt: new Date(ts),
77478
+ members
77479
+ };
77480
+ }
77481
+ list(orgId2) {
77482
+ const rows = this.db.prepare(`SELECT g.*, (SELECT COUNT(*) FROM group_chat_members WHERE group_chat_id = g.id) AS member_count
77483
+ FROM group_chats g WHERE g.org_id = ? ORDER BY g.created_at DESC`).all(orgId2);
77484
+ return rows.map((r) => ({
77485
+ ...this.mapRow(r),
77486
+ memberCount: r["member_count"]
77487
+ }));
77488
+ }
77489
+ getById(id) {
77490
+ const r = this.db.prepare("SELECT * FROM group_chats WHERE id = ?").get(id);
77491
+ if (!r)
77492
+ return void 0;
77493
+ const members = this.getMembers(id);
77494
+ return { ...this.mapRow(r), members };
77495
+ }
77496
+ getByChannelKey(channelKey) {
77497
+ const r = this.db.prepare("SELECT * FROM group_chats WHERE channel_key = ?").get(channelKey);
77498
+ if (!r)
77499
+ return void 0;
77500
+ const gc = this.mapRow(r);
77501
+ return { ...gc, members: this.getMembers(gc.id) };
77502
+ }
77503
+ getMembers(groupChatId) {
77504
+ const rows = this.db.prepare("SELECT * FROM group_chat_members WHERE group_chat_id = ?").all(groupChatId);
77505
+ return rows.map((r) => ({
77506
+ groupChatId: r["group_chat_id"],
77507
+ memberId: r["member_id"],
77508
+ memberType: r["member_type"],
77509
+ memberName: r["member_name"],
77510
+ addedAt: toDate(r["added_at"])
77511
+ }));
77512
+ }
77513
+ getAgentMemberIds(channelKey) {
77514
+ const gc = this.db.prepare("SELECT id FROM group_chats WHERE channel_key = ?").get(channelKey);
77515
+ if (!gc)
77516
+ return [];
77517
+ const rows = this.db.prepare("SELECT member_id FROM group_chat_members WHERE group_chat_id = ? AND member_type = 'agent'").all(gc.id);
77518
+ return rows.map((r) => r.member_id);
77519
+ }
77520
+ addMember(groupChatId, memberId, memberType, memberName) {
77521
+ this.db.prepare("INSERT OR IGNORE INTO group_chat_members (group_chat_id, member_id, member_type, member_name, added_at) VALUES (?,?,?,?,?)").run(groupChatId, memberId, memberType, memberName, now());
77522
+ }
77523
+ removeMember(groupChatId, memberId) {
77524
+ this.db.prepare("DELETE FROM group_chat_members WHERE group_chat_id = ? AND member_id = ?").run(groupChatId, memberId);
77525
+ }
77526
+ updateName(id, name) {
77527
+ this.db.prepare("UPDATE group_chats SET name = ? WHERE id = ?").run(name, id);
77528
+ }
77529
+ delete(id) {
77530
+ this.db.prepare("DELETE FROM group_chats WHERE id = ?").run(id);
77531
+ }
77532
+ mapRow(r) {
77533
+ return {
77534
+ id: r["id"],
77535
+ orgId: r["org_id"],
77536
+ name: r["name"],
77537
+ channelKey: r["channel_key"],
77538
+ creatorId: r["creator_id"],
77539
+ creatorName: r["creator_name"],
77540
+ createdAt: toDate(r["created_at"])
77541
+ };
77542
+ }
77543
+ };
76711
77544
  }
76712
77545
  });
76713
77546
 
@@ -76724,6 +77557,7 @@ __export(dist_exports3, {
76724
77557
  SqliteDeliverableRepo: () => SqliteDeliverableRepo,
76725
77558
  SqliteExecutionStreamRepo: () => SqliteExecutionStreamRepo,
76726
77559
  SqliteExternalAgentRepo: () => SqliteExternalAgentRepo,
77560
+ SqliteGroupChatRepo: () => SqliteGroupChatRepo,
76727
77561
  SqliteMailboxRepo: () => SqliteMailboxRepo,
76728
77562
  SqliteMarketplaceRatingRepo: () => SqliteMarketplaceRatingRepo,
76729
77563
  SqliteMarketplaceSkillRepo: () => SqliteMarketplaceSkillRepo,
@@ -76793,7 +77627,8 @@ async function initSqliteStorage(url) {
76793
77627
  mailboxRepo: new storage.SqliteMailboxRepo(db),
76794
77628
  decisionRepo: new storage.SqliteDecisionRepo(db),
76795
77629
  notificationRepo: new storage.SqliteNotificationRepo(db),
76796
- approvalRepo: new storage.SqliteApprovalRepo(db)
77630
+ approvalRepo: new storage.SqliteApprovalRepo(db),
77631
+ groupChatRepo: new storage.SqliteGroupChatRepo(db)
76797
77632
  };
76798
77633
  log72.info("SQLite storage initialized", { path: dbPath });
76799
77634
  return bridge;
@@ -77884,8 +78719,8 @@ var init_startupProgress = __esm({
77884
78719
  });
77885
78720
 
77886
78721
  // src/connector-service.ts
77887
- import { resolve as resolve15, join as join28, dirname as dirname8 } from "node:path";
77888
- import { existsSync as existsSync31, readFileSync as readFileSync23, writeFileSync as writeFileSync18, mkdirSync as mkdirSync24, readdirSync as readdirSync12, cpSync as cpSync3 } from "node:fs";
78722
+ import { resolve as resolve14, join as join28, dirname as dirname8 } from "node:path";
78723
+ import { existsSync as existsSync31, readFileSync as readFileSync23, writeFileSync as writeFileSync19, mkdirSync as mkdirSync24, readdirSync as readdirSync12, cpSync as cpSync3 } from "node:fs";
77889
78724
  import { homedir as homedir18 } from "node:os";
77890
78725
  import { execSync as execSync4 } from "node:child_process";
77891
78726
  import { fileURLToPath as fileURLToPath4 } from "node:url";
@@ -77894,9 +78729,9 @@ function expandHome(p) {
77894
78729
  }
77895
78730
  function loadConnectors() {
77896
78731
  const connectors = /* @__PURE__ */ new Map();
77897
- const builtinDir = resolve15(__dirname3, "..", "connectors");
78732
+ const builtinDir = resolve14(__dirname3, "..", "connectors");
77898
78733
  loadFromDir(builtinDir, connectors);
77899
- const devDir = resolve15(process.cwd(), "packages", "cli", "connectors");
78734
+ const devDir = resolve14(process.cwd(), "packages", "cli", "connectors");
77900
78735
  if (devDir !== builtinDir) loadFromDir(devDir, connectors);
77901
78736
  const userDir = join28(homedir18(), ".markus", "connectors");
77902
78737
  loadFromDir(userDir, connectors);
@@ -77986,7 +78821,7 @@ function writePlatformConfig(connector, markusUrl, token) {
77986
78821
  setNestedField(config, connector.integration.tokenField, token);
77987
78822
  try {
77988
78823
  const content = JSON.stringify(config, null, 2);
77989
- writeFileSync18(configPath, content, "utf-8");
78824
+ writeFileSync19(configPath, content, "utf-8");
77990
78825
  return true;
77991
78826
  } catch {
77992
78827
  return false;
@@ -78000,8 +78835,8 @@ function installSkillTemplate(connector) {
78000
78835
  const templateName = connector.integration.skillTemplateName;
78001
78836
  const candidates = [
78002
78837
  join28(homedir18(), ".markus", "templates", templateName),
78003
- resolve15(process.cwd(), "templates", templateName),
78004
- resolve15(__dirname3, "..", "templates", templateName)
78838
+ resolve14(process.cwd(), "templates", templateName),
78839
+ resolve14(__dirname3, "..", "templates", templateName)
78005
78840
  ];
78006
78841
  let sourceDir;
78007
78842
  for (const c of candidates) {
@@ -78065,11 +78900,11 @@ __export(init_exports, {
78065
78900
  quickInit: () => quickInit,
78066
78901
  registerInitCommand: () => registerInitCommand
78067
78902
  });
78068
- import { resolve as resolve16 } from "node:path";
78903
+ import { resolve as resolve15 } from "node:path";
78069
78904
  import { readFileSync as readFileSync24, existsSync as existsSync32, cpSync as cpSync4 } from "node:fs";
78070
78905
  import { homedir as homedir19 } from "node:os";
78071
78906
  function registerInitCommand(program2) {
78072
- program2.command("init").description("Setup wizard: configure LLM provider, API keys, and server settings").option("--force", "Overwrite existing configuration").option("--non-interactive", "Run without prompts (use env vars or --import-from)").option("--provider <name>", "LLM provider (anthropic/openai/google/minimax/siliconflow/zai/ollama)").option("--api-key <key>", "LLM API key").option("--port <port>", "API server port", "8056").option("--import-from <platform>", "Import LLM config from an installed agent platform (e.g. openclaw, hermes)").option("--auto-connect", "Auto-connect detected agent platforms after init").action(async (opts) => {
78907
+ program2.command("init").description("Setup wizard: configure LLM provider, API keys, and server settings").option("--force", "Overwrite existing configuration").option("--non-interactive", "Run without prompts (use env vars or --import-from)").option("--provider <name>", "LLM provider (anthropic/openai/google/minimax/siliconflow/zai/deepseek/ollama)").option("--api-key <key>", "LLM API key").option("--port <port>", "API server port", "8056").option("--import-from <platform>", "Import LLM config from an installed agent platform (e.g. openclaw, hermes)").option("--auto-connect", "Auto-connect detected agent platforms after init").action(async (opts) => {
78073
78908
  await quickInit({
78074
78909
  force: opts.force,
78075
78910
  nonInteractive: opts.nonInteractive,
@@ -78082,7 +78917,7 @@ function registerInitCommand(program2) {
78082
78917
  });
78083
78918
  }
78084
78919
  async function quickInit(options) {
78085
- const { writeFileSync: writeFileSync20, mkdirSync: mkdirSync26 } = await import("node:fs");
78920
+ const { writeFileSync: writeFileSync21, mkdirSync: mkdirSync26 } = await import("node:fs");
78086
78921
  const { join: pathJoin } = await import("node:path");
78087
78922
  const readline4 = await import("node:readline");
78088
78923
  const nonInteractive = options?.nonInteractive ?? false;
@@ -78126,7 +78961,8 @@ async function quickInit(options) {
78126
78961
  siliconflow: "Qwen/Qwen3.5-35B-A3B",
78127
78962
  ollama: "llama3",
78128
78963
  openrouter: "xiaomi/mimo-v2-pro",
78129
- zai: "glm-5.1"
78964
+ zai: "glm-5.1",
78965
+ deepseek: "deepseek-v4-flash"
78130
78966
  };
78131
78967
  const ENV_KEY_MAP = [
78132
78968
  { provider: "anthropic", label: "Anthropic", envKey: "ANTHROPIC_API_KEY" },
@@ -78135,7 +78971,8 @@ async function quickInit(options) {
78135
78971
  { provider: "siliconflow", label: "SiliconFlow", envKey: "SILICONFLOW_API_KEY", baseUrl: "https://api.siliconflow.cn/v1" },
78136
78972
  { provider: "minimax", label: "MiniMax", envKey: "MINIMAX_API_KEY", baseUrl: "https://api.minimax.io/v1" },
78137
78973
  { provider: "openrouter", label: "OpenRouter", envKey: "OPENROUTER_API_KEY", baseUrl: "https://openrouter.ai/api/v1" },
78138
- { provider: "zai", label: "ZAI", envKey: "ZAI_API_KEY", baseUrl: "https://api.z.ai/api/paas/v4" }
78974
+ { provider: "zai", label: "ZAI", envKey: "ZAI_API_KEY", baseUrl: "https://api.z.ai/api/paas/v4" },
78975
+ { provider: "deepseek", label: "DeepSeek", envKey: "DEEPSEEK_API_KEY", baseUrl: "https://api.deepseek.com" }
78139
78976
  ];
78140
78977
  console.log(" [1/3] LLM Provider Configuration\n");
78141
78978
  const envProviders = [];
@@ -78294,7 +79131,8 @@ async function quickInit(options) {
78294
79131
  const baseUrlMap = {
78295
79132
  minimax: "https://api.minimax.io/v1",
78296
79133
  siliconflow: "https://api.siliconflow.cn/v1",
78297
- zai: "https://api.z.ai/api/paas/v4"
79134
+ zai: "https://api.z.ai/api/paas/v4",
79135
+ deepseek: "https://api.deepseek.com"
78298
79136
  };
78299
79137
  providers[provider] = {
78300
79138
  ...apiKey ? { apiKey } : {},
@@ -78307,7 +79145,7 @@ async function quickInit(options) {
78307
79145
  console.log(" Warning: No API key provided. Configure manually in ~/.markus/markus.json");
78308
79146
  }
78309
79147
  } else {
78310
- const provider = await ask(" LLM provider (anthropic/openai/google/minimax/siliconflow/zai/ollama)", "anthropic");
79148
+ const provider = await ask(" LLM provider (anthropic/openai/google/minimax/siliconflow/zai/deepseek/ollama)", "anthropic");
78311
79149
  defaultProvider = provider;
78312
79150
  let apiKey = "";
78313
79151
  if (provider !== "ollama") {
@@ -78317,7 +79155,8 @@ async function quickInit(options) {
78317
79155
  const baseUrlMap = {
78318
79156
  minimax: "https://api.minimax.io/v1",
78319
79157
  siliconflow: "https://api.siliconflow.cn/v1",
78320
- zai: "https://api.z.ai/api/paas/v4"
79158
+ zai: "https://api.z.ai/api/paas/v4",
79159
+ deepseek: "https://api.deepseek.com"
78321
79160
  };
78322
79161
  providers[provider] = {
78323
79162
  ...apiKey ? { apiKey } : {},
@@ -78353,7 +79192,7 @@ async function quickInit(options) {
78353
79192
  const userTemplatesDir = pathJoin(homedir19(), ".markus", "templates");
78354
79193
  const builtinTemplatesDir = resolveTemplatesDir("roles");
78355
79194
  if (builtinTemplatesDir && existsSync32(builtinTemplatesDir) && !existsSync32(userTemplatesDir)) {
78356
- const builtinRoot = resolve16(builtinTemplatesDir, "..");
79195
+ const builtinRoot = resolve15(builtinTemplatesDir, "..");
78357
79196
  mkdirSync26(userTemplatesDir, { recursive: true });
78358
79197
  cpSync4(builtinRoot, userTemplatesDir, { recursive: true });
78359
79198
  console.log(` Copied templates to ${userTemplatesDir}`);
@@ -78361,7 +79200,7 @@ async function quickInit(options) {
78361
79200
  const devRoleDir = pathJoin(userTemplatesDir || pathJoin(process.cwd(), "templates"), "roles", "developer");
78362
79201
  if (!existsSync32(devRoleDir)) {
78363
79202
  mkdirSync26(devRoleDir, { recursive: true });
78364
- writeFileSync20(
79203
+ writeFileSync21(
78365
79204
  pathJoin(devRoleDir, "ROLE.md"),
78366
79205
  [
78367
79206
  "---",
@@ -78421,7 +79260,7 @@ var start_exports = {};
78421
79260
  __export(start_exports, {
78422
79261
  registerStartCommand: () => registerStartCommand
78423
79262
  });
78424
- import { resolve as resolve17, join as join29, dirname as dirname9 } from "node:path";
79263
+ import { resolve as resolve16, join as join29, dirname as dirname9 } from "node:path";
78425
79264
  import { existsSync as existsSync33 } from "node:fs";
78426
79265
  import { homedir as homedir20 } from "node:os";
78427
79266
  function registerStartCommand(program2) {
@@ -78516,6 +79355,19 @@ async function createServices(config) {
78516
79355
  defaultProvider = "zai";
78517
79356
  }
78518
79357
  }
79358
+ const deepseekKey = config.llm.providers["deepseek"]?.apiKey ?? process.env["DEEPSEEK_API_KEY"];
79359
+ if (deepseekKey) {
79360
+ providerConfigs["deepseek"] = {
79361
+ provider: "deepseek",
79362
+ model: config.llm.providers["deepseek"]?.model ?? process.env["DEEPSEEK_MODEL"] ?? "deepseek-v4-flash",
79363
+ apiKey: deepseekKey,
79364
+ baseUrl: config.llm.providers["deepseek"]?.baseUrl ?? process.env["DEEPSEEK_BASE_URL"] ?? "https://api.deepseek.com",
79365
+ timeoutMs: llmTimeoutMs
79366
+ };
79367
+ if (config.llm.defaultProvider === "deepseek") {
79368
+ defaultProvider = "deepseek";
79369
+ }
79370
+ }
78519
79371
  if (!providerConfigs[defaultProvider]) {
78520
79372
  const available = Object.keys(providerConfigs);
78521
79373
  if (available.length > 0) {
@@ -78659,7 +79511,7 @@ async function startServer(config, values) {
78659
79511
  startupLog("INFO", "\u6B63\u5728\u542F\u52A8\u670D\u52A1...");
78660
79512
  const currentPath = process.env["PATH"] ?? "";
78661
79513
  const extraPaths = [];
78662
- const selfBinDir = dirname9(resolve17(process.argv[1] ?? ""));
79514
+ const selfBinDir = dirname9(resolve16(process.argv[1] ?? ""));
78663
79515
  if (selfBinDir && !currentPath.includes(selfBinDir)) extraPaths.push(selfBinDir);
78664
79516
  const cwdBin = join29(process.cwd(), "node_modules", ".bin");
78665
79517
  if (existsSync33(cwdBin) && !currentPath.includes(cwdBin)) extraPaths.push(cwdBin);
@@ -78801,7 +79653,7 @@ async function startServer(config, values) {
78801
79653
  });
78802
79654
  agentManager.setUserNotifier((opts) => {
78803
79655
  hitlService.notify({
78804
- targetUserId: "default",
79656
+ targetUserId: "all",
78805
79657
  type: opts.type,
78806
79658
  title: opts.title,
78807
79659
  body: opts.body,
@@ -78868,7 +79720,7 @@ ${body}`;
78868
79720
  });
78869
79721
  const hasTask = !!taskId2;
78870
79722
  hitlService.notify({
78871
- targetUserId: "default",
79723
+ targetUserId: "all",
78872
79724
  type: "agent_report",
78873
79725
  title,
78874
79726
  body,
@@ -78902,7 +79754,7 @@ ${reason}`;
78902
79754
  isMainSession: true
78903
79755
  });
78904
79756
  hitlService.notify({
78905
- targetUserId: "default",
79757
+ targetUserId: "all",
78906
79758
  type: "system",
78907
79759
  title: "Agent needs help",
78908
79760
  body: reason,
@@ -79269,6 +80121,19 @@ ${reason}`;
79269
80121
  }, triageProvider);
79270
80122
  return response.content;
79271
80123
  });
80124
+ agent.getAttentionController().setLLMJudge(async (prompt) => {
80125
+ const response = await llmRouter.chat({
80126
+ messages: [
80127
+ { role: "system", content: 'You are an attention interrupt judge. Decide whether to interrupt current work for a new incoming message. Reply with ONLY one word: continue, preempt, cancel, merge, or defer. Use "preempt" to pause (resume later) and "cancel" to permanently stop current work.' },
80128
+ { role: "user", content: prompt }
80129
+ ],
80130
+ temperature: 0.1,
80131
+ maxTokens: 32
80132
+ }, triageProvider);
80133
+ const raw = response.content.trim().toLowerCase();
80134
+ const valid = ["continue", "preempt", "cancel", "merge", "defer"];
80135
+ return valid.includes(raw) ? raw : "continue";
80136
+ });
79272
80137
  agent.getAttentionController().setTriageChatFn(async (messages, tools) => {
79273
80138
  const llmTools = tools?.map((t) => ({
79274
80139
  name: t.name,
@@ -79281,7 +80146,7 @@ ${reason}`;
79281
80146
  temperature: TRIAGE_TEMPERATURE,
79282
80147
  maxTokens: TRIAGE_MAX_TOKENS
79283
80148
  }, triageProvider);
79284
- return { content: response.content, toolCalls: response.toolCalls };
80149
+ return { content: response.content, toolCalls: response.toolCalls, reasoningContent: response.reasoningContent };
79285
80150
  });
79286
80151
  const triageToolMap = /* @__PURE__ */ new Map();
79287
80152
  const agentTools = agent.getTools();
@@ -79996,9 +80861,9 @@ var init_model = __esm({
79996
80861
  id: "deepseek",
79997
80862
  label: "DeepSeek",
79998
80863
  envKey: "DEEPSEEK_API_KEY",
79999
- baseUrl: "https://api.deepseek.com/v1",
80000
- defaultModel: "deepseek-chat",
80001
- models: ["deepseek-chat", "deepseek-coder", "deepseek-chat-v3"]
80864
+ baseUrl: "https://api.deepseek.com",
80865
+ defaultModel: "deepseek-v4-flash",
80866
+ models: ["deepseek-v4-flash", "deepseek-v4-pro", "deepseek-chat", "deepseek-reasoner"]
80002
80867
  }
80003
80868
  ];
80004
80869
  C = {
@@ -80295,6 +81160,12 @@ ${C3.BOLD}${C3.CYAN}\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
80295
81160
  checkWarn("Skills directory not found", `${skillsDir} \u2014 run: markus skill install <name>`);
80296
81161
  }
80297
81162
  section("Network");
81163
+ const proxyUrl = process.env["HTTPS_PROXY"] || process.env["HTTP_PROXY"] || process.env["https_proxy"] || process.env["http_proxy"];
81164
+ if (proxyUrl) {
81165
+ checkOk("HTTP proxy configured", proxyUrl);
81166
+ } else {
81167
+ checkWarn("HTTP proxy", "not configured \u2014 if behind firewall, set HTTPS_PROXY env var");
81168
+ }
80298
81169
  try {
80299
81170
  const res = await fetch("https://api.anthropic.com/.well-known/ready", {
80300
81171
  method: "HEAD",
@@ -80387,7 +81258,7 @@ var init_doctor = __esm({
80387
81258
  { id: "siliconflow", label: "SiliconFlow", envKey: "SILICONFLOW_API_KEY", baseUrl: "https://api.siliconflow.cn/v1", defaultModel: "Qwen/Qwen3.5-35B-A3B" },
80388
81259
  { id: "openrouter", label: "OpenRouter", envKey: "OPENROUTER_API_KEY", baseUrl: "https://openrouter.ai/api/v1", defaultModel: "xiaomi/mimo-v2-pro:free" },
80389
81260
  { id: "zai", label: "ZAI", envKey: "ZAI_API_KEY", baseUrl: "https://api.z.ai/api/paas/v4", defaultModel: "glm-5.1" },
80390
- { id: "deepseek", label: "DeepSeek", envKey: "DEEPSEEK_API_KEY", baseUrl: "https://api.deepseek.com/v1", defaultModel: "deepseek-chat" }
81261
+ { id: "deepseek", label: "DeepSeek", envKey: "DEEPSEEK_API_KEY", baseUrl: "https://api.deepseek.com", defaultModel: "deepseek-v4-flash" }
80391
81262
  ];
80392
81263
  }
80393
81264
  });
@@ -80775,7 +81646,7 @@ var init_auth = __esm({
80775
81646
  { id: "siliconflow", label: "SiliconFlow", envKey: "SILICONFLOW_API_KEY", baseUrl: "https://api.siliconflow.cn/v1", defaultModel: "Qwen/Qwen3.5-35B-A3B" },
80776
81647
  { id: "openrouter", label: "OpenRouter", envKey: "OPENROUTER_API_KEY", baseUrl: "https://openrouter.ai/api/v1", defaultModel: "xiaomi/mimo-v2-pro:free" },
80777
81648
  { id: "zai", label: "ZAI", envKey: "ZAI_API_KEY", baseUrl: "https://api.z.ai/api/paas/v4", defaultModel: "glm-5.1" },
80778
- { id: "deepseek", label: "DeepSeek", envKey: "DEEPSEEK_API_KEY", defaultModel: "deepseek-chat" }
81649
+ { id: "deepseek", label: "DeepSeek", envKey: "DEEPSEEK_API_KEY", baseUrl: "https://api.deepseek.com", defaultModel: "deepseek-v4-flash" }
80779
81650
  ];
80780
81651
  PLACEHOLDER_PATTERNS3 = ["***", "your-", "dummy", "fake", "test-key", "replace-me"];
80781
81652
  }
@@ -82976,8 +83847,8 @@ var skill_exports = {};
82976
83847
  __export(skill_exports, {
82977
83848
  registerSkillCommands: () => registerSkillCommands
82978
83849
  });
82979
- import { writeFileSync as writeFileSync19, mkdirSync as mkdirSync25 } from "node:fs";
82980
- import { resolve as resolve18 } from "node:path";
83850
+ import { writeFileSync as writeFileSync20, mkdirSync as mkdirSync25 } from "node:fs";
83851
+ import { resolve as resolve17 } from "node:path";
82981
83852
  function registerSkillCommands(program2) {
82982
83853
  const root = program2.command("skill").description("Skills registry and local scaffold");
82983
83854
  root.command("list").action(async (_opts, cmd) => {
@@ -83065,7 +83936,7 @@ function registerSkillCommands(program2) {
83065
83936
  const g = cmd.optsWithGlobals();
83066
83937
  const out = { json: !!g.json };
83067
83938
  if (!opts.dir && !opts.name) fail("Specify --dir or --name");
83068
- const dir = resolve18(process.cwd(), opts.dir ?? opts.name);
83939
+ const dir = resolve17(process.cwd(), opts.dir ?? opts.name);
83069
83940
  mkdirSync25(dir, { recursive: true });
83070
83941
  const skillName = opts.name ?? (opts.dir ? opts.dir.replace(/.*[/\\]/, "") : "my-skill");
83071
83942
  const skillJson = {
@@ -83084,8 +83955,8 @@ Describe what this skill does for agents.
83084
83955
  ## Tools / behavior
83085
83956
 
83086
83957
  `;
83087
- writeFileSync19(resolve18(dir, "skill.json"), JSON.stringify(skillJson, null, 2) + "\n");
83088
- writeFileSync19(resolve18(dir, "SKILL.md"), skillMd);
83958
+ writeFileSync20(resolve17(dir, "skill.json"), JSON.stringify(skillJson, null, 2) + "\n");
83959
+ writeFileSync20(resolve17(dir, "SKILL.md"), skillMd);
83089
83960
  if (out.json) {
83090
83961
  console.log(JSON.stringify({ dir, files: ["skill.json", "SKILL.md"] }, null, 2));
83091
83962
  } else {
@@ -83109,12 +83980,12 @@ __export(system_exports, {
83109
83980
  });
83110
83981
  import { execSync as execSync6 } from "node:child_process";
83111
83982
  import { existsSync as existsSync34, readFileSync as readFileSync25 } from "node:fs";
83112
- import { resolve as resolve19, dirname as dirname10 } from "node:path";
83983
+ import { resolve as resolve18, dirname as dirname10 } from "node:path";
83113
83984
  import { fileURLToPath as fileURLToPath5 } from "node:url";
83114
83985
  function findMarkusRoot() {
83115
83986
  let dir = dirname10(fileURLToPath5(import.meta.url));
83116
83987
  for (let i = 0; i < 10; i++) {
83117
- if (existsSync34(resolve19(dir, "package.json")) && existsSync34(resolve19(dir, "packages"))) return dir;
83988
+ if (existsSync34(resolve18(dir, "package.json")) && existsSync34(resolve18(dir, "packages"))) return dir;
83118
83989
  dir = dirname10(dir);
83119
83990
  }
83120
83991
  return null;
@@ -83255,13 +84126,13 @@ function registerSystemCommands(program2) {
83255
84126
  if (markusRoot) {
83256
84127
  if (!info4.currentVersion) {
83257
84128
  try {
83258
- const pkg = JSON.parse(readFileSync25(resolve19(markusRoot, "package.json"), "utf-8"));
84129
+ const pkg = JSON.parse(readFileSync25(resolve18(markusRoot, "package.json"), "utf-8"));
83259
84130
  info4.currentVersion = pkg.version;
83260
84131
  } catch {
83261
84132
  }
83262
84133
  }
83263
84134
  try {
83264
- const isGit = existsSync34(resolve19(markusRoot, ".git"));
84135
+ const isGit = existsSync34(resolve18(markusRoot, ".git"));
83265
84136
  if (isGit) {
83266
84137
  info4.gitBranch = execSync6("git rev-parse --abbrev-ref HEAD", { cwd: markusRoot, encoding: "utf-8" }).trim();
83267
84138
  info4.gitCommit = execSync6("git rev-parse --short HEAD", { cwd: markusRoot, encoding: "utf-8" }).trim();
@@ -83300,7 +84171,7 @@ function registerSystemCommands(program2) {
83300
84171
  fail("Cannot locate Markus installation directory");
83301
84172
  return;
83302
84173
  }
83303
- if (!existsSync34(resolve19(markusRoot, ".git"))) {
84174
+ if (!existsSync34(resolve18(markusRoot, ".git"))) {
83304
84175
  fail("Markus installation is not a git repository. Update manually.");
83305
84176
  return;
83306
84177
  }
@@ -83865,7 +84736,7 @@ var init_settings2 = __esm({
83865
84736
  });
83866
84737
 
83867
84738
  // src/index.ts
83868
- import { resolve as resolve20 } from "node:path";
84739
+ import { resolve as resolve19 } from "node:path";
83869
84740
  import { readFileSync as readFileSync26, existsSync as existsSync35 } from "node:fs";
83870
84741
  import process2 from "node:process";
83871
84742
 
@@ -83889,7 +84760,7 @@ var {
83889
84760
  // src/index.ts
83890
84761
  init_dist();
83891
84762
  init_output();
83892
- var envPath = resolve20(process2.cwd(), ".env");
84763
+ var envPath = resolve19(process2.cwd(), ".env");
83893
84764
  if (existsSync35(envPath)) {
83894
84765
  for (const line of readFileSync26(envPath, "utf-8").split("\n")) {
83895
84766
  const trimmed = line.trim();