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.
- package/LICENSE +1 -1
- package/THIRD_PARTY_NOTICES.txt +3 -3
- package/dist/pi-tui-runner.js +26 -29
- package/dist/tui-copy-catalog.js +9 -9
- package/native/runtime-host-peer/prebuilds/darwin-arm64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/linux-arm64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/linux-x64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-peer/prebuilds/win32-x64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@maka/core/dist/settings.js +2 -2
- package/node_modules/@maka/core/dist/tool-ledger-scanner.js +53 -150
- package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +8 -1
- package/node_modules/@maka/runtime/dist/conversation-copy.js +6 -0
- package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +17 -0
- package/node_modules/@maka/runtime/dist/session-manager.js +25 -40
- package/node_modules/@maka/runtime/dist/stream-graph-coordinator.js +79 -13
- package/node_modules/@maka/runtime/dist/stream-graph-projection.js +64 -0
- package/node_modules/@maka/runtime/dist/stream-graph-read-model.js +138 -20
- package/node_modules/@maka/runtime/dist/tool-availability.js +10 -3
- package/node_modules/@maka/runtime-host/dist/candidate-entry.js +15 -0
- package/node_modules/@maka/runtime-host/dist/candidate-startup-failure.js +5 -0
- package/node_modules/@maka/runtime-host/dist/client/session-catalog-summary.js +1 -0
- package/node_modules/@maka/runtime-host/dist/client/startup-error.js +5 -0
- package/node_modules/@maka/runtime-host/dist/protocol/agent-graph.js +46 -1
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +14 -1
- package/node_modules/@maka/runtime-host/dist/protocol/operations.js +5 -1
- package/node_modules/@maka/runtime-host/dist/protocol/session-catalog.js +12 -0
- package/node_modules/@maka/runtime-host/dist/protocol/session-retirement.js +46 -10
- package/node_modules/@maka/runtime-host/dist/protocol/storage-usage.js +155 -0
- package/node_modules/@maka/runtime-host/dist/server/agent-graph-coordinator.js +23 -0
- package/node_modules/@maka/runtime-host/dist/server/canonical-turn-snapshot.js +5 -0
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +27 -6
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +2 -0
- package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +6 -0
- package/node_modules/@maka/runtime-host/dist/server/session-retirement-coordinator.js +112 -36
- package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +27 -1
- package/node_modules/@maka/runtime-host/dist/server/storage-usage-coordinator.js +67 -0
- package/node_modules/@maka/runtime-host/dist/server/workhub-inspection-tool.js +208 -0
- package/node_modules/@maka/storage/dist/git-worktree-child-executor.js +3 -1
- package/node_modules/@maka/storage/dist/operational-state-store.js +0 -13
- package/node_modules/@maka/storage/dist/session-store.js +9 -6
- package/node_modules/@maka/storage/dist/sqlite-file-set.js +22 -0
- package/node_modules/@maka/storage/dist/sqlite-long-term-memory-store.js +1 -3
- package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +10 -260
- package/node_modules/@maka/storage/dist/sqlite-session-catalog-query.js +1 -0
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-schema.js +18 -6
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +28 -6
- package/node_modules/@maka/storage/dist/storage-footprint.js +239 -0
- package/node_modules/@maka/storage/dist/storage-writer-composition.js +10 -1
- package/node_modules/axios/CHANGELOG.md +62 -0
- package/node_modules/axios/README.md +297 -5
- package/node_modules/axios/dist/axios.js +728 -170
- package/node_modules/axios/dist/axios.min.js +3 -3
- package/node_modules/axios/dist/browser/axios.cjs +871 -188
- package/node_modules/axios/dist/esm/axios.js +871 -188
- package/node_modules/axios/dist/esm/axios.min.js +2 -2
- package/node_modules/axios/dist/node/axios.cjs +1037 -225
- package/node_modules/axios/lib/adapters/fetch.js +64 -11
- package/node_modules/axios/lib/adapters/http.js +140 -67
- package/node_modules/axios/lib/adapters/xhr.js +52 -3
- package/node_modules/axios/lib/core/Axios.js +43 -20
- package/node_modules/axios/lib/core/AxiosError.js +33 -2
- package/node_modules/axios/lib/core/AxiosHeaders.js +124 -1
- package/node_modules/axios/lib/core/InterceptorManager.js +107 -8
- package/node_modules/axios/lib/core/buildFullPath.js +41 -15
- package/node_modules/axios/lib/core/dispatchRequest.js +8 -2
- package/node_modules/axios/lib/core/mergeConfig.js +19 -4
- package/node_modules/axios/lib/core/methodList.js +17 -0
- package/node_modules/axios/lib/core/setFormDataHeaders.js +27 -0
- package/node_modules/axios/lib/defaults/index.js +2 -1
- package/node_modules/axios/lib/env/data.js +1 -1
- package/node_modules/axios/lib/helpers/Http2Sessions.js +2 -0
- package/node_modules/axios/lib/helpers/HttpStatusCode.js +12 -1
- package/node_modules/axios/lib/helpers/combineURLs.js +11 -3
- package/node_modules/axios/lib/helpers/composeSignals.js +12 -1
- package/node_modules/axios/lib/helpers/estimateDataURLDecodedBytes.js +115 -54
- package/node_modules/axios/lib/helpers/formDataToJSON.js +11 -5
- package/node_modules/axios/lib/helpers/fromDataURI.js +5 -4
- package/node_modules/axios/lib/helpers/normalizeURLForProtocolCheck.js +24 -0
- package/node_modules/axios/lib/helpers/parseHeaders.js +5 -3
- package/node_modules/axios/lib/helpers/progressEventReducer.js +4 -4
- package/node_modules/axios/lib/helpers/resolveConfig.js +5 -17
- package/node_modules/axios/lib/helpers/shouldBypassProxy.js +300 -5
- package/node_modules/axios/lib/helpers/throttle.js +3 -2
- package/node_modules/axios/lib/helpers/toFormData.js +17 -23
- package/node_modules/axios/lib/platform/node/classes/Buffer.js +11 -0
- package/node_modules/axios/lib/utils.js +151 -18
- package/node_modules/axios/package.json +21 -19
- package/node_modules/brace-expansion/dist/commonjs/index.js +69 -25
- package/node_modules/brace-expansion/dist/esm/index.js +68 -24
- package/node_modules/brace-expansion/package.json +2 -1
- package/node_modules/fast-uri/index.js +10 -6
- package/node_modules/fast-uri/package.json +1 -1
- 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
|
|
588
|
-
const
|
|
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
|
|
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(
|
|
596
|
+
recoveryCandidateSet(this.deps.shellRuns ? () => this.deps.shellRuns.listRecoverySessionIds() : undefined),
|
|
612
597
|
continuationAuthority?.listUnsettledContinuationClaimsForRecovery
|
|
613
|
-
?
|
|
598
|
+
? recoverOr(policy, () => continuationAuthority.listUnsettledContinuationClaimsForRecovery(interrupted.map((session) => session.id)), undefined)
|
|
614
599
|
: Promise.resolve(undefined),
|
|
615
|
-
recoveryCandidateSet(
|
|
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
|
|
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
|
|
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
|
|
664
|
+
const planState = await recoverOr(policy, () => this.deps.planStore.readState(session.id), undefined);
|
|
680
665
|
if (planState?.activeExecutionId) {
|
|
681
|
-
const preservesHandoff = await
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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,
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
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
|
-
*
|
|
47
|
-
* identities, lifecycle state, wait reasons,
|
|
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
|
-
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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(
|
|
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
|
|
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
|
}
|