maka-agent 0.2.0-dev.46.20260922 → 0.2.0-dev.48.20260923
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/DISCLAIMER-WIP +6 -1
- package/README.md +6 -1
- package/dist/acp/maka-acp-agent.js +1 -1
- package/dist/acp/session-mcp.js +212 -0
- package/dist/acp/session-registry.js +52 -8
- package/dist/acp/stdio-server.js +3 -0
- package/dist/cli-core.js +1 -1
- package/dist/mcp-capability-provider.js +6 -1
- package/dist/mcp-capability-publication.js +139 -0
- package/dist/pi-tui-layout.js +46 -17
- package/dist/pi-tui-pickers.js +25 -14
- package/dist/pi-tui-runner.js +24 -15
- package/dist/runtime-host-capability-provider-command.js +25 -6
- package/dist/runtime-host-service-manager.js +26 -2
- package/dist/tui-context-refresh.js +15 -26
- package/dist/tui-mcp-control.js +66 -112
- package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@maka/acp-executor-plugin/dist/acp-errors.js +26 -0
- package/node_modules/@maka/acp-executor-plugin/dist/acp-filesystem.js +103 -0
- package/node_modules/@maka/acp-executor-plugin/dist/acp-process.js +119 -0
- package/node_modules/@maka/acp-executor-plugin/dist/acp-projection.js +146 -0
- package/node_modules/@maka/acp-executor-plugin/dist/index.js +653 -0
- package/node_modules/@maka/acp-executor-plugin/dist/plugin.mjs +24446 -0
- package/node_modules/@maka/acp-executor-plugin/package.json +15 -0
- package/node_modules/@maka/antigravity-acp-plugin/dist/index.js +88 -0
- package/node_modules/@maka/antigravity-acp-plugin/dist/model-presentation.js +53 -0
- package/node_modules/@maka/antigravity-acp-plugin/dist/plugin.mjs +115 -0
- package/node_modules/@maka/antigravity-acp-plugin/package.json +15 -0
- package/node_modules/@maka/core/dist/client-capability-grant.js +2 -2
- package/node_modules/@maka/core/dist/executor-catalog.js +106 -0
- package/node_modules/@maka/core/dist/mcp.js +4 -1
- package/node_modules/@maka/core/dist/model-metadata.js +28 -1
- package/node_modules/@maka/core/dist/model-thinking.js +36 -1
- package/node_modules/@maka/core/dist/provider-registry.js +0 -1
- package/node_modules/@maka/core/dist/refresh-read-coordinator.js +62 -0
- package/node_modules/@maka/core/dist/runtime-event-store.js +25 -0
- package/node_modules/@maka/core/dist/runtime-invocation.js +5 -0
- package/node_modules/@maka/core/dist/runtime-policy/connection-catalog-codec.js +4 -0
- package/node_modules/@maka/core/dist/runtime-policy/credential-vault-codec.js +6 -0
- package/node_modules/@maka/core/dist/runtime-policy/policy-codec.js +21 -0
- package/node_modules/@maka/core/dist/session.js +19 -1
- package/node_modules/@maka/core/dist/settings.js +3 -0
- package/node_modules/@maka/core/dist/tool-ledger-scanner.js +365 -104
- package/node_modules/@maka/core/dist/turn-origin.js +14 -0
- package/node_modules/@maka/core/package.json +3 -0
- package/node_modules/@maka/mcp/dist/credential-coordinator.js +6 -1
- package/node_modules/@maka/mcp/dist/index.js +31 -5
- package/node_modules/@maka/mcp/dist/oauth.js +54 -6
- package/node_modules/@maka/runtime/dist/agent-run-inspect.js +1 -1
- package/node_modules/@maka/runtime/dist/ai-sdk-message-projection.js +15 -60
- package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +18 -0
- package/node_modules/@maka/runtime/dist/apply-patch-profile.js +64 -14
- package/node_modules/@maka/runtime/dist/builtin-tools.js +3 -2
- package/node_modules/@maka/runtime/dist/codex-v4a-patch.js +18 -0
- package/node_modules/@maka/runtime/dist/conversation-copy.js +56 -1
- package/node_modules/@maka/runtime/dist/mcp-tools.js +9 -2
- package/node_modules/@maka/runtime/dist/model-adapter.js +80 -3
- package/node_modules/@maka/runtime/dist/model-runtime.js +6 -0
- package/node_modules/@maka/runtime/dist/openai-apply-patch.js +5 -0
- package/node_modules/@maka/runtime/dist/plugin-executor-backend.js +122 -3
- package/node_modules/@maka/runtime/dist/plugin-executor-service.js +212 -16
- package/node_modules/@maka/runtime/dist/runtime-ledger-repair.js +2 -2
- package/node_modules/@maka/runtime/dist/session-event-runtime-mapper.js +3 -0
- package/node_modules/@maka/runtime/dist/session-manager.js +184 -126
- package/node_modules/@maka/runtime/dist/shell-run-manager.js +3 -0
- package/node_modules/@maka/runtime/dist/stream-graph-coordinator.js +29 -4
- package/node_modules/@maka/runtime-host/dist/candidate-entry.js +8 -5
- package/node_modules/@maka/runtime-host/dist/client/capability-provider-service.js +2 -0
- package/node_modules/@maka/runtime-host/dist/client/client-capability-channel.js +59 -20
- package/node_modules/@maka/runtime-host/dist/client/connection.js +6 -4
- package/node_modules/@maka/runtime-host/dist/client/index.js +1 -1
- package/node_modules/@maka/runtime-host/dist/client/reconnect-lifecycle.js +8 -1
- package/node_modules/@maka/runtime-host/dist/client/reconnecting-connection.js +4 -4
- package/node_modules/@maka/runtime-host/dist/client/session-catalog-summary.js +6 -1
- package/node_modules/@maka/runtime-host/dist/composition-identity.js +19 -0
- package/node_modules/@maka/runtime-host/dist/protocol/client-capability.js +20 -3
- package/node_modules/@maka/runtime-host/dist/protocol/external-session.js +8 -5
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +20 -2
- package/node_modules/@maka/runtime-host/dist/protocol/plugin-platform.js +34 -0
- package/node_modules/@maka/runtime-host/dist/protocol/session-catalog.js +75 -5
- package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +1 -5
- package/node_modules/@maka/runtime-host/dist/server/builtin-external-agent-plugins.js +188 -0
- package/node_modules/@maka/runtime-host/dist/server/candidate.js +12 -7
- package/node_modules/@maka/runtime-host/dist/server/client-capability-coordinator.js +234 -70
- package/node_modules/@maka/runtime-host/dist/server/client-capability-invocation-broker.js +14 -8
- package/node_modules/@maka/runtime-host/dist/server/client-capability-recovery.js +44 -46
- package/node_modules/@maka/runtime-host/dist/server/context-coordinator.js +2 -0
- package/node_modules/@maka/runtime-host/dist/server/execution-composition-factory.js +1 -1
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +188 -21
- package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +6 -0
- package/node_modules/@maka/runtime-host/dist/server/extension-bundle.js +4 -0
- package/node_modules/@maka/runtime-host/dist/server/external-session-coordinator.js +29 -7
- package/node_modules/@maka/runtime-host/dist/server/host-composition-source.js +47 -0
- package/node_modules/@maka/runtime-host/dist/server/host-composition.js +1 -29
- package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +2 -9
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +75 -9
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +5 -1
- package/node_modules/@maka/runtime-host/dist/server/interaction-coordinator.js +45 -0
- package/node_modules/@maka/runtime-host/dist/server/jev-routing-model.js +157 -0
- package/node_modules/@maka/runtime-host/dist/server/listener-set.js +2 -2
- package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +4 -0
- package/node_modules/@maka/runtime-host/dist/server/plugin-platform-coordinator.js +11 -1
- package/node_modules/@maka/runtime-host/dist/server/process-lifecycle.js +1 -1
- package/node_modules/@maka/runtime-host/dist/server/process-termination-error.js +27 -0
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +91 -12
- package/node_modules/@maka/runtime-host/dist/server/runtime-policy-coordinator.js +2 -0
- package/node_modules/@maka/runtime-host/dist/server/session-bundle-coordinator.js +4 -1
- package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +128 -13
- package/node_modules/@maka/runtime-host/dist/server/session-effect-coordinator.js +12 -6
- package/node_modules/@maka/runtime-host/dist/server/session-retirement-coordinator.js +2 -2
- package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +33 -0
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +4 -0
- package/node_modules/@maka/runtime-host/dist/server/workhub-result-coordinator.js +173 -0
- package/node_modules/@maka/runtime-host/dist/server/workhub-result-runtime.js +293 -0
- package/node_modules/@maka/runtime-host/package.json +2 -0
- package/node_modules/@maka/storage/dist/agent-run-store-contract.js +8 -0
- package/node_modules/@maka/storage/dist/execution-stores.js +15 -3
- package/node_modules/@maka/storage/dist/mcp-config-store.js +91 -2
- package/node_modules/@maka/storage/dist/operational-state-store.js +15 -1
- package/node_modules/@maka/storage/dist/plan-authority.js +2 -0
- package/node_modules/@maka/storage/dist/plan-store.js +30 -0
- package/node_modules/@maka/storage/dist/runtime-event-persistence.js +1 -0
- package/node_modules/@maka/storage/dist/runtime-policy/credential-vault-document.js +4 -1
- package/node_modules/@maka/storage/dist/runtime-policy/policy-document.js +2 -0
- package/node_modules/@maka/storage/dist/session-store-values.js +16 -4
- package/node_modules/@maka/storage/dist/shell-run-authority.js +1 -0
- package/node_modules/@maka/storage/dist/shell-run-store.js +15 -0
- package/node_modules/@maka/storage/dist/sqlite-runtime-schema.js +74 -1
- package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +724 -84
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +16 -0
- package/package.json +1 -1
|
@@ -36,7 +36,7 @@ import { readLogicalRuntimeExecutionForRun } from '@maka/core/runtime-logical-ex
|
|
|
36
36
|
import { buildInvocationOpenedEvent, isSessionInlineInvocation, runtimeInvocationOutcome, } from '@maka/core/runtime-invocation';
|
|
37
37
|
import { isArtifactChildResultOutput } from '@maka/core/artifacts';
|
|
38
38
|
import { invocationMatchesClaimTarget } from '@maka/core/runtime-boundary';
|
|
39
|
-
import { readRunInvocation, } from '@maka/core/runtime-event-store';
|
|
39
|
+
import { readRunInvocation, runtimeInvocationRecoveryInventoryFromInvocations, } from '@maka/core/runtime-event-store';
|
|
40
40
|
import { AGENT_GRAPH_OPERATOR_PROVISION_SCHEMA_VERSION } from '@maka/core/agent-graph-topology';
|
|
41
41
|
import { runtimeInvocationFailureClass, } from './runtime-event-read-model.js';
|
|
42
42
|
import { RuntimeReadModel, RuntimeReadModelError, } from './runtime-read-model.js';
|
|
@@ -381,6 +381,8 @@ export class SessionManager {
|
|
|
381
381
|
if (current.revision !== input.expectedRevision) {
|
|
382
382
|
throw new SessionConfigurationRevisionConflictError(input.expectedRevision, current.revision);
|
|
383
383
|
}
|
|
384
|
+
if (current.header.executorId)
|
|
385
|
+
throw new SessionConfigurationTransitionError('operation_unavailable', 'External executor workspace is fixed. Start a new task.');
|
|
384
386
|
if (current.header.isArchived) {
|
|
385
387
|
throw new SessionConfigurationTransitionError('operation_conflict', 'Archived Session workspace cannot be relocated');
|
|
386
388
|
}
|
|
@@ -547,15 +549,63 @@ export class SessionManager {
|
|
|
547
549
|
async recoverInterruptedSessions() {
|
|
548
550
|
return this.recoverInterruptedSessionsWithPolicy({ kind: 'best_effort' });
|
|
549
551
|
}
|
|
552
|
+
/**
|
|
553
|
+
* Recover only the Sessions named by an already-held quiescent operation.
|
|
554
|
+
*
|
|
555
|
+
* Bundle export uses this after fencing a Session subtree. A persisted open
|
|
556
|
+
* invocation can survive a Host restart even though no live execution claim
|
|
557
|
+
* remains; settling that invocation is safe under the fence and lets export
|
|
558
|
+
* distinguish an interrupted run from a live one without scanning or
|
|
559
|
+
* mutating unrelated Sessions.
|
|
560
|
+
*/
|
|
561
|
+
async recoverInterruptedSessionsForSessions(sessionIds) {
|
|
562
|
+
const scope = new Set(sessionIds);
|
|
563
|
+
return this.recoverInterruptedSessionsWithPolicy({ kind: 'best_effort' }, scope);
|
|
564
|
+
}
|
|
550
565
|
async recoverInterruptedSessionsStrict(stores) {
|
|
551
566
|
if (stores.sessionStore !== this.deps.store || stores.agentRunStore !== this.deps.runStore) {
|
|
552
567
|
throw new Error('Strict recovery stores must match the SessionManager composition');
|
|
553
568
|
}
|
|
554
569
|
return this.recoverInterruptedSessionsWithPolicy({ kind: 'strict', stores });
|
|
555
570
|
}
|
|
556
|
-
async recoverInterruptedSessionsWithPolicy(policy) {
|
|
557
|
-
const
|
|
571
|
+
async recoverInterruptedSessionsWithPolicy(policy, scope) {
|
|
572
|
+
const profileEnabled = process.env.MAKA_STARTUP_PROFILE === '1';
|
|
573
|
+
const profileTotals = new Map();
|
|
574
|
+
const profileCalls = new Map();
|
|
575
|
+
const measure = async (label, operation) => {
|
|
576
|
+
if (!profileEnabled)
|
|
577
|
+
return operation();
|
|
578
|
+
const started = performance.now();
|
|
579
|
+
try {
|
|
580
|
+
return await operation();
|
|
581
|
+
}
|
|
582
|
+
finally {
|
|
583
|
+
profileTotals.set(label, (profileTotals.get(label) ?? 0) + performance.now() - started);
|
|
584
|
+
profileCalls.set(label, (profileCalls.get(label) ?? 0) + 1);
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
const interrupted = (await measure('list-sessions', () => listSessionsForRecovery(this.deps.store, policy))).filter((session) => !session.isArchived && (scope === undefined || scope.has(session.id)));
|
|
588
|
+
const recoveryCandidateSet = async (label, operation) => {
|
|
589
|
+
if (!operation)
|
|
590
|
+
return undefined;
|
|
591
|
+
const sessionIds = await measure(label, () => recoverOr(policy, operation, undefined));
|
|
592
|
+
return sessionIds ? new Set(sessionIds) : undefined;
|
|
593
|
+
};
|
|
594
|
+
const continuationAuthority = runtimeContinuationAuthority(this.deps.runtimeEventStore);
|
|
595
|
+
const [shellRecoverySessions, unsettledContinuationClaims, planRecoverySessions] = await Promise.all([
|
|
596
|
+
recoveryCandidateSet('shell-inventory', this.deps.shellRuns ? () => this.deps.shellRuns.listRecoverySessionIds() : undefined),
|
|
597
|
+
continuationAuthority?.listUnsettledContinuationClaimsForRecovery
|
|
598
|
+
? measure('continuation-inventory', () => recoverOr(policy, () => continuationAuthority.listUnsettledContinuationClaimsForRecovery(interrupted.map((session) => session.id)), undefined))
|
|
599
|
+
: Promise.resolve(undefined),
|
|
600
|
+
recoveryCandidateSet('plan-inventory', this.deps.planStore?.listPlanRecoverySessionIds
|
|
601
|
+
? () => this.deps.planStore.listPlanRecoverySessionIds()
|
|
602
|
+
: undefined),
|
|
603
|
+
]);
|
|
604
|
+
const continuationClaimsBySession = unsettledContinuationClaims
|
|
605
|
+
? groupContinuationClaimsBySession(unsettledContinuationClaims)
|
|
606
|
+
: undefined;
|
|
558
607
|
const recovered = new Set();
|
|
608
|
+
const runRecoveryQueue = [];
|
|
559
609
|
for (const session of interrupted) {
|
|
560
610
|
if (this.runtimeKernel.hasActiveRuns(session.id))
|
|
561
611
|
continue;
|
|
@@ -576,8 +626,9 @@ export class SessionManager {
|
|
|
576
626
|
}), undefined);
|
|
577
627
|
}
|
|
578
628
|
}
|
|
579
|
-
if (this.deps.shellRuns
|
|
580
|
-
|
|
629
|
+
if (this.deps.shellRuns &&
|
|
630
|
+
(!shellRecoverySessions || shellRecoverySessions.has(session.id))) {
|
|
631
|
+
const recoveredShellRuns = await measure('shell', () => recoverOr(policy, () => this.deps.shellRuns.recoverOrphanedSession(session.id), 0));
|
|
581
632
|
if (recoveredShellRuns > 0)
|
|
582
633
|
recovered.add(session.id);
|
|
583
634
|
}
|
|
@@ -586,10 +637,13 @@ export class SessionManager {
|
|
|
586
637
|
// recovery. Deciding it a second time here — off a transcript scan, and
|
|
587
638
|
// ending in `remove()` — could only ever contradict it.
|
|
588
639
|
let continuationClaimRecovered = false;
|
|
589
|
-
const
|
|
590
|
-
if (this.deps.runStore &&
|
|
640
|
+
const unsettledClaims = continuationClaimsBySession?.get(session.id);
|
|
641
|
+
if (this.deps.runStore &&
|
|
642
|
+
continuationAuthority &&
|
|
643
|
+
(!continuationClaimsBySession ||
|
|
644
|
+
unsettledClaims?.some((state) => state.startEventId === undefined || state.startKind === 'claim_repair'))) {
|
|
591
645
|
try {
|
|
592
|
-
continuationClaimRecovered = await this.recoverContinuationClaimsBeforeProvider(session.id, continuationAuthority, policy);
|
|
646
|
+
continuationClaimRecovered = await measure('continuation', () => this.recoverContinuationClaimsBeforeProvider(session.id, continuationAuthority, policy, unsettledClaims));
|
|
593
647
|
}
|
|
594
648
|
catch (error) {
|
|
595
649
|
if (policy.kind === 'strict')
|
|
@@ -603,26 +657,62 @@ export class SessionManager {
|
|
|
603
657
|
if (continuationClaimRecovered)
|
|
604
658
|
recovered.add(session.id);
|
|
605
659
|
}
|
|
606
|
-
if (this.deps.planStore) {
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
660
|
+
if (this.deps.planStore && (!planRecoverySessions || planRecoverySessions.has(session.id))) {
|
|
661
|
+
// Plan events are the domain authority for whether there is a Plan
|
|
662
|
+
// obligation at all. Runtime evidence is needed only to decide whether
|
|
663
|
+
// an existing active Plan belongs to a pending handoff and must survive.
|
|
664
|
+
const planState = await measure('plan-state', () => recoverOr(policy, () => this.deps.planStore.readState(session.id), undefined));
|
|
665
|
+
if (planState?.activeExecutionId) {
|
|
666
|
+
const preservesHandoff = await measure('plan-handoff', () => recoverOr(policy, async () => {
|
|
667
|
+
if (!continuationAuthority)
|
|
668
|
+
return false;
|
|
669
|
+
const latest = latestInvocation((await this.listInvocations(session.id)).filter((run) => isSessionInlineInvocation(run.opening)));
|
|
670
|
+
if (!latest)
|
|
671
|
+
return false;
|
|
672
|
+
// Only the current logical execution may preserve a session-owned Plan.
|
|
673
|
+
// Read after claim repair: an abandoned successor now has a real failure.
|
|
674
|
+
return Boolean((await readLogicalRuntimeExecutionForRun(continuationAuthority, latest))
|
|
675
|
+
?.pendingHandoff);
|
|
676
|
+
}, false));
|
|
677
|
+
if (!preservesHandoff) {
|
|
678
|
+
const planRecovery = await measure('plan-interrupt', () => recoverOr(policy, () => this.deps.planStore.interruptActiveExecution(session.id, 'runtime_recovery'), null));
|
|
679
|
+
if (planRecovery)
|
|
680
|
+
recovered.add(session.id);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
runRecoveryQueue.push({
|
|
685
|
+
session,
|
|
686
|
+
continuationClaimRecovered,
|
|
687
|
+
...(continuationClaimsBySession
|
|
688
|
+
? {
|
|
689
|
+
claimOwnedUnsettledRunIds: new Set((unsettledClaims ?? []).map((state) => state.claim.target.runId)),
|
|
690
|
+
}
|
|
691
|
+
: {}),
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
let inventoryBySession;
|
|
695
|
+
if (this.deps.runStore &&
|
|
696
|
+
this.deps.runtimeEventStore?.listInvocationRecoveryInventory &&
|
|
697
|
+
runRecoveryQueue.length > 0) {
|
|
698
|
+
try {
|
|
699
|
+
const inventory = await measure('run-inventory', () => this.deps.runtimeEventStore.listInvocationRecoveryInventory(runRecoveryQueue.map(({ session }) => session.id)));
|
|
700
|
+
const mutable = new Map();
|
|
701
|
+
for (const entry of inventory) {
|
|
702
|
+
const entries = mutable.get(entry.sessionId) ?? [];
|
|
703
|
+
entries.push(entry);
|
|
704
|
+
mutable.set(entry.sessionId, entries);
|
|
622
705
|
}
|
|
706
|
+
inventoryBySession = mutable;
|
|
707
|
+
}
|
|
708
|
+
catch (error) {
|
|
709
|
+
if (policy.kind === 'strict')
|
|
710
|
+
throw error;
|
|
623
711
|
}
|
|
712
|
+
}
|
|
713
|
+
for (const { session, continuationClaimRecovered, claimOwnedUnsettledRunIds, } of runRecoveryQueue) {
|
|
624
714
|
if (this.deps.runStore) {
|
|
625
|
-
const runRecovery = await recoverOr(policy, () => this.recoverAgentRunsFromLedger(session.id, policy), undefined);
|
|
715
|
+
const runRecovery = await measure('runs', () => recoverOr(policy, () => this.recoverAgentRunsFromLedger(session.id, policy, inventoryBySession ? (inventoryBySession.get(session.id) ?? []) : undefined, claimOwnedUnsettledRunIds), undefined));
|
|
626
716
|
if (runRecovery?.hasLedger) {
|
|
627
717
|
if (runRecovery.recovered || continuationClaimRecovered) {
|
|
628
718
|
await recoverOr(policy, () => this.updateStatus(session.id, 'active'), undefined);
|
|
@@ -646,6 +736,15 @@ export class SessionManager {
|
|
|
646
736
|
recovered.add(session.id);
|
|
647
737
|
}
|
|
648
738
|
}
|
|
739
|
+
if (profileEnabled) {
|
|
740
|
+
console.error(`[startup-profile] session-recovery ${JSON.stringify({
|
|
741
|
+
sessions: interrupted.length,
|
|
742
|
+
totals: Object.fromEntries([...profileTotals].map(([label, ms]) => [
|
|
743
|
+
label,
|
|
744
|
+
{ calls: profileCalls.get(label) ?? 0, ms },
|
|
745
|
+
])),
|
|
746
|
+
})}`);
|
|
747
|
+
}
|
|
649
748
|
return [...recovered];
|
|
650
749
|
}
|
|
651
750
|
async setSessionStatus(sessionId, status, blockedReason) {
|
|
@@ -680,66 +779,6 @@ export class SessionManager {
|
|
|
680
779
|
await this.deps.store.readHeader(sessionId);
|
|
681
780
|
return this.runtimeKernel.listActiveInteractions?.(sessionId) ?? [];
|
|
682
781
|
}
|
|
683
|
-
async setPermissionMode(sessionId, mode) {
|
|
684
|
-
const readHeaderRecordSnapshot = this.deps.store.readHeaderRecordSnapshot?.bind(this.deps.store);
|
|
685
|
-
if (!readHeaderRecordSnapshot || !this.deps.store.updateSessionConfiguration) {
|
|
686
|
-
// Temporary compatibility bridge for SessionStore embeddings that predate
|
|
687
|
-
// versioned configuration authority. A follow-up PR will shortly remove
|
|
688
|
-
// setPermissionMode and this redundant fallback after callers migrate.
|
|
689
|
-
return this.setPermissionModeWithLegacyStore(sessionId, mode);
|
|
690
|
-
}
|
|
691
|
-
const current = await readHeaderRecordSnapshot(sessionId);
|
|
692
|
-
const next = await this.transitionSessionConfiguration(sessionId, {
|
|
693
|
-
expectedRevision: current.revision,
|
|
694
|
-
clearConnectionBlock: false,
|
|
695
|
-
permissionModeOnly: true,
|
|
696
|
-
configuration: sessionConfigurationWithPermissionMode(current.header, mode),
|
|
697
|
-
});
|
|
698
|
-
return headerToSummary(next.header);
|
|
699
|
-
}
|
|
700
|
-
async setPermissionModeWithLegacyStore(sessionId, mode) {
|
|
701
|
-
const previous = await this.deps.store.readHeader(sessionId);
|
|
702
|
-
const boundary = await this.deps.store.readExecutionBoundary(sessionId);
|
|
703
|
-
if (previous.permissionMode === mode &&
|
|
704
|
-
executionBoundaryMatchesPermissionMode(boundary, mode)) {
|
|
705
|
-
return headerToSummary(previous);
|
|
706
|
-
}
|
|
707
|
-
const labels = previous.labels;
|
|
708
|
-
const kind = mode === 'bypass' ? 'bypass' : 'managed';
|
|
709
|
-
await this.commitExecutionBoundaryTransition(sessionId, boundary, mode, async () => {
|
|
710
|
-
const current = await this.deps.store.readHeader(sessionId);
|
|
711
|
-
if (current.status === 'waiting_for_user') {
|
|
712
|
-
throw new SessionConfigurationTransitionError('session_busy', 'Session has a pending Interaction');
|
|
713
|
-
}
|
|
714
|
-
return () => this.deps.store.setExecutionBoundaryKind(sessionId, kind, {
|
|
715
|
-
permissionMode: mode,
|
|
716
|
-
labels,
|
|
717
|
-
});
|
|
718
|
-
});
|
|
719
|
-
const next = await this.deps.store.readHeader(sessionId);
|
|
720
|
-
this.runtimeKernel.updateCachedHeader(sessionId, next);
|
|
721
|
-
return headerToSummary(next);
|
|
722
|
-
}
|
|
723
|
-
async setExecutionBoundaryKind(sessionId, kind) {
|
|
724
|
-
const current = await this.deps.store.readExecutionBoundary(sessionId);
|
|
725
|
-
const header = await this.deps.store.readHeader(sessionId);
|
|
726
|
-
// Managed includes Explore. Match Storage's default projection, then pass
|
|
727
|
-
// it explicitly so classification and commit describe the same transition.
|
|
728
|
-
const permissionMode = kind === 'bypass'
|
|
729
|
-
? 'bypass'
|
|
730
|
-
: header.permissionMode === 'bypass'
|
|
731
|
-
? 'ask'
|
|
732
|
-
: header.permissionMode;
|
|
733
|
-
const narrows = narrowsExecutionAuthority(current, permissionMode);
|
|
734
|
-
if (narrows && this.runtimeKernel.hasActiveRuns(sessionId)) {
|
|
735
|
-
throw new SessionConfigurationTransitionError('session_busy', 'Execution boundary cannot change while a Turn is running');
|
|
736
|
-
}
|
|
737
|
-
if (header.status === 'waiting_for_user') {
|
|
738
|
-
throw new SessionConfigurationTransitionError('session_busy', 'Execution boundary cannot change while an Interaction is pending');
|
|
739
|
-
}
|
|
740
|
-
const boundary = await this.commitExecutionBoundaryTransition(sessionId, current, permissionMode, async () => () => this.deps.store.setExecutionBoundaryKind(sessionId, kind, { permissionMode }));
|
|
741
|
-
return boundary;
|
|
742
|
-
}
|
|
743
782
|
async commitExecutionBoundaryTransition(sessionId, current, nextPermissionMode, prepareCommit) {
|
|
744
783
|
const prepareBoundaryCommit = async () => {
|
|
745
784
|
const latest = await this.deps.store.readExecutionBoundary(sessionId);
|
|
@@ -3010,10 +3049,10 @@ export class SessionManager {
|
|
|
3010
3049
|
* dead, so that state remains `continuation_started_indeterminate` and is
|
|
3011
3050
|
* deliberately left non-terminal.
|
|
3012
3051
|
*/
|
|
3013
|
-
async recoverContinuationClaimsBeforeProvider(sessionId, authority, _policy) {
|
|
3052
|
+
async recoverContinuationClaimsBeforeProvider(sessionId, authority, _policy, recoveryStates) {
|
|
3014
3053
|
if (!this.deps.runStore)
|
|
3015
3054
|
return false;
|
|
3016
|
-
const states = await authority.listContinuationClaimsForRecovery(sessionId);
|
|
3055
|
+
const states = recoveryStates ?? (await authority.listContinuationClaimsForRecovery(sessionId));
|
|
3017
3056
|
let recovered = false;
|
|
3018
3057
|
for (const initialState of states) {
|
|
3019
3058
|
const { claim } = initialState;
|
|
@@ -3084,24 +3123,41 @@ export class SessionManager {
|
|
|
3084
3123
|
}
|
|
3085
3124
|
return recovered;
|
|
3086
3125
|
}
|
|
3087
|
-
async recoverAgentRunsFromLedger(sessionId, policy = { kind: 'best_effort' }) {
|
|
3126
|
+
async recoverAgentRunsFromLedger(sessionId, policy = { kind: 'best_effort' }, recoveryInventory, recoveryClaimRunIds) {
|
|
3088
3127
|
if (!this.deps.runStore || !this.deps.runtimeEventStore)
|
|
3089
3128
|
return { hasLedger: false, recovered: false };
|
|
3090
3129
|
// The importer may have committed only a prefix before a restart. Sealing
|
|
3091
3130
|
// that prefix here would make the next read skip the unconverted history.
|
|
3092
|
-
const
|
|
3093
|
-
|
|
3131
|
+
const inventory = recoveryInventory
|
|
3132
|
+
? recoveryInventory
|
|
3133
|
+
: this.deps.runtimeEventStore.listInvocationRecoveryInventory
|
|
3134
|
+
? await this.deps.runtimeEventStore.listInvocationRecoveryInventory([sessionId])
|
|
3135
|
+
: runtimeInvocationRecoveryInventoryFromInvocations(await this.listInvocations(sessionId));
|
|
3136
|
+
const ownedInventory = inventory.filter((entry) => {
|
|
3137
|
+
const identity = entry.candidate ?? entry.identity;
|
|
3138
|
+
return !identity || !isTranscriptLedgerInvocation(identity);
|
|
3139
|
+
});
|
|
3140
|
+
if (ownedInventory.length === 0)
|
|
3094
3141
|
return { hasLedger: false, recovered: false };
|
|
3142
|
+
let claimOwnedUnsettledRunIds = recoveryClaimRunIds;
|
|
3095
3143
|
const continuationAuthority = runtimeContinuationAuthority(this.deps.runtimeEventStore);
|
|
3096
|
-
|
|
3097
|
-
|
|
3144
|
+
if (!claimOwnedUnsettledRunIds && continuationAuthority) {
|
|
3145
|
+
const discovered = new Set();
|
|
3098
3146
|
for (const state of await continuationAuthority.listContinuationClaimsForRecovery(sessionId)) {
|
|
3099
3147
|
const events = await continuationAuthority.readImmutableRuntimeEvents(state.claim.target.sessionId, state.claim.target.runId);
|
|
3100
|
-
if (!events.some(isTerminalRuntimeEvent))
|
|
3101
|
-
|
|
3102
|
-
}
|
|
3148
|
+
if (!events.some(isTerminalRuntimeEvent))
|
|
3149
|
+
discovered.add(state.claim.target.runId);
|
|
3103
3150
|
}
|
|
3151
|
+
claimOwnedUnsettledRunIds = discovered;
|
|
3104
3152
|
}
|
|
3153
|
+
const runsNeedingRecovery = ownedInventory.flatMap((entry) => entry.candidate ? [entry.candidate] : []);
|
|
3154
|
+
if (runsNeedingRecovery.length === 0)
|
|
3155
|
+
return { hasLedger: true, recovered: false };
|
|
3156
|
+
const recoveryRunStore = policy.kind === 'strict'
|
|
3157
|
+
? {
|
|
3158
|
+
readEvents: (candidateSessionId, runId) => policy.stores.agentRunStore.readEventsForRecovery(candidateSessionId, runId),
|
|
3159
|
+
}
|
|
3160
|
+
: this.deps.runStore;
|
|
3105
3161
|
// Read once per recovered session, and only when a failure actually needs
|
|
3106
3162
|
// attributing, so healthy sessions pay nothing for the query.
|
|
3107
3163
|
let boundaryClosures;
|
|
@@ -3114,11 +3170,14 @@ export class SessionManager {
|
|
|
3114
3170
|
return boundaryClosures;
|
|
3115
3171
|
};
|
|
3116
3172
|
let recovered = false;
|
|
3117
|
-
for (const run of
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3173
|
+
for (const run of runsNeedingRecovery) {
|
|
3174
|
+
let inspected = await inspectAgentRunReadModel(recoveryRunStore, this.deps.runtimeEventStore, {
|
|
3175
|
+
sessionId,
|
|
3176
|
+
runId: run.runId,
|
|
3177
|
+
invocation: run,
|
|
3178
|
+
includeModelReplay: false,
|
|
3179
|
+
includeProjection: false,
|
|
3180
|
+
});
|
|
3122
3181
|
if (inspected.sourceHealth.runtimeLedger === 'read_failed') {
|
|
3123
3182
|
if (policy.kind === 'strict') {
|
|
3124
3183
|
throw new Error(`RuntimeEvent ledger is unreadable for run ${run.runId}`);
|
|
@@ -3138,7 +3197,7 @@ export class SessionManager {
|
|
|
3138
3197
|
// restart repair must neither replay its tools nor manufacture failure.
|
|
3139
3198
|
continue;
|
|
3140
3199
|
}
|
|
3141
|
-
if (claimOwnedUnsettledRunIds
|
|
3200
|
+
if (claimOwnedUnsettledRunIds?.has(run.runId) &&
|
|
3142
3201
|
!inspected.runtimeEvents.some(isTerminalRuntimeEvent)) {
|
|
3143
3202
|
// Every unresolved claim target belongs to the claim saga. This
|
|
3144
3203
|
// includes claim-only, deterministic repair-start, and live provider
|
|
@@ -3163,7 +3222,13 @@ export class SessionManager {
|
|
|
3163
3222
|
continue;
|
|
3164
3223
|
}
|
|
3165
3224
|
if (interruptedOutcomes.length > 0) {
|
|
3166
|
-
inspected = await inspectAgentRunReadModel(
|
|
3225
|
+
inspected = await inspectAgentRunReadModel(recoveryRunStore, this.deps.runtimeEventStore, {
|
|
3226
|
+
sessionId,
|
|
3227
|
+
runId: run.runId,
|
|
3228
|
+
invocation: run,
|
|
3229
|
+
includeModelReplay: false,
|
|
3230
|
+
includeProjection: false,
|
|
3231
|
+
});
|
|
3167
3232
|
}
|
|
3168
3233
|
}
|
|
3169
3234
|
const terminalLedger = classifyTerminalRuntimeLedger(run, inspected.runtimeEvents);
|
|
@@ -3260,6 +3325,16 @@ function continuationExecutionErrorClass(error) {
|
|
|
3260
3325
|
}
|
|
3261
3326
|
return error instanceof Error ? error.name : 'unknown';
|
|
3262
3327
|
}
|
|
3328
|
+
function groupContinuationClaimsBySession(states) {
|
|
3329
|
+
const grouped = new Map();
|
|
3330
|
+
for (const state of states) {
|
|
3331
|
+
const sessionId = state.claim.target.sessionId;
|
|
3332
|
+
const sessionStates = grouped.get(sessionId) ?? [];
|
|
3333
|
+
sessionStates.push(state);
|
|
3334
|
+
grouped.set(sessionId, sessionStates);
|
|
3335
|
+
}
|
|
3336
|
+
return grouped;
|
|
3337
|
+
}
|
|
3263
3338
|
const MAX_BEST_EFFORT_OUTCOME_COMMIT_ATTEMPTS = 2;
|
|
3264
3339
|
function listSessionsForRecovery(store, policy) {
|
|
3265
3340
|
return policy.kind === 'strict' ? policy.stores.sessionStore.listForRecovery() : store.list();
|
|
@@ -3391,7 +3466,12 @@ export function headerToSummary(h) {
|
|
|
3391
3466
|
...(h.revisionIndex !== undefined ? { revisionIndex: h.revisionIndex } : {}),
|
|
3392
3467
|
...(h.revisionState ? { revisionState: h.revisionState } : {}),
|
|
3393
3468
|
backend: h.backend,
|
|
3394
|
-
...(h.executorId
|
|
3469
|
+
...(h.executorId
|
|
3470
|
+
? {
|
|
3471
|
+
executorId: h.executorId,
|
|
3472
|
+
...(h.executorConfig ? { executorConfig: h.executorConfig } : {}),
|
|
3473
|
+
}
|
|
3474
|
+
: {}),
|
|
3395
3475
|
...(h.llmConnectionId === undefined ? {} : { llmConnectionId: h.llmConnectionId }),
|
|
3396
3476
|
llmConnectionSlug: h.llmConnectionSlug,
|
|
3397
3477
|
connectionLocked: h.connectionLocked,
|
|
@@ -3527,23 +3607,10 @@ function claimedAgentGraphIntentResult(claim, result) {
|
|
|
3527
3607
|
...result,
|
|
3528
3608
|
};
|
|
3529
3609
|
}
|
|
3530
|
-
function sessionConfigurationWithPermissionMode(header, permissionMode) {
|
|
3531
|
-
return {
|
|
3532
|
-
backend: header.backend,
|
|
3533
|
-
executorId: header.executorId,
|
|
3534
|
-
llmConnectionId: header.llmConnectionId,
|
|
3535
|
-
llmConnectionSlug: header.llmConnectionSlug,
|
|
3536
|
-
connectionLocked: header.connectionLocked,
|
|
3537
|
-
model: header.model,
|
|
3538
|
-
thinkingLevel: header.thinkingLevel,
|
|
3539
|
-
permissionMode,
|
|
3540
|
-
collaborationMode: header.collaborationMode ?? 'agent',
|
|
3541
|
-
orchestrationMode: header.orchestrationMode ?? 'default',
|
|
3542
|
-
};
|
|
3543
|
-
}
|
|
3544
3610
|
function sessionConfigurationMatchesExceptPermissionMode(header, configuration) {
|
|
3545
3611
|
return (header.backend === configuration.backend &&
|
|
3546
3612
|
header.executorId === configuration.executorId &&
|
|
3613
|
+
header.executorConfig?.model === configuration.executorConfig?.model &&
|
|
3547
3614
|
header.llmConnectionId === configuration.llmConnectionId &&
|
|
3548
3615
|
header.llmConnectionSlug === configuration.llmConnectionSlug &&
|
|
3549
3616
|
header.connectionLocked === configuration.connectionLocked &&
|
|
@@ -3556,15 +3623,6 @@ function sessionConfigurationMatches(header, configuration) {
|
|
|
3556
3623
|
return (header.permissionMode === configuration.permissionMode &&
|
|
3557
3624
|
sessionConfigurationMatchesExceptPermissionMode(header, configuration));
|
|
3558
3625
|
}
|
|
3559
|
-
function executionBoundaryMatchesPermissionMode(boundary, mode) {
|
|
3560
|
-
if (mode === 'bypass')
|
|
3561
|
-
return boundary.kind === 'bypass';
|
|
3562
|
-
if (boundary.kind !== 'managed')
|
|
3563
|
-
return false;
|
|
3564
|
-
return mode === 'explore'
|
|
3565
|
-
? boundary.profile.name === 'read-only'
|
|
3566
|
-
: boundary.profile.name !== 'read-only';
|
|
3567
|
-
}
|
|
3568
3626
|
function narrowsExecutionAuthority(boundary, nextPermissionMode) {
|
|
3569
3627
|
if (nextPermissionMode === 'bypass')
|
|
3570
3628
|
return false;
|
|
@@ -424,6 +424,9 @@ export class ShellRunProcessManager {
|
|
|
424
424
|
const records = await this.input.store.listSessionShellRuns(sessionId);
|
|
425
425
|
return records.map(shellRunUpdate);
|
|
426
426
|
}
|
|
427
|
+
listRecoverySessionIds() {
|
|
428
|
+
return this.input.store.listShellRunRecoverySessionIds?.() ?? Promise.resolve(undefined);
|
|
429
|
+
}
|
|
427
430
|
async getSessionUpdate(sessionId, ref) {
|
|
428
431
|
const target = parseShellRunResourceRef(ref);
|
|
429
432
|
if (!target)
|
|
@@ -324,16 +324,41 @@ export class AgentGraphCoordinator {
|
|
|
324
324
|
*/
|
|
325
325
|
async recover() {
|
|
326
326
|
const recovered = [];
|
|
327
|
+
const listedRecoveryGraphIds = this.#input.controlStore.listAgentGraphScheduleRecoveryGraphIds
|
|
328
|
+
? await this.#input.controlStore.listAgentGraphScheduleRecoveryGraphIds()
|
|
329
|
+
: undefined;
|
|
330
|
+
const recoveryGraphIds = listedRecoveryGraphIds ? new Set(listedRecoveryGraphIds) : undefined;
|
|
331
|
+
if (recoveryGraphIds?.size === 0)
|
|
332
|
+
return recovered;
|
|
333
|
+
const recoveryRootSessionIds = new Set();
|
|
334
|
+
if (recoveryGraphIds && this.#input.epochStore) {
|
|
335
|
+
for (const graphId of recoveryGraphIds) {
|
|
336
|
+
const binding = await this.#input.epochStore.readAgentGraphEpochByGraphId(graphId);
|
|
337
|
+
if (binding)
|
|
338
|
+
recoveryRootSessionIds.add(binding.rootSessionId);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
327
341
|
for (const header of await this.#input.sessionStore.listForRecovery()) {
|
|
328
342
|
if (this.#input.rootSessionId && header.id !== this.#input.rootSessionId)
|
|
329
343
|
continue;
|
|
330
344
|
if (header.subagentParent || header.isArchived)
|
|
331
345
|
continue;
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
346
|
+
if (recoveryGraphIds &&
|
|
347
|
+
!recoveryGraphIds.has(agentGraphIdForRootSession(header.id)) &&
|
|
348
|
+
!recoveryRootSessionIds.has(header.id)) {
|
|
335
349
|
continue;
|
|
336
|
-
|
|
350
|
+
}
|
|
351
|
+
const graphId = await this.currentGraphId(header.id);
|
|
352
|
+
if (recoveryGraphIds) {
|
|
353
|
+
if (!recoveryGraphIds.has(graphId))
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
const updates = await this.#input.controlStore.listAgentGraphScheduleUpdates(graphId);
|
|
358
|
+
if (updates.length === 0)
|
|
359
|
+
continue;
|
|
360
|
+
updates.forEach((update) => this.#assertScheduleOwnedByRoot(update, header.id, graphId));
|
|
361
|
+
}
|
|
337
362
|
await this.reconcile(header.id);
|
|
338
363
|
recovered.push(header.id);
|
|
339
364
|
}
|
|
@@ -21,7 +21,6 @@ import { fileURLToPath } from 'node:url';
|
|
|
21
21
|
import { candidateStartupFailureExitCode, classifyCandidateStartupFailure, } from './candidate-startup-failure.js';
|
|
22
22
|
import { createRuntimeHostLaunchOwnerGuard } from './candidate-launch-owner-guard.js';
|
|
23
23
|
import { parseInteractiveRuntimeHostCandidateArguments } from './candidate-cli.js';
|
|
24
|
-
import { writeCandidateStartupDiagnostic } from './control/startup-diagnostic.js';
|
|
25
24
|
import { installRuntimeHostLogCapture, runtimeHostLogBuffer } from './process-diagnostics.js';
|
|
26
25
|
import { startExecutionRuntimeHostCandidate, } from './server/execution-candidate.js';
|
|
27
26
|
import { runRuntimeHostProcessLifecycle } from './server/process-lifecycle.js';
|
|
@@ -60,13 +59,17 @@ export async function runExecutionCandidateEntry(argv, entrypointUrl, hooks = {}
|
|
|
60
59
|
const logs = runtimeHostLogBuffer.snapshot();
|
|
61
60
|
console.error('[runtime-host] startup failed:', error);
|
|
62
61
|
if (rootId && startupAttemptId) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
const diagnosticRootId = rootId;
|
|
63
|
+
const diagnosticStartupAttemptId = startupAttemptId;
|
|
64
|
+
await import('./control/startup-diagnostic.js')
|
|
65
|
+
.then(({ writeCandidateStartupDiagnostic }) => writeCandidateStartupDiagnostic({
|
|
66
|
+
rootId: diagnosticRootId,
|
|
67
|
+
startupAttemptId: diagnosticStartupAttemptId,
|
|
66
68
|
failure,
|
|
67
69
|
error,
|
|
68
70
|
logs,
|
|
69
|
-
})
|
|
71
|
+
}))
|
|
72
|
+
.catch(() => undefined);
|
|
70
73
|
}
|
|
71
74
|
process.exit(candidateStartupFailureExitCode(failure));
|
|
72
75
|
}
|
|
@@ -140,12 +140,14 @@ function snapshotProvider(provider) {
|
|
|
140
140
|
const call = provider.call?.bind(provider);
|
|
141
141
|
const callService = provider.callService?.bind(provider);
|
|
142
142
|
const close = provider.close?.bind(provider);
|
|
143
|
+
const currentRegistrationRetired = provider.currentRegistrationRetired?.bind(provider);
|
|
143
144
|
return {
|
|
144
145
|
offers: () => canonical.offers,
|
|
145
146
|
...(canonical.services === undefined ? {} : { services: () => canonical.services ?? [] }),
|
|
146
147
|
...(call ? { call } : {}),
|
|
147
148
|
...(callService ? { callService } : {}),
|
|
148
149
|
...(close ? { close } : {}),
|
|
150
|
+
...(currentRegistrationRetired ? { currentRegistrationRetired } : {}),
|
|
149
151
|
};
|
|
150
152
|
}
|
|
151
153
|
async function closeProvider(provider) {
|