@jungjaehoon/mama-os 0.51.2 → 0.52.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.
Files changed (204) hide show
  1. package/dist/agent/agent-loop.d.ts +50 -0
  2. package/dist/agent/agent-loop.d.ts.map +1 -1
  3. package/dist/agent/agent-loop.js +480 -138
  4. package/dist/agent/agent-loop.js.map +1 -1
  5. package/dist/agent/code-act/constants.d.ts +1 -1
  6. package/dist/agent/code-act/constants.d.ts.map +1 -1
  7. package/dist/agent/code-act/constants.js +5 -2
  8. package/dist/agent/code-act/constants.js.map +1 -1
  9. package/dist/agent/code-act/host-bridge.d.ts.map +1 -1
  10. package/dist/agent/code-act/host-bridge.js +102 -4
  11. package/dist/agent/code-act/host-bridge.js.map +1 -1
  12. package/dist/agent/code-act/tool-catalog.d.ts +2 -0
  13. package/dist/agent/code-act/tool-catalog.d.ts.map +1 -1
  14. package/dist/agent/code-act/tool-catalog.js +20 -7
  15. package/dist/agent/code-act/tool-catalog.js.map +1 -1
  16. package/dist/agent/codex-app-server-process.d.ts +112 -0
  17. package/dist/agent/codex-app-server-process.d.ts.map +1 -1
  18. package/dist/agent/codex-app-server-process.js +518 -1
  19. package/dist/agent/codex-app-server-process.js.map +1 -1
  20. package/dist/agent/gateway-tool-executor.d.ts +17 -0
  21. package/dist/agent/gateway-tool-executor.d.ts.map +1 -1
  22. package/dist/agent/gateway-tool-executor.js +236 -53
  23. package/dist/agent/gateway-tool-executor.js.map +1 -1
  24. package/dist/agent/gateway-tools.md +9 -3
  25. package/dist/agent/model-runner.d.ts +5 -0
  26. package/dist/agent/model-runner.d.ts.map +1 -1
  27. package/dist/agent/model-runner.js.map +1 -1
  28. package/dist/agent/skill-loader.d.ts +7 -2
  29. package/dist/agent/skill-loader.d.ts.map +1 -1
  30. package/dist/agent/skill-loader.js +29 -8
  31. package/dist/agent/skill-loader.js.map +1 -1
  32. package/dist/agent/subagent-run-reconcile.d.ts +23 -0
  33. package/dist/agent/subagent-run-reconcile.d.ts.map +1 -0
  34. package/dist/agent/subagent-run-reconcile.js +56 -0
  35. package/dist/agent/subagent-run-reconcile.js.map +1 -0
  36. package/dist/agent/tool-registry.d.ts.map +1 -1
  37. package/dist/agent/tool-registry.js +126 -3
  38. package/dist/agent/tool-registry.js.map +1 -1
  39. package/dist/agent/types.d.ts +52 -2
  40. package/dist/agent/types.d.ts.map +1 -1
  41. package/dist/agent/types.js.map +1 -1
  42. package/dist/api/graph-api.d.ts.map +1 -1
  43. package/dist/api/graph-api.js +0 -22
  44. package/dist/api/graph-api.js.map +1 -1
  45. package/dist/cli/commands/start.d.ts +8 -7
  46. package/dist/cli/commands/start.d.ts.map +1 -1
  47. package/dist/cli/commands/start.js +148 -74
  48. package/dist/cli/commands/start.js.map +1 -1
  49. package/dist/cli/config/types.d.ts.map +1 -1
  50. package/dist/cli/config/types.js +2 -1
  51. package/dist/cli/config/types.js.map +1 -1
  52. package/dist/cli/runtime/api-routes-init.d.ts +1 -0
  53. package/dist/cli/runtime/api-routes-init.d.ts.map +1 -1
  54. package/dist/cli/runtime/api-routes-init.js +76 -32
  55. package/dist/cli/runtime/api-routes-init.js.map +1 -1
  56. package/dist/db/migrations/operator-procedures.d.ts +4 -0
  57. package/dist/db/migrations/operator-procedures.d.ts.map +1 -0
  58. package/dist/db/migrations/operator-procedures.js +63 -0
  59. package/dist/db/migrations/operator-procedures.js.map +1 -0
  60. package/dist/gateways/message-router.d.ts +4 -3
  61. package/dist/gateways/message-router.d.ts.map +1 -1
  62. package/dist/gateways/message-router.js +43 -131
  63. package/dist/gateways/message-router.js.map +1 -1
  64. package/dist/gateways/telegram-format.d.ts.map +1 -1
  65. package/dist/gateways/telegram-format.js +2 -0
  66. package/dist/gateways/telegram-format.js.map +1 -1
  67. package/dist/multi-agent/runtime-process.d.ts +8 -0
  68. package/dist/multi-agent/runtime-process.d.ts.map +1 -1
  69. package/dist/multi-agent/runtime-process.js +6 -0
  70. package/dist/multi-agent/runtime-process.js.map +1 -1
  71. package/dist/operator/board-delta-gate.d.ts +34 -10
  72. package/dist/operator/board-delta-gate.d.ts.map +1 -1
  73. package/dist/operator/board-delta-gate.js +68 -24
  74. package/dist/operator/board-delta-gate.js.map +1 -1
  75. package/dist/operator/board-refresh-gate.d.ts +11 -6
  76. package/dist/operator/board-refresh-gate.d.ts.map +1 -1
  77. package/dist/operator/board-refresh-gate.js +12 -11
  78. package/dist/operator/board-refresh-gate.js.map +1 -1
  79. package/dist/operator/board-slot-instructions.d.ts +30 -0
  80. package/dist/operator/board-slot-instructions.d.ts.map +1 -1
  81. package/dist/operator/board-slot-instructions.js +110 -6
  82. package/dist/operator/board-slot-instructions.js.map +1 -1
  83. package/dist/operator/console-brief.d.ts +26 -13
  84. package/dist/operator/console-brief.d.ts.map +1 -1
  85. package/dist/operator/console-brief.js +159 -83
  86. package/dist/operator/console-brief.js.map +1 -1
  87. package/dist/operator/experience-evidence.d.ts +18 -0
  88. package/dist/operator/experience-evidence.d.ts.map +1 -0
  89. package/dist/operator/experience-evidence.js +82 -0
  90. package/dist/operator/experience-evidence.js.map +1 -0
  91. package/dist/operator/experience-hints.d.ts +44 -0
  92. package/dist/operator/experience-hints.d.ts.map +1 -0
  93. package/dist/operator/experience-hints.js +98 -0
  94. package/dist/operator/experience-hints.js.map +1 -0
  95. package/dist/operator/mama-memory-port.js +1 -3
  96. package/dist/operator/mama-memory-port.js.map +1 -1
  97. package/dist/operator/operator-trigger-loop.d.ts.map +1 -1
  98. package/dist/operator/operator-trigger-loop.js +3 -2
  99. package/dist/operator/operator-trigger-loop.js.map +1 -1
  100. package/dist/operator/owner-action-effects.d.ts +13 -1
  101. package/dist/operator/owner-action-effects.d.ts.map +1 -1
  102. package/dist/operator/owner-action-effects.js +17 -4
  103. package/dist/operator/owner-action-effects.js.map +1 -1
  104. package/dist/operator/owner-event-inbox.d.ts +7 -1
  105. package/dist/operator/owner-event-inbox.d.ts.map +1 -1
  106. package/dist/operator/owner-event-inbox.js +8 -0
  107. package/dist/operator/owner-event-inbox.js.map +1 -1
  108. package/dist/operator/owner-event-loop.d.ts +28 -2
  109. package/dist/operator/owner-event-loop.d.ts.map +1 -1
  110. package/dist/operator/owner-event-loop.js +60 -2
  111. package/dist/operator/owner-event-loop.js.map +1 -1
  112. package/dist/operator/owner-event-policy.d.ts +20 -0
  113. package/dist/operator/owner-event-policy.d.ts.map +1 -1
  114. package/dist/operator/owner-event-policy.js +34 -5
  115. package/dist/operator/owner-event-policy.js.map +1 -1
  116. package/dist/operator/owner-event-prompt.d.ts +13 -4
  117. package/dist/operator/owner-event-prompt.d.ts.map +1 -1
  118. package/dist/operator/owner-event-prompt.js +31 -38
  119. package/dist/operator/owner-event-prompt.js.map +1 -1
  120. package/dist/operator/owner-runtime-journal.d.ts.map +1 -1
  121. package/dist/operator/owner-runtime-journal.js +7 -3
  122. package/dist/operator/owner-runtime-journal.js.map +1 -1
  123. package/dist/operator/owner-runtime.d.ts +6 -0
  124. package/dist/operator/owner-runtime.d.ts.map +1 -1
  125. package/dist/operator/owner-runtime.js +48 -5
  126. package/dist/operator/owner-runtime.js.map +1 -1
  127. package/dist/operator/procedure-activation.d.ts +7 -0
  128. package/dist/operator/procedure-activation.d.ts.map +1 -0
  129. package/dist/operator/procedure-activation.js +132 -0
  130. package/dist/operator/procedure-activation.js.map +1 -0
  131. package/dist/operator/procedure-projection.d.ts +25 -0
  132. package/dist/operator/procedure-projection.d.ts.map +1 -0
  133. package/dist/operator/procedure-projection.js +80 -0
  134. package/dist/operator/procedure-projection.js.map +1 -0
  135. package/dist/operator/procedure-runtime.d.ts +45 -0
  136. package/dist/operator/procedure-runtime.d.ts.map +1 -0
  137. package/dist/operator/procedure-runtime.js +496 -0
  138. package/dist/operator/procedure-runtime.js.map +1 -0
  139. package/dist/operator/procedure-store.d.ts +97 -0
  140. package/dist/operator/procedure-store.d.ts.map +1 -0
  141. package/dist/operator/procedure-store.js +368 -0
  142. package/dist/operator/procedure-store.js.map +1 -0
  143. package/dist/operator/subagent-stimulus.d.ts +34 -0
  144. package/dist/operator/subagent-stimulus.d.ts.map +1 -0
  145. package/dist/operator/subagent-stimulus.js +145 -0
  146. package/dist/operator/subagent-stimulus.js.map +1 -0
  147. package/dist/operator/task-ledger.d.ts +40 -3
  148. package/dist/operator/task-ledger.d.ts.map +1 -1
  149. package/dist/operator/task-ledger.js +63 -10
  150. package/dist/operator/task-ledger.js.map +1 -1
  151. package/dist/operator/thread-brief-memory.d.ts +29 -0
  152. package/dist/operator/thread-brief-memory.d.ts.map +1 -0
  153. package/dist/operator/thread-brief-memory.js +61 -0
  154. package/dist/operator/thread-brief-memory.js.map +1 -0
  155. package/dist/operator/trigger-author.d.ts +2 -1
  156. package/dist/operator/trigger-author.d.ts.map +1 -1
  157. package/dist/operator/trigger-author.js +23 -0
  158. package/dist/operator/trigger-author.js.map +1 -1
  159. package/dist/operator/trigger-matcher.js +1 -0
  160. package/dist/operator/trigger-matcher.js.map +1 -1
  161. package/dist/operator/trigger-registry.d.ts +5 -4
  162. package/dist/operator/trigger-registry.d.ts.map +1 -1
  163. package/dist/operator/trigger-registry.js +78 -24
  164. package/dist/operator/trigger-registry.js.map +1 -1
  165. package/dist/operator/trigger-review.d.ts +1 -1
  166. package/dist/operator/trigger-review.d.ts.map +1 -1
  167. package/dist/operator/trigger-review.js +24 -4
  168. package/dist/operator/trigger-review.js.map +1 -1
  169. package/dist/operator/trigger-types.d.ts +10 -1
  170. package/dist/operator/trigger-types.d.ts.map +1 -1
  171. package/dist/operator/worker-run.d.ts +7 -1
  172. package/dist/operator/worker-run.d.ts.map +1 -1
  173. package/dist/operator/worker-run.js +5 -2
  174. package/dist/operator/worker-run.js.map +1 -1
  175. package/dist/operator/workorder-consumer.d.ts +91 -14
  176. package/dist/operator/workorder-consumer.d.ts.map +1 -1
  177. package/dist/operator/workorder-consumer.js +272 -93
  178. package/dist/operator/workorder-consumer.js.map +1 -1
  179. package/dist/operator/workorder-hooks.d.ts +42 -9
  180. package/dist/operator/workorder-hooks.d.ts.map +1 -1
  181. package/dist/operator/workorder-hooks.js +52 -13
  182. package/dist/operator/workorder-hooks.js.map +1 -1
  183. package/dist/operator/workorder-publishers.d.ts +9 -1
  184. package/dist/operator/workorder-publishers.d.ts.map +1 -1
  185. package/dist/operator/workorder-publishers.js +21 -2
  186. package/dist/operator/workorder-publishers.js.map +1 -1
  187. package/package.json +2 -2
  188. package/dist/operator/learning-context.d.ts +0 -49
  189. package/dist/operator/learning-context.d.ts.map +0 -1
  190. package/dist/operator/learning-context.js +0 -129
  191. package/dist/operator/learning-context.js.map +0 -1
  192. package/dist/operator/learning-markers.d.ts +0 -19
  193. package/dist/operator/learning-markers.d.ts.map +0 -1
  194. package/dist/operator/learning-markers.js +0 -147
  195. package/dist/operator/learning-markers.js.map +0 -1
  196. package/dist/operator/learning-read.d.ts +0 -16
  197. package/dist/operator/learning-read.d.ts.map +0 -1
  198. package/dist/operator/learning-read.js +0 -18
  199. package/dist/operator/learning-read.js.map +0 -1
  200. package/dist/operator/turn-observer.d.ts +0 -34
  201. package/dist/operator/turn-observer.d.ts.map +0 -1
  202. package/dist/operator/turn-observer.js +0 -79
  203. package/dist/operator/turn-observer.js.map +0 -1
  204. package/templates/skills/heartbeat-report.md +0 -75
