@jungjaehoon/mama-os 0.20.1 → 0.22.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/CHANGELOG.md +31 -0
- package/README.md +12 -4
- package/dist/agent/agent-loop.d.ts.map +1 -1
- package/dist/agent/agent-loop.js +76 -11
- package/dist/agent/agent-loop.js.map +1 -1
- package/dist/agent/code-act/host-bridge.d.ts.map +1 -1
- package/dist/agent/code-act/host-bridge.js +88 -3
- package/dist/agent/code-act/host-bridge.js.map +1 -1
- package/dist/agent/codex-mcp-process.d.ts +1 -0
- package/dist/agent/codex-mcp-process.d.ts.map +1 -1
- package/dist/agent/codex-mcp-process.js +3 -2
- package/dist/agent/codex-mcp-process.js.map +1 -1
- package/dist/agent/gateway-tool-executor.d.ts +24 -11
- package/dist/agent/gateway-tool-executor.d.ts.map +1 -1
- package/dist/agent/gateway-tool-executor.js +349 -30
- package/dist/agent/gateway-tool-executor.js.map +1 -1
- package/dist/agent/gateway-tools.md +7 -2
- package/dist/agent/model-runner.d.ts +8 -0
- package/dist/agent/model-runner.d.ts.map +1 -1
- package/dist/agent/model-runner.js.map +1 -1
- package/dist/agent/os-agent-capabilities.md +13 -12
- package/dist/agent/persistent-cli-adapter.d.ts +6 -0
- package/dist/agent/persistent-cli-adapter.d.ts.map +1 -1
- package/dist/agent/persistent-cli-adapter.js +21 -10
- package/dist/agent/persistent-cli-adapter.js.map +1 -1
- package/dist/agent/tool-registry.d.ts.map +1 -1
- package/dist/agent/tool-registry.js +33 -2
- package/dist/agent/tool-registry.js.map +1 -1
- package/dist/agent/types.d.ts +30 -2
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/api/graph-api.d.ts.map +1 -1
- package/dist/api/graph-api.js +78 -5
- package/dist/api/graph-api.js.map +1 -1
- package/dist/api/index.d.ts +8 -1
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +18 -12
- package/dist/api/index.js.map +1 -1
- package/dist/api/operator-handler.d.ts +19 -0
- package/dist/api/operator-handler.d.ts.map +1 -0
- package/dist/api/operator-handler.js +106 -0
- package/dist/api/operator-handler.js.map +1 -0
- package/dist/api/operator-summary.d.ts +3 -0
- package/dist/api/operator-summary.d.ts.map +1 -0
- package/dist/api/operator-summary.js +72 -0
- package/dist/api/operator-summary.js.map +1 -0
- package/dist/api/operator-tasks-handler.d.ts +7 -0
- package/dist/api/operator-tasks-handler.d.ts.map +1 -0
- package/dist/api/operator-tasks-handler.js +165 -0
- package/dist/api/operator-tasks-handler.js.map +1 -0
- package/dist/api/report-handler.d.ts +7 -0
- package/dist/api/report-handler.d.ts.map +1 -1
- package/dist/api/report-handler.js +30 -0
- package/dist/api/report-handler.js.map +1 -1
- package/dist/api/report-persistence.d.ts +17 -0
- package/dist/api/report-persistence.d.ts.map +1 -0
- package/dist/api/report-persistence.js +91 -0
- package/dist/api/report-persistence.js.map +1 -0
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +402 -250
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/config/config-manager.d.ts.map +1 -1
- package/dist/cli/config/config-manager.js +93 -55
- package/dist/cli/config/config-manager.js.map +1 -1
- package/dist/cli/config/types.d.ts +17 -2
- package/dist/cli/config/types.d.ts.map +1 -1
- package/dist/cli/config/types.js +4 -0
- package/dist/cli/config/types.js.map +1 -1
- package/dist/cli/runtime/agent-loop-init.d.ts +3 -1
- package/dist/cli/runtime/agent-loop-init.d.ts.map +1 -1
- package/dist/cli/runtime/agent-loop-init.js +16 -7
- package/dist/cli/runtime/agent-loop-init.js.map +1 -1
- package/dist/cli/runtime/api-routes-init.d.ts.map +1 -1
- package/dist/cli/runtime/api-routes-init.js +503 -259
- package/dist/cli/runtime/api-routes-init.js.map +1 -1
- package/dist/cli/runtime/api-server-init.d.ts.map +1 -1
- package/dist/cli/runtime/api-server-init.js +12 -7
- package/dist/cli/runtime/api-server-init.js.map +1 -1
- package/dist/cli/runtime/connector-init.d.ts +19 -1
- package/dist/cli/runtime/connector-init.d.ts.map +1 -1
- package/dist/cli/runtime/connector-init.js +32 -2
- package/dist/cli/runtime/connector-init.js.map +1 -1
- package/dist/cli/runtime/gateway-init.d.ts.map +1 -1
- package/dist/cli/runtime/gateway-init.js +3 -1
- package/dist/cli/runtime/gateway-init.js.map +1 -1
- package/dist/cli/runtime/memory-agent-init.d.ts +2 -1
- package/dist/cli/runtime/memory-agent-init.d.ts.map +1 -1
- package/dist/cli/runtime/memory-agent-init.js +15 -3
- package/dist/cli/runtime/memory-agent-init.js.map +1 -1
- package/dist/cli/runtime/message-router-config.d.ts +4 -0
- package/dist/cli/runtime/message-router-config.d.ts.map +1 -0
- package/dist/cli/runtime/message-router-config.js +11 -0
- package/dist/cli/runtime/message-router-config.js.map +1 -0
- package/dist/cli/runtime/scheduler-init.d.ts.map +1 -1
- package/dist/cli/runtime/scheduler-init.js +12 -12
- package/dist/cli/runtime/scheduler-init.js.map +1 -1
- package/dist/connectors/framework/polling-scheduler.d.ts.map +1 -1
- package/dist/connectors/framework/polling-scheduler.js +53 -2
- package/dist/connectors/framework/polling-scheduler.js.map +1 -1
- package/dist/connectors/framework/types.d.ts +8 -0
- package/dist/connectors/framework/types.d.ts.map +1 -1
- package/dist/connectors/kagemusha/query-tools.d.ts.map +1 -1
- package/dist/connectors/kagemusha/query-tools.js +14 -3
- package/dist/connectors/kagemusha/query-tools.js.map +1 -1
- package/dist/connectors/slack/index.d.ts.map +1 -1
- package/dist/connectors/slack/index.js +3 -1
- package/dist/connectors/slack/index.js.map +1 -1
- package/dist/connectors/trello/index.d.ts.map +1 -1
- package/dist/connectors/trello/index.js +6 -2
- package/dist/connectors/trello/index.js.map +1 -1
- package/dist/db/agent-store.d.ts +5 -0
- package/dist/db/agent-store.d.ts.map +1 -1
- package/dist/db/agent-store.js +6 -3
- package/dist/db/agent-store.js.map +1 -1
- package/dist/db/migrations/agent-activity-tool-name.d.ts +8 -0
- package/dist/db/migrations/agent-activity-tool-name.d.ts.map +1 -0
- package/dist/db/migrations/agent-activity-tool-name.js +24 -0
- package/dist/db/migrations/agent-activity-tool-name.js.map +1 -0
- package/dist/db/migrations/wiki-artifacts.d.ts +3 -0
- package/dist/db/migrations/wiki-artifacts.d.ts.map +1 -0
- package/dist/db/migrations/wiki-artifacts.js +24 -0
- package/dist/db/migrations/wiki-artifacts.js.map +1 -0
- package/dist/envelope/run-guard.d.ts +10 -0
- package/dist/envelope/run-guard.d.ts.map +1 -0
- package/dist/envelope/run-guard.js +19 -0
- package/dist/envelope/run-guard.js.map +1 -0
- package/dist/gateways/message-router.d.ts.map +1 -1
- package/dist/gateways/message-router.js +15 -3
- package/dist/gateways/message-router.js.map +1 -1
- package/dist/gateways/types.d.ts +4 -0
- package/dist/gateways/types.d.ts.map +1 -1
- package/dist/multi-agent/agent-event-bus.d.ts +14 -0
- package/dist/multi-agent/agent-event-bus.d.ts.map +1 -1
- package/dist/multi-agent/agent-event-bus.js +34 -0
- package/dist/multi-agent/agent-event-bus.js.map +1 -1
- package/dist/multi-agent/conductor-persona.js +2 -2
- package/dist/multi-agent/dashboard-agent-persona.d.ts +6 -2
- package/dist/multi-agent/dashboard-agent-persona.d.ts.map +1 -1
- package/dist/multi-agent/dashboard-agent-persona.js +65 -32
- package/dist/multi-agent/dashboard-agent-persona.js.map +1 -1
- package/dist/multi-agent/memory-agent-persona.d.ts +1 -1
- package/dist/multi-agent/memory-agent-persona.d.ts.map +1 -1
- package/dist/multi-agent/memory-agent-persona.js +25 -4
- package/dist/multi-agent/memory-agent-persona.js.map +1 -1
- package/dist/multi-agent/runtime-process.d.ts +4 -2
- package/dist/multi-agent/runtime-process.d.ts.map +1 -1
- package/dist/multi-agent/runtime-process.js +7 -4
- package/dist/multi-agent/runtime-process.js.map +1 -1
- package/dist/multi-agent/types.d.ts +1 -1
- package/dist/multi-agent/wiki-agent-persona.d.ts +7 -1
- package/dist/multi-agent/wiki-agent-persona.d.ts.map +1 -1
- package/dist/multi-agent/wiki-agent-persona.js +82 -68
- package/dist/multi-agent/wiki-agent-persona.js.map +1 -1
- package/dist/operator/action-verifier.d.ts +50 -0
- package/dist/operator/action-verifier.d.ts.map +1 -0
- package/dist/operator/action-verifier.js +68 -0
- package/dist/operator/action-verifier.js.map +1 -0
- package/dist/operator/board-reconcile.d.ts +62 -0
- package/dist/operator/board-reconcile.d.ts.map +1 -0
- package/dist/operator/board-reconcile.js +160 -0
- package/dist/operator/board-reconcile.js.map +1 -0
- package/dist/operator/board-slot-instructions.d.ts +26 -0
- package/dist/operator/board-slot-instructions.d.ts.map +1 -0
- package/dist/operator/board-slot-instructions.js +76 -0
- package/dist/operator/board-slot-instructions.js.map +1 -0
- package/dist/operator/connector-delta-repo.d.ts +33 -0
- package/dist/operator/connector-delta-repo.d.ts.map +1 -0
- package/dist/operator/connector-delta-repo.js +0 -0
- package/dist/operator/connector-delta-repo.js.map +1 -0
- package/dist/operator/mama-memory-port.d.ts +13 -0
- package/dist/operator/mama-memory-port.d.ts.map +1 -0
- package/dist/operator/mama-memory-port.js +35 -0
- package/dist/operator/mama-memory-port.js.map +1 -0
- package/dist/operator/operator-interfaces.d.ts +136 -0
- package/dist/operator/operator-interfaces.d.ts.map +1 -0
- package/dist/operator/operator-interfaces.js +15 -0
- package/dist/operator/operator-interfaces.js.map +1 -0
- package/dist/operator/operator-trigger-loop.d.ts +119 -0
- package/dist/operator/operator-trigger-loop.d.ts.map +1 -0
- package/dist/operator/operator-trigger-loop.js +267 -0
- package/dist/operator/operator-trigger-loop.js.map +1 -0
- package/dist/operator/report-run.d.ts +78 -0
- package/dist/operator/report-run.d.ts.map +1 -0
- package/dist/operator/report-run.js +141 -0
- package/dist/operator/report-run.js.map +1 -0
- package/dist/operator/report-scheduler.d.ts +66 -0
- package/dist/operator/report-scheduler.d.ts.map +1 -0
- package/dist/operator/report-scheduler.js +135 -0
- package/dist/operator/report-scheduler.js.map +1 -0
- package/dist/operator/situation-report.d.ts +90 -0
- package/dist/operator/situation-report.d.ts.map +1 -0
- package/dist/operator/situation-report.js +221 -0
- package/dist/operator/situation-report.js.map +1 -0
- package/dist/operator/task-ledger.d.ts +96 -0
- package/dist/operator/task-ledger.d.ts.map +1 -0
- package/dist/operator/task-ledger.js +277 -0
- package/dist/operator/task-ledger.js.map +1 -0
- package/dist/operator/trigger-author.d.ts +45 -0
- package/dist/operator/trigger-author.d.ts.map +1 -0
- package/dist/operator/trigger-author.js +217 -0
- package/dist/operator/trigger-author.js.map +1 -0
- package/dist/operator/trigger-evolve.d.ts +31 -0
- package/dist/operator/trigger-evolve.d.ts.map +1 -0
- package/dist/operator/trigger-evolve.js +51 -0
- package/dist/operator/trigger-evolve.js.map +1 -0
- package/dist/operator/trigger-fire.d.ts +29 -0
- package/dist/operator/trigger-fire.d.ts.map +1 -0
- package/dist/operator/trigger-fire.js +41 -0
- package/dist/operator/trigger-fire.js.map +1 -0
- package/dist/operator/trigger-matcher.d.ts +19 -0
- package/dist/operator/trigger-matcher.d.ts.map +1 -0
- package/dist/operator/trigger-matcher.js +61 -0
- package/dist/operator/trigger-matcher.js.map +1 -0
- package/dist/operator/trigger-registry.d.ts +36 -0
- package/dist/operator/trigger-registry.d.ts.map +1 -0
- package/dist/operator/trigger-registry.js +135 -0
- package/dist/operator/trigger-registry.js.map +1 -0
- package/dist/operator/trigger-review.d.ts +23 -0
- package/dist/operator/trigger-review.d.ts.map +1 -0
- package/dist/operator/trigger-review.js +127 -0
- package/dist/operator/trigger-review.js.map +1 -0
- package/dist/operator/trigger-types.d.ts +84 -0
- package/dist/operator/trigger-types.d.ts.map +1 -0
- package/dist/operator/trigger-types.js +14 -0
- package/dist/operator/trigger-types.js.map +1 -0
- package/dist/scheduler/cron-worker.d.ts.map +1 -1
- package/dist/scheduler/cron-worker.js +3 -1
- package/dist/scheduler/cron-worker.js.map +1 -1
- package/dist/security/security-monitor.d.ts.map +1 -1
- package/dist/security/security-monitor.js +1 -0
- package/dist/security/security-monitor.js.map +1 -1
- package/dist/wiki/obsidian-writer.d.ts +1 -1
- package/dist/wiki/obsidian-writer.d.ts.map +1 -1
- package/dist/wiki/obsidian-writer.js +67 -27
- package/dist/wiki/obsidian-writer.js.map +1 -1
- package/dist/wiki/path-safety.d.ts +2 -0
- package/dist/wiki/path-safety.d.ts.map +1 -0
- package/dist/wiki/path-safety.js +40 -0
- package/dist/wiki/path-safety.js.map +1 -0
- package/dist/wiki/types.d.ts +4 -2
- package/dist/wiki/types.d.ts.map +1 -1
- package/dist/wiki/types.js +1 -1
- package/dist/wiki/types.js.map +1 -1
- package/dist/wiki-artifacts/normalization.d.ts +8 -0
- package/dist/wiki-artifacts/normalization.d.ts.map +1 -0
- package/dist/wiki-artifacts/normalization.js +41 -0
- package/dist/wiki-artifacts/normalization.js.map +1 -0
- package/dist/wiki-artifacts/types.d.ts +52 -0
- package/dist/wiki-artifacts/types.d.ts.map +1 -0
- package/dist/wiki-artifacts/types.js +3 -0
- package/dist/wiki-artifacts/types.js.map +1 -0
- package/dist/wiki-artifacts/wiki-publish-adapter.d.ts +14 -0
- package/dist/wiki-artifacts/wiki-publish-adapter.d.ts.map +1 -0
- package/dist/wiki-artifacts/wiki-publish-adapter.js +71 -0
- package/dist/wiki-artifacts/wiki-publish-adapter.js.map +1 -0
- package/package.json +5 -3
- package/public/ui/assets/index-CKOJQYXA.css +1 -0
- package/public/ui/assets/index-OSgiwL24.js +62 -0
- package/public/ui/index.html +14 -0
- package/public/ui/theme-init.js +20 -0
- package/public/viewer/js/utils/api.js +2 -2
- package/public/viewer/src/utils/api.ts +9 -2
- package/public/viewer/sw.js +1 -1
- package/public/viewer/viewer.html +17 -27
|
@@ -53,6 +53,7 @@ const session_service_js_1 = require("../../validation/session-service.js");
|
|
|
53
53
|
const agent_store_js_1 = require("../../db/agent-store.js");
|
|
54
54
|
const memory_agent_dashboard_js_1 = require("../../memory/memory-agent-dashboard.js");
|
|
55
55
|
const upload_handler_js_1 = require("../../api/upload-handler.js");
|
|
56
|
+
const operator_tasks_handler_js_1 = require("../../api/operator-tasks-handler.js");
|
|
56
57
|
const auth_middleware_js_1 = require("../../api/auth-middleware.js");
|
|
57
58
|
const utilities_js_1 = require("./utilities.js");
|
|
58
59
|
const debugLogger = __importStar(require("@jungjaehoon/mama-core/debug-logger"));
|
|
@@ -200,46 +201,46 @@ async function registerApiRoutes(params) {
|
|
|
200
201
|
}
|
|
201
202
|
// Wire EventBus to tool executor for agent_notices tool
|
|
202
203
|
toolExecutor.setAgentEventBus(eventBus);
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
agent: 'dashboard-agent',
|
|
224
|
-
action: 'publish',
|
|
225
|
-
target: 'briefing',
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
// ── Conductor Persona (section injection — non-destructive) ────────
|
|
229
|
-
const { ensureConductorPersona } = await import('../../multi-agent/conductor-persona.js');
|
|
230
|
-
ensureConductorPersona();
|
|
231
|
-
// ── Dashboard Agent ─────────────────────────────────────────────────
|
|
232
|
-
const { ensureDashboardPersona } = await import('../../multi-agent/dashboard-agent-persona.js');
|
|
233
|
-
ensureDashboardPersona();
|
|
234
|
-
routesLogger.debug('[Dashboard Agent] Persona ensured at ~/.mama/personas/dashboard.md');
|
|
235
|
-
// Merge code-act MCP server into mama-mcp-config.json
|
|
236
|
-
// Makes code_act available to all CLI processes (AgentProcessManager agents)
|
|
204
|
+
(0, operator_tasks_handler_js_1.registerOperatorTaskRoutes)(apiServer.app, {
|
|
205
|
+
getTaskLedger: () => toolExecutor.getTaskLedger(),
|
|
206
|
+
});
|
|
207
|
+
const hasEnabledAgentConfig = (agentId) => {
|
|
208
|
+
const agentConfig = config.multi_agent?.agents?.[agentId];
|
|
209
|
+
return Boolean(agentConfig && agentConfig.enabled !== false);
|
|
210
|
+
};
|
|
211
|
+
// ── Report Slots + legacy dashboard/wiki fanout ───────────────────────
|
|
212
|
+
const dashboardAgentConfigured = hasEnabledAgentConfig('dashboard-agent');
|
|
213
|
+
const wikiAgentConfigured = hasEnabledAgentConfig('wiki-agent');
|
|
214
|
+
// Manual refresh endpoint (kept for compatibility)
|
|
215
|
+
apiServer.app.post('/api/report/refresh', auth_middleware_js_1.requireAuth, (_req, res) => {
|
|
216
|
+
res.json({ ok: true, message: 'Viewer now renders data directly from Intelligence API' });
|
|
217
|
+
});
|
|
218
|
+
// ── Conductor Persona (section injection — non-destructive) ────────
|
|
219
|
+
const { ensureConductorPersona } = await import('../../multi-agent/conductor-persona.js');
|
|
220
|
+
ensureConductorPersona();
|
|
221
|
+
if (dashboardAgentConfigured || wikiAgentConfigured) {
|
|
222
|
+
// Merge code-act MCP server into mama-mcp-config.json.
|
|
223
|
+
// Makes code_act available to configured legacy self-paced agents.
|
|
237
224
|
const codeActServerPath = node_path_1.default.join(__dirname, '../../mcp/code-act-server.js');
|
|
238
225
|
try {
|
|
239
226
|
const mamaMcpConfigPath = node_path_1.default.join((0, node_os_1.homedir)(), '.mama', 'mama-mcp-config.json');
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
227
|
+
let existing = { mcpServers: {} };
|
|
228
|
+
if ((0, node_fs_1.existsSync)(mamaMcpConfigPath)) {
|
|
229
|
+
try {
|
|
230
|
+
const parsedConfig = JSON.parse((0, node_fs_1.readFileSync)(mamaMcpConfigPath, 'utf-8'));
|
|
231
|
+
if (parsedConfig && typeof parsedConfig === 'object' && !Array.isArray(parsedConfig)) {
|
|
232
|
+
existing = parsedConfig;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
catch (parseErr) {
|
|
236
|
+
routesLogger.warn('[api-routes-init] Invalid MCP config JSON; recreating code-act entry:', parseErr);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (!existing.mcpServers ||
|
|
240
|
+
typeof existing.mcpServers !== 'object' ||
|
|
241
|
+
Array.isArray(existing.mcpServers)) {
|
|
242
|
+
existing.mcpServers = {};
|
|
243
|
+
}
|
|
243
244
|
existing.mcpServers['code-act'] = {
|
|
244
245
|
command: 'node',
|
|
245
246
|
args: [codeActServerPath],
|
|
@@ -251,8 +252,23 @@ async function registerApiRoutes(params) {
|
|
|
251
252
|
catch (err) {
|
|
252
253
|
routesLogger.warn('[api-routes-init] Failed to merge code-act into MCP config:', err);
|
|
253
254
|
}
|
|
255
|
+
}
|
|
256
|
+
// report_publish is a core surface (feeds the /ui operator board), not a
|
|
257
|
+
// multi-agent feature: wire it unconditionally. All slot ids are accepted;
|
|
258
|
+
// size/count caps and loud logging live in createReportPublisher.
|
|
259
|
+
{
|
|
260
|
+
const { createReportPublisher } = await import('../../api/report-handler.js');
|
|
261
|
+
toolExecutor.setReportPublisher(createReportPublisher(apiServer.reportStore, apiServer.reportSseClients));
|
|
262
|
+
}
|
|
263
|
+
if (dashboardAgentConfigured) {
|
|
264
|
+
// ── Dashboard Agent ───────────────────────────────────────────────
|
|
265
|
+
const { ensureDashboardPersona } = await import('../../multi-agent/dashboard-agent-persona.js');
|
|
266
|
+
ensureDashboardPersona();
|
|
267
|
+
routesLogger.debug('[Dashboard Agent] Persona ensured at ~/.mama/personas/dashboard.md');
|
|
254
268
|
// Dashboard cron: 30-min interval via AgentProcessManager
|
|
255
|
-
|
|
269
|
+
// Built PER RUN: the tracker's D-day arithmetic needs today's date, and a
|
|
270
|
+
// module-const prompt would freeze it (#134 lesson).
|
|
271
|
+
const buildDashboardPrompt = () => `You are triggered on a schedule. Today is ${new Date().toISOString().slice(0, 10)}. Before writing anything, determine if an update is needed:
|
|
256
272
|
|
|
257
273
|
1. Use agent_notices({limit: 50}) to find the most recent dashboard-agent publish/task_complete notice. Treat that as the last briefing boundary.
|
|
258
274
|
2. Use context_compile first to find recent substantive decisions (limit 20, max_tool_calls 2, strictness "balanced").
|
|
@@ -260,279 +276,507 @@ async function registerApiRoutes(params) {
|
|
|
260
276
|
Do not include dashboard_briefing, wiki_compilation, system-audit, or audit-log labels in the context_compile task text; filter those operational summaries after the packet returns.
|
|
261
277
|
If context_compile is unavailable because there is no active worker envelope, fall back to mama_search once (limit 20).
|
|
262
278
|
3. If NO substantive decisions or agent alerts exist since the last dashboard publish → respond "NO_UPDATE" and stop. Do NOT call report_publish.
|
|
263
|
-
4. If new substantive information exists
|
|
279
|
+
4. If new substantive information exists -> analyze it and publish ALL FOUR board slots (briefing, action_required, decisions, pipeline) in a SINGLE report_publish call, using the board HTML vocabulary from your persona. The pipeline slot is the item tracker projected from task_list (see your persona); compute D-day from today's date above.
|
|
264
280
|
5. Do NOT call mama_save for the briefing; report_publish and agent_activity are the durable operational record.
|
|
265
281
|
|
|
266
282
|
This saves resources. Only publish when there is genuinely new information to report.`;
|
|
267
|
-
|
|
283
|
+
// Owner-initiated refresh skips the NO_UPDATE delta gate: an explicit
|
|
284
|
+
// request means "rebuild the board now", not "tell me nothing changed".
|
|
285
|
+
const buildForcedDashboardPrompt = () => buildDashboardPrompt().replace(/3\. If NO substantive decisions[^\n]*\n/, '3. The owner explicitly requested a fresh board: do NOT reply NO_UPDATE. Rebuild and publish even if nothing changed since the last publish.\n');
|
|
286
|
+
const doDashboardRun = async (opts) => {
|
|
268
287
|
const pm = toolExecutor.getAgentProcessManager();
|
|
269
288
|
if (!pm) {
|
|
270
289
|
routesLogger.warn('[Dashboard Agent] AgentProcessManager not available yet');
|
|
271
290
|
return;
|
|
272
291
|
}
|
|
273
292
|
try {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
293
|
+
// console.log on run outcomes: the DebugLogger only surfaces warn/error in the
|
|
294
|
+
// daemon log, and a silent outcome reads as a hang from the outside.
|
|
295
|
+
console.log(`[Dashboard Agent] run started${opts?.force ? ' (owner-forced, delta gate bypassed)' : ''}`);
|
|
296
|
+
const { noUpdate } = await executeValidatedRun('dashboard-agent', opts?.force ? buildForcedDashboardPrompt() : buildDashboardPrompt());
|
|
297
|
+
console.log(noUpdate
|
|
298
|
+
? '[Dashboard Agent] no changes detected, publish skipped'
|
|
299
|
+
: '[Dashboard Agent] board published');
|
|
282
300
|
}
|
|
283
301
|
catch (err) {
|
|
284
302
|
routesLogger.error('[Dashboard Agent] Error:', err instanceof Error ? err.message : err);
|
|
285
303
|
}
|
|
286
304
|
};
|
|
305
|
+
// ONE board-writer queue: dashboard cron, manual refresh, AND reconcile runs
|
|
306
|
+
// all serialize here -- the shared agent process rejects concurrent requests
|
|
307
|
+
// ('Process is busy' class). Per-job rejection propagates to the caller
|
|
308
|
+
// while the chain itself survives (M8 review: a caller must be able to
|
|
309
|
+
// distinguish its own job's failure).
|
|
310
|
+
let boardWriterChain = Promise.resolve();
|
|
311
|
+
const boardWriterQueue = {
|
|
312
|
+
push(job) {
|
|
313
|
+
const jobPromise = boardWriterChain.then(job);
|
|
314
|
+
boardWriterChain = jobPromise.catch(() => { });
|
|
315
|
+
return jobPromise;
|
|
316
|
+
},
|
|
317
|
+
};
|
|
318
|
+
const runDashboardAgent = (opts) => boardWriterQueue.push(() => doDashboardRun(opts)).catch(() => { });
|
|
287
319
|
// First run after 10s (let connectors poll first), then every 30 min
|
|
288
320
|
setTimeout(runDashboardAgent, 10_000);
|
|
289
321
|
setInterval(runDashboardAgent, 30 * 60 * 1000);
|
|
290
|
-
// Manual trigger
|
|
322
|
+
// Manual trigger (owner-forced: bypasses the delta gate)
|
|
291
323
|
apiServer.app.post('/api/report/agent-refresh', auth_middleware_js_1.requireAuth, async (_req, res) => {
|
|
292
|
-
runDashboardAgent().catch(() => { });
|
|
293
|
-
res.json({ ok: true, message: 'Dashboard agent triggered' });
|
|
324
|
+
runDashboardAgent({ force: true }).catch(() => { });
|
|
325
|
+
res.json({ ok: true, message: 'Dashboard agent triggered (forced refresh)' });
|
|
294
326
|
});
|
|
295
|
-
//
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
const {
|
|
300
|
-
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
//
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
327
|
+
// -- M8 board reconcile leg (freshness layer; default OFF, opt-in like
|
|
328
|
+
// MAMA_TRIGGER_LOOP). The trigger loop emits operator:channel-delta after
|
|
329
|
+
// committing its cursor; the 30-min cron above remains the repair pass.
|
|
330
|
+
if (process.env.MAMA_BOARD_RECONCILE === '1') {
|
|
331
|
+
const { buildReconcilePrompt, ReconcileScheduler } = await import('../../operator/board-reconcile.js');
|
|
332
|
+
const { captureSnapshot, verifyAfterRun, OBLIGATED_TOOLS } = await import('../../operator/action-verifier.js');
|
|
333
|
+
const kagemushaContext = (process.env.MAMA_RECONCILE_TASK_CONTEXT ?? 'native') === 'kagemusha';
|
|
334
|
+
// Verifier deps (M8 Phase 2): run-bound signals only -- trace rows from
|
|
335
|
+
// the dashboard agent's gateway_tool_call activity, notes from the
|
|
336
|
+
// operator ledger. Observe, never block.
|
|
337
|
+
const reconcileLedger = toolExecutor.getTaskLedger();
|
|
338
|
+
// gateway_tool_call rows now populate normalized_tool_name at the logging
|
|
339
|
+
// site; the input_summary fallback covers rows written before that fix.
|
|
340
|
+
const traceToolList = OBLIGATED_TOOLS.map((t) => `'${t}'`).join(',');
|
|
341
|
+
const verifierDeps = {
|
|
342
|
+
getSlots: () => apiServer.reportStore.getAllSorted(),
|
|
343
|
+
getLedgerHash: () => reconcileLedger?.payloadHash() ?? '',
|
|
344
|
+
getScopedNoteMaxId: (scope) => reconcileLedger?.maxNoUpdateId(scope) ?? 0,
|
|
345
|
+
getTraceMaxId: () => {
|
|
346
|
+
if (!sessionsDb)
|
|
347
|
+
return 0;
|
|
348
|
+
const row = sessionsDb
|
|
349
|
+
.prepare(`SELECT MAX(id) AS max_id FROM agent_activity
|
|
350
|
+
WHERE type = 'gateway_tool_call' AND agent_id = 'dashboard-agent'`)
|
|
351
|
+
.get();
|
|
352
|
+
return row.max_id ?? 0;
|
|
353
|
+
},
|
|
354
|
+
countObligatedTraceRowsSince: (maxId) => {
|
|
355
|
+
if (!sessionsDb)
|
|
356
|
+
return 0;
|
|
357
|
+
const row = sessionsDb
|
|
358
|
+
.prepare(`SELECT COUNT(*) AS n FROM agent_activity
|
|
359
|
+
WHERE type = 'gateway_tool_call' AND agent_id = 'dashboard-agent'
|
|
360
|
+
AND id > ? AND (normalized_tool_name IN (${traceToolList}) OR input_summary IN (${traceToolList}))`)
|
|
361
|
+
.get(maxId);
|
|
362
|
+
return row.n;
|
|
363
|
+
},
|
|
364
|
+
};
|
|
365
|
+
const reconcileScheduler = new ReconcileScheduler({
|
|
366
|
+
debounceMs: Number(process.env.MAMA_RECONCILE_DEBOUNCE_MS) || undefined,
|
|
367
|
+
maxWaitMs: Number(process.env.MAMA_RECONCILE_MAX_WAIT_MS) || undefined,
|
|
368
|
+
globalMaxPerHour: Number(process.env.MAMA_RECONCILE_MAX_PER_HOUR) || undefined,
|
|
369
|
+
log: (line) => console.log(line),
|
|
370
|
+
run: (channelKey, deltaLines) => boardWriterQueue.push(async () => {
|
|
371
|
+
const scope = `reconcile:${channelKey}`;
|
|
372
|
+
const before = captureSnapshot(verifierDeps, scope);
|
|
373
|
+
const prompt = buildReconcilePrompt({
|
|
374
|
+
channelKey,
|
|
375
|
+
deltaLines,
|
|
376
|
+
todayIso: new Date().toISOString().slice(0, 10),
|
|
377
|
+
kagemushaContext,
|
|
378
|
+
});
|
|
379
|
+
await executeValidatedRun('dashboard-agent', prompt, {
|
|
380
|
+
requestTimeout: 300_000,
|
|
381
|
+
});
|
|
382
|
+
const verdict = verifyAfterRun(verifierDeps, before, scope);
|
|
383
|
+
const outcome = verdict.verified ? 'reconcile_verified' : 'reconcile_unverified';
|
|
384
|
+
console.log(`[reconcile] ${outcome} channel=${channelKey}${verdict.effects.length > 0 ? ` (${verdict.effects.join('; ')})` : ''}`);
|
|
385
|
+
try {
|
|
386
|
+
if (sessionsDb) {
|
|
387
|
+
(0, agent_store_js_1.logActivity)(sessionsDb, {
|
|
388
|
+
agent_id: 'dashboard-agent',
|
|
389
|
+
agent_version: 0,
|
|
390
|
+
type: outcome,
|
|
391
|
+
input_summary: `reconcile ${channelKey}`,
|
|
392
|
+
output_summary: verdict.effects.join('; '),
|
|
393
|
+
execution_status: 'completed',
|
|
394
|
+
trigger_reason: 'reconcile',
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
catch {
|
|
399
|
+
/* telemetry only */
|
|
400
|
+
}
|
|
401
|
+
if (!verdict.verified) {
|
|
402
|
+
// Loud, never blocking (observability over restriction).
|
|
403
|
+
eventBus.emit({
|
|
404
|
+
type: 'agent:action',
|
|
405
|
+
agent: 'Dashboard Agent',
|
|
406
|
+
action: 'reconcile_unverified',
|
|
407
|
+
target: channelKey,
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
}),
|
|
411
|
+
});
|
|
412
|
+
eventBus.on('operator:channel-delta', (event) => {
|
|
413
|
+
if (event.type === 'operator:channel-delta') {
|
|
414
|
+
reconcileScheduler.enqueue(event.channelKey, event.lines);
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
// Manual reconcile: lines from the body, or the caller must supply them
|
|
418
|
+
// (no silent alternate data path -- M8 review #17).
|
|
419
|
+
apiServer.app.post('/api/operator/reconcile', auth_middleware_js_1.requireAuth, (req, res) => {
|
|
420
|
+
const { channelKey, lines } = (req.body ?? {});
|
|
421
|
+
if (!channelKey || !Array.isArray(lines) || lines.length === 0) {
|
|
422
|
+
res.status(400).json({
|
|
423
|
+
ok: false,
|
|
424
|
+
error: 'channelKey and non-empty lines[] are required',
|
|
425
|
+
});
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
reconcileScheduler.enqueue(channelKey, lines);
|
|
429
|
+
res.json({ ok: true, message: 'Reconcile queued (async; runs after debounce)' });
|
|
430
|
+
});
|
|
431
|
+
console.log('[reconcile] Board reconcile leg enabled (MAMA_BOARD_RECONCILE=1)');
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
else {
|
|
435
|
+
routesLogger.debug('[Dashboard Agent] Skipped; dashboard-agent is not configured');
|
|
436
|
+
}
|
|
437
|
+
// ── Wiki Agent ──────────────────────────────────────────────────────
|
|
438
|
+
const wikiConfig = config.wiki;
|
|
439
|
+
if (wikiAgentConfigured && wikiConfig?.enabled && wikiConfig.vaultPath) {
|
|
440
|
+
const { ensureWikiPersona } = await import('../../multi-agent/wiki-agent-persona.js');
|
|
441
|
+
const { ObsidianWriter } = await import('../../wiki/obsidian-writer.js');
|
|
442
|
+
ensureWikiPersona();
|
|
443
|
+
const obsWriter = new ObsidianWriter(wikiConfig.vaultPath, wikiConfig.wikiDir || 'wiki');
|
|
444
|
+
obsWriter.ensureDirectories();
|
|
445
|
+
routesLogger.debug(`[Wiki Agent] Persona ensured, vault: ${obsWriter.getWikiPath()}`);
|
|
446
|
+
// Wire Obsidian vault path for CLI tool. The wiki directory itself is what
|
|
447
|
+
// gets registered as an Obsidian vault (agent-facing paths like daily/... are
|
|
448
|
+
// relative to it), so the CLI vault name is the wiki path's basename.
|
|
449
|
+
const fullWikiPath = obsWriter.getWikiPath();
|
|
450
|
+
const obsidianVaultName = node_path_1.default.basename(fullWikiPath);
|
|
451
|
+
toolExecutor.setObsidianVaultPath(fullWikiPath, obsidianVaultName);
|
|
452
|
+
routesLogger.debug(`[Wiki Agent] Obsidian CLI vault: ${fullWikiPath} (vault=${obsidianVaultName})`);
|
|
453
|
+
// Ensure Obsidian is running with the wiki vault open (macOS only).
|
|
454
|
+
// NOTE: obsidian://open?vault= only opens vaults ALREADY registered in
|
|
455
|
+
// Obsidian; registering the wiki directory as a vault is a one-time manual
|
|
456
|
+
// setup step. If it is not registered, the CLI reports unavailable and the
|
|
457
|
+
// agent falls back to wiki_publish (direct file writes still work).
|
|
458
|
+
if (process.platform === 'darwin') {
|
|
309
459
|
try {
|
|
310
|
-
const {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
stdio: 'ignore',
|
|
460
|
+
const { execFile: execFileChild } = await import('child_process');
|
|
461
|
+
execFileChild('open', [`obsidian://open?vault=${encodeURIComponent(obsidianVaultName)}`], { timeout: 5000 }, () => {
|
|
462
|
+
/* non-fatal: CLI will return error, agent falls back to wiki_publish */
|
|
314
463
|
});
|
|
315
464
|
}
|
|
316
465
|
catch {
|
|
317
|
-
/* non-fatal
|
|
466
|
+
/* non-fatal */
|
|
318
467
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
468
|
+
}
|
|
469
|
+
// Wire wiki_publish tool to shared gateway executor (used by code-act path)
|
|
470
|
+
toolExecutor.setWikiPublisher((pages) => {
|
|
471
|
+
for (const page of pages) {
|
|
472
|
+
obsWriter.writePage(page);
|
|
473
|
+
}
|
|
474
|
+
if (pages.length > 0) {
|
|
475
|
+
obsWriter.updateIndex(pages);
|
|
476
|
+
obsWriter.appendLog('compile', `Published ${pages.length} pages`);
|
|
477
|
+
}
|
|
478
|
+
routesLogger.debug(`[Wiki Agent] Published ${pages.length} pages to vault`);
|
|
479
|
+
eventBus.emit({
|
|
480
|
+
type: 'wiki:compiled',
|
|
481
|
+
pages: pages.map((p) => p.path || ''),
|
|
333
482
|
});
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
483
|
+
});
|
|
484
|
+
// Wiki trigger via executeValidatedRun
|
|
485
|
+
const doWikiRun = async () => {
|
|
486
|
+
if (!toolExecutor.getAgentProcessManager()) {
|
|
487
|
+
routesLogger.warn('[Wiki Agent] AgentProcessManager not available yet');
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
try {
|
|
491
|
+
routesLogger.debug('[Wiki Agent] Checking for updates...');
|
|
492
|
+
const wikiPrompt = `You are triggered on a schedule. Before writing anything, determine if an update is needed:
|
|
343
493
|
|
|
344
494
|
1. Use agent_notices({limit: 100}) to find the most recent wiki-agent compiled/publish/task_complete notice. Treat that as the last compilation boundary.
|
|
345
|
-
2.
|
|
495
|
+
2. NOVELTY CHECK by recency, not semantics: call mama_search({limit: 30}) with NO query -- that returns the newest decisions in creation order regardless of language or wording (semantic/lexical search misses cross-language items, which caused missed compilations). Compare created_at against the boundary.
|
|
496
|
+
3. If NO substantive decisions are newer than the boundary -> respond "NO_UPDATE" and stop. Do NOT call obsidian or wiki_publish.
|
|
497
|
+
4. If new items exist, use context_compile first to gather supporting context (limit 30, max_tool_calls 3, strictness "balanced").
|
|
346
498
|
Use this exact task text for context_compile: "recent substantive project decisions, task progress, agent alerts, and major changes".
|
|
347
499
|
Do not include dashboard_briefing, wiki_compilation, system-audit, or audit-log labels in the context_compile task text; filter those operational summaries after the packet returns.
|
|
348
|
-
If
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
500
|
+
If the packet misses some of the new items from step 2 (it often will for cross-language content), write from the step-2 items directly -- the recency list is authoritative for WHAT is new; the packet only enriches.
|
|
501
|
+
If context_compile is unavailable because there is no active worker envelope, fall back to ONE queried mama_search for enrichment and continue from the step-2 recency list.
|
|
502
|
+
5. Then follow your persona: APPEND today's daily note (daily/YYYY-MM-DD.md, create with the section skeleton on first write of the day), then promote qualifying lesson candidates into lessons/ pages (search before create; update, never duplicate). Do NOT write per-task status pages.
|
|
503
|
+
6. Do NOT call mama_save for the compilation; Obsidian/wiki files plus agent_activity are the durable operational record.
|
|
352
504
|
|
|
353
505
|
This saves resources. Only compile when there is genuinely new information to document.`;
|
|
354
|
-
|
|
355
|
-
|
|
506
|
+
const { noUpdate } = await executeValidatedRun('wiki-agent', wikiPrompt, {
|
|
507
|
+
requestTimeout: 600_000,
|
|
508
|
+
});
|
|
509
|
+
if (noUpdate) {
|
|
510
|
+
routesLogger.debug('[Wiki Agent] No changes detected, skipped');
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
routesLogger.debug('[Wiki Agent] Compilation complete');
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
catch (err) {
|
|
517
|
+
routesLogger.error('[Wiki Agent] Error:', err instanceof Error ? err.message : err);
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
// Serialize ALL wiki runs (boot, event-driven, manual) on one chain -- the
|
|
521
|
+
// shared agent process rejects concurrent requests, so the boot run and a
|
|
522
|
+
// manual trigger raced into 'Process is busy' (same pattern as the
|
|
523
|
+
// dashboard agent's runDashboardAgent chain above).
|
|
524
|
+
let wikiRunChain = Promise.resolve();
|
|
525
|
+
const runWikiAgent = () => {
|
|
526
|
+
wikiRunChain = wikiRunChain.then(doWikiRun).catch(() => { });
|
|
527
|
+
return wikiRunChain;
|
|
528
|
+
};
|
|
529
|
+
// Event-driven: compile when extraction completes. Trailing-edge debounce so
|
|
530
|
+
// bursts of extraction:completed events coalesce into one wiki compile run.
|
|
531
|
+
eventBus.onDebounced('extraction:completed', () => runWikiAgent(), 30_000);
|
|
532
|
+
// Promotion feeds the wiki: freshly promoted decisions are exactly the
|
|
533
|
+
// material daily notes and lessons compile from.
|
|
534
|
+
eventBus.onDebounced('memory:promoted', () => runWikiAgent(), 30_000);
|
|
535
|
+
// Emit agent:action notices when wiki pages are compiled
|
|
536
|
+
eventBus.on('wiki:compiled', (event) => {
|
|
537
|
+
if (event.type === 'wiki:compiled') {
|
|
538
|
+
for (const page of event.pages) {
|
|
539
|
+
eventBus.emit({
|
|
540
|
+
type: 'agent:action',
|
|
541
|
+
agent: 'Wiki Agent',
|
|
542
|
+
action: 'compiled',
|
|
543
|
+
target: page,
|
|
356
544
|
});
|
|
357
|
-
if (noUpdate) {
|
|
358
|
-
routesLogger.debug('[Wiki Agent] No changes detected, skipped');
|
|
359
|
-
}
|
|
360
|
-
else {
|
|
361
|
-
routesLogger.debug('[Wiki Agent] Compilation complete');
|
|
362
|
-
}
|
|
363
545
|
}
|
|
364
|
-
|
|
365
|
-
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
// Manual trigger API
|
|
549
|
+
apiServer.app.post('/api/wiki/compile', auth_middleware_js_1.requireAuth, async (_req, res) => {
|
|
550
|
+
runWikiAgent().catch(() => { });
|
|
551
|
+
res.json({ ok: true, message: 'Wiki compilation triggered' });
|
|
552
|
+
});
|
|
553
|
+
// First run after 15s (let connectors and dashboard agent go first)
|
|
554
|
+
setTimeout(runWikiAgent, 15_000);
|
|
555
|
+
routesLogger.info('[Wiki Agent] Ready — triggers: extraction:completed event, POST /api/wiki/compile');
|
|
556
|
+
}
|
|
557
|
+
// -- Memory Promotion: scheduled observation->decision curation --
|
|
558
|
+
// Owner directive 2026-07-11: the decision layer starved after the 07-03/04
|
|
559
|
+
// backfill because nothing periodically judges connector-ingested business
|
|
560
|
+
// data (the M0 kill switch removed direct LLM extraction, and the memory
|
|
561
|
+
// agent only audits owner conversation turns). This pass has the memory
|
|
562
|
+
// agent promote DURABLE judgments only -- task states stay on the board.
|
|
563
|
+
const memoryAgentConfigured = hasEnabledAgentConfig('memory');
|
|
564
|
+
if (memoryAgentConfigured) {
|
|
565
|
+
const PROMOTION_INTERVAL_MS = Math.max(1, Number(process.env.MAMA_MEMORY_PROMOTION_HOURS) || 6) * 60 * 60 * 1000;
|
|
566
|
+
const PROMOTION_INITIAL_DELAY_MS = 10 * 60 * 1000; // let connectors poll first
|
|
567
|
+
const buildPromotionPrompt = (nowIso) => 'PROMOTION RUN. You are curating durable business memory from recent data. ' +
|
|
568
|
+
`The current time is ${nowIso}.\n` +
|
|
569
|
+
'1. agent_notices({limit: 100}): find your latest promotion notice (action "promoted" ' +
|
|
570
|
+
'or "no_update") and treat it as the boundary; default to the last 24h when absent.\n' +
|
|
571
|
+
'2. kagemusha_entities({activeOnly: true}) to find the rooms active since the boundary, ' +
|
|
572
|
+
'then kagemusha_messages({channelId, since: <boundary ISO>}) on the busiest 3-4 rooms.\n' +
|
|
573
|
+
'3. For each candidate judgment, mama_search first to find the existing topic; reuse it ' +
|
|
574
|
+
'so the evolution chain stays intact.\n' +
|
|
575
|
+
'4. Promote at most 5 durable judgments per run via mama_save, following the PROMOTION RUN ' +
|
|
576
|
+
'rules in your persona (pricing/scope agreements, standing client preferences, process ' +
|
|
577
|
+
'rules, recurring risk patterns; NEVER task lifecycle states, greetings, or logistics). ' +
|
|
578
|
+
'Include scopes (the source channel, and the project when identifiable) and event_date.\n' +
|
|
579
|
+
'5. Finish with exactly PROMOTED <n> or NO_UPDATE.';
|
|
580
|
+
const doPromotionRun = async () => {
|
|
581
|
+
if (!toolExecutor.getAgentProcessManager()) {
|
|
582
|
+
routesLogger.warn('[Memory Promotion] AgentProcessManager not available yet');
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
try {
|
|
586
|
+
const { response, noUpdate } = await executeValidatedRun('memory', buildPromotionPrompt(new Date().toISOString()), { requestTimeout: 600_000 });
|
|
587
|
+
const promotedMatch = response?.match(/PROMOTED\s+(\d+)/);
|
|
588
|
+
const saved = promotedMatch ? Number(promotedMatch[1]) : 0;
|
|
589
|
+
eventBus.emit({
|
|
590
|
+
type: 'agent:action',
|
|
591
|
+
agent: 'Memory Agent',
|
|
592
|
+
action: noUpdate || saved === 0 ? 'no_update' : 'promoted',
|
|
593
|
+
target: `promotion run: ${saved} saved`,
|
|
594
|
+
});
|
|
595
|
+
if (saved > 0) {
|
|
596
|
+
eventBus.emit({ type: 'memory:promoted', saved });
|
|
597
|
+
console.log(`[Memory Promotion] Promoted ${saved} durable judgments`);
|
|
366
598
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
eventBus.on('extraction:completed', () => runWikiAgent());
|
|
370
|
-
// Emit agent:action notices when wiki pages are compiled
|
|
371
|
-
eventBus.on('wiki:compiled', (event) => {
|
|
372
|
-
if (event.type === 'wiki:compiled') {
|
|
373
|
-
for (const page of event.pages) {
|
|
374
|
-
eventBus.emit({
|
|
375
|
-
type: 'agent:action',
|
|
376
|
-
agent: 'Wiki Agent',
|
|
377
|
-
action: 'compiled',
|
|
378
|
-
target: page,
|
|
379
|
-
});
|
|
380
|
-
}
|
|
599
|
+
else {
|
|
600
|
+
routesLogger.debug('[Memory Promotion] Nothing qualified for promotion');
|
|
381
601
|
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
602
|
+
}
|
|
603
|
+
catch (err) {
|
|
604
|
+
routesLogger.error('[Memory Promotion] Error:', err instanceof Error ? err.message : err);
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
// Serialize all promotion runs on one chain (same 'Process is busy' class
|
|
608
|
+
// as the dashboard and wiki agents).
|
|
609
|
+
let promotionRunChain = Promise.resolve();
|
|
610
|
+
const runMemoryPromotion = () => {
|
|
611
|
+
promotionRunChain = promotionRunChain.then(doPromotionRun).catch(() => { });
|
|
612
|
+
return promotionRunChain;
|
|
613
|
+
};
|
|
614
|
+
setTimeout(runMemoryPromotion, PROMOTION_INITIAL_DELAY_MS);
|
|
615
|
+
setInterval(runMemoryPromotion, PROMOTION_INTERVAL_MS);
|
|
616
|
+
apiServer.app.post('/api/memory/promote', auth_middleware_js_1.requireAuth, (_req, res) => {
|
|
617
|
+
runMemoryPromotion().catch(() => { });
|
|
618
|
+
res.json({ ok: true, message: 'Memory promotion run triggered' });
|
|
619
|
+
});
|
|
620
|
+
console.log(`[Memory Promotion] Ready: every ${PROMOTION_INTERVAL_MS / 3_600_000}h, POST /api/memory/promote`);
|
|
392
621
|
}
|
|
393
622
|
// ── Conductor Audit — hourly system health check ──────────────────────
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
623
|
+
const AUDIT_INTERVAL_MS = 60 * 60 * 1000; // 1 hour
|
|
624
|
+
const AUDIT_INITIAL_DELAY_MS = 5 * 60 * 1000; // 5 min after startup
|
|
625
|
+
// Owner verdict 2026-07-11: the hourly audit re-sent the same MAJOR alert every
|
|
626
|
+
// run because each audit was stateless. agent_notices does not carry audit
|
|
627
|
+
// findings (review feedback on #134), so dedup state lives in a JSON file the
|
|
628
|
+
// conductor reads/writes itself, and the current time is interpolated per run
|
|
629
|
+
// so the 24h comparison is grounded.
|
|
630
|
+
const buildAuditPrompt = (nowIso) => 'Perform a system audit. Read ~/.mama/skills/audit-checklist.md and execute each step. ' +
|
|
631
|
+
'Classify findings as MINOR or MAJOR. ' +
|
|
632
|
+
'For MINOR items: execute the auto-fix command immediately (Bash curl). Do NOT just report — fix it. ' +
|
|
633
|
+
'For MAJOR items: report to human via channel alert, subject to the dedup rule below. ' +
|
|
634
|
+
`Alert dedup (MANDATORY): the current time is ${nowIso}. Read ~/.mama/state/audit-findings.json ` +
|
|
635
|
+
'(it may not exist on the first run). Re-alert a MAJOR finding only if it is NEW (no entry ' +
|
|
636
|
+
'with the same id), has ESCALATED, or its last_alerted_at is older than 24 hours compared to ' +
|
|
637
|
+
'the current time; otherwise stay silent about it. Never alert MINOR findings to the owner. ' +
|
|
638
|
+
'After the audit, Write ~/.mama/state/audit-findings.json with an array of every current ' +
|
|
639
|
+
'finding: {id (stable kebab-case slug), severity, first_seen, last_seen, last_alerted_at}. ' +
|
|
640
|
+
'Carry forward first_seen and last_alerted_at from the previous file; set last_alerted_at to ' +
|
|
641
|
+
'the current time only for findings you actually alerted this run. ' +
|
|
642
|
+
// Owner verdict 2026-07-10: hourly-audit self-notes are memory noise -- the two
|
|
643
|
+
// "durable policy" saves it produced were unscoped near-duplicates polluting
|
|
644
|
+
// global recall. The audit record lives in validation sessions + agent_activity;
|
|
645
|
+
// MAJOR findings reach the owner via the channel alert. No memory writes here.
|
|
646
|
+
'Do NOT call mama_save under any circumstances: the validation session and agent_activity ' +
|
|
647
|
+
'are the durable audit record, and repeated audits must not accumulate policy notes in memory.';
|
|
648
|
+
const runConductorAudit = async () => {
|
|
649
|
+
let auditSession = null;
|
|
650
|
+
let auditVersion = 0;
|
|
651
|
+
const auditStart = Date.now();
|
|
652
|
+
try {
|
|
653
|
+
routesLogger.debug('[Conductor Audit] Starting hourly audit...');
|
|
407
654
|
try {
|
|
408
|
-
|
|
655
|
+
const ver = sessionsDb ? (0, agent_store_js_1.getLatestVersion)(sessionsDb, 'conductor') : null;
|
|
656
|
+
auditVersion = ver?.version ?? 0;
|
|
657
|
+
auditSession =
|
|
658
|
+
validationService?.startSession('conductor', auditVersion, 'audit', {
|
|
659
|
+
goal: 'hourly system audit',
|
|
660
|
+
}) ?? null;
|
|
661
|
+
}
|
|
662
|
+
catch (bootstrapErr) {
|
|
663
|
+
routesLogger.warn('[Conductor Audit] Failed to initialize audit telemetry:', bootstrapErr);
|
|
664
|
+
auditVersion = 0;
|
|
665
|
+
auditSession = null;
|
|
666
|
+
}
|
|
667
|
+
if (sessionsDb) {
|
|
409
668
|
try {
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
if (sessionsDb) {
|
|
423
|
-
try {
|
|
424
|
-
const startRow = (0, agent_store_js_1.logActivity)(sessionsDb, {
|
|
425
|
-
agent_id: 'conductor',
|
|
426
|
-
agent_version: auditVersion,
|
|
427
|
-
type: 'audit_start',
|
|
428
|
-
input_summary: 'Hourly system audit',
|
|
429
|
-
run_id: auditSession?.id,
|
|
430
|
-
execution_status: 'started',
|
|
431
|
-
trigger_reason: 'audit',
|
|
432
|
-
});
|
|
433
|
-
if (auditSession) {
|
|
434
|
-
try {
|
|
435
|
-
validationService?.recordRun(auditSession.id, { activityId: startRow.id });
|
|
436
|
-
}
|
|
437
|
-
catch (telemetryErr) {
|
|
438
|
-
routesLogger.warn('[Conductor Audit] Failed to link startup activity to validation session:', telemetryErr);
|
|
439
|
-
}
|
|
669
|
+
const startRow = (0, agent_store_js_1.logActivity)(sessionsDb, {
|
|
670
|
+
agent_id: 'conductor',
|
|
671
|
+
agent_version: auditVersion,
|
|
672
|
+
type: 'audit_start',
|
|
673
|
+
input_summary: 'Hourly system audit',
|
|
674
|
+
run_id: auditSession?.id,
|
|
675
|
+
execution_status: 'started',
|
|
676
|
+
trigger_reason: 'audit',
|
|
677
|
+
});
|
|
678
|
+
if (auditSession) {
|
|
679
|
+
try {
|
|
680
|
+
validationService?.recordRun(auditSession.id, { activityId: startRow.id });
|
|
440
681
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
routesLogger.warn('[Conductor Audit] Failed to write startup telemetry:', telemetryErr);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
const auditChannelId = `conductor-audit-${Date.now()}`;
|
|
447
|
-
await messageRouter.process({
|
|
448
|
-
source: 'system',
|
|
449
|
-
channelId: auditChannelId,
|
|
450
|
-
userId: 'system',
|
|
451
|
-
text: auditPrompt,
|
|
452
|
-
});
|
|
453
|
-
const auditDuration = Date.now() - auditStart;
|
|
454
|
-
try {
|
|
455
|
-
if (sessionsDb) {
|
|
456
|
-
const activityRow = (0, agent_store_js_1.logActivity)(sessionsDb, {
|
|
457
|
-
agent_id: 'conductor',
|
|
458
|
-
agent_version: auditVersion,
|
|
459
|
-
type: 'audit_complete',
|
|
460
|
-
input_summary: 'Hourly system audit',
|
|
461
|
-
duration_ms: auditDuration,
|
|
462
|
-
run_id: auditSession?.id,
|
|
463
|
-
execution_status: 'completed',
|
|
464
|
-
trigger_reason: 'audit',
|
|
465
|
-
});
|
|
466
|
-
if (auditSession) {
|
|
467
|
-
validationService?.recordRun(auditSession.id, { activityId: activityRow.id });
|
|
682
|
+
catch (telemetryErr) {
|
|
683
|
+
routesLogger.warn('[Conductor Audit] Failed to link startup activity to validation session:', telemetryErr);
|
|
468
684
|
}
|
|
469
685
|
}
|
|
470
686
|
}
|
|
471
687
|
catch (telemetryErr) {
|
|
472
|
-
routesLogger.warn('[Conductor Audit] Failed to write
|
|
473
|
-
}
|
|
474
|
-
try {
|
|
475
|
-
if (auditSession && validationService) {
|
|
476
|
-
validationService.finalizeSession(auditSession.id, {
|
|
477
|
-
execution_status: 'completed',
|
|
478
|
-
metrics: { duration_ms: auditDuration },
|
|
479
|
-
});
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
catch (telemetryErr) {
|
|
483
|
-
routesLogger.warn('[Conductor Audit] Failed to finalize validation session:', telemetryErr);
|
|
688
|
+
routesLogger.warn('[Conductor Audit] Failed to write startup telemetry:', telemetryErr);
|
|
484
689
|
}
|
|
485
|
-
routesLogger.debug('[Conductor Audit] Audit complete');
|
|
486
690
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
691
|
+
const auditChannelId = `conductor-audit-${Date.now()}`;
|
|
692
|
+
await messageRouter.process({
|
|
693
|
+
source: 'system',
|
|
694
|
+
channelId: auditChannelId,
|
|
695
|
+
userId: 'system',
|
|
696
|
+
text: buildAuditPrompt(new Date().toISOString()),
|
|
697
|
+
});
|
|
698
|
+
const auditDuration = Date.now() - auditStart;
|
|
699
|
+
try {
|
|
700
|
+
if (sessionsDb) {
|
|
701
|
+
const activityRow = (0, agent_store_js_1.logActivity)(sessionsDb, {
|
|
702
|
+
agent_id: 'conductor',
|
|
703
|
+
agent_version: auditVersion,
|
|
704
|
+
type: 'audit_complete',
|
|
705
|
+
input_summary: 'Hourly system audit',
|
|
706
|
+
duration_ms: auditDuration,
|
|
707
|
+
run_id: auditSession?.id,
|
|
708
|
+
execution_status: 'completed',
|
|
709
|
+
trigger_reason: 'audit',
|
|
710
|
+
});
|
|
711
|
+
if (auditSession) {
|
|
712
|
+
validationService?.recordRun(auditSession.id, { activityId: activityRow.id });
|
|
505
713
|
}
|
|
506
714
|
}
|
|
507
|
-
|
|
508
|
-
|
|
715
|
+
}
|
|
716
|
+
catch (telemetryErr) {
|
|
717
|
+
routesLogger.warn('[Conductor Audit] Failed to write completion telemetry:', telemetryErr);
|
|
718
|
+
}
|
|
719
|
+
try {
|
|
720
|
+
if (auditSession && validationService) {
|
|
721
|
+
validationService.finalizeSession(auditSession.id, {
|
|
722
|
+
execution_status: 'completed',
|
|
723
|
+
metrics: { duration_ms: auditDuration },
|
|
724
|
+
});
|
|
509
725
|
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
726
|
+
}
|
|
727
|
+
catch (telemetryErr) {
|
|
728
|
+
routesLogger.warn('[Conductor Audit] Failed to finalize validation session:', telemetryErr);
|
|
729
|
+
}
|
|
730
|
+
routesLogger.debug('[Conductor Audit] Audit complete');
|
|
731
|
+
}
|
|
732
|
+
catch (err) {
|
|
733
|
+
const auditDuration = Date.now() - auditStart;
|
|
734
|
+
try {
|
|
735
|
+
if (sessionsDb) {
|
|
736
|
+
const failRow = (0, agent_store_js_1.logActivity)(sessionsDb, {
|
|
737
|
+
agent_id: 'conductor',
|
|
738
|
+
agent_version: auditVersion,
|
|
739
|
+
type: 'audit_failed',
|
|
740
|
+
input_summary: 'Hourly system audit failed',
|
|
741
|
+
error_message: err instanceof Error ? err.message : String(err),
|
|
742
|
+
duration_ms: auditDuration,
|
|
743
|
+
run_id: auditSession?.id,
|
|
744
|
+
execution_status: 'failed',
|
|
745
|
+
trigger_reason: 'audit',
|
|
746
|
+
});
|
|
747
|
+
if (auditSession) {
|
|
748
|
+
validationService?.recordRun(auditSession.id, { activityId: failRow.id });
|
|
517
749
|
}
|
|
518
750
|
}
|
|
519
|
-
|
|
520
|
-
|
|
751
|
+
}
|
|
752
|
+
catch (telemetryErr) {
|
|
753
|
+
routesLogger.warn('[Conductor Audit] Failed to write failure telemetry:', telemetryErr);
|
|
754
|
+
}
|
|
755
|
+
try {
|
|
756
|
+
if (auditSession && validationService) {
|
|
757
|
+
validationService.finalizeSession(auditSession.id, {
|
|
758
|
+
execution_status: 'failed',
|
|
759
|
+
error_message: err instanceof Error ? err.message : String(err),
|
|
760
|
+
metrics: { duration_ms: auditDuration },
|
|
761
|
+
});
|
|
521
762
|
}
|
|
522
|
-
routesLogger.error('[Conductor Audit] Failed:', err instanceof Error ? err.message : String(err));
|
|
523
763
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
764
|
+
catch (telemetryErr) {
|
|
765
|
+
routesLogger.warn('[Conductor Audit] Failed to finalize failed session:', telemetryErr);
|
|
766
|
+
}
|
|
767
|
+
routesLogger.error('[Conductor Audit] Failed:', err instanceof Error ? err.message : String(err));
|
|
768
|
+
}
|
|
769
|
+
};
|
|
770
|
+
setTimeout(() => {
|
|
771
|
+
runConductorAudit();
|
|
772
|
+
setInterval(runConductorAudit, AUDIT_INTERVAL_MS);
|
|
773
|
+
}, AUDIT_INITIAL_DELAY_MS);
|
|
774
|
+
// Manual trigger
|
|
775
|
+
apiServer.app.post('/api/conductor/audit', auth_middleware_js_1.requireAuth, async (_req, res) => {
|
|
776
|
+
runConductorAudit().catch(() => { });
|
|
777
|
+
res.json({ ok: true, message: 'Conductor audit triggered' });
|
|
778
|
+
});
|
|
779
|
+
routesLogger.info('[Conductor Audit] Ready — runs every 60 min, POST /api/conductor/audit for manual trigger');
|
|
536
780
|
// ── Memory Agent stats API ────────────────────────────────────────────
|
|
537
781
|
apiServer.app.get('/api/memory-agent/stats', auth_middleware_js_1.requireAuth, (_req, res) => {
|
|
538
782
|
const stats = messageRouter.getMemoryAgentStats();
|
|
@@ -1187,7 +1431,7 @@ Keep the report under 2000 characters as it will be sent to Discord.`;
|
|
|
1187
1431
|
res.sendFile(node_path_1.default.join(publicDir, 'setup.html'));
|
|
1188
1432
|
});
|
|
1189
1433
|
apiServer.app.use(express_1.default.static(publicDir, {
|
|
1190
|
-
setHeaders: (res) => {
|
|
1434
|
+
setHeaders: (res, _filePath) => {
|
|
1191
1435
|
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
|
|
1192
1436
|
res.setHeader('Pragma', 'no-cache');
|
|
1193
1437
|
res.setHeader('Expires', '0');
|