@markus-global/cli 0.8.5 → 0.9.0-rc.0
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/model.d.ts.map +1 -1
- package/dist/commands/model.js +9 -2
- package/dist/commands/model.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +227 -181
- package/dist/commands/start.js.map +1 -1
- package/dist/markus-browser-extension.zip +0 -0
- package/dist/markus.mjs +13015 -6404
- package/dist/web-ui/assets/ChatHelpers-Cb26xxzl.js +3 -0
- package/dist/web-ui/assets/CommentInput-CmSnFGbv.js +1 -0
- package/dist/web-ui/assets/ContentRenderer-CFJndxtW.js +82 -0
- package/dist/web-ui/assets/Deliverables-CL88tGZv.js +6 -0
- package/dist/web-ui/assets/Notifications-BX3Fx5j5.js +1 -0
- package/dist/web-ui/assets/Search-YJ4NBHyk.js +1 -0
- package/dist/web-ui/assets/Settings-B8xQ3rEN.js +8 -0
- package/dist/web-ui/assets/Store-WYrzY0Mr.js +1 -0
- package/dist/web-ui/assets/Team-DwRSN0-a.js +39 -0
- package/dist/web-ui/assets/Work-BZV40eAE.css +1 -0
- package/dist/web-ui/assets/Work-DNGfvBLY.js +11 -0
- package/dist/web-ui/assets/{arc-BdRlL2iO.js → arc-BfS9Mvj7.js} +1 -1
- package/dist/web-ui/assets/architectureDiagram-3BPJPVTR-CFNOE9Ft.js +36 -0
- package/dist/web-ui/assets/{blockDiagram-GPEHLZMM-BpzalW_a.js → blockDiagram-GPEHLZMM-gD6Hylng.js} +2 -2
- package/dist/web-ui/assets/{c4Diagram-AAUBKEIU-BCAI0V-n.js → c4Diagram-AAUBKEIU-8QoBVsn5.js} +2 -2
- package/dist/web-ui/assets/channel-1ow_lsxl.js +1 -0
- package/dist/web-ui/assets/{chunk-2J33WTMH-Dg7rYV2F.js → chunk-2J33WTMH--5nv1yIK.js} +1 -1
- package/dist/web-ui/assets/{chunk-4BX2VUAB-NjIIklhn.js → chunk-4BX2VUAB-DMHNCjRo.js} +1 -1
- package/dist/web-ui/assets/chunk-55IACEB6-DoagUtYg.js +1 -0
- package/dist/web-ui/assets/{chunk-727SXJPM-D4yVMcHy.js → chunk-727SXJPM-sOd6G91V.js} +1 -1
- package/dist/web-ui/assets/{chunk-AQP2D5EJ-BZ2R83hR.js → chunk-AQP2D5EJ-BA1hEJE7.js} +1 -1
- package/dist/web-ui/assets/{chunk-FMBD7UC4-dKKoHR7V.js → chunk-FMBD7UC4-D-4YgaFe.js} +1 -1
- package/dist/web-ui/assets/chunk-ND2GUHAM-CaESAIrE.js +1 -0
- package/dist/web-ui/assets/{chunk-QZHKN3VN-QK1OECHO.js → chunk-QZHKN3VN-k2TrAqxw.js} +1 -1
- package/dist/web-ui/assets/classDiagram-4FO5ZUOK-zJLmwj73.js +1 -0
- package/dist/web-ui/assets/classDiagram-v2-Q7XG4LA2-zJLmwj73.js +1 -0
- package/dist/web-ui/assets/cose-bilkent-S5V4N54A-BWzmZrwb.js +1 -0
- package/dist/web-ui/assets/dagre-BM42HDAG-DESAnnYv.js +4 -0
- package/dist/web-ui/assets/diagram-2AECGRRQ-BYqdvN4F.js +43 -0
- package/dist/web-ui/assets/diagram-5GNKFQAL-CjpQ4xfS.js +10 -0
- package/dist/web-ui/assets/{diagram-KO2AKTUF-BAPzH4PL.js → diagram-KO2AKTUF-CfsDLuOy.js} +2 -2
- package/dist/web-ui/assets/diagram-LMA3HP47-BZdG-8fy.js +24 -0
- package/dist/web-ui/assets/diagram-OG6HWLK6-jCwDnemx.js +24 -0
- package/dist/web-ui/assets/{erDiagram-TEJ5UH35-C5lPAlAw.js → erDiagram-TEJ5UH35-BwJwkebk.js} +7 -7
- package/dist/web-ui/assets/{flowDiagram-I6XJVG4X-B3yPTMtT.js → flowDiagram-I6XJVG4X-DbFMgK3R.js} +2 -2
- package/dist/web-ui/assets/ganttDiagram-6RSMTGT7-BxVoytl3.js +292 -0
- package/dist/web-ui/assets/gitGraphDiagram-PVQCEYII-CEgYJOJY.js +106 -0
- package/dist/web-ui/assets/index-D_HH4ju2.js +666 -0
- package/dist/web-ui/assets/index-YDpknaqo.css +1 -0
- package/dist/web-ui/assets/infoDiagram-5YYISTIA-DQWzABtc.js +2 -0
- package/dist/web-ui/assets/{ishikawaDiagram-YF4QCWOH-BRM-2a0M.js → ishikawaDiagram-YF4QCWOH-DzmIWtTU.js} +2 -2
- package/dist/web-ui/assets/{journeyDiagram-JHISSGLW-ETvNwLl4.js → journeyDiagram-JHISSGLW-BoiaqjiH.js} +2 -2
- package/dist/web-ui/assets/{kanban-definition-UN3LZRKU-D066SbN9.js → kanban-definition-UN3LZRKU-CZeB8ThH.js} +6 -6
- package/dist/web-ui/assets/linear-D1g5s_fh.js +1 -0
- package/dist/web-ui/assets/{mermaid.core-rPZMstL0.js → mermaid.core-B_8QQ1RU.js} +10 -10
- package/dist/web-ui/assets/{mindmap-definition-RKZ34NQL-BKYYirSK.js → mindmap-definition-RKZ34NQL-DFF-GTkZ.js} +2 -2
- package/dist/web-ui/assets/pieDiagram-4H26LBE5-45MXMyT3.js +30 -0
- package/dist/web-ui/assets/{quadrantDiagram-W4KKPZXB-V5VbcvJY.js → quadrantDiagram-W4KKPZXB-kRCd2Nqe.js} +2 -2
- package/dist/web-ui/assets/{requirementDiagram-4Y6WPE33-D2uVfqfP.js → requirementDiagram-4Y6WPE33-DSILTvsw.js} +2 -2
- package/dist/web-ui/assets/{sankeyDiagram-5OEKKPKP-C8ejeGTc.js → sankeyDiagram-5OEKKPKP-CwkyEhye.js} +2 -2
- package/dist/web-ui/assets/{sequenceDiagram-3UESZ5HK-CI3unFup.js → sequenceDiagram-3UESZ5HK-0fo5Z81y.js} +2 -2
- package/dist/web-ui/assets/stateDiagram-AJRCARHV-B74U4HO9.js +1 -0
- package/dist/web-ui/assets/stateDiagram-v2-BHNVJYJU-bSRzYPvh.js +1 -0
- package/dist/web-ui/assets/{timeline-definition-PNZ67QCA-BrkWr53g.js → timeline-definition-PNZ67QCA-mRhWzQjp.js} +2 -2
- package/dist/web-ui/assets/transform-CpIVI4vC.js +1 -0
- package/dist/web-ui/assets/value-DIujSiJ7.js +1 -0
- package/dist/web-ui/assets/{vennDiagram-CIIHVFJN-C8mr8EAq.js → vennDiagram-CIIHVFJN-C82YisLk.js} +2 -2
- package/dist/web-ui/assets/{wardley-L42UT6IY-BFlL89Fu.js → wardley-L42UT6IY-Ba6AG0iH.js} +1 -1
- package/dist/web-ui/assets/{wardleyDiagram-YWT4CUSO-CezrtcCI.js → wardleyDiagram-YWT4CUSO-BSmLAatg.js} +2 -2
- package/dist/web-ui/assets/{xychartDiagram-2RQKCTM6-DrvIX-i_.js → xychartDiagram-2RQKCTM6-Z72DDYUU.js} +2 -2
- package/dist/web-ui/index.html +2 -2
- package/package.json +1 -1
- package/templates/roles/SHARED.md +11 -4
- package/templates/roles/agent-father/ROLE.md +1 -1
- package/templates/roles/secretary/HEARTBEAT.md +2 -2
- package/templates/roles/secretary/ROLE.md +26 -23
- package/templates/roles/skill-architect/ROLE.md +2 -2
- package/templates/roles/team-factory/ROLE.md +1 -1
- package/templates/skills/agent-building/SKILL.md +29 -10
- package/templates/skills/agent-building/skill.json +1 -1
- package/templates/skills/markus-hub-connector/server.mjs +12 -1
- package/templates/skills/skill-building/SKILL.md +30 -10
- package/templates/skills/skill-building/skill.json +1 -1
- package/templates/skills/team-building/SKILL.md +32 -13
- package/templates/skills/team-building/skill.json +1 -1
- package/dist/web-ui/assets/architectureDiagram-3BPJPVTR-D0tUssz3.js +0 -36
- package/dist/web-ui/assets/channel-CVrfB_bR.js +0 -1
- package/dist/web-ui/assets/chunk-55IACEB6-D1NbvEGQ.js +0 -1
- package/dist/web-ui/assets/chunk-ND2GUHAM-8DcNh3kD.js +0 -1
- package/dist/web-ui/assets/classDiagram-4FO5ZUOK-vzqgiM0p.js +0 -1
- package/dist/web-ui/assets/classDiagram-v2-Q7XG4LA2-vzqgiM0p.js +0 -1
- package/dist/web-ui/assets/cose-bilkent-S5V4N54A-70zGiErc.js +0 -1
- package/dist/web-ui/assets/dagre-BM42HDAG-CjH95T53.js +0 -4
- package/dist/web-ui/assets/diagram-2AECGRRQ-D1h-kJfb.js +0 -43
- package/dist/web-ui/assets/diagram-5GNKFQAL-BfpyuUIG.js +0 -10
- package/dist/web-ui/assets/diagram-LMA3HP47-BzZcoqw9.js +0 -24
- package/dist/web-ui/assets/diagram-OG6HWLK6-UzfnxP-8.js +0 -24
- package/dist/web-ui/assets/ganttDiagram-6RSMTGT7-M0LiSJYB.js +0 -292
- package/dist/web-ui/assets/gitGraphDiagram-PVQCEYII-DMJ8bJ-V.js +0 -106
- package/dist/web-ui/assets/index-C0TJwd4k.js +0 -776
- package/dist/web-ui/assets/index-C1_HX8vL.css +0 -1
- package/dist/web-ui/assets/infoDiagram-5YYISTIA-H3O_dXaX.js +0 -2
- package/dist/web-ui/assets/linear-w-ZA_ecm.js +0 -1
- package/dist/web-ui/assets/pieDiagram-4H26LBE5-Cwjk7qIl.js +0 -30
- package/dist/web-ui/assets/stateDiagram-AJRCARHV-C4edfdOw.js +0 -1
- package/dist/web-ui/assets/stateDiagram-v2-BHNVJYJU-CzloKUKx.js +0 -1
package/dist/commands/start.js
CHANGED
|
@@ -3,7 +3,7 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { createRequire } from 'node:module';
|
|
5
5
|
import { allTemplateDirs, resolveTemplatesDir, resolveWebUiDir } from '../paths.js';
|
|
6
|
-
import { loadConfig, getDefaultConfigPath, createLogger, closeRuntimeLogger, checkForUpdate, userId,
|
|
6
|
+
import { loadConfig, saveConfig, getDefaultConfigPath, createLogger, closeRuntimeLogger, checkForUpdate, userId, PROVIDERS, } from '@markus/shared';
|
|
7
7
|
import { AgentManager, LLMRouter, LLMLogger, ModelCatalogService, RoleLoader, createDefaultSkillRegistry, ExternalAgentGateway, } from '@markus/core';
|
|
8
8
|
import { OrganizationService, TaskService, APIServer, HITLService, BillingService, LicenseService, TelemetryService, AuditService, ProjectService, RequirementService, KnowledgeService, FileKnowledgeStore, DeliverableService, ReportService, TrustService, ArchiveService, StaleDetector, ScheduledTaskRunner, initStorage, searchRegistries, installSkill, WorkflowService, WorkflowRunner, WorkflowScheduler, } from '@markus/org-manager';
|
|
9
9
|
import { MessageRouter, FeishuAdapter, WebUIAdapter } from '@markus/comms';
|
|
@@ -40,128 +40,134 @@ export async function createServices(config) {
|
|
|
40
40
|
const providerConfigs = {};
|
|
41
41
|
let defaultProvider = config.llm.defaultProvider;
|
|
42
42
|
const llmTimeoutMs = Number(process.env['LLM_TIMEOUT_MS']) || config.llm.timeoutMs || undefined;
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
const siliconflowKey = config.llm.providers['siliconflow']?.apiKey ?? process.env['SILICONFLOW_API_KEY'];
|
|
62
|
-
if (siliconflowKey) {
|
|
63
|
-
providerConfigs['siliconflow'] = {
|
|
64
|
-
provider: 'siliconflow',
|
|
65
|
-
model: config.llm.providers['siliconflow']?.model ?? process.env['SILICONFLOW_MODEL'] ?? 'Qwen/Qwen3.5-35B-A3B',
|
|
66
|
-
apiKey: siliconflowKey,
|
|
67
|
-
baseUrl: config.llm.providers['siliconflow']?.baseUrl ??
|
|
68
|
-
process.env['SILICONFLOW_BASE_URL'] ??
|
|
69
|
-
'https://api.siliconflow.cn/v1',
|
|
70
|
-
timeoutMs: llmTimeoutMs,
|
|
71
|
-
};
|
|
72
|
-
if (config.llm.defaultProvider === 'siliconflow') {
|
|
73
|
-
defaultProvider = 'siliconflow';
|
|
43
|
+
const providerMeta = new Map(PROVIDERS.map((p) => [p.id, p]));
|
|
44
|
+
const envPrefix = (id) => id.toUpperCase().replace(/-/g, '_');
|
|
45
|
+
// Markus Cloud — OpenRouter member key only (chat + search).
|
|
46
|
+
const markusCfg = config.llm.providers['markus'] ?? {};
|
|
47
|
+
const markusOrKey = markusCfg.apiKey?.startsWith('sk-or-')
|
|
48
|
+
? markusCfg.apiKey
|
|
49
|
+
: (markusCfg.apiKey && !/^markus[_-]/i.test(markusCfg.apiKey) ? markusCfg.apiKey : '');
|
|
50
|
+
if (markusOrKey) {
|
|
51
|
+
const cfgBaseUrl = markusCfg.baseUrl ?? '';
|
|
52
|
+
const markusOrBase = (cfgBaseUrl && !/localhost:8787|127\.0\.0\.1:8787|workers\.dev|markus-proxy/i.test(cfgBaseUrl))
|
|
53
|
+
? cfgBaseUrl
|
|
54
|
+
: 'https://openrouter.ai/api/v1';
|
|
55
|
+
let markusModelsUrl = markusCfg.modelsUrl ?? process.env['MARKUS_MODELS_URL'] ?? '';
|
|
56
|
+
const hubBase = (config.hub?.url || process.env['MARKUS_HUB_URL'] || '').replace(/\/+$/, '');
|
|
57
|
+
if (!markusModelsUrl && hubBase) {
|
|
58
|
+
markusModelsUrl = `${hubBase}/api/models/live/markus`;
|
|
74
59
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
'https://api-st.siliconflow.cn/v1',
|
|
85
|
-
timeoutMs: llmTimeoutMs,
|
|
86
|
-
};
|
|
87
|
-
if (config.llm.defaultProvider === 'siliconflow-intl') {
|
|
88
|
-
defaultProvider = 'siliconflow-intl';
|
|
60
|
+
if (markusModelsUrl && hubBase) {
|
|
61
|
+
try {
|
|
62
|
+
const mu = new URL(markusModelsUrl);
|
|
63
|
+
const hu = new URL(hubBase.includes('://') ? hubBase : `http://${hubBase}`);
|
|
64
|
+
if (mu.host !== hu.host) {
|
|
65
|
+
markusModelsUrl = `${hubBase}/api/models/live/markus`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch { /* keep */ }
|
|
89
69
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
70
|
+
const prev = markusCfg;
|
|
71
|
+
const needPersist = (markusModelsUrl && markusModelsUrl !== (prev.modelsUrl ?? ''))
|
|
72
|
+
|| (markusOrBase && markusOrBase !== (prev.baseUrl ?? ''))
|
|
73
|
+
|| (markusOrKey && markusOrKey !== (prev.apiKey ?? ''))
|
|
74
|
+
|| !!(prev.subscriptionKey || prev.proxyUrl);
|
|
75
|
+
if (needPersist) {
|
|
76
|
+
try {
|
|
77
|
+
saveConfig({
|
|
78
|
+
llm: {
|
|
79
|
+
providers: {
|
|
80
|
+
markus: {
|
|
81
|
+
apiKey: markusOrKey,
|
|
82
|
+
baseUrl: markusOrBase,
|
|
83
|
+
...(markusModelsUrl ? { modelsUrl: markusModelsUrl } : {}),
|
|
84
|
+
...(prev.model ? { model: prev.model } : {}),
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
log.info('Persisted Markus OpenRouter credentials', {
|
|
90
|
+
modelsUrl: markusModelsUrl,
|
|
91
|
+
baseUrl: markusOrBase,
|
|
92
|
+
hasOrKey: true,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
log.warn('Failed to persist Markus config', { error: String(err) });
|
|
97
|
+
}
|
|
104
98
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
baseUrl: config.llm.providers['minimax-cn']?.baseUrl ??
|
|
113
|
-
process.env['MINIMAX_CN_BASE_URL'] ??
|
|
114
|
-
'https://api.minimaxi.com/v1',
|
|
115
|
-
timeoutMs: llmTimeoutMs,
|
|
116
|
-
};
|
|
117
|
-
if (config.llm.defaultProvider === 'minimax-cn') {
|
|
118
|
-
defaultProvider = 'minimax-cn';
|
|
99
|
+
process.env['MARKUS_OPENROUTER_KEY'] = markusOrKey;
|
|
100
|
+
process.env['MARKUS_OPENROUTER_BASE'] = markusOrBase;
|
|
101
|
+
delete process.env['MARKUS_SUBSCRIPTION_KEY'];
|
|
102
|
+
delete process.env['MARKUS_PROXY_URL'];
|
|
103
|
+
delete process.env['MARKUS_SEARCH_URL'];
|
|
104
|
+
if (markusModelsUrl && !process.env['MARKUS_MODELS_URL']) {
|
|
105
|
+
process.env['MARKUS_MODELS_URL'] = markusModelsUrl;
|
|
119
106
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
apiKey: openrouterKey,
|
|
127
|
-
baseUrl: config.llm.providers['openrouter']?.baseUrl ??
|
|
128
|
-
process.env['OPENROUTER_BASE_URL'] ??
|
|
129
|
-
'https://openrouter.ai/api/v1',
|
|
107
|
+
providerConfigs['markus'] = {
|
|
108
|
+
provider: 'markus',
|
|
109
|
+
model: markusCfg.model ?? '',
|
|
110
|
+
apiKey: markusOrKey,
|
|
111
|
+
baseUrl: markusOrBase,
|
|
112
|
+
...(markusModelsUrl ? { modelsUrl: markusModelsUrl } : {}),
|
|
130
113
|
timeoutMs: llmTimeoutMs,
|
|
131
114
|
};
|
|
132
|
-
if (config.llm.defaultProvider === 'openrouter') {
|
|
133
|
-
defaultProvider = 'openrouter';
|
|
134
|
-
}
|
|
135
115
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
116
|
+
else if (config.llm.defaultProvider === 'markus') {
|
|
117
|
+
log.warn('Markus Cloud AI is set as the default provider but no OpenRouter member key is configured. ' +
|
|
118
|
+
'Sign in to Markus Hub to obtain credentials.');
|
|
119
|
+
}
|
|
120
|
+
// Load every provider from markus.json (+ env fallbacks). Previously only a
|
|
121
|
+
// hardcoded subset was wired on boot, so Google/Ollama/DashScope/custom
|
|
122
|
+
// providers saved via Settings vanished after restart (#244).
|
|
123
|
+
const candidateNames = new Set([
|
|
124
|
+
...Object.keys(config.llm.providers ?? {}),
|
|
125
|
+
...PROVIDERS.map((p) => p.id),
|
|
126
|
+
]);
|
|
127
|
+
candidateNames.delete('markus');
|
|
128
|
+
for (const name of candidateNames) {
|
|
129
|
+
const cfg = config.llm.providers[name] ?? {};
|
|
130
|
+
const meta = providerMeta.get(name);
|
|
131
|
+
const prefix = envPrefix(name);
|
|
132
|
+
if (name === 'ollama') {
|
|
133
|
+
const baseUrl = cfg.baseUrl
|
|
134
|
+
?? process.env['OLLAMA_BASE_URL']
|
|
135
|
+
?? meta?.baseUrl;
|
|
136
|
+
const model = cfg.model
|
|
137
|
+
?? process.env['OLLAMA_MODEL']
|
|
138
|
+
?? meta?.defaultModel
|
|
139
|
+
?? 'llama3';
|
|
140
|
+
if (baseUrl || cfg.model || process.env['OLLAMA_BASE_URL']) {
|
|
141
|
+
providerConfigs['ollama'] = {
|
|
142
|
+
provider: 'ollama',
|
|
143
|
+
model,
|
|
144
|
+
...(baseUrl ? { baseUrl } : {}),
|
|
145
|
+
timeoutMs: llmTimeoutMs,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
continue;
|
|
149
149
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
150
|
+
const apiKey = cfg.apiKey
|
|
151
|
+
?? (meta ? process.env[meta.envKey] : undefined)
|
|
152
|
+
?? process.env[`${prefix}_API_KEY`];
|
|
153
|
+
if (!apiKey)
|
|
154
|
+
continue;
|
|
155
|
+
const model = cfg.model
|
|
156
|
+
?? process.env[`${prefix}_MODEL`]
|
|
157
|
+
?? meta?.defaultModel
|
|
158
|
+
?? (name === 'anthropic' ? config.llm.defaultModel : undefined)
|
|
159
|
+
?? '';
|
|
160
|
+
const baseUrl = cfg.baseUrl
|
|
161
|
+
?? process.env[`${prefix}_BASE_URL`]
|
|
162
|
+
?? meta?.baseUrl;
|
|
163
|
+
providerConfigs[name] = {
|
|
164
|
+
// Name may be a known LLMProvider or a custom OpenAI-compatible id.
|
|
165
|
+
provider: name,
|
|
166
|
+
model,
|
|
167
|
+
apiKey,
|
|
168
|
+
...(baseUrl ? { baseUrl } : {}),
|
|
160
169
|
timeoutMs: llmTimeoutMs,
|
|
161
170
|
};
|
|
162
|
-
if (config.llm.defaultProvider === 'deepseek') {
|
|
163
|
-
defaultProvider = 'deepseek';
|
|
164
|
-
}
|
|
165
171
|
}
|
|
166
172
|
// If the configured default provider has no API key, fall back to the first available one
|
|
167
173
|
if (!providerConfigs[defaultProvider]) {
|
|
@@ -474,6 +480,20 @@ async function startServerCore(config, values, opts) {
|
|
|
474
480
|
if (config.integrations?.search?.bochaApiKey && !process.env['BOCHA_API_KEY']) {
|
|
475
481
|
process.env['BOCHA_API_KEY'] = config.integrations.search.bochaApiKey;
|
|
476
482
|
}
|
|
483
|
+
// Markus-hosted search (OpenRouter member key): opt-out + BYOK disable list.
|
|
484
|
+
if (config.integrations?.search?.useMarkusHosted === false) {
|
|
485
|
+
process.env['MARKUS_SEARCH_ENABLED'] = '0';
|
|
486
|
+
}
|
|
487
|
+
if (config.integrations?.search?.markusSearchModel) {
|
|
488
|
+
process.env['MARKUS_SEARCH_OR_MODEL'] = config.integrations.search.markusSearchModel;
|
|
489
|
+
}
|
|
490
|
+
{
|
|
491
|
+
const s = config.integrations?.search ?? {};
|
|
492
|
+
const disabled = ['serper', 'tavily', 'bing', 'google', 'serpapi', 'brave', 'exa', 'bocha']
|
|
493
|
+
.filter(id => s[`${id}Enabled`] === false);
|
|
494
|
+
if (disabled.length)
|
|
495
|
+
process.env['SEARCH_DISABLED_PROVIDERS'] = disabled.join(',');
|
|
496
|
+
}
|
|
477
497
|
const apiPort = Number(values['port']) || config.server.apiPort;
|
|
478
498
|
const { orgService, taskService, agentManager, llmRouter, skillRegistry, hitlService, billingService, licenseService, telemetryService, auditService, bootstrapOwnerId, } = await createServices(config);
|
|
479
499
|
progress?.complete(3, 'SQLite storage initialised');
|
|
@@ -673,17 +693,40 @@ async function startServerCore(config, values, opts) {
|
|
|
673
693
|
if (builtinTeamsDir && existsSync(builtinTeamsDir)) {
|
|
674
694
|
builderService.setBuiltinTeamTemplatesDir(builtinTeamsDir);
|
|
675
695
|
}
|
|
696
|
+
builderService.ensureArtifactDirs();
|
|
676
697
|
agentManager.setBuilderService(builderService);
|
|
677
698
|
}
|
|
678
699
|
const hubClient = apiServer.getHubClient();
|
|
679
700
|
if (hubClient) {
|
|
680
701
|
agentManager.setHubClient(hubClient);
|
|
681
702
|
}
|
|
682
|
-
// Wire team/agent update callbacks for manager tools
|
|
703
|
+
// Wire team/agent update callbacks for manager + secretary tools
|
|
683
704
|
agentManager.setTeamUpdater(async (teamId, data) => {
|
|
684
705
|
const team = await orgService.updateTeam(teamId, data);
|
|
685
706
|
return { id: teamId, name: team.name, description: team.description };
|
|
686
707
|
});
|
|
708
|
+
agentManager.setTeamCreator(async (orgId, name, description) => {
|
|
709
|
+
const team = await orgService.createTeam(orgId, name, description);
|
|
710
|
+
// Same event POST /api/teams emits — Team Chat sidebar refreshes without a full page reload.
|
|
711
|
+
apiServer.getWSBroadcaster()?.broadcast({
|
|
712
|
+
type: 'chat:group_created',
|
|
713
|
+
payload: {
|
|
714
|
+
chatId: `group:${team.id}`,
|
|
715
|
+
name: team.name,
|
|
716
|
+
creatorId: '',
|
|
717
|
+
creatorName: '',
|
|
718
|
+
teamId: team.id,
|
|
719
|
+
},
|
|
720
|
+
timestamp: new Date().toISOString(),
|
|
721
|
+
});
|
|
722
|
+
return { id: team.id, name: team.name, description: team.description };
|
|
723
|
+
});
|
|
724
|
+
agentManager.setOrgTeamLister((orgId) => orgService.listTeams(orgId).map(t => ({
|
|
725
|
+
id: t.id,
|
|
726
|
+
name: t.name,
|
|
727
|
+
description: t.description,
|
|
728
|
+
memberAgentIds: t.memberAgentIds,
|
|
729
|
+
})));
|
|
687
730
|
if (storage) {
|
|
688
731
|
agentManager.setAgentConfigPersister(async (agentId, data) => {
|
|
689
732
|
await storage.agentRepo.updateConfig(agentId, data);
|
|
@@ -717,6 +760,7 @@ async function startServerCore(config, values, opts) {
|
|
|
717
760
|
targetUserId: ownerUserId,
|
|
718
761
|
options: opts.options,
|
|
719
762
|
allowFreeform: opts.allowFreeform,
|
|
763
|
+
questions: opts.questions,
|
|
720
764
|
details: { priority: opts.priority, taskId: opts.relatedTaskId, taskTitle },
|
|
721
765
|
});
|
|
722
766
|
});
|
|
@@ -733,6 +777,16 @@ async function startServerCore(config, values, opts) {
|
|
|
733
777
|
metadata: opts.metadata,
|
|
734
778
|
});
|
|
735
779
|
});
|
|
780
|
+
// Seed autonomous-run locale/timezone from the owner's stored preferences.
|
|
781
|
+
try {
|
|
782
|
+
const ownerIdentity = orgService.resolveHumanIdentity(ownerUserId);
|
|
783
|
+
if (ownerIdentity && (ownerIdentity.locale || ownerIdentity.timezone)) {
|
|
784
|
+
agentManager.setRuntimeViewerContext({ locale: ownerIdentity.locale, timezone: ownerIdentity.timezone });
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
catch (e) {
|
|
788
|
+
log.warn('Failed to seed runtime viewer context from owner preferences', { error: String(e) });
|
|
789
|
+
}
|
|
736
790
|
// Ensure every agent has a main session on startup, then persist activity logs to it
|
|
737
791
|
if (storage?.chatSessionRepo) {
|
|
738
792
|
// Migrate legacy assistant messages that lack segments metadata
|
|
@@ -795,6 +849,42 @@ async function startServerCore(config, values, opts) {
|
|
|
795
849
|
log.warn('Failed to persist activity log', { agentId, error: String(e) });
|
|
796
850
|
}
|
|
797
851
|
});
|
|
852
|
+
// Heartbeat interval changed (by an agent via set_heartbeat_interval, or any
|
|
853
|
+
// in-process update): persist the new value so it survives restart.
|
|
854
|
+
agentManager.getEventBus().on('agent:heartbeat-interval-changed', async (evt) => {
|
|
855
|
+
const { agentId, intervalMs } = evt;
|
|
856
|
+
try {
|
|
857
|
+
await storage.agentRepo.updateConfig(agentId, { heartbeatIntervalMs: intervalMs });
|
|
858
|
+
}
|
|
859
|
+
catch (e) {
|
|
860
|
+
log.warn('Failed to persist heartbeat interval change', { agentId, error: String(e) });
|
|
861
|
+
}
|
|
862
|
+
});
|
|
863
|
+
// Team Chat right-panel open/collapse — deliver to the interacting user's UI.
|
|
864
|
+
agentManager.getEventBus().on('agent:ui-layout', (evt) => {
|
|
865
|
+
const event = evt;
|
|
866
|
+
try {
|
|
867
|
+
const payload = {
|
|
868
|
+
agentId: event.agentId,
|
|
869
|
+
action: event.action,
|
|
870
|
+
...(event.panel ? { panel: event.panel } : {}),
|
|
871
|
+
};
|
|
872
|
+
const wsEvent = {
|
|
873
|
+
type: 'ui:right_panel',
|
|
874
|
+
payload,
|
|
875
|
+
timestamp: new Date().toISOString(),
|
|
876
|
+
};
|
|
877
|
+
if (event.targetUserId) {
|
|
878
|
+
ws.sendToUser(event.targetUserId, wsEvent);
|
|
879
|
+
}
|
|
880
|
+
else {
|
|
881
|
+
ws.broadcast(wsEvent);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
catch (e) {
|
|
885
|
+
log.warn('Failed to broadcast ui-layout event', { agentId: event.agentId, error: String(e) });
|
|
886
|
+
}
|
|
887
|
+
});
|
|
798
888
|
// notify_user: persist as regular chat message + WS broadcast + notification bell
|
|
799
889
|
agentManager.getEventBus().on('agent:notify-user', async (evt) => {
|
|
800
890
|
const { agentId, title, body, priority, taskId, requirementId, targetUserId } = evt;
|
|
@@ -1061,15 +1151,30 @@ async function startServerCore(config, values, opts) {
|
|
|
1061
1151
|
durationMs: event.durationMs,
|
|
1062
1152
|
success: event.success,
|
|
1063
1153
|
detail: event.detail,
|
|
1154
|
+
metadata: {
|
|
1155
|
+
cost: event.cost ?? 0,
|
|
1156
|
+
cuCost: event.cuCost,
|
|
1157
|
+
provider: event.provider,
|
|
1158
|
+
inputTokens: event.inputTokens,
|
|
1159
|
+
outputTokens: event.outputTokens,
|
|
1160
|
+
},
|
|
1064
1161
|
});
|
|
1065
1162
|
if (event.tokensUsed && event.type === 'llm_request') {
|
|
1066
|
-
|
|
1163
|
+
const inputTok = event.inputTokens ?? Math.floor(event.tokensUsed * 0.7);
|
|
1164
|
+
const outputTok = event.outputTokens ?? Math.ceil(event.tokensUsed * 0.3);
|
|
1165
|
+
auditService.recordLLMUsage('default', agentId, inputTok, outputTok);
|
|
1067
1166
|
billingService.recordUsage({
|
|
1068
1167
|
orgId: 'default',
|
|
1069
1168
|
agentId,
|
|
1070
1169
|
type: 'llm_tokens',
|
|
1071
1170
|
amount: event.tokensUsed,
|
|
1072
|
-
metadata: {
|
|
1171
|
+
metadata: {
|
|
1172
|
+
action: event.action,
|
|
1173
|
+
cuCost: event.cuCost,
|
|
1174
|
+
provider: event.provider,
|
|
1175
|
+
inputTokens: event.inputTokens,
|
|
1176
|
+
outputTokens: event.outputTokens,
|
|
1177
|
+
},
|
|
1073
1178
|
});
|
|
1074
1179
|
}
|
|
1075
1180
|
if (event.type === 'tool_call') {
|
|
@@ -1082,17 +1187,6 @@ async function startServerCore(config, values, opts) {
|
|
|
1082
1187
|
});
|
|
1083
1188
|
}
|
|
1084
1189
|
});
|
|
1085
|
-
billingService.setToolCallsTodayProvider(() => {
|
|
1086
|
-
let total = 0;
|
|
1087
|
-
for (const a of agentManager.listAgents()) {
|
|
1088
|
-
try {
|
|
1089
|
-
total += agentManager.getAgent(a.id).getUsageStats().toolCallsToday;
|
|
1090
|
-
}
|
|
1091
|
-
catch { /* not loaded */ }
|
|
1092
|
-
}
|
|
1093
|
-
return total;
|
|
1094
|
-
});
|
|
1095
|
-
agentManager.setToolCallLimitChecker(() => billingService.checkLimit('default', 'tool_call'));
|
|
1096
1190
|
// Wire agent state changes to DB persistence + WS broadcast
|
|
1097
1191
|
if (storage) {
|
|
1098
1192
|
agentManager.setStateChangeHandler(async (agentId, state) => {
|
|
@@ -1282,27 +1376,11 @@ async function startServerCore(config, values, opts) {
|
|
|
1282
1376
|
}
|
|
1283
1377
|
},
|
|
1284
1378
|
});
|
|
1285
|
-
// Wire TriageJudge — used as fallback when full-session deliberation
|
|
1286
|
-
// (delegate.performDeliberation) is not triggered or returns null.
|
|
1287
|
-
const triageProvider = agent.config.llmConfig?.modelMode === 'custom'
|
|
1288
|
-
? agent.config.llmConfig.primary : undefined;
|
|
1289
|
-
const triageSystemPrompt = 'You are a mailbox triage assistant. Output ONLY a single JSON object — no explanation, no markdown fences, no <think> tags. Start your response with {';
|
|
1290
|
-
const triageCacheSegments = [{ content: triageSystemPrompt, cacheBreakpoint: true }];
|
|
1291
|
-
agent.getAttentionController().setTriageJudge(async (prompt) => {
|
|
1292
|
-
const response = await llmRouter.chat({
|
|
1293
|
-
messages: [
|
|
1294
|
-
{ role: 'system', content: triageSystemPrompt },
|
|
1295
|
-
{ role: 'user', content: prompt },
|
|
1296
|
-
],
|
|
1297
|
-
systemCacheSegments: triageCacheSegments,
|
|
1298
|
-
temperature: TRIAGE_TEMPERATURE,
|
|
1299
|
-
maxTokens: TRIAGE_MAX_TOKENS,
|
|
1300
|
-
}, triageProvider);
|
|
1301
|
-
return response.content;
|
|
1302
|
-
});
|
|
1303
1379
|
// Wire LLM interrupt judge — evaluates whether to preempt current
|
|
1304
1380
|
// work when a new message arrives (e.g. "stop publishing", "pause that task").
|
|
1305
1381
|
// Only called when heuristics return 'continue' for ambiguous cases.
|
|
1382
|
+
const triageProvider = agent.config.llmConfig?.modelMode === 'custom'
|
|
1383
|
+
? agent.config.llmConfig.primary : undefined;
|
|
1306
1384
|
const interruptSystemPrompt = '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.';
|
|
1307
1385
|
const interruptCacheSegments = [{ content: interruptSystemPrompt, cacheBreakpoint: true }];
|
|
1308
1386
|
agent.getAttentionController().setLLMJudge(async (prompt) => {
|
|
@@ -1313,47 +1391,15 @@ async function startServerCore(config, values, opts) {
|
|
|
1313
1391
|
],
|
|
1314
1392
|
systemCacheSegments: interruptCacheSegments,
|
|
1315
1393
|
temperature: 0.1,
|
|
1316
|
-
maxTokens: 32
|
|
1394
|
+
// No maxTokens cap: a tight cap (e.g. 32) is fully consumed by a
|
|
1395
|
+
// reasoning model's hidden reasoning, leaving content empty so the
|
|
1396
|
+
// judge always degrades to 'continue'. The router fills the model's
|
|
1397
|
+
// real limit; the one-word answer stops the generation naturally.
|
|
1317
1398
|
}, triageProvider);
|
|
1318
1399
|
const raw = response.content.trim().toLowerCase();
|
|
1319
1400
|
const valid = ['continue', 'preempt', 'cancel', 'merge', 'defer'];
|
|
1320
1401
|
return valid.includes(raw) ? raw : 'continue';
|
|
1321
1402
|
});
|
|
1322
|
-
// Wire triage chat function (fallback mini tool loop, used when
|
|
1323
|
-
// performDeliberation is not available or deliberation is disabled).
|
|
1324
|
-
// The attention controller prefers full-session deliberation via
|
|
1325
|
-
// delegate.performDeliberation when available.
|
|
1326
|
-
agent.getAttentionController().setTriageChatFn(async (messages, tools) => {
|
|
1327
|
-
const llmTools = tools?.map(t => ({
|
|
1328
|
-
name: t.name,
|
|
1329
|
-
description: t.description,
|
|
1330
|
-
inputSchema: t.inputSchema,
|
|
1331
|
-
}));
|
|
1332
|
-
const response = await llmRouter.chat({
|
|
1333
|
-
messages: messages,
|
|
1334
|
-
tools: llmTools,
|
|
1335
|
-
temperature: TRIAGE_TEMPERATURE,
|
|
1336
|
-
maxTokens: TRIAGE_MAX_TOKENS,
|
|
1337
|
-
}, triageProvider);
|
|
1338
|
-
return { content: response.content, toolCalls: response.toolCalls, reasoningContent: response.reasoningContent };
|
|
1339
|
-
});
|
|
1340
|
-
// Wire read-only triage tools from the agent's tool set
|
|
1341
|
-
const triageToolMap = new Map();
|
|
1342
|
-
const agentTools = agent.getTools();
|
|
1343
|
-
for (const toolName of TRIAGE_ALLOWED_TOOLS) {
|
|
1344
|
-
const handler = agentTools.get(toolName);
|
|
1345
|
-
if (handler) {
|
|
1346
|
-
triageToolMap.set(toolName, {
|
|
1347
|
-
name: handler.name,
|
|
1348
|
-
description: handler.description,
|
|
1349
|
-
inputSchema: handler.inputSchema,
|
|
1350
|
-
execute: handler.execute.bind(handler),
|
|
1351
|
-
});
|
|
1352
|
-
}
|
|
1353
|
-
}
|
|
1354
|
-
if (triageToolMap.size > 0) {
|
|
1355
|
-
agent.getAttentionController().setTriageTools(triageToolMap);
|
|
1356
|
-
}
|
|
1357
1403
|
}
|
|
1358
1404
|
catch { /* agent not found */ }
|
|
1359
1405
|
};
|