@@ -1,16 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * MAMA Tool Executor for MAMA Standalone
4
- *
5
- * Executes MAMA gateway tools (mama_search, mama_save, mama_update, mama_load_checkpoint, Read, discord_send).
6
- * NOT MCP - uses Claude Messages API tool definitions.
7
- * Supports both direct API integration and mock API for testing.
8
- *
9
- * Role-Based Permission Control:
10
- * - Each tool execution is checked against the current AgentContext's role
11
- * - Blocked tools return permission errors instead of executing
12
- * - Path-based tools (Read, Write) also check path permissions
13
- */
14
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
15
3
  if (k2 === undefined) k2 = k;
16
4
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -50,6 +38,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
50
38
  Object.defineProperty(exports, "__esModule", { value: true });
51
39
  exports.GatewayToolExecutor = void 0;
52
40
  exports.serializeCodeActModelResult = serializeCodeActModelResult;
41
+ const skill_loader_js_1 = require("./skill-loader.js");
42
+ const experience_evidence_js_1 = require("../operator/experience-evidence.js");
43
+ /**
44
+ * MAMA Tool Executor for MAMA Standalone
45
+ *
46
+ * Executes MAMA gateway tools (mama_search, mama_save, mama_update, mama_load_checkpoint, Read, discord_send).
47
+ * NOT MCP - uses Claude Messages API tool definitions.
48
+ * Supports both direct API integration and mock API for testing.
49
+ *
50
+ * Role-Based Permission Control:
51
+ * - Each tool execution is checked against the current AgentContext's role
52
+ * - Blocked tools return permission errors instead of executing
53
+ * - Path-based tools (Read, Write) also check path permissions
54
+ */
55
+ const procedure_runtime_js_1 = require("../operator/procedure-runtime.js");
56
+ const host_bridge_js_1 = require("./code-act/host-bridge.js");
53
57
  const fs_1 = require("fs");
