@jungjaehoon/mama-os 0.49.2 → 0.50.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 (135) hide show
  1. package/dist/agent/agent-loop.d.ts +5 -1
  2. package/dist/agent/agent-loop.d.ts.map +1 -1
  3. package/dist/agent/agent-loop.js +24 -4
  4. package/dist/agent/agent-loop.js.map +1 -1
  5. package/dist/agent/code-act/host-bridge.d.ts.map +1 -1
  6. package/dist/agent/code-act/host-bridge.js +39 -7
  7. package/dist/agent/code-act/host-bridge.js.map +1 -1
  8. package/dist/agent/codex-app-server-process.d.ts.map +1 -1
  9. package/dist/agent/codex-app-server-process.js +37 -0
  10. package/dist/agent/codex-app-server-process.js.map +1 -1
  11. package/dist/agent/gateway-tool-executor.d.ts +17 -6
  12. package/dist/agent/gateway-tool-executor.d.ts.map +1 -1
  13. package/dist/agent/gateway-tool-executor.js +512 -143
  14. package/dist/agent/gateway-tool-executor.js.map +1 -1
  15. package/dist/agent/gateway-tools.md +7 -6
  16. package/dist/agent/native-effect-observer.d.ts +19 -0
  17. package/dist/agent/native-effect-observer.d.ts.map +1 -0
  18. package/dist/agent/native-effect-observer.js +70 -0
  19. package/dist/agent/native-effect-observer.js.map +1 -0
  20. package/dist/agent/persistent-cli-process.d.ts.map +1 -1
  21. package/dist/agent/persistent-cli-process.js +6 -1
  22. package/dist/agent/persistent-cli-process.js.map +1 -1
  23. package/dist/agent/tool-registry.d.ts.map +1 -1
  24. package/dist/agent/tool-registry.js +15 -8
  25. package/dist/agent/tool-registry.js.map +1 -1
  26. package/dist/agent/types.d.ts +8 -2
  27. package/dist/agent/types.d.ts.map +1 -1
  28. package/dist/agent/types.js.map +1 -1
  29. package/dist/api/report-handler.d.ts +19 -3
  30. package/dist/api/report-handler.d.ts.map +1 -1
  31. package/dist/api/report-handler.js +123 -17
  32. package/dist/api/report-handler.js.map +1 -1
  33. package/dist/api/report-persistence.d.ts +3 -3
  34. package/dist/api/report-persistence.d.ts.map +1 -1
  35. package/dist/api/report-persistence.js +11 -21
  36. package/dist/api/report-persistence.js.map +1 -1
  37. package/dist/cli/commands/start.d.ts +4 -0
  38. package/dist/cli/commands/start.d.ts.map +1 -1
  39. package/dist/cli/commands/start.js +49 -157
  40. package/dist/cli/commands/start.js.map +1 -1
  41. package/dist/cli/runtime/agent-loop-init.d.ts.map +1 -1
  42. package/dist/cli/runtime/agent-loop-init.js +1 -0
  43. package/dist/cli/runtime/agent-loop-init.js.map +1 -1
  44. package/dist/cli/runtime/api-routes-init.d.ts +5 -19
  45. package/dist/cli/runtime/api-routes-init.d.ts.map +1 -1
  46. package/dist/cli/runtime/api-routes-init.js +7 -79
  47. package/dist/cli/runtime/api-routes-init.js.map +1 -1
  48. package/dist/db/migrations/operator-owner-action-candidates.d.ts +19 -0
  49. package/dist/db/migrations/operator-owner-action-candidates.d.ts.map +1 -0
  50. package/dist/db/migrations/operator-owner-action-candidates.js +254 -0
  51. package/dist/db/migrations/operator-owner-action-candidates.js.map +1 -0
  52. package/dist/db/migrations/operator-task-external-lifecycle.d.ts +9 -0
  53. package/dist/db/migrations/operator-task-external-lifecycle.d.ts.map +1 -1
  54. package/dist/db/migrations/operator-task-external-lifecycle.js +52 -22
  55. package/dist/db/migrations/operator-task-external-lifecycle.js.map +1 -1
  56. package/dist/db/migrations/operator-task-review-lifecycle.d.ts.map +1 -1
  57. package/dist/db/migrations/operator-task-review-lifecycle.js +3 -0
  58. package/dist/db/migrations/operator-task-review-lifecycle.js.map +1 -1
  59. package/dist/db/migrations/owner-action-effects.d.ts +18 -0
  60. package/dist/db/migrations/owner-action-effects.d.ts.map +1 -0
  61. package/dist/db/migrations/owner-action-effects.js +111 -0
  62. package/dist/db/migrations/owner-action-effects.js.map +1 -0
  63. package/dist/gateways/message-router.d.ts.map +1 -1
  64. package/dist/gateways/message-router.js +14 -4
  65. package/dist/gateways/message-router.js.map +1 -1
  66. package/dist/gateways/telegram-text-entities.d.ts +99 -0
  67. package/dist/gateways/telegram-text-entities.d.ts.map +1 -0
  68. package/dist/gateways/telegram-text-entities.js +189 -0
  69. package/dist/gateways/telegram-text-entities.js.map +1 -0
  70. package/dist/gateways/telegram.d.ts.map +1 -1
  71. package/dist/gateways/telegram.js +13 -2
  72. package/dist/gateways/telegram.js.map +1 -1
  73. package/dist/gateways/types.d.ts +8 -0
  74. package/dist/gateways/types.d.ts.map +1 -1
  75. package/dist/operator/board-read-views.d.ts +9 -4
  76. package/dist/operator/board-read-views.d.ts.map +1 -1
  77. package/dist/operator/board-read-views.js +11 -0
  78. package/dist/operator/board-read-views.js.map +1 -1
  79. package/dist/operator/external-lifecycle-candidates.d.ts +15 -1
  80. package/dist/operator/external-lifecycle-candidates.d.ts.map +1 -1
  81. package/dist/operator/external-lifecycle-candidates.js +164 -0
  82. package/dist/operator/external-lifecycle-candidates.js.map +1 -1
  83. package/dist/operator/external-lifecycle-discovery.d.ts +61 -0
  84. package/dist/operator/external-lifecycle-discovery.d.ts.map +1 -0
  85. package/dist/operator/external-lifecycle-discovery.js +103 -0
  86. package/dist/operator/external-lifecycle-discovery.js.map +1 -0
  87. package/dist/operator/external-lifecycle.d.ts +23 -0
  88. package/dist/operator/external-lifecycle.d.ts.map +1 -1
  89. package/dist/operator/owner-action-effects.d.ts +101 -0
  90. package/dist/operator/owner-action-effects.d.ts.map +1 -0
  91. package/dist/operator/owner-action-effects.js +335 -0
  92. package/dist/operator/owner-action-effects.js.map +1 -0
  93. package/dist/operator/owner-event-inbox.d.ts +1 -0
  94. package/dist/operator/owner-event-inbox.d.ts.map +1 -1
  95. package/dist/operator/owner-event-inbox.js +6 -0
  96. package/dist/operator/owner-event-inbox.js.map +1 -1
  97. package/dist/operator/owner-event-loop.d.ts +3 -0
  98. package/dist/operator/owner-event-loop.d.ts.map +1 -1
  99. package/dist/operator/owner-event-loop.js +34 -0
  100. package/dist/operator/owner-event-loop.js.map +1 -1
  101. package/dist/operator/owner-event-outcome.d.ts.map +1 -1
  102. package/dist/operator/owner-event-outcome.js +6 -0
  103. package/dist/operator/owner-event-outcome.js.map +1 -1
  104. package/dist/operator/owner-event-policy.d.ts.map +1 -1
  105. package/dist/operator/owner-event-policy.js +2 -26
  106. package/dist/operator/owner-event-policy.js.map +1 -1
  107. package/dist/operator/owner-event-prompt.d.ts.map +1 -1
  108. package/dist/operator/owner-event-prompt.js +2 -5
  109. package/dist/operator/owner-event-prompt.js.map +1 -1
  110. package/dist/operator/task-ledger.d.ts +93 -14
  111. package/dist/operator/task-ledger.d.ts.map +1 -1
  112. package/dist/operator/task-ledger.js +430 -103
  113. package/dist/operator/task-ledger.js.map +1 -1
  114. package/dist/operator/temporal-reconcile.d.ts.map +1 -1
  115. package/dist/operator/temporal-reconcile.js +6 -2
  116. package/dist/operator/temporal-reconcile.js.map +1 -1
  117. package/dist/operator/workorder-consumer.d.ts +2 -0
  118. package/dist/operator/workorder-consumer.d.ts.map +1 -1
  119. package/dist/operator/workorder-consumer.js +20 -5
  120. package/dist/operator/workorder-consumer.js.map +1 -1
  121. package/dist/scheduler/heartbeat.d.ts.map +1 -1
  122. package/dist/scheduler/heartbeat.js +1 -0
  123. package/dist/scheduler/heartbeat.js.map +1 -1
  124. package/dist/sqlite.d.ts +1 -1
  125. package/dist/sqlite.d.ts.map +1 -1
  126. package/dist/sqlite.js +3 -2
  127. package/dist/sqlite.js.map +1 -1
  128. package/dist/wiki/wiki-read.d.ts +2 -1
  129. package/dist/wiki/wiki-read.d.ts.map +1 -1
  130. package/dist/wiki/wiki-read.js +13 -2
  131. package/dist/wiki/wiki-read.js.map +1 -1
  132. package/dist/wiki/wiki-turn-contract.js +1 -1
  133. package/dist/wiki/wiki-turn-contract.js.map +1 -1
  134. package/package.json +1 -1
  135. package/public/viewer/operator/operator.js +3 -1
