@jungjaehoon/mama-os 0.48.4 → 0.49.1
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 +106 -80
- 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 +7 -9
- 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 +1 -2
- 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 +27 -8
- package/dist/agent/tool-registry.js.map +1 -1
- package/dist/agent/types.d.ts +20 -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 +188 -189
- 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 +31 -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 +15 -71
- package/dist/gateways/message-router.d.ts.map +1 -1
- package/dist/gateways/message-router.js +124 -493
- 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 +9 -8
- package/dist/operator/owner-event-loop.d.ts.map +1 -1
- package/dist/operator/owner-event-loop.js +8 -22
- 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 +28 -37
- package/dist/operator/situation-report.js.map +1 -1
- package/dist/operator/task-ledger.d.ts +8 -1
- package/dist/operator/task-ledger.d.ts.map +1 -1
- package/dist/operator/task-ledger.js +51 -16
- package/dist/operator/task-ledger.js.map +1 -1
- package/dist/operator/task-list-views.d.ts.map +1 -1
- package/dist/operator/task-list-views.js +45 -24
- package/dist/operator/task-list-views.js.map +1 -1
- package/dist/operator/task-temporal.d.ts +3 -0
- package/dist/operator/task-temporal.d.ts.map +1 -1
- package/dist/operator/task-temporal.js +12 -0
- package/dist/operator/task-temporal.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,27 @@ 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
|
|
807
|
+
const runtimeOwnsSession = runtimeSessionKey === owner_runtime_js_1.OWNER_RUNTIME_SESSION_KEY && this.agentLoop.managesOwnerQueue === true;
|
|
808
|
+
const ownerSession = runtimeOwnsSession
|
|
809
|
+
? sessionPool.peekSession(runtimeSessionKey)
|
|
810
|
+
: undefined;
|
|
811
|
+
const initialSession = runtimeOwnsSession
|
|
812
|
+
? {
|
|
813
|
+
sessionId: ownerSession?.sessionId ?? session.id,
|
|
814
|
+
isNew: !ownerSession?.sessionId,
|
|
815
|
+
busy: false,
|
|
816
|
+
}
|
|
817
|
+
: sessionPool.getSession(runtimeSessionKey);
|
|
818
|
+
if (ownerSession?.busy)
|
|
819
|
+
processOptions?.onQueued?.();
|
|
860
820
|
let cliSessionId = initialSession.sessionId;
|
|
861
821
|
let isNewCliSession = initialSession.isNew;
|
|
862
822
|
const busy = initialSession.busy;
|
|
@@ -864,12 +824,13 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
864
824
|
// assignment sits inside the agent-run block and is not visible at the return.
|
|
865
825
|
let completedModelRunId = null;
|
|
866
826
|
let completedProvenanceReason = 'backend_no_run';
|
|
827
|
+
let completedOwnerJournalProvenance;
|
|
867
828
|
const sourceTurnId = message.metadata?.messageId ?? `generated:${(0, node_crypto_1.randomUUID)().replace(/-/g, '')}`;
|
|
868
829
|
const sourceMessageRef = [message.source, message.channelId, sourceTurnId]
|
|
869
830
|
.filter(Boolean)
|
|
870
831
|
.join(':');
|
|
871
832
|
// Track lock ownership for proper cleanup in finally block
|
|
872
|
-
let acquiredLock = !
|
|
833
|
+
let acquiredLock = !runtimeOwnsSession && !busy;
|
|
873
834
|
// If session is busy, notify caller immediately and wait for it to be released
|
|
874
835
|
if (busy) {
|
|
875
836
|
logger.debug(`Session busy for ${channelKey}, notifying client`);
|
|
@@ -882,7 +843,7 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
882
843
|
while (Date.now() - waitStart < maxWaitMs) {
|
|
883
844
|
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
884
845
|
// Use read-only peek to avoid side effects (no lock increment)
|
|
885
|
-
const check = sessionPool.peekSession(
|
|
846
|
+
const check = sessionPool.peekSession(runtimeSessionKey);
|
|
886
847
|
if (!check.busy) {
|
|
887
848
|
logger.debug(`Session released for ${channelKey} after ${Math.round((Date.now() - waitStart) / 1000)}s`);
|
|
888
849
|
break;
|
|
@@ -894,7 +855,7 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
894
855
|
}
|
|
895
856
|
}
|
|
896
857
|
// Re-acquire session after wait (properly locks it this time)
|
|
897
|
-
const reacquired = sessionPool.getSession(
|
|
858
|
+
const reacquired = sessionPool.getSession(runtimeSessionKey);
|
|
898
859
|
if (reacquired.busy) {
|
|
899
860
|
// Still busy after timeout - we never acquired the lock
|
|
900
861
|
throw new Error(`Session for ${channelKey} timed out after ${maxWaitMs / 1000}s`);
|
|
@@ -907,35 +868,17 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
907
868
|
const releaseCliSessionLock = () => {
|
|
908
869
|
if (!acquiredLock || lockReleased)
|
|
909
870
|
return;
|
|
910
|
-
sessionPool.releaseSession(
|
|
871
|
+
sessionPool.releaseSession(runtimeSessionKey, cliSessionId);
|
|
911
872
|
lockReleased = true;
|
|
912
873
|
};
|
|
913
874
|
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
875
|
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.
|
|
876
|
+
// Save user message immediately for crash/refresh resilience.
|
|
934
877
|
this.sessionStore.appendMessage(session.id, {
|
|
935
878
|
role: 'user',
|
|
936
879
|
content: [message.text, mediaInstructions].filter(Boolean).join('\n\n'),
|
|
937
880
|
timestamp: Date.now(),
|
|
938
|
-
}
|
|
881
|
+
});
|
|
939
882
|
let response = '';
|
|
940
883
|
let context = { prompt: '', decisions: [], hasContext: false };
|
|
941
884
|
let streamFlushTimer = null;
|
|
@@ -948,7 +891,6 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
948
891
|
// CONTINUE turns: Codex server retains full conversation via threadId,
|
|
949
892
|
// so skip expensive prompt rebuilding (embedding search, DB history, etc.)
|
|
950
893
|
let systemPrompt;
|
|
951
|
-
let ownerReportHistoryPrompt = '';
|
|
952
894
|
const historyContext = message.metadata?.historyContext;
|
|
953
895
|
// Public turns never inspect private workspace rules, skills, or AGENTS files.
|
|
954
896
|
const enhanced = isPublicLane
|
|
@@ -957,7 +899,13 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
957
899
|
// TG-05: every main backend keeps its live conversation in a durable
|
|
958
900
|
// runtime session. Only a genuinely new/replacement session rebuilds
|
|
959
901
|
// bounded MAMA context.
|
|
960
|
-
|
|
902
|
+
// The in-memory SessionPool is empty after a daemon restart, while a
|
|
903
|
+
// durable Codex thread may still be alive. Codex first attempts that
|
|
904
|
+
// thread with a minimal continuation; AgentLoop invokes the lazy full
|
|
905
|
+
// builder only when the registry says the thread is actually missing.
|
|
906
|
+
const lazyDurableResume = runtimeOwnsSession ||
|
|
907
|
+
(this.config.backend === 'codex' && this.agentLoop.probesDurableSession === true);
|
|
908
|
+
const needsFullContext = isNewCliSession && !lazyDurableResume;
|
|
961
909
|
// Persistent backends skip repeated retrieval after the first turn.
|
|
962
910
|
context =
|
|
963
911
|
!isPublicLane && needsFullContext && this.config.implicitLegacyContextSearch
|
|
@@ -982,8 +930,8 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
982
930
|
source: message.source,
|
|
983
931
|
channelId: message.channelId,
|
|
984
932
|
});
|
|
985
|
-
systemPrompt = this.buildSystemPrompt(session, context.prompt, historyContext, sessionStartupContext, agentContext, enhanced,
|
|
986
|
-
|
|
933
|
+
systemPrompt = this.buildSystemPrompt(session, context.prompt, historyContext, sessionStartupContext, agentContext, enhanced, !(runtimeSessionKey === owner_runtime_js_1.OWNER_RUNTIME_SESSION_KEY &&
|
|
934
|
+
this.agentLoop.ownerRecoveryJournalEnabled === true), trelloAvailable);
|
|
987
935
|
}
|
|
988
936
|
}
|
|
989
937
|
// 7. Run agent loop (with session info for lane-based concurrency)
|
|
@@ -1018,13 +966,14 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1018
966
|
// After a daemon restart the pool is new, but the durable Codex thread may
|
|
1019
967
|
// still exist and should be resumed. Keep prompt/context freshness separate
|
|
1020
968
|
// from backend conversation continuity.
|
|
1021
|
-
const shouldResumeBackend = this.config.backend === 'codex' ? true : shouldResume;
|
|
969
|
+
const shouldResumeBackend = runtimeOwnsSession || this.config.backend === 'codex' ? true : shouldResume;
|
|
970
|
+
const usesContinuationContext = shouldResume || lazyDurableResume;
|
|
1022
971
|
// For resumed sessions: inject minimal context only
|
|
1023
972
|
// Persistent CLI keeps the process alive with full system prompt from initial request
|
|
1024
973
|
// Only inject per-message context (related decisions) to avoid context overflow
|
|
1025
974
|
const effectivePrompt = isPublicLane
|
|
1026
975
|
? publicLaneBaseInstructions
|
|
1027
|
-
:
|
|
976
|
+
: usesContinuationContext
|
|
1028
977
|
? this.buildMinimalResumePrompt(context.prompt, agentContext)
|
|
1029
978
|
: systemPrompt;
|
|
1030
979
|
// Wrap stream callbacks to accumulate deltas and periodically flush to DB
|
|
@@ -1047,7 +996,6 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1047
996
|
}
|
|
1048
997
|
}, streamFlushIntervalMs);
|
|
1049
998
|
}
|
|
1050
|
-
let parentModelRunId;
|
|
1051
999
|
// Skill on-demand injection: prepend matched skill content to user message
|
|
1052
1000
|
// (not system prompt — PersistentCLI can't update system prompt after creation)
|
|
1053
1001
|
const skillPrefix = !isPublicLane && enhanced.skillContent
|
|
@@ -1067,22 +1015,28 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1067
1015
|
let pendingNotices = false;
|
|
1068
1016
|
let pendingChannelNoticeCount = 0;
|
|
1069
1017
|
let pendingBroadcastNoticeCount = 0;
|
|
1070
|
-
const envelope =
|
|
1018
|
+
const envelope = runtimeOwnsSession
|
|
1019
|
+
? undefined
|
|
1020
|
+
: this.buildReactiveEnvelope(message, agentContext, memberEffectiveScope);
|
|
1071
1021
|
const options = {
|
|
1072
1022
|
systemPrompt: effectivePrompt,
|
|
1073
|
-
...(ownerReportHistoryPrompt ? { ownerReportHistoryPrompt } : {}),
|
|
1074
1023
|
sessionPolicyFingerprint,
|
|
1075
1024
|
...(memberEffectiveScope ? { memberEffectiveScope } : {}),
|
|
1076
1025
|
...(memberEffectiveScope ? { memberScopeRequired: true } : {}),
|
|
1077
1026
|
userId: message.userId,
|
|
1078
1027
|
model: roleModel, // Role-specific model override
|
|
1079
1028
|
maxTurns: roleMaxTurns, // Role-specific max turns
|
|
1080
|
-
sessionKey:
|
|
1029
|
+
sessionKey: runtimeSessionKey,
|
|
1030
|
+
...(runtimeSessionKey === owner_runtime_js_1.OWNER_RUNTIME_SESSION_KEY ? { lanePriority: 100 } : {}),
|
|
1081
1031
|
source: message.source,
|
|
1082
1032
|
channelId: message.channelId,
|
|
1083
1033
|
agentContext,
|
|
1034
|
+
...(runtimeSessionKey === owner_runtime_js_1.OWNER_RUNTIME_SESSION_KEY
|
|
1035
|
+
? { sessionPolicyRole: ownerRuntimeRole }
|
|
1036
|
+
: {}),
|
|
1084
1037
|
resumeSession: shouldResumeBackend,
|
|
1085
|
-
|
|
1038
|
+
// Queued owner input must not claim a busy session or pin a stale pool id.
|
|
1039
|
+
...(runtimeOwnsSession ? {} : { cliSessionId }),
|
|
1086
1040
|
// AgentLoop may replace a stale backend session while this request still
|
|
1087
1041
|
// owns the channel lock. Keep cleanup guarded by the current owner ID.
|
|
1088
1042
|
onCliSessionReset: (sessionId) => {
|
|
@@ -1090,8 +1044,16 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1090
1044
|
},
|
|
1091
1045
|
streamCallbacks: wrappedOnStream || processOptions?.onStream,
|
|
1092
1046
|
envelope,
|
|
1047
|
+
...(runtimeOwnsSession
|
|
1048
|
+
? {
|
|
1049
|
+
prepareEnvelope: () => this.buildReactiveEnvelope(message, agentContext, memberEffectiveScope),
|
|
1050
|
+
}
|
|
1051
|
+
: {}),
|
|
1093
1052
|
sourceTurnId,
|
|
1094
1053
|
sourceMessageRef,
|
|
1054
|
+
...(runtimeSessionKey === owner_runtime_js_1.OWNER_RUNTIME_SESSION_KEY
|
|
1055
|
+
? { ownerJournalPrompt: message.text, ownerJournalTrust: 'owner' }
|
|
1056
|
+
: {}),
|
|
1095
1057
|
// The owner's message is what caused whatever this turn changes, and the router
|
|
1096
1058
|
// knows it BEFORE the agent runs - the same shape as a reconcile work order
|
|
1097
1059
|
// carrying its delta batch. Without this the chat lane had no cause wire at all:
|
|
@@ -1104,7 +1066,7 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1104
1066
|
// citation this ledger exists to refuse.
|
|
1105
1067
|
...(causeFromOwnerMessage(sourceTurnId, sourceMessageRef) ?? {}),
|
|
1106
1068
|
};
|
|
1107
|
-
if (
|
|
1069
|
+
if (shouldResumeBackend) {
|
|
1108
1070
|
options.freshSessionSystemPrompt = async () => {
|
|
1109
1071
|
if (isPublicLane) {
|
|
1110
1072
|
return memberEffectiveScope
|
|
@@ -1118,27 +1080,21 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1118
1080
|
source: message.source,
|
|
1119
1081
|
channelId: message.channelId,
|
|
1120
1082
|
});
|
|
1121
|
-
return this.buildSystemPrompt(session, freshContext.prompt, historyContext, sessionStartupContext, agentContext, enhanced,
|
|
1083
|
+
return this.buildSystemPrompt(session, freshContext.prompt, historyContext, sessionStartupContext, agentContext, enhanced, !(runtimeSessionKey === owner_runtime_js_1.OWNER_RUNTIME_SESSION_KEY &&
|
|
1084
|
+
this.agentLoop.ownerRecoveryJournalEnabled === true), trelloAvailable);
|
|
1122
1085
|
};
|
|
1123
|
-
if (!isPublicLane) {
|
|
1124
|
-
options.freshSessionOwnerReportHistoryPrompt = () => this.buildOwnerReportHistoryPrompt(session);
|
|
1125
|
-
}
|
|
1126
1086
|
}
|
|
1127
|
-
if (
|
|
1087
|
+
if (usesContinuationContext) {
|
|
1128
1088
|
logger.info(`Resuming CLI session (minimal: ${effectivePrompt.length} chars)`);
|
|
1129
1089
|
}
|
|
1130
1090
|
else {
|
|
1131
1091
|
logger.info(`New CLI session (full: ${systemPrompt.length} chars)`);
|
|
1132
1092
|
}
|
|
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
1093
|
try {
|
|
1138
1094
|
if (isPublicLane) {
|
|
1139
1095
|
memoryPrefix = '';
|
|
1140
1096
|
}
|
|
1141
|
-
else if (
|
|
1097
|
+
else if (usesContinuationContext) {
|
|
1142
1098
|
// Per-channel notices PLUS the operator broadcast key: host-code
|
|
1143
1099
|
// alarms (workorder failures) have no conversation channel at
|
|
1144
1100
|
// enqueue time - without the broadcast read they dead-letter
|
|
@@ -1166,7 +1122,7 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1166
1122
|
}
|
|
1167
1123
|
catch (err) {
|
|
1168
1124
|
logger.warn(`[memory-prefix] Failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1169
|
-
if (!isPublicLane &&
|
|
1125
|
+
if (!isPublicLane && usesContinuationContext) {
|
|
1170
1126
|
try {
|
|
1171
1127
|
memoryPrefix = await this.getPerTurnMemoryPrefix(message);
|
|
1172
1128
|
}
|
|
@@ -1212,7 +1168,7 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1212
1168
|
}
|
|
1213
1169
|
// Add text content (with memory context, skill context, and page context)
|
|
1214
1170
|
const pageCtx = isPublicLane ? '' : this.getPageContextPrefix(message);
|
|
1215
|
-
const effectiveMessageText = `${pageCtx}${
|
|
1171
|
+
const effectiveMessageText = `${pageCtx}${memoryPrefix}${learningPrefix}${skillPrefix}${messageText || ''}`;
|
|
1216
1172
|
if (effectiveMessageText) {
|
|
1217
1173
|
contentBlocks.push({ type: 'text', text: effectiveMessageText });
|
|
1218
1174
|
}
|
|
@@ -1247,48 +1203,27 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1247
1203
|
const turnStart = Date.now();
|
|
1248
1204
|
const result = await this.agentLoop.runWithContent(contentBlocks, options);
|
|
1249
1205
|
response = result.response;
|
|
1250
|
-
parentModelRunId = result.modelRunId ?? undefined;
|
|
1251
1206
|
completedModelRunId = result.modelRunId ?? completedModelRunId;
|
|
1252
1207
|
if (result.modelRunProvenance === 'commit_failed') {
|
|
1253
1208
|
completedProvenanceReason = 'commit_failed';
|
|
1254
1209
|
}
|
|
1210
|
+
completedOwnerJournalProvenance = result.ownerJournalProvenance;
|
|
1255
1211
|
this.logFrontdoorActivity(message, message.text, response, Date.now() - turnStart);
|
|
1256
1212
|
}
|
|
1257
1213
|
else {
|
|
1258
1214
|
const pageCtx = isPublicLane ? '' : this.getPageContextPrefix(message);
|
|
1259
|
-
const effectiveText = `${pageCtx}${
|
|
1215
|
+
const effectiveText = `${pageCtx}${memoryPrefix}${learningPrefix}${skillPrefix}${message.text}`;
|
|
1260
1216
|
const turnStart = Date.now();
|
|
1261
1217
|
const result = await this.agentLoop.run(effectiveText, options);
|
|
1262
1218
|
response = result.response;
|
|
1263
|
-
parentModelRunId = result.modelRunId ?? undefined;
|
|
1264
1219
|
completedModelRunId = result.modelRunId ?? completedModelRunId;
|
|
1265
1220
|
if (result.modelRunProvenance === 'commit_failed') {
|
|
1266
1221
|
completedProvenanceReason = 'commit_failed';
|
|
1267
1222
|
}
|
|
1223
|
+
completedOwnerJournalProvenance = result.ownerJournalProvenance;
|
|
1268
1224
|
this.logFrontdoorActivity(message, message.text, response, Date.now() - turnStart);
|
|
1269
1225
|
}
|
|
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) {
|
|
1226
|
+
if (usesContinuationContext && pendingNotices) {
|
|
1292
1227
|
// Drain each queue by ITS OWN peeked count - notices enqueued while
|
|
1293
1228
|
// the agent run was in flight stay for the next turn (review N2).
|
|
1294
1229
|
if (pendingChannelNoticeCount > 0) {
|
|
@@ -1312,7 +1247,7 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1312
1247
|
normalizedErrorMsg.includes('exited with code');
|
|
1313
1248
|
if (isCriticalError) {
|
|
1314
1249
|
logger.warn(`CLI error detected, invalidating session: ${errorMsg}`);
|
|
1315
|
-
sessionPool.invalidateSession(
|
|
1250
|
+
sessionPool.invalidateSession(runtimeSessionKey, cliSessionId);
|
|
1316
1251
|
}
|
|
1317
1252
|
// Release session lock before re-throwing
|
|
1318
1253
|
releaseCliSessionLock();
|
|
@@ -1358,36 +1293,14 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1358
1293
|
});
|
|
1359
1294
|
}
|
|
1360
1295
|
// 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
|
-
},
|
|
1296
|
+
const persisted = this.sessionStore.flushStreamingResponse(session.id, response) ||
|
|
1297
|
+
this.sessionStore.appendMessage(session.id, {
|
|
1298
|
+
role: 'assistant',
|
|
1299
|
+
content: response,
|
|
1300
|
+
timestamp: Date.now(),
|
|
1377
1301
|
});
|
|
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
|
-
}
|
|
1302
|
+
if (!persisted) {
|
|
1303
|
+
throw new Error('Unable to persist final assistant response');
|
|
1391
1304
|
}
|
|
1392
1305
|
if ((message.principal?.lane ?? 'owner') !== 'public' &&
|
|
1393
1306
|
response &&
|
|
@@ -1400,19 +1313,6 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1400
1313
|
logger.warn(`[learning] observer failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1401
1314
|
});
|
|
1402
1315
|
}
|
|
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
1316
|
// Release session lock AFTER final persistence to prevent out-of-order turns
|
|
1417
1317
|
releaseCliSessionLock();
|
|
1418
1318
|
// 6. Return result. Run identity rides out with it: the router already generated
|
|
@@ -1429,6 +1329,14 @@ This protects your credentials from being exposed in chat logs.`;
|
|
|
1429
1329
|
: { status: 'unavailable', reason: completedProvenanceReason },
|
|
1430
1330
|
sourceTurnId,
|
|
1431
1331
|
sourceMessageRef,
|
|
1332
|
+
...(completedOwnerJournalProvenance
|
|
1333
|
+
? {
|
|
1334
|
+
recoveryProvenance: {
|
|
1335
|
+
status: 'unavailable',
|
|
1336
|
+
reason: 'journal_commit_failed',
|
|
1337
|
+
},
|
|
1338
|
+
}
|
|
1339
|
+
: {}),
|
|
1432
1340
|
};
|
|
1433
1341
|
}
|
|
1434
1342
|
finally {
|
|
@@ -1572,22 +1480,6 @@ ${historyContext}
|
|
|
1572
1480
|
}
|
|
1573
1481
|
return this.projectPrivatePromptText(prompt);
|
|
1574
1482
|
}
|
|
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
1483
|
buildSessionPolicyFingerprint(agentContext, enhanced, model, trelloAvailable) {
|
|
1592
1484
|
const baseInstructions = (0, agent_loop_js_1.loadComposedSystemPrompt)(false, agentContext);
|
|
1593
1485
|
const surface = (0, private_connector_policy_js_1.resolvePrivatePrincipalSurface)({ agentContext });
|
|
@@ -1761,145 +1653,9 @@ ${historyContext}
|
|
|
1761
1653
|
updateChannelName(source, channelId, channelName) {
|
|
1762
1654
|
return this.sessionStore.updateChannelName(source, channelId, channelName);
|
|
1763
1655
|
}
|
|
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
1656
|
getRuntimeProjectId() {
|
|
1772
1657
|
return process.env.MAMA_WORKSPACE || process.cwd();
|
|
1773
1658
|
}
|
|
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
1659
|
async observeOwnerLearning(message) {
|
|
1904
1660
|
if (!this.mamaApi.saveMemory || !this.mamaApi.queryRelevantTruth) {
|
|
1905
1661
|
return;
|
|
@@ -1992,131 +1748,6 @@ INSTRUCTION:
|
|
|
1992
1748
|
}
|
|
1993
1749
|
return `[MAMA Memory]\n${context.prompt}\n[/MAMA Memory]\n\n`;
|
|
1994
1750
|
}
|
|
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
1751
|
// ── Activity Logging (shared by MAMA frontdoor + memory agent) ──────
|
|
2121
1752
|
resolveFrontdoorAgentId(message) {
|
|
2122
1753
|
return message.source === 'viewer' ? 'os-agent' : 'mama';
|