@markus-global/cli 0.4.21 → 0.4.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +8 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +16 -0
- package/dist/commands/start.js.map +1 -1
- package/dist/markus.mjs +1157 -299
- package/dist/web-ui/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/dist/web-ui/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/dist/web-ui/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/dist/web-ui/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/dist/web-ui/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/dist/web-ui/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/dist/web-ui/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/dist/web-ui/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/dist/web-ui/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/dist/web-ui/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/dist/web-ui/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/dist/web-ui/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/dist/web-ui/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/dist/web-ui/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/dist/web-ui/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/dist/web-ui/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/dist/web-ui/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/dist/web-ui/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/dist/web-ui/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/dist/web-ui/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/dist/web-ui/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/dist/web-ui/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/dist/web-ui/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/dist/web-ui/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/dist/web-ui/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/dist/web-ui/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/dist/web-ui/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/dist/web-ui/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/dist/web-ui/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/dist/web-ui/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/dist/web-ui/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/dist/web-ui/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/dist/web-ui/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/dist/web-ui/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/dist/web-ui/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/dist/web-ui/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/dist/web-ui/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/dist/web-ui/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/dist/web-ui/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/dist/web-ui/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/dist/web-ui/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/dist/web-ui/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/dist/web-ui/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/dist/web-ui/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/dist/web-ui/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/dist/web-ui/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/dist/web-ui/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/dist/web-ui/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/dist/web-ui/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/dist/web-ui/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/dist/web-ui/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/dist/web-ui/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/dist/web-ui/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/dist/web-ui/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/dist/web-ui/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/dist/web-ui/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/dist/web-ui/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/dist/web-ui/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/dist/web-ui/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/dist/web-ui/assets/index-FxtMolbG.js +343 -0
- package/dist/web-ui/assets/index-jBDtl4o2.css +1 -0
- package/dist/web-ui/index.html +2 -2
- package/package.json +1 -1
- package/templates/roles/org-manager/HEARTBEAT.md +8 -0
- package/templates/roles/secretary/HEARTBEAT.md +8 -0
- package/templates/skills/agent-building/SKILL.md +19 -10
- package/templates/skills/self-evolution/SKILL.md +43 -7
- package/templates/skills/team-building/SKILL.md +26 -10
- package/dist/web-ui/assets/index-BWjiUsem.js +0 -66
- package/dist/web-ui/assets/index-BgcwiZ1h.css +0 -1
package/dist/markus.mjs
CHANGED
|
@@ -3566,7 +3566,7 @@ function buildManifest(type, raw) {
|
|
|
3566
3566
|
if (type === "agent") {
|
|
3567
3567
|
const agentRaw = raw.agent ?? raw;
|
|
3568
3568
|
base.agent = {
|
|
3569
|
-
roleName: agentRaw.roleName
|
|
3569
|
+
roleName: agentRaw.roleName || raw.roleName || void 0,
|
|
3570
3570
|
agentRole: agentRaw.agentRole ?? raw.agentRole ?? "worker",
|
|
3571
3571
|
llmProvider: agentRaw.llmProvider || raw.llmProvider || void 0,
|
|
3572
3572
|
llmModel: agentRaw.llmModel || raw.llmModel || void 0,
|
|
@@ -3585,7 +3585,7 @@ function buildManifest(type, raw) {
|
|
|
3585
3585
|
members: rawMembers.map((m) => ({
|
|
3586
3586
|
name: m.name ?? "Agent",
|
|
3587
3587
|
role: m.role ?? "worker",
|
|
3588
|
-
roleName: m.roleName
|
|
3588
|
+
roleName: m.roleName || void 0,
|
|
3589
3589
|
count: m.count ?? 1,
|
|
3590
3590
|
skills: toArr(m.skills).length > 0 ? toArr(m.skills) : void 0
|
|
3591
3591
|
}))
|
|
@@ -3652,8 +3652,8 @@ function validateManifest(m) {
|
|
|
3652
3652
|
errors.push("version must be semver (e.g. 1.0.0)");
|
|
3653
3653
|
if (o.type === "agent" && o.agent) {
|
|
3654
3654
|
const a = o.agent;
|
|
3655
|
-
if (
|
|
3656
|
-
errors.push("agent.roleName
|
|
3655
|
+
if (a.roleName !== void 0 && typeof a.roleName !== "string")
|
|
3656
|
+
errors.push("agent.roleName must be a string if provided");
|
|
3657
3657
|
}
|
|
3658
3658
|
if (o.type === "team" && o.team) {
|
|
3659
3659
|
const t = o.team;
|
|
@@ -3662,10 +3662,12 @@ function validateManifest(m) {
|
|
|
3662
3662
|
}
|
|
3663
3663
|
return errors;
|
|
3664
3664
|
}
|
|
3665
|
-
function kebab(s) {
|
|
3665
|
+
function kebab(s, fallback) {
|
|
3666
3666
|
const result = s.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/^-+|-+$/g, "");
|
|
3667
3667
|
if (result)
|
|
3668
3668
|
return result;
|
|
3669
|
+
if (fallback)
|
|
3670
|
+
return fallback;
|
|
3669
3671
|
let hash = 0;
|
|
3670
3672
|
for (let i = 0; i < s.length; i++)
|
|
3671
3673
|
hash = (hash << 5) - hash + s.charCodeAt(i) | 0;
|
|
@@ -4409,6 +4411,7 @@ __export(dist_exports, {
|
|
|
4409
4411
|
getTextContent: () => getTextContent,
|
|
4410
4412
|
isPlaceholder: () => isPlaceholder,
|
|
4411
4413
|
isValidTaskTransition: () => isValidTaskTransition,
|
|
4414
|
+
kebab: () => kebab,
|
|
4412
4415
|
loadConfig: () => loadConfig,
|
|
4413
4416
|
manifestFilename: () => manifestFilename,
|
|
4414
4417
|
msgId: () => msgId,
|
|
@@ -41152,13 +41155,41 @@ var init_web_fetch = __esm({
|
|
|
41152
41155
|
});
|
|
41153
41156
|
|
|
41154
41157
|
// ../core/dist/tools/web-search.js
|
|
41158
|
+
async function resolveProxyDispatcher() {
|
|
41159
|
+
if (_dispatcher !== false)
|
|
41160
|
+
return _dispatcher || void 0;
|
|
41161
|
+
const proxyUrl = process.env["HTTPS_PROXY"] || process.env["HTTP_PROXY"] || process.env["https_proxy"] || process.env["http_proxy"];
|
|
41162
|
+
if (!proxyUrl) {
|
|
41163
|
+
_dispatcher = void 0;
|
|
41164
|
+
return void 0;
|
|
41165
|
+
}
|
|
41166
|
+
try {
|
|
41167
|
+
const load = new Function("id", "return import(id)");
|
|
41168
|
+
const { ProxyAgent } = await load("undici");
|
|
41169
|
+
_dispatcher = new ProxyAgent(proxyUrl);
|
|
41170
|
+
} catch {
|
|
41171
|
+
_dispatcher = void 0;
|
|
41172
|
+
}
|
|
41173
|
+
return _dispatcher || void 0;
|
|
41174
|
+
}
|
|
41175
|
+
async function proxyFetch(url, init) {
|
|
41176
|
+
const dispatcher = await resolveProxyDispatcher();
|
|
41177
|
+
const signal = init?.signal ?? AbortSignal.timeout(SEARCH_TIMEOUT_MS);
|
|
41178
|
+
const opts = { ...init, signal };
|
|
41179
|
+
if (dispatcher)
|
|
41180
|
+
opts["dispatcher"] = dispatcher;
|
|
41181
|
+
return fetch(url, opts);
|
|
41182
|
+
}
|
|
41183
|
+
function hasProxy() {
|
|
41184
|
+
return !!(process.env["HTTPS_PROXY"] || process.env["HTTP_PROXY"] || process.env["https_proxy"] || process.env["http_proxy"]);
|
|
41185
|
+
}
|
|
41155
41186
|
async function searchSerper(query2, maxResults) {
|
|
41156
41187
|
const apiKey = process.env["SERPER_API_KEY"];
|
|
41157
41188
|
if (!apiKey)
|
|
41158
41189
|
throw new Error("SERPER_API_KEY not configured");
|
|
41159
41190
|
let res;
|
|
41160
41191
|
try {
|
|
41161
|
-
res = await
|
|
41192
|
+
res = await proxyFetch("https://google.serper.dev/search", {
|
|
41162
41193
|
method: "POST",
|
|
41163
41194
|
headers: {
|
|
41164
41195
|
"X-API-KEY": apiKey,
|
|
@@ -41186,7 +41217,7 @@ async function searchBrave(query2, maxResults) {
|
|
|
41186
41217
|
const params = new URLSearchParams({ q: query2, count: String(maxResults) });
|
|
41187
41218
|
let res;
|
|
41188
41219
|
try {
|
|
41189
|
-
res = await
|
|
41220
|
+
res = await proxyFetch(`https://api.search.brave.com/res/v1/web/search?${params}`, {
|
|
41190
41221
|
headers: {
|
|
41191
41222
|
"Accept": "application/json",
|
|
41192
41223
|
"Accept-Encoding": "gzip",
|
|
@@ -41206,23 +41237,28 @@ async function searchBrave(query2, maxResults) {
|
|
|
41206
41237
|
date: r.page_age
|
|
41207
41238
|
}));
|
|
41208
41239
|
}
|
|
41209
|
-
async function
|
|
41240
|
+
async function searchDuckDuckGo(query2, maxResults) {
|
|
41210
41241
|
const encoded = encodeURIComponent(query2);
|
|
41211
|
-
let
|
|
41212
|
-
|
|
41213
|
-
|
|
41214
|
-
|
|
41215
|
-
"User-Agent":
|
|
41242
|
+
let lastError;
|
|
41243
|
+
for (const base of DDG_ENDPOINTS) {
|
|
41244
|
+
try {
|
|
41245
|
+
const res = await proxyFetch(`${base}?q=${encoded}`, {
|
|
41246
|
+
headers: { "User-Agent": DDG_UA }
|
|
41247
|
+
});
|
|
41248
|
+
if (!res.ok) {
|
|
41249
|
+
lastError = new Error(`HTTP ${res.status} ${res.statusText}`);
|
|
41250
|
+
continue;
|
|
41216
41251
|
}
|
|
41217
|
-
|
|
41218
|
-
|
|
41219
|
-
|
|
41220
|
-
|
|
41221
|
-
|
|
41222
|
-
|
|
41252
|
+
const html = await res.text();
|
|
41253
|
+
const results = base.includes("lite") ? parseDDGLite(html, maxResults) : parseDDGHtml(html, maxResults);
|
|
41254
|
+
if (results.length > 0)
|
|
41255
|
+
return results;
|
|
41256
|
+
lastError = new Error(`Parsed 0 results from ${base}`);
|
|
41257
|
+
} catch (err) {
|
|
41258
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
41259
|
+
}
|
|
41223
41260
|
}
|
|
41224
|
-
|
|
41225
|
-
return parseDDGLite(html, maxResults);
|
|
41261
|
+
throw new Error(`Network error: ${lastError?.message ?? "all DuckDuckGo endpoints failed"}`);
|
|
41226
41262
|
}
|
|
41227
41263
|
function parseDDGLite(html, max) {
|
|
41228
41264
|
const results = [];
|
|
@@ -41261,21 +41297,7 @@ function parseDDGLite(html, max) {
|
|
|
41261
41297
|
}
|
|
41262
41298
|
return results;
|
|
41263
41299
|
}
|
|
41264
|
-
|
|
41265
|
-
const encoded = encodeURIComponent(query2);
|
|
41266
|
-
let res;
|
|
41267
|
-
try {
|
|
41268
|
-
res = await fetch(`https://html.duckduckgo.com/html/?q=${encoded}`, {
|
|
41269
|
-
headers: {
|
|
41270
|
-
"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"
|
|
41271
|
-
}
|
|
41272
|
-
});
|
|
41273
|
-
} catch (err) {
|
|
41274
|
-
throw new Error(`Network error: ${err instanceof Error ? err.message : String(err)}`);
|
|
41275
|
-
}
|
|
41276
|
-
if (!res.ok)
|
|
41277
|
-
throw new Error(`HTTP ${res.status} ${res.statusText}`);
|
|
41278
|
-
const html = await res.text();
|
|
41300
|
+
function parseDDGHtml(html, maxResults) {
|
|
41279
41301
|
const results = [];
|
|
41280
41302
|
const linkRegex = /<a[^>]+class="result__a"[^>]*href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/g;
|
|
41281
41303
|
const snippetRegex = /<a[^>]+class="result__snippet"[^>]*>([\s\S]*?)<\/a>/g;
|
|
@@ -41303,10 +41325,12 @@ async function searchDuckDuckGoHtml(query2, maxResults) {
|
|
|
41303
41325
|
function stripHtml(html) {
|
|
41304
41326
|
return html.replace(/<[^>]*>/g, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/'/g, "'").replace(/ /g, " ").replace(/\s+/g, " ").trim();
|
|
41305
41327
|
}
|
|
41306
|
-
var WebSearchTool;
|
|
41328
|
+
var SEARCH_TIMEOUT_MS, _dispatcher, WebSearchTool, DDG_UA, DDG_ENDPOINTS;
|
|
41307
41329
|
var init_web_search = __esm({
|
|
41308
41330
|
"../core/dist/tools/web-search.js"() {
|
|
41309
41331
|
"use strict";
|
|
41332
|
+
SEARCH_TIMEOUT_MS = 15e3;
|
|
41333
|
+
_dispatcher = false;
|
|
41310
41334
|
WebSearchTool = {
|
|
41311
41335
|
name: "web_search",
|
|
41312
41336
|
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.",
|
|
@@ -41330,7 +41354,7 @@ var init_web_search = __esm({
|
|
|
41330
41354
|
const backends = [
|
|
41331
41355
|
{ name: "Serper", fn: searchSerper },
|
|
41332
41356
|
{ name: "Brave", fn: searchBrave },
|
|
41333
|
-
{ name: "DuckDuckGo", fn:
|
|
41357
|
+
{ name: "DuckDuckGo", fn: searchDuckDuckGo }
|
|
41334
41358
|
];
|
|
41335
41359
|
const errors = [];
|
|
41336
41360
|
for (const { name, fn } of backends) {
|
|
@@ -41345,13 +41369,27 @@ var init_web_search = __esm({
|
|
|
41345
41369
|
errors.push({ backend: name, error: message });
|
|
41346
41370
|
}
|
|
41347
41371
|
}
|
|
41372
|
+
const hasNetworkErr = errors.some((e) => e.error.includes("Network error") || e.error.includes("timed out"));
|
|
41373
|
+
const hints = [];
|
|
41374
|
+
if (hasNetworkErr && !hasProxy()) {
|
|
41375
|
+
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).");
|
|
41376
|
+
}
|
|
41377
|
+
if (hasNetworkErr && hasProxy()) {
|
|
41378
|
+
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.`);
|
|
41379
|
+
}
|
|
41348
41380
|
return JSON.stringify({
|
|
41349
41381
|
status: "error",
|
|
41350
41382
|
error: "All search backends failed.",
|
|
41351
|
-
details: errors
|
|
41383
|
+
details: errors,
|
|
41384
|
+
...hints.length > 0 ? { hints } : {}
|
|
41352
41385
|
});
|
|
41353
41386
|
}
|
|
41354
41387
|
};
|
|
41388
|
+
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";
|
|
41389
|
+
DDG_ENDPOINTS = [
|
|
41390
|
+
"https://lite.duckduckgo.com/lite/",
|
|
41391
|
+
"https://html.duckduckgo.com/html/"
|
|
41392
|
+
];
|
|
41355
41393
|
}
|
|
41356
41394
|
});
|
|
41357
41395
|
|
|
@@ -42914,6 +42952,18 @@ ${typeLabel}${item.payload.content}`;
|
|
|
42914
42952
|
this.persistence?.updateStatus(item.id, "deferred", { deferredUntil: until });
|
|
42915
42953
|
return item;
|
|
42916
42954
|
}
|
|
42955
|
+
/**
|
|
42956
|
+
* Defer an item that has already been dequeued (not in the queue).
|
|
42957
|
+
* Used when processing is interrupted (preempted) and the item should
|
|
42958
|
+
* be resumed later. Only updates persistence — does NOT re-insert
|
|
42959
|
+
* into the in-memory queue (resurfaceDue handles that on the next idle cycle).
|
|
42960
|
+
*/
|
|
42961
|
+
deferDequeued(item, until) {
|
|
42962
|
+
item.status = "deferred";
|
|
42963
|
+
item.deferredUntil = until;
|
|
42964
|
+
item.startedAt = void 0;
|
|
42965
|
+
this.persistence?.updateStatus(item.id, "deferred", { deferredUntil: until });
|
|
42966
|
+
}
|
|
42917
42967
|
/**
|
|
42918
42968
|
* Mark an item as merged into another item.
|
|
42919
42969
|
*/
|
|
@@ -43155,7 +43205,7 @@ function detectAbnormalCompletion(reply, item) {
|
|
|
43155
43205
|
const registry = MAILBOX_TYPE_REGISTRY[item.sourceType];
|
|
43156
43206
|
if (!registry?.invokesLLM)
|
|
43157
43207
|
return void 0;
|
|
43158
|
-
if (reply === "[preempted]")
|
|
43208
|
+
if (reply === "[preempted]" || reply === "[cancelled]")
|
|
43159
43209
|
return void 0;
|
|
43160
43210
|
const isUserFacing = item.sourceType === "human_chat" || item.sourceType === "a2a_message";
|
|
43161
43211
|
if (isUserFacing)
|
|
@@ -43192,6 +43242,8 @@ var init_attention = __esm({
|
|
|
43192
43242
|
triageToolHandlers;
|
|
43193
43243
|
triageChatFn;
|
|
43194
43244
|
lastTriageResult;
|
|
43245
|
+
/** Tracks whether the last yield-point decision was 'cancel' vs 'preempt'. */
|
|
43246
|
+
lastYieldDecision;
|
|
43195
43247
|
unsubscribeNewItem;
|
|
43196
43248
|
decisions = [];
|
|
43197
43249
|
watchdogTimer;
|
|
@@ -43388,6 +43440,7 @@ var init_attention = __esm({
|
|
|
43388
43440
|
this.interruptSignal = false;
|
|
43389
43441
|
this.pendingInterruptItem = void 0;
|
|
43390
43442
|
this.criticalInterruptResolve = void 0;
|
|
43443
|
+
this.lastYieldDecision = void 0;
|
|
43391
43444
|
this.processingStartedAt = Date.now();
|
|
43392
43445
|
this.delegate?.onFocusChanged(item);
|
|
43393
43446
|
let reply = void 0;
|
|
@@ -43422,6 +43475,27 @@ var init_attention = __esm({
|
|
|
43422
43475
|
this.processingStartedAt = void 0;
|
|
43423
43476
|
if (timedOut) {
|
|
43424
43477
|
this.mailbox.requeue(item);
|
|
43478
|
+
} else if (reply === "[cancelled]" || this.lastYieldDecision === "cancel") {
|
|
43479
|
+
log14.info("Item cancelled \u2014 dropping permanently", {
|
|
43480
|
+
agentId: this.agentId,
|
|
43481
|
+
itemId: item.id,
|
|
43482
|
+
type: item.sourceType
|
|
43483
|
+
});
|
|
43484
|
+
this.mailbox.complete(item.id);
|
|
43485
|
+
} else if (reply === "[preempted]" || this.lastYieldDecision === "preempt") {
|
|
43486
|
+
log14.info("Item preempted (paused) \u2014 deferring for later resumption", {
|
|
43487
|
+
agentId: this.agentId,
|
|
43488
|
+
itemId: item.id,
|
|
43489
|
+
type: item.sourceType
|
|
43490
|
+
});
|
|
43491
|
+
this.mailbox.deferDequeued(item);
|
|
43492
|
+
} else if (!this.running) {
|
|
43493
|
+
log14.info("Agent stopped during processing \u2014 requeueing item", {
|
|
43494
|
+
agentId: this.agentId,
|
|
43495
|
+
itemId: item.id,
|
|
43496
|
+
type: item.sourceType
|
|
43497
|
+
});
|
|
43498
|
+
this.mailbox.requeue(item);
|
|
43425
43499
|
} else {
|
|
43426
43500
|
const abnormalReason = detectAbnormalCompletion(reply, item);
|
|
43427
43501
|
const retries = item.retryCount ?? 0;
|
|
@@ -43497,7 +43571,8 @@ var init_attention = __esm({
|
|
|
43497
43571
|
*
|
|
43498
43572
|
* Returns the decision type so the caller knows what to do:
|
|
43499
43573
|
* - 'continue' → keep working on current task
|
|
43500
|
-
* - 'preempt' → caller should save state and return;
|
|
43574
|
+
* - 'preempt' → caller should save state and return '[preempted]'; item is deferred for later resumption
|
|
43575
|
+
* - 'cancel' → caller should abort and return '[cancelled]'; item is permanently dropped
|
|
43501
43576
|
* - 'merge' → item was absorbed into current work (caller may inject into session)
|
|
43502
43577
|
*/
|
|
43503
43578
|
async checkYieldPoint() {
|
|
@@ -43530,8 +43605,13 @@ var init_attention = __esm({
|
|
|
43530
43605
|
return { decision: "defer", item: newItem, reasoning };
|
|
43531
43606
|
}
|
|
43532
43607
|
if (decisionType === "preempt") {
|
|
43608
|
+
this.lastYieldDecision = "preempt";
|
|
43533
43609
|
return { decision: "preempt", item: newItem, reasoning };
|
|
43534
43610
|
}
|
|
43611
|
+
if (decisionType === "cancel") {
|
|
43612
|
+
this.lastYieldDecision = "cancel";
|
|
43613
|
+
return { decision: "cancel", item: newItem, reasoning };
|
|
43614
|
+
}
|
|
43535
43615
|
return { decision: "continue", reasoning };
|
|
43536
43616
|
}
|
|
43537
43617
|
/**
|
|
@@ -43614,7 +43694,7 @@ var init_attention = __esm({
|
|
|
43614
43694
|
try {
|
|
43615
43695
|
const prompt = this.buildJudgePrompt(currentItem, newItem);
|
|
43616
43696
|
const llmDecision = await this.llmJudge(prompt);
|
|
43617
|
-
const validDecisions = ["continue", "preempt", "merge", "defer"];
|
|
43697
|
+
const validDecisions = ["continue", "preempt", "cancel", "merge", "defer"];
|
|
43618
43698
|
if (validDecisions.includes(llmDecision))
|
|
43619
43699
|
return llmDecision;
|
|
43620
43700
|
} catch (err) {
|
|
@@ -43629,17 +43709,29 @@ var init_attention = __esm({
|
|
|
43629
43709
|
const recentDecs = this.decisions.slice(-5).map((d) => `- ${d.decisionType}: ${d.reasoning}`).join("\n");
|
|
43630
43710
|
const queuedItems = this.mailbox.getQueuedItems();
|
|
43631
43711
|
const queueSection = queuedItems.length > 0 ? queuedItems.map((i) => ` - [${i.sourceType}] p${i.priority}: "${i.payload.summary}"`).join("\n") : " (empty)";
|
|
43712
|
+
const currentContent = currentItem.payload.content?.slice(0, 500) ?? "";
|
|
43713
|
+
const newContent = newItem.payload.content?.slice(0, 800) ?? "";
|
|
43632
43714
|
return [
|
|
43633
|
-
"You are deciding
|
|
43634
|
-
"
|
|
43715
|
+
"You are deciding whether to INTERRUPT your current work to handle a new incoming message.",
|
|
43716
|
+
"",
|
|
43717
|
+
"RULES (in priority order):",
|
|
43718
|
+
"1. Human messages (human_chat) ALWAYS preempt non-human work \u2014 users are top priority.",
|
|
43719
|
+
'2. If the new message explicitly CANCELS or REVOKES the current work (e.g. "cancel that task",',
|
|
43720
|
+
` "don't publish this anymore", "delete the draft"), choose cancel \u2014 current work is permanently abandoned.`,
|
|
43721
|
+
'3. If the new message asks to PAUSE or DELAY current work (e.g. "hold off for now",',
|
|
43722
|
+
' "wait before deploying", "pause that"), choose preempt \u2014 current work is saved and can be resumed later.',
|
|
43723
|
+
"4. If the new message is about the SAME task/topic as current work and adds info, consider merge.",
|
|
43724
|
+
"5. If the new message is lower priority and unrelated, continue current work.",
|
|
43635
43725
|
"",
|
|
43636
43726
|
`CURRENT FOCUS: [${currentItem.sourceType}] priority=${currentItem.priority}`,
|
|
43637
|
-
` "${currentItem.payload.summary}"`,
|
|
43727
|
+
` Summary: "${currentItem.payload.summary}"`,
|
|
43638
43728
|
` Task: ${currentItem.payload.taskId ?? "none"}`,
|
|
43729
|
+
currentContent ? ` Content preview: ${currentContent}` : "",
|
|
43639
43730
|
"",
|
|
43640
43731
|
`NEW ITEM: [${newItem.sourceType}] priority=${newItem.priority}`,
|
|
43641
|
-
` "${newItem.payload.summary}"`,
|
|
43732
|
+
` Summary: "${newItem.payload.summary}"`,
|
|
43642
43733
|
` Task: ${newItem.payload.taskId ?? "none"}`,
|
|
43734
|
+
newContent ? ` Content: ${newContent}` : "",
|
|
43643
43735
|
"",
|
|
43644
43736
|
`FULL QUEUE (${this.mailbox.depth} items):`,
|
|
43645
43737
|
queueSection,
|
|
@@ -43647,7 +43739,14 @@ var init_attention = __esm({
|
|
|
43647
43739
|
"RECENT DECISIONS:",
|
|
43648
43740
|
recentDecs || "(none)",
|
|
43649
43741
|
"",
|
|
43650
|
-
"
|
|
43742
|
+
"DECISIONS:",
|
|
43743
|
+
"- preempt: PAUSE current work (session saved for later resumption) and handle the new item",
|
|
43744
|
+
"- cancel: PERMANENTLY STOP current work (session dropped, will NOT be resumed) and handle the new item",
|
|
43745
|
+
"- merge: Inject new item into current session as additional context (no interruption)",
|
|
43746
|
+
"- defer: Put the new item aside for later (no interruption)",
|
|
43747
|
+
"- continue: Ignore the new item for now, keep working",
|
|
43748
|
+
"",
|
|
43749
|
+
"Choose exactly one: continue | preempt | cancel | merge | defer",
|
|
43651
43750
|
"Reply with ONLY the decision word, nothing else."
|
|
43652
43751
|
].join("\n");
|
|
43653
43752
|
}
|
|
@@ -45193,6 +45292,7 @@ ${notification.stdoutTail}`);
|
|
|
45193
45292
|
});
|
|
45194
45293
|
});
|
|
45195
45294
|
const roleFilePath = join10(this.dataDir, "role", "ROLE.md");
|
|
45295
|
+
const heartbeatFilePath = join10(this.dataDir, "role", "HEARTBEAT.md");
|
|
45196
45296
|
this.toolHooks.register({
|
|
45197
45297
|
name: "role-auto-reload",
|
|
45198
45298
|
after: async (ctx) => {
|
|
@@ -45202,6 +45302,10 @@ ${notification.stdoutTail}`);
|
|
|
45202
45302
|
log17.info("Agent modified its own ROLE.md \u2014 reloading role definition");
|
|
45203
45303
|
this.reloadRole();
|
|
45204
45304
|
}
|
|
45305
|
+
if (targetPath === heartbeatFilePath || targetPath.endsWith("/role/HEARTBEAT.md")) {
|
|
45306
|
+
log17.info("Agent modified its own HEARTBEAT.md \u2014 reloading heartbeat checklist");
|
|
45307
|
+
this.reloadHeartbeat();
|
|
45308
|
+
}
|
|
45205
45309
|
}
|
|
45206
45310
|
}
|
|
45207
45311
|
});
|
|
@@ -45264,6 +45368,7 @@ ${notification.stdoutTail}`);
|
|
|
45264
45368
|
log17.info(`Agent started: ${this.config.name}`);
|
|
45265
45369
|
}
|
|
45266
45370
|
async stop() {
|
|
45371
|
+
this.cancelActiveStream();
|
|
45267
45372
|
this.attentionController.stop();
|
|
45268
45373
|
this.heartbeat.stop();
|
|
45269
45374
|
this._bgCompletionUnsub?.();
|
|
@@ -45465,7 +45570,7 @@ ${notification.stdoutTail}`);
|
|
|
45465
45570
|
}
|
|
45466
45571
|
},
|
|
45467
45572
|
evaluateInterrupt: async (currentItem, newItem) => {
|
|
45468
|
-
return this.attentionController.
|
|
45573
|
+
return this.attentionController.evaluateWithLLMFallback(currentItem, newItem);
|
|
45469
45574
|
},
|
|
45470
45575
|
getTriageContext: async () => {
|
|
45471
45576
|
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) })) : [];
|
|
@@ -45561,7 +45666,7 @@ ${notification.stdoutTail}`);
|
|
|
45561
45666
|
case "requirement_update": {
|
|
45562
45667
|
if (extra.actionRequired) {
|
|
45563
45668
|
const reqId = item.payload.requirementId ?? "unknown";
|
|
45564
|
-
const reply = await this.handleMessage(item.payload.content + markerSuffix, item.metadata?.senderId, senderInfo, buildHandleOpts({ sessionId: `
|
|
45669
|
+
const reply = await this.handleMessage(item.payload.content + markerSuffix, item.metadata?.senderId, senderInfo, buildHandleOpts({ sessionId: `requirement_${reqId}_${ts}`, scenario: "requirement_action" }));
|
|
45565
45670
|
resolveResponse(reply);
|
|
45566
45671
|
return reply;
|
|
45567
45672
|
}
|
|
@@ -45710,6 +45815,25 @@ ${notification.stdoutTail}`);
|
|
|
45710
45815
|
log17.warn(`Failed to reload role for agent ${this.config.name}`, { error: String(err) });
|
|
45711
45816
|
}
|
|
45712
45817
|
}
|
|
45818
|
+
/**
|
|
45819
|
+
* Reload the agent's heartbeat checklist from its HEARTBEAT.md file on disk.
|
|
45820
|
+
* Called when the agent modifies its own HEARTBEAT.md via file_edit/file_write.
|
|
45821
|
+
*/
|
|
45822
|
+
reloadHeartbeat() {
|
|
45823
|
+
const heartbeatFile = join10(this.dataDir, "role", "HEARTBEAT.md");
|
|
45824
|
+
if (!existsSync15(heartbeatFile))
|
|
45825
|
+
return;
|
|
45826
|
+
try {
|
|
45827
|
+
const content = readFileSync10(heartbeatFile, "utf-8");
|
|
45828
|
+
this.role = {
|
|
45829
|
+
...this.role,
|
|
45830
|
+
heartbeatChecklist: content
|
|
45831
|
+
};
|
|
45832
|
+
log17.info(`Heartbeat checklist reloaded from disk for agent ${this.config.name}`);
|
|
45833
|
+
} catch (err) {
|
|
45834
|
+
log17.warn(`Failed to reload heartbeat for agent ${this.config.name}`, { error: String(err) });
|
|
45835
|
+
}
|
|
45836
|
+
}
|
|
45713
45837
|
/**
|
|
45714
45838
|
* Start a fresh conversation session, discarding the current in-memory session context.
|
|
45715
45839
|
* Called when the user explicitly starts a "New Chat".
|
|
@@ -45776,6 +45900,7 @@ ${notification.stdoutTail}`);
|
|
|
45776
45900
|
if (this.state.status === "offline")
|
|
45777
45901
|
return;
|
|
45778
45902
|
this.pauseReason = reason;
|
|
45903
|
+
this.cancelActiveStream();
|
|
45779
45904
|
this.heartbeat.stop();
|
|
45780
45905
|
this.attentionController.stop();
|
|
45781
45906
|
if (this.memoryConsolidationTimer) {
|
|
@@ -46606,10 +46731,11 @@ ${report}`);
|
|
|
46606
46731
|
if (!this.state.currentActivity) {
|
|
46607
46732
|
let actType = "chat";
|
|
46608
46733
|
let actLabel;
|
|
46734
|
+
const peerName = senderInfo?.name || senderId || void 0;
|
|
46609
46735
|
switch (scenario) {
|
|
46610
46736
|
case "a2a":
|
|
46611
46737
|
actType = "a2a";
|
|
46612
|
-
actLabel = `Chat from ${
|
|
46738
|
+
actLabel = peerName ? `Chat from ${peerName}` : "Agent Message";
|
|
46613
46739
|
break;
|
|
46614
46740
|
case "heartbeat":
|
|
46615
46741
|
actType = "internal";
|
|
@@ -46617,14 +46743,14 @@ ${report}`);
|
|
|
46617
46743
|
break;
|
|
46618
46744
|
case "comment_response":
|
|
46619
46745
|
actType = "chat";
|
|
46620
|
-
actLabel = `Comment reply to ${
|
|
46746
|
+
actLabel = peerName ? `Comment reply to ${peerName}` : "Comment Reply";
|
|
46621
46747
|
break;
|
|
46622
46748
|
case "review":
|
|
46623
46749
|
actType = "internal";
|
|
46624
|
-
actLabel = `Reviewing task from ${
|
|
46750
|
+
actLabel = peerName ? `Reviewing task from ${peerName}` : "Task Review";
|
|
46625
46751
|
break;
|
|
46626
46752
|
default:
|
|
46627
|
-
actLabel = `Chat with ${
|
|
46753
|
+
actLabel = peerName ? `Chat with ${peerName}` : "Human Chat";
|
|
46628
46754
|
break;
|
|
46629
46755
|
}
|
|
46630
46756
|
chatActivityId = this.startActivity(actType, actLabel);
|
|
@@ -46867,13 +46993,14 @@ ${block}
|
|
|
46867
46993
|
}
|
|
46868
46994
|
}
|
|
46869
46995
|
const chatYield = await this.checkAttentionYieldPoint();
|
|
46870
|
-
if (chatYield.decision === "preempt" && isPreemptable) {
|
|
46871
|
-
|
|
46996
|
+
if ((chatYield.decision === "preempt" || chatYield.decision === "cancel") && isPreemptable) {
|
|
46997
|
+
const marker = chatYield.decision === "cancel" ? "[cancelled]" : "[preempted]";
|
|
46998
|
+
log17.info(`handleMessage ${chatYield.decision} by higher-priority item`, {
|
|
46872
46999
|
agentId: this.id,
|
|
46873
47000
|
scenario,
|
|
46874
47001
|
preemptedBy: chatYield.item?.sourceType
|
|
46875
47002
|
});
|
|
46876
|
-
return
|
|
47003
|
+
return marker;
|
|
46877
47004
|
}
|
|
46878
47005
|
if (chatYield.decision === "merge" && chatYield.item) {
|
|
46879
47006
|
const mergeMsg = `[LIVE UPDATE] ${chatYield.item.payload.summary}
|
|
@@ -47054,6 +47181,7 @@ ${chatYield.item.payload.content}`;
|
|
|
47054
47181
|
this.checkDailyTokenBudget();
|
|
47055
47182
|
const llmStart = Date.now();
|
|
47056
47183
|
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");
|
|
47184
|
+
streamMarkerDelta.flush();
|
|
47057
47185
|
const tokensThisCall = response.usage.inputTokens + response.usage.outputTokens;
|
|
47058
47186
|
this.updateTokensUsed(tokensThisCall);
|
|
47059
47187
|
this.calibrateTokenCounter(response.usage.inputTokens);
|
|
@@ -47204,6 +47332,7 @@ ${chatYield.item.payload.content}`;
|
|
|
47204
47332
|
}
|
|
47205
47333
|
const llmStart2 = Date.now();
|
|
47206
47334
|
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");
|
|
47335
|
+
streamMarkerDelta.flush();
|
|
47207
47336
|
const tokens2 = response.usage.inputTokens + response.usage.outputTokens;
|
|
47208
47337
|
this.updateTokensUsed(tokens2);
|
|
47209
47338
|
this.calibrateTokenCounter(response.usage.inputTokens);
|
|
@@ -47610,14 +47739,16 @@ ${chatYield.item.payload.content}`;
|
|
|
47610
47739
|
}
|
|
47611
47740
|
this.flushPendingInjections(sessionId);
|
|
47612
47741
|
const yieldResult = await this.checkAttentionYieldPoint();
|
|
47613
|
-
if (yieldResult.decision === "preempt") {
|
|
47614
|
-
|
|
47742
|
+
if (yieldResult.decision === "preempt" || yieldResult.decision === "cancel") {
|
|
47743
|
+
const statusLabel = yieldResult.decision === "cancel" ? "cancelled" : "preempted";
|
|
47744
|
+
emit("status", statusLabel, {
|
|
47615
47745
|
reason: yieldResult.reasoning,
|
|
47616
47746
|
preemptedBy: yieldResult.item?.sourceType
|
|
47617
47747
|
});
|
|
47618
|
-
log17.info(
|
|
47748
|
+
log17.info(`Task ${statusLabel} by higher-priority mailbox item`, {
|
|
47619
47749
|
taskId: taskId2,
|
|
47620
47750
|
agentId: this.id,
|
|
47751
|
+
decision: yieldResult.decision,
|
|
47621
47752
|
preemptedBy: yieldResult.item?.sourceType,
|
|
47622
47753
|
reasoning: yieldResult.reasoning?.slice(0, 120)
|
|
47623
47754
|
});
|
|
@@ -48627,6 +48758,10 @@ ${todayLog.slice(0, HEARTBEAT_DAILY_LOG_CHARS)}
|
|
|
48627
48758
|
'- Check existing skills first: `discover_tools({ mode: "list_skills" })` and `builder_list`.',
|
|
48628
48759
|
"- To update an existing skill: edit files in `~/.markus/builder-artifacts/skills/{name}/`, bump version, re-install with `builder_install`.",
|
|
48629
48760
|
"",
|
|
48761
|
+
"**Direct self-evolution** (simplest and most impactful):",
|
|
48762
|
+
"- **Update ROLE.md** \u2014 When you discover a behavioral rule, working style, or guiding principle that should always apply, append it to your ROLE.md via `file_edit`. ROLE.md is loaded into every conversation, so changes take effect immediately. Read first, then append. No need to accumulate 3 insights \u2014 even a single validated lesson can warrant a role update if it is fundamental.",
|
|
48763
|
+
"- **Update HEARTBEAT.md** \u2014 When you realize your patrol routine should include a new recurring check (or remove an obsolete one), modify your HEARTBEAT.md via `file_edit`. This is your personal checklist \u2014 customize it to match your actual responsibilities. Changes take effect at the next heartbeat.",
|
|
48764
|
+
"",
|
|
48630
48765
|
"**Decision guide \u2014 where does this insight go?**",
|
|
48631
48766
|
"| Observation type | Action |",
|
|
48632
48767
|
"|---|---|",
|
|
@@ -48634,7 +48769,8 @@ ${todayLog.slice(0, HEARTBEAT_DAILY_LOG_CHARS)}
|
|
|
48634
48769
|
'| Tool tip or preference | `memory_save` with tags: `["insight", "tool:<name>"]` |',
|
|
48635
48770
|
'| Multi-step repeatable workflow | `memory_update_longterm({ section: "procedures", mode: "patch" })` |',
|
|
48636
48771
|
"| Practice worth sharing with the team | Create skill via **skill-building**, then install with `builder_install` |",
|
|
48637
|
-
"|
|
|
48772
|
+
"| Behavioral rule or guiding principle | Update ROLE.md (`file_read` \u2192 `file_edit` to append) |",
|
|
48773
|
+
"| New recurring check for your patrol | Update HEARTBEAT.md (`file_read` \u2192 `file_edit`) |",
|
|
48638
48774
|
"",
|
|
48639
48775
|
"Quality bar: Only record insights that are **specific**, **actionable**, and **non-obvious**.",
|
|
48640
48776
|
"Skip if nothing meaningful happened since last heartbeat."
|
|
@@ -48662,7 +48798,8 @@ ${todayLog.slice(0, HEARTBEAT_DAILY_LOG_CHARS)}
|
|
|
48662
48798
|
"- Tasks with `executionRound > 1` required revision \u2014 your initial approach had issues.",
|
|
48663
48799
|
"- A high revision rate (>30%) suggests your knowledge is not being applied effectively.",
|
|
48664
48800
|
"- Check: does your MEMORY.md knowledge actually cover the failure patterns you see?",
|
|
48665
|
-
"- If you keep making the same type of mistake, escalate: save as insight \u2192 add to MEMORY.md \u2192 update ROLE.md."
|
|
48801
|
+
"- If you keep making the same type of mistake, escalate: save as insight \u2192 add to MEMORY.md \u2192 update ROLE.md or HEARTBEAT.md.",
|
|
48802
|
+
"- Consider: would a ROLE.md rule or a HEARTBEAT.md check have prevented any recent failures?"
|
|
48666
48803
|
].join("\n");
|
|
48667
48804
|
const prompt = [
|
|
48668
48805
|
"[HEARTBEAT CHECK-IN]",
|
|
@@ -50326,6 +50463,67 @@ function createA2ATools(ctx) {
|
|
|
50326
50463
|
return JSON.stringify({ status: "error", error: String(err) });
|
|
50327
50464
|
}
|
|
50328
50465
|
}
|
|
50466
|
+
}] : [],
|
|
50467
|
+
...ctx.getChannelMessages ? [{
|
|
50468
|
+
name: "recall_context",
|
|
50469
|
+
description: [
|
|
50470
|
+
"Recall historical context you may need to respond effectively.",
|
|
50471
|
+
"Use this BEFORE responding when the provided context is insufficient.",
|
|
50472
|
+
"",
|
|
50473
|
+
"Supported scopes:",
|
|
50474
|
+
'\u2022 "channel" \u2014 Read chat messages from a group chat or DM channel. Requires channel_key.',
|
|
50475
|
+
" Use when you joined a discussion late, were @mentioned, or need to understand prior conversation.",
|
|
50476
|
+
"",
|
|
50477
|
+
"For other context types, use these existing tools instead:",
|
|
50478
|
+
"\u2022 task_get \u2014 Full task details including all comments (scope=task context)",
|
|
50479
|
+
"\u2022 requirement_get \u2014 Full requirement details including all comments (scope=requirement context)",
|
|
50480
|
+
"\u2022 recall_activity \u2014 Your own execution history, activity logs, and past work (scope=your activities)",
|
|
50481
|
+
"\u2022 memory_search \u2014 Your saved memories and notes (scope=personal knowledge)"
|
|
50482
|
+
].join("\n"),
|
|
50483
|
+
inputSchema: {
|
|
50484
|
+
type: "object",
|
|
50485
|
+
properties: {
|
|
50486
|
+
scope: {
|
|
50487
|
+
type: "string",
|
|
50488
|
+
enum: ["channel"],
|
|
50489
|
+
description: 'Type of context to recall. Currently: "channel" for chat/group messages.'
|
|
50490
|
+
},
|
|
50491
|
+
channel_key: {
|
|
50492
|
+
type: "string",
|
|
50493
|
+
description: 'Required when scope="channel". The channel key (e.g., "group:<teamId>" for team chats, "dm:<id1>_<id2>" for DMs).'
|
|
50494
|
+
},
|
|
50495
|
+
limit: { type: "number", description: "Number of items to fetch (default 30, max 50)." },
|
|
50496
|
+
before: { type: "string", description: "ISO timestamp \u2014 fetch items older than this for pagination. Omit for most recent." }
|
|
50497
|
+
},
|
|
50498
|
+
required: ["scope"]
|
|
50499
|
+
},
|
|
50500
|
+
async execute(args) {
|
|
50501
|
+
const scope = args["scope"];
|
|
50502
|
+
if (scope === "channel") {
|
|
50503
|
+
const channelKey = args["channel_key"];
|
|
50504
|
+
if (!channelKey) {
|
|
50505
|
+
return JSON.stringify({ status: "error", error: 'channel_key is required when scope="channel"' });
|
|
50506
|
+
}
|
|
50507
|
+
const limit = Math.min(args["limit"] ?? 30, 50);
|
|
50508
|
+
const before2 = args["before"];
|
|
50509
|
+
try {
|
|
50510
|
+
const result = await ctx.getChannelMessages(channelKey, limit, before2);
|
|
50511
|
+
const formatted = result.messages.map((m) => `[${m.createdAt}] ${m.senderType === "agent" ? `[agent] ${m.senderName}` : `[human] ${m.senderName}`}: ${m.text.slice(0, 500)}`);
|
|
50512
|
+
return JSON.stringify({
|
|
50513
|
+
messages: formatted,
|
|
50514
|
+
count: result.messages.length,
|
|
50515
|
+
hasMore: result.hasMore,
|
|
50516
|
+
oldestTimestamp: result.messages[0]?.createdAt
|
|
50517
|
+
});
|
|
50518
|
+
} catch (err) {
|
|
50519
|
+
return JSON.stringify({ status: "error", error: String(err) });
|
|
50520
|
+
}
|
|
50521
|
+
}
|
|
50522
|
+
return JSON.stringify({
|
|
50523
|
+
status: "error",
|
|
50524
|
+
error: `Unknown scope: "${scope}". Use "channel" for chat messages, or use task_get / requirement_get / recall_activity for other context.`
|
|
50525
|
+
});
|
|
50526
|
+
}
|
|
50329
50527
|
}] : []
|
|
50330
50528
|
];
|
|
50331
50529
|
}
|
|
@@ -52842,7 +53040,7 @@ var init_dist3 = __esm({
|
|
|
52842
53040
|
|
|
52843
53041
|
// ../core/dist/agent-manager.js
|
|
52844
53042
|
import { join as join13 } from "node:path";
|
|
52845
|
-
import { mkdirSync as mkdirSync12, readFileSync as readFileSync13, existsSync as existsSync18, copyFileSync, rmSync, readdirSync as readdirSync4 } from "node:fs";
|
|
53043
|
+
import { mkdirSync as mkdirSync12, readFileSync as readFileSync13, existsSync as existsSync18, copyFileSync, rmSync, readdirSync as readdirSync4, writeFileSync as writeFileSync11 } from "node:fs";
|
|
52846
53044
|
import { homedir as homedir6 } from "node:os";
|
|
52847
53045
|
function resolveCurrentTaskId(agentObj, ts, agentId2) {
|
|
52848
53046
|
const activeTasks = agentObj?.getActiveTasks?.() ?? [];
|
|
@@ -53209,24 +53407,50 @@ Priority: ${delegation.priority}`, envelope.from, { name: envelope.from, role: "
|
|
|
53209
53407
|
if (!request.name?.trim())
|
|
53210
53408
|
throw new Error("Agent name is required");
|
|
53211
53409
|
const id = agentId();
|
|
53212
|
-
const
|
|
53410
|
+
const roleName = request.roleName || "custom";
|
|
53411
|
+
const isCustomRole = roleName === "custom";
|
|
53412
|
+
const role = isCustomRole ? {
|
|
53413
|
+
id: generateId("role"),
|
|
53414
|
+
name: request.name,
|
|
53415
|
+
description: "",
|
|
53416
|
+
category: "custom",
|
|
53417
|
+
systemPrompt: `# ${request.name}
|
|
53418
|
+
|
|
53419
|
+
You are ${request.name}.`,
|
|
53420
|
+
defaultSkills: [],
|
|
53421
|
+
heartbeatChecklist: "",
|
|
53422
|
+
defaultPolicies: [],
|
|
53423
|
+
builtIn: false
|
|
53424
|
+
} : this.roleLoader.loadRole(roleName);
|
|
53213
53425
|
const agentDataDir = join13(this.dataDir, id);
|
|
53214
53426
|
mkdirSync12(agentDataDir, { recursive: true });
|
|
53215
53427
|
const agentRoleDir = join13(agentDataDir, "role");
|
|
53216
53428
|
mkdirSync12(agentRoleDir, { recursive: true });
|
|
53217
|
-
|
|
53218
|
-
|
|
53219
|
-
|
|
53220
|
-
const
|
|
53221
|
-
|
|
53222
|
-
|
|
53429
|
+
if (!isCustomRole && !request.skipTemplateCopy) {
|
|
53430
|
+
const templateDir = this.roleLoader.resolveTemplateDir(roleName);
|
|
53431
|
+
if (templateDir) {
|
|
53432
|
+
for (const file of ["ROLE.md", "HEARTBEAT.md", "POLICIES.md", "CONTEXT.md"]) {
|
|
53433
|
+
const src = join13(templateDir, file);
|
|
53434
|
+
if (existsSync18(src))
|
|
53435
|
+
copyFileSync(src, join13(agentRoleDir, file));
|
|
53436
|
+
}
|
|
53223
53437
|
}
|
|
53224
53438
|
}
|
|
53439
|
+
const heartbeatPath = join13(agentRoleDir, "HEARTBEAT.md");
|
|
53440
|
+
if (!existsSync18(heartbeatPath)) {
|
|
53441
|
+
writeFileSync11(heartbeatPath, [
|
|
53442
|
+
"# Heartbeat Checklist",
|
|
53443
|
+
"",
|
|
53444
|
+
"- [ ] Check mailbox for new messages and respond to urgent items",
|
|
53445
|
+
"- [ ] Review assigned tasks \u2014 update progress, unblock if possible",
|
|
53446
|
+
"- [ ] Check team announcements for new information",
|
|
53447
|
+
"- [ ] Scan recent channel messages for anything requiring attention"
|
|
53448
|
+
].join("\n"), "utf-8");
|
|
53449
|
+
}
|
|
53225
53450
|
const config = {
|
|
53226
53451
|
id,
|
|
53227
53452
|
name: request.name,
|
|
53228
|
-
|
|
53229
|
-
roleId: request.roleName,
|
|
53453
|
+
roleId: roleName,
|
|
53230
53454
|
orgId: request.orgId ?? "default",
|
|
53231
53455
|
teamId: request.teamId,
|
|
53232
53456
|
agentRole: request.agentRole ?? "worker",
|
|
@@ -53388,7 +53612,8 @@ Priority: ${delegation.priority}`, envelope.from, { name: envelope.from, role: "
|
|
|
53388
53612
|
...this.groupChatHandlers ? {
|
|
53389
53613
|
sendGroupMessage: this.groupChatHandlers.sendGroupMessage,
|
|
53390
53614
|
createGroupChat: (name, memberIds) => this.groupChatHandlers.createGroupChat(name, id, config.name, memberIds),
|
|
53391
|
-
listGroupChats: this.groupChatHandlers.listGroupChats
|
|
53615
|
+
listGroupChats: this.groupChatHandlers.listGroupChats,
|
|
53616
|
+
getChannelMessages: this.groupChatHandlers.getChannelMessages
|
|
53392
53617
|
} : {}
|
|
53393
53618
|
};
|
|
53394
53619
|
for (const tool of createA2ATools(a2aContext))
|
|
@@ -53742,6 +53967,20 @@ Known issues: ${knownIssues}` : ""}`
|
|
|
53742
53967
|
let role;
|
|
53743
53968
|
if (existsSync18(join13(agentRoleDir, "ROLE.md"))) {
|
|
53744
53969
|
role = this.roleLoader.loadRole(agentRoleDir);
|
|
53970
|
+
} else if (row.roleId === "custom") {
|
|
53971
|
+
role = {
|
|
53972
|
+
id: generateId("role"),
|
|
53973
|
+
name: row.name,
|
|
53974
|
+
description: "",
|
|
53975
|
+
category: "custom",
|
|
53976
|
+
systemPrompt: `# ${row.name}
|
|
53977
|
+
|
|
53978
|
+
You are ${row.name}.`,
|
|
53979
|
+
defaultSkills: [],
|
|
53980
|
+
heartbeatChecklist: "",
|
|
53981
|
+
defaultPolicies: [],
|
|
53982
|
+
builtIn: false
|
|
53983
|
+
};
|
|
53745
53984
|
} else {
|
|
53746
53985
|
role = (() => {
|
|
53747
53986
|
try {
|
|
@@ -53947,7 +54186,8 @@ Known issues: ${knownIssues}` : ""}`
|
|
|
53947
54186
|
...this.groupChatHandlers ? {
|
|
53948
54187
|
sendGroupMessage: this.groupChatHandlers.sendGroupMessage,
|
|
53949
54188
|
createGroupChat: (name, memberIds) => this.groupChatHandlers.createGroupChat(name, id, config.name, memberIds),
|
|
53950
|
-
listGroupChats: this.groupChatHandlers.listGroupChats
|
|
54189
|
+
listGroupChats: this.groupChatHandlers.listGroupChats,
|
|
54190
|
+
getChannelMessages: this.groupChatHandlers.getChannelMessages
|
|
53951
54191
|
} : {}
|
|
53952
54192
|
};
|
|
53953
54193
|
for (const tool of createA2ATools(a2aCtx))
|
|
@@ -55884,7 +56124,7 @@ var init_ollama = __esm({
|
|
|
55884
56124
|
});
|
|
55885
56125
|
|
|
55886
56126
|
// ../core/dist/llm/auth-profiles.js
|
|
55887
|
-
import { readFileSync as readFileSync14, writeFileSync as
|
|
56127
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync12, mkdirSync as mkdirSync13, existsSync as existsSync19, unlinkSync as unlinkSync2 } from "node:fs";
|
|
55888
56128
|
import { join as join14 } from "node:path";
|
|
55889
56129
|
import { homedir as homedir7 } from "node:os";
|
|
55890
56130
|
var log34, AuthProfileStore;
|
|
@@ -55915,7 +56155,7 @@ var init_auth_profiles = __esm({
|
|
|
55915
56155
|
}
|
|
55916
56156
|
}
|
|
55917
56157
|
write(data) {
|
|
55918
|
-
|
|
56158
|
+
writeFileSync12(this.filePath, JSON.stringify(data, null, 2) + "\n", { mode: 384 });
|
|
55919
56159
|
}
|
|
55920
56160
|
/**
|
|
55921
56161
|
* Acquire a simple filesystem lock for atomic profile updates (token refresh).
|
|
@@ -55924,7 +56164,7 @@ var init_auth_profiles = __esm({
|
|
|
55924
56164
|
acquireLock() {
|
|
55925
56165
|
const lockContent = `${process.pid}-${Date.now()}`;
|
|
55926
56166
|
try {
|
|
55927
|
-
|
|
56167
|
+
writeFileSync12(this.lockPath, lockContent, { flag: "wx" });
|
|
55928
56168
|
return () => {
|
|
55929
56169
|
try {
|
|
55930
56170
|
unlinkSync2(this.lockPath);
|
|
@@ -55936,7 +56176,7 @@ var init_auth_profiles = __esm({
|
|
|
55936
56176
|
const existing = readFileSync14(this.lockPath, "utf-8");
|
|
55937
56177
|
const ts = Number(existing.split("-").pop());
|
|
55938
56178
|
if (Date.now() - ts > 3e4) {
|
|
55939
|
-
|
|
56179
|
+
writeFileSync12(this.lockPath, lockContent, { mode: 384 });
|
|
55940
56180
|
return () => {
|
|
55941
56181
|
try {
|
|
55942
56182
|
unlinkSync2(this.lockPath);
|
|
@@ -58049,7 +58289,7 @@ var init_registry = __esm({
|
|
|
58049
58289
|
skills = /* @__PURE__ */ new Map();
|
|
58050
58290
|
aliases = /* @__PURE__ */ new Map();
|
|
58051
58291
|
static normalize(s) {
|
|
58052
|
-
return
|
|
58292
|
+
return kebab(s);
|
|
58053
58293
|
}
|
|
58054
58294
|
register(skill) {
|
|
58055
58295
|
const name = skill.manifest.name;
|
|
@@ -59286,7 +59526,7 @@ var init_dist4 = __esm({
|
|
|
59286
59526
|
});
|
|
59287
59527
|
|
|
59288
59528
|
// ../org-manager/dist/org-service.js
|
|
59289
|
-
import { mkdirSync as mkdirSync15, writeFileSync as
|
|
59529
|
+
import { mkdirSync as mkdirSync15, writeFileSync as writeFileSync13, existsSync as existsSync23, rmSync as rmSync2 } from "node:fs";
|
|
59290
59530
|
import { join as join19 } from "node:path";
|
|
59291
59531
|
import { homedir as homedir10 } from "node:os";
|
|
59292
59532
|
var log50, OrganizationService;
|
|
@@ -59420,15 +59660,15 @@ var init_org_service = __esm({
|
|
|
59420
59660
|
mkdirSync15(dir, { recursive: true });
|
|
59421
59661
|
const annPath = join19(dir, "ANNOUNCEMENT.md");
|
|
59422
59662
|
if (announcements) {
|
|
59423
|
-
|
|
59663
|
+
writeFileSync13(annPath, announcements, "utf-8");
|
|
59424
59664
|
} else if (!existsSync23(annPath)) {
|
|
59425
|
-
|
|
59665
|
+
writeFileSync13(annPath, "", "utf-8");
|
|
59426
59666
|
}
|
|
59427
59667
|
const normsPath = join19(dir, "NORMS.md");
|
|
59428
59668
|
if (norms) {
|
|
59429
|
-
|
|
59669
|
+
writeFileSync13(normsPath, norms, "utf-8");
|
|
59430
59670
|
} else if (!existsSync23(normsPath)) {
|
|
59431
|
-
|
|
59671
|
+
writeFileSync13(normsPath, "", "utf-8");
|
|
59432
59672
|
}
|
|
59433
59673
|
}
|
|
59434
59674
|
async createTeam(orgId2, name, description) {
|
|
@@ -59794,15 +60034,17 @@ var init_org_service = __esm({
|
|
|
59794
60034
|
}
|
|
59795
60035
|
}
|
|
59796
60036
|
this.refreshIdentityContextsForOrg(request.orgId);
|
|
59797
|
-
|
|
59798
|
-
|
|
59799
|
-
|
|
59800
|
-
|
|
59801
|
-
|
|
59802
|
-
|
|
59803
|
-
|
|
59804
|
-
|
|
59805
|
-
|
|
60037
|
+
if (!request.skipAutoStart) {
|
|
60038
|
+
try {
|
|
60039
|
+
await this.agentManager.startAgent(agent.id);
|
|
60040
|
+
log50.info(`Agent onboarded: ${request.name} (auto-started)`, {
|
|
60041
|
+
orgId: request.orgId,
|
|
60042
|
+
agentId: agent.id,
|
|
60043
|
+
agentRole: request.agentRole ?? "worker"
|
|
60044
|
+
});
|
|
60045
|
+
} catch (error) {
|
|
60046
|
+
log50.warn(`Agent created but auto-start failed: ${request.name}`, { error: String(error) });
|
|
60047
|
+
}
|
|
59806
60048
|
}
|
|
59807
60049
|
return agent;
|
|
59808
60050
|
}
|
|
@@ -60125,9 +60367,9 @@ var init_org_service = __esm({
|
|
|
60125
60367
|
const roleNames = this.listAvailableRoles();
|
|
60126
60368
|
if (roleNames.length > 0) {
|
|
60127
60369
|
parts.push("");
|
|
60128
|
-
parts.push("##
|
|
60370
|
+
parts.push("## Built-in Role Templates (for reference only)");
|
|
60129
60371
|
parts.push("");
|
|
60130
|
-
parts.push("
|
|
60372
|
+
parts.push("These templates are available for reading as **reference** when writing custom ROLE.md files. You do NOT need to set `roleName` \u2014 the agent's identity is fully defined by its ROLE.md.");
|
|
60131
60373
|
parts.push("");
|
|
60132
60374
|
for (const r of roleNames) {
|
|
60133
60375
|
parts.push(`- \`${r}\``);
|
|
@@ -60135,9 +60377,9 @@ var init_org_service = __esm({
|
|
|
60135
60377
|
const templateDirs = this.roleLoader.getTemplateDirs();
|
|
60136
60378
|
if (templateDirs.length > 0) {
|
|
60137
60379
|
parts.push("");
|
|
60138
|
-
parts.push("**Tip**:
|
|
60380
|
+
parts.push("**Tip**: Read existing templates for inspiration when writing custom ROLE.md files.");
|
|
60139
60381
|
parts.push(`Use \`file_read\` to inspect any template, e.g.: \`file_read("${templateDirs[0]}/developer/ROLE.md")\``);
|
|
60140
|
-
parts.push("This is
|
|
60382
|
+
parts.push("This is useful for understanding the level of detail and workflow guidance expected in a good ROLE.md.");
|
|
60141
60383
|
}
|
|
60142
60384
|
}
|
|
60143
60385
|
parts.push("");
|
|
@@ -60162,7 +60404,7 @@ var init_org_service = __esm({
|
|
|
60162
60404
|
});
|
|
60163
60405
|
|
|
60164
60406
|
// ../org-manager/dist/task-service.js
|
|
60165
|
-
import { mkdirSync as mkdirSync16, writeFileSync as
|
|
60407
|
+
import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync14, readFileSync as readFileSync18, existsSync as existsSync24, cpSync } from "node:fs";
|
|
60166
60408
|
import { join as join20, resolve as resolve12 } from "node:path";
|
|
60167
60409
|
import { homedir as homedir11 } from "node:os";
|
|
60168
60410
|
function formatLocalTimestamp(d = /* @__PURE__ */ new Date()) {
|
|
@@ -60515,7 +60757,7 @@ var init_task_service = __esm({
|
|
|
60515
60757
|
mkdirSync16(join20(dir, "knowledge"), { recursive: true });
|
|
60516
60758
|
const userMdPath = join20(dir, "USER.md");
|
|
60517
60759
|
if (!existsSync24(userMdPath)) {
|
|
60518
|
-
|
|
60760
|
+
writeFileSync14(userMdPath, [
|
|
60519
60761
|
"# About the Owner",
|
|
60520
60762
|
"",
|
|
60521
60763
|
"- Name:",
|
|
@@ -62581,7 +62823,7 @@ Action: ${guidance}` : ""
|
|
|
62581
62823
|
testResults: d.testResults
|
|
62582
62824
|
}))
|
|
62583
62825
|
};
|
|
62584
|
-
|
|
62826
|
+
writeFileSync14(join20(taskSharedDir, "manifest.json"), JSON.stringify(manifest, null, 2));
|
|
62585
62827
|
for (const d of deliverables) {
|
|
62586
62828
|
if (d.type === "file" && d.reference) {
|
|
62587
62829
|
const src = resolve12(d.reference);
|
|
@@ -62596,7 +62838,7 @@ Action: ${guidance}` : ""
|
|
|
62596
62838
|
}
|
|
62597
62839
|
if (d.type === "file" && d.summary && !existsSync24(d.reference)) {
|
|
62598
62840
|
const safeName = d.reference.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
|
|
62599
|
-
|
|
62841
|
+
writeFileSync14(join20(taskSharedDir, `${safeName}.md`), d.summary);
|
|
62600
62842
|
}
|
|
62601
62843
|
}
|
|
62602
62844
|
log51.info("Deliverables published to shared workspace", { taskId: task.id, dir: taskSharedDir });
|
|
@@ -62714,7 +62956,11 @@ Action: ${guidance}` : ""
|
|
|
62714
62956
|
"",
|
|
62715
62957
|
'Save each lesson using `memory_save` with tags `["lesson", ...]`.',
|
|
62716
62958
|
'If it is a repeatable multi-step procedure, promote to SOP via `memory_update_longterm({ section: "sops", mode: "patch" })`.',
|
|
62717
|
-
"If the best practice would benefit other agents on the team, create a shareable skill via **skill-building** and install it with `builder_install`."
|
|
62959
|
+
"If the best practice would benefit other agents on the team, create a shareable skill via **skill-building** and install it with `builder_install`.",
|
|
62960
|
+
"",
|
|
62961
|
+
"**Direct self-evolution** \u2014 consider the simplest, most impactful options:",
|
|
62962
|
+
"- If this lesson reveals a behavioral rule that should always guide your work, append it to your ROLE.md via `file_edit`.",
|
|
62963
|
+
"- If you should be checking for this class of issue regularly, add a check to your HEARTBEAT.md via `file_edit`."
|
|
62718
62964
|
].join("\n") : [
|
|
62719
62965
|
"[SELF-EVOLUTION \u2014 Post-Task Reflection (Success)]",
|
|
62720
62966
|
"",
|
|
@@ -62731,6 +62977,11 @@ Action: ${guidance}` : ""
|
|
|
62731
62977
|
'If you identify a meaningful insight, save it using `memory_save` with tags `["lesson", "best-practice", ...]`.',
|
|
62732
62978
|
'If it is a multi-step workflow, promote to SOP via `memory_update_longterm({ section: "sops", mode: "patch" })`.',
|
|
62733
62979
|
"If worth sharing with the team, create a skill via **skill-building** and install with `builder_install`.",
|
|
62980
|
+
"",
|
|
62981
|
+
"**Direct self-evolution** \u2014 consider the simplest, most impactful options:",
|
|
62982
|
+
"- If this success reveals a guiding principle or working style worth keeping, append it to your ROLE.md via `file_edit`.",
|
|
62983
|
+
"- If there is a periodic check that would help maintain this quality, add it to your HEARTBEAT.md via `file_edit`.",
|
|
62984
|
+
"",
|
|
62734
62985
|
"If nothing noteworthy stands out, it is fine to skip saving."
|
|
62735
62986
|
].join("\n");
|
|
62736
62987
|
void agent.sendMessage(prompt, void 0, void 0, {
|
|
@@ -63413,7 +63664,7 @@ ${task.description}`;
|
|
|
63413
63664
|
|
|
63414
63665
|
// ../org-manager/dist/builder-service.js
|
|
63415
63666
|
import { join as join21 } from "node:path";
|
|
63416
|
-
import { readdirSync as readdirSync8, readFileSync as readFileSync19, existsSync as existsSync25, writeFileSync as
|
|
63667
|
+
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";
|
|
63417
63668
|
import { homedir as homedir12 } from "node:os";
|
|
63418
63669
|
var log52, FS_HELPER, BuilderService;
|
|
63419
63670
|
var init_builder_service = __esm({
|
|
@@ -63497,17 +63748,17 @@ var init_builder_service = __esm({
|
|
|
63497
63748
|
async installAgent(artDir, manifest, mfName, artifactName) {
|
|
63498
63749
|
const agentManager = this.orgService.getAgentManager();
|
|
63499
63750
|
const agentName = manifest.displayName ?? manifest.name ?? artifactName;
|
|
63500
|
-
const
|
|
63501
|
-
const requestedRole = manifest.agent?.roleName ?? "developer";
|
|
63502
|
-
const roleName = knownRoles.includes(requestedRole) ? requestedRole : "developer";
|
|
63751
|
+
const hasCustomRole = existsSync25(join21(artDir, "ROLE.md"));
|
|
63503
63752
|
const skills = manifest.dependencies?.skills ?? [];
|
|
63504
63753
|
const agentRole = manifest.agent?.agentRole ?? "worker";
|
|
63505
63754
|
const agent = await this.orgService.hireAgent({
|
|
63506
63755
|
name: agentName,
|
|
63507
|
-
roleName,
|
|
63756
|
+
roleName: manifest.agent?.roleName || (hasCustomRole ? "custom" : "developer"),
|
|
63508
63757
|
orgId: "default",
|
|
63509
63758
|
agentRole,
|
|
63510
|
-
skills
|
|
63759
|
+
skills,
|
|
63760
|
+
skipAutoStart: true,
|
|
63761
|
+
skipTemplateCopy: hasCustomRole
|
|
63511
63762
|
});
|
|
63512
63763
|
const agentRoleDir = join21(agentManager.getDataDir(), agent.id, "role");
|
|
63513
63764
|
mkdirSync17(agentRoleDir, { recursive: true });
|
|
@@ -63519,7 +63770,7 @@ var init_builder_service = __esm({
|
|
|
63519
63770
|
copyFileSync2(srcFile, join21(agentRoleDir, fname));
|
|
63520
63771
|
}
|
|
63521
63772
|
}
|
|
63522
|
-
|
|
63773
|
+
writeFileSync15(join21(agentRoleDir, ".role-origin.json"), JSON.stringify({ customRole: true, source: "builder-artifact", artifact: artifactName, artifactType: "agent" }));
|
|
63523
63774
|
agent.reloadRole();
|
|
63524
63775
|
await agentManager.startAgent(agent.id);
|
|
63525
63776
|
return {
|
|
@@ -63547,38 +63798,42 @@ var init_builder_service = __esm({
|
|
|
63547
63798
|
const norms = existsSync25(normsPath) ? readFileSync19(normsPath, "utf-8") : "";
|
|
63548
63799
|
this.orgService.ensureTeamDataDir(team.id, announcements, norms);
|
|
63549
63800
|
const members = manifest.team?.members ?? [];
|
|
63550
|
-
const knownRoles = this.orgService.listAvailableRoles();
|
|
63551
63801
|
const createdAgents = [];
|
|
63802
|
+
const usedMemberDirs = /* @__PURE__ */ new Set();
|
|
63552
63803
|
for (const member of members) {
|
|
63553
63804
|
const count = member.count ?? 1;
|
|
63554
63805
|
const memberRole = member.role ?? "worker";
|
|
63555
63806
|
const memberName = member.name ?? "Agent";
|
|
63556
|
-
const roleName = knownRoles.includes(member.roleName) ? member.roleName : "developer";
|
|
63557
63807
|
const memberSkills = member.skills ?? [];
|
|
63558
|
-
const
|
|
63559
|
-
const
|
|
63808
|
+
const memberFilesDir = this.findMemberDir(artDir, memberName, usedMemberDirs);
|
|
63809
|
+
const hasCustomRole = !!memberFilesDir && existsSync25(join21(memberFilesDir, "ROLE.md"));
|
|
63810
|
+
if (memberFilesDir)
|
|
63811
|
+
usedMemberDirs.add(memberFilesDir);
|
|
63812
|
+
log52.info("installTeam: member lookup", { memberName, memberFilesDir, hasCustomRole });
|
|
63560
63813
|
for (let i = 0; i < count; i++) {
|
|
63561
63814
|
const displayName = count > 1 ? `${memberName} ${i + 1}` : memberName;
|
|
63562
63815
|
const agent = await this.orgService.hireAgent({
|
|
63563
63816
|
name: displayName,
|
|
63564
|
-
roleName,
|
|
63817
|
+
roleName: member.roleName || (hasCustomRole ? "custom" : "developer"),
|
|
63565
63818
|
orgId: "default",
|
|
63566
63819
|
teamId: team.id,
|
|
63567
63820
|
agentRole: memberRole,
|
|
63568
|
-
skills: memberSkills.length > 0 ? memberSkills : void 0
|
|
63821
|
+
skills: memberSkills.length > 0 ? memberSkills : void 0,
|
|
63822
|
+
skipAutoStart: true,
|
|
63823
|
+
skipTemplateCopy: hasCustomRole
|
|
63569
63824
|
});
|
|
63570
63825
|
const agentRoleDir = join21(agentManager.getDataDir(), agent.id, "role");
|
|
63571
63826
|
mkdirSync17(agentRoleDir, { recursive: true });
|
|
63572
|
-
if (existsSync25(memberFilesDir)) {
|
|
63827
|
+
if (memberFilesDir && existsSync25(memberFilesDir)) {
|
|
63573
63828
|
for (const fname of readdirSync8(memberFilesDir)) {
|
|
63574
63829
|
const srcFile = join21(memberFilesDir, fname);
|
|
63575
63830
|
if (statSync4(srcFile).isFile()) {
|
|
63576
63831
|
copyFileSync2(srcFile, join21(agentRoleDir, fname));
|
|
63577
63832
|
}
|
|
63578
63833
|
}
|
|
63579
|
-
agent.reloadRole();
|
|
63580
63834
|
}
|
|
63581
|
-
|
|
63835
|
+
writeFileSync15(join21(agentRoleDir, ".role-origin.json"), JSON.stringify({ customRole: true, source: "builder-artifact", artifact: artifactName, artifactType: "team" }));
|
|
63836
|
+
agent.reloadRole();
|
|
63582
63837
|
if (memberRole === "manager") {
|
|
63583
63838
|
await this.orgService.updateTeam(team.id, { managerId: agent.id, managerType: "agent" });
|
|
63584
63839
|
}
|
|
@@ -63591,13 +63846,56 @@ var init_builder_service = __esm({
|
|
|
63591
63846
|
installed: { team: { id: team.id, name: teamName }, agents: createdAgents }
|
|
63592
63847
|
};
|
|
63593
63848
|
}
|
|
63849
|
+
/**
|
|
63850
|
+
* Find the member directory under artDir/members/ by trying multiple slug strategies.
|
|
63851
|
+
* Returns the absolute path to the member directory, or null if not found.
|
|
63852
|
+
*/
|
|
63853
|
+
findMemberDir(artDir, memberName, usedDirs) {
|
|
63854
|
+
const membersBase = join21(artDir, "members");
|
|
63855
|
+
if (!existsSync25(membersBase))
|
|
63856
|
+
return null;
|
|
63857
|
+
const slug = kebab(memberName, "agent");
|
|
63858
|
+
const exact = join21(membersBase, slug);
|
|
63859
|
+
if (existsSync25(exact) && !usedDirs.has(exact))
|
|
63860
|
+
return exact;
|
|
63861
|
+
try {
|
|
63862
|
+
for (const entry of readdirSync8(membersBase, { withFileTypes: true })) {
|
|
63863
|
+
if (!entry.isDirectory())
|
|
63864
|
+
continue;
|
|
63865
|
+
const candidateDir = join21(membersBase, entry.name);
|
|
63866
|
+
if (usedDirs.has(candidateDir))
|
|
63867
|
+
continue;
|
|
63868
|
+
const rolePath = join21(candidateDir, "ROLE.md");
|
|
63869
|
+
if (!existsSync25(rolePath))
|
|
63870
|
+
continue;
|
|
63871
|
+
try {
|
|
63872
|
+
const content = readFileSync19(rolePath, "utf-8");
|
|
63873
|
+
const title = content.match(/^#\s+(.+)$/m)?.[1]?.trim();
|
|
63874
|
+
if (title && title.toLowerCase() === memberName.toLowerCase())
|
|
63875
|
+
return candidateDir;
|
|
63876
|
+
} catch {
|
|
63877
|
+
}
|
|
63878
|
+
}
|
|
63879
|
+
} catch {
|
|
63880
|
+
}
|
|
63881
|
+
try {
|
|
63882
|
+
const remaining = readdirSync8(membersBase, { withFileTypes: true }).filter((e) => e.isDirectory() && !usedDirs.has(join21(membersBase, e.name)));
|
|
63883
|
+
if (remaining.length === 1)
|
|
63884
|
+
return join21(membersBase, remaining[0].name);
|
|
63885
|
+
} catch {
|
|
63886
|
+
}
|
|
63887
|
+
return null;
|
|
63888
|
+
}
|
|
63594
63889
|
async installSkill(artDir, manifest, artifactName) {
|
|
63595
63890
|
const skillDir = join21(homedir12(), ".markus", "skills", artifactName);
|
|
63596
63891
|
mkdirSync17(skillDir, { recursive: true });
|
|
63597
63892
|
for (const fname of readdirSync8(artDir)) {
|
|
63598
63893
|
const srcFile = join21(artDir, fname);
|
|
63894
|
+
const destFile = join21(skillDir, fname);
|
|
63599
63895
|
if (statSync4(srcFile).isFile()) {
|
|
63600
|
-
copyFileSync2(srcFile,
|
|
63896
|
+
copyFileSync2(srcFile, destFile);
|
|
63897
|
+
} else if (statSync4(srcFile).isDirectory()) {
|
|
63898
|
+
cpSync2(srcFile, destFile, { recursive: true });
|
|
63601
63899
|
}
|
|
63602
63900
|
}
|
|
63603
63901
|
if (this.skillRegistry) {
|
|
@@ -64249,23 +64547,6 @@ var init_sse_handler = __esm({
|
|
|
64249
64547
|
this.sseBuffer.sendProgress(this.processedTokens, this.totalTokens, `\u5DE5\u5177\u6267\u884C\u5B8C\u6210: ${event.tool}`);
|
|
64250
64548
|
}
|
|
64251
64549
|
} else if (event.type === "message_end") {
|
|
64252
|
-
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
64253
|
-
let turnThinking;
|
|
64254
|
-
if (this.thinkingBuf) {
|
|
64255
|
-
this.sseBuffer.send({ type: "thinking_commit", thinking: this.thinkingBuf, createdAt: now2 });
|
|
64256
|
-
turnThinking = this.thinkingBuf;
|
|
64257
|
-
this.thinkingBuf = "";
|
|
64258
|
-
}
|
|
64259
|
-
if (this.textBuf) {
|
|
64260
|
-
this.sseBuffer.send({ type: "text_commit", text: this.textBuf, createdAt: now2 });
|
|
64261
|
-
const seg2 = { type: "text", content: this.textBuf, createdAt: now2 };
|
|
64262
|
-
if (turnThinking)
|
|
64263
|
-
seg2.thinking = turnThinking;
|
|
64264
|
-
this.msgSegments.push(seg2);
|
|
64265
|
-
this.textBuf = "";
|
|
64266
|
-
} else if (turnThinking) {
|
|
64267
|
-
this.msgSegments.push({ type: "text", content: "", thinking: turnThinking, createdAt: now2 });
|
|
64268
|
-
}
|
|
64269
64550
|
if (event.usage?.outputTokens) {
|
|
64270
64551
|
this.totalTokens = Math.max(this.totalTokens, event.usage.outputTokens);
|
|
64271
64552
|
this.processedTokens = event.usage.outputTokens;
|
|
@@ -64339,7 +64620,7 @@ var init_sse_handler = __esm({
|
|
|
64339
64620
|
|
|
64340
64621
|
// ../org-manager/dist/skill-service.js
|
|
64341
64622
|
import { join as join22, resolve as resolve13 } from "node:path";
|
|
64342
|
-
import { existsSync as existsSync26, writeFileSync as
|
|
64623
|
+
import { existsSync as existsSync26, writeFileSync as writeFileSync16, mkdirSync as mkdirSync18, readFileSync as readFileSync20, readdirSync as readdirSync9, copyFileSync as copyFileSync3 } from "node:fs";
|
|
64343
64624
|
import { homedir as homedir13 } from "node:os";
|
|
64344
64625
|
import { execSync as execSync2 } from "node:child_process";
|
|
64345
64626
|
async function searchSkillHub(query2) {
|
|
@@ -64490,7 +64771,7 @@ async function installSkill(request, skillRegistry) {
|
|
|
64490
64771
|
if (zipResp.ok) {
|
|
64491
64772
|
const tmpZip = join22(skillsDir, `_tmp_${safeName}.zip`);
|
|
64492
64773
|
const buffer = Buffer.from(await zipResp.arrayBuffer());
|
|
64493
|
-
|
|
64774
|
+
writeFileSync16(tmpZip, buffer);
|
|
64494
64775
|
mkdirSync18(targetDir, { recursive: true });
|
|
64495
64776
|
try {
|
|
64496
64777
|
execSync2(`unzip -o "${tmpZip}" -d "${targetDir}"`, { timeout: 3e4 });
|
|
@@ -64516,7 +64797,7 @@ async function installSkill(request, skillRegistry) {
|
|
|
64516
64797
|
const mdResp = await fetch(skillMdUrl, { signal: AbortSignal.timeout(15e3) });
|
|
64517
64798
|
if (mdResp.ok) {
|
|
64518
64799
|
mkdirSync18(targetDir, { recursive: true });
|
|
64519
|
-
|
|
64800
|
+
writeFileSync16(join22(targetDir, "SKILL.md"), await mdResp.text(), "utf-8");
|
|
64520
64801
|
installed = true;
|
|
64521
64802
|
installMethod = "github-skillmd";
|
|
64522
64803
|
}
|
|
@@ -64530,7 +64811,7 @@ async function installSkill(request, skillRegistry) {
|
|
|
64530
64811
|
const mdResp = await fetch(rootMd, { signal: AbortSignal.timeout(15e3) });
|
|
64531
64812
|
if (mdResp.ok) {
|
|
64532
64813
|
mkdirSync18(targetDir, { recursive: true });
|
|
64533
|
-
|
|
64814
|
+
writeFileSync16(join22(targetDir, "SKILL.md"), await mdResp.text(), "utf-8");
|
|
64534
64815
|
installed = true;
|
|
64535
64816
|
installMethod = "github-root-skillmd";
|
|
64536
64817
|
}
|
|
@@ -64546,7 +64827,7 @@ async function installSkill(request, skillRegistry) {
|
|
|
64546
64827
|
if (zipResp.ok) {
|
|
64547
64828
|
const tmpZip = join22(skillsDir, `_tmp_${safeName}.zip`);
|
|
64548
64829
|
const buffer = Buffer.from(await zipResp.arrayBuffer());
|
|
64549
|
-
|
|
64830
|
+
writeFileSync16(tmpZip, buffer);
|
|
64550
64831
|
mkdirSync18(targetDir, { recursive: true });
|
|
64551
64832
|
try {
|
|
64552
64833
|
execSync2(`unzip -o "${tmpZip}" -d "${targetDir}"`, { timeout: 3e4 });
|
|
@@ -64578,13 +64859,13 @@ async function installSkill(request, skillRegistry) {
|
|
|
64578
64859
|
};
|
|
64579
64860
|
const manifest = buildManifest("skill", raw);
|
|
64580
64861
|
manifest.source = skillSource;
|
|
64581
|
-
|
|
64862
|
+
writeFileSync16(skillMfPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
64582
64863
|
} else {
|
|
64583
64864
|
try {
|
|
64584
64865
|
const mf = JSON.parse(readFileSync20(skillMfPath, "utf-8"));
|
|
64585
64866
|
if (!mf.source) {
|
|
64586
64867
|
mf.source = skillSource;
|
|
64587
|
-
|
|
64868
|
+
writeFileSync16(skillMfPath, JSON.stringify(mf, null, 2), "utf-8");
|
|
64588
64869
|
}
|
|
64589
64870
|
} catch {
|
|
64590
64871
|
}
|
|
@@ -64618,7 +64899,7 @@ var init_skill_service = __esm({
|
|
|
64618
64899
|
// ../org-manager/dist/api-server.js
|
|
64619
64900
|
import { createServer as createServer2 } from "node:http";
|
|
64620
64901
|
import { join as join23, resolve as resolve14, dirname as dirname6 } from "node:path";
|
|
64621
|
-
import { readdirSync as readdirSync10, readFileSync as readFileSync21, existsSync as existsSync27, writeFileSync as
|
|
64902
|
+
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";
|
|
64622
64903
|
import { homedir as homedir14 } from "node:os";
|
|
64623
64904
|
import { execSync as execSync3 } from "node:child_process";
|
|
64624
64905
|
async function signToken(payload, secret) {
|
|
@@ -64708,7 +64989,7 @@ var init_api_server = __esm({
|
|
|
64708
64989
|
init_skill_service();
|
|
64709
64990
|
log57 = createLogger("api-server");
|
|
64710
64991
|
PBKDF2_ITERATIONS = 1e4;
|
|
64711
|
-
APIServer = class {
|
|
64992
|
+
APIServer = class _APIServer {
|
|
64712
64993
|
orgService;
|
|
64713
64994
|
taskService;
|
|
64714
64995
|
port;
|
|
@@ -64733,7 +65014,7 @@ var init_api_server = __esm({
|
|
|
64733
65014
|
builderService;
|
|
64734
65015
|
workflowEngine;
|
|
64735
65016
|
teamTemplateRegistry;
|
|
64736
|
-
|
|
65017
|
+
// Custom group chats are now persisted in SQLite via storage.groupChatRepo
|
|
64737
65018
|
constructor(orgService, taskService, port = 8056) {
|
|
64738
65019
|
this.orgService = orgService;
|
|
64739
65020
|
this.taskService = taskService;
|
|
@@ -64748,15 +65029,18 @@ var init_api_server = __esm({
|
|
|
64748
65029
|
am.setGroupChatHandlers({
|
|
64749
65030
|
sendGroupMessage: async (channelKey, message, senderId, senderName) => {
|
|
64750
65031
|
const cleanText = stripInternalBlocks(message);
|
|
65032
|
+
const orgId2 = "default";
|
|
65033
|
+
let persistedMsgId;
|
|
64751
65034
|
if (this.storage) {
|
|
64752
|
-
await this.storage.channelMessageRepo.append({
|
|
64753
|
-
orgId:
|
|
65035
|
+
const saved = await this.storage.channelMessageRepo.append({
|
|
65036
|
+
orgId: orgId2,
|
|
64754
65037
|
channel: channelKey,
|
|
64755
65038
|
senderId,
|
|
64756
65039
|
senderType: "agent",
|
|
64757
65040
|
senderName,
|
|
64758
65041
|
text: cleanText
|
|
64759
65042
|
});
|
|
65043
|
+
persistedMsgId = saved.id;
|
|
64760
65044
|
}
|
|
64761
65045
|
this.ws.broadcast({
|
|
64762
65046
|
type: "chat:message",
|
|
@@ -64769,41 +65053,81 @@ var init_api_server = __esm({
|
|
|
64769
65053
|
},
|
|
64770
65054
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
64771
65055
|
});
|
|
64772
|
-
|
|
64773
|
-
if (
|
|
64774
|
-
|
|
64775
|
-
|
|
64776
|
-
|
|
64777
|
-
|
|
64778
|
-
|
|
64779
|
-
|
|
64780
|
-
|
|
64781
|
-
|
|
64782
|
-
|
|
64783
|
-
|
|
64784
|
-
|
|
65056
|
+
let allAgentIds = [];
|
|
65057
|
+
if (channelKey.startsWith("group:custom:") && this.storage?.groupChatRepo) {
|
|
65058
|
+
allAgentIds = this.storage.groupChatRepo.getAgentMemberIds(channelKey);
|
|
65059
|
+
} else if (channelKey.startsWith("group:")) {
|
|
65060
|
+
const teamId = channelKey.replace(/^group:/, "");
|
|
65061
|
+
const team = this.orgService.getTeam(teamId);
|
|
65062
|
+
allAgentIds = team?.memberAgentIds ?? [];
|
|
65063
|
+
}
|
|
65064
|
+
const peerAgentIds = allAgentIds.filter((id) => id !== senderId);
|
|
65065
|
+
if (peerAgentIds.length > 0) {
|
|
65066
|
+
const agentManager = this.orgService.getAgentManager();
|
|
65067
|
+
const nameMap = this.buildAgentNameMap(allAgentIds, agentManager);
|
|
65068
|
+
const mentionedNames = this.parseAgentMentions(cleanText, [...nameMap.keys()]);
|
|
65069
|
+
const filteredMentions = mentionedNames.filter((n) => {
|
|
65070
|
+
const id = nameMap.get(n);
|
|
65071
|
+
return id && id !== senderId;
|
|
65072
|
+
});
|
|
65073
|
+
if (filteredMentions.length > 0) {
|
|
65074
|
+
let channelContext = [];
|
|
65075
|
+
if (this.storage) {
|
|
65076
|
+
try {
|
|
65077
|
+
const recent = await this.storage.channelMessageRepo.getMessages(channelKey, 20);
|
|
65078
|
+
channelContext = (recent.messages ?? []).map((m) => ({
|
|
65079
|
+
role: m.senderType === "agent" ? "assistant" : "user",
|
|
65080
|
+
content: m.senderType === "agent" ? stripInternalBlocks(m.text) : `[${m.senderName}]: ${m.text}`
|
|
65081
|
+
}));
|
|
65082
|
+
} catch {
|
|
65083
|
+
}
|
|
65084
|
+
}
|
|
65085
|
+
const roundId = `round_${Date.now()}`;
|
|
65086
|
+
const respondedAgents = /* @__PURE__ */ new Set([senderId]);
|
|
65087
|
+
void this.triggerAgentToAgentChain(filteredMentions, nameMap, senderName, senderId, cleanText, persistedMsgId, channelKey, orgId2, agentManager, allAgentIds.length, { roundId, depth: 1, respondedAgents, allAgentIds });
|
|
65088
|
+
} else {
|
|
65089
|
+
for (const peerId of peerAgentIds) {
|
|
65090
|
+
try {
|
|
65091
|
+
agentManager.getAgent(peerId).enqueueToMailbox("a2a_message", {
|
|
65092
|
+
summary: `Group chat message from ${senderName}`,
|
|
65093
|
+
content: `[Group chat message from ${senderName}]:
|
|
65094
|
+
${cleanText}`,
|
|
65095
|
+
extra: { senderId, senderName, channelKey }
|
|
65096
|
+
}, {
|
|
65097
|
+
metadata: { senderId, senderName, senderRole: "agent" }
|
|
65098
|
+
});
|
|
65099
|
+
} catch {
|
|
65100
|
+
}
|
|
64785
65101
|
}
|
|
64786
65102
|
}
|
|
64787
65103
|
}
|
|
64788
65104
|
return "Message sent to group chat";
|
|
64789
65105
|
},
|
|
64790
65106
|
createGroupChat: async (name, creatorId, creatorName, memberIds) => {
|
|
64791
|
-
|
|
64792
|
-
|
|
64793
|
-
|
|
64794
|
-
|
|
64795
|
-
|
|
64796
|
-
|
|
64797
|
-
|
|
64798
|
-
|
|
64799
|
-
|
|
64800
|
-
|
|
64801
|
-
|
|
64802
|
-
|
|
64803
|
-
|
|
64804
|
-
|
|
64805
|
-
|
|
64806
|
-
|
|
65107
|
+
if (this.storage?.groupChatRepo) {
|
|
65108
|
+
const members = memberIds.map((id) => {
|
|
65109
|
+
try {
|
|
65110
|
+
const agentInfo = this.orgService.getAgentManager().getAgent(id);
|
|
65111
|
+
return { id, type: "agent", name: agentInfo?.config?.name ?? id };
|
|
65112
|
+
} catch {
|
|
65113
|
+
return { id, type: "agent", name: id };
|
|
65114
|
+
}
|
|
65115
|
+
});
|
|
65116
|
+
const gc = this.storage.groupChatRepo.create({
|
|
65117
|
+
orgId: "default",
|
|
65118
|
+
name,
|
|
65119
|
+
creatorId,
|
|
65120
|
+
creatorName,
|
|
65121
|
+
members
|
|
65122
|
+
});
|
|
65123
|
+
this.ws.broadcast({
|
|
65124
|
+
type: "chat:group_created",
|
|
65125
|
+
payload: { chatId: gc.channelKey, name, creatorId, creatorName },
|
|
65126
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
65127
|
+
});
|
|
65128
|
+
return { id: gc.channelKey, name };
|
|
65129
|
+
}
|
|
65130
|
+
return { id: `group:custom:${Date.now().toString(36)}`, name };
|
|
64807
65131
|
},
|
|
64808
65132
|
listGroupChats: async () => {
|
|
64809
65133
|
const teams = this.orgService.listTeamsWithMembers("default");
|
|
@@ -64813,13 +65137,27 @@ var init_api_server = __esm({
|
|
|
64813
65137
|
type: "team",
|
|
64814
65138
|
channelKey: `group:${t.id}`
|
|
64815
65139
|
}));
|
|
64816
|
-
const customChats = this.
|
|
64817
|
-
id: c.
|
|
65140
|
+
const customChats = this.storage?.groupChatRepo ? this.storage.groupChatRepo.list("default").map((c) => ({
|
|
65141
|
+
id: c.channelKey,
|
|
64818
65142
|
name: c.name,
|
|
64819
65143
|
type: "custom",
|
|
64820
|
-
channelKey: c.
|
|
64821
|
-
}));
|
|
65144
|
+
channelKey: c.channelKey
|
|
65145
|
+
})) : [];
|
|
64822
65146
|
return [...teamChats, ...customChats];
|
|
65147
|
+
},
|
|
65148
|
+
getChannelMessages: async (channelKey, limit, before2) => {
|
|
65149
|
+
if (!this.storage)
|
|
65150
|
+
return { messages: [], hasMore: false };
|
|
65151
|
+
const result = this.storage.channelMessageRepo.getMessages(channelKey, limit, before2);
|
|
65152
|
+
return {
|
|
65153
|
+
messages: result.messages.map((m) => ({
|
|
65154
|
+
senderName: m.senderName,
|
|
65155
|
+
senderType: m.senderType,
|
|
65156
|
+
text: stripInternalBlocks(m.text),
|
|
65157
|
+
createdAt: m.createdAt instanceof Date ? m.createdAt.toISOString() : String(m.createdAt)
|
|
65158
|
+
})),
|
|
65159
|
+
hasMore: result.hasMore
|
|
65160
|
+
};
|
|
64823
65161
|
}
|
|
64824
65162
|
});
|
|
64825
65163
|
}
|
|
@@ -64876,7 +65214,7 @@ var init_api_server = __esm({
|
|
|
64876
65214
|
throw new Error(`Hub download failed: ${res.status}`);
|
|
64877
65215
|
const data = await res.json();
|
|
64878
65216
|
const name = data.name;
|
|
64879
|
-
const slug = name
|
|
65217
|
+
const slug = kebab(name, "hub-pkg");
|
|
64880
65218
|
const mode = data.itemType === "team" ? "team" : data.itemType === "skill" ? "skill" : "agent";
|
|
64881
65219
|
const typeDir = mode === "agent" ? "agents" : mode === "team" ? "teams" : "skills";
|
|
64882
65220
|
const artDir = join23(homedir14(), ".markus", "builder-artifacts", typeDir, slug);
|
|
@@ -64885,10 +65223,10 @@ var init_api_server = __esm({
|
|
|
64885
65223
|
for (const [fname, content] of Object.entries(data.files)) {
|
|
64886
65224
|
const filePath = join23(artDir, fname);
|
|
64887
65225
|
mkdirSync19(dirname6(filePath), { recursive: true });
|
|
64888
|
-
|
|
65226
|
+
writeFileSync17(filePath, content, "utf-8");
|
|
64889
65227
|
}
|
|
64890
65228
|
} else if (data.config) {
|
|
64891
|
-
|
|
65229
|
+
writeFileSync17(join23(artDir, manifestFilename(mode)), JSON.stringify(data.config, null, 2), "utf-8");
|
|
64892
65230
|
}
|
|
64893
65231
|
return self.builderService.installArtifact(mode, slug);
|
|
64894
65232
|
}
|
|
@@ -65119,48 +65457,167 @@ var init_api_server = __esm({
|
|
|
65119
65457
|
log57.warn("Failed to persist chat turn", { error: String(err) });
|
|
65120
65458
|
}
|
|
65121
65459
|
}
|
|
65460
|
+
// ── Agent-to-agent reply storm prevention ───────────────────────────────────
|
|
65461
|
+
static A2A_MAX_DEPTH = 3;
|
|
65462
|
+
static A2A_COOLDOWN_MS = 3e4;
|
|
65463
|
+
/** Per-channel cooldown tracker: channel → agentId → last reply timestamp */
|
|
65464
|
+
a2aCooldowns = /* @__PURE__ */ new Map();
|
|
65465
|
+
/** Extract @mentions from agent reply text. Returns agent names found.
|
|
65466
|
+
* Handles multi-word names (e.g. "Ryan 莱恩") by checking known names after each @ sign.
|
|
65467
|
+
* Also matches partial names: "@Sofia" matches "Sofia 索菲亚" if "Sofia" is a token in the name. */
|
|
65468
|
+
parseAgentMentions(text, knownAgentNames) {
|
|
65469
|
+
const mentioned = /* @__PURE__ */ new Set();
|
|
65470
|
+
const sorted = [...knownAgentNames].sort((a, b) => b.length - a.length);
|
|
65471
|
+
const tokenToName = /* @__PURE__ */ new Map();
|
|
65472
|
+
for (const name of sorted) {
|
|
65473
|
+
for (const token of name.split(/\s+/)) {
|
|
65474
|
+
if (token.length >= 2) {
|
|
65475
|
+
const key2 = token.toLowerCase();
|
|
65476
|
+
if (!tokenToName.has(key2))
|
|
65477
|
+
tokenToName.set(key2, name);
|
|
65478
|
+
}
|
|
65479
|
+
}
|
|
65480
|
+
}
|
|
65481
|
+
let idx = 0;
|
|
65482
|
+
while (idx < text.length) {
|
|
65483
|
+
const atPos = text.indexOf("@", idx);
|
|
65484
|
+
if (atPos < 0)
|
|
65485
|
+
break;
|
|
65486
|
+
if (text[atPos + 1] === "[") {
|
|
65487
|
+
const close = text.indexOf("]", atPos + 2);
|
|
65488
|
+
if (close > atPos + 2) {
|
|
65489
|
+
const bracketed = text.slice(atPos + 2, close);
|
|
65490
|
+
const match = sorted.find((n) => n.toLowerCase() === bracketed.toLowerCase());
|
|
65491
|
+
if (match)
|
|
65492
|
+
mentioned.add(match);
|
|
65493
|
+
idx = close + 1;
|
|
65494
|
+
continue;
|
|
65495
|
+
}
|
|
65496
|
+
}
|
|
65497
|
+
const after2 = text.slice(atPos + 1);
|
|
65498
|
+
const afterLower = after2.toLowerCase();
|
|
65499
|
+
const fullMatch = sorted.find((n) => afterLower.startsWith(n.toLowerCase()));
|
|
65500
|
+
if (fullMatch) {
|
|
65501
|
+
mentioned.add(fullMatch);
|
|
65502
|
+
idx = atPos + 1 + fullMatch.length;
|
|
65503
|
+
continue;
|
|
65504
|
+
}
|
|
65505
|
+
const tokenMatch = after2.match(/^(\S+)/);
|
|
65506
|
+
if (tokenMatch) {
|
|
65507
|
+
const token = tokenMatch[1].toLowerCase().replace(/[,,。!?!?;;::、()()[\]【】]+$/, "");
|
|
65508
|
+
const resolved = tokenToName.get(token);
|
|
65509
|
+
if (resolved) {
|
|
65510
|
+
mentioned.add(resolved);
|
|
65511
|
+
idx = atPos + 1 + tokenMatch[1].length;
|
|
65512
|
+
continue;
|
|
65513
|
+
}
|
|
65514
|
+
}
|
|
65515
|
+
idx = atPos + 1;
|
|
65516
|
+
}
|
|
65517
|
+
return [...mentioned];
|
|
65518
|
+
}
|
|
65519
|
+
/** Build name→id lookup for agents in a channel */
|
|
65520
|
+
buildAgentNameMap(agentIds, agentManager) {
|
|
65521
|
+
const map = /* @__PURE__ */ new Map();
|
|
65522
|
+
for (const id of agentIds) {
|
|
65523
|
+
try {
|
|
65524
|
+
map.set(agentManager.getAgent(id).config.name, id);
|
|
65525
|
+
} catch {
|
|
65526
|
+
}
|
|
65527
|
+
}
|
|
65528
|
+
return map;
|
|
65529
|
+
}
|
|
65122
65530
|
/**
|
|
65123
65531
|
* Process a single agent's reply in a group chat broadcast.
|
|
65124
65532
|
* Each agent decides independently whether to respond.
|
|
65125
65533
|
* Replies are persisted and broadcast via WebSocket.
|
|
65534
|
+
* If the agent @mentions other agents, a chain reply is triggered (with storm prevention).
|
|
65126
65535
|
*/
|
|
65127
|
-
async processGroupChatReply(agentId2, userMessage, senderId, senderInfo, channel, orgId2, channelContext, agentManager, teamSize) {
|
|
65536
|
+
async processGroupChatReply(agentId2, userMessage, senderId, senderInfo, channel, orgId2, channelContext, agentManager, teamSize, opts, chainCtx) {
|
|
65128
65537
|
try {
|
|
65129
65538
|
const agent = agentManager.getAgent(agentId2);
|
|
65130
65539
|
const agentName = agent.config.name;
|
|
65131
|
-
const
|
|
65132
|
-
|
|
65133
|
-
|
|
65134
|
-
|
|
65135
|
-
|
|
65136
|
-
|
|
65137
|
-
|
|
65138
|
-
|
|
65139
|
-
|
|
65140
|
-
|
|
65141
|
-
|
|
65142
|
-
|
|
65540
|
+
const isA2A = !!chainCtx && chainCtx.depth > 0;
|
|
65541
|
+
const hasReplyTarget = !!opts?.replyToAgentName;
|
|
65542
|
+
const hasMentions = !!opts?.mentionedNames?.length;
|
|
65543
|
+
const isTargeted = hasReplyTarget || hasMentions;
|
|
65544
|
+
const targetNames = /* @__PURE__ */ new Set();
|
|
65545
|
+
if (opts?.replyToAgentName)
|
|
65546
|
+
targetNames.add(opts.replyToAgentName);
|
|
65547
|
+
if (opts?.mentionedNames)
|
|
65548
|
+
opts.mentionedNames.forEach((n) => targetNames.add(n));
|
|
65549
|
+
const thisAgentIsTarget = targetNames.has(agentName);
|
|
65550
|
+
const prefixLines = [
|
|
65551
|
+
`[GROUP CHAT \u2014 ${teamSize} team members | You are: ${agentName}]`,
|
|
65143
65552
|
""
|
|
65144
|
-
]
|
|
65553
|
+
];
|
|
65554
|
+
if (isA2A) {
|
|
65555
|
+
prefixLines.push(`[AGENT COLLABORATION] This message is from a fellow agent (${opts?.replyToAgentName ?? "teammate"}), not from the user.`);
|
|
65556
|
+
prefixLines.push("You were @mentioned because your expertise is needed. Respond concisely to the specific request.");
|
|
65557
|
+
prefixLines.push("You may @mention another agent if (and ONLY if) you genuinely need their specific expertise to answer.");
|
|
65558
|
+
prefixLines.push("Do NOT @mention agents just to be polite or to pass the conversation along.");
|
|
65559
|
+
prefixLines.push("");
|
|
65560
|
+
}
|
|
65561
|
+
if (isTargeted && !isA2A) {
|
|
65562
|
+
if (hasReplyTarget) {
|
|
65563
|
+
prefixLines.push(`[REPLY] The user is replying to ${opts.replyToAgentName}'s message: "${(opts.replyToText ?? "").slice(0, 200)}"`);
|
|
65564
|
+
}
|
|
65565
|
+
if (hasMentions) {
|
|
65566
|
+
prefixLines.push(`[MENTIONED] The user @mentioned: ${opts.mentionedNames.join(", ")}`);
|
|
65567
|
+
}
|
|
65568
|
+
prefixLines.push("");
|
|
65569
|
+
if (thisAgentIsTarget) {
|
|
65570
|
+
prefixLines.push(">>> You are the target of this message. You SHOULD respond. <<<");
|
|
65571
|
+
} else {
|
|
65572
|
+
prefixLines.push(">>> STOP. This message is NOT for you. The user is talking to " + [...targetNames].join(", ") + ", not you.");
|
|
65573
|
+
prefixLines.push("You MUST respond with exactly: [NO_RESPONSE]");
|
|
65574
|
+
prefixLines.push('The ONLY exception: you are directly contradicted by a factual error. Offering opinions, agreement, "me too", or generic help does NOT count. <<<');
|
|
65575
|
+
}
|
|
65576
|
+
} else if (!isA2A) {
|
|
65577
|
+
prefixLines.push("This is an open group message (no specific @mention or reply target).");
|
|
65578
|
+
}
|
|
65579
|
+
prefixLines.push("");
|
|
65580
|
+
prefixLines.push("Rules for ALL group chat messages:");
|
|
65581
|
+
prefixLines.push("1. Check channel history \u2014 if another agent already answered, do NOT repeat.");
|
|
65582
|
+
prefixLines.push("2. Only respond if your specific role/expertise is directly relevant.");
|
|
65583
|
+
prefixLines.push("3. Be concise \u2014 short, actionable responses only.");
|
|
65584
|
+
if (agent.config.agentRole === "manager") {
|
|
65585
|
+
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.");
|
|
65586
|
+
} else {
|
|
65587
|
+
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.");
|
|
65588
|
+
}
|
|
65589
|
+
prefixLines.push("5. DEFAULT IS SILENCE. When in doubt, respond with exactly: [NO_RESPONSE]");
|
|
65590
|
+
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.');
|
|
65591
|
+
if (isTargeted && !thisAgentIsTarget && !isA2A) {
|
|
65592
|
+
prefixLines.push("7. REMINDER: This message is directed at " + [...targetNames].join(", ") + ". You are " + agentName + ". Respond ONLY with [NO_RESPONSE].");
|
|
65593
|
+
}
|
|
65594
|
+
prefixLines.push("---", "");
|
|
65595
|
+
const groupChatPrefix = prefixLines.join("\n");
|
|
65145
65596
|
const toolEvents = [];
|
|
65146
65597
|
const reply = await agent.sendMessage(groupChatPrefix + userMessage, senderId, senderInfo, {
|
|
65147
|
-
sourceType: "human_chat",
|
|
65598
|
+
sourceType: isA2A ? "a2a_message" : "human_chat",
|
|
65599
|
+
scenario: isA2A ? "a2a" : void 0,
|
|
65148
65600
|
channelContext,
|
|
65149
65601
|
toolEventCollector: toolEvents
|
|
65150
65602
|
});
|
|
65151
|
-
if (!reply || reply.trim()
|
|
65603
|
+
if (!reply || !reply.trim() || reply.includes("[NO_RESPONSE]")) {
|
|
65152
65604
|
return;
|
|
65153
65605
|
}
|
|
65154
|
-
const { thinking, clean:
|
|
65155
|
-
|
|
65606
|
+
const { thinking, clean: rawClean } = extractThinkBlocks(reply);
|
|
65607
|
+
const cleanReply = rawClean.replace(/\[NO_RESPONSE\]/gi, "").trim();
|
|
65608
|
+
if (!cleanReply)
|
|
65156
65609
|
return;
|
|
65157
65610
|
const metadata = {};
|
|
65158
65611
|
if (thinking.length > 0)
|
|
65159
65612
|
metadata["thinking"] = thinking;
|
|
65160
65613
|
if (toolEvents.length > 0)
|
|
65161
65614
|
metadata["toolCalls"] = toolEvents;
|
|
65615
|
+
if (isA2A && opts?.replyToAgentName) {
|
|
65616
|
+
metadata["replyToAgent"] = opts.replyToAgentName;
|
|
65617
|
+
}
|
|
65618
|
+
let persistedMsgId;
|
|
65162
65619
|
if (this.storage) {
|
|
65163
|
-
await this.storage.channelMessageRepo.append({
|
|
65620
|
+
const saved = await this.storage.channelMessageRepo.append({
|
|
65164
65621
|
orgId: orgId2,
|
|
65165
65622
|
channel,
|
|
65166
65623
|
senderId: agentId2,
|
|
@@ -65168,8 +65625,10 @@ var init_api_server = __esm({
|
|
|
65168
65625
|
senderName: agentName,
|
|
65169
65626
|
text: cleanReply,
|
|
65170
65627
|
mentions: [],
|
|
65171
|
-
metadata: Object.keys(metadata).length > 0 ? metadata : void 0
|
|
65628
|
+
metadata: Object.keys(metadata).length > 0 ? metadata : void 0,
|
|
65629
|
+
replyToId: isA2A ? opts?.replyToMsgId : void 0
|
|
65172
65630
|
});
|
|
65631
|
+
persistedMsgId = saved.id;
|
|
65173
65632
|
}
|
|
65174
65633
|
this.ws.broadcast({
|
|
65175
65634
|
type: "chat:message",
|
|
@@ -65179,10 +65638,29 @@ var init_api_server = __esm({
|
|
|
65179
65638
|
senderType: "agent",
|
|
65180
65639
|
senderName: agentName,
|
|
65181
65640
|
text: cleanReply,
|
|
65182
|
-
metadata: Object.keys(metadata).length > 0 ? metadata : void 0
|
|
65641
|
+
metadata: Object.keys(metadata).length > 0 ? metadata : void 0,
|
|
65642
|
+
...isA2A ? {
|
|
65643
|
+
replyToId: opts?.replyToMsgId,
|
|
65644
|
+
replyToSender: opts?.replyToAgentName,
|
|
65645
|
+
replyToText: (opts?.replyToText ?? "").slice(0, 120)
|
|
65646
|
+
} : {}
|
|
65183
65647
|
},
|
|
65184
65648
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
65185
65649
|
});
|
|
65650
|
+
const allAgentIds = chainCtx?.allAgentIds ?? [];
|
|
65651
|
+
if (allAgentIds.length > 0) {
|
|
65652
|
+
const nameMap = this.buildAgentNameMap(allAgentIds, agentManager);
|
|
65653
|
+
const mentionedNames = this.parseAgentMentions(cleanReply, [...nameMap.keys()]);
|
|
65654
|
+
const filteredMentions = mentionedNames.filter((n) => n !== agentName);
|
|
65655
|
+
log57.info("A2A mention scan", { agentName, mentionedNames, filteredMentions, depth: chainCtx?.depth ?? 0 });
|
|
65656
|
+
if (filteredMentions.length > 0) {
|
|
65657
|
+
const depth = (chainCtx?.depth ?? 0) + 1;
|
|
65658
|
+
const roundId = chainCtx?.roundId ?? `round_${Date.now()}`;
|
|
65659
|
+
const responded = chainCtx?.respondedAgents ?? /* @__PURE__ */ new Set();
|
|
65660
|
+
responded.add(agentId2);
|
|
65661
|
+
void this.triggerAgentToAgentChain(filteredMentions, nameMap, agentName, agentId2, cleanReply, persistedMsgId, channel, orgId2, agentManager, teamSize, { roundId, depth, respondedAgents: responded, allAgentIds });
|
|
65662
|
+
}
|
|
65663
|
+
}
|
|
65186
65664
|
} catch (err) {
|
|
65187
65665
|
log57.warn("Group chat agent reply failed", { agentId: agentId2, error: String(err) });
|
|
65188
65666
|
if (this.storage) {
|
|
@@ -65202,6 +65680,56 @@ var init_api_server = __esm({
|
|
|
65202
65680
|
}
|
|
65203
65681
|
}
|
|
65204
65682
|
}
|
|
65683
|
+
/**
|
|
65684
|
+
* Trigger agent-to-agent chain replies with 3-layer storm prevention:
|
|
65685
|
+
* 1. Depth limit (max 3 hops)
|
|
65686
|
+
* 2. Per-round dedup (each agent responds at most once per conversation round)
|
|
65687
|
+
* 3. Cooldown window (same agent won't be triggered twice within 30s on same channel)
|
|
65688
|
+
*/
|
|
65689
|
+
async triggerAgentToAgentChain(mentionedNames, nameMap, fromAgentName, fromAgentId, fromText, fromMsgId, channel, orgId2, agentManager, teamSize, chainCtx) {
|
|
65690
|
+
if (chainCtx.depth > _APIServer.A2A_MAX_DEPTH) {
|
|
65691
|
+
log57.info("A2A chain depth limit reached", { channel, depth: chainCtx.depth, roundId: chainCtx.roundId });
|
|
65692
|
+
return;
|
|
65693
|
+
}
|
|
65694
|
+
const now2 = Date.now();
|
|
65695
|
+
if (!this.a2aCooldowns.has(channel)) {
|
|
65696
|
+
this.a2aCooldowns.set(channel, /* @__PURE__ */ new Map());
|
|
65697
|
+
}
|
|
65698
|
+
const channelCooldowns = this.a2aCooldowns.get(channel);
|
|
65699
|
+
let channelContext = [];
|
|
65700
|
+
if (this.storage) {
|
|
65701
|
+
try {
|
|
65702
|
+
const recent = await this.storage.channelMessageRepo.getMessages(channel, 20);
|
|
65703
|
+
channelContext = (recent.messages ?? []).map((m) => ({
|
|
65704
|
+
role: m.senderType === "agent" ? "assistant" : "user",
|
|
65705
|
+
content: m.senderType === "agent" ? stripInternalBlocks(m.text) : `[${m.senderName}]: ${m.text}`
|
|
65706
|
+
}));
|
|
65707
|
+
} catch {
|
|
65708
|
+
}
|
|
65709
|
+
}
|
|
65710
|
+
for (const targetName of mentionedNames) {
|
|
65711
|
+
const targetId = nameMap.get(targetName);
|
|
65712
|
+
if (!targetId)
|
|
65713
|
+
continue;
|
|
65714
|
+
if (chainCtx.respondedAgents.has(targetId)) {
|
|
65715
|
+
log57.info("A2A skipping (already responded this round)", { targetName, roundId: chainCtx.roundId });
|
|
65716
|
+
continue;
|
|
65717
|
+
}
|
|
65718
|
+
const lastReply = channelCooldowns.get(targetId) ?? 0;
|
|
65719
|
+
if (now2 - lastReply < _APIServer.A2A_COOLDOWN_MS) {
|
|
65720
|
+
log57.info("A2A skipping (cooldown)", { targetName, channel, cooldownRemaining: _APIServer.A2A_COOLDOWN_MS - (now2 - lastReply) });
|
|
65721
|
+
continue;
|
|
65722
|
+
}
|
|
65723
|
+
channelCooldowns.set(targetId, now2);
|
|
65724
|
+
const a2aMessage = `[From @${fromAgentName}]: ${fromText}`;
|
|
65725
|
+
void this.processGroupChatReply(targetId, a2aMessage, fromAgentId, { name: fromAgentName, role: "agent" }, channel, orgId2, channelContext, agentManager, teamSize, {
|
|
65726
|
+
mentionedNames: [targetName],
|
|
65727
|
+
replyToAgentName: fromAgentName,
|
|
65728
|
+
replyToText: fromText.slice(0, 200),
|
|
65729
|
+
replyToMsgId: fromMsgId
|
|
65730
|
+
}, chainCtx);
|
|
65731
|
+
}
|
|
65732
|
+
}
|
|
65205
65733
|
/** Persist the user message first (before LLM), returns session id for subsequent assistant persistence.
|
|
65206
65734
|
* When sessionId is provided, appends to that session; when null/undefined, creates a new session. */
|
|
65207
65735
|
async persistUserMessage(agentId2, userMessage, senderId, images, sessionId) {
|
|
@@ -65457,7 +65985,7 @@ var init_api_server = __esm({
|
|
|
65457
65985
|
const avatarDir = join23(homedir14(), ".markus", "avatars");
|
|
65458
65986
|
mkdirSync19(avatarDir, { recursive: true });
|
|
65459
65987
|
const filename = `${targetType}_${targetId}.${ext}`;
|
|
65460
|
-
|
|
65988
|
+
writeFileSync17(join23(avatarDir, filename), buf);
|
|
65461
65989
|
const avatarUrl = `/api/avatars/${filename}`;
|
|
65462
65990
|
if (targetType === "user" && this.storage) {
|
|
65463
65991
|
this.storage.userRepo.updateAvatarUrl(targetId, avatarUrl);
|
|
@@ -65534,6 +66062,7 @@ var init_api_server = __esm({
|
|
|
65534
66062
|
const senderName = body["senderName"] ?? "You";
|
|
65535
66063
|
const mentions = body["mentions"] ?? [];
|
|
65536
66064
|
const targetAgentId = body["targetAgentId"];
|
|
66065
|
+
const replyToId = body["replyToId"];
|
|
65537
66066
|
const orgId2 = body["orgId"] ?? "default";
|
|
65538
66067
|
let userMsg;
|
|
65539
66068
|
if (this.storage) {
|
|
@@ -65544,7 +66073,8 @@ var init_api_server = __esm({
|
|
|
65544
66073
|
senderType: "human",
|
|
65545
66074
|
senderName,
|
|
65546
66075
|
text,
|
|
65547
|
-
mentions
|
|
66076
|
+
mentions,
|
|
66077
|
+
replyToId
|
|
65548
66078
|
});
|
|
65549
66079
|
}
|
|
65550
66080
|
const humanOnly = body["humanOnly"] === true;
|
|
@@ -65562,21 +66092,50 @@ var init_api_server = __esm({
|
|
|
65562
66092
|
return [];
|
|
65563
66093
|
}
|
|
65564
66094
|
};
|
|
65565
|
-
|
|
65566
|
-
|
|
65567
|
-
|
|
65568
|
-
|
|
66095
|
+
let replyToAgentName;
|
|
66096
|
+
let replyToText;
|
|
66097
|
+
if (replyToId && this.storage) {
|
|
66098
|
+
try {
|
|
66099
|
+
const original = this.storage.channelMessageRepo.getMessageById(replyToId);
|
|
66100
|
+
if (original) {
|
|
66101
|
+
replyToAgentName = original.senderName;
|
|
66102
|
+
replyToText = original.text.slice(0, 200);
|
|
66103
|
+
}
|
|
66104
|
+
} catch {
|
|
66105
|
+
}
|
|
66106
|
+
}
|
|
66107
|
+
const enrichedUserMsg = userMsg ? {
|
|
66108
|
+
...userMsg,
|
|
66109
|
+
...replyToAgentName ? { replyToSender: replyToAgentName, replyToText } : {}
|
|
66110
|
+
} : null;
|
|
66111
|
+
if (!isHumanChannel && channel.startsWith("group:")) {
|
|
66112
|
+
let allAgentIds;
|
|
66113
|
+
if (channel.startsWith("group:custom:")) {
|
|
66114
|
+
allAgentIds = this.storage?.groupChatRepo?.getAgentMemberIds(channel) ?? [];
|
|
66115
|
+
} else {
|
|
66116
|
+
const teamId = channel.replace(/^group:/, "");
|
|
66117
|
+
const team = this.orgService.getTeam(teamId);
|
|
66118
|
+
allAgentIds = team?.memberAgentIds ?? [];
|
|
66119
|
+
}
|
|
65569
66120
|
if (allAgentIds.length === 0) {
|
|
65570
|
-
this.json(res, 200, { userMessage:
|
|
66121
|
+
this.json(res, 200, { userMessage: enrichedUserMsg, agentMessage: null });
|
|
65571
66122
|
return;
|
|
65572
66123
|
}
|
|
65573
66124
|
const channelContext2 = await buildChannelContext();
|
|
65574
66125
|
const senderInfo2 = this.orgService.resolveHumanIdentity(senderId);
|
|
65575
66126
|
const agentManager = this.orgService.getAgentManager();
|
|
66127
|
+
const broadcastOpts = {
|
|
66128
|
+
mentionedNames: mentions.length > 0 ? mentions : void 0,
|
|
66129
|
+
replyToAgentName,
|
|
66130
|
+
replyToText
|
|
66131
|
+
};
|
|
66132
|
+
const roundId = `round_${Date.now()}`;
|
|
66133
|
+
const respondedAgents = /* @__PURE__ */ new Set();
|
|
66134
|
+
const chainCtx = { roundId, depth: 0, respondedAgents, allAgentIds };
|
|
65576
66135
|
for (const agentId2 of allAgentIds) {
|
|
65577
|
-
void this.processGroupChatReply(agentId2, text, senderId, senderInfo2, channel, orgId2, channelContext2, agentManager, allAgentIds.length);
|
|
66136
|
+
void this.processGroupChatReply(agentId2, text, senderId, senderInfo2, channel, orgId2, channelContext2, agentManager, allAgentIds.length, broadcastOpts, chainCtx);
|
|
65578
66137
|
}
|
|
65579
|
-
this.json(res, 200, { userMessage:
|
|
66138
|
+
this.json(res, 200, { userMessage: enrichedUserMsg, agentMessage: null });
|
|
65580
66139
|
return;
|
|
65581
66140
|
}
|
|
65582
66141
|
let routedAgentId = null;
|
|
@@ -65590,7 +66149,7 @@ var init_api_server = __esm({
|
|
|
65590
66149
|
}
|
|
65591
66150
|
}
|
|
65592
66151
|
if (!routedAgentId) {
|
|
65593
|
-
this.json(res, 200, { userMessage:
|
|
66152
|
+
this.json(res, 200, { userMessage: enrichedUserMsg, agentMessage: null });
|
|
65594
66153
|
return;
|
|
65595
66154
|
}
|
|
65596
66155
|
const agent = this.orgService.getAgentManager().getAgent(routedAgentId);
|
|
@@ -65633,7 +66192,7 @@ var init_api_server = __esm({
|
|
|
65633
66192
|
}
|
|
65634
66193
|
const statusCode = raw.includes("402") ? 402 : raw.includes("401") ? 401 : raw.includes("429") ? 429 : 502;
|
|
65635
66194
|
this.json(res, statusCode, {
|
|
65636
|
-
userMessage:
|
|
66195
|
+
userMessage: enrichedUserMsg,
|
|
65637
66196
|
agentMessage: errorMsg ?? null,
|
|
65638
66197
|
error: detail2
|
|
65639
66198
|
});
|
|
@@ -65660,7 +66219,7 @@ var init_api_server = __esm({
|
|
|
65660
66219
|
void this.persistChatTurn(routedAgentId, text, reply, senderId);
|
|
65661
66220
|
}
|
|
65662
66221
|
this.json(res, 200, {
|
|
65663
|
-
userMessage:
|
|
66222
|
+
userMessage: enrichedUserMsg,
|
|
65664
66223
|
agentMessage: agentMsg ?? {
|
|
65665
66224
|
id: `tmp_${Date.now()}`,
|
|
65666
66225
|
channel,
|
|
@@ -65705,13 +66264,9 @@ var init_api_server = __esm({
|
|
|
65705
66264
|
this.json(res, 400, { error: "name is required" });
|
|
65706
66265
|
return;
|
|
65707
66266
|
}
|
|
65708
|
-
if (!roleName?.trim()) {
|
|
65709
|
-
this.json(res, 400, { error: "roleName is required" });
|
|
65710
|
-
return;
|
|
65711
|
-
}
|
|
65712
66267
|
const agent = await this.orgService.hireAgent({
|
|
65713
66268
|
name: agentName,
|
|
65714
|
-
roleName,
|
|
66269
|
+
roleName: roleName?.trim() || void 0,
|
|
65715
66270
|
orgId: body["orgId"] ?? "default",
|
|
65716
66271
|
teamId: body["teamId"],
|
|
65717
66272
|
skills: body["skills"],
|
|
@@ -65864,7 +66419,7 @@ var init_api_server = __esm({
|
|
|
65864
66419
|
if (path === "/api/group-chats" && req.method === "GET") {
|
|
65865
66420
|
const orgId2 = url.searchParams.get("orgId") ?? "default";
|
|
65866
66421
|
const teams = this.orgService.listTeamsWithMembers(orgId2);
|
|
65867
|
-
const
|
|
66422
|
+
const teamChats = teams.map((t) => ({
|
|
65868
66423
|
id: `group:${t.id}`,
|
|
65869
66424
|
name: t.name,
|
|
65870
66425
|
type: "team",
|
|
@@ -65872,21 +66427,16 @@ var init_api_server = __esm({
|
|
|
65872
66427
|
memberCount: t.members.length,
|
|
65873
66428
|
channelKey: `group:${t.id}`
|
|
65874
66429
|
}));
|
|
65875
|
-
const customChats = this.
|
|
65876
|
-
|
|
65877
|
-
|
|
65878
|
-
|
|
65879
|
-
|
|
65880
|
-
|
|
65881
|
-
|
|
65882
|
-
|
|
65883
|
-
|
|
65884
|
-
|
|
65885
|
-
memberCount: c.memberIds?.length ?? 0,
|
|
65886
|
-
channelKey: c.id
|
|
65887
|
-
}))
|
|
65888
|
-
]
|
|
65889
|
-
});
|
|
66430
|
+
const customChats = this.storage?.groupChatRepo ? this.storage.groupChatRepo.list(orgId2).map((c) => ({
|
|
66431
|
+
id: c.id,
|
|
66432
|
+
name: c.name,
|
|
66433
|
+
type: "custom",
|
|
66434
|
+
creatorId: c.creatorId,
|
|
66435
|
+
creatorName: c.creatorName,
|
|
66436
|
+
memberCount: c.memberCount,
|
|
66437
|
+
channelKey: c.channelKey
|
|
66438
|
+
})) : [];
|
|
66439
|
+
this.json(res, 200, { chats: [...teamChats, ...customChats] });
|
|
65890
66440
|
return;
|
|
65891
66441
|
}
|
|
65892
66442
|
if (path === "/api/group-chats" && req.method === "POST") {
|
|
@@ -65896,31 +66446,129 @@ var init_api_server = __esm({
|
|
|
65896
66446
|
const creatorId = body["creatorId"];
|
|
65897
66447
|
const creatorName = body["creatorName"];
|
|
65898
66448
|
const memberIds = body["memberIds"];
|
|
66449
|
+
const memberTypes = body["memberTypes"];
|
|
66450
|
+
const memberNames = body["memberNames"];
|
|
65899
66451
|
if (!name) {
|
|
65900
66452
|
this.json(res, 400, { error: "name is required" });
|
|
65901
66453
|
return;
|
|
65902
66454
|
}
|
|
65903
|
-
|
|
65904
|
-
|
|
65905
|
-
|
|
65906
|
-
|
|
65907
|
-
|
|
65908
|
-
|
|
65909
|
-
|
|
65910
|
-
|
|
65911
|
-
|
|
65912
|
-
|
|
65913
|
-
|
|
66455
|
+
if (!this.storage?.groupChatRepo) {
|
|
66456
|
+
this.json(res, 500, { error: "Storage not initialized" });
|
|
66457
|
+
return;
|
|
66458
|
+
}
|
|
66459
|
+
const members = (memberIds ?? []).map((id) => {
|
|
66460
|
+
const mType = memberTypes?.[id] ?? "agent";
|
|
66461
|
+
let mName = memberNames?.[id] ?? id;
|
|
66462
|
+
if (mType === "agent") {
|
|
66463
|
+
try {
|
|
66464
|
+
mName = this.orgService.getAgentManager().getAgent(id)?.config?.name ?? mName;
|
|
66465
|
+
} catch {
|
|
66466
|
+
}
|
|
66467
|
+
}
|
|
66468
|
+
return { id, type: mType, name: mName };
|
|
66469
|
+
});
|
|
66470
|
+
const gc = this.storage.groupChatRepo.create({ orgId: orgId2, name, creatorId, creatorName, members });
|
|
65914
66471
|
this.ws?.broadcast({
|
|
65915
66472
|
type: "chat:group_created",
|
|
65916
|
-
payload: { chatId, name, creatorId, creatorName },
|
|
66473
|
+
payload: { chatId: gc.channelKey, name, creatorId, creatorName },
|
|
65917
66474
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
65918
66475
|
});
|
|
65919
66476
|
this.json(res, 201, {
|
|
65920
|
-
chat: {
|
|
66477
|
+
chat: {
|
|
66478
|
+
id: gc.id,
|
|
66479
|
+
name: gc.name,
|
|
66480
|
+
type: "custom",
|
|
66481
|
+
creatorId: gc.creatorId,
|
|
66482
|
+
creatorName: gc.creatorName,
|
|
66483
|
+
channelKey: gc.channelKey,
|
|
66484
|
+
memberCount: gc.members.length,
|
|
66485
|
+
members: gc.members.map((m) => ({ id: m.memberId, name: m.memberName, type: m.memberType }))
|
|
66486
|
+
}
|
|
66487
|
+
});
|
|
66488
|
+
return;
|
|
66489
|
+
}
|
|
66490
|
+
if (path.match(/^\/api\/group-chats\/[^/]+$/) && req.method === "GET") {
|
|
66491
|
+
const gcId = path.split("/").pop();
|
|
66492
|
+
if (!this.storage?.groupChatRepo) {
|
|
66493
|
+
this.json(res, 500, { error: "Storage not initialized" });
|
|
66494
|
+
return;
|
|
66495
|
+
}
|
|
66496
|
+
const gc = this.storage.groupChatRepo.getById(gcId);
|
|
66497
|
+
if (!gc) {
|
|
66498
|
+
this.json(res, 404, { error: "Group chat not found" });
|
|
66499
|
+
return;
|
|
66500
|
+
}
|
|
66501
|
+
this.json(res, 200, {
|
|
66502
|
+
chat: {
|
|
66503
|
+
id: gc.id,
|
|
66504
|
+
name: gc.name,
|
|
66505
|
+
type: "custom",
|
|
66506
|
+
channelKey: gc.channelKey,
|
|
66507
|
+
creatorId: gc.creatorId,
|
|
66508
|
+
creatorName: gc.creatorName,
|
|
66509
|
+
members: gc.members.map((m) => ({ id: m.memberId, name: m.memberName, type: m.memberType }))
|
|
66510
|
+
}
|
|
65921
66511
|
});
|
|
65922
66512
|
return;
|
|
65923
66513
|
}
|
|
66514
|
+
if (path.match(/^\/api\/group-chats\/[^/]+$/) && req.method === "PATCH") {
|
|
66515
|
+
const gcId = path.split("/").pop();
|
|
66516
|
+
if (!this.storage?.groupChatRepo) {
|
|
66517
|
+
this.json(res, 500, { error: "Storage not initialized" });
|
|
66518
|
+
return;
|
|
66519
|
+
}
|
|
66520
|
+
const body = await this.readBody(req);
|
|
66521
|
+
const newName = body["name"];
|
|
66522
|
+
if (newName)
|
|
66523
|
+
this.storage.groupChatRepo.updateName(gcId, newName);
|
|
66524
|
+
this.json(res, 200, { ok: true });
|
|
66525
|
+
return;
|
|
66526
|
+
}
|
|
66527
|
+
if (path.match(/^\/api\/group-chats\/[^/]+$/) && req.method === "DELETE") {
|
|
66528
|
+
const gcId = path.split("/").pop();
|
|
66529
|
+
if (!this.storage?.groupChatRepo) {
|
|
66530
|
+
this.json(res, 500, { error: "Storage not initialized" });
|
|
66531
|
+
return;
|
|
66532
|
+
}
|
|
66533
|
+
this.storage.groupChatRepo.delete(gcId);
|
|
66534
|
+
this.ws?.broadcast({ type: "chat:group_deleted", payload: { chatId: gcId }, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
66535
|
+
this.json(res, 200, { ok: true });
|
|
66536
|
+
return;
|
|
66537
|
+
}
|
|
66538
|
+
if (path.match(/^\/api\/group-chats\/[^/]+\/members$/) && req.method === "POST") {
|
|
66539
|
+
const gcId = path.split("/")[3];
|
|
66540
|
+
if (!this.storage?.groupChatRepo) {
|
|
66541
|
+
this.json(res, 500, { error: "Storage not initialized" });
|
|
66542
|
+
return;
|
|
66543
|
+
}
|
|
66544
|
+
const body = await this.readBody(req);
|
|
66545
|
+
const memberId = body["memberId"];
|
|
66546
|
+
const memberType = body["memberType"] ?? "agent";
|
|
66547
|
+
let memberName = body["memberName"] ?? memberId;
|
|
66548
|
+
if (memberType === "agent") {
|
|
66549
|
+
try {
|
|
66550
|
+
memberName = this.orgService.getAgentManager().getAgent(memberId)?.config?.name ?? memberName;
|
|
66551
|
+
} catch {
|
|
66552
|
+
}
|
|
66553
|
+
}
|
|
66554
|
+
this.storage.groupChatRepo.addMember(gcId, memberId, memberType, memberName);
|
|
66555
|
+
this.ws?.broadcast({ type: "chat:group_updated", payload: { chatId: gcId }, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
66556
|
+
this.json(res, 200, { ok: true });
|
|
66557
|
+
return;
|
|
66558
|
+
}
|
|
66559
|
+
if (path.match(/^\/api\/group-chats\/[^/]+\/members\/[^/]+$/) && req.method === "DELETE") {
|
|
66560
|
+
const parts = path.split("/");
|
|
66561
|
+
const gcId = parts[3];
|
|
66562
|
+
const memberId = parts[5];
|
|
66563
|
+
if (!this.storage?.groupChatRepo) {
|
|
66564
|
+
this.json(res, 500, { error: "Storage not initialized" });
|
|
66565
|
+
return;
|
|
66566
|
+
}
|
|
66567
|
+
this.storage.groupChatRepo.removeMember(gcId, memberId);
|
|
66568
|
+
this.ws?.broadcast({ type: "chat:group_updated", payload: { chatId: gcId }, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
66569
|
+
this.json(res, 200, { ok: true });
|
|
66570
|
+
return;
|
|
66571
|
+
}
|
|
65924
66572
|
if (path === "/api/teams" && req.method === "GET") {
|
|
65925
66573
|
const orgId2 = url.searchParams.get("orgId") ?? "default";
|
|
65926
66574
|
const teams = this.orgService.listTeamsWithMembers(orgId2);
|
|
@@ -66155,7 +66803,7 @@ var init_api_server = __esm({
|
|
|
66155
66803
|
const content = body["content"];
|
|
66156
66804
|
const dir = this.orgService.getTeamDataDir(teamId);
|
|
66157
66805
|
mkdirSync19(dir, { recursive: true });
|
|
66158
|
-
|
|
66806
|
+
writeFileSync17(join23(dir, filename), content ?? "", "utf-8");
|
|
66159
66807
|
this.json(res, 200, { ok: true });
|
|
66160
66808
|
return;
|
|
66161
66809
|
}
|
|
@@ -66685,7 +67333,7 @@ var init_api_server = __esm({
|
|
|
66685
67333
|
const roleDir = this.resolveAgentRoleDir(agent);
|
|
66686
67334
|
if (!roleDir)
|
|
66687
67335
|
continue;
|
|
66688
|
-
const slug = agent.config.name
|
|
67336
|
+
const slug = kebab(agent.config.name, agentId2);
|
|
66689
67337
|
for (const fname of roleFileNames) {
|
|
66690
67338
|
const fpath = join23(roleDir, fname);
|
|
66691
67339
|
if (existsSync27(fpath)) {
|
|
@@ -67000,7 +67648,7 @@ var init_api_server = __esm({
|
|
|
67000
67648
|
}
|
|
67001
67649
|
const body = await this.readBody(req);
|
|
67002
67650
|
const content = body["content"] ?? "";
|
|
67003
|
-
|
|
67651
|
+
writeFileSync17(join23(roleDir, filename), content, "utf-8");
|
|
67004
67652
|
this.json(res, 200, { ok: true });
|
|
67005
67653
|
} catch {
|
|
67006
67654
|
this.json(res, 404, { error: `Agent not found: ${agentId2}` });
|
|
@@ -68339,7 +68987,7 @@ EXPLANATION_END`;
|
|
|
68339
68987
|
const mfName = manifestFilename(pkgType);
|
|
68340
68988
|
const artDir = join23(homedir14(), ".markus", "builder-artifacts", typeDir, manifest.name);
|
|
68341
68989
|
mkdirSync19(artDir, { recursive: true });
|
|
68342
|
-
|
|
68990
|
+
writeFileSync17(join23(artDir, mfName), JSON.stringify(manifest, null, 2), "utf-8");
|
|
68343
68991
|
const artFiles = artifact.files;
|
|
68344
68992
|
if (artFiles) {
|
|
68345
68993
|
for (const [fn, c] of Object.entries(artFiles)) {
|
|
@@ -68347,38 +68995,38 @@ EXPLANATION_END`;
|
|
|
68347
68995
|
continue;
|
|
68348
68996
|
const filePath = join23(artDir, fn);
|
|
68349
68997
|
mkdirSync19(dirname6(filePath), { recursive: true });
|
|
68350
|
-
|
|
68998
|
+
writeFileSync17(filePath, c, "utf-8");
|
|
68351
68999
|
}
|
|
68352
69000
|
}
|
|
68353
69001
|
if (mode === "team") {
|
|
68354
69002
|
const fileSet = new Set(artFiles ? Object.keys(artFiles) : []);
|
|
68355
69003
|
const announcement = artifact.announcement;
|
|
68356
69004
|
if (announcement && !fileSet.has("ANNOUNCEMENT.md")) {
|
|
68357
|
-
|
|
69005
|
+
writeFileSync17(join23(artDir, "ANNOUNCEMENT.md"), announcement, "utf-8");
|
|
68358
69006
|
}
|
|
68359
69007
|
const norms = artifact.norms;
|
|
68360
69008
|
if (norms && !fileSet.has("NORMS.md")) {
|
|
68361
|
-
|
|
69009
|
+
writeFileSync17(join23(artDir, "NORMS.md"), norms, "utf-8");
|
|
68362
69010
|
}
|
|
68363
69011
|
const rawMembers = Array.isArray(artifact.team?.members) ? artifact.team.members : Array.isArray(artifact.members) ? artifact.members : [];
|
|
68364
69012
|
for (const m of rawMembers) {
|
|
68365
69013
|
const mName = m.name ?? "Agent";
|
|
68366
|
-
const slug = mName
|
|
69014
|
+
const slug = kebab(mName, "agent");
|
|
68367
69015
|
const memberDir = join23(artDir, "members", slug);
|
|
68368
69016
|
const roleContent = m.roleContent || m.role_md;
|
|
68369
69017
|
const policiesContent = m.policiesContent || m.policies_md;
|
|
68370
69018
|
const contextContent = m.contextContent || m.context_md;
|
|
68371
69019
|
if (roleContent && !fileSet.has(`members/${slug}/ROLE.md`)) {
|
|
68372
69020
|
mkdirSync19(memberDir, { recursive: true });
|
|
68373
|
-
|
|
69021
|
+
writeFileSync17(join23(memberDir, "ROLE.md"), roleContent, "utf-8");
|
|
68374
69022
|
}
|
|
68375
69023
|
if (policiesContent && !fileSet.has(`members/${slug}/POLICIES.md`)) {
|
|
68376
69024
|
mkdirSync19(memberDir, { recursive: true });
|
|
68377
|
-
|
|
69025
|
+
writeFileSync17(join23(memberDir, "POLICIES.md"), policiesContent, "utf-8");
|
|
68378
69026
|
}
|
|
68379
69027
|
if (contextContent && !fileSet.has(`members/${slug}/CONTEXT.md`)) {
|
|
68380
69028
|
mkdirSync19(memberDir, { recursive: true });
|
|
68381
|
-
|
|
69029
|
+
writeFileSync17(join23(memberDir, "CONTEXT.md"), contextContent, "utf-8");
|
|
68382
69030
|
}
|
|
68383
69031
|
}
|
|
68384
69032
|
const memberFiles = artifact.memberFiles;
|
|
@@ -68387,7 +69035,7 @@ EXPLANATION_END`;
|
|
|
68387
69035
|
const memberDir = join23(artDir, "members", slug);
|
|
68388
69036
|
mkdirSync19(memberDir, { recursive: true });
|
|
68389
69037
|
for (const [fn, c] of Object.entries(files))
|
|
68390
|
-
|
|
69038
|
+
writeFileSync17(join23(memberDir, fn), c, "utf-8");
|
|
68391
69039
|
}
|
|
68392
69040
|
}
|
|
68393
69041
|
}
|
|
@@ -68425,7 +69073,7 @@ EXPLANATION_END`;
|
|
|
68425
69073
|
for (const [fn, content] of Object.entries(files)) {
|
|
68426
69074
|
const filePath = join23(artDir, fn);
|
|
68427
69075
|
mkdirSync19(dirname6(filePath), { recursive: true });
|
|
68428
|
-
|
|
69076
|
+
writeFileSync17(filePath, content, "utf-8");
|
|
68429
69077
|
}
|
|
68430
69078
|
if (source) {
|
|
68431
69079
|
const mfName = manifestFilename(type);
|
|
@@ -68434,7 +69082,7 @@ EXPLANATION_END`;
|
|
|
68434
69082
|
try {
|
|
68435
69083
|
const mf = JSON.parse(readFileSync21(mfPath, "utf-8"));
|
|
68436
69084
|
mf.source = source;
|
|
68437
|
-
|
|
69085
|
+
writeFileSync17(mfPath, JSON.stringify(mf, null, 2), "utf-8");
|
|
68438
69086
|
} catch {
|
|
68439
69087
|
}
|
|
68440
69088
|
}
|
|
@@ -69168,11 +69816,18 @@ EXPLANATION_END`;
|
|
|
69168
69816
|
"Content-Type": "application/json",
|
|
69169
69817
|
"Authorization": `Bearer ${hubToken}`
|
|
69170
69818
|
};
|
|
69171
|
-
|
|
69819
|
+
let hubRes = await fetch(`${hubUrl}/api/items`, {
|
|
69172
69820
|
method: "POST",
|
|
69173
69821
|
headers,
|
|
69174
|
-
body: JSON.stringify(body["payload"])
|
|
69822
|
+
body: JSON.stringify(body["payload"]),
|
|
69823
|
+
redirect: "manual"
|
|
69175
69824
|
});
|
|
69825
|
+
if (hubRes.status >= 300 && hubRes.status < 400) {
|
|
69826
|
+
const location = hubRes.headers.get("location");
|
|
69827
|
+
if (location) {
|
|
69828
|
+
hubRes = await fetch(location, { method: "POST", headers, body: JSON.stringify(body["payload"]), redirect: "manual" });
|
|
69829
|
+
}
|
|
69830
|
+
}
|
|
69176
69831
|
const hubData = await hubRes.json();
|
|
69177
69832
|
this.json(res, hubRes.status, hubData);
|
|
69178
69833
|
} catch (err) {
|
|
@@ -69180,6 +69835,43 @@ EXPLANATION_END`;
|
|
|
69180
69835
|
}
|
|
69181
69836
|
return;
|
|
69182
69837
|
}
|
|
69838
|
+
if (path.startsWith("/api/hub/")) {
|
|
69839
|
+
const hubPath = path.slice("/api/hub".length);
|
|
69840
|
+
const reqUrl = new URL(req.url, `http://${req.headers.host}`);
|
|
69841
|
+
const hubTargetUrl = `${this.hubUrl}/api${hubPath}${reqUrl.search}`;
|
|
69842
|
+
const proxyHeaders = { "Content-Type": "application/json" };
|
|
69843
|
+
const authHeader = req.headers["authorization"];
|
|
69844
|
+
if (authHeader)
|
|
69845
|
+
proxyHeaders["Authorization"] = authHeader;
|
|
69846
|
+
try {
|
|
69847
|
+
let body;
|
|
69848
|
+
if (req.method === "POST" || req.method === "PUT" || req.method === "PATCH") {
|
|
69849
|
+
body = JSON.stringify(await this.readBody(req));
|
|
69850
|
+
}
|
|
69851
|
+
let hubRes = await fetch(hubTargetUrl, {
|
|
69852
|
+
method: req.method,
|
|
69853
|
+
headers: proxyHeaders,
|
|
69854
|
+
body,
|
|
69855
|
+
redirect: "manual"
|
|
69856
|
+
});
|
|
69857
|
+
if (hubRes.status >= 300 && hubRes.status < 400) {
|
|
69858
|
+
const location = hubRes.headers.get("location");
|
|
69859
|
+
if (location) {
|
|
69860
|
+
hubRes = await fetch(location, {
|
|
69861
|
+
method: req.method,
|
|
69862
|
+
headers: proxyHeaders,
|
|
69863
|
+
body,
|
|
69864
|
+
redirect: "manual"
|
|
69865
|
+
});
|
|
69866
|
+
}
|
|
69867
|
+
}
|
|
69868
|
+
const data = await hubRes.json();
|
|
69869
|
+
this.json(res, hubRes.status, data);
|
|
69870
|
+
} catch (err) {
|
|
69871
|
+
this.json(res, 502, { error: `Hub request failed: ${String(err)}` });
|
|
69872
|
+
}
|
|
69873
|
+
return;
|
|
69874
|
+
}
|
|
69183
69875
|
if (path === "/api/settings/hub" && req.method === "GET") {
|
|
69184
69876
|
this.json(res, 200, { hubUrl: this.hubUrl });
|
|
69185
69877
|
return;
|
|
@@ -69191,7 +69883,7 @@ EXPLANATION_END`;
|
|
|
69191
69883
|
try {
|
|
69192
69884
|
if (token) {
|
|
69193
69885
|
mkdirSync19(join23(homedir14(), ".markus"), { recursive: true });
|
|
69194
|
-
|
|
69886
|
+
writeFileSync17(tokenPath, token, "utf-8");
|
|
69195
69887
|
} else if (existsSync27(tokenPath)) {
|
|
69196
69888
|
rmSync3(tokenPath);
|
|
69197
69889
|
}
|
|
@@ -72483,11 +73175,13 @@ var init_requirement_service = __esm({
|
|
|
72483
73175
|
req.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
72484
73176
|
}
|
|
72485
73177
|
/**
|
|
72486
|
-
* Check if all tasks for a requirement are done.
|
|
73178
|
+
* Check if all tasks for a requirement are done. Instead of auto-completing,
|
|
73179
|
+
* notify the creator agent so they can review results and decide whether to
|
|
73180
|
+
* complete the requirement or create additional tasks.
|
|
72487
73181
|
*/
|
|
72488
73182
|
checkCompletion(requirementId2, taskStatuses) {
|
|
72489
73183
|
const req = this.requirements.get(requirementId2);
|
|
72490
|
-
if (!req || req.status === "completed")
|
|
73184
|
+
if (!req || req.status === "completed" || req.status === "cancelled")
|
|
72491
73185
|
return false;
|
|
72492
73186
|
if (req.taskIds.length === 0)
|
|
72493
73187
|
return false;
|
|
@@ -72495,14 +73189,28 @@ var init_requirement_service = __esm({
|
|
|
72495
73189
|
const status = taskStatuses.get(tid);
|
|
72496
73190
|
return status === "completed" || status === "cancelled" || status === "archived";
|
|
72497
73191
|
});
|
|
72498
|
-
if (allDone) {
|
|
72499
|
-
|
|
72500
|
-
|
|
72501
|
-
|
|
72502
|
-
|
|
73192
|
+
if (allDone && req.createdBy && this.agentManager) {
|
|
73193
|
+
try {
|
|
73194
|
+
const agent = this.agentManager.getAgent(req.createdBy);
|
|
73195
|
+
if (agent) {
|
|
73196
|
+
agent.enqueueToMailbox("requirement_update", {
|
|
73197
|
+
summary: `All tasks for requirement "${req.title}" are done \u2014 review needed`,
|
|
73198
|
+
content: [
|
|
73199
|
+
`[REQUIREMENT REVIEW NEEDED] All linked tasks for "${req.title}" (ID: ${requirementId2}) have reached terminal state.`,
|
|
73200
|
+
"",
|
|
73201
|
+
"Please review the results and decide:",
|
|
73202
|
+
`1. If the requirement is fully satisfied, complete it: requirement_update_status(requirement_id="${requirementId2}", status="completed")`,
|
|
73203
|
+
`2. If more work is needed, create additional tasks with task_create(requirement_id="${requirementId2}", ...)`
|
|
73204
|
+
].join("\n"),
|
|
73205
|
+
requirementId: requirementId2,
|
|
73206
|
+
extra: { actionRequired: true }
|
|
73207
|
+
}, {
|
|
73208
|
+
priority: 2,
|
|
73209
|
+
metadata: { senderName: "System", senderRole: "manager" }
|
|
73210
|
+
});
|
|
73211
|
+
}
|
|
73212
|
+
} catch {
|
|
72503
73213
|
}
|
|
72504
|
-
this.broadcast("requirement:completed", req);
|
|
72505
|
-
log62.info("Requirement completed", { id: requirementId2 });
|
|
72506
73214
|
return true;
|
|
72507
73215
|
}
|
|
72508
73216
|
return false;
|
|
@@ -72646,14 +73354,13 @@ var init_requirement_service = __esm({
|
|
|
72646
73354
|
parts.push("2. Abandon this requirement if the feedback indicates it is not needed");
|
|
72647
73355
|
}
|
|
72648
73356
|
const agent = this.agentManager.getAgent(creatorId);
|
|
72649
|
-
const isRejection = decision === "rejected";
|
|
72650
73357
|
agent.enqueueToMailbox("requirement_update", {
|
|
72651
73358
|
summary: `Requirement "${req.title}" ${decision}`,
|
|
72652
73359
|
content: parts.join("\n"),
|
|
72653
73360
|
requirementId: req.id,
|
|
72654
|
-
extra:
|
|
73361
|
+
extra: { actionRequired: true }
|
|
72655
73362
|
}, {
|
|
72656
|
-
priority:
|
|
73363
|
+
priority: 1,
|
|
72657
73364
|
metadata: { senderName: "System", senderRole: "manager" }
|
|
72658
73365
|
});
|
|
72659
73366
|
log62.info("Notified creator agent about requirement decision", {
|
|
@@ -72871,7 +73578,7 @@ var init_knowledge_service = __esm({
|
|
|
72871
73578
|
});
|
|
72872
73579
|
|
|
72873
73580
|
// ../org-manager/dist/file-knowledge-store.js
|
|
72874
|
-
import { readFileSync as readFileSync22, writeFileSync as
|
|
73581
|
+
import { readFileSync as readFileSync22, writeFileSync as writeFileSync18, existsSync as existsSync28, mkdirSync as mkdirSync20, readdirSync as readdirSync11, unlinkSync as unlinkSync3 } from "node:fs";
|
|
72875
73582
|
import { join as join24 } from "node:path";
|
|
72876
73583
|
function readdirSafe(dir) {
|
|
72877
73584
|
try {
|
|
@@ -72927,12 +73634,12 @@ var init_file_knowledge_store = __esm({
|
|
|
72927
73634
|
saveEntry(entry) {
|
|
72928
73635
|
const dir = this.scopeDir(entry.scope, entry.scopeId);
|
|
72929
73636
|
mkdirSync20(dir, { recursive: true });
|
|
72930
|
-
|
|
73637
|
+
writeFileSync18(join24(dir, `${entry.id}.json`), JSON.stringify(entry, null, 2));
|
|
72931
73638
|
}
|
|
72932
73639
|
saveIndex(scope, scopeId, entries2) {
|
|
72933
73640
|
const dir = this.scopeDir(scope, scopeId);
|
|
72934
73641
|
mkdirSync20(dir, { recursive: true });
|
|
72935
|
-
|
|
73642
|
+
writeFileSync18(join24(dir, "_index.json"), JSON.stringify(entries2, null, 2));
|
|
72936
73643
|
}
|
|
72937
73644
|
removeEntryFile(entry) {
|
|
72938
73645
|
const p = join24(this.scopeDir(entry.scope, entry.scopeId), `${entry.id}.json`);
|
|
@@ -73970,7 +74677,8 @@ function openSqlite(dbPath) {
|
|
|
73970
74677
|
{ table: "chat_sessions", column: "is_main", sql: "ALTER TABLE chat_sessions ADD COLUMN is_main INTEGER NOT NULL DEFAULT 0" },
|
|
73971
74678
|
{ table: "mailbox_items", column: "retry_count", sql: "ALTER TABLE mailbox_items ADD COLUMN retry_count INTEGER NOT NULL DEFAULT 0" },
|
|
73972
74679
|
{ table: "users", column: "avatar_url", sql: "ALTER TABLE users ADD COLUMN avatar_url TEXT" },
|
|
73973
|
-
{ table: "agents", column: "avatar_url", sql: "ALTER TABLE agents ADD COLUMN avatar_url TEXT" }
|
|
74680
|
+
{ table: "agents", column: "avatar_url", sql: "ALTER TABLE agents ADD COLUMN avatar_url TEXT" },
|
|
74681
|
+
{ table: "channel_messages", column: "reply_to_id", sql: "ALTER TABLE channel_messages ADD COLUMN reply_to_id TEXT" }
|
|
73974
74682
|
];
|
|
73975
74683
|
for (const m of migrations) {
|
|
73976
74684
|
const cols = _db.prepare(`PRAGMA table_info(${m.table})`).all();
|
|
@@ -74027,7 +74735,7 @@ function migrateToExecutionStreamLogs(db) {
|
|
|
74027
74735
|
log71.info(`Migration: copied ${actLogCount} agent_activity_logs to execution_stream_logs`);
|
|
74028
74736
|
}
|
|
74029
74737
|
}
|
|
74030
|
-
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;
|
|
74738
|
+
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;
|
|
74031
74739
|
var init_sqlite_storage = __esm({
|
|
74032
74740
|
"../storage/dist/sqlite-storage.js"() {
|
|
74033
74741
|
"use strict";
|
|
@@ -74165,6 +74873,7 @@ CREATE TABLE IF NOT EXISTS channel_messages (
|
|
|
74165
74873
|
text TEXT NOT NULL,
|
|
74166
74874
|
mentions TEXT NOT NULL DEFAULT '[]',
|
|
74167
74875
|
metadata TEXT DEFAULT '{}',
|
|
74876
|
+
reply_to_id TEXT,
|
|
74168
74877
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
74169
74878
|
);
|
|
74170
74879
|
CREATE INDEX IF NOT EXISTS idx_channel_messages_channel ON channel_messages(channel, created_at);
|
|
@@ -74525,6 +75234,26 @@ CREATE TABLE IF NOT EXISTS approvals (
|
|
|
74525
75234
|
selected_option TEXT
|
|
74526
75235
|
);
|
|
74527
75236
|
CREATE INDEX IF NOT EXISTS idx_approvals_status ON approvals(status, requested_at DESC);
|
|
75237
|
+
|
|
75238
|
+
CREATE TABLE IF NOT EXISTS group_chats (
|
|
75239
|
+
id TEXT PRIMARY KEY,
|
|
75240
|
+
org_id TEXT NOT NULL DEFAULT 'default',
|
|
75241
|
+
name TEXT NOT NULL,
|
|
75242
|
+
channel_key TEXT NOT NULL UNIQUE,
|
|
75243
|
+
creator_id TEXT NOT NULL,
|
|
75244
|
+
creator_name TEXT NOT NULL,
|
|
75245
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
75246
|
+
);
|
|
75247
|
+
|
|
75248
|
+
CREATE TABLE IF NOT EXISTS group_chat_members (
|
|
75249
|
+
group_chat_id TEXT NOT NULL REFERENCES group_chats(id) ON DELETE CASCADE,
|
|
75250
|
+
member_id TEXT NOT NULL,
|
|
75251
|
+
member_type TEXT NOT NULL CHECK(member_type IN ('human', 'agent')),
|
|
75252
|
+
member_name TEXT NOT NULL,
|
|
75253
|
+
added_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
75254
|
+
PRIMARY KEY (group_chat_id, member_id)
|
|
75255
|
+
);
|
|
75256
|
+
CREATE INDEX IF NOT EXISTS idx_group_chat_members_group ON group_chat_members(group_chat_id);
|
|
74528
75257
|
`;
|
|
74529
75258
|
_db = null;
|
|
74530
75259
|
SqliteOrgRepo = class {
|
|
@@ -75393,7 +76122,7 @@ CREATE INDEX IF NOT EXISTS idx_approvals_status ON approvals(status, requested_a
|
|
|
75393
76122
|
async append(data) {
|
|
75394
76123
|
const id = generateId2("chm");
|
|
75395
76124
|
const ts = now();
|
|
75396
|
-
this.db.prepare("INSERT INTO channel_messages (id, org_id, channel, sender_id, sender_type, sender_name, text, mentions, metadata, created_at) VALUES (
|
|
76125
|
+
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);
|
|
75397
76126
|
return {
|
|
75398
76127
|
id,
|
|
75399
76128
|
orgId: data.orgId,
|
|
@@ -75404,17 +76133,20 @@ CREATE INDEX IF NOT EXISTS idx_approvals_status ON approvals(status, requested_a
|
|
|
75404
76133
|
text: data.text,
|
|
75405
76134
|
mentions: data.mentions ?? [],
|
|
75406
76135
|
metadata: data.metadata ?? null,
|
|
76136
|
+
replyToId: data.replyToId,
|
|
75407
76137
|
createdAt: new Date(ts)
|
|
75408
76138
|
};
|
|
75409
76139
|
}
|
|
75410
76140
|
getMessages(channel, limit = 50, before2) {
|
|
75411
|
-
let q =
|
|
76141
|
+
let q = `SELECT m.*, r.sender_name AS reply_to_sender, SUBSTR(r.text, 1, 120) AS reply_to_text
|
|
76142
|
+
FROM channel_messages m LEFT JOIN channel_messages r ON m.reply_to_id = r.id
|
|
76143
|
+
WHERE m.channel = ?`;
|
|
75412
76144
|
const vals = [channel];
|
|
75413
76145
|
if (before2) {
|
|
75414
|
-
q += " AND created_at < ?";
|
|
76146
|
+
q += " AND m.created_at < ?";
|
|
75415
76147
|
vals.push(before2);
|
|
75416
76148
|
}
|
|
75417
|
-
q += " ORDER BY created_at DESC LIMIT ?";
|
|
76149
|
+
q += " ORDER BY m.created_at DESC LIMIT ?";
|
|
75418
76150
|
vals.push(limit + 1);
|
|
75419
76151
|
const rows = this.db.prepare(q).all(...vals).map((r) => ({
|
|
75420
76152
|
id: r["id"],
|
|
@@ -75426,11 +76158,26 @@ CREATE INDEX IF NOT EXISTS idx_approvals_status ON approvals(status, requested_a
|
|
|
75426
76158
|
text: r["text"],
|
|
75427
76159
|
mentions: fromJson(r["mentions"]),
|
|
75428
76160
|
metadata: fromJson(r["metadata"]),
|
|
76161
|
+
replyToId: r["reply_to_id"] ?? void 0,
|
|
76162
|
+
replyToSender: r["reply_to_sender"] ?? void 0,
|
|
76163
|
+
replyToText: r["reply_to_text"] ?? void 0,
|
|
75429
76164
|
createdAt: toDate(r["created_at"])
|
|
75430
76165
|
}));
|
|
75431
76166
|
const hasMore = rows.length > limit;
|
|
75432
76167
|
return { messages: rows.slice(0, limit).reverse(), hasMore };
|
|
75433
76168
|
}
|
|
76169
|
+
getMessageById(id) {
|
|
76170
|
+
const r = this.db.prepare("SELECT * FROM channel_messages WHERE id = ?").get(id);
|
|
76171
|
+
if (!r)
|
|
76172
|
+
return void 0;
|
|
76173
|
+
return {
|
|
76174
|
+
id: r["id"],
|
|
76175
|
+
senderName: r["sender_name"],
|
|
76176
|
+
senderType: r["sender_type"],
|
|
76177
|
+
senderId: r["sender_id"],
|
|
76178
|
+
text: r["text"]
|
|
76179
|
+
};
|
|
76180
|
+
}
|
|
75434
76181
|
};
|
|
75435
76182
|
SqliteUserRepo = class {
|
|
75436
76183
|
db;
|
|
@@ -76548,6 +77295,96 @@ CREATE INDEX IF NOT EXISTS idx_approvals_status ON approvals(status, requested_a
|
|
|
76548
77295
|
};
|
|
76549
77296
|
}
|
|
76550
77297
|
};
|
|
77298
|
+
SqliteGroupChatRepo = class {
|
|
77299
|
+
db;
|
|
77300
|
+
constructor(db) {
|
|
77301
|
+
this.db = db;
|
|
77302
|
+
}
|
|
77303
|
+
create(data) {
|
|
77304
|
+
const id = generateId2("gc");
|
|
77305
|
+
const channelKey = `group:custom:${id}`;
|
|
77306
|
+
const ts = now();
|
|
77307
|
+
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);
|
|
77308
|
+
const insertMember = this.db.prepare("INSERT OR IGNORE INTO group_chat_members (group_chat_id, member_id, member_type, member_name, added_at) VALUES (?,?,?,?,?)");
|
|
77309
|
+
const members = [];
|
|
77310
|
+
for (const m of data.members) {
|
|
77311
|
+
insertMember.run(id, m.id, m.type, m.name, ts);
|
|
77312
|
+
members.push({ groupChatId: id, memberId: m.id, memberType: m.type, memberName: m.name, addedAt: new Date(ts) });
|
|
77313
|
+
}
|
|
77314
|
+
return {
|
|
77315
|
+
id,
|
|
77316
|
+
orgId: data.orgId,
|
|
77317
|
+
name: data.name,
|
|
77318
|
+
channelKey,
|
|
77319
|
+
creatorId: data.creatorId,
|
|
77320
|
+
creatorName: data.creatorName,
|
|
77321
|
+
createdAt: new Date(ts),
|
|
77322
|
+
members
|
|
77323
|
+
};
|
|
77324
|
+
}
|
|
77325
|
+
list(orgId2) {
|
|
77326
|
+
const rows = this.db.prepare(`SELECT g.*, (SELECT COUNT(*) FROM group_chat_members WHERE group_chat_id = g.id) AS member_count
|
|
77327
|
+
FROM group_chats g WHERE g.org_id = ? ORDER BY g.created_at DESC`).all(orgId2);
|
|
77328
|
+
return rows.map((r) => ({
|
|
77329
|
+
...this.mapRow(r),
|
|
77330
|
+
memberCount: r["member_count"]
|
|
77331
|
+
}));
|
|
77332
|
+
}
|
|
77333
|
+
getById(id) {
|
|
77334
|
+
const r = this.db.prepare("SELECT * FROM group_chats WHERE id = ?").get(id);
|
|
77335
|
+
if (!r)
|
|
77336
|
+
return void 0;
|
|
77337
|
+
const members = this.getMembers(id);
|
|
77338
|
+
return { ...this.mapRow(r), members };
|
|
77339
|
+
}
|
|
77340
|
+
getByChannelKey(channelKey) {
|
|
77341
|
+
const r = this.db.prepare("SELECT * FROM group_chats WHERE channel_key = ?").get(channelKey);
|
|
77342
|
+
if (!r)
|
|
77343
|
+
return void 0;
|
|
77344
|
+
const gc = this.mapRow(r);
|
|
77345
|
+
return { ...gc, members: this.getMembers(gc.id) };
|
|
77346
|
+
}
|
|
77347
|
+
getMembers(groupChatId) {
|
|
77348
|
+
const rows = this.db.prepare("SELECT * FROM group_chat_members WHERE group_chat_id = ?").all(groupChatId);
|
|
77349
|
+
return rows.map((r) => ({
|
|
77350
|
+
groupChatId: r["group_chat_id"],
|
|
77351
|
+
memberId: r["member_id"],
|
|
77352
|
+
memberType: r["member_type"],
|
|
77353
|
+
memberName: r["member_name"],
|
|
77354
|
+
addedAt: toDate(r["added_at"])
|
|
77355
|
+
}));
|
|
77356
|
+
}
|
|
77357
|
+
getAgentMemberIds(channelKey) {
|
|
77358
|
+
const gc = this.db.prepare("SELECT id FROM group_chats WHERE channel_key = ?").get(channelKey);
|
|
77359
|
+
if (!gc)
|
|
77360
|
+
return [];
|
|
77361
|
+
const rows = this.db.prepare("SELECT member_id FROM group_chat_members WHERE group_chat_id = ? AND member_type = 'agent'").all(gc.id);
|
|
77362
|
+
return rows.map((r) => r.member_id);
|
|
77363
|
+
}
|
|
77364
|
+
addMember(groupChatId, memberId, memberType, memberName) {
|
|
77365
|
+
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());
|
|
77366
|
+
}
|
|
77367
|
+
removeMember(groupChatId, memberId) {
|
|
77368
|
+
this.db.prepare("DELETE FROM group_chat_members WHERE group_chat_id = ? AND member_id = ?").run(groupChatId, memberId);
|
|
77369
|
+
}
|
|
77370
|
+
updateName(id, name) {
|
|
77371
|
+
this.db.prepare("UPDATE group_chats SET name = ? WHERE id = ?").run(name, id);
|
|
77372
|
+
}
|
|
77373
|
+
delete(id) {
|
|
77374
|
+
this.db.prepare("DELETE FROM group_chats WHERE id = ?").run(id);
|
|
77375
|
+
}
|
|
77376
|
+
mapRow(r) {
|
|
77377
|
+
return {
|
|
77378
|
+
id: r["id"],
|
|
77379
|
+
orgId: r["org_id"],
|
|
77380
|
+
name: r["name"],
|
|
77381
|
+
channelKey: r["channel_key"],
|
|
77382
|
+
creatorId: r["creator_id"],
|
|
77383
|
+
creatorName: r["creator_name"],
|
|
77384
|
+
createdAt: toDate(r["created_at"])
|
|
77385
|
+
};
|
|
77386
|
+
}
|
|
77387
|
+
};
|
|
76551
77388
|
}
|
|
76552
77389
|
});
|
|
76553
77390
|
|
|
@@ -76564,6 +77401,7 @@ __export(dist_exports3, {
|
|
|
76564
77401
|
SqliteDeliverableRepo: () => SqliteDeliverableRepo,
|
|
76565
77402
|
SqliteExecutionStreamRepo: () => SqliteExecutionStreamRepo,
|
|
76566
77403
|
SqliteExternalAgentRepo: () => SqliteExternalAgentRepo,
|
|
77404
|
+
SqliteGroupChatRepo: () => SqliteGroupChatRepo,
|
|
76567
77405
|
SqliteMailboxRepo: () => SqliteMailboxRepo,
|
|
76568
77406
|
SqliteMarketplaceRatingRepo: () => SqliteMarketplaceRatingRepo,
|
|
76569
77407
|
SqliteMarketplaceSkillRepo: () => SqliteMarketplaceSkillRepo,
|
|
@@ -76633,7 +77471,8 @@ async function initSqliteStorage(url) {
|
|
|
76633
77471
|
mailboxRepo: new storage.SqliteMailboxRepo(db),
|
|
76634
77472
|
decisionRepo: new storage.SqliteDecisionRepo(db),
|
|
76635
77473
|
notificationRepo: new storage.SqliteNotificationRepo(db),
|
|
76636
|
-
approvalRepo: new storage.SqliteApprovalRepo(db)
|
|
77474
|
+
approvalRepo: new storage.SqliteApprovalRepo(db),
|
|
77475
|
+
groupChatRepo: new storage.SqliteGroupChatRepo(db)
|
|
76637
77476
|
};
|
|
76638
77477
|
log72.info("SQLite storage initialized", { path: dbPath });
|
|
76639
77478
|
return bridge;
|
|
@@ -77725,7 +78564,7 @@ var init_startupProgress = __esm({
|
|
|
77725
78564
|
|
|
77726
78565
|
// src/connector-service.ts
|
|
77727
78566
|
import { resolve as resolve15, join as join28, dirname as dirname8 } from "node:path";
|
|
77728
|
-
import { existsSync as existsSync31, readFileSync as readFileSync23, writeFileSync as
|
|
78567
|
+
import { existsSync as existsSync31, readFileSync as readFileSync23, writeFileSync as writeFileSync19, mkdirSync as mkdirSync24, readdirSync as readdirSync12, cpSync as cpSync3 } from "node:fs";
|
|
77729
78568
|
import { homedir as homedir18 } from "node:os";
|
|
77730
78569
|
import { execSync as execSync4 } from "node:child_process";
|
|
77731
78570
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
@@ -77826,7 +78665,7 @@ function writePlatformConfig(connector, markusUrl, token) {
|
|
|
77826
78665
|
setNestedField(config, connector.integration.tokenField, token);
|
|
77827
78666
|
try {
|
|
77828
78667
|
const content = JSON.stringify(config, null, 2);
|
|
77829
|
-
|
|
78668
|
+
writeFileSync19(configPath, content, "utf-8");
|
|
77830
78669
|
return true;
|
|
77831
78670
|
} catch {
|
|
77832
78671
|
return false;
|
|
@@ -77856,7 +78695,7 @@ function installSkillTemplate(connector) {
|
|
|
77856
78695
|
mkdirSync24(targetDir, { recursive: true });
|
|
77857
78696
|
}
|
|
77858
78697
|
try {
|
|
77859
|
-
|
|
78698
|
+
cpSync3(sourceDir, targetDir, { recursive: true });
|
|
77860
78699
|
return true;
|
|
77861
78700
|
} catch {
|
|
77862
78701
|
return false;
|
|
@@ -77906,7 +78745,7 @@ __export(init_exports, {
|
|
|
77906
78745
|
registerInitCommand: () => registerInitCommand
|
|
77907
78746
|
});
|
|
77908
78747
|
import { resolve as resolve16 } from "node:path";
|
|
77909
|
-
import { readFileSync as readFileSync24, existsSync as existsSync32, cpSync as
|
|
78748
|
+
import { readFileSync as readFileSync24, existsSync as existsSync32, cpSync as cpSync4 } from "node:fs";
|
|
77910
78749
|
import { homedir as homedir19 } from "node:os";
|
|
77911
78750
|
function registerInitCommand(program2) {
|
|
77912
78751
|
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) => {
|
|
@@ -77922,7 +78761,7 @@ function registerInitCommand(program2) {
|
|
|
77922
78761
|
});
|
|
77923
78762
|
}
|
|
77924
78763
|
async function quickInit(options) {
|
|
77925
|
-
const { writeFileSync:
|
|
78764
|
+
const { writeFileSync: writeFileSync21, mkdirSync: mkdirSync26 } = await import("node:fs");
|
|
77926
78765
|
const { join: pathJoin } = await import("node:path");
|
|
77927
78766
|
const readline4 = await import("node:readline");
|
|
77928
78767
|
const nonInteractive = options?.nonInteractive ?? false;
|
|
@@ -78195,13 +79034,13 @@ async function quickInit(options) {
|
|
|
78195
79034
|
if (builtinTemplatesDir && existsSync32(builtinTemplatesDir) && !existsSync32(userTemplatesDir)) {
|
|
78196
79035
|
const builtinRoot = resolve16(builtinTemplatesDir, "..");
|
|
78197
79036
|
mkdirSync26(userTemplatesDir, { recursive: true });
|
|
78198
|
-
|
|
79037
|
+
cpSync4(builtinRoot, userTemplatesDir, { recursive: true });
|
|
78199
79038
|
console.log(` Copied templates to ${userTemplatesDir}`);
|
|
78200
79039
|
}
|
|
78201
79040
|
const devRoleDir = pathJoin(userTemplatesDir || pathJoin(process.cwd(), "templates"), "roles", "developer");
|
|
78202
79041
|
if (!existsSync32(devRoleDir)) {
|
|
78203
79042
|
mkdirSync26(devRoleDir, { recursive: true });
|
|
78204
|
-
|
|
79043
|
+
writeFileSync21(
|
|
78205
79044
|
pathJoin(devRoleDir, "ROLE.md"),
|
|
78206
79045
|
[
|
|
78207
79046
|
"---",
|
|
@@ -79109,6 +79948,19 @@ ${reason}`;
|
|
|
79109
79948
|
}, triageProvider);
|
|
79110
79949
|
return response.content;
|
|
79111
79950
|
});
|
|
79951
|
+
agent.getAttentionController().setLLMJudge(async (prompt) => {
|
|
79952
|
+
const response = await llmRouter.chat({
|
|
79953
|
+
messages: [
|
|
79954
|
+
{ 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.' },
|
|
79955
|
+
{ role: "user", content: prompt }
|
|
79956
|
+
],
|
|
79957
|
+
temperature: 0.1,
|
|
79958
|
+
maxTokens: 32
|
|
79959
|
+
}, triageProvider);
|
|
79960
|
+
const raw = response.content.trim().toLowerCase();
|
|
79961
|
+
const valid = ["continue", "preempt", "cancel", "merge", "defer"];
|
|
79962
|
+
return valid.includes(raw) ? raw : "continue";
|
|
79963
|
+
});
|
|
79112
79964
|
agent.getAttentionController().setTriageChatFn(async (messages, tools) => {
|
|
79113
79965
|
const llmTools = tools?.map((t) => ({
|
|
79114
79966
|
name: t.name,
|
|
@@ -80135,6 +80987,12 @@ ${C3.BOLD}${C3.CYAN}\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
|
80135
80987
|
checkWarn("Skills directory not found", `${skillsDir} \u2014 run: markus skill install <name>`);
|
|
80136
80988
|
}
|
|
80137
80989
|
section("Network");
|
|
80990
|
+
const proxyUrl = process.env["HTTPS_PROXY"] || process.env["HTTP_PROXY"] || process.env["https_proxy"] || process.env["http_proxy"];
|
|
80991
|
+
if (proxyUrl) {
|
|
80992
|
+
checkOk("HTTP proxy configured", proxyUrl);
|
|
80993
|
+
} else {
|
|
80994
|
+
checkWarn("HTTP proxy", "not configured \u2014 if behind firewall, set HTTPS_PROXY env var");
|
|
80995
|
+
}
|
|
80138
80996
|
try {
|
|
80139
80997
|
const res = await fetch("https://api.anthropic.com/.well-known/ready", {
|
|
80140
80998
|
method: "HEAD",
|
|
@@ -82816,7 +83674,7 @@ var skill_exports = {};
|
|
|
82816
83674
|
__export(skill_exports, {
|
|
82817
83675
|
registerSkillCommands: () => registerSkillCommands
|
|
82818
83676
|
});
|
|
82819
|
-
import { writeFileSync as
|
|
83677
|
+
import { writeFileSync as writeFileSync20, mkdirSync as mkdirSync25 } from "node:fs";
|
|
82820
83678
|
import { resolve as resolve18 } from "node:path";
|
|
82821
83679
|
function registerSkillCommands(program2) {
|
|
82822
83680
|
const root = program2.command("skill").description("Skills registry and local scaffold");
|
|
@@ -82924,8 +83782,8 @@ Describe what this skill does for agents.
|
|
|
82924
83782
|
## Tools / behavior
|
|
82925
83783
|
|
|
82926
83784
|
`;
|
|
82927
|
-
|
|
82928
|
-
|
|
83785
|
+
writeFileSync20(resolve18(dir, "skill.json"), JSON.stringify(skillJson, null, 2) + "\n");
|
|
83786
|
+
writeFileSync20(resolve18(dir, "SKILL.md"), skillMd);
|
|
82929
83787
|
if (out.json) {
|
|
82930
83788
|
console.log(JSON.stringify({ dir, files: ["skill.json", "SKILL.md"] }, null, 2));
|
|
82931
83789
|
} else {
|