54
58
  const async_hooks_1 = require("async_hooks");
55
59
  const crypto_1 = require("crypto");
@@ -62,7 +66,7 @@ const mama_core_1 = require("@jungjaehoon/mama-core");
62
66
  const security_monitor_js_1 = require("../security/security-monitor.js");
63
67
  const node_fs_1 = require("node:fs");
64
68
  const secret_filter_js_1 = require("../memory/secret-filter.js");
65
- const learning_context_js_1 = require("../operator/learning-context.js");
69
+ const board_slot_instructions_js_1 = require("../operator/board-slot-instructions.js");
66
70
  const file_export_js_1 = require("../operator/file-export.js");
67
71
  const repair_request_js_1 = require("../operator/repair-request.js");
68
72
  const scope_context_js_1 = require("../memory/scope-context.js");
@@ -463,6 +467,9 @@ function sanitizeCommandForAudit(command) {
463
467
  */
464
468
  const tool_registry_js_1 = require("./tool-registry.js");
465
469
  const VALID_TOOLS = tool_registry_js_1.ToolRegistry.getValidToolNames();
470
+ const TOOL_CATALOG_REVISION = (0, crypto_1.createHash)('sha256')
471
+ .update(JSON.stringify(tool_registry_js_1.ToolRegistry.getAllTools()))
472
+ .digest('hex');
466
473
  /**
467
474
  * Sensitive patterns that should be masked in config output
468
475
  */
@@ -487,7 +494,35 @@ function isExactTelegramDeliveryReceipt(value, deliveryId, variant) {
487
494
  Number.isFinite(value.confirmedAt) &&
488
495
  value.confirmedAt >= 0);
489
496
  }
