@jungjaehoon/mama-os 0.22.1 → 0.23.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 +22 -0
- package/README.md +3 -1
- package/dist/agent/agent-loop.d.ts +0 -3
- package/dist/agent/agent-loop.d.ts.map +1 -1
- package/dist/agent/agent-loop.js +49 -34
- package/dist/agent/agent-loop.js.map +1 -1
- package/dist/agent/gateway-tool-executor.d.ts +18 -0
- package/dist/agent/gateway-tool-executor.d.ts.map +1 -1
- package/dist/agent/gateway-tool-executor.js +185 -6
- package/dist/agent/gateway-tool-executor.js.map +1 -1
- package/dist/agent/gateway-tools.md +10 -5
- package/dist/agent/role-manager.d.ts +22 -1
- package/dist/agent/role-manager.d.ts.map +1 -1
- package/dist/agent/role-manager.js +39 -2
- package/dist/agent/role-manager.js.map +1 -1
- package/dist/agent/tool-registry.d.ts.map +1 -1
- package/dist/agent/tool-registry.js +35 -5
- package/dist/agent/tool-registry.js.map +1 -1
- package/dist/agent/types.d.ts +9 -1
- 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 +43 -33
- package/dist/api/graph-api.js.map +1 -1
- package/dist/api/operator-tasks-handler.d.ts.map +1 -1
- package/dist/api/operator-tasks-handler.js +6 -2
- package/dist/api/operator-tasks-handler.js.map +1 -1
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +185 -2
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/config/config-manager.d.ts +0 -5
- package/dist/cli/config/config-manager.d.ts.map +1 -1
- package/dist/cli/config/config-manager.js +57 -2
- package/dist/cli/config/config-manager.js.map +1 -1
- package/dist/cli/config/types.d.ts.map +1 -1
- package/dist/cli/config/types.js +35 -0
- package/dist/cli/config/types.js.map +1 -1
- package/dist/cli/runtime/agent-loop-init.d.ts.map +1 -1
- package/dist/cli/runtime/agent-loop-init.js +96 -51
- package/dist/cli/runtime/agent-loop-init.js.map +1 -1
- package/dist/cli/runtime/api-routes-init.d.ts +3 -0
- package/dist/cli/runtime/api-routes-init.d.ts.map +1 -1
- package/dist/cli/runtime/api-routes-init.js +226 -51
- package/dist/cli/runtime/api-routes-init.js.map +1 -1
- package/dist/cli/runtime/gateway-init.d.ts.map +1 -1
- package/dist/cli/runtime/gateway-init.js +5 -0
- package/dist/cli/runtime/gateway-init.js.map +1 -1
- package/dist/envelope/reactive-config.d.ts.map +1 -1
- package/dist/envelope/reactive-config.js +26 -1
- package/dist/envelope/reactive-config.js.map +1 -1
- package/dist/gateways/message-router.d.ts +18 -1
- package/dist/gateways/message-router.d.ts.map +1 -1
- package/dist/gateways/message-router.js +155 -22
- package/dist/gateways/message-router.js.map +1 -1
- package/dist/gateways/telegram.d.ts.map +1 -1
- package/dist/gateways/telegram.js +13 -0
- package/dist/gateways/telegram.js.map +1 -1
- package/dist/gateways/types.d.ts +3 -0
- package/dist/gateways/types.d.ts.map +1 -1
- package/dist/memory/history-extractor.d.ts +0 -24
- package/dist/memory/history-extractor.d.ts.map +1 -1
- package/dist/memory/history-extractor.js +0 -132
- package/dist/memory/history-extractor.js.map +1 -1
- package/dist/memory/save-candidate-extractor.d.ts +3 -0
- package/dist/memory/save-candidate-extractor.d.ts.map +1 -1
- package/dist/memory/save-candidate-extractor.js +12 -1
- package/dist/memory/save-candidate-extractor.js.map +1 -1
- package/dist/memory/secret-filter.d.ts +22 -0
- package/dist/memory/secret-filter.d.ts.map +1 -0
- package/dist/memory/secret-filter.js +77 -0
- package/dist/memory/secret-filter.js.map +1 -0
- package/dist/observability/code-audit.d.ts +4 -0
- package/dist/observability/code-audit.d.ts.map +1 -1
- package/dist/observability/code-audit.js +69 -0
- package/dist/observability/code-audit.js.map +1 -1
- package/dist/operator/briefs.d.ts +26 -0
- package/dist/operator/briefs.d.ts.map +1 -0
- package/dist/operator/briefs.js +128 -0
- package/dist/operator/briefs.js.map +1 -0
- package/dist/operator/operator-trigger-loop.d.ts +24 -0
- package/dist/operator/operator-trigger-loop.d.ts.map +1 -1
- package/dist/operator/operator-trigger-loop.js +71 -12
- package/dist/operator/operator-trigger-loop.js.map +1 -1
- package/dist/operator/report-carry.d.ts +29 -0
- package/dist/operator/report-carry.d.ts.map +1 -0
- package/dist/operator/report-carry.js +81 -0
- package/dist/operator/report-carry.js.map +1 -0
- package/dist/operator/shadow-capture.d.ts +20 -0
- package/dist/operator/shadow-capture.d.ts.map +1 -0
- package/dist/operator/shadow-capture.js +34 -0
- package/dist/operator/shadow-capture.js.map +1 -0
- package/dist/operator/situation-report.d.ts +6 -0
- package/dist/operator/situation-report.d.ts.map +1 -1
- package/dist/operator/situation-report.js +13 -0
- package/dist/operator/situation-report.js.map +1 -1
- package/dist/operator/task-ledger.d.ts +90 -2
- package/dist/operator/task-ledger.d.ts.map +1 -1
- package/dist/operator/task-ledger.js +359 -23
- package/dist/operator/task-ledger.js.map +1 -1
- package/dist/operator/worker-run.d.ts +59 -0
- package/dist/operator/worker-run.d.ts.map +1 -0
- package/dist/operator/worker-run.js +64 -0
- package/dist/operator/worker-run.js.map +1 -0
- package/dist/operator/workorder-consumer.d.ts +118 -0
- package/dist/operator/workorder-consumer.d.ts.map +1 -0
- package/dist/operator/workorder-consumer.js +248 -0
- package/dist/operator/workorder-consumer.js.map +1 -0
- package/dist/operator/workorder-hooks.d.ts +30 -0
- package/dist/operator/workorder-hooks.d.ts.map +1 -0
- package/dist/operator/workorder-hooks.js +70 -0
- package/dist/operator/workorder-hooks.js.map +1 -0
- package/dist/operator/workorder-publishers.d.ts +68 -0
- package/dist/operator/workorder-publishers.d.ts.map +1 -0
- package/dist/operator/workorder-publishers.js +150 -0
- package/dist/operator/workorder-publishers.js.map +1 -0
- package/dist/security/security-monitor.d.ts +7 -0
- package/dist/security/security-monitor.d.ts.map +1 -1
- package/dist/security/security-monitor.js +17 -0
- package/dist/security/security-monitor.js.map +1 -1
- package/dist/utils/untrusted-content.d.ts +13 -0
- package/dist/utils/untrusted-content.d.ts.map +1 -1
- package/dist/utils/untrusted-content.js +43 -0
- package/dist/utils/untrusted-content.js.map +1 -1
- package/package.json +2 -2
|
@@ -57,12 +57,29 @@ const operator_tasks_handler_js_1 = require("../../api/operator-tasks-handler.js
|
|
|
57
57
|
const auth_middleware_js_1 = require("../../api/auth-middleware.js");
|
|
58
58
|
const utilities_js_1 = require("./utilities.js");
|
|
59
59
|
const code_audit_js_1 = require("../../observability/code-audit.js");
|
|
60
|
+
const workorder_publishers_js_1 = require("../../operator/workorder-publishers.js");
|
|
60
61
|
const security_monitor_js_1 = require("../../security/security-monitor.js");
|
|
61
62
|
const debugLogger = __importStar(require("@jungjaehoon/mama-core/debug-logger"));
|
|
62
63
|
const { DebugLogger } = debugLogger;
|
|
63
64
|
const routesLogger = new DebugLogger('api-routes');
|
|
64
65
|
async function registerApiRoutes(params) {
|
|
65
|
-
const { config, apiServer, eventBus, oauthManager: _oauthManager, mamaApi: _mamaApi, messageRouter, agentLoop, toolExecutor, discordGateway, slackGateway, graphHandler, getAdapter, sessionsDb, } = params;
|
|
66
|
+
const { config, apiServer, eventBus, oauthManager: _oauthManager, mamaApi: _mamaApi, messageRouter, agentLoop, toolExecutor, discordGateway, slackGateway, graphHandler, getAdapter, sessionsDb, workOrderConsumer, } = params;
|
|
67
|
+
// ── Stage-2 publisher gate (plan S2-T2) ───────────────────────────────
|
|
68
|
+
// Throws at boot on a malformed flag value (no-fallback: a typo must not
|
|
69
|
+
// silently revert a believed-active migration to legacy).
|
|
70
|
+
const stage2Flag = (0, workorder_publishers_js_1.readStage2Flag)();
|
|
71
|
+
if (stage2Flag !== 'off') {
|
|
72
|
+
console.log(`[stage2] workorder publishers active (flag=${stage2Flag})`);
|
|
73
|
+
}
|
|
74
|
+
const enqueueWorkOrderOrThrow = (workKind, idempotencyKey, payload, priority) => {
|
|
75
|
+
const ledger = toolExecutor.getTaskLedger();
|
|
76
|
+
if (!ledger) {
|
|
77
|
+
throw new Error(`[stage2] TaskLedger unavailable - cannot enqueue ${workKind} workorder`);
|
|
78
|
+
}
|
|
79
|
+
(0, workorder_publishers_js_1.validateWorkOrderPayload)(workKind, payload);
|
|
80
|
+
const wo = ledger.enqueueWorkOrder({ workKind, idempotencyKey, input: payload, priority });
|
|
81
|
+
console.log(`[stage2] enqueued workorder ${workKind}#${wo.id} key=${idempotencyKey}`);
|
|
82
|
+
};
|
|
66
83
|
// ── Validation Session Service ────────────────────────────────────────
|
|
67
84
|
const validationService = sessionsDb ? new session_service_js_1.ValidationSessionService(sessionsDb) : null;
|
|
68
85
|
/**
|
|
@@ -261,6 +278,22 @@ async function registerApiRoutes(params) {
|
|
|
261
278
|
{
|
|
262
279
|
const { createReportPublisher } = await import('../../api/report-handler.js');
|
|
263
280
|
toolExecutor.setReportPublisher(createReportPublisher(apiServer.reportStore, apiServer.reportSseClients));
|
|
281
|
+
// S1-T4 artifact hub: the console READS the board it directs. Same store
|
|
282
|
+
// the publisher writes - the conductor's status answers cite these slots
|
|
283
|
+
// instead of re-deriving state from memory copies.
|
|
284
|
+
toolExecutor.setReportReader(() => {
|
|
285
|
+
const slots = apiServer.reportStore.getAll();
|
|
286
|
+
const projected = {};
|
|
287
|
+
for (const [name, slot] of Object.entries(slots)) {
|
|
288
|
+
projected[name] = {
|
|
289
|
+
html: slot.html,
|
|
290
|
+
updatedAt: Number.isFinite(slot.updatedAt)
|
|
291
|
+
? new Date(slot.updatedAt).toISOString()
|
|
292
|
+
: null,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
return projected;
|
|
296
|
+
});
|
|
264
297
|
}
|
|
265
298
|
if (dashboardAgentConfigured) {
|
|
266
299
|
// ── Dashboard Agent ───────────────────────────────────────────────
|
|
@@ -286,6 +319,23 @@ This saves resources. Only publish when there is genuinely new information to re
|
|
|
286
319
|
// request means "rebuild the board now", not "tell me nothing changed".
|
|
287
320
|
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');
|
|
288
321
|
const doDashboardRun = async (opts) => {
|
|
322
|
+
// Stage-2 gate at the FUNCTION level: schedule, boot, manual REST all
|
|
323
|
+
// funnel here, so this single gate covers every entry path (plan F4).
|
|
324
|
+
const boardAction = (0, workorder_publishers_js_1.resolvePublishAction)(stage2Flag, 'board');
|
|
325
|
+
if (boardAction !== 'legacy') {
|
|
326
|
+
try {
|
|
327
|
+
const now = Date.now();
|
|
328
|
+
enqueueWorkOrderOrThrow('board', opts?.force ? (0, workorder_publishers_js_1.boardManualKey)(now) : (0, workorder_publishers_js_1.boardFullKey)(now), { mode: 'full', ...(opts?.force ? { force: true } : {}) }, opts?.force ? 'high' : undefined);
|
|
329
|
+
}
|
|
330
|
+
catch (err) {
|
|
331
|
+
routesLogger.error('[stage2] board enqueue failed:', err instanceof Error ? err.message : err);
|
|
332
|
+
}
|
|
333
|
+
// 'enqueue' (on): legacy stops - an enqueue failure must NOT silently
|
|
334
|
+
// fall back to a legacy run; the next occurrence retries.
|
|
335
|
+
// 'both' (shadow): legacy keeps publishing live below.
|
|
336
|
+
if (boardAction === 'enqueue')
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
289
339
|
const pm = toolExecutor.getAgentProcessManager();
|
|
290
340
|
if (!pm) {
|
|
291
341
|
routesLogger.warn('[Dashboard Agent] AgentProcessManager not available yet');
|
|
@@ -364,52 +414,130 @@ This saves resources. Only publish when there is genuinely new information to re
|
|
|
364
414
|
return row.n;
|
|
365
415
|
},
|
|
366
416
|
};
|
|
417
|
+
// Stage-2 board completion hook (plan D1/E3/G1): the consumer wraps
|
|
418
|
+
// reconcile workorders with the SAME bracket the legacy closure owns,
|
|
419
|
+
// but trace rows from worker runs carry the worker's identity, not
|
|
420
|
+
// agent_id='dashboard-agent' - the re-key goes through details JSON
|
|
421
|
+
// (agent_activity has NO channel_id column; json_extract is the only
|
|
422
|
+
// schema-supported key; agent_id re-keying is a green-test trap).
|
|
423
|
+
if (workOrderConsumer) {
|
|
424
|
+
const { buildWorkerTraceQueries } = await import('../../operator/workorder-hooks.js');
|
|
425
|
+
const workerVerifierDeps = {
|
|
426
|
+
...verifierDeps,
|
|
427
|
+
...buildWorkerTraceQueries(sessionsDb, 'worker:board'),
|
|
428
|
+
};
|
|
429
|
+
workOrderConsumer.registerHook('board', {
|
|
430
|
+
before: (wo) => {
|
|
431
|
+
if (wo.payload.mode !== 'reconcile')
|
|
432
|
+
return null;
|
|
433
|
+
return captureSnapshot(workerVerifierDeps, `reconcile:${String(wo.payload.channelKey)}`);
|
|
434
|
+
},
|
|
435
|
+
after: (wo, response, beforeState) => {
|
|
436
|
+
if (wo.payload.mode !== 'reconcile' || !beforeState) {
|
|
437
|
+
// FULL runs are not bracket-verified (plan: reconcile mode only).
|
|
438
|
+
if (response.includes('NO_UPDATE')) {
|
|
439
|
+
console.log('[stage2] board worker: no changes detected, publish skipped');
|
|
440
|
+
}
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
const scope = `reconcile:${String(wo.payload.channelKey)}`;
|
|
444
|
+
const verdict = verifyAfterRun(workerVerifierDeps, beforeState, scope);
|
|
445
|
+
const outcome = verdict.verified ? 'reconcile_verified' : 'reconcile_unverified';
|
|
446
|
+
console.log(`[stage2] ${outcome} channel=${String(wo.payload.channelKey)}${verdict.effects.length > 0 ? ` (${verdict.effects.join('; ')})` : ''}`);
|
|
447
|
+
try {
|
|
448
|
+
if (sessionsDb) {
|
|
449
|
+
(0, agent_store_js_1.logActivity)(sessionsDb, {
|
|
450
|
+
agent_id: 'workorder-board',
|
|
451
|
+
agent_version: 0,
|
|
452
|
+
type: outcome,
|
|
453
|
+
input_summary: `reconcile ${String(wo.payload.channelKey)}`,
|
|
454
|
+
output_summary: verdict.effects.join('; '),
|
|
455
|
+
execution_status: 'completed',
|
|
456
|
+
trigger_reason: 'workorder',
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
catch {
|
|
461
|
+
/* telemetry only */
|
|
462
|
+
}
|
|
463
|
+
if (!verdict.verified) {
|
|
464
|
+
eventBus.emit({
|
|
465
|
+
type: 'agent:action',
|
|
466
|
+
agent: 'Dashboard Agent',
|
|
467
|
+
action: 'reconcile_unverified',
|
|
468
|
+
target: String(wo.payload.channelKey),
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
},
|
|
472
|
+
});
|
|
473
|
+
}
|
|
367
474
|
const reconcileScheduler = new ReconcileScheduler({
|
|
368
475
|
debounceMs: Number(process.env.MAMA_RECONCILE_DEBOUNCE_MS) || undefined,
|
|
369
476
|
maxWaitMs: Number(process.env.MAMA_RECONCILE_MAX_WAIT_MS) || undefined,
|
|
370
477
|
globalMaxPerHour: Number(process.env.MAMA_RECONCILE_MAX_PER_HOUR) || undefined,
|
|
371
478
|
log: (line) => console.log(line),
|
|
372
|
-
run: (channelKey, deltaLines) =>
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
requestTimeout: 300_000,
|
|
383
|
-
});
|
|
384
|
-
const verdict = verifyAfterRun(verifierDeps, before, scope);
|
|
385
|
-
const outcome = verdict.verified ? 'reconcile_verified' : 'reconcile_unverified';
|
|
386
|
-
console.log(`[reconcile] ${outcome} channel=${channelKey}${verdict.effects.length > 0 ? ` (${verdict.effects.join('; ')})` : ''}`);
|
|
387
|
-
try {
|
|
388
|
-
if (sessionsDb) {
|
|
389
|
-
(0, agent_store_js_1.logActivity)(sessionsDb, {
|
|
390
|
-
agent_id: 'dashboard-agent',
|
|
391
|
-
agent_version: 0,
|
|
392
|
-
type: outcome,
|
|
393
|
-
input_summary: `reconcile ${channelKey}`,
|
|
394
|
-
output_summary: verdict.effects.join('; '),
|
|
395
|
-
execution_status: 'completed',
|
|
396
|
-
trigger_reason: 'reconcile',
|
|
479
|
+
run: (channelKey, deltaLines) => {
|
|
480
|
+
// Stage-2: at 'on' the reconcile leg becomes a board workorder (its
|
|
481
|
+
// bracket verification moves to the consumer's completion hook);
|
|
482
|
+
// shadow/off keep the legacy bracket-verified path (plan C1).
|
|
483
|
+
if ((0, workorder_publishers_js_1.resolveReconcileAction)(stage2Flag) === 'enqueue') {
|
|
484
|
+
try {
|
|
485
|
+
enqueueWorkOrderOrThrow('board', (0, workorder_publishers_js_1.boardReconcileKey)(channelKey, Date.now()), {
|
|
486
|
+
mode: 'reconcile',
|
|
487
|
+
channelKey,
|
|
488
|
+
deltaLines,
|
|
397
489
|
});
|
|
398
490
|
}
|
|
491
|
+
catch (err) {
|
|
492
|
+
routesLogger.error('[stage2] reconcile enqueue failed:', err instanceof Error ? err.message : err);
|
|
493
|
+
// REJECT so ReconcileScheduler restores pendingLines - a
|
|
494
|
+
// resolved failure silently drops this delta (PR bot round).
|
|
495
|
+
return Promise.reject(err instanceof Error ? err : new Error(String(err)));
|
|
496
|
+
}
|
|
497
|
+
return Promise.resolve();
|
|
399
498
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
action: 'reconcile_unverified',
|
|
409
|
-
target: channelKey,
|
|
499
|
+
return boardWriterQueue.push(async () => {
|
|
500
|
+
const scope = `reconcile:${channelKey}`;
|
|
501
|
+
const before = captureSnapshot(verifierDeps, scope);
|
|
502
|
+
const prompt = buildReconcilePrompt({
|
|
503
|
+
channelKey,
|
|
504
|
+
deltaLines,
|
|
505
|
+
todayIso: new Date().toISOString().slice(0, 10),
|
|
506
|
+
kagemushaContext,
|
|
410
507
|
});
|
|
411
|
-
|
|
412
|
-
|
|
508
|
+
await executeValidatedRun('dashboard-agent', prompt, {
|
|
509
|
+
requestTimeout: 300_000,
|
|
510
|
+
});
|
|
511
|
+
const verdict = verifyAfterRun(verifierDeps, before, scope);
|
|
512
|
+
const outcome = verdict.verified ? 'reconcile_verified' : 'reconcile_unverified';
|
|
513
|
+
console.log(`[reconcile] ${outcome} channel=${channelKey}${verdict.effects.length > 0 ? ` (${verdict.effects.join('; ')})` : ''}`);
|
|
514
|
+
try {
|
|
515
|
+
if (sessionsDb) {
|
|
516
|
+
(0, agent_store_js_1.logActivity)(sessionsDb, {
|
|
517
|
+
agent_id: 'dashboard-agent',
|
|
518
|
+
agent_version: 0,
|
|
519
|
+
type: outcome,
|
|
520
|
+
input_summary: `reconcile ${channelKey}`,
|
|
521
|
+
output_summary: verdict.effects.join('; '),
|
|
522
|
+
execution_status: 'completed',
|
|
523
|
+
trigger_reason: 'reconcile',
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
catch {
|
|
528
|
+
/* telemetry only */
|
|
529
|
+
}
|
|
530
|
+
if (!verdict.verified) {
|
|
531
|
+
// Loud, never blocking (observability over restriction).
|
|
532
|
+
eventBus.emit({
|
|
533
|
+
type: 'agent:action',
|
|
534
|
+
agent: 'Dashboard Agent',
|
|
535
|
+
action: 'reconcile_unverified',
|
|
536
|
+
target: channelKey,
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
},
|
|
413
541
|
});
|
|
414
542
|
eventBus.on('operator:channel-delta', (event) => {
|
|
415
543
|
if (event.type === 'operator:channel-delta') {
|
|
@@ -484,7 +612,20 @@ This saves resources. Only publish when there is genuinely new information to re
|
|
|
484
612
|
});
|
|
485
613
|
});
|
|
486
614
|
// Wiki trigger via executeValidatedRun
|
|
487
|
-
const doWikiRun = async () => {
|
|
615
|
+
const doWikiRun = async (trigger) => {
|
|
616
|
+
// Stage-2 gate (function level, plan F4). Wiki never enters shadow
|
|
617
|
+
// (resolvePublishAction returns 'legacy' there) - its Obsidian writes
|
|
618
|
+
// have no capture seam.
|
|
619
|
+
if ((0, workorder_publishers_js_1.resolvePublishAction)(stage2Flag, 'wiki') === 'enqueue') {
|
|
620
|
+
try {
|
|
621
|
+
const now = Date.now();
|
|
622
|
+
enqueueWorkOrderOrThrow('wiki', (0, workorder_publishers_js_1.wikiBatchKey)(trigger, now), { batchId: `${now}-${trigger}`, events: [trigger] }, trigger === 'manual' ? 'high' : undefined);
|
|
623
|
+
}
|
|
624
|
+
catch (err) {
|
|
625
|
+
routesLogger.error('[stage2] wiki enqueue failed:', err instanceof Error ? err.message : err);
|
|
626
|
+
}
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
488
629
|
if (!toolExecutor.getAgentProcessManager()) {
|
|
489
630
|
routesLogger.warn('[Wiki Agent] AgentProcessManager not available yet');
|
|
490
631
|
return;
|
|
@@ -519,21 +660,29 @@ This saves resources. Only compile when there is genuinely new information to do
|
|
|
519
660
|
routesLogger.error('[Wiki Agent] Error:', err instanceof Error ? err.message : err);
|
|
520
661
|
}
|
|
521
662
|
};
|
|
663
|
+
// Stage-2 wiki completion hook (plan E4): outcome reading only - the
|
|
664
|
+
// wiki:compiled events flow through the wikiPublisher independently.
|
|
665
|
+
if (workOrderConsumer) {
|
|
666
|
+
const { buildWikiAfterHook } = await import('../../operator/workorder-hooks.js');
|
|
667
|
+
workOrderConsumer.registerHook('wiki', {
|
|
668
|
+
after: buildWikiAfterHook((line) => routesLogger.debug(line)),
|
|
669
|
+
});
|
|
670
|
+
}
|
|
522
671
|
// Serialize ALL wiki runs (boot, event-driven, manual) on one chain -- the
|
|
523
672
|
// shared agent process rejects concurrent requests, so the boot run and a
|
|
524
673
|
// manual trigger raced into 'Process is busy' (same pattern as the
|
|
525
674
|
// dashboard agent's runDashboardAgent chain above).
|
|
526
675
|
let wikiRunChain = Promise.resolve();
|
|
527
|
-
const runWikiAgent = () => {
|
|
528
|
-
wikiRunChain = wikiRunChain.then(doWikiRun).catch(() => { });
|
|
676
|
+
const runWikiAgent = (trigger) => {
|
|
677
|
+
wikiRunChain = wikiRunChain.then(() => doWikiRun(trigger)).catch(() => { });
|
|
529
678
|
return wikiRunChain;
|
|
530
679
|
};
|
|
531
680
|
// Event-driven: compile when extraction completes. Trailing-edge debounce so
|
|
532
681
|
// bursts of extraction:completed events coalesce into one wiki compile run.
|
|
533
|
-
eventBus.onDebounced('extraction:completed', () => runWikiAgent(), 30_000);
|
|
682
|
+
eventBus.onDebounced('extraction:completed', () => runWikiAgent('extraction:completed'), 30_000);
|
|
534
683
|
// Promotion feeds the wiki: freshly promoted decisions are exactly the
|
|
535
684
|
// material daily notes and lessons compile from.
|
|
536
|
-
eventBus.onDebounced('memory:promoted', () => runWikiAgent(), 30_000);
|
|
685
|
+
eventBus.onDebounced('memory:promoted', () => runWikiAgent('memory:promoted'), 30_000);
|
|
537
686
|
// Emit agent:action notices when wiki pages are compiled
|
|
538
687
|
eventBus.on('wiki:compiled', (event) => {
|
|
539
688
|
if (event.type === 'wiki:compiled') {
|
|
@@ -549,11 +698,11 @@ This saves resources. Only compile when there is genuinely new information to do
|
|
|
549
698
|
});
|
|
550
699
|
// Manual trigger API
|
|
551
700
|
apiServer.app.post('/api/wiki/compile', auth_middleware_js_1.requireAuth, async (_req, res) => {
|
|
552
|
-
runWikiAgent().catch(() => { });
|
|
701
|
+
runWikiAgent('manual').catch(() => { });
|
|
553
702
|
res.json({ ok: true, message: 'Wiki compilation triggered' });
|
|
554
703
|
});
|
|
555
704
|
// First run after 15s (let connectors and dashboard agent go first)
|
|
556
|
-
setTimeout(runWikiAgent, 15_000);
|
|
705
|
+
setTimeout(() => runWikiAgent('boot'), 15_000);
|
|
557
706
|
routesLogger.info('[Wiki Agent] Ready — triggers: extraction:completed event, POST /api/wiki/compile');
|
|
558
707
|
}
|
|
559
708
|
// -- Memory Promotion: scheduled observation->decision curation --
|
|
@@ -579,7 +728,19 @@ This saves resources. Only compile when there is genuinely new information to do
|
|
|
579
728
|
'rules, recurring risk patterns; NEVER task lifecycle states, greetings, or logistics). ' +
|
|
580
729
|
'Include scopes (the source channel, and the project when identifiable) and event_date.\n' +
|
|
581
730
|
'5. Finish with exactly PROMOTED <n> or NO_UPDATE.';
|
|
582
|
-
const doPromotionRun = async () => {
|
|
731
|
+
const doPromotionRun = async (opts) => {
|
|
732
|
+
// Stage-2 gate (function level, plan F4). Promotion never enters shadow
|
|
733
|
+
// - its mama_save writes have no capture seam.
|
|
734
|
+
if ((0, workorder_publishers_js_1.resolvePublishAction)(stage2Flag, 'memory-curation') === 'enqueue') {
|
|
735
|
+
try {
|
|
736
|
+
const now = Date.now();
|
|
737
|
+
enqueueWorkOrderOrThrow('memory-curation', opts?.manual ? (0, workorder_publishers_js_1.promotionManualKey)(now) : (0, workorder_publishers_js_1.promotionKey)(now), { scheduledAt: new Date(now).toISOString() }, opts?.manual ? 'high' : undefined);
|
|
738
|
+
}
|
|
739
|
+
catch (err) {
|
|
740
|
+
routesLogger.error('[stage2] promotion enqueue failed:', err instanceof Error ? err.message : err);
|
|
741
|
+
}
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
583
744
|
if (!toolExecutor.getAgentProcessManager()) {
|
|
584
745
|
routesLogger.warn('[Memory Promotion] AgentProcessManager not available yet');
|
|
585
746
|
return;
|
|
@@ -606,17 +767,30 @@ This saves resources. Only compile when there is genuinely new information to do
|
|
|
606
767
|
routesLogger.error('[Memory Promotion] Error:', err instanceof Error ? err.message : err);
|
|
607
768
|
}
|
|
608
769
|
};
|
|
770
|
+
// Stage-2 promotion completion hook (plan E4): the PROMOTED <n> parse and
|
|
771
|
+
// its event emissions are the wiki ingress chain's second link - losing
|
|
772
|
+
// them in the workorder conversion would silently sever memory:promoted.
|
|
773
|
+
if (workOrderConsumer) {
|
|
774
|
+
const { buildPromotionAfterHook } = await import('../../operator/workorder-hooks.js');
|
|
775
|
+
workOrderConsumer.registerHook('memory-curation', {
|
|
776
|
+
after: buildPromotionAfterHook({
|
|
777
|
+
emitAgentAction: (action, target) => eventBus.emit({ type: 'agent:action', agent: 'Memory Agent', action, target }),
|
|
778
|
+
emitMemoryPromoted: (saved) => eventBus.emit({ type: 'memory:promoted', saved }),
|
|
779
|
+
log: (line) => console.log(line),
|
|
780
|
+
}),
|
|
781
|
+
});
|
|
782
|
+
}
|
|
609
783
|
// Serialize all promotion runs on one chain (same 'Process is busy' class
|
|
610
784
|
// as the dashboard and wiki agents).
|
|
611
785
|
let promotionRunChain = Promise.resolve();
|
|
612
|
-
const runMemoryPromotion = () => {
|
|
613
|
-
promotionRunChain = promotionRunChain.then(doPromotionRun).catch(() => { });
|
|
786
|
+
const runMemoryPromotion = (opts) => {
|
|
787
|
+
promotionRunChain = promotionRunChain.then(() => doPromotionRun(opts)).catch(() => { });
|
|
614
788
|
return promotionRunChain;
|
|
615
789
|
};
|
|
616
|
-
setTimeout(runMemoryPromotion, PROMOTION_INITIAL_DELAY_MS);
|
|
617
|
-
setInterval(runMemoryPromotion, PROMOTION_INTERVAL_MS);
|
|
790
|
+
setTimeout(() => runMemoryPromotion(), PROMOTION_INITIAL_DELAY_MS);
|
|
791
|
+
setInterval(() => runMemoryPromotion(), PROMOTION_INTERVAL_MS);
|
|
618
792
|
apiServer.app.post('/api/memory/promote', auth_middleware_js_1.requireAuth, (_req, res) => {
|
|
619
|
-
runMemoryPromotion().catch(() => { });
|
|
793
|
+
runMemoryPromotion({ manual: true }).catch(() => { });
|
|
620
794
|
res.json({ ok: true, message: 'Memory promotion run triggered' });
|
|
621
795
|
});
|
|
622
796
|
console.log(`[Memory Promotion] Ready: every ${PROMOTION_INTERVAL_MS / 3_600_000}h, POST /api/memory/promote`);
|
|
@@ -638,6 +812,7 @@ This saves resources. Only compile when there is genuinely new information to do
|
|
|
638
812
|
config: {
|
|
639
813
|
telegram: config.telegram,
|
|
640
814
|
multi_agent: config.multi_agent,
|
|
815
|
+
roles: config.roles,
|
|
641
816
|
},
|
|
642
817
|
// Evaluated at audit time: gateway-wiring registers the sender only
|
|
643
818
|
// for ACTIVE gateways, so a configured target on a dead gateway must
|