maka-agent 0.2.0-dev.64.20260929 → 0.2.0-dev.67.20261002

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 (94) hide show
  1. package/LICENSE +1 -1
  2. package/THIRD_PARTY_NOTICES.txt +3 -3
  3. package/dist/pi-tui-runner.js +26 -29
  4. package/dist/tui-copy-catalog.js +9 -9
  5. package/native/runtime-host-peer/prebuilds/darwin-arm64/maka_runtime_host_peer.node +0 -0
  6. package/native/runtime-host-peer/prebuilds/linux-arm64/maka_runtime_host_peer.node +0 -0
  7. package/native/runtime-host-peer/prebuilds/linux-x64/maka_runtime_host_peer.node +0 -0
  8. package/native/runtime-host-peer/prebuilds/win32-x64/maka_runtime_host_peer.node +0 -0
  9. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  10. package/node_modules/@maka/core/dist/settings.js +2 -2
  11. package/node_modules/@maka/core/dist/tool-ledger-scanner.js +53 -150
  12. package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +8 -1
  13. package/node_modules/@maka/runtime/dist/conversation-copy.js +6 -0
  14. package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +17 -0
  15. package/node_modules/@maka/runtime/dist/session-manager.js +25 -40
  16. package/node_modules/@maka/runtime/dist/stream-graph-coordinator.js +79 -13
  17. package/node_modules/@maka/runtime/dist/stream-graph-projection.js +64 -0
  18. package/node_modules/@maka/runtime/dist/stream-graph-read-model.js +138 -20
  19. package/node_modules/@maka/runtime/dist/tool-availability.js +10 -3
  20. package/node_modules/@maka/runtime-host/dist/candidate-entry.js +15 -0
  21. package/node_modules/@maka/runtime-host/dist/candidate-startup-failure.js +5 -0
  22. package/node_modules/@maka/runtime-host/dist/client/session-catalog-summary.js +1 -0
  23. package/node_modules/@maka/runtime-host/dist/client/startup-error.js +5 -0
  24. package/node_modules/@maka/runtime-host/dist/protocol/agent-graph.js +46 -1
  25. package/node_modules/@maka/runtime-host/dist/protocol/index.js +14 -1
  26. package/node_modules/@maka/runtime-host/dist/protocol/operations.js +5 -1
  27. package/node_modules/@maka/runtime-host/dist/protocol/session-catalog.js +12 -0
  28. package/node_modules/@maka/runtime-host/dist/protocol/session-retirement.js +46 -10
  29. package/node_modules/@maka/runtime-host/dist/protocol/storage-usage.js +155 -0
  30. package/node_modules/@maka/runtime-host/dist/server/agent-graph-coordinator.js +23 -0
  31. package/node_modules/@maka/runtime-host/dist/server/canonical-turn-snapshot.js +5 -0
  32. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +27 -6
  33. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +2 -0
  34. package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +6 -0
  35. package/node_modules/@maka/runtime-host/dist/server/session-retirement-coordinator.js +112 -36
  36. package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +27 -1
  37. package/node_modules/@maka/runtime-host/dist/server/storage-usage-coordinator.js +67 -0
  38. package/node_modules/@maka/runtime-host/dist/server/workhub-inspection-tool.js +208 -0
  39. package/node_modules/@maka/storage/dist/git-worktree-child-executor.js +3 -1
  40. package/node_modules/@maka/storage/dist/operational-state-store.js +0 -13
  41. package/node_modules/@maka/storage/dist/session-store.js +9 -6
  42. package/node_modules/@maka/storage/dist/sqlite-file-set.js +22 -0
  43. package/node_modules/@maka/storage/dist/sqlite-long-term-memory-store.js +1 -3
  44. package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +10 -260
  45. package/node_modules/@maka/storage/dist/sqlite-session-catalog-query.js +1 -0
  46. package/node_modules/@maka/storage/dist/sqlite-session-metadata-schema.js +18 -6
  47. package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +28 -6
  48. package/node_modules/@maka/storage/dist/storage-footprint.js +239 -0
  49. package/node_modules/@maka/storage/dist/storage-writer-composition.js +10 -1
  50. package/node_modules/axios/CHANGELOG.md +62 -0
  51. package/node_modules/axios/README.md +297 -5
  52. package/node_modules/axios/dist/axios.js +728 -170
  53. package/node_modules/axios/dist/axios.min.js +3 -3
  54. package/node_modules/axios/dist/browser/axios.cjs +871 -188
  55. package/node_modules/axios/dist/esm/axios.js +871 -188
  56. package/node_modules/axios/dist/esm/axios.min.js +2 -2
  57. package/node_modules/axios/dist/node/axios.cjs +1037 -225
  58. package/node_modules/axios/lib/adapters/fetch.js +64 -11
  59. package/node_modules/axios/lib/adapters/http.js +140 -67
  60. package/node_modules/axios/lib/adapters/xhr.js +52 -3
  61. package/node_modules/axios/lib/core/Axios.js +43 -20
  62. package/node_modules/axios/lib/core/AxiosError.js +33 -2
  63. package/node_modules/axios/lib/core/AxiosHeaders.js +124 -1
  64. package/node_modules/axios/lib/core/InterceptorManager.js +107 -8
  65. package/node_modules/axios/lib/core/buildFullPath.js +41 -15
  66. package/node_modules/axios/lib/core/dispatchRequest.js +8 -2
  67. package/node_modules/axios/lib/core/mergeConfig.js +19 -4
  68. package/node_modules/axios/lib/core/methodList.js +17 -0
  69. package/node_modules/axios/lib/core/setFormDataHeaders.js +27 -0
  70. package/node_modules/axios/lib/defaults/index.js +2 -1
  71. package/node_modules/axios/lib/env/data.js +1 -1
  72. package/node_modules/axios/lib/helpers/Http2Sessions.js +2 -0
  73. package/node_modules/axios/lib/helpers/HttpStatusCode.js +12 -1
  74. package/node_modules/axios/lib/helpers/combineURLs.js +11 -3
  75. package/node_modules/axios/lib/helpers/composeSignals.js +12 -1
  76. package/node_modules/axios/lib/helpers/estimateDataURLDecodedBytes.js +115 -54
  77. package/node_modules/axios/lib/helpers/formDataToJSON.js +11 -5
  78. package/node_modules/axios/lib/helpers/fromDataURI.js +5 -4
  79. package/node_modules/axios/lib/helpers/normalizeURLForProtocolCheck.js +24 -0
  80. package/node_modules/axios/lib/helpers/parseHeaders.js +5 -3
  81. package/node_modules/axios/lib/helpers/progressEventReducer.js +4 -4
  82. package/node_modules/axios/lib/helpers/resolveConfig.js +5 -17
  83. package/node_modules/axios/lib/helpers/shouldBypassProxy.js +300 -5
  84. package/node_modules/axios/lib/helpers/throttle.js +3 -2
  85. package/node_modules/axios/lib/helpers/toFormData.js +17 -23
  86. package/node_modules/axios/lib/platform/node/classes/Buffer.js +11 -0
  87. package/node_modules/axios/lib/utils.js +151 -18
  88. package/node_modules/axios/package.json +21 -19
  89. package/node_modules/brace-expansion/dist/commonjs/index.js +69 -25
  90. package/node_modules/brace-expansion/dist/esm/index.js +68 -24
  91. package/node_modules/brace-expansion/package.json +2 -1
  92. package/node_modules/fast-uri/index.js +10 -6
  93. package/node_modules/fast-uri/package.json +1 -1
  94. package/package.json +1 -1