497
+ /** Gateway tools whose completed execution leaves a durable effect (ledger, memory, file, send). */
498
+ const DURABLE_WRITE_TOOL = /_(?:create|update|publish|send|save|reclassify|retire|observe|bind|reconcile|export|upload|write)$|^console_brief_update$|^Write$|^Bash$/;
490
499
  class GatewayToolExecutor {
500
+ procedureRuntime = null;
501
+ setProcedureStore(store) {
502
+ this.procedureRuntime = new procedure_runtime_js_1.ProcedureRuntime(store);
503
+ }
504
+ /**
505
+ * Turn-1 hints for the thread this prompt goes to (procedure-runtime.ts prepareContext).
506
+ * Skills stay in the system prompt and execution evidence behind experience_read and the
507
+ * experience_ref on each tool result: neither is re-sent per turn.
508
+ */
509
+ prepareProcedureContext(state, turn) {
510
+ return this.procedureRuntime?.prepareContext(state, turn) ?? { text: '', hints: [] };
511
+ }
512
+ /** Did this run complete any tool that durably writes? null when traces are unavailable. */
513
+ async runHadDurableWrite(modelRunId) {
514
+ const api = await this.initializeMAMAApi();
515
+ if (!api.listToolTracesForRun)
516
+ return null;
517
+ const traces = await api.listToolTracesForRun(modelRunId);
518
+ return traces.some((trace) => trace.execution_status === 'completed' && DURABLE_WRITE_TOOL.test(trace.tool_name));
519
+ }
520
+ releaseProcedureRun(state) {
521
+ this.procedureRuntime?.releaseRun(state);
522
+ }
523
+ readCanonicalProcedureBrief(state) {
524
+ return this.procedureRuntime?.canonicalBrief(state) ?? null;
525
+ }
491
526
  driveTools;
492
527
  imageTranslationTools;
493
528
  driveDestinationCapabilities = new Map();
@@ -668,6 +703,8 @@ class GatewayToolExecutor {
668
703
  executionSurface: executionContext?.executionSurface,
669
704
  sourceTurnId: executionContext?.sourceTurnId,
670
705
  sourceMessageRef: executionContext?.sourceMessageRef,
706
+ procedureStimulus: executionContext?.procedureStimulus,
707
+ procedureRefs: executionContext?.procedureRefs,
671
708
  modelRunId: executionContext?.modelRunId ?? null,
672
709
  gatewayCallId: executionContext?.gatewayCallId,
673
710
  workorderAttemptId: executionContext?.workorderAttemptId,
@@ -708,6 +745,8 @@ class GatewayToolExecutor {
708
745
  executionSurface: active.executionSurface ?? fallback.executionSurface,
709
746
  sourceTurnId: active.sourceTurnId ?? fallback.sourceTurnId,
710
747
  sourceMessageRef: active.sourceMessageRef ?? fallback.sourceMessageRef,
748
+ procedureStimulus: active.procedureStimulus,
749
+ procedureRefs: active.procedureRefs,
711
750
  modelRunId: active.modelRunId ?? fallback.modelRunId,
712
751
  gatewayCallId: active.gatewayCallId ?? fallback.gatewayCallId,
713
752
  // Never merged from fallback - attempt identity is issued for one claimed run only.
@@ -999,6 +1038,8 @@ class GatewayToolExecutor {
999
1038
  getModelRun: mama.getModelRun?.bind(mama),
1000
1039
  appendToolTrace: mama.appendToolTrace?.bind(mama),
1001
1040
  listToolTracesForRun: mama.listToolTracesForRun?.bind(mama),
1041
+ listToolTraces: mama.listToolTraces?.bind(mama),
1042
+ readToolTrace: mama.readToolTrace?.bind(mama),
1002
1043
  createAuditFinding: mama.createAuditFinding?.bind(mama),
1003
1044
  listOpenAuditFindings: (mama.listOpenAuditFindings ?? mama.listAuditFindings)?.bind(mama),
1004
1045
  listAuditFindings: mama.listAuditFindings?.bind(mama),
@@ -1313,6 +1354,8 @@ class GatewayToolExecutor {
1313
1354
  const activeCtx = traceState ? { ...ctx, modelRunId: traceState.modelRunId } : ctx;
1314
1355
  let result;
1315
1356
  let auditResult;
1357
+ let executionEvidence;
1358
+ let experienceRef;
1316
1359
  try {
1317
1360
  const rawResult = await this.executionContextStorage.run(activeCtx, () => this.executeWithEnvelopeAndPermissions(toolName, effectiveInput, gatewayCallId));
1318
1361
  if (activeCtx.temporalWorkContext &&
@@ -1322,6 +1365,13 @@ class GatewayToolExecutor {
1322
1365
  this.requireActiveTemporalAuthority(toolName);
1323
1366
  });
1324
1367
  }
1368
+ const inspectedEvidence = toolName === 'experience_read' ||
1369
+ (toolName === 'code_act' &&
1370
+ Array.isArray(rawResult.hostToolExecutions) &&
1371
+ rawResult.hostToolExecutions.some((call) => call.name === 'experience_read'));
1372
+ if ((0, procedure_runtime_js_1.deriveProcedureAccess)(activeCtx) && !inspectedEvidence) {
1373
+ executionEvidence = (0, experience_evidence_js_1.captureExecutionEvidence)(effectiveInput, rawResult);
1374
+ }
1325
1375
  const rawFailure = rawResult;
1326
1376
  if (rawFailure.success === false) {
1327
1377
  // Captured BEFORE sanitizeGatewayFailureResult: the raw text only exists here, and
@@ -1366,7 +1416,12 @@ class GatewayToolExecutor {
1366
1416
  const auditError = shouldSanitizeAuditFailure
1367
1417
  ? sanitizeGatewayError(error, Boolean(activeCtx.temporalWorkContext))
1368
1418
  : error;
1369
- await this.appendToolTraceIfNeeded(traceState, activeCtx, toolName, undefined, Date.now() - startedAt, gatewayCallId, auditError).catch((appendError) => {
1419
+ await this.appendToolTraceIfNeeded(traceState, activeCtx, toolName, undefined, Date.now() - startedAt, gatewayCallId, auditError, (0, procedure_runtime_js_1.deriveProcedureAccess)(activeCtx)
1420
+ ? (0, experience_evidence_js_1.captureExecutionEvidence)(effectiveInput, {
1421
+ success: false,
1422
+ error: error instanceof Error ? error.message : String(error),
1423
+ })
1424
+ : undefined).catch((appendError) => {
1370
1425
  securityLogger.warn('[model-run] failed to append failed tool trace before finalization', appendError);
1371
1426
  });
1372
1427
  await this.failDirectModelRunIfNeeded(traceState, toolName, auditError).catch((finalizationError) => {
@@ -1380,7 +1435,7 @@ class GatewayToolExecutor {
1380
1435
  }
1381
1436
  try {
1382
1437
  try {
1383
- await this.appendToolTraceIfNeeded(traceState, activeCtx, toolName, auditResult, Date.now() - startedAt, gatewayCallId);
1438
+ experienceRef = await this.appendToolTraceIfNeeded(traceState, activeCtx, toolName, auditResult, Date.now() - startedAt, gatewayCallId, undefined, executionEvidence);
1384
1439
  }
1385
1440
  finally {
1386
1441
  await this.completeDirectModelRunIfNeeded(traceState, toolName, auditResult);
@@ -1397,7 +1452,9 @@ class GatewayToolExecutor {
1397
1452
  if (scopeAudit.mismatch) {
1398
1453
  this.alarmScopeMismatch(activeCtx, toolName);
1399
1454
  }
1400
- return result;
1455
+ return experienceRef && executionEvidence
1456
+ ? { ...result, experience_ref: experienceRef }
1457
+ : result;
1401
1458
  }
1402
1459
  /**
1403
1460
  * External lifecycle decisions are only valid while the host-created model run that owns the
@@ -1548,16 +1605,17 @@ class GatewayToolExecutor {
1548
1605
  }
1549
1606
  return api;
1550
1607
  }
1551
- async appendToolTraceIfNeeded(traceState, ctx, toolName, result, durationMs, gatewayCallId, error) {
1608
+ async appendToolTraceIfNeeded(traceState, ctx, toolName, result, durationMs, gatewayCallId, error, evidence) {
1552
1609
  if (!traceState) {
1553
1610
  return;
1554
1611
  }
1555
- await traceState.api.appendToolTrace({
1612
+ const access = (0, procedure_runtime_js_1.deriveProcedureAccess)(ctx);
1613
+ const stored = await traceState.api.appendToolTrace({
1556
1614
  model_run_id: traceState.modelRunId,
1557
1615
  gateway_call_id: gatewayCallId,
1558
1616
  tool_name: toolName,
1559
1617
  input_summary: `tool:${toolName}`,
1560
- output_summary: this.summarizeToolTraceOutput(result, error),
1618
+ output_summary: (0, experience_evidence_js_1.safeExperienceSummary)(this.summarizeToolTraceOutput(result, error)),
1561
1619
  execution_status: error || result?.success === false ? 'failed' : 'completed',
1562
1620
  duration_ms: durationMs,
1563
1621
  envelope_hash: ctx.envelope?.envelope_hash ?? null,
@@ -1565,7 +1623,27 @@ class GatewayToolExecutor {
1565
1623
  // preserves `code`); the trace was the one place that dropped it,
1566
1624
  // leaving only sha256 digests. Carried, never invented: no code = NULL.
1567
1625
  failure_code: this.extractFailureCode(result, error),
1626
+ ...(access
1627
+ ? {
1628
+ owner_scope: access.ownerScope,
1629
+ project_id: access.projectId,
1630
+ channel_id: access.channelId ?? null,
1631
+ catalog_revision: TOOL_CATALOG_REVISION,
1632
+ diagnostic_json: JSON.stringify({
1633
+ tool: toolName,
1634
+ status: error || result?.success === false ? 'failed' : 'completed',
1635
+ failureCode: this.extractFailureCode(result, error),
1636
+ evidenceCompleteness: evidence?.completeness ?? 'unavailable',
1637
+ sourceRefs: [
1638
+ ctx.sourceMessageRef,
1639
+ ctx.parentToolName ? `parent-tool:${ctx.parentToolName}` : undefined,
1640
+ ].filter(Boolean),
1641
+ }),
1642
+ ...(evidence ? { evidence_json: JSON.stringify(evidence) } : {}),
1643
+ }
1644
+ : {}),
1568
1645
  });
1646
+ return stored?.trace_id;
1569
1647
  }
1570
1648
  extractFailureCode(result, error) {
1571
1649
  const candidate = error instanceof types_js_1.AgentError && typeof error.code === 'string'
@@ -2028,6 +2106,14 @@ class GatewayToolExecutor {
2028
2106
  }
2029
2107
  }
2030
2108
  try {
2109
+ // A selected running revision remains fixed, but retirement/revocation wins before effects.
2110
+ if (this.procedureRuntime &&
2111
+ toolName !== 'code_act' &&
2112
+ !toolName.startsWith('procedure_') &&
2113
+ toolName !== 'console_brief_update' &&
2114
+ (0, host_bridge_js_1.isCodeActMutatingTool)(toolName)) {
2115
+ this.procedureRuntime.assertWritable(activeState);
2116
+ }
2031
2117
  // Lazy MAMA API init — only for tools that need it
2032
2118
  const getApi = () => this.initializeMAMAApi();
2033
2119
  // Handle non-MAMA tools first
@@ -2375,16 +2461,6 @@ class GatewayToolExecutor {
2375
2461
  error: `Refusing to save: content matches secret pattern(s): ${saveSecretScan.matches.join(', ')}. Secrets must never enter memory.`,
2376
2462
  };
2377
2463
  }
2378
- if ((0, learning_context_js_1.isLearningTopic)(saveInput.topic)) {
2379
- // Standing policy and lessons come from the OWNER through the turn observer.
2380
- // An agent that could write policy: would grant itself instructions injected
2381
- // into every later turn. Refuse at the choke, like secrets.
2382
- return {
2383
- success: false,
2384
- code: 'learning_topic_refused',
2385
- error: 'Topics starting with policy: or lesson: are written by the host from owner instructions, never by the agent.',
2386
- };
2387
- }
2388
2464
  const api = await getApi();
2389
2465
  await this.recordMemoryWriteWarnings(api, 'mama_save', saveSecretScan.warnings);
2390
2466
  let trustedOptions;
@@ -2506,41 +2582,148 @@ class GatewayToolExecutor {
2506
2582
  if (acceptedSlotIds.length === 0) {
2507
2583
  throw new types_js_1.AgentError('Report publisher accepted no slots', 'TOOL_ERROR', undefined, false);
2508
2584
  }
2585
+ // Observability, not enforcement: a board slot is a reversible
2586
+ // durable write, so HTML that misses the board class vocabulary is
2587
+ // published as supplied and reported back as a warning. Without
2588
+ // this the slot silently renders as unstyled plain text.
2589
+ const runLabel = activeState.modelRunId ?? 'unknown';
2590
+ const vocabularyWarnings = [];
2591
+ for (const slotId of acceptedSlotIds) {
2592
+ const html = slotsInput[slotId];
2593
+ if (typeof html !== 'string' || !html.trim())
2594
+ continue;
2595
+ if ((0, board_slot_instructions_js_1.htmlUsesBoardVocabulary)(html))
2596
+ continue;
2597
+ vocabularyWarnings.push(`slot ${slotId} uses none of the board structural classes (report-summary / report-card / report-section-title / report-table) and will render as plain text; republish using the report_publish contract (tool_describe report_publish)`);
2598
+ console.warn(`[board] slot ${slotId} published without the board vocabulary (run ${runLabel})`);
2599
+ }
2509
2600
  return {
2510
2601
  success: true,
2511
2602
  acceptedSlotIds,
2512
2603
  changedSlotIds,
2513
2604
  message: `Dashboard report accepted: ${acceptedSlotIds.join(', ')} (${acceptedSlotIds.length} accepted, ${changedSlotIds.length} changed)`,
2605
+ ...(vocabularyWarnings.length > 0
2606
+ ? {
2607
+ warnings: vocabularyWarnings,
2608
+ // The shape and classes in hand once, not per slot, so the
2609
+ // republish needs no second lookup.
2610
+ contract: (0, board_slot_instructions_js_1.buildReportPublishToolContract)(),
2611
+ }
2612
+ : {}),
2514
2613
  };
2515
2614
  }
2516
2615
  throw new types_js_1.AgentError('Report publisher not configured', 'TOOL_ERROR', undefined, false);
2517
2616
  }
2518
- case 'console_brief_update': {
2519
- const { appendConsoleBriefLesson } = await import('../operator/console-brief.js');
2520
- // Append-only (live incident 2026-07-24): a full-replace contract had
2521
- // the model overwrite the entire seeded manual with its one new
2522
- // lesson. Accept `lesson`, with `content` as a lenient alias for
2523
- // threads still anchored on the old schema.
2524
- const rawInput = input;
2525
- const lessonInput = rawInput.lesson ?? rawInput.content;
2526
- if (typeof lessonInput !== 'string') {
2527
- return { success: false, error: 'console_brief_update requires a string lesson' };
2528
- }
2529
- let briefAfter;
2530
- try {
2531
- briefAfter = appendConsoleBriefLesson(lessonInput);
2617
+ case 'experience_read': {
2618
+ const access = (0, procedure_runtime_js_1.deriveProcedureAccess)(activeState);
2619
+ if (!access)
2620
+ throw new Error('Execution evidence authority required');
2621
+ const args = input;
2622
+ const allowed = new Set([
2623
+ 'kind',
2624
+ 'trace_id',
2625
+ 'run_id',
2626
+ 'tool_name',
2627
+ 'cursor',
2628
+ 'limit',
2629
+ 'offset',
2630
+ 'chars',
2631
+ ]);
2632
+ if (Object.keys(args).some((key) => !allowed.has(key)))
2633
+ throw new Error('Execution evidence scope is host-owned; unknown argument');
2634
+ for (const key of ['trace_id', 'run_id', 'tool_name', 'cursor']) {
2635
+ if (args[key] !== undefined &&
2636
+ (typeof args[key] !== 'string' || !String(args[key]).trim()))
2637
+ throw new Error(`Execution evidence ${key} invalid`);
2638
+ }
2639
+ if (args.kind !== undefined && args.kind !== 'skills' && args.kind !== 'executions')
2640
+ throw new Error('Execution evidence kind invalid');
2641
+ if (args.kind === 'skills') {
2642
+ if (access.ownerScope !== 'owner:runtime')
2643
+ throw new Error('Installed skill catalog owner scope required');
2644
+ if (['trace_id', 'run_id', 'tool_name', 'cursor', 'chars'].some((key) => args[key] !== undefined))
2645
+ throw new Error('Skill catalog accepts offset and limit only');
2646
+ const offset = args.offset ?? 0;
2647
+ const limit = args.limit ?? 20;
2648
+ if (typeof offset !== 'number' ||
2649
+ !Number.isSafeInteger(offset) ||
2650
+ offset < 0 ||
2651
+ typeof limit !== 'number' ||
2652
+ !Number.isSafeInteger(limit) ||
2653
+ limit < 1 ||
2654
+ limit > 100)
2655
+ throw new Error('Skill catalog offset/limit invalid');
2656
+ const skills = (0, skill_loader_js_1.filterSkillCatalogForContext)((0, skill_loader_js_1.loadInstalledSkills)(false, { includePaths: true }), activeState.agentContext);
2657
+ const end = Math.min(offset + limit, skills.length);
2658
+ return {
2659
+ success: true,
2660
+ skills: skills.slice(offset, end),
2661
+ total: skills.length,
2662
+ next_offset: end < skills.length ? end : null,
2663
+ };
2532
2664
  }
2533
- catch (err) {
2534
- return { success: false, error: err instanceof Error ? err.message : String(err) };
2665
+ const api = await this.initializeMAMAApi();
2666
+ if (!api.listToolTraces || !api.readToolTrace)
2667
+ throw new Error('Execution evidence storage unavailable');
2668
+ const scope = (0, experience_evidence_js_1.traceReadScope)(access);
2669
+ if (typeof args.trace_id === 'string') {
2670
+ const trace = await api.readToolTrace(args.trace_id, scope);
2671
+ if (!trace)
2672
+ throw new Error('Execution evidence unavailable');
2673
+ const offset = args.offset ?? 0;
2674
+ const chars = args.chars ?? 4000;
2675
+ if (typeof offset !== 'number' ||
2676
+ !Number.isSafeInteger(offset) ||
2677
+ offset < 0 ||
2678
+ typeof chars !== 'number' ||
2679
+ !Number.isSafeInteger(chars) ||
2680
+ chars < 1 ||
2681
+ chars > 8000) {
2682
+ throw new Error('Execution evidence offset/chars invalid');
2683
+ }
2684
+ const text = Array.from(trace.evidence_json ?? '');
2685
+ if (offset > text.length)
2686
+ throw new Error('Execution evidence offset out of range');
2687
+ const end = Math.min(offset + chars, text.length);
2688
+ return {
2689
+ success: true,
2690
+ trace: { ...trace, evidence_json: null },
2691
+ content: text.slice(offset, end).join(''),
2692
+ offset,
2693
+ total_chars: text.length,
2694
+ next_offset: end < text.length ? end : null,
2695
+ behaviorVerified: false,
2696
+ };
2535
2697
  }
2536
- // Observability over restriction: the agent editing its own operating
2537
- // brief is logged loudly, never silently absorbed. The next NEW/re-anchored
2538
- // owner session picks it up via the policy fingerprint.
2539
- console.log(`[console-brief] agent appended a lesson (brief now ${briefAfter.length} chars)`);
2540
- return {
2541
- success: true,
2542
- message: 'Lesson appended to your operating brief; it applies from the next session re-anchor.',
2543
- };
2698
+ const limit = args.limit;
2699
+ if (limit !== undefined &&
2700
+ (typeof limit !== 'number' || !Number.isSafeInteger(limit) || limit < 1 || limit > 100)) {
2701
+ throw new Error('Execution evidence limit invalid');
2702
+ }
2703
+ const page = await api.listToolTraces({
2704
+ ...scope,
2705
+ ...(typeof args.run_id === 'string' ? { model_run_id: args.run_id } : {}),
2706
+ ...(typeof args.tool_name === 'string' ? { tool_name: args.tool_name } : {}),
2707
+ ...(typeof args.cursor === 'string' ? { cursor: args.cursor } : {}),
2708
+ ...(limit === undefined ? {} : { limit }),
2709
+ });
2710
+ return { success: true, ...page, behaviorVerified: false };
2711
+ }
2712
+ case 'procedure_list':
2713
+ case 'procedure_read':
2714
+ case 'procedure_update':
2715
+ case 'procedure_retire':
2716
+ case 'procedure_observe': {
2717
+ if (!this.procedureRuntime) {
2718
+ throw new Error('Procedure store not configured');
2719
+ }
2720
+ return this.procedureRuntime.execute(toolName, input, activeState);
2721
+ }
2722
+ case 'console_brief_update': {
2723
+ if (!this.procedureRuntime) {
2724
+ return { success: false, error: 'procedure store unavailable' };
2725
+ }
2726
+ return this.procedureRuntime.execute(toolName, input, activeState);
2544
2727
  }
2545
2728
  case 'member_candidates': {
2546
2729
  const candidates = (0, member_candidate_store_js_1.getMemberCandidateStore)()