@jungjaehoon/mama-os 0.48.4 → 0.49.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/agent/agent-loop.d.ts +6 -7
- package/dist/agent/agent-loop.d.ts.map +1 -1
- package/dist/agent/agent-loop.js +93 -79
- 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 +0 -8
- package/dist/agent/code-act/host-bridge.js.map +1 -1
- package/dist/agent/codex-home.d.ts.map +1 -1
- package/dist/agent/codex-home.js +3 -1
- package/dist/agent/codex-home.js.map +1 -1
- package/dist/agent/gateway-tool-executor.d.ts +0 -16
- package/dist/agent/gateway-tool-executor.d.ts.map +1 -1
- package/dist/agent/gateway-tool-executor.js +0 -51
- package/dist/agent/gateway-tool-executor.js.map +1 -1
- package/dist/agent/gateway-tools.md +0 -1
- package/dist/agent/os-agent-capabilities.md +14 -34
- package/dist/agent/role-manager.d.ts.map +1 -1
- package/dist/agent/role-manager.js +6 -3
- 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 +0 -6
- package/dist/agent/tool-registry.js.map +1 -1
- package/dist/agent/types.d.ts +18 -6
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/api/graph-api-types.d.ts +0 -18
- package/dist/api/graph-api-types.d.ts.map +1 -1
- package/dist/api/graph-api.d.ts.map +1 -1
- package/dist/api/graph-api.js +9 -81
- package/dist/api/graph-api.js.map +1 -1
- package/dist/cli/commands/start.d.ts +7 -16
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +181 -188
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/config/types.d.ts.map +1 -1
- package/dist/cli/config/types.js +11 -4
- 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 +28 -11
- package/dist/cli/runtime/agent-loop-init.js.map +1 -1
- package/dist/cli/runtime/api-routes-init.d.ts +0 -2
- package/dist/cli/runtime/api-routes-init.d.ts.map +1 -1
- package/dist/cli/runtime/api-routes-init.js +9 -95
- package/dist/cli/runtime/api-routes-init.js.map +1 -1
- package/dist/cli/runtime/api-server-init.d.ts +2 -2
- package/dist/cli/runtime/api-server-init.d.ts.map +1 -1
- package/dist/cli/runtime/api-server-init.js +2 -2
- package/dist/cli/runtime/api-server-init.js.map +1 -1
- package/dist/cli/runtime/scheduler-init.d.ts +3 -4
- package/dist/cli/runtime/scheduler-init.d.ts.map +1 -1
- package/dist/cli/runtime/scheduler-init.js +6 -16
- package/dist/cli/runtime/scheduler-init.js.map +1 -1
- package/dist/cli/runtime/shutdown.d.ts +0 -1
- package/dist/cli/runtime/shutdown.d.ts.map +1 -1
- package/dist/cli/runtime/shutdown.js +1 -1
- package/dist/cli/runtime/shutdown.js.map +1 -1
- package/dist/cli/runtime/types.d.ts +0 -5
- package/dist/cli/runtime/types.d.ts.map +1 -1
- package/dist/concurrency/lane-manager.d.ts +1 -1
- package/dist/concurrency/lane-manager.d.ts.map +1 -1
- package/dist/concurrency/lane-manager.js +4 -2
- package/dist/concurrency/lane-manager.js.map +1 -1
- package/dist/concurrency/types.d.ts +4 -0
- package/dist/concurrency/types.d.ts.map +1 -1
- package/dist/concurrency/types.js.map +1 -1
- package/dist/envelope/types.d.ts +1 -1
- package/dist/envelope/types.d.ts.map +1 -1
- package/dist/envelope/types.js.map +1 -1
- package/dist/gateways/message-router.d.ts +13 -71
- package/dist/gateways/message-router.d.ts.map +1 -1
- package/dist/gateways/message-router.js +99 -489
- package/dist/gateways/message-router.js.map +1 -1
- package/dist/gateways/session-store.d.ts +3 -23
- package/dist/gateways/session-store.d.ts.map +1 -1
- package/dist/gateways/session-store.js +2 -64
- package/dist/gateways/session-store.js.map +1 -1
- package/dist/gateways/telegram-report-context-store.d.ts +4 -66
- package/dist/gateways/telegram-report-context-store.d.ts.map +1 -1
- package/dist/gateways/telegram-report-context-store.js +11 -235
- package/dist/gateways/telegram-report-context-store.js.map +1 -1
- package/dist/gateways/turn-contract.d.ts +7 -3
- package/dist/gateways/turn-contract.d.ts.map +1 -1
- package/dist/operator/operator-trigger-loop.d.ts +1 -12
- package/dist/operator/operator-trigger-loop.d.ts.map +1 -1
- package/dist/operator/operator-trigger-loop.js +6 -40
- package/dist/operator/operator-trigger-loop.js.map +1 -1
- package/dist/operator/owner-event-loop.d.ts +8 -7
- package/dist/operator/owner-event-loop.d.ts.map +1 -1
- package/dist/operator/owner-event-loop.js +7 -20
- package/dist/operator/owner-event-loop.js.map +1 -1
- package/dist/operator/owner-event-policy.d.ts.map +1 -1
- package/dist/operator/owner-event-policy.js +0 -2
- package/dist/operator/owner-event-policy.js.map +1 -1
- package/dist/operator/owner-event-prompt.d.ts +0 -2
- package/dist/operator/owner-event-prompt.d.ts.map +1 -1
- package/dist/operator/owner-event-prompt.js +1 -8
- package/dist/operator/owner-event-prompt.js.map +1 -1
- package/dist/operator/owner-runtime-journal.d.ts +21 -0
- package/dist/operator/owner-runtime-journal.d.ts.map +1 -0
- package/dist/operator/owner-runtime-journal.js +131 -0
- package/dist/operator/owner-runtime-journal.js.map +1 -0
- package/dist/operator/owner-runtime.d.ts +32 -0
- package/dist/operator/owner-runtime.d.ts.map +1 -0
- package/dist/operator/owner-runtime.js +31 -0
- package/dist/operator/owner-runtime.js.map +1 -0
- package/dist/operator/pending-report-store.d.ts +2 -2
- package/dist/operator/pending-report-store.d.ts.map +1 -1
- package/dist/operator/pending-report-store.js +4 -1
- package/dist/operator/pending-report-store.js.map +1 -1
- package/dist/operator/report-run.d.ts +7 -10
- package/dist/operator/report-run.d.ts.map +1 -1
- package/dist/operator/report-run.js +5 -26
- package/dist/operator/report-run.js.map +1 -1
- package/dist/operator/situation-report.d.ts +25 -6
- package/dist/operator/situation-report.d.ts.map +1 -1
- package/dist/operator/situation-report.js +25 -37
- package/dist/operator/situation-report.js.map +1 -1
- package/dist/operator/trigger-author.d.ts +0 -45
- package/dist/operator/trigger-author.d.ts.map +1 -1
- package/dist/operator/trigger-author.js +0 -122
- package/dist/operator/trigger-author.js.map +1 -1
- package/dist/operator/worker-run.d.ts +9 -23
- package/dist/operator/worker-run.d.ts.map +1 -1
- package/dist/operator/worker-run.js +18 -74
- package/dist/operator/worker-run.js.map +1 -1
- package/dist/operator/workorder-consumer.d.ts.map +1 -1
- package/dist/operator/workorder-consumer.js +7 -0
- package/dist/operator/workorder-consumer.js.map +1 -1
- package/dist/scheduler/cron-worker.d.ts +0 -3
- package/dist/scheduler/cron-worker.d.ts.map +1 -1
- package/dist/scheduler/cron-worker.js +2 -13
- package/dist/scheduler/cron-worker.js.map +1 -1
- package/dist/scheduler/heartbeat.d.ts +3 -1
- package/dist/scheduler/heartbeat.d.ts.map +1 -1
- package/dist/scheduler/heartbeat.js +13 -4
- package/dist/scheduler/heartbeat.js.map +1 -1
- package/dist/validation/types.d.ts +1 -1
- package/dist/validation/types.js +1 -1
- package/package.json +1 -1
- package/dist/cli/runtime/memory-agent-init.d.ts +0 -37
- package/dist/cli/runtime/memory-agent-init.d.ts.map +0 -1
- package/dist/cli/runtime/memory-agent-init.js +0 -202
- package/dist/cli/runtime/memory-agent-init.js.map +0 -1
- package/dist/gateways/owner-report-inbox.d.ts +0 -40
- package/dist/gateways/owner-report-inbox.d.ts.map +0 -1
- package/dist/gateways/owner-report-inbox.js +0 -128
- package/dist/gateways/owner-report-inbox.js.map +0 -1
- package/dist/multi-agent/memory-agent-persona.d.ts +0 -11
- package/dist/multi-agent/memory-agent-persona.d.ts.map +0 -1
- package/dist/multi-agent/memory-agent-persona.js +0 -175
- package/dist/multi-agent/memory-agent-persona.js.map +0 -1
- package/dist/operator/report-context.d.ts +0 -184
- package/dist/operator/report-context.d.ts.map +0 -1
- package/dist/operator/report-context.js +0 -1052
- package/dist/operator/report-context.js.map +0 -1
|
@@ -50,6 +50,7 @@ exports.causeFromOwnerMessage = causeFromOwnerMessage;
|
|
|
50
50
|
exports.protectImageAnalysis = protectImageAnalysis;
|
|
51
51
|
exports.buildUploadedMediaInstructions = buildUploadedMediaInstructions;
|
|
52
52
|
exports.hashSessionPolicyFingerprint = hashSessionPolicyFingerprint;
|
|
53
|
+
exports.projectOwnerConversationAgency = projectOwnerConversationAgency;
|
|
53
54
|
exports.agentSavedInTurn = agentSavedInTurn;
|
|
54
55
|
exports.createMockAgentLoop = createMockAgentLoop;
|
|
55
56
|
const node_path_1 = require("node:path");
|
|
@@ -61,6 +62,7 @@ const member_effective_scope_js_1 = require("./member-effective-scope.js");
|
|
|
61
62
|
const session_pool_js_1 = require("../agent/session-pool.js");
|
|
62
63
|
const agent_loop_js_1 = require("../agent/agent-loop.js");
|
|
63
64
|
const console_brief_js_1 = require("../operator/console-brief.js");
|
|
65
|
+
const owner_runtime_js_1 = require("../operator/owner-runtime.js");
|
|
64
66
|
const role_manager_js_1 = require("../agent/role-manager.js");
|
|
65
67
|
const gateway_tool_catalog_js_1 = require("../agent/gateway-tool-catalog.js");
|
|
66
68
|
const private_prompt_overlay_js_1 = require("../connectors/private-prompt-overlay.js");
|
|
@@ -68,7 +70,6 @@ const context_prompt_builder_js_1 = require("../agent/context-prompt-builder.js"
|
|
|
68
70
|
const prompt_enhancer_js_1 = require("../agent/prompt-enhancer.js");
|
|
69
71
|
const principal_js_1 = require("./principal.js");
|
|
70
72
|
const debugLogger = __importStar(require("@jungjaehoon/mama-core/debug-logger"));
|
|
71
|
-
const audit_task_queue_js_1 = require("../memory/audit-task-queue.js");
|
|
72
73
|
const agent_notice_queue_js_1 = require("../memory/agent-notice-queue.js");
|
|
73
74
|
const scope_context_js_1 = require("../memory/scope-context.js");
|
|
74
75
|
const learning_context_js_1 = require("../operator/learning-context.js");
|
|
@@ -76,7 +77,6 @@ const learning_read_js_1 = require("../operator/learning-read.js");
|
|
|
76
77
|
const turn_observer_js_1 = require("../operator/turn-observer.js");
|
|
77
78
|
const learning_markers_js_1 = require("../operator/learning-markers.js");
|
|
78
79
|
const recall_bundle_formatter_js_1 = require("../memory/recall-bundle-formatter.js");
|
|
79
|
-
const save_candidate_extractor_js_1 = require("../memory/save-candidate-extractor.js");
|
|
80
80
|
const untrusted_content_js_1 = require("../utils/untrusted-content.js");
|
|
81
81
|
const security_monitor_js_1 = require("../security/security-monitor.js");
|
|
82
82
|
const agent_store_js_1 = require("../db/agent-store.js");
|
|
@@ -221,12 +221,19 @@ const OWNER_CONSOLE_OPERATING_DISCIPLINE = `## Owner console operating disciplin
|
|
|
221
221
|
- You are the owner's operator, not an advisor. Report what you found and what you did; do not hand back work you could have done yourself.
|
|
222
222
|
- Gather before answering. Any question about status, work, or what happened is answered by CALLING your gateway tools first. Never reply "please check X" when a gateway tool can check X - check it, then report.
|
|
223
223
|
- Never claim a check you did not run. If a tool errored or was denied, name the tool and the error; a missing answer is reported as missing, never smoothed over.
|
|
224
|
-
- Act by default; ask only before the irreversible. Reversible work is yours to do and then report: reading, analysing, ranking, drafting, and writes whose only audience is this console (mama_save, task_create/task_update on your task board
|
|
224
|
+
- Act by default; ask only before the irreversible. Reversible work is yours to do and then report: reading, analysing, ranking, drafting, and writes whose only audience is this console (mama_save, task_create/task_update on your task board). Ask first only when the effect leaves this conversation and cannot be taken back - sending to another channel (telegram_send), uploading or overwriting shared files (drive_upload), storing credentials, or delegating a run you cannot cancel.
|
|
225
|
+
- A status or full-report request is YOUR judgment task in THIS conversation. Use compact descriptors and overview reads first, deepen only the material uncertainties through bounded pages, take authorized reversible actions, and answer the owner directly. Never hand it to another report agent and never reply with only an acknowledgement.
|
|
225
226
|
- Do not close by offering work you could have done. "Shall I rank these?" or "want it broken down by person?" is work, not a question - if you can do it now it belongs in this reply. End with what you did and what the OWNER has to decide, never with a menu of things you are willing to do next.
|
|
226
227
|
- Multi-step requests: decide the steps, carry out the reversible ones, then report the outcome. Do not return the plan as a suggestion and stop.
|
|
227
228
|
- Synthesize, do not dump. A raw tool result is evidence, not an answer - say what it means for the owner and cite which source answered.
|
|
228
229
|
- Before saying something is done, verify it (re-read the artifact or re-run the query) and say what you verified.
|
|
229
230
|
- Reply in the language the owner writes in.`;
|
|
231
|
+
function projectOwnerConversationAgency(roleName, role) {
|
|
232
|
+
if (roleName !== 'owner_console') {
|
|
233
|
+
return role;
|
|
234
|
+
}
|
|
235
|
+
return (0, owner_runtime_js_1.projectOwnerRuntimeRole)(role);
|
|
236
|
+
}
|
|
230
237
|
/** Channel-less host alarms (workorder failures) park here; every resumed
|
|
231
238
|
* owner turn reads this key in addition to its own channel key. */
|
|
232
239
|
exports.OPERATOR_BROADCAST_NOTICE_KEY = 'operator:broadcast';
|
|
@@ -268,7 +275,7 @@ const MEMBER_BLOCKED_TOOLS = [
|
|
|
268
275
|
'Read',
|
|
269
276
|
'Write',
|
|
270
277
|
'Bash',
|
|
271
|
-
'
|
|
278
|
+
'native_subagent',
|
|
272
279
|
'discord_send',
|
|
273
280
|
'slack_send',
|
|
274
281
|
'telegram_send',
|
|
@@ -276,7 +283,6 @@ const MEMBER_BLOCKED_TOOLS = [
|
|
|
276
283
|
'task_create',
|
|
277
284
|
'task_update',
|
|
278
285
|
'task_reclassify',
|
|
279
|
-
'report_request',
|
|
280
286
|
'report_publish',
|
|
281
287
|
'wiki_read',
|
|
282
288
|
'wiki_publish',
|
|
@@ -358,9 +364,6 @@ function sanitizeForPrompt(text) {
|
|
|
358
364
|
.replace(/</g, '<')
|
|
359
365
|
.replace(/>/g, '>');
|
|
360
366
|
}
|
|
361
|
-
function stripGatewayDecorations(text) {
|
|
362
|
-
return text.replace(/^(?:\s*\|\|[\s\S]*?\|\|\s*)+/u, '').trim();
|
|
363
|
-
}
|
|
364
367
|
/**
|
|
365
368
|
* True when the agent persisted memory during THIS turn (gateway mama_save in
|
|
366
369
|
* the reasoning header) - the extractor safety net then skips to avoid the
|
|
@@ -406,28 +409,11 @@ class MessageRouter {
|
|
|
406
409
|
envelopeConfig;
|
|
407
410
|
envelopeAuthority;
|
|
408
411
|
privateConnectorPolicy;
|
|
409
|
-
reportCarry;
|
|
410
|
-
ownerReportInbox;
|
|
411
412
|
memberScopeResolver;
|
|
412
413
|
roleManager;
|
|
413
414
|
promptEnhancer;
|
|
414
|
-
gatewayRegistry = null;
|
|
415
|
-
memoryAgentProcessManager;
|
|
416
|
-
memoryAuditQueue;
|
|
417
415
|
memoryNoticeQueue = new agent_notice_queue_js_1.AgentNoticeQueue();
|
|
418
|
-
memoryAuditCooldowns = new Map();
|
|
419
416
|
channelTails = new Map();
|
|
420
|
-
memoryAgentStats = {
|
|
421
|
-
turnsObserved: 0,
|
|
422
|
-
candidatesDetected: 0,
|
|
423
|
-
factsExtracted: 0,
|
|
424
|
-
factsSaved: 0,
|
|
425
|
-
acksApplied: 0,
|
|
426
|
-
acksSkipped: 0,
|
|
427
|
-
acksFailed: 0,
|
|
428
|
-
lastExtraction: null,
|
|
429
|
-
recentExtractions: [],
|
|
430
|
-
};
|
|
431
417
|
// Sessions DB for MAMA frontdoor activity logging
|
|
432
418
|
sessionsDb = null;
|
|
433
419
|
setSessionsDb(db) {
|
|
@@ -438,11 +424,7 @@ class MessageRouter {
|
|
|
438
424
|
setUICommandQueue(queue) {
|
|
439
425
|
this.uiCommandQueue = queue;
|
|
440
426
|
}
|
|
441
|
-
|
|
442
|
-
validationService = null;
|
|
443
|
-
setValidationService(svc) {
|
|
444
|
-
this.validationService = svc;
|
|
445
|
-
}
|
|
427
|
+
setValidationService(_svc) { }
|
|
446
428
|
getPageContextPrefix(message) {
|
|
447
429
|
if (!this.uiCommandQueue) {
|
|
448
430
|
return '';
|
|
@@ -518,30 +500,19 @@ class MessageRouter {
|
|
|
518
500
|
lines.push('</viewer-context>');
|
|
519
501
|
return lines.join('\n') + '\n';
|
|
520
502
|
}
|
|
521
|
-
setGatewayRegistry(
|
|
522
|
-
this.gatewayRegistry = registry;
|
|
523
|
-
}
|
|
524
|
-
setMemoryAgent(processManager) {
|
|
525
|
-
this.memoryAgentProcessManager = processManager;
|
|
526
|
-
this.memoryAuditQueue = new audit_task_queue_js_1.AuditTaskQueue(async (job) => {
|
|
527
|
-
const process = await processManager.getSharedProcess('memory');
|
|
528
|
-
const result = await process.sendMessage(this.buildMemoryAuditPrompt(job), {
|
|
529
|
-
sourceTurnId: job.turnId,
|
|
530
|
-
sourceMessageRef: [job.source, job.channelId, job.turnId].filter(Boolean).join(':'),
|
|
531
|
-
parentModelRunId: job.parentModelRunId,
|
|
532
|
-
});
|
|
533
|
-
if (result &&
|
|
534
|
-
typeof result === 'object' &&
|
|
535
|
-
'ack' in result &&
|
|
536
|
-
result.ack &&
|
|
537
|
-
typeof result.ack === 'object') {
|
|
538
|
-
return result.ack;
|
|
539
|
-
}
|
|
540
|
-
return this.classifyMemoryAuditResponse(typeof result?.response === 'string' ? result.response : '');
|
|
541
|
-
});
|
|
542
|
-
}
|
|
503
|
+
setGatewayRegistry(_registry) { }
|
|
543
504
|
getMemoryAgentStats() {
|
|
544
|
-
return {
|
|
505
|
+
return {
|
|
506
|
+
turnsObserved: 0,
|
|
507
|
+
candidatesDetected: 0,
|
|
508
|
+
factsExtracted: 0,
|
|
509
|
+
factsSaved: 0,
|
|
510
|
+
acksApplied: 0,
|
|
511
|
+
acksSkipped: 0,
|
|
512
|
+
acksFailed: 0,
|
|
513
|
+
lastExtraction: null,
|
|
514
|
+
recentExtractions: [],
|
|
515
|
+
};
|
|
545
516
|
}
|
|
546
517
|
/**
|
|
547
518
|
* Host-code enqueue into the owner notice surface (Stage-2 workorder
|
|
@@ -561,39 +532,6 @@ class MessageRouter {
|
|
|
561
532
|
relevant_memories: [],
|
|
562
533
|
});
|
|
563
534
|
}
|
|
564
|
-
/**
|
|
565
|
-
* Public API for auditing a conversation via the memory agent.
|
|
566
|
-
* Used by the /api/mama/audit-conversation endpoint for benchmarking.
|
|
567
|
-
* Bypasses cooldown and candidate detection — caller provides conversation + optional candidates.
|
|
568
|
-
*/
|
|
569
|
-
async auditConversation(job) {
|
|
570
|
-
if (!this.memoryAuditQueue) {
|
|
571
|
-
throw new Error('Memory agent not initialized — cannot audit conversation');
|
|
572
|
-
}
|
|
573
|
-
const auditJob = {
|
|
574
|
-
turnId: `audit_${Date.now()}`,
|
|
575
|
-
channelKey: 'api:default',
|
|
576
|
-
source: 'api',
|
|
577
|
-
scopeContext: job.scopes.map((s) => ({
|
|
578
|
-
kind: s.kind,
|
|
579
|
-
id: s.id,
|
|
580
|
-
})),
|
|
581
|
-
conversation: job.conversation,
|
|
582
|
-
candidates: job.candidates?.map((c) => ({
|
|
583
|
-
id: `api_${Date.now()}`,
|
|
584
|
-
kind: c.kind,
|
|
585
|
-
topicHint: c.topicHint,
|
|
586
|
-
confidence: c.confidence,
|
|
587
|
-
summary: c.summary,
|
|
588
|
-
evidence: [c.summary],
|
|
589
|
-
channelKey: 'api:default',
|
|
590
|
-
source: 'api',
|
|
591
|
-
channelId: 'default',
|
|
592
|
-
createdAt: Date.now(),
|
|
593
|
-
})),
|
|
594
|
-
};
|
|
595
|
-
return this.memoryAuditQueue.enqueue(auditJob);
|
|
596
|
-
}
|
|
597
535
|
constructor(sessionStore, agentLoop, mamaApi, config = {}, envelopeConfig, envelopeAuthority, dependencies = {}) {
|
|
598
536
|
this.sessionStore = sessionStore;
|
|
599
537
|
this.agentLoop = agentLoop;
|
|
@@ -605,8 +543,6 @@ class MessageRouter {
|
|
|
605
543
|
this.envelopeAuthority = envelopeAuthority;
|
|
606
544
|
this.privateConnectorPolicy =
|
|
607
545
|
dependencies.privateConnectorPolicy ?? DEFAULT_PRIVATE_CONNECTOR_POLICY;
|
|
608
|
-
this.reportCarry = dependencies.reportCarry;
|
|
609
|
-
this.ownerReportInbox = dependencies.ownerReportInbox;
|
|
610
546
|
this.memberScopeResolver = dependencies.memberScopeResolver;
|
|
611
547
|
if (this.envelopeConfig && !this.envelopeAuthority) {
|
|
612
548
|
throw new Error('[envelope] ReactiveEnvelopeConfig provided without EnvelopeAuthority');
|
|
@@ -647,19 +583,16 @@ class MessageRouter {
|
|
|
647
583
|
createAgentContext(message, sessionId) {
|
|
648
584
|
// Per-message trust context: chatType is runtime state (a locked allowlist
|
|
649
585
|
// alone cannot prove owner - allowlisted groups contain third parties).
|
|
650
|
-
const { roleName, role } = this.
|
|
651
|
-
channelId: message.channelId,
|
|
652
|
-
chatType: typeof message.metadata?.chatType === 'string' ? message.metadata.chatType : undefined,
|
|
653
|
-
principal: message.principal,
|
|
654
|
-
});
|
|
586
|
+
const { roleName, role } = this.resolveMessageRole(message);
|
|
655
587
|
const surface = roleName === 'owner_console'
|
|
656
588
|
? 'owner_console'
|
|
657
589
|
: roleName === 'os_agent'
|
|
658
590
|
? 'os_agent'
|
|
659
591
|
: 'multi-agent-generic';
|
|
660
|
-
const
|
|
592
|
+
const connectorProjectedRole = message.principal?.class === 'member'
|
|
661
593
|
? memberReadOnlyRole(role)
|
|
662
594
|
: this.privateConnectorPolicy.projectRole(surface, role);
|
|
595
|
+
const projectedRole = projectOwnerConversationAgency(roleName, connectorProjectedRole);
|
|
663
596
|
const capabilities = this.roleManager.getCapabilities(projectedRole);
|
|
664
597
|
const limitations = this.roleManager.getLimitations(projectedRole);
|
|
665
598
|
const ctx = (0, context_prompt_builder_js_1.createAgentContext)(message.source, roleName, projectedRole, {
|
|
@@ -677,6 +610,13 @@ class MessageRouter {
|
|
|
677
610
|
}
|
|
678
611
|
return ctx;
|
|
679
612
|
}
|
|
613
|
+
resolveMessageRole(message) {
|
|
614
|
+
return this.roleManager.getRoleForSource(message.source, {
|
|
615
|
+
channelId: message.channelId,
|
|
616
|
+
chatType: typeof message.metadata?.chatType === 'string' ? message.metadata.chatType : undefined,
|
|
617
|
+
principal: message.principal,
|
|
618
|
+
});
|
|
619
|
+
}
|
|
680
620
|
/**
|
|
681
621
|
* Check if message should trigger auto-translation
|
|
682
622
|
* Returns true for short messages or image-related text
|
|
@@ -766,14 +706,17 @@ class MessageRouter {
|
|
|
766
706
|
});
|
|
767
707
|
assertUsableMemberEffectiveScope(memberEffectiveScope, admittedPrincipal.principalId ?? '');
|
|
768
708
|
}
|
|
769
|
-
const
|
|
770
|
-
const
|
|
709
|
+
const admissionRoleName = this.resolveMessageRole(admittedMessage).roleName;
|
|
710
|
+
const admissionKey = admissionRoleName === 'owner_console'
|
|
711
|
+
? owner_runtime_js_1.OWNER_RUNTIME_SESSION_KEY
|
|
712
|
+
: (0, session_pool_js_1.buildChannelKey)(admittedMessage.source, (0, principal_js_1.laneChannelId)(admittedMessage.channelId, lane));
|
|
713
|
+
const previous = this.channelTails.get(admissionKey);
|
|
771
714
|
let release;
|
|
772
715
|
const gate = new Promise((resolve) => {
|
|
773
716
|
release = resolve;
|
|
774
717
|
});
|
|
775
718
|
const currentTail = (previous ?? Promise.resolve()).catch(() => { }).then(() => gate);
|
|
776
|
-
this.channelTails.set(
|
|
719
|
+
this.channelTails.set(admissionKey, currentTail);
|
|
777
720
|
try {
|
|
778
721
|
if (previous) {
|
|
779
722
|
processOptions?.onQueued?.();
|
|
@@ -786,8 +729,8 @@ class MessageRouter {
|
|
|
786
729
|
}
|
|
787
730
|
finally {
|
|
788
731
|
release();
|
|
789
|
-
if (this.channelTails.get(
|
|
790
|
-
this.channelTails.delete(
|
|
732
|
+
if (this.channelTails.get(admissionKey) === currentTail) {
|
|
733
|
+
this.channelTails.delete(admissionKey);
|
|
791
734
|
}
|
|
792
735
|
}
|
|
793
736
|
}
|
|
@@ -853,10 +796,15 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
853
796
|
}
|
|
854
797
|
// 1. Get or create session (by source + channelId)
|
|
855
798
|
const session = this.sessionStore.getOrCreate(message.source, sessionChannelId, message.userId, message.channelName);
|
|
799
|
+
const agentContext = this.createAgentContext(message, session.id);
|
|
800
|
+
const ownerRuntimeRole = agentContext.roleName === 'owner_console'
|
|
801
|
+
? projectOwnerConversationAgency('owner_console', this.resolveMessageRole(message).role)
|
|
802
|
+
: agentContext.role;
|
|
856
803
|
// 2. Check if session is busy (another request in progress)
|
|
857
804
|
const channelKey = (0, session_pool_js_1.buildChannelKey)(message.source, sessionChannelId);
|
|
805
|
+
const runtimeSessionKey = agentContext.roleName === 'owner_console' ? owner_runtime_js_1.OWNER_RUNTIME_SESSION_KEY : channelKey;
|
|
858
806
|
const sessionPool = (0, session_pool_js_1.getSessionPool)();
|
|
859
|
-
const initialSession = sessionPool.getSession(
|
|
807
|
+
const initialSession = sessionPool.getSession(runtimeSessionKey);
|
|
860
808
|
let cliSessionId = initialSession.sessionId;
|
|
861
809
|
let isNewCliSession = initialSession.isNew;
|
|
862
810
|
const busy = initialSession.busy;
|
|
@@ -864,6 +812,7 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
864
812
|
// assignment sits inside the agent-run block and is not visible at the return.
|
|
865
813
|
let completedModelRunId = null;
|
|
866
814
|
let completedProvenanceReason = 'backend_no_run';
|
|
815
|
+
let completedOwnerJournalProvenance;
|
|
867
816
|
const sourceTurnId = message.metadata?.messageId ?? `generated:${(0, node_crypto_1.randomUUID)().replace(/-/g, '')}`;
|
|
868
817
|
const sourceMessageRef = [message.source, message.channelId, sourceTurnId]
|
|
869
818
|
.filter(Boolean)
|
|
@@ -882,7 +831,7 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
882
831
|
while (Date.now() - waitStart < maxWaitMs) {
|
|
883
832
|
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
884
833
|
// Use read-only peek to avoid side effects (no lock increment)
|
|
885
|
-
const check = sessionPool.peekSession(
|
|
834
|
+
const check = sessionPool.peekSession(runtimeSessionKey);
|
|
886
835
|
if (!check.busy) {
|
|
887
836
|
logger.debug(`Session released for ${channelKey} after ${Math.round((Date.now() - waitStart) / 1000)}s`);
|
|
888
837
|
break;
|
|
@@ -894,7 +843,7 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
894
843
|
}
|
|
895
844
|
}
|
|
896
845
|
// Re-acquire session after wait (properly locks it this time)
|
|
897
|
-
const reacquired = sessionPool.getSession(
|
|
846
|
+
const reacquired = sessionPool.getSession(runtimeSessionKey);
|
|
898
847
|
if (reacquired.busy) {
|
|
899
848
|
// Still busy after timeout - we never acquired the lock
|
|
900
849
|
throw new Error(`Session for ${channelKey} timed out after ${maxWaitMs / 1000}s`);
|
|
@@ -907,35 +856,17 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
907
856
|
const releaseCliSessionLock = () => {
|
|
908
857
|
if (!acquiredLock || lockReleased)
|
|
909
858
|
return;
|
|
910
|
-
sessionPool.releaseSession(
|
|
859
|
+
sessionPool.releaseSession(runtimeSessionKey, cliSessionId);
|
|
911
860
|
lockReleased = true;
|
|
912
861
|
};
|
|
913
862
|
try {
|
|
914
|
-
const agentContext = this.createAgentContext(message, session.id);
|
|
915
|
-
const reportCarryTarget = agentContext.roleName === 'owner_console' && message.source === 'telegram'
|
|
916
|
-
? { source: message.source, channelId: message.channelId }
|
|
917
|
-
: null;
|
|
918
|
-
const reportCarryChannelKey = reportCarryTarget
|
|
919
|
-
? (0, session_pool_js_1.buildChannelKey)(reportCarryTarget.source, (0, principal_js_1.laneChannelId)(reportCarryTarget.channelId, lane))
|
|
920
|
-
: null;
|
|
921
|
-
const reportCarryPeek = reportCarryTarget
|
|
922
|
-
? (this.reportCarry?.peek(reportCarryTarget) ?? null)
|
|
923
|
-
: null;
|
|
924
|
-
// TG-05: inbox lookup only AFTER the owner-console trust check
|
|
925
|
-
// (reportCarryTarget is non-null only for a verified owner turn). The
|
|
926
|
-
// snapshot is captured once; its exact ordered IDs are the only
|
|
927
|
-
// consumable set for this turn.
|
|
928
|
-
const ownerReportSnapshot = reportCarryTarget
|
|
929
|
-
? (this.ownerReportInbox?.snapshot(reportCarryTarget) ?? null)
|
|
930
|
-
: null;
|
|
931
863
|
const mediaInstructions = buildUploadedMediaInstructions(message, agentContext.role.allowedTools, agentContext.roleName === 'owner_console');
|
|
932
|
-
// Save user message immediately for crash/refresh resilience.
|
|
933
|
-
// report-consuming turn is provisional until its atomic final commit.
|
|
864
|
+
// Save user message immediately for crash/refresh resilience.
|
|
934
865
|
this.sessionStore.appendMessage(session.id, {
|
|
935
866
|
role: 'user',
|
|
936
867
|
content: [message.text, mediaInstructions].filter(Boolean).join('\n\n'),
|
|
937
868
|
timestamp: Date.now(),
|
|
938
|
-
}
|
|
869
|
+
});
|
|
939
870
|
let response = '';
|
|
940
871
|
let context = { prompt: '', decisions: [], hasContext: false };
|
|
941
872
|
let streamFlushTimer = null;
|
|
@@ -948,7 +879,6 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
948
879
|
// CONTINUE turns: Codex server retains full conversation via threadId,
|
|
949
880
|
// so skip expensive prompt rebuilding (embedding search, DB history, etc.)
|
|
950
881
|
let systemPrompt;
|
|
951
|
-
let ownerReportHistoryPrompt = '';
|
|
952
882
|
const historyContext = message.metadata?.historyContext;
|
|
953
883
|
// Public turns never inspect private workspace rules, skills, or AGENTS files.
|
|
954
884
|
const enhanced = isPublicLane
|
|
@@ -957,7 +887,12 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
957
887
|
// TG-05: every main backend keeps its live conversation in a durable
|
|
958
888
|
// runtime session. Only a genuinely new/replacement session rebuilds
|
|
959
889
|
// bounded MAMA context.
|
|
960
|
-
|
|
890
|
+
// The in-memory SessionPool is empty after a daemon restart, while a
|
|
891
|
+
// durable Codex thread may still be alive. Codex first attempts that
|
|
892
|
+
// thread with a minimal continuation; AgentLoop invokes the lazy full
|
|
893
|
+
// builder only when the registry says the thread is actually missing.
|
|
894
|
+
const lazyDurableResume = this.config.backend === 'codex' && this.agentLoop.probesDurableSession === true;
|
|
895
|
+
const needsFullContext = isNewCliSession && !lazyDurableResume;
|
|
961
896
|
// Persistent backends skip repeated retrieval after the first turn.
|
|
962
897
|
context =
|
|
963
898
|
!isPublicLane && needsFullContext && this.config.implicitLegacyContextSearch
|
|
@@ -982,8 +917,8 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
982
917
|
source: message.source,
|
|
983
918
|
channelId: message.channelId,
|
|
984
919
|
});
|
|
985
|
-
systemPrompt = this.buildSystemPrompt(session, context.prompt, historyContext, sessionStartupContext, agentContext, enhanced,
|
|
986
|
-
|
|
920
|
+
systemPrompt = this.buildSystemPrompt(session, context.prompt, historyContext, sessionStartupContext, agentContext, enhanced, !(runtimeSessionKey === owner_runtime_js_1.OWNER_RUNTIME_SESSION_KEY &&
|
|
921
|
+
this.agentLoop.ownerRecoveryJournalEnabled === true), trelloAvailable);
|
|
987
922
|
}
|
|
988
923
|
}
|
|
989
924
|
// 7. Run agent loop (with session info for lane-based concurrency)
|
|
@@ -1019,12 +954,13 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1019
954
|
// still exist and should be resumed. Keep prompt/context freshness separate
|
|
1020
955
|
// from backend conversation continuity.
|
|
1021
956
|
const shouldResumeBackend = this.config.backend === 'codex' ? true : shouldResume;
|
|
957
|
+
const usesContinuationContext = shouldResume || lazyDurableResume;
|
|
1022
958
|
// For resumed sessions: inject minimal context only
|
|
1023
959
|
// Persistent CLI keeps the process alive with full system prompt from initial request
|
|
1024
960
|
// Only inject per-message context (related decisions) to avoid context overflow
|
|
1025
961
|
const effectivePrompt = isPublicLane
|
|
1026
962
|
? publicLaneBaseInstructions
|
|
1027
|
-
:
|
|
963
|
+
: usesContinuationContext
|
|
1028
964
|
? this.buildMinimalResumePrompt(context.prompt, agentContext)
|
|
1029
965
|
: systemPrompt;
|
|
1030
966
|
// Wrap stream callbacks to accumulate deltas and periodically flush to DB
|
|
@@ -1047,7 +983,6 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1047
983
|
}
|
|
1048
984
|
}, streamFlushIntervalMs);
|
|
1049
985
|
}
|
|
1050
|
-
let parentModelRunId;
|
|
1051
986
|
// Skill on-demand injection: prepend matched skill content to user message
|
|
1052
987
|
// (not system prompt — PersistentCLI can't update system prompt after creation)
|
|
1053
988
|
const skillPrefix = !isPublicLane && enhanced.skillContent
|
|
@@ -1070,17 +1005,20 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1070
1005
|
const envelope = this.buildReactiveEnvelope(message, agentContext, memberEffectiveScope);
|
|
1071
1006
|
const options = {
|
|
1072
1007
|
systemPrompt: effectivePrompt,
|
|
1073
|
-
...(ownerReportHistoryPrompt ? { ownerReportHistoryPrompt } : {}),
|
|
1074
1008
|
sessionPolicyFingerprint,
|
|
1075
1009
|
...(memberEffectiveScope ? { memberEffectiveScope } : {}),
|
|
1076
1010
|
...(memberEffectiveScope ? { memberScopeRequired: true } : {}),
|
|
1077
1011
|
userId: message.userId,
|
|
1078
1012
|
model: roleModel, // Role-specific model override
|
|
1079
1013
|
maxTurns: roleMaxTurns, // Role-specific max turns
|
|
1080
|
-
sessionKey:
|
|
1014
|
+
sessionKey: runtimeSessionKey,
|
|
1015
|
+
...(runtimeSessionKey === owner_runtime_js_1.OWNER_RUNTIME_SESSION_KEY ? { lanePriority: 100 } : {}),
|
|
1081
1016
|
source: message.source,
|
|
1082
1017
|
channelId: message.channelId,
|
|
1083
1018
|
agentContext,
|
|
1019
|
+
...(runtimeSessionKey === owner_runtime_js_1.OWNER_RUNTIME_SESSION_KEY
|
|
1020
|
+
? { sessionPolicyRole: ownerRuntimeRole }
|
|
1021
|
+
: {}),
|
|
1084
1022
|
resumeSession: shouldResumeBackend,
|
|
1085
1023
|
cliSessionId, // Pass CLI session ID to avoid double-locking
|
|
1086
1024
|
// AgentLoop may replace a stale backend session while this request still
|
|
@@ -1092,6 +1030,9 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1092
1030
|
envelope,
|
|
1093
1031
|
sourceTurnId,
|
|
1094
1032
|
sourceMessageRef,
|
|
1033
|
+
...(runtimeSessionKey === owner_runtime_js_1.OWNER_RUNTIME_SESSION_KEY
|
|
1034
|
+
? { ownerJournalPrompt: message.text, ownerJournalTrust: 'owner' }
|
|
1035
|
+
: {}),
|
|
1095
1036
|
// The owner's message is what caused whatever this turn changes, and the router
|
|
1096
1037
|
// knows it BEFORE the agent runs - the same shape as a reconcile work order
|
|
1097
1038
|
// carrying its delta batch. Without this the chat lane had no cause wire at all:
|
|
@@ -1104,7 +1045,7 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1104
1045
|
// citation this ledger exists to refuse.
|
|
1105
1046
|
...(causeFromOwnerMessage(sourceTurnId, sourceMessageRef) ?? {}),
|
|
1106
1047
|
};
|
|
1107
|
-
if (
|
|
1048
|
+
if (shouldResumeBackend) {
|
|
1108
1049
|
options.freshSessionSystemPrompt = async () => {
|
|
1109
1050
|
if (isPublicLane) {
|
|
1110
1051
|
return memberEffectiveScope
|
|
@@ -1118,27 +1059,21 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1118
1059
|
source: message.source,
|
|
1119
1060
|
channelId: message.channelId,
|
|
1120
1061
|
});
|
|
1121
|
-
return this.buildSystemPrompt(session, freshContext.prompt, historyContext, sessionStartupContext, agentContext, enhanced,
|
|
1062
|
+
return this.buildSystemPrompt(session, freshContext.prompt, historyContext, sessionStartupContext, agentContext, enhanced, !(runtimeSessionKey === owner_runtime_js_1.OWNER_RUNTIME_SESSION_KEY &&
|
|
1063
|
+
this.agentLoop.ownerRecoveryJournalEnabled === true), trelloAvailable);
|
|
1122
1064
|
};
|
|
1123
|
-
if (!isPublicLane) {
|
|
1124
|
-
options.freshSessionOwnerReportHistoryPrompt = () => this.buildOwnerReportHistoryPrompt(session);
|
|
1125
|
-
}
|
|
1126
1065
|
}
|
|
1127
|
-
if (
|
|
1066
|
+
if (usesContinuationContext) {
|
|
1128
1067
|
logger.info(`Resuming CLI session (minimal: ${effectivePrompt.length} chars)`);
|
|
1129
1068
|
}
|
|
1130
1069
|
else {
|
|
1131
1070
|
logger.info(`New CLI session (full: ${systemPrompt.length} chars)`);
|
|
1132
1071
|
}
|
|
1133
|
-
// TG-05/TG-06: carry is captured once before the model call and travels
|
|
1134
|
-
// only in this turn's user message, never in a rebuilt system prompt.
|
|
1135
|
-
const carryPrefix = !isPublicLane ? (reportCarryPeek?.prefix ?? '') : '';
|
|
1136
|
-
const inboxPrefix = !isPublicLane && ownerReportSnapshot ? `${ownerReportSnapshot.text}\n\n` : '';
|
|
1137
1072
|
try {
|
|
1138
1073
|
if (isPublicLane) {
|
|
1139
1074
|
memoryPrefix = '';
|
|
1140
1075
|
}
|
|
1141
|
-
else if (
|
|
1076
|
+
else if (usesContinuationContext) {
|
|
1142
1077
|
// Per-channel notices PLUS the operator broadcast key: host-code
|
|
1143
1078
|
// alarms (workorder failures) have no conversation channel at
|
|
1144
1079
|
// enqueue time - without the broadcast read they dead-letter
|
|
@@ -1166,7 +1101,7 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1166
1101
|
}
|
|
1167
1102
|
catch (err) {
|
|
1168
1103
|
logger.warn(`[memory-prefix] Failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1169
|
-
if (!isPublicLane &&
|
|
1104
|
+
if (!isPublicLane && usesContinuationContext) {
|
|
1170
1105
|
try {
|
|
1171
1106
|
memoryPrefix = await this.getPerTurnMemoryPrefix(message);
|
|
1172
1107
|
}
|
|
@@ -1212,7 +1147,7 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1212
1147
|
}
|
|
1213
1148
|
// Add text content (with memory context, skill context, and page context)
|
|
1214
1149
|
const pageCtx = isPublicLane ? '' : this.getPageContextPrefix(message);
|
|
1215
|
-
const effectiveMessageText = `${pageCtx}${
|
|
1150
|
+
const effectiveMessageText = `${pageCtx}${memoryPrefix}${learningPrefix}${skillPrefix}${messageText || ''}`;
|
|
1216
1151
|
if (effectiveMessageText) {
|
|
1217
1152
|
contentBlocks.push({ type: 'text', text: effectiveMessageText });
|
|
1218
1153
|
}
|
|
@@ -1247,48 +1182,27 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1247
1182
|
const turnStart = Date.now();
|
|
1248
1183
|
const result = await this.agentLoop.runWithContent(contentBlocks, options);
|
|
1249
1184
|
response = result.response;
|
|
1250
|
-
parentModelRunId = result.modelRunId ?? undefined;
|
|
1251
1185
|
completedModelRunId = result.modelRunId ?? completedModelRunId;
|
|
1252
1186
|
if (result.modelRunProvenance === 'commit_failed') {
|
|
1253
1187
|
completedProvenanceReason = 'commit_failed';
|
|
1254
1188
|
}
|
|
1189
|
+
completedOwnerJournalProvenance = result.ownerJournalProvenance;
|
|
1255
1190
|
this.logFrontdoorActivity(message, message.text, response, Date.now() - turnStart);
|
|
1256
1191
|
}
|
|
1257
1192
|
else {
|
|
1258
1193
|
const pageCtx = isPublicLane ? '' : this.getPageContextPrefix(message);
|
|
1259
|
-
const effectiveText = `${pageCtx}${
|
|
1194
|
+
const effectiveText = `${pageCtx}${memoryPrefix}${learningPrefix}${skillPrefix}${message.text}`;
|
|
1260
1195
|
const turnStart = Date.now();
|
|
1261
1196
|
const result = await this.agentLoop.run(effectiveText, options);
|
|
1262
1197
|
response = result.response;
|
|
1263
|
-
parentModelRunId = result.modelRunId ?? undefined;
|
|
1264
1198
|
completedModelRunId = result.modelRunId ?? completedModelRunId;
|
|
1265
1199
|
if (result.modelRunProvenance === 'commit_failed') {
|
|
1266
1200
|
completedProvenanceReason = 'commit_failed';
|
|
1267
1201
|
}
|
|
1202
|
+
completedOwnerJournalProvenance = result.ownerJournalProvenance;
|
|
1268
1203
|
this.logFrontdoorActivity(message, message.text, response, Date.now() - turnStart);
|
|
1269
1204
|
}
|
|
1270
|
-
|
|
1271
|
-
// Dual-save dedup: when the agent ALREADY persisted memory during this
|
|
1272
|
-
// turn (gateway mama_save in the reasoning header), the extractor safety
|
|
1273
|
-
// net would save the same instruction twice (proven live 2026-07-17:
|
|
1274
|
-
// decision_* + mem_* duplicates for one directive). The in-turn save is
|
|
1275
|
-
// the agent's judgment; the net exists for turns where it did not act.
|
|
1276
|
-
const agentSavedThisTurn = agentSavedInTurn(response);
|
|
1277
|
-
if (agentSavedThisTurn) {
|
|
1278
|
-
logger.info('[memory-agent] extraction skipped - agent saved in-turn (dual-save dedup)');
|
|
1279
|
-
}
|
|
1280
|
-
if (!isPublicLane && response && message.text && !agentSavedThisTurn) {
|
|
1281
|
-
const rawAssistantText = stripGatewayDecorations(response);
|
|
1282
|
-
void (async () => {
|
|
1283
|
-
try {
|
|
1284
|
-
await this.triggerMemoryAgent(channelKey, message.text, rawAssistantText, message, sourceTurnId, sourceMessageRef, parentModelRunId);
|
|
1285
|
-
}
|
|
1286
|
-
catch {
|
|
1287
|
-
/* non-fatal */
|
|
1288
|
-
}
|
|
1289
|
-
})();
|
|
1290
|
-
}
|
|
1291
|
-
if (shouldResume && pendingNotices) {
|
|
1205
|
+
if (usesContinuationContext && pendingNotices) {
|
|
1292
1206
|
// Drain each queue by ITS OWN peeked count - notices enqueued while
|
|
1293
1207
|
// the agent run was in flight stay for the next turn (review N2).
|
|
1294
1208
|
if (pendingChannelNoticeCount > 0) {
|
|
@@ -1312,7 +1226,7 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1312
1226
|
normalizedErrorMsg.includes('exited with code');
|
|
1313
1227
|
if (isCriticalError) {
|
|
1314
1228
|
logger.warn(`CLI error detected, invalidating session: ${errorMsg}`);
|
|
1315
|
-
sessionPool.invalidateSession(
|
|
1229
|
+
sessionPool.invalidateSession(runtimeSessionKey, cliSessionId);
|
|
1316
1230
|
}
|
|
1317
1231
|
// Release session lock before re-throwing
|
|
1318
1232
|
releaseCliSessionLock();
|
|
@@ -1358,36 +1272,14 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1358
1272
|
});
|
|
1359
1273
|
}
|
|
1360
1274
|
// 6. Update session context — finalize assistant response
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
this.sessionStore.finalizeTurnWithReportReceipt({
|
|
1367
|
-
sessionId: session.id,
|
|
1368
|
-
sourceMessageRef,
|
|
1369
|
-
finalResponse: response,
|
|
1370
|
-
committedAtIso: new Date().toISOString(),
|
|
1371
|
-
receipt: {
|
|
1372
|
-
deliveryIds: ownerReportSnapshot.deliveryIds,
|
|
1373
|
-
projectionVersion: ownerReportSnapshot.version,
|
|
1374
|
-
projectionText: ownerReportSnapshot.text,
|
|
1375
|
-
projectionHash: ownerReportSnapshot.projectionHash,
|
|
1376
|
-
},
|
|
1275
|
+
const persisted = this.sessionStore.flushStreamingResponse(session.id, response) ||
|
|
1276
|
+
this.sessionStore.appendMessage(session.id, {
|
|
1277
|
+
role: 'assistant',
|
|
1278
|
+
content: response,
|
|
1279
|
+
timestamp: Date.now(),
|
|
1377
1280
|
});
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
// Use flushStreamingResponse first (updates existing turn from periodic flush),
|
|
1381
|
-
// fall back to appendMessage if no turn exists yet (non-streaming path)
|
|
1382
|
-
const persisted = this.sessionStore.flushStreamingResponse(session.id, response) ||
|
|
1383
|
-
this.sessionStore.appendMessage(session.id, {
|
|
1384
|
-
role: 'assistant',
|
|
1385
|
-
content: response,
|
|
1386
|
-
timestamp: Date.now(),
|
|
1387
|
-
});
|
|
1388
|
-
if (!persisted) {
|
|
1389
|
-
throw new Error('Unable to persist final assistant response');
|
|
1390
|
-
}
|
|
1281
|
+
if (!persisted) {
|
|
1282
|
+
throw new Error('Unable to persist final assistant response');
|
|
1391
1283
|
}
|
|
1392
1284
|
if ((message.principal?.lane ?? 'owner') !== 'public' &&
|
|
1393
1285
|
response &&
|
|
@@ -1400,19 +1292,6 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1400
1292
|
logger.warn(`[learning] observer failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1401
1293
|
});
|
|
1402
1294
|
}
|
|
1403
|
-
if (reportCarryPeek && reportCarryTarget && reportCarryChannelKey) {
|
|
1404
|
-
try {
|
|
1405
|
-
this.reportCarry?.acknowledge({
|
|
1406
|
-
deliveryId: reportCarryPeek.deliveryId,
|
|
1407
|
-
target: reportCarryTarget,
|
|
1408
|
-
consumingChannelKey: reportCarryChannelKey,
|
|
1409
|
-
consumedAtIso: new Date().toISOString(),
|
|
1410
|
-
});
|
|
1411
|
-
}
|
|
1412
|
-
catch (error) {
|
|
1413
|
-
logger.warn(`[report-carry] acknowledgement failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
1295
|
// Release session lock AFTER final persistence to prevent out-of-order turns
|
|
1417
1296
|
releaseCliSessionLock();
|
|
1418
1297
|
// 6. Return result. Run identity rides out with it: the router already generated
|
|
@@ -1429,6 +1308,14 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1429
1308
|
: { status: 'unavailable', reason: completedProvenanceReason },
|
|
1430
1309
|
sourceTurnId,
|
|
1431
1310
|
sourceMessageRef,
|
|
1311
|
+
...(completedOwnerJournalProvenance
|
|
1312
|
+
? {
|
|
1313
|
+
recoveryProvenance: {
|
|
1314
|
+
status: 'unavailable',
|
|
1315
|
+
reason: 'journal_commit_failed',
|
|
1316
|
+
},
|
|
1317
|
+
}
|
|
1318
|
+
: {}),
|
|
1432
1319
|
};
|
|
1433
1320
|
}
|
|
1434
1321
|
finally {
|
|
@@ -1572,22 +1459,6 @@ ${historyContext}
|
|
|
1572
1459
|
}
|
|
1573
1460
|
return this.projectPrivatePromptText(prompt);
|
|
1574
1461
|
}
|
|
1575
|
-
/** TG-05 Decision 7: exact receipt history travels as a separate budget layer. */
|
|
1576
|
-
buildOwnerReportHistoryPrompt(session) {
|
|
1577
|
-
if (!this.ownerReportInbox || session.source !== 'telegram') {
|
|
1578
|
-
return '';
|
|
1579
|
-
}
|
|
1580
|
-
const restoredTurns = this.sessionStore
|
|
1581
|
-
.getHistory(session.id)
|
|
1582
|
-
.filter((turn) => turn.state !== 'provisional')
|
|
1583
|
-
.slice(-5);
|
|
1584
|
-
const reportHistory = this.ownerReportInbox.historyBlock({ source: 'telegram', channelId: session.channelId }, restoredTurns);
|
|
1585
|
-
if (!reportHistory) {
|
|
1586
|
-
return '';
|
|
1587
|
-
}
|
|
1588
|
-
logger.info(`Prepared ${reportHistory.length} chars of owner-report history (new session)`);
|
|
1589
|
-
return `## Previously Consumed Owner Reports (reference only)\n${reportHistory}`;
|
|
1590
|
-
}
|
|
1591
1462
|
buildSessionPolicyFingerprint(agentContext, enhanced, model, trelloAvailable) {
|
|
1592
1463
|
const baseInstructions = (0, agent_loop_js_1.loadComposedSystemPrompt)(false, agentContext);
|
|
1593
1464
|
const surface = (0, private_connector_policy_js_1.resolvePrivatePrincipalSurface)({ agentContext });
|
|
@@ -1761,145 +1632,9 @@ ${historyContext}
|
|
|
1761
1632
|
updateChannelName(source, channelId, channelName) {
|
|
1762
1633
|
return this.sessionStore.updateChannelName(source, channelId, channelName);
|
|
1763
1634
|
}
|
|
1764
|
-
/**
|
|
1765
|
-
* Trigger memory agent to extract facts (fire-and-forget).
|
|
1766
|
-
* Uses AgentProcessManager persistent process.
|
|
1767
|
-
*/
|
|
1768
|
-
static EXTRACT_COOLDOWN_MS = 30_000;
|
|
1769
|
-
static MIN_CONTENT_LENGTH = 100;
|
|
1770
|
-
static MAX_CONTENT_LENGTH = 10_000;
|
|
1771
1635
|
getRuntimeProjectId() {
|
|
1772
1636
|
return process.env.MAMA_WORKSPACE || process.cwd();
|
|
1773
1637
|
}
|
|
1774
|
-
buildMemoryAuditPrompt(job) {
|
|
1775
|
-
const escapeBackticks = (s) => s.replace(/```/g, '\\`\\`\\`');
|
|
1776
|
-
const scopeContext = job.scopeContext.map((scope) => `${scope.kind}:${scope.id}`).join(', ');
|
|
1777
|
-
const candidateLines = (job.candidates ?? []).map((candidate) => {
|
|
1778
|
-
const topic = candidate.topicHint ? ` topic=${escapeBackticks(candidate.topicHint)}` : '';
|
|
1779
|
-
return `- kind=${candidate.kind}${topic} confidence=${candidate.confidence} summary=${JSON.stringify(candidate.summary)}`;
|
|
1780
|
-
});
|
|
1781
|
-
const safeConversation = escapeBackticks(job.conversation);
|
|
1782
|
-
return `Memory scopes: ${scopeContext}
|
|
1783
|
-
Conversation:
|
|
1784
|
-
\`\`\`conversation
|
|
1785
|
-
${safeConversation}
|
|
1786
|
-
\`\`\`
|
|
1787
|
-
|
|
1788
|
-
Candidates:
|
|
1789
|
-
${candidateLines.length > 0 ? candidateLines.join('\n') : '- none'}
|
|
1790
|
-
|
|
1791
|
-
INSTRUCTION:
|
|
1792
|
-
- Call mama_search exactly once first.
|
|
1793
|
-
- If the conversation contains a decision, preference, fact, lesson, or superseding update, call mama_save exactly once.
|
|
1794
|
-
- If nothing should be saved, do not call mama_save.
|
|
1795
|
-
- Do not call resource discovery tools.
|
|
1796
|
-
- Do not ask follow-up questions.
|
|
1797
|
-
- After tool work finishes, respond with exactly DONE or SKIP.`;
|
|
1798
|
-
}
|
|
1799
|
-
classifyMemoryAuditResponse(response) {
|
|
1800
|
-
const normalized = response.trim().toLowerCase();
|
|
1801
|
-
if (normalized.length === 0) {
|
|
1802
|
-
return {
|
|
1803
|
-
status: 'skipped',
|
|
1804
|
-
action: 'no_op',
|
|
1805
|
-
event_ids: [],
|
|
1806
|
-
reason: 'memory agent returned an empty response',
|
|
1807
|
-
};
|
|
1808
|
-
}
|
|
1809
|
-
if (normalized.includes('nothing worth saving') ||
|
|
1810
|
-
normalized.includes('nothing is worth saving') ||
|
|
1811
|
-
normalized.includes('no-op') ||
|
|
1812
|
-
normalized.includes('no op') ||
|
|
1813
|
-
normalized.includes('skip')) {
|
|
1814
|
-
return {
|
|
1815
|
-
status: 'skipped',
|
|
1816
|
-
action: 'no_op',
|
|
1817
|
-
event_ids: [],
|
|
1818
|
-
reason: response,
|
|
1819
|
-
};
|
|
1820
|
-
}
|
|
1821
|
-
if (normalized === 'failed' || normalized.includes('failed')) {
|
|
1822
|
-
return {
|
|
1823
|
-
status: 'failed',
|
|
1824
|
-
action: 'no_op',
|
|
1825
|
-
event_ids: [],
|
|
1826
|
-
reason: response,
|
|
1827
|
-
};
|
|
1828
|
-
}
|
|
1829
|
-
if (normalized === 'done' || normalized === 'applied') {
|
|
1830
|
-
return {
|
|
1831
|
-
status: 'applied',
|
|
1832
|
-
action: 'save',
|
|
1833
|
-
event_ids: [],
|
|
1834
|
-
reason: response,
|
|
1835
|
-
};
|
|
1836
|
-
}
|
|
1837
|
-
return {
|
|
1838
|
-
status: 'skipped',
|
|
1839
|
-
action: 'no_op',
|
|
1840
|
-
event_ids: [],
|
|
1841
|
-
reason: response,
|
|
1842
|
-
};
|
|
1843
|
-
}
|
|
1844
|
-
recordMemoryAuditAck(ack, topic, channelKey, displayTopic, deltaKey) {
|
|
1845
|
-
const timestamp = Date.now();
|
|
1846
|
-
if (ack.status === 'applied') {
|
|
1847
|
-
this.memoryAgentStats.acksApplied++;
|
|
1848
|
-
this.memoryAgentStats.factsSaved++;
|
|
1849
|
-
// Fire-and-forget save confirmation to originating channel
|
|
1850
|
-
if (this.gatewayRegistry && channelKey) {
|
|
1851
|
-
const [source, ...channelParts] = channelKey.split(':');
|
|
1852
|
-
const channelId = channelParts.join(':');
|
|
1853
|
-
const confirmMsg = `✅ Memory saved: ${displayTopic}`;
|
|
1854
|
-
this.gatewayRegistry.sendMessage(source, channelId, confirmMsg).catch((err) => {
|
|
1855
|
-
logger.warn(`[memory-feedback] Failed to send confirmation: ${err instanceof Error ? err.message : String(err)}`);
|
|
1856
|
-
});
|
|
1857
|
-
}
|
|
1858
|
-
}
|
|
1859
|
-
else if (ack.status === 'skipped') {
|
|
1860
|
-
this.memoryAgentStats.acksSkipped++;
|
|
1861
|
-
}
|
|
1862
|
-
else {
|
|
1863
|
-
this.memoryAgentStats.acksFailed++;
|
|
1864
|
-
logger.warn(`[memory-agent] Audit failed: ${ack.reason ?? 'unknown failure'}`);
|
|
1865
|
-
this.memoryNoticeQueue.enqueue(channelKey ?? 'memory-agent:shared', {
|
|
1866
|
-
type: 'memory_warning',
|
|
1867
|
-
severity: 'high',
|
|
1868
|
-
summary: 'memory audit failed',
|
|
1869
|
-
evidence: [],
|
|
1870
|
-
recommended_action: 'consult_memory',
|
|
1871
|
-
relevant_memories: [],
|
|
1872
|
-
});
|
|
1873
|
-
}
|
|
1874
|
-
this.memoryAgentStats.recentExtractions.unshift({
|
|
1875
|
-
topic,
|
|
1876
|
-
timestamp,
|
|
1877
|
-
channelKey,
|
|
1878
|
-
status: ack.status,
|
|
1879
|
-
});
|
|
1880
|
-
this.memoryAgentStats.recentExtractions = this.memoryAgentStats.recentExtractions.slice(0, 10);
|
|
1881
|
-
this.memoryAgentStats.lastExtraction = timestamp;
|
|
1882
|
-
if (ack.status === 'applied' && channelKey && this.mamaApi.upsertChannelSummary) {
|
|
1883
|
-
const summaryMarkdown = [
|
|
1884
|
-
'## Channel Summary',
|
|
1885
|
-
`- Last memory update: ${displayTopic ?? topic}`,
|
|
1886
|
-
`- Status: ${ack.action}`,
|
|
1887
|
-
ack.reason ? `- Notes: ${ack.reason.slice(0, 240)}` : '',
|
|
1888
|
-
]
|
|
1889
|
-
.filter(Boolean)
|
|
1890
|
-
.join('\n');
|
|
1891
|
-
void this.mamaApi
|
|
1892
|
-
.upsertChannelSummary({
|
|
1893
|
-
channelKey,
|
|
1894
|
-
summaryMarkdown,
|
|
1895
|
-
deltaHash: `${deltaKey ?? topic}:${ack.action}`,
|
|
1896
|
-
})
|
|
1897
|
-
.catch((error) => {
|
|
1898
|
-
logger.warn(`[channel-summary] Failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1899
|
-
});
|
|
1900
|
-
}
|
|
1901
|
-
}
|
|
1902
|
-
/** Classify a committed owner message and save a policy:/lesson: row when it carries one. */
|
|
1903
1638
|
async observeOwnerLearning(message) {
|
|
1904
1639
|
if (!this.mamaApi.saveMemory || !this.mamaApi.queryRelevantTruth) {
|
|
1905
1640
|
return;
|
|
@@ -1992,131 +1727,6 @@ INSTRUCTION:
|
|
|
1992
1727
|
}
|
|
1993
1728
|
return `[MAMA Memory]\n${context.prompt}\n[/MAMA Memory]\n\n`;
|
|
1994
1729
|
}
|
|
1995
|
-
async triggerMemoryAgent(channelKey, userText, botResponse, message, sourceTurnId, _sourceMessageRef, parentModelRunId) {
|
|
1996
|
-
const memoryAuditQueue = this.memoryAuditQueue;
|
|
1997
|
-
if (!this.memoryAgentProcessManager || !memoryAuditQueue) {
|
|
1998
|
-
return;
|
|
1999
|
-
}
|
|
2000
|
-
const now = Date.now();
|
|
2001
|
-
const source = message?.source ?? 'memory-agent';
|
|
2002
|
-
const channelId = message?.channelId ?? 'shared';
|
|
2003
|
-
const userId = message?.userId;
|
|
2004
|
-
const candidates = (0, save_candidate_extractor_js_1.extractSaveCandidates)({
|
|
2005
|
-
userText,
|
|
2006
|
-
gatewayWrapped: message?.metadata?.untrustedWrapped === true,
|
|
2007
|
-
botResponse,
|
|
2008
|
-
channelKey,
|
|
2009
|
-
source,
|
|
2010
|
-
channelId,
|
|
2011
|
-
userId,
|
|
2012
|
-
projectId: this.getRuntimeProjectId(),
|
|
2013
|
-
createdAt: now,
|
|
2014
|
-
});
|
|
2015
|
-
const cooldownKey = `${source}:${channelId}:${userId ?? 'anonymous'}`;
|
|
2016
|
-
const lastExtractTime = this.memoryAuditCooldowns.get(cooldownKey) ?? 0;
|
|
2017
|
-
// The cooldown rate-limits memory-agent invocations for ORDINARY chatter.
|
|
2018
|
-
// Candidate-bearing turns are owner directives/decisions - discarding one
|
|
2019
|
-
// because another arrived within 30s loses real instructions, so they
|
|
2020
|
-
// bypass the cooldown (bounded by how fast an owner can type directives).
|
|
2021
|
-
if (candidates.length === 0 && now - lastExtractTime < MessageRouter.EXTRACT_COOLDOWN_MS) {
|
|
2022
|
-
return;
|
|
2023
|
-
}
|
|
2024
|
-
let content = `User: ${userText}\nAssistant: ${botResponse}`;
|
|
2025
|
-
if (content.length < MessageRouter.MIN_CONTENT_LENGTH && candidates.length === 0) {
|
|
2026
|
-
return;
|
|
2027
|
-
}
|
|
2028
|
-
if (content.length > MessageRouter.MAX_CONTENT_LENGTH) {
|
|
2029
|
-
content = content.substring(0, MessageRouter.MAX_CONTENT_LENGTH);
|
|
2030
|
-
}
|
|
2031
|
-
if (candidates.length === 0)
|
|
2032
|
-
return;
|
|
2033
|
-
this.memoryAuditCooldowns.set(cooldownKey, now);
|
|
2034
|
-
// Evict stale cooldown entries (older than 2x cooldown window)
|
|
2035
|
-
if (this.memoryAuditCooldowns.size > 100) {
|
|
2036
|
-
const staleThreshold = now - MessageRouter.EXTRACT_COOLDOWN_MS * 2;
|
|
2037
|
-
for (const [key, ts] of this.memoryAuditCooldowns) {
|
|
2038
|
-
if (ts < staleThreshold)
|
|
2039
|
-
this.memoryAuditCooldowns.delete(key);
|
|
2040
|
-
}
|
|
2041
|
-
}
|
|
2042
|
-
this.memoryAgentStats.turnsObserved++;
|
|
2043
|
-
this.memoryAgentStats.candidatesDetected += candidates.length;
|
|
2044
|
-
this.memoryAgentStats.factsExtracted += candidates.length;
|
|
2045
|
-
const scopes = (0, scope_context_js_1.deriveMemoryScopes)({
|
|
2046
|
-
source,
|
|
2047
|
-
channelId,
|
|
2048
|
-
userId,
|
|
2049
|
-
projectId: this.getRuntimeProjectId(),
|
|
2050
|
-
});
|
|
2051
|
-
const job = {
|
|
2052
|
-
turnId: sourceTurnId ?? `turn_${now}`,
|
|
2053
|
-
channelKey,
|
|
2054
|
-
source,
|
|
2055
|
-
channelId,
|
|
2056
|
-
userId,
|
|
2057
|
-
parentModelRunId,
|
|
2058
|
-
scopeContext: scopes,
|
|
2059
|
-
conversation: content,
|
|
2060
|
-
candidates,
|
|
2061
|
-
};
|
|
2062
|
-
const rawTopic = userText.slice(0, 40).toLowerCase();
|
|
2063
|
-
const asciiTopic = rawTopic.replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '');
|
|
2064
|
-
const topic = asciiTopic || `topic_${(0, node_crypto_1.createHash)('sha256').update(rawTopic).digest('hex').slice(0, 8)}`;
|
|
2065
|
-
const displayTopic = candidates[0]?.topicHint || userText.slice(0, 80).trim() || 'memory_audit';
|
|
2066
|
-
const deltaKeySource = candidates[0]?.id || userText;
|
|
2067
|
-
const deltaKey = (0, node_crypto_1.createHash)('sha256').update(deltaKeySource).digest('hex').slice(0, 16);
|
|
2068
|
-
const memoryStart = Date.now();
|
|
2069
|
-
// Start validation session for memory agent
|
|
2070
|
-
let memValSession = null;
|
|
2071
|
-
if (this.validationService && this.sessionsDb) {
|
|
2072
|
-
try {
|
|
2073
|
-
const ver = (0, agent_store_js_1.getLatestVersion)(this.sessionsDb, 'memory');
|
|
2074
|
-
memValSession = this.validationService.startSession('memory', ver?.version ?? 0, 'audit', {
|
|
2075
|
-
goal: `Extract: ${displayTopic.slice(0, 100)}`,
|
|
2076
|
-
});
|
|
2077
|
-
}
|
|
2078
|
-
catch {
|
|
2079
|
-
/* non-fatal */
|
|
2080
|
-
}
|
|
2081
|
-
}
|
|
2082
|
-
void (async () => {
|
|
2083
|
-
try {
|
|
2084
|
-
const ack = await memoryAuditQueue.enqueue(job);
|
|
2085
|
-
this.recordMemoryAuditAck(ack, topic, channelKey, displayTopic, deltaKey);
|
|
2086
|
-
const dur = Date.now() - memoryStart;
|
|
2087
|
-
this.logAgentActivity('memory', 'task_complete', displayTopic.slice(0, 200), undefined, dur);
|
|
2088
|
-
if (memValSession && this.validationService) {
|
|
2089
|
-
try {
|
|
2090
|
-
this.validationService.finalizeSession(memValSession.id, {
|
|
2091
|
-
execution_status: 'completed',
|
|
2092
|
-
metrics: { duration_ms: dur },
|
|
2093
|
-
});
|
|
2094
|
-
}
|
|
2095
|
-
catch {
|
|
2096
|
-
/* non-fatal */
|
|
2097
|
-
}
|
|
2098
|
-
}
|
|
2099
|
-
}
|
|
2100
|
-
catch (err) {
|
|
2101
|
-
this.memoryAgentStats.acksFailed++;
|
|
2102
|
-
const dur = Date.now() - memoryStart;
|
|
2103
|
-
this.logAgentActivity('memory', 'task_error', displayTopic.slice(0, 200), undefined, dur, err instanceof Error ? err.message : String(err));
|
|
2104
|
-
if (memValSession && this.validationService) {
|
|
2105
|
-
try {
|
|
2106
|
-
this.validationService.finalizeSession(memValSession.id, {
|
|
2107
|
-
execution_status: 'failed',
|
|
2108
|
-
error_message: err instanceof Error ? err.message : String(err),
|
|
2109
|
-
metrics: { duration_ms: dur },
|
|
2110
|
-
});
|
|
2111
|
-
}
|
|
2112
|
-
catch {
|
|
2113
|
-
/* non-fatal */
|
|
2114
|
-
}
|
|
2115
|
-
}
|
|
2116
|
-
logger.warn(`[memory-agent] Failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2117
|
-
}
|
|
2118
|
-
})();
|
|
2119
|
-
}
|
|
2120
1730
|
// ── Activity Logging (shared by MAMA frontdoor + memory agent) ──────
|
|
2121
1731
|
resolveFrontdoorAgentId(message) {
|
|
2122
1732
|
return message.source === 'viewer' ? 'os-agent' : 'mama';
|