@@ -584,35 +584,20 @@ export class SessionManager {
584
584
  return this.recoverInterruptedSessionsWithPolicy({ kind: 'strict', stores });
585
585
  }
586
586
  async recoverInterruptedSessionsWithPolicy(policy, scope) {
587
- const profileEnabled = process.env.MAKA_STARTUP_PROFILE === '1';
588
- const profileTotals = new Map();
589
- const profileCalls = new Map();
590
- const measure = async (label, operation) => {
591
- if (!profileEnabled)
592
- return operation();
593
- const started = performance.now();
594
- try {
595
- return await operation();
596
- }
597
- finally {
598
- profileTotals.set(label, (profileTotals.get(label) ?? 0) + performance.now() - started);
599
- profileCalls.set(label, (profileCalls.get(label) ?? 0) + 1);
600
- }
601
- };
602
- const interrupted = (await measure('list-sessions', () => listSessionsForRecovery(this.deps.store, policy))).filter((session) => !session.isArchived && (scope === undefined || scope.has(session.id)));
603
- const recoveryCandidateSet = async (label, operation) => {
587
+ const interrupted = (await listSessionsForRecovery(this.deps.store, policy)).filter((session) => !session.isArchived && (scope === undefined || scope.has(session.id)));
588
+ const recoveryCandidateSet = async (operation) => {
604
589
  if (!operation)
605
590
  return undefined;
606
- const sessionIds = await measure(label, () => recoverOr(policy, operation, undefined));
591
+ const sessionIds = await recoverOr(policy, operation, undefined);
607
592
  return sessionIds ? new Set(sessionIds) : undefined;
608
593
  };
609
594
  const continuationAuthority = runtimeContinuationAuthority(this.deps.runtimeEventStore);
610
595
  const [shellRecoverySessions, unsettledContinuationClaims, planRecoverySessions] = await Promise.all([
611
- recoveryCandidateSet('shell-inventory', this.deps.shellRuns ? () => this.deps.shellRuns.listRecoverySessionIds() : undefined),
596
+ recoveryCandidateSet(this.deps.shellRuns ? () => this.deps.shellRuns.listRecoverySessionIds() : undefined),
612
597
  continuationAuthority?.listUnsettledContinuationClaimsForRecovery
613
- ? measure('continuation-inventory', () => recoverOr(policy, () => continuationAuthority.listUnsettledContinuationClaimsForRecovery(interrupted.map((session) => session.id)), undefined))
598
+ ? recoverOr(policy, () => continuationAuthority.listUnsettledContinuationClaimsForRecovery(interrupted.map((session) => session.id)), undefined)
614
599
  : Promise.resolve(undefined),
615
- recoveryCandidateSet('plan-inventory', this.deps.planStore?.listPlanRecoverySessionIds
600
+ recoveryCandidateSet(this.deps.planStore?.listPlanRecoverySessionIds
616
601
  ? () => this.deps.planStore.listPlanRecoverySessionIds()
617
602
  : undefined),
618
603
  ]);
@@ -643,7 +628,7 @@ export class SessionManager {
643
628
  }
644
629
  if (this.deps.shellRuns &&
645
630
  (!shellRecoverySessions || shellRecoverySessions.has(session.id))) {
646
- const recoveredShellRuns = await measure('shell', () => recoverOr(policy, () => this.deps.shellRuns.recoverOrphanedSession(session.id), 0));
631
+ const recoveredShellRuns = await recoverOr(policy, () => this.deps.shellRuns.recoverOrphanedSession(session.id), 0);
647
632
  if (recoveredShellRuns > 0)
648
633
  recovered.add(session.id);
649
634
  }
@@ -658,7 +643,7 @@ export class SessionManager {
658
643
  (!continuationClaimsBySession ||
659
644
  unsettledClaims?.some((state) => state.startEventId === undefined || state.startKind === 'claim_repair'))) {
660
645
  try {
661
- continuationClaimRecovered = await measure('continuation', () => this.recoverContinuationClaimsBeforeProvider(session.id, continuationAuthority, policy, unsettledClaims));
646
+ continuationClaimRecovered = await this.recoverContinuationClaimsBeforeProvider(session.id, continuationAuthority, policy, unsettledClaims);
662
647
  }
663
648
  catch (error) {
664
649
  if (policy.kind === 'strict')
@@ -676,9 +661,9 @@ export class SessionManager {
676
661
  // Plan events are the domain authority for whether there is a Plan
677
662
  // obligation at all. Runtime evidence is needed only to decide whether
678
663
  // an existing active Plan belongs to a pending handoff and must survive.
679
- const planState = await measure('plan-state', () => recoverOr(policy, () => this.deps.planStore.readState(session.id), undefined));
664
+ const planState = await recoverOr(policy, () => this.deps.planStore.readState(session.id), undefined);
680
665
  if (planState?.activeExecutionId) {
681
- const preservesHandoff = await measure('plan-handoff', () => recoverOr(policy, async () => {
666
+ const preservesHandoff = await recoverOr(policy, async () => {
682
667
  if (!continuationAuthority)
683
668
  return false;
684
669
  const latest = latestInvocation((await this.listInvocations(session.id)).filter((run) => isSessionInlineInvocation(run.opening)));
@@ -688,9 +673,9 @@ export class SessionManager {
688
673
  // Read after claim repair: an abandoned successor now has a real failure.
689
674
  return Boolean((await readLogicalRuntimeExecutionForRun(continuationAuthority, latest))
690
675
  ?.pendingHandoff);
691
- }, false));
676
+ }, false);
692
677
  if (!preservesHandoff) {
693
- const planRecovery = await measure('plan-interrupt', () => recoverOr(policy, () => this.deps.planStore.interruptActiveExecution(session.id, 'runtime_recovery'), null));
678
+ const planRecovery = await recoverOr(policy, () => this.deps.planStore.interruptActiveExecution(session.id, 'runtime_recovery'), null);
694
679
  if (planRecovery)
695
680
  recovered.add(session.id);
696
681
  }
@@ -711,7 +696,7 @@ export class SessionManager {
711
696
  this.deps.runtimeEventStore?.listInvocationRecoveryInventory &&
712
697
  runRecoveryQueue.length > 0) {
713
698
  try {
714
- const inventory = await measure('run-inventory', () => this.deps.runtimeEventStore.listInvocationRecoveryInventory(runRecoveryQueue.map(({ session }) => session.id)));
699
+ const inventory = await this.deps.runtimeEventStore.listInvocationRecoveryInventory(runRecoveryQueue.map(({ session }) => session.id));
715
700
  const mutable = new Map();
716
701
  for (const entry of inventory) {
717
702
  const entries = mutable.get(entry.sessionId) ?? [];
@@ -727,7 +712,7 @@ export class SessionManager {
727
712
  }
728
713
  for (const { session, continuationClaimRecovered, claimOwnedUnsettledRunIds, } of runRecoveryQueue) {
729
714
  if (this.deps.runStore) {
730
- const runRecovery = await measure('runs', () => recoverOr(policy, () => this.recoverAgentRunsFromLedger(session.id, policy, inventoryBySession ? (inventoryBySession.get(session.id) ?? []) : undefined, claimOwnedUnsettledRunIds), undefined));
715
+ const runRecovery = await recoverOr(policy, () => this.recoverAgentRunsFromLedger(session.id, policy, inventoryBySession ? (inventoryBySession.get(session.id) ?? []) : undefined, claimOwnedUnsettledRunIds), undefined);
731
716
  if (runRecovery?.hasLedger) {
732
717
  if (runRecovery.recovered || continuationClaimRecovered) {
733
718
  await recoverOr(policy, () => this.updateStatus(session.id, 'active'), undefined);
@@ -751,15 +736,6 @@ export class SessionManager {
751
736
  recovered.add(session.id);
752
737
  }
753
738
  }
754
- if (profileEnabled) {
755
- console.error(`[startup-profile] session-recovery ${JSON.stringify({
756
- sessions: interrupted.length,
757
- totals: Object.fromEntries([...profileTotals].map(([label, ms]) => [
758
- label,
759
- { calls: profileCalls.get(label) ?? 0, ms },
760
- ])),
761
- })}`);
762
- }
763
739
  return [...recovered];
764
740
  }
765
741
  async setSessionStatus(sessionId, status, blockedReason) {
@@ -3273,9 +3249,13 @@ export class SessionManager {
3273
3249
  return { hasLedger: true, recovered };
3274
3250
  }
3275
3251
  classifyRuntimeEventRecovery(inspected) {
3276
- if (!inspected.terminalRuntimeFact)
3252
+ const fact = inspected.terminalRuntimeFact;
3253
+ // A handed-off run ended on purpose; its continuation is owned by the
3254
+ // handoff claim saga, and generic restart repair must not manufacture an
3255
+ // outcome for it.
3256
+ if (!fact || fact.runStatus === 'handed_off')
3277
3257
  return undefined;
3278
- return runtimeTerminalFactToRecoveryDecision(inspected.invocation, inspected.terminalRuntimeFact);
3258
+ return runtimeTerminalFactToRecoveryDecision(inspected.invocation, fact);
3279
3259
  }
3280
3260
  async applyAgentRunRecovery(sessionId, decision, inspected, policy = { kind: 'best_effort' }, unsettledOperationIds = []) {
3281
3261
  if (!this.deps.runStore || !this.deps.runtimeEventStore)
@@ -3723,6 +3703,11 @@ function diagnosticRecoveryReason(diagnostic) {
3723
3703
  : 'agent_run_recovery';
3724
3704
  }
3725
3705
  function runtimeTerminalFactToRecoveryDecision(invocation, fact) {
3706
+ // classifyRuntimeEventRecovery filters handed-off runs before this; the
3707
+ // guard keeps the recovery status type honest at the boundary.
3708
+ if (fact.runStatus === 'handed_off') {
3709
+ throw new Error('A handed-off run has no recovery decision');
3710
+ }
3726
3711
  return {
3727
3712
  runId: fact.runId,
3728
3713
  turnId: fact.turnId,
@@ -18,7 +18,7 @@
18
18
  */
19
19
  import { AGENT_GRAPH_CLIENT_PROJECTION_SCHEMA_VERSION, AgentGraphClientProjectionConflictError, AgentGraphClientTerminalCursorError, } from '@maka/core/agent-graph-client-projection';
20
20
  import { decodeAgentGraphIntentClaim } from '@maka/core/agent-graph-control';
21
- import { readCommittedAgentGraphProjection, } from './stream-graph-projection.js';
21
+ import { AGENT_GRAPH_OUTPUT_PREVIEW_MAX_CODE_POINTS, readCommittedAgentGraphProjection, } from './stream-graph-projection.js';
22
22
  import { hydrateAgentGraphInputHandoffs, renderAgentGraphScheduledWorkPrompt, } from './stream-graph-handoff.js';
23
23
  import { buildAgentGraphReadinessSnapshot as buildReadinessSnapshot } from './stream-graph-readiness.js';
24
24
  import { reconcileAgentGraphSchedule, } from './stream-graph-schedule-reconcile.js';
@@ -30,6 +30,7 @@ import { isAgentGraphSupervisorMilestone } from './agent-graph-supervisor-wake.j
30
30
  import { buildAgentSwarmStatusTool, projectAgentSwarmStatus } from './agent-swarm-status-tool.js';
31
31
  const DEFAULT_MAX_NEW_ACTIVATIONS = 32;
32
32
  const MAX_CLIENT_PROJECTION_COMMIT_ATTEMPTS = 4;
33
+ const OUTPUT_DELTA_PROJECTION_INTERVAL_MS = 100;
33
34
  export class AgentGraphClientOperationError extends Error {
34
35
  code;
35
36
  name = 'AgentGraphClientOperationError';
@@ -606,7 +607,10 @@ export class AgentGraphCoordinator {
606
607
  if (event.event.type === 'complete' || event.event.type === 'abort') {
607
608
  driver.runtimeFailureRunIds.delete(event.claim.targetRunId);
608
609
  }
609
- if (isMaterializedGraphClientEvent(event.event.type)) {
610
+ if (event.event.type === 'text_delta') {
611
+ this.#queueOutputDelta(driver, event, activationHadError);
612
+ }
613
+ else if (isMaterializedGraphClientEvent(event.event.type)) {
610
614
  this.#queueClientProjectionUpdate(driver, async () => {
611
615
  const advancement = await this.#advanceClientProjection(driver, event, activationHadError);
612
616
  if (advancement &&
@@ -823,8 +827,67 @@ export class AgentGraphCoordinator {
823
827
  }
824
828
  });
825
829
  }
830
+ #queueOutputDelta(driver, event, activationHadError) {
831
+ const operatorId = event.claim.targetOperatorId;
832
+ const pending = driver.pendingOutputDeltas.get(operatorId);
833
+ if (pending && pending.event.event.type === 'text_delta' && event.event.type === 'text_delta') {
834
+ const previous = pending.event.event;
835
+ const current = event.event;
836
+ if (pending.event.claim.targetRunId === event.claim.targetRunId &&
837
+ previous.messageId === current.messageId &&
838
+ ((previous.startOffset === undefined && current.startOffset === undefined) ||
839
+ (previous.startOffset !== undefined &&
840
+ current.startOffset === previous.startOffset + previous.text.length))) {
841
+ const joined = Array.from(previous.text + current.text);
842
+ const truncated = joined.length > AGENT_GRAPH_OUTPUT_PREVIEW_MAX_CODE_POINTS + 1;
843
+ pending.event = {
844
+ ...event,
845
+ event: {
846
+ ...current,
847
+ text: truncated
848
+ ? joined.slice(-(AGENT_GRAPH_OUTPUT_PREVIEW_MAX_CODE_POINTS + 1)).join('')
849
+ : joined.join(''),
850
+ startOffset: truncated ? undefined : previous.startOffset,
851
+ },
852
+ };
853
+ }
854
+ else {
855
+ pending.event = event;
856
+ pending.sampleStartedAt = event.event.ts;
857
+ }
858
+ pending.activationHadError ||= activationHadError;
859
+ return;
860
+ }
861
+ const next = { event, activationHadError, sampleStartedAt: event.event.ts };
862
+ driver.pendingOutputDeltas.set(operatorId, next);
863
+ this.#scheduleOutputProjectionFlush(driver);
864
+ }
865
+ #scheduleOutputProjectionFlush(driver) {
866
+ if (driver.outputProjectionFlushScheduled)
867
+ return;
868
+ driver.outputProjectionFlushScheduled = true;
869
+ this.#queueClientProjectionUpdate(driver, async () => {
870
+ try {
871
+ await new Promise((resolve) => {
872
+ setTimeout(resolve, OUTPUT_DELTA_PROJECTION_INTERVAL_MS);
873
+ });
874
+ const pending = [...driver.pendingOutputDeltas.values()];
875
+ driver.pendingOutputDeltas.clear();
876
+ for (const delta of pending) {
877
+ await this.#advanceClientProjection(driver, delta.event, delta.activationHadError, delta.sampleStartedAt);
878
+ }
879
+ }
880
+ finally {
881
+ driver.outputProjectionFlushScheduled = false;
882
+ if (driver.pendingOutputDeltas.size > 0) {
883
+ this.#scheduleOutputProjectionFlush(driver);
884
+ }
885
+ }
886
+ });
887
+ }
826
888
  async #waitForClientProjectionUpdates(driver) {
827
- await driver.clientProjectionTask?.catch(() => {
889
+ const task = driver.clientProjectionTask;
890
+ await task?.catch(() => {
828
891
  // A best-effort repair or later durable observation may repair this
829
892
  // derived read side; graph authority never depends on it.
830
893
  });
@@ -840,7 +903,7 @@ export class AgentGraphCoordinator {
840
903
  await notify(this.#input.onError, driver.rootSessionId, error);
841
904
  }
842
905
  }
843
- async #advanceClientProjection(driver, event, activationHadError) {
906
+ async #advanceClientProjection(driver, event, activationHadError, sampleStartedAt) {
844
907
  for (let attempt = 0; attempt < MAX_CLIENT_PROJECTION_COMMIT_ATTEMPTS; attempt += 1) {
845
908
  const graph = await this.#input.controlStore.readAgentGraphClientProjection(driver.graphId);
846
909
  const operator = await this.#input.controlStore.readAgentGraphClientOperatorProjection(driver.graphId, event.claim.targetOperatorId);
@@ -858,7 +921,7 @@ export class AgentGraphCoordinator {
858
921
  operatorId: event.claim.targetOperatorId,
859
922
  snapshotVersion: operator.snapshotVersion,
860
923
  });
861
- const advanced = advanceMaterializedAgentGraphClientProjection(snapshot, inspection, event, activationHadError);
924
+ const advanced = advanceMaterializedAgentGraphClientProjection(snapshot, inspection, event, activationHadError, sampleStartedAt);
862
925
  if (!advanced)
863
926
  return undefined;
864
927
  try {
@@ -881,13 +944,15 @@ export class AgentGraphCoordinator {
881
944
  // stop race). Only the authoritative RuntimeEvent fold populates the
882
945
  // immutable terminal-history table.
883
946
  terminalActivities: [],
884
- activityRecords: [
885
- {
886
- recordId: advanced.activity.recordId,
887
- eventTime: advanced.activity.eventTime,
888
- },
889
- ],
890
- incrementalRecordId: advanced.activity.recordId,
947
+ activityRecords: advanced.activity
948
+ ? [
949
+ {
950
+ recordId: advanced.activity.recordId,
951
+ eventTime: advanced.activity.eventTime,
952
+ },
953
+ ]
954
+ : [],
955
+ ...(advanced.activity ? { incrementalRecordId: advanced.activity.recordId } : {}),
891
956
  });
892
957
  if (committed.snapshotVersion === advanced.snapshot.snapshotVersion) {
893
958
  this.#notifyClientChanged(driver, 'runtime_activity');
@@ -1151,6 +1216,8 @@ export class AgentGraphCoordinator {
1151
1216
  closed: false,
1152
1217
  reconciliationReaders: 0,
1153
1218
  clientProjectionDirty: false,
1219
+ outputProjectionFlushScheduled: false,
1220
+ pendingOutputDeltas: new Map(),
1154
1221
  runtimeFailureRunIds: new Set(),
1155
1222
  yieldWaiters: new Set(),
1156
1223
  };
@@ -1327,7 +1394,6 @@ function requireRootSessionId(rootSessionId) {
1327
1394
  }
1328
1395
  function isMaterializedGraphClientEvent(type) {
1329
1396
  return ![
1330
- 'text_delta',
1331
1397
  'thinking_delta',
1332
1398
  'tool_output_delta',
1333
1399
  'tool_progress',
@@ -21,6 +21,7 @@ import { isSessionInlineInvocation } from '@maka/core/runtime-invocation';
21
21
  import { stableHash, stableStringify } from './request-shape.js';
22
22
  import { compareAgentGraphIdentity } from './stream-graph-identity.js';
23
23
  export const AGENT_GRAPH_RECORD_SCHEMA_VERSION = 1;
24
+ export const AGENT_GRAPH_OUTPUT_PREVIEW_MAX_CODE_POINTS = 280;
24
25
  export const AGENT_GRAPH_RECORD_FACETS = [
25
26
  'message',
26
27
  'thinking',
@@ -79,9 +80,72 @@ export async function readCommittedAgentGraphProjectionWithRuns(input) {
79
80
  records: projected.records,
80
81
  supervisorMetaStream: projected.supervisorMetaStream,
81
82
  state,
83
+ operatorOutputs: projectOperatorOutputs(streams),
82
84
  },
83
85
  };
84
86
  }
87
+ function projectOperatorOutputs(streams) {
88
+ const latestByOperator = new Map();
89
+ for (const stream of streams) {
90
+ const current = latestByOperator.get(stream.operator.operatorId);
91
+ if (!current ||
92
+ stream.run.openedAt > current.run.openedAt ||
93
+ (stream.run.openedAt === current.run.openedAt &&
94
+ compareAgentGraphIdentity(stream.run.runId, current.run.runId) > 0)) {
95
+ latestByOperator.set(stream.operator.operatorId, stream);
96
+ }
97
+ }
98
+ return [...latestByOperator.values()]
99
+ .map(projectOperatorOutput)
100
+ .filter((output) => output !== undefined)
101
+ .sort((left, right) => compareAgentGraphIdentity(left.operatorId, right.operatorId));
102
+ }
103
+ function projectOperatorOutput(stream) {
104
+ const orderedEvents = stream.events
105
+ .slice()
106
+ .sort((left, right) => left.ts - right.ts || compareAgentGraphIdentity(left.id, right.id));
107
+ const events = orderedEvents.filter((event) => !event.partial);
108
+ const textEvents = events.filter((event) => event.role === 'model' && event.content?.kind === 'text');
109
+ const latestText = textEvents.at(-1);
110
+ if (!latestText || latestText.content?.kind !== 'text')
111
+ return undefined;
112
+ const preview = boundOutputPreview(latestText.content.text);
113
+ if (!preview.text)
114
+ return undefined;
115
+ const usageEvents = events.filter((event) => event.actions?.tokenUsage !== undefined);
116
+ const latestUsage = usageEvents.at(-1);
117
+ const outputTokens = latestUsage?.actions?.tokenUsage?.output;
118
+ const usageEndedAt = latestUsage?.ts;
119
+ const sampleStartedAt = orderedEvents.find((event) => event.role === 'model' && event.content?.kind === 'text').ts;
120
+ const sampleDurationMs = outputTokens !== undefined && outputTokens > 0 && usageEndedAt !== undefined
121
+ ? Math.max(0, usageEndedAt - sampleStartedAt)
122
+ : undefined;
123
+ const tokensPerSecond = outputTokens !== undefined && sampleDurationMs !== undefined && sampleDurationMs > 0
124
+ ? Math.round((outputTokens * 10_000) / sampleDurationMs) / 10
125
+ : undefined;
126
+ return {
127
+ operatorId: stream.operator.operatorId,
128
+ activationId: stream.run.runId,
129
+ preview: preview.text,
130
+ previewTruncated: preview.truncated,
131
+ phase: runtimeInvocationOutcome(stream.run) ? 'completed' : 'streaming',
132
+ previewUpdatedAt: latestText.ts,
133
+ sourceEventId: latestText.id,
134
+ ...(latestText.refs?.providerEventId ? { messageId: latestText.refs.providerEventId } : {}),
135
+ sampleStartedAt,
136
+ ...(outputTokens !== undefined ? { outputTokens } : {}),
137
+ ...(sampleDurationMs !== undefined ? { sampleDurationMs } : {}),
138
+ ...(tokensPerSecond !== undefined ? { tokensPerSecond } : {}),
139
+ };
140
+ }
141
+ function boundOutputPreview(text) {
142
+ const codePoints = Array.from(text);
143
+ if (codePoints.length <= AGENT_GRAPH_OUTPUT_PREVIEW_MAX_CODE_POINTS) {
144
+ return { text: codePoints.join(''), truncated: false };
145
+ }
146
+ const visible = codePoints.slice(-AGENT_GRAPH_OUTPUT_PREVIEW_MAX_CODE_POINTS);
147
+ return { text: visible.join(''), truncated: true };
148
+ }
85
149
  export function projectAgentGraphRecords(input) {
86
150
  const operators = uniqueBindings(input.streams.map((stream) => stream.operator));
87
151
  assertGraphIdentity(input.graphId, operators);
@@ -17,6 +17,7 @@
17
17
  * under the License.
18
18
  */
19
19
  import { failureClassFromCompleteStopReason } from '@maka/core/events';
20
+ import { AGENT_GRAPH_OUTPUT_PREVIEW_MAX_CODE_POINTS } from './stream-graph-projection.js';
20
21
  import { projectAgentGraphSchedule, } from './stream-graph-supervisor-tools.js';
21
22
  import { stableHash } from './request-shape.js';
22
23
  export const AGENT_GRAPH_CLIENT_SNAPSHOT_SCHEMA_VERSION = 1;
@@ -43,8 +44,9 @@ const MAX_INSTRUCTION_PREVIEW_CHARS = 500;
43
44
  /**
44
45
  * Durable, bounded graph-facing projection for untrusted presentation clients.
45
46
  *
46
- * Payloads remain in Session/Runtime stores. This surface carries only
47
- * identities, lifecycle state, wait reasons, and bounded instruction previews.
47
+ * Full payloads remain in Session/Runtime stores. This surface carries only
48
+ * identities, lifecycle state, wait reasons, bounded instruction/output
49
+ * previews, and provider-reported usage-derived throughput.
48
50
  */
49
51
  export function buildAgentGraphClientSnapshot(input, options = {}) {
50
52
  const model = buildReadModel(input);
@@ -64,9 +66,10 @@ export function materializeAgentGraphClientProjection(input) {
64
66
  * Advance the bounded materialized client view from one already-durable
65
67
  * SessionEvent without replaying the RuntimeEvent ledger.
66
68
  */
67
- export function advanceMaterializedAgentGraphClientProjection(snapshotInput, inspectionInput, runtime, activationHadError) {
69
+ export function advanceMaterializedAgentGraphClientProjection(snapshotInput, inspectionInput, runtime, activationHadError, sampleStartedAt) {
68
70
  const projected = projectClientSessionEvent(runtime.event, activationHadError);
69
- if (!projected)
71
+ const output = advanceClientOperatorOutput(inspectionInput.operator.output, runtime.event, runtime.claim.targetRunId, ['completed', 'failed', 'aborted', 'cancelled'].includes(inspectionInput.operator.status), sampleStartedAt, inspectionInput.operator.currentActivation?.activationId);
72
+ if (!projected && !output)
70
73
  return undefined;
71
74
  if (snapshotInput.graphId !== runtime.intent.graphId ||
72
75
  inspectionInput.graphId !== runtime.intent.graphId ||
@@ -74,21 +77,34 @@ export function advanceMaterializedAgentGraphClientProjection(snapshotInput, ins
74
77
  inspectionInput.operator.childSessionId !== runtime.claim.targetSessionId) {
75
78
  throw new Error('Agent graph runtime activity does not match its materialized projection');
76
79
  }
77
- const activity = {
78
- recordId: clientRuntimeRecordId(runtime),
79
- operatorId: runtime.claim.targetOperatorId,
80
- activationId: runtime.claim.targetRunId,
81
- eventTime: runtime.event.ts,
82
- facets: projected.facets,
83
- signals: projected.signals,
84
- run: {
85
- sessionId: runtime.claim.targetSessionId,
86
- agentRunId: runtime.claim.targetRunId,
87
- turnId: runtime.claim.targetTurnId,
88
- },
89
- };
80
+ const activity = projected
81
+ ? {
82
+ recordId: clientRuntimeRecordId(runtime),
83
+ operatorId: runtime.claim.targetOperatorId,
84
+ activationId: runtime.claim.targetRunId,
85
+ eventTime: runtime.event.ts,
86
+ facets: projected.facets,
87
+ signals: projected.signals,
88
+ run: {
89
+ sessionId: runtime.claim.targetSessionId,
90
+ agentRunId: runtime.claim.targetRunId,
91
+ turnId: runtime.claim.targetTurnId,
92
+ },
93
+ }
94
+ : undefined;
90
95
  const snapshot = structuredClone(snapshotInput);
91
96
  const inspection = structuredClone(inspectionInput);
97
+ if (output)
98
+ inspection.operator.output = output;
99
+ const visibleOperatorIndex = snapshot.operators.findIndex((operator) => operator.operatorId === inspection.operator.operatorId);
100
+ if (visibleOperatorIndex >= 0 && output) {
101
+ snapshot.operators[visibleOperatorIndex] = structuredClone(inspection.operator);
102
+ }
103
+ if (!projected || !activity) {
104
+ snapshot.snapshotVersion = clientSnapshotVersion(snapshot);
105
+ inspection.snapshotVersion = snapshot.snapshotVersion;
106
+ return { snapshot, operator: inspection };
107
+ }
92
108
  if (snapshot.recentActivity.some((record) => record.recordId === activity.recordId) ||
93
109
  inspection.recentRecords.some((record) => record.recordId === activity.recordId)) {
94
110
  return undefined;
@@ -178,7 +194,6 @@ export function advanceMaterializedAgentGraphClientProjection(snapshotInput, ins
178
194
  .sort(compareClientActivity)
179
195
  .slice(-MAX_OPERATOR_INSPECTION_RECORDS);
180
196
  inspection.omitted.records = Math.max(0, inspectionRecordCount - inspection.recentRecords.length);
181
- const visibleOperatorIndex = snapshot.operators.findIndex((operator) => operator.operatorId === inspection.operator.operatorId);
182
197
  if (visibleOperatorIndex >= 0) {
183
198
  snapshot.operators[visibleOperatorIndex] = structuredClone(inspection.operator);
184
199
  }
@@ -320,6 +335,10 @@ function buildReadModel(input) {
320
335
  .map(clientActivity)
321
336
  .sort(compareClientActivity);
322
337
  const work = schedule.work.map(clientWork);
338
+ const outputByOperator = new Map((input.observation.projection.operatorOutputs ?? []).map((output) => [
339
+ output.operatorId,
340
+ output,
341
+ ]));
323
342
  const operators = input.observation.projection.operators.map((binding) => {
324
343
  const provision = provisionByOperator.get(binding.operatorId);
325
344
  if (!provision || provision.targetSessionId !== binding.sessionId) {
@@ -336,6 +355,7 @@ function buildReadModel(input) {
336
355
  const readiness = allReadiness.slice(0, MAX_OPERATOR_READINESS);
337
356
  const state = input.observation.projection.state.operators[binding.operatorId];
338
357
  const currentActivation = state?.activations[state.currentActivationId];
358
+ const output = outputByOperator.get(binding.operatorId);
339
359
  const currentClaim = currentActivation
340
360
  ? claims.find((claim) => claim.operatorId === binding.operatorId &&
341
361
  claim.run.agentRunId === currentActivation.agentRunId)
@@ -392,6 +412,9 @@ function buildReadModel(input) {
392
412
  },
393
413
  }
394
414
  : {}),
415
+ ...(currentActivation && output?.activationId === currentActivation.activationId
416
+ ? { output: clientOperatorOutput(output) }
417
+ : {}),
395
418
  };
396
419
  });
397
420
  const stoppedTargets = schedule.stoppedTargets.map(clientStoppedTarget);
@@ -432,6 +455,98 @@ function buildReadModel(input) {
432
455
  ])),
433
456
  };
434
457
  }
458
+ function clientOperatorOutput(output) {
459
+ return {
460
+ activationId: output.activationId,
461
+ preview: output.preview,
462
+ previewTruncated: output.previewTruncated,
463
+ phase: output.phase,
464
+ previewUpdatedAt: output.previewUpdatedAt,
465
+ sourceEventId: output.sourceEventId,
466
+ ...(output.messageId ? { messageId: output.messageId } : {}),
467
+ sampleStartedAt: output.sampleStartedAt,
468
+ ...(output.outputTokens !== undefined ? { outputTokens: output.outputTokens } : {}),
469
+ ...(output.sampleDurationMs !== undefined ? { sampleDurationMs: output.sampleDurationMs } : {}),
470
+ ...(output.tokensPerSecond !== undefined ? { tokensPerSecond: output.tokensPerSecond } : {}),
471
+ };
472
+ }
473
+ function advanceClientOperatorOutput(current, event, activationId, operatorSettled, sampleStartedAt, currentActivationId) {
474
+ if (currentActivationId && currentActivationId !== activationId)
475
+ return undefined;
476
+ if (current?.activationId === activationId && current.sourceEventId === event.id) {
477
+ return undefined;
478
+ }
479
+ const existing = current?.activationId === activationId ? current : undefined;
480
+ if (existing &&
481
+ (event.type === 'text_delta' || event.type === 'text_complete') &&
482
+ event.ts < existing.previewUpdatedAt) {
483
+ return undefined;
484
+ }
485
+ if (event.type === 'text_delta' || event.type === 'text_complete') {
486
+ const sameMessage = existing?.messageId === event.messageId;
487
+ if (event.type === 'text_delta' && sameMessage && existing.phase === 'completed') {
488
+ return undefined;
489
+ }
490
+ const append = event.type === 'text_delta' && sameMessage;
491
+ const discontinuous = event.type === 'text_delta' &&
492
+ event.startOffset !== undefined &&
493
+ event.startOffset >
494
+ (sameMessage && existing && !existing.previewTruncated ? existing.preview.length : 0);
495
+ const text = append ? foldClientOutputDelta(existing, event) : event.text;
496
+ const preview = boundClientOutputPreview(text);
497
+ if (!preview.text)
498
+ return undefined;
499
+ return {
500
+ activationId,
501
+ preview: preview.text,
502
+ previewTruncated: preview.truncated || discontinuous || (append && existing.previewTruncated),
503
+ phase: operatorSettled || existing?.phase === 'completed' ? 'completed' : 'streaming',
504
+ previewUpdatedAt: event.ts,
505
+ sourceEventId: event.id,
506
+ ...(event.messageId ? { messageId: event.messageId } : {}),
507
+ sampleStartedAt: existing?.sampleStartedAt ?? sampleStartedAt ?? event.ts,
508
+ ...(existing?.outputTokens !== undefined ? { outputTokens: existing.outputTokens } : {}),
509
+ ...(existing?.sampleDurationMs !== undefined
510
+ ? { sampleDurationMs: existing.sampleDurationMs }
511
+ : {}),
512
+ ...(existing?.tokensPerSecond !== undefined
513
+ ? { tokensPerSecond: existing.tokensPerSecond }
514
+ : {}),
515
+ };
516
+ }
517
+ if (event.type === 'token_usage' && existing) {
518
+ const sampleDurationMs = Math.max(0, event.ts - existing.sampleStartedAt);
519
+ return {
520
+ ...existing,
521
+ outputTokens: event.output,
522
+ sampleDurationMs,
523
+ ...(sampleDurationMs > 0
524
+ ? { tokensPerSecond: Math.round((event.output * 10_000) / sampleDurationMs) / 10 }
525
+ : {}),
526
+ };
527
+ }
528
+ if ((event.type === 'complete' || event.type === 'abort') && existing) {
529
+ return { ...existing, phase: 'completed' };
530
+ }
531
+ return undefined;
532
+ }
533
+ function foldClientOutputDelta(existing, event) {
534
+ if (event.startOffset === undefined || existing.previewTruncated) {
535
+ return `${existing.preview}${event.text}`;
536
+ }
537
+ if (event.startOffset <= existing.preview.length) {
538
+ return `${existing.preview.slice(0, event.startOffset)}${event.text}`;
539
+ }
540
+ return event.text;
541
+ }
542
+ function boundClientOutputPreview(text) {
543
+ const codePoints = Array.from(text);
544
+ if (codePoints.length <= AGENT_GRAPH_OUTPUT_PREVIEW_MAX_CODE_POINTS) {
545
+ return { text: codePoints.join(''), truncated: false };
546
+ }
547
+ const visible = codePoints.slice(-AGENT_GRAPH_OUTPUT_PREVIEW_MAX_CODE_POINTS);
548
+ return { text: visible.join(''), truncated: true };
549
+ }
435
550
  function operatorStatus(runtimeStatus, readiness, currentRecord) {
436
551
  if (runtimeStatus === 'running') {
437
552
  return currentRecord?.signals.some((signal) => signal.kind === 'attention')
@@ -884,8 +999,11 @@ function compareClientActivity(a, b) {
884
999
  return compareActivityKey(a.eventTime, a.recordId, b.eventTime, b.recordId);
885
1000
  }
886
1001
  function clientSnapshotVersion(snapshot) {
887
- const { snapshotVersion: _snapshotVersion, terminalHistory: _terminalHistory, ...boundedContent } = snapshot;
888
- return stableHash(boundedContent);
1002
+ const { snapshotVersion: _snapshotVersion, terminalHistory: _terminalHistory, operators, ...boundedContent } = snapshot;
1003
+ return stableHash({
1004
+ ...boundedContent,
1005
+ operators: operators.map(({ output: _output, ...operator }) => operator),
1006
+ });
889
1007
  }
890
1008
  function normalizeReconciliationFailures(failures) {
891
1009
  const byWorkId = new Map();
@@ -94,6 +94,15 @@ export function toolAvailabilityHash(config) {
94
94
  function compareExactString(left, right) {
95
95
  return left < right ? -1 : left > right ? 1 : 0;
96
96
  }
97
+ function searchableToolNames(tools, config) {
98
+ if (config === undefined)
99
+ return new Set();
100
+ return new Set(tools.map(({ name }) => name).filter((name) => !DIRECT_TOOL_NAMES.has(name)));
101
+ }
102
+ /** Returns the synthetic connector names injected by ToolAvailabilityRuntime. */
103
+ export function toolAvailabilityConnectorNames(tools, config) {
104
+ return searchableToolNames(tools, config).size > 0 ? [TOOL_SEARCH_NAME] : [];
105
+ }
97
106
  /**
98
107
  * Immutable, backend-scoped bound-tool inventory and MiniSearch index.
99
108
  *
@@ -122,9 +131,7 @@ export class ToolAvailabilityRuntime {
122
131
  this.toolsByName = new Map(tools.map((tool) => [tool.name, tool]));
123
132
  this.activationKeysByName = new Map(tools.map((tool) => [tool.name, toolActivationKey(tool)]));
124
133
  const known = new Set(this.toolsByName.keys());
125
- const searchable = config === undefined
126
- ? new Set()
127
- : new Set([...known].filter((name) => !DIRECT_TOOL_NAMES.has(name)));
134
+ const searchable = searchableToolNames(this.tools, config);
128
135
  const claimed = new Set();
129
136
  const groups = [];
130
137
  for (const group of config?.groups ?? []) {
@@ -74,6 +74,21 @@ export async function runExecutionCandidateEntry(argv, entrypointUrl, hooks = {}
74
74
  process.exit(candidateStartupFailureExitCode(failure));
75
75
  }
76
76
  if (result.kind === 'loser') {
77
+ // Losing the launch election is a normal outcome the replacement loop
78
+ // relies on, but a silent exit left no trace on disk — during replacement
79
+ // storms every loss was invisible (issue #5843). Leave the same startup
80
+ // diagnostic the failure paths write before exiting.
81
+ if (rootId && startupAttemptId) {
82
+ await import('./control/startup-diagnostic.js')
83
+ .then(({ writeCandidateStartupDiagnostic }) => writeCandidateStartupDiagnostic({
84
+ rootId,
85
+ startupAttemptId,
86
+ failure: { reason: 'launch_election_lost' },
87
+ error: new Error('Lost the Runtime Host launch election: another candidate owns this workspace'),
88
+ logs: runtimeHostLogBuffer.snapshot(),
89
+ }))
90
+ .catch(() => undefined);
91
+ }
77
92
  await launchOwnerGuard?.dispose();
78
93
  process.exit(2);
79
94
  }