@@ -113,8 +113,6 @@ const shutdown_js_1 = require("../runtime/shutdown.js");
113
113
  const envelope_bootstrap_js_1 = require("../runtime/envelope-bootstrap.js");
114
114
  const config_loader_js_1 = require("../../connectors/config-loader.js");
115
115
  const connector_bootstrap_js_1 = require("../runtime/connector-bootstrap.js");
116
- const private_connector_policy_js_1 = require("../../connectors/private-connector-policy.js");
117
- const wiki_continuity_js_1 = require("../../operator/wiki-continuity.js");
118
116
  const message_router_config_js_1 = require("../runtime/message-router-config.js");
119
117
  const reactive_config_js_1 = require("../../envelope/reactive-config.js");
120
118
  const scope_context_js_1 = require("../../memory/scope-context.js");
@@ -133,6 +131,7 @@ const operational_issues_js_1 = require("../../observability/operational-issues.
133
131
  const repair_request_js_1 = require("../../operator/repair-request.js");
134
132
  const agent_loop_init_js_2 = require("../runtime/agent-loop-init.js");
135
133
  const workorder_publishers_js_1 = require("../../operator/workorder-publishers.js");
134
+ const owner_action_effects_js_1 = require("../../operator/owner-action-effects.js");
136
135
  const owner_event_effects_js_1 = require("../../operator/owner-event-effects.js");
137
136
  const owner_event_loop_js_1 = require("../../operator/owner-event-loop.js");
138
137
  const board_pipeline_render_js_1 = require("../../operator/board-pipeline-render.js");
@@ -154,11 +153,6 @@ const principalRegistryLogger = new DebugLogger('PrincipalRegistry');
154
153
  function workOrderActivityDetails(event) {
155
154
  return event.briefHash ? { brief_hash: event.briefHash } : undefined;
156
155
  }
157
- const DISABLED_PRIVATE_CONNECTOR_POLICY = (0, private_connector_policy_js_1.resolvePrivateConnectorPolicy)({
158
- ok: true,
159
- config: {},
160
- enabledNames: [],
161
- });
162
156
  function createCorePrincipalRegistry(adapter) {
163
157
  const { createPrincipalRepository } = mamaCore;
164
158
  return createPrincipalRepository(adapter);
@@ -393,19 +387,10 @@ function causeEventIdsFromPayload(payload) {
393
387
  return batch.filter((id) => typeof id === 'string' && id.trim().length > 0);
394
388
  }
395
389
  function workOrderEnvelopeScope(input) {
396
- const isTemporal = input.workKind === 'temporal';
397
- // One principal for every turn; what differs per kind is WHICH connectors the run
398
- // may read, and that is data: a recheck reads its task's connector or nothing, a
399
- // wiki turn reads no PRIVATE connector (personal channels never feed public pages),
400
- // the board and curation turns keep the lane's configured connectors.
390
+ // The trusted owner grant decides readable connectors. Work kind and temporal binding
391
+ // remain selection and receipt metadata; neither attenuates ordinary read authority.
401
392
  const surface = exports.ONE_AGENT_TURN_POLICY.roleName;
402
- const candidateConnectors = isTemporal
403
- ? input.temporalBinding
404
- ? [input.temporalBinding.connector]
405
- : []
406
- : input.workKind === 'wiki'
407
- ? (0, wiki_continuity_js_1.publicWikiConnectorScope)(input.laneConnectors, input.privateConnectorPolicy.enabledPrivateConnectors)
408
- : input.laneConnectors;
393
+ const candidateConnectors = input.laneConnectors;
409
394
  return {
410
395
  project_refs: [{ kind: 'project', id: input.projectId }],
411
396
  // A temporal run reads its task's connector or nothing. Every other lane keeps the
@@ -425,25 +410,15 @@ function workOrderEnvelopeScope(input) {
425
410
  // mama_save's write binding, so issuing the mirror here re-opened
426
411
  // per-channel raw isolation and bound every saved memory to every
427
412
  // granted channel (PR #217 review, blocking #2/#3).
428
- ...(input.temporalBinding
429
- ? [
430
- {
431
- kind: 'channel',
432
- id: `${input.temporalBinding.connector}:${input.temporalBinding.channel}`,
433
- },
434
- ]
435
- : []),
436
- // Same rule for a board reconcile: the run is bound to the channel whose delta it
437
- // judges, and connector memories are scoped by the canonical channel key.
438
- ...(input.workKind === 'board' && input.reconcileChannelKey
439
- ? [{ kind: 'channel', id: input.reconcileChannelKey }]
440
- : []),
441
413
  ],
442
414
  allowed_destinations: [],
443
415
  };
444
416
  }
445
417
  function temporalTaskBinding(ledger, taskId) {
446
- const sourceChannel = ledger.getById(taskId)?.sourceChannel;
418
+ const task = ledger.getById(taskId);
419
+ const sourceChannel = task?.status === 'review'
420
+ ? (task.reviewAnchorSourceChannel ?? task.sourceChannel)
421
+ : task?.sourceChannel;
447
422
  if (typeof sourceChannel !== 'string')
448
423
  return null;
449
424
  const separator = sourceChannel.indexOf(':');
@@ -510,37 +485,7 @@ exports.TURN_KIND_REQUIRED_TOOLS = {
510
485
  * that is advertised but never injected is a hallucinated-call generator. Member data and
511
486
  * workspace file reads are owner-conversation material with no use in a scheduled turn.
512
487
  */
513
- exports.SCHEDULED_TURN_BLOCKED_TOOLS = new Set([
514
- // Records and tasks are SEPARATE (owner policy). An unattended turn reads connector
515
- // evidence, and connector observations, principles and open questions are records -
516
- // letting a scheduled turn mint owner rows is what filled the board with non-tasks.
517
- // Only an owner CONVERSATION creates a task; unattended turns recorrect with
518
- // task_reclassify instead.
519
- 'task_create',
520
- // workspace file reads, the shell and the file writer are owner-conversation tools;
521
- // no unattended turn section instructs one
522
- 'Read',
523
- 'Bash',
524
- 'Write',
525
- // every outbound channel, not only Telegram: the grant is derived from the owner's
526
- // (editable) role config, so a send tool added there must still never run unattended
527
- 'telegram_send',
528
- 'discord_send',
529
- 'slack_send',
530
- 'webchat_send',
531
- 'drive_upload',
532
- 'drive_list_drives',
533
- 'drive_browse',
534
- 'drive_find_folder',
535
- 'drive_download',
536
- 'ocr_image',
537
- 'create_fb_overlay',
538
- 'translate_conti',
539
- 'drive_translate_conti',
540
- 'member_candidates',
541
- 'member_list',
542
- 'member_scope_list',
543
- ]);
488
+ exports.SCHEDULED_TURN_BLOCKED_TOOLS = exports.ADMINISTRATION_TOOLS;
544
489
  /**
545
490
  * Per-turn-kind blocked lists, projected by the HOST. One principal and one brief do
546
491
  * not mean one grant for every trigger: an unattended board or wiki turn has no owner
@@ -548,61 +493,11 @@ exports.SCHEDULED_TURN_BLOCKED_TOOLS = new Set([
548
493
  * artifact. Each entry names the artifact it protects.
549
494
  */
550
495
  exports.TURN_KIND_BLOCKED_TOOLS = {
551
- // board writes judgment slots and recorrects existing task lifecycle through
552
- // task_update/task_reclassify; it does not create rows, touch memory, or touch the
553
- // vault (obsidian is the wiki lane's write path)
554
- board: new Set([
555
- 'wiki_publish',
556
- 'obsidian',
557
- 'mama_save',
558
- 'mama_update',
559
- 'task_temporal_reconcile',
560
- ]),
561
- // wiki writes pages; the ledger, the board and memory are not its artifact
562
- wiki: new Set([
563
- 'report_publish',
564
- 'mama_save',
565
- 'mama_update',
566
- 'task_create',
567
- 'task_update',
568
- 'task_reclassify',
569
- 'task_temporal_reconcile',
570
- 'obsidian',
571
- ]),
572
- // curation writes memory; nothing else
573
- 'memory-curation': new Set([
574
- 'report_publish',
575
- 'wiki_publish',
576
- 'task_create',
577
- 'task_update',
578
- 'task_reclassify',
579
- 'task_temporal_reconcile',
580
- 'obsidian',
581
- ]),
582
- // recheck resolves ONE task through task_temporal_reconcile; generic mutation is out
583
- // self-check triages the system's own failures; it files repair requests and closes
584
- // issues, and touches nothing the owner or another turn owns
585
- 'self-check': new Set([
586
- 'report_publish',
587
- 'wiki_publish',
588
- 'mama_save',
589
- 'mama_update',
590
- 'task_create',
591
- 'task_update',
592
- 'task_reclassify',
593
- 'task_temporal_reconcile',
594
- 'obsidian',
595
- ]),
596
- temporal: new Set([
597
- 'report_publish',
598
- 'wiki_publish',
599
- 'mama_save',
600
- 'mama_update',
601
- 'task_create',
602
- 'task_update',
603
- 'task_reclassify',
604
- 'obsidian',
605
- ]),
496
+ board: new Set(),
497
+ wiki: new Set(),
498
+ 'memory-curation': new Set(),
499
+ 'self-check': new Set(),
500
+ temporal: new Set(),
606
501
  };
607
502
  /** Progressive evidence primitives for a scheduled owner-runtime report turn. */
608
503
  exports.OPERATOR_REPORT_TOOL_POLICY = {
@@ -704,17 +599,12 @@ function buildTurnAgentPolicy(kind, model, backend, privateConnectorPolicy, rawC
704
599
  }
705
600
  // Same downgrade the event turn applies: a run with no private binding advertises
706
601
  // no private connector tools and gets the disabled brief projection.
707
- const scopedSurface = requiredPrivatePolicy.enabledPrivateConnectors.some((name) => rawConnectorScope.includes(name))
708
- ? exports.ONE_AGENT_TURN_POLICY.roleName
709
- : 'multi-agent-generic';
602
+ const scopedSurface = exports.ONE_AGENT_TURN_POLICY.roleName;
710
603
  const blockedSet = new Set([
711
604
  ...exports.ADMINISTRATION_TOOLS,
712
605
  ...exports.SCHEDULED_TURN_BLOCKED_TOOLS,
713
606
  ...kindBlocked,
714
607
  ...(ownerRole.blockedTools ?? []),
715
- // Shape rule on top of the named lists: the role config is owner-editable, so a
716
- // send or upload tool that appears there tomorrow is still never unattended.
717
- ...ownerRole.allowedTools.filter((tool) => isOutboundToolName(tool)),
718
608
  ]);
719
609
  const projectedRole = buildProjectedLaneRole(scopedSurface, uniqueToolList([...ownerRole.allowedTools, ...exports.TURN_KIND_REQUIRED_TOOLS[kind]]).filter((tool) => !blockedSet.has(tool)), requiredPrivatePolicy);
720
610
  const blockedTools = [...new Set([...(projectedRole.blockedTools ?? []), ...blockedSet])];
@@ -733,15 +623,13 @@ function buildTurnAgentPolicy(kind, model, backend, privateConnectorPolicy, rawC
733
623
  },
734
624
  capabilities: allowedTools,
735
625
  limitations: blockedTools.map((tool) => `Cannot use ${tool}`),
736
- tier: 2,
626
+ tier: 1,
737
627
  backend,
738
628
  };
739
629
  return {
740
630
  agentContext,
741
631
  gatewayToolsPrompt: buildProjectedLanePrompt(innerTools, scopedSurface, requiredPrivatePolicy),
742
- briefProjectionPolicy: scopedSurface === 'multi-agent-generic'
743
- ? DISABLED_PRIVATE_CONNECTOR_POLICY
744
- : requiredPrivatePolicy,
632
+ briefProjectionPolicy: requiredPrivatePolicy,
745
633
  };
746
634
  }
747
635
  function bindCodeActParentModelRun(adapter, executionContext, options) {
@@ -803,7 +691,8 @@ function resolveOwnerEventTerminalReceipt(batch, deps) {
803
691
  const confirmedEffects = deps.ownerEventEffectLedger
804
692
  .confirmedKinds(batch.id)
805
693
  .filter((kind) => kind !== 'telegram_send');
806
- const durable = [...new Set([...ledgerEffects, ...confirmedEffects])];
694
+ const genericEffects = deps.ownerActionEffectLedger?.confirmedKindsForOccurrence(`owner-event:${batch.id}`) ?? [];
695
+ const durable = [...new Set([...ledgerEffects, ...confirmedEffects, ...genericEffects])];
807
696
  if (durable.length > 0) {
808
697
  return { status: 'acted', tools: durable, ownerDecisionRequested: false };
809
698
  }
@@ -1322,6 +1211,7 @@ async function runAgentLoop(config, options = {}) {
1322
1211
  sessionKey: owner_runtime_js_1.OWNER_RUNTIME_SESSION_KEY,
1323
1212
  source: 'operator',
1324
1213
  channelId,
1214
+ sourceMessageRef: `owner-stimulus:${channelId}:${(0, node_crypto_1.randomUUID)()}`,
1325
1215
  agentContext: backgroundOwnerContext,
1326
1216
  sessionPolicyRole: ownerRole,
1327
1217
  prepareEnvelope: async () => {
@@ -1393,8 +1283,11 @@ async function runAgentLoop(config, options = {}) {
1393
1283
  (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(operatorDbPath), { recursive: true });
1394
1284
  const operatorDb = new sqlite_js_1.default(operatorDbPath);
1395
1285
  let taskLedger;
1286
+ const refreshTaskBoard = { current: null };
1396
1287
  try {
1397
- taskLedger = new task_ledger_js_1.TaskLedger(operatorDb);
1288
+ taskLedger = new task_ledger_js_1.TaskLedger(operatorDb, {
1289
+ onOwnerTaskChangeCommitted: () => refreshTaskBoard.current?.(),
1290
+ });
1398
1291
  toolExecutor.setTaskLedger(taskLedger);
1399
1292
  }
1400
1293
  catch (err) {
@@ -1414,6 +1307,8 @@ async function runAgentLoop(config, options = {}) {
1414
1307
  const boardRefreshGate = new board_refresh_gate_js_1.BoardRefreshGate({ initialGeneration: Date.now() });
1415
1308
  const ownerEventEffectLedger = new owner_event_effects_js_1.OwnerEventEffectLedger(operatorDb);
1416
1309
  toolExecutor.setOwnerEventEffectLedger(ownerEventEffectLedger);
1310
+ const ownerActionEffectLedger = new owner_action_effects_js_1.OwnerActionEffectLedger(operatorDb);
1311
+ toolExecutor.setOwnerActionEffectLedger(ownerActionEffectLedger);
1417
1312
  let stopOwnerEventRuntime = null;
1418
1313
  // S2: leg cadence watchdog - its OWN timer, deliberately outside the
1419
1314
  // trigger loop (a watchdog inside the thing it watches dies with it).
@@ -1557,9 +1452,6 @@ async function runAgentLoop(config, options = {}) {
1557
1452
  resolveWorkerRequestTimeoutMs();
1558
1453
  let workOrderConsumer = null;
1559
1454
  const boardRepairNudge = { current: null };
1560
- const pipelineSlotPublisher = {
1561
- current: null,
1562
- };
1563
1455
  const learningBlockRef = { current: null };
1564
1456
  let temporalRuntime = null;
1565
1457
  gateways.push({
@@ -1623,6 +1515,8 @@ async function runAgentLoop(config, options = {}) {
1623
1515
  },
1624
1516
  };
1625
1517
  workOrderConsumer = new WorkOrderConsumer({
1518
+ hasUnsettledEffects: (wo) => ownerActionEffectLedger.hasUnsettledEffects(`workorder:${wo.idempotencyKey}`),
1519
+ hasUnsafeReplayEffects: (wo) => ownerActionEffectLedger.hasUnsafeReplayEffects(`workorder:${wo.idempotencyKey}`),
1626
1520
  ledger: taskLedger,
1627
1521
  runner: workerRunner,
1628
1522
  loadOwnerBrief: () => (0, console_brief_js_1.loadConsoleBrief)(),
@@ -1664,14 +1558,11 @@ async function runAgentLoop(config, options = {}) {
1664
1558
  noUpdateScope: `self-check:${new Date().toLocaleDateString('sv-SE')}`,
1665
1559
  }),
1666
1560
  publishPipelineSlot: () => {
1667
- const publish = pipelineSlotPublisher.current;
1668
- if (!publish)
1669
- throw new Error('pipeline slot publisher is not bound yet');
1670
- const page = taskLedger.listPage({
1671
- includeTerminal: false,
1672
- order: 'deadline_priority',
1673
- });
1674
- publish({ pipeline: (0, board_pipeline_render_js_1.renderPipelineSlot)(page.tasks, Date.now(), page.total) });
1561
+ const refresh = refreshTaskBoard.current;
1562
+ if (!refresh) {
1563
+ throw new Error('task board projection is not bound yet');
1564
+ }
1565
+ refresh();
1675
1566
  },
1676
1567
  noticeOwner: (summary) => messageRouter.enqueueOperatorNotice(summary),
1677
1568
  opsAlarm,
@@ -1708,19 +1599,8 @@ async function runAgentLoop(config, options = {}) {
1708
1599
  sessionPolicyRole: ownerRole,
1709
1600
  workOrderBriefProjectionPolicy: workOrderPolicy.briefProjectionPolicy,
1710
1601
  }, wo.id);
1711
- // A temporal run is bound to ONE task on ONE channel, and the packet check
1712
- // (temporalPacketRawSourcesWithinBoundSource) enforces exactly that: every raw ref
1713
- // must match the task's connector:channel or the reconcile is refused.
1714
- //
1715
- // While raw reads returned nothing that check was vacuous. Once the channel grant
1716
- // makes them return rows it stops being vacuous, and a lane-wide envelope hands
1717
- // the compile every configured channel of every granted connector - so every
1718
- // raw-backed temporal run would fail a check it cannot satisfy. The envelope has
1719
- // to say what the run is actually bound to.
1720
- //
1721
- // A task with no source channel gets no connector at all, which is the same answer
1722
- // from the other direction: the check requires zero raw refs for those, so the
1723
- // grant must be empty rather than merely narrow.
1602
+ // Temporal identity and the required review anchor remain host-bound. Additional
1603
+ // corroboration is selected by the owner and checked against current exact grants.
1724
1604
  // A reconcile carries the channel's delta batch. Handing it to the run makes every
1725
1605
  // durable change the run produces rest on it WITHOUT the agent restating anything -
1726
1606
  // the system knew the batch before the run began. This is the whole difference
@@ -2120,8 +2000,11 @@ async function runAgentLoop(config, options = {}) {
2120
2000
  },
2121
2001
  issueEnvelope: ownerEventIssueEnvelope,
2122
2002
  getNoUpdateMaxId: (scope) => taskLedger.maxNoUpdateId(scope),
2003
+ hasUnsafeReplayEffects: (batch) => ownerActionEffectLedger.hasUnsafeReplayEffects(`owner-event:${batch.id}`),
2004
+ hasUnsettledEffects: (batch) => ownerActionEffectLedger.hasUnsettledEffects(`owner-event:${batch.id}`),
2123
2005
  getTerminalReceipt: (batch) => resolveOwnerEventTerminalReceipt(batch, {
2124
2006
  ownerEventEffectLedger,
2007
+ ownerActionEffectLedger,
2125
2008
  taskLedger,
2126
2009
  }),
2127
2010
  recordTriggerOutcome: (triggerId, outcome) => triggerRegistry.recordOutcome(triggerId, outcome),
@@ -2219,8 +2102,17 @@ async function runAgentLoop(config, options = {}) {
2219
2102
  });
2220
2103
  boardRepairNudge.current = apiRoutesHandle.requestBoardRepair;
2221
2104
  {
2222
- const { createReportPublisher } = await import('../../api/report-handler.js');
2223
- pipelineSlotPublisher.current = createReportPublisher(apiServer.reportStore, apiServer.reportSseClients);
2105
+ const { broadcastReportUpdate } = await import('../../api/report-handler.js');
2106
+ apiServer.reportStore.setTaskProjectionProvider(() => operatorDb.transaction(() => {
2107
+ const page = taskLedger.listPage({ includeTerminal: false, order: 'deadline_priority' });
2108
+ return {
2109
+ basisRevision: taskLedger.readGeneration(),
2110
+ html: (0, board_pipeline_render_js_1.renderPipelineSlot)(page.tasks, Date.now(), page.total),
2111
+ };
2112
+ })(), (slots) => broadcastReportUpdate(apiServer.reportSseClients, { slots }));
2113
+ refreshTaskBoard.current = () => {
2114
+ apiServer.reportStore.refreshTaskProjection();
2115
+ };
2224
2116
  }
2225
2117
  gateways.push({ stop: async () => apiRoutesHandle.stop() });
2226
2118
  // ── Stage-2 boot pass (plan S2-T3): runtime assembly registered hooks;