@opengeni/worker-bundle 1.0.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/activities/agent-turn/codex.d.ts +1 -1
- package/dist/activities/agent-turn/compaction-prep.d.ts +1 -5
- package/dist/activities/agent-turn/errors.d.ts +31 -5
- package/dist/activities/agent-turn/finalization-monitor.d.ts +17 -0
- package/dist/activities/agent-turn/finalization.d.ts +1 -1
- package/dist/activities/agent-turn/governance-model.d.ts +0 -2
- package/dist/activities/agent-turn/quiescence.d.ts +1 -0
- package/dist/activities/agent-turn/recovery-warning.d.ts +8 -0
- package/dist/activities/agent-turn/run-credentials.d.ts +0 -1
- package/dist/activities/agent-turn/sandbox-establish.d.ts +1 -1
- package/dist/activities/agent-turn/sandbox-route.d.ts +0 -20
- package/dist/activities/agent-turn/sandbox-runtime.d.ts +1 -1
- package/dist/activities/agent-turn/skill-remove.d.ts +14 -0
- package/dist/activities/agent-turn/skill-search.d.ts +1 -1
- package/dist/activities/agent-turn/stream-attempt.d.ts +1 -0
- package/dist/activities/agent-turn/tool-environment.d.ts +0 -4
- package/dist/activities/agent-turn/turn-context.d.ts +2 -0
- package/dist/activities/agent-turn/turn-sandbox-access.d.ts +3 -0
- package/dist/activities/codex-capacity.d.ts +6 -1
- package/dist/activities/codex-rotation.d.ts +1 -1
- package/dist/activities/context-compaction.d.ts +7 -1
- package/dist/activities/mcp-account-routes.d.ts +22 -0
- package/dist/activities/mcp-credentials.d.ts +12 -13
- package/dist/activities/mcp-operation-authority.d.ts +0 -5
- package/dist/activities/rig-verification.d.ts +2 -3
- package/dist/activities/sandbox-images.d.ts +1 -1
- package/dist/activities/sandbox-lease.d.ts +4 -3
- package/dist/activities/session-state.d.ts +57 -2
- package/dist/activities/types.d.ts +12 -3
- package/dist/{activities-control-HMFGSKC5.js → activities-control-BOLIY3H4.js} +366 -187
- package/dist/activities-control-BOLIY3H4.js.map +1 -0
- package/dist/activities-control.d.ts +110 -2
- package/dist/{activities-turn-AP67JADS.js → activities-turn-2B3WMHBB.js} +767 -471
- package/dist/activities-turn-2B3WMHBB.js.map +1 -0
- package/dist/activities.d.ts +110 -2
- package/dist/artifact-materializer-entry.js +5 -2
- package/dist/artifact-materializer-entry.js.map +1 -1
- package/dist/artifact-outbox-entry.js +2 -2
- package/dist/artifact-outbox-entry.js.map +1 -1
- package/dist/{chunk-A4XQWVZK.js → chunk-I7HKKMUJ.js} +77 -1
- package/dist/chunk-I7HKKMUJ.js.map +1 -0
- package/dist/{chunk-CYITU5UH.js → chunk-TVEE2UUC.js} +275 -146
- package/dist/chunk-TVEE2UUC.js.map +1 -0
- package/dist/editable-artifact-materializer-service.d.ts +2 -2
- package/dist/editable-artifact-outbox-service.d.ts +2 -2
- package/dist/index.d.ts +55 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/observability-metrics.d.ts +11 -3
- package/dist/retained-process-diagnostics.d.ts +4 -0
- package/dist/sandbox-resume.d.ts +2 -12
- package/dist/sandbox-routing.d.ts +5 -3
- package/dist/sandbox-snapshot-diagnostics.d.ts +20 -0
- package/dist/warm-capture-holder.d.ts +3 -0
- package/dist/workflow-bundle.js +51 -11
- package/package.json +15 -15
- package/src/activities/agent-turn/agent-build.ts +42 -5
- package/src/activities/agent-turn/claim.ts +12 -0
- package/src/activities/agent-turn/codex-capacity.ts +26 -4
- package/src/activities/agent-turn/codex.ts +6 -2
- package/src/activities/agent-turn/compaction-prep.ts +53 -72
- package/src/activities/agent-turn/errors.ts +98 -10
- package/src/activities/agent-turn/failure-settlement.ts +49 -28
- package/src/activities/agent-turn/finalization-monitor.ts +121 -0
- package/src/activities/agent-turn/finalization.ts +79 -21
- package/src/activities/agent-turn/governance-model.ts +15 -19
- package/src/activities/agent-turn/history.ts +24 -10
- package/src/activities/agent-turn/quiescence.ts +3 -0
- package/src/activities/agent-turn/recovery-warning.ts +18 -0
- package/src/activities/agent-turn/run-credentials.ts +3 -7
- package/src/activities/agent-turn/run.ts +68 -52
- package/src/activities/agent-turn/sandbox-establish.ts +10 -2
- package/src/activities/agent-turn/sandbox-route.ts +0 -20
- package/src/activities/agent-turn/sandbox-runtime.ts +40 -7
- package/src/activities/agent-turn/skill-remove.ts +46 -0
- package/src/activities/agent-turn/skill-save.ts +1 -1
- package/src/activities/agent-turn/skill-search.ts +1 -1
- package/src/activities/agent-turn/skill-selection.ts +2 -0
- package/src/activities/agent-turn/skill-tools.ts +10 -5
- package/src/activities/agent-turn/stream-attempt.ts +38 -15
- package/src/activities/agent-turn/tool-environment.ts +44 -29
- package/src/activities/agent-turn/turn-context.ts +3 -0
- package/src/activities/agent-turn/turn-sandbox-access.ts +7 -1
- package/src/activities/capabilities.ts +17 -2
- package/src/activities/codex-capacity.ts +38 -9
- package/src/activities/codex-rotation.ts +18 -5
- package/src/activities/context-compaction.ts +26 -4
- package/src/activities/goals.ts +1 -0
- package/src/activities/google-drive-publication.ts +1 -3
- package/src/activities/mcp-account-routes.ts +139 -0
- package/src/activities/mcp-credentials.ts +83 -194
- package/src/activities/mcp-operation-authority.ts +16 -41
- package/src/activities/parent-wake.ts +8 -0
- package/src/activities/rig-verification.ts +38 -37
- package/src/activities/run-input.ts +25 -16
- package/src/activities/sandbox-images.ts +0 -1
- package/src/activities/sandbox-lease.ts +177 -44
- package/src/activities/scheduled-tasks.ts +118 -93
- package/src/activities/session-state.ts +83 -3
- package/src/activities/types.ts +18 -2
- package/src/activities/workflow-wake.ts +2 -17
- package/src/editable-artifact-materializer-service.ts +7 -3
- package/src/editable-artifact-outbox-service.ts +4 -4
- package/src/github-rest-mcp.ts +0 -1
- package/src/index.ts +2 -1
- package/src/observability-metrics.ts +105 -2
- package/src/personal-github-git-credentials.ts +0 -1
- package/src/pr-review-credentials.ts +0 -2
- package/src/retained-process-diagnostics.ts +50 -0
- package/src/sandbox-resume.ts +58 -73
- package/src/sandbox-routing.ts +44 -19
- package/src/sandbox-snapshot-diagnostics.ts +112 -0
- package/src/warm-capture-holder.ts +15 -0
- package/src/workflows/session.ts +64 -2
- package/dist/activities/agent-turn/knowledge-sources.d.ts +0 -20
- package/dist/activities/packs.d.ts +0 -55
- package/dist/activities-control-HMFGSKC5.js.map +0 -1
- package/dist/activities-turn-AP67JADS.js.map +0 -1
- package/dist/chunk-A4XQWVZK.js.map +0 -1
- package/dist/chunk-CYITU5UH.js.map +0 -1
- package/src/activities/agent-turn/knowledge-sources.ts +0 -86
- package/src/activities/packs.ts +0 -166
|
@@ -41,7 +41,7 @@ export function createSkillSaveAttemptToolDefinition(input: {
|
|
|
41
41
|
codemodePath: ["opengeni", "skill_save"],
|
|
42
42
|
title: "Save Skill text",
|
|
43
43
|
description:
|
|
44
|
-
"Create or edit a
|
|
44
|
+
"Create or edit a Skill for reusable procedures or context-specific and personal behavioral preferences, not retrieval-only facts. Put its applicability in the SKILL.md description so the prompt index can guide skill_read; the full body is loaded when relevant. Short always-on workspace rules use instruction_policy_save. Supply only changed UTF-8 text files and explicit deletions; omitted files are preserved. No sandbox is needed. Use the revision and scope version returned by Skill discovery for stale-write protection. For creation choose a new UUID, set expectedRevisionId to null and expectedScopeVersion to 1. The effective Skills setting governs publication: Automatic publishes, Review first saves a pending revision in Knowledge > Needs review while the task continues, and Off prevents agent authoring. Report the actual receipt without asking an approval question. Private chats save personal Skills; shared chats save workspace Skills. Preserve the intended scope; another destination is not a workaround for unavailable scope or learning restrictions.",
|
|
45
45
|
inputSchema: {
|
|
46
46
|
type: "object",
|
|
47
47
|
properties: {
|
|
@@ -8,7 +8,7 @@ export type WorkspaceSkillSearchEntry = Readonly<{
|
|
|
8
8
|
revisionId?: string;
|
|
9
9
|
scopeVersion?: number;
|
|
10
10
|
installationVersion?: number;
|
|
11
|
-
source?: "workspace" | "builtin" | "session"
|
|
11
|
+
source?: "workspace" | "builtin" | "session";
|
|
12
12
|
}>;
|
|
13
13
|
|
|
14
14
|
export function createSkillSearchAttemptToolDefinition(input: {
|
|
@@ -14,6 +14,8 @@ export function configuredBundledSkillNames(context: BundledSkillConfiguration):
|
|
|
14
14
|
const tools = new Set(context.firstPartyTools);
|
|
15
15
|
const artifacts = () => tools.has("editable_artifact_list") && tools.has("editable_artifact_get");
|
|
16
16
|
const definitions = [
|
|
17
|
+
{ name: "opengeni-help", include: () => true },
|
|
18
|
+
{ name: "opengeni-client", include: () => true },
|
|
17
19
|
{ name: "opengeni-visualize", include: () => true },
|
|
18
20
|
{ name: "document-parsing", include: () => true },
|
|
19
21
|
{ name: "opengeni-skills", include: () => true },
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
readSkill,
|
|
19
19
|
resolveSkillImport,
|
|
20
20
|
saveSkill,
|
|
21
|
+
removeSkill,
|
|
21
22
|
} from "@opengeni/core";
|
|
22
23
|
import {
|
|
23
24
|
buildPortableSkillArtifact,
|
|
@@ -31,6 +32,7 @@ import { createSkillReadAttemptToolDefinition, type SkillReadContent } from "./s
|
|
|
31
32
|
import { createSkillSearchAttemptToolDefinition } from "./skill-search";
|
|
32
33
|
import { createSkillSaveAttemptToolDefinition, type SkillSaveRequest } from "./skill-save";
|
|
33
34
|
import { createSkillInstallAttemptToolDefinition } from "./skill-install";
|
|
35
|
+
import { createSkillRemoveAttemptToolDefinition } from "./skill-remove";
|
|
34
36
|
import {
|
|
35
37
|
createSkillCheckoutAttemptToolDefinition,
|
|
36
38
|
createSkillPublishAttemptToolDefinition,
|
|
@@ -122,6 +124,13 @@ export function createWorkspaceSkillTools(input: {
|
|
|
122
124
|
);
|
|
123
125
|
};
|
|
124
126
|
return [
|
|
127
|
+
createSkillRemoveAttemptToolDefinition({
|
|
128
|
+
authorize,
|
|
129
|
+
remove: async (request) =>
|
|
130
|
+
withReviewState(
|
|
131
|
+
await removeSkill(input.db, { ...context, ...request, actor: input.actor }),
|
|
132
|
+
),
|
|
133
|
+
}),
|
|
125
134
|
createSkillReadAttemptToolDefinition({ authorize, load }),
|
|
126
135
|
createSkillSearchAttemptToolDefinition({
|
|
127
136
|
authorize,
|
|
@@ -140,11 +149,7 @@ export function createWorkspaceSkillTools(input: {
|
|
|
140
149
|
id: entry.id,
|
|
141
150
|
name: entry.artifact.name,
|
|
142
151
|
description: entry.artifact.description || entry.artifact.name,
|
|
143
|
-
source: entry.id.startsWith("builtin:")
|
|
144
|
-
? ("builtin" as const)
|
|
145
|
-
: entry.id.startsWith("session:")
|
|
146
|
-
? ("session" as const)
|
|
147
|
-
: ("pack" as const),
|
|
152
|
+
source: entry.id.startsWith("builtin:") ? ("builtin" as const) : ("session" as const),
|
|
148
153
|
})),
|
|
149
154
|
],
|
|
150
155
|
publicSearch: createPublicSkillSearchClient(input.settings),
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
interruptionKindForCallItem,
|
|
23
23
|
releaseMcpResultCustomDataFromSdkEvent,
|
|
24
24
|
findCompactionNeededError,
|
|
25
|
+
compactionProviderRejection,
|
|
25
26
|
withRunCredentialsSession,
|
|
26
27
|
runOwnedSandboxSetup,
|
|
27
28
|
type SandboxFileDownload,
|
|
@@ -69,6 +70,7 @@ import {
|
|
|
69
70
|
isCompletedGeneratedImageSdkEvent,
|
|
70
71
|
} from "../generated-images";
|
|
71
72
|
import { ToolResultSpill } from "./tool-result-spill";
|
|
73
|
+
import { ownedTurnSandboxForAgent } from "./turn-sandbox-access";
|
|
72
74
|
import { createTurnCredentialLeases } from "./credential-leases";
|
|
73
75
|
import { createTurnMediaArtifacts } from "./media-artifacts";
|
|
74
76
|
import { createTurnHistorySink } from "./history-sink";
|
|
@@ -96,6 +98,7 @@ import {
|
|
|
96
98
|
compactionFailureReason,
|
|
97
99
|
safeErrorDiagnostic,
|
|
98
100
|
compactionFailureReasonFromError,
|
|
101
|
+
compactionFailureTurnEventPayload,
|
|
99
102
|
isCompactionSummaryFailure,
|
|
100
103
|
PostCompactionContinuationEmptyError,
|
|
101
104
|
shouldRecoverCompactionProviderFailure,
|
|
@@ -221,7 +224,7 @@ export type TurnStreamAttemptDeps = {
|
|
|
221
224
|
activeSandboxBackend: Settings["sandboxBackend"] | undefined;
|
|
222
225
|
groupBoxBackend: Settings["sandboxBackend"];
|
|
223
226
|
turnExecutionPolicy: TurnExecutionPolicyV1;
|
|
224
|
-
turn: { executionGeneration: number; model: string };
|
|
227
|
+
turn: { executionGeneration: number; model: string; source?: string };
|
|
225
228
|
trigger: NonNullable<Awaited<ReturnType<typeof getSessionEvent>>>;
|
|
226
229
|
humanInputResume: Awaited<ReturnType<typeof getHumanInputResumeForEvent>>;
|
|
227
230
|
attachPendingUpdatesAfterOpenSuffix: () => Promise<boolean>;
|
|
@@ -650,8 +653,7 @@ export async function runTurnStreamAttempt(
|
|
|
650
653
|
});
|
|
651
654
|
}
|
|
652
655
|
};
|
|
653
|
-
const ownedEstablished =
|
|
654
|
-
sandboxState.resolvedSandbox?.established ?? sandboxState.lazyOwnedSandbox;
|
|
656
|
+
const ownedEstablished = ownedTurnSandboxForAgent(sandboxState);
|
|
655
657
|
const runStreamOnce = async (): ReturnType<OpenGeniRuntime["runStream"]> => {
|
|
656
658
|
const eagerResolvedSandbox = sandboxState.resolvedSandbox;
|
|
657
659
|
// Eager owned sessions must settle the exact platform-setup provider
|
|
@@ -759,8 +761,8 @@ export async function runTurnStreamAttempt(
|
|
|
759
761
|
eventing.firstModelRequestPreparationStartedAt = performance.now();
|
|
760
762
|
eventing.firstModelRequestCheckpointAt = eventing.firstModelRequestPreparationStartedAt;
|
|
761
763
|
}
|
|
762
|
-
const
|
|
763
|
-
let
|
|
764
|
+
const streamInitializationStartedAt = performance.now();
|
|
765
|
+
let streamInitializationOutcome: "completed" | "failed" = "completed";
|
|
764
766
|
try {
|
|
765
767
|
// This histogram describes worker preparation until the first entry
|
|
766
768
|
// into the runtime. Lazy SDK request preparation and the durable
|
|
@@ -894,15 +896,15 @@ export async function runTurnStreamAttempt(
|
|
|
894
896
|
: {}),
|
|
895
897
|
});
|
|
896
898
|
} catch (error) {
|
|
897
|
-
|
|
899
|
+
streamInitializationOutcome = "failed";
|
|
898
900
|
throw error;
|
|
899
901
|
} finally {
|
|
900
902
|
recordTurnStartupPhase(observability, {
|
|
901
|
-
phase: "
|
|
903
|
+
phase: "runtime_stream_initialization",
|
|
902
904
|
provider: turnExecutionPolicy.providerId,
|
|
903
905
|
backend: activeSandboxBackend ?? groupBoxBackend,
|
|
904
|
-
outcome:
|
|
905
|
-
durationSeconds: (performance.now() -
|
|
906
|
+
outcome: streamInitializationOutcome,
|
|
907
|
+
durationSeconds: (performance.now() - streamInitializationStartedAt) / 1_000,
|
|
906
908
|
});
|
|
907
909
|
}
|
|
908
910
|
};
|
|
@@ -1747,6 +1749,7 @@ export async function runTurnStreamAttempt(
|
|
|
1747
1749
|
return claimedResult({ status: "cancelled" });
|
|
1748
1750
|
}
|
|
1749
1751
|
if (
|
|
1752
|
+
turn.source !== "compaction" &&
|
|
1750
1753
|
generateSessionTitleInParallel &&
|
|
1751
1754
|
runtime.generateSessionTitle &&
|
|
1752
1755
|
!runtimeCancellationSignal.aborted
|
|
@@ -1822,6 +1825,7 @@ export async function runTurnStreamAttempt(
|
|
|
1822
1825
|
let compacted = false;
|
|
1823
1826
|
let compactionHandled = false;
|
|
1824
1827
|
let compactionFailureMessage: string | null = null;
|
|
1828
|
+
let compactionFailureError: unknown = null;
|
|
1825
1829
|
let compactionRequestCleared = false;
|
|
1826
1830
|
try {
|
|
1827
1831
|
const outcome = await forceContextCompaction(
|
|
@@ -1856,6 +1860,7 @@ export async function runTurnStreamAttempt(
|
|
|
1856
1860
|
{
|
|
1857
1861
|
clearRequestedCompaction: recoveryKind === "operator",
|
|
1858
1862
|
publishLiveEvents: publishCompactionLiveEvents,
|
|
1863
|
+
providerRejection: compactionProviderRejection(compactError),
|
|
1859
1864
|
},
|
|
1860
1865
|
);
|
|
1861
1866
|
compactionRequestCleared = landmark.requestConsumed;
|
|
@@ -1863,6 +1868,7 @@ export async function runTurnStreamAttempt(
|
|
|
1863
1868
|
await finishParallelSessionTitle();
|
|
1864
1869
|
const deferredSteer = await settleDeferredSteerAfterCompaction();
|
|
1865
1870
|
if (deferredSteer) return deferredSteer;
|
|
1871
|
+
compactionFailureError = compactError;
|
|
1866
1872
|
compactionFailureMessage = String(compactionFailureReasonFromError(compactError));
|
|
1867
1873
|
observability.warn("context compaction recovery compaction failed", {
|
|
1868
1874
|
sessionId: input.sessionId,
|
|
@@ -1880,13 +1886,9 @@ export async function runTurnStreamAttempt(
|
|
|
1880
1886
|
events: [
|
|
1881
1887
|
{
|
|
1882
1888
|
type: "turn.failed",
|
|
1883
|
-
payload: {
|
|
1889
|
+
payload: compactionFailureTurnEventPayload(compactionFailureError, {
|
|
1884
1890
|
error: errorMessage,
|
|
1885
|
-
|
|
1886
|
-
retryable: false,
|
|
1887
|
-
recovery: "user_message",
|
|
1888
|
-
compacted: false,
|
|
1889
|
-
},
|
|
1891
|
+
}),
|
|
1890
1892
|
},
|
|
1891
1893
|
{
|
|
1892
1894
|
type: "session.status.changed",
|
|
@@ -1919,6 +1921,27 @@ export async function runTurnStreamAttempt(
|
|
|
1919
1921
|
await finishParallelSessionTitle();
|
|
1920
1922
|
const deferredSteer = await settleDeferredSteerAfterCompaction();
|
|
1921
1923
|
if (deferredSteer) return deferredSteer;
|
|
1924
|
+
if (turn.source === "compaction") {
|
|
1925
|
+
const settled = await eventing.settle!({
|
|
1926
|
+
events: [
|
|
1927
|
+
{
|
|
1928
|
+
type: "turn.completed",
|
|
1929
|
+
payload: {
|
|
1930
|
+
maintenance: "context_compaction",
|
|
1931
|
+
result: compacted ? "compacted" : "already_applied",
|
|
1932
|
+
},
|
|
1933
|
+
},
|
|
1934
|
+
{ type: "session.status.changed", payload: { status: "idle" } },
|
|
1935
|
+
],
|
|
1936
|
+
turnStatus: "completed",
|
|
1937
|
+
sessionStatus: "idle",
|
|
1938
|
+
activeTurnId: null,
|
|
1939
|
+
});
|
|
1940
|
+
if (!settled) return claimedResult({ status: "cancelled" });
|
|
1941
|
+
control.turnMetricOutcome = "completed";
|
|
1942
|
+
control.activityStatus = "idle";
|
|
1943
|
+
return claimedResult({ status: "idle" });
|
|
1944
|
+
}
|
|
1922
1945
|
// Codex parity: compaction remains inside the same logical turn and
|
|
1923
1946
|
// the same activity. Rebuild the model-visible history from the
|
|
1924
1947
|
// durable replacement and continue the sampling loop; do not create
|
|
@@ -38,6 +38,11 @@ import {
|
|
|
38
38
|
resolveGoogleDrivePublicationTarget,
|
|
39
39
|
} from "../google-drive-publication";
|
|
40
40
|
import { connectionTokenResolverForTurn } from "../mcp-credentials";
|
|
41
|
+
import {
|
|
42
|
+
accountRouteAuthNeededPayload,
|
|
43
|
+
expandApiIntegrationAccountRoutes,
|
|
44
|
+
expandMcpAccountRoutes,
|
|
45
|
+
} from "../mcp-account-routes";
|
|
41
46
|
import { createMcpOperationPersistence } from "@opengeni/db/mcp-operations";
|
|
42
47
|
import { createMcpOperationReadStore } from "../mcp-operation-store";
|
|
43
48
|
import { createMcpOperationObserverResolver } from "../mcp-operation-observer";
|
|
@@ -51,7 +56,7 @@ import { buildCodexTokenResolver } from "../codex-auth";
|
|
|
51
56
|
import { CODEX_CLIENT_VERSION } from "@opengeni/codex";
|
|
52
57
|
import { mergeResourceRefs } from "../common";
|
|
53
58
|
import {
|
|
54
|
-
|
|
59
|
+
workspaceSessionToolPolicyDefaultServerIds,
|
|
55
60
|
loadRigDefaultVariableSetEnvironment,
|
|
56
61
|
mergeRigDefaultVariableSetEnvironment,
|
|
57
62
|
buildApiIntegrationMcpServers,
|
|
@@ -64,7 +69,7 @@ import {
|
|
|
64
69
|
import { loadWorkspaceEnvironmentForRunWithCredentials } from "../environment";
|
|
65
70
|
import { withFirstPartyTools } from "../goals";
|
|
66
71
|
import type { TurnActivityServices as ActivityServices, RunAgentTurnInput } from "../types";
|
|
67
|
-
import { recordTurnStartupPhase } from "../../observability-metrics";
|
|
72
|
+
import { recordToolPreparationPhase, recordTurnStartupPhase } from "../../observability-metrics";
|
|
68
73
|
import { ToolResultSpill } from "./tool-result-spill";
|
|
69
74
|
import { createTurnMediaArtifacts } from "./media-artifacts";
|
|
70
75
|
import { SandboxChannelAService } from "@opengeni/runtime/sandbox";
|
|
@@ -103,7 +108,6 @@ import { createListModelsAttemptToolDefinition } from "./list-models";
|
|
|
103
108
|
import { createWorkspaceSkillTools } from "./skill-tools";
|
|
104
109
|
import { loadConfiguredBundledSkills } from "./skill-selection";
|
|
105
110
|
import { guardSkillFilesystem } from "./skill-transfer";
|
|
106
|
-
import type { RuntimeSkillActivation } from "@opengeni/runtime";
|
|
107
111
|
|
|
108
112
|
export type PrepareTurnToolPolicyDeps = {
|
|
109
113
|
input: RunAgentTurnInput;
|
|
@@ -125,7 +129,6 @@ export type PrepareTurnToolRuntimeDeps = {
|
|
|
125
129
|
input: import("../types").RunKnowledgeSourceSyncBatchInput,
|
|
126
130
|
) => Promise<import("../types").RunKnowledgeSourceSyncBatchResult>)
|
|
127
131
|
| undefined;
|
|
128
|
-
selectedSkillActivations: readonly RuntimeSkillActivation[];
|
|
129
132
|
input: RunAgentTurnInput;
|
|
130
133
|
catalogSourceSettings: Settings;
|
|
131
134
|
db: ActivityServices["db"];
|
|
@@ -134,7 +137,6 @@ export type PrepareTurnToolRuntimeDeps = {
|
|
|
134
137
|
objectStorage: ActivityServices["objectStorage"];
|
|
135
138
|
observability: ActivityServices["observability"];
|
|
136
139
|
cancellationSignal: AbortSignal | undefined;
|
|
137
|
-
connectionCredentials: ActivityServices["connectionCredentials"];
|
|
138
140
|
eventing: EventingState;
|
|
139
141
|
attempt: AttemptIdentityState;
|
|
140
142
|
sandboxState: SandboxRuntimeState;
|
|
@@ -152,7 +154,6 @@ export type PrepareTurnToolRuntimeDeps = {
|
|
|
152
154
|
lazyToolTransport: GovernanceModelOk["lazyToolTransport"];
|
|
153
155
|
turnTools: ReturnType<typeof withFirstPartyTools>;
|
|
154
156
|
connectionScope: { accountId: string; workspaceId: string };
|
|
155
|
-
hostCredentialRootSessionId: string | null;
|
|
156
157
|
sandboxArtifactRuntime: ReturnType<typeof sandboxArtifactRuntimeAdmission>;
|
|
157
158
|
activeSandboxBackend: Settings["sandboxBackend"] | undefined;
|
|
158
159
|
groupBoxBackend: Settings["sandboxBackend"];
|
|
@@ -212,7 +213,15 @@ export async function prepareTurnToolPolicy(deps: PrepareTurnToolPolicyDeps) {
|
|
|
212
213
|
? scheduledEffectiveMcpServerIds.filter((id) => currentMcpServerIds.has(id))
|
|
213
214
|
: [...currentMcpServerIds],
|
|
214
215
|
defaultMcpServerIds:
|
|
215
|
-
scheduledEffectiveMcpServerIds ??
|
|
216
|
+
scheduledEffectiveMcpServerIds ??
|
|
217
|
+
(session.toolPolicy.mode === "workspace_default"
|
|
218
|
+
? await workspaceSessionToolPolicyDefaultServerIds(
|
|
219
|
+
db,
|
|
220
|
+
input.workspaceId,
|
|
221
|
+
capabilitySettings,
|
|
222
|
+
fileAuthoritySubjectId ?? undefined,
|
|
223
|
+
)
|
|
224
|
+
: []),
|
|
216
225
|
});
|
|
217
226
|
const mcpAvailabilityNote = unavailableMcpOperationalContext({
|
|
218
227
|
droppedIds: resolvedToolPolicy.effectivePolicy.droppedIds,
|
|
@@ -349,7 +358,6 @@ export async function prepareTurnToolRuntime(deps: PrepareTurnToolRuntimeDeps) {
|
|
|
349
358
|
objectStorage,
|
|
350
359
|
observability,
|
|
351
360
|
cancellationSignal,
|
|
352
|
-
connectionCredentials,
|
|
353
361
|
eventing,
|
|
354
362
|
attempt,
|
|
355
363
|
sandboxState,
|
|
@@ -361,10 +369,9 @@ export async function prepareTurnToolRuntime(deps: PrepareTurnToolRuntimeDeps) {
|
|
|
361
369
|
codexAppsCredentialId,
|
|
362
370
|
turnExecutionPolicy,
|
|
363
371
|
trigger,
|
|
364
|
-
runSettings,
|
|
372
|
+
runSettings: canonicalRunSettings,
|
|
365
373
|
lazyToolTransport,
|
|
366
|
-
turnTools,
|
|
367
|
-
hostCredentialRootSessionId,
|
|
374
|
+
turnTools: canonicalTurnTools,
|
|
368
375
|
sandboxArtifactRuntime,
|
|
369
376
|
activeSandboxBackend,
|
|
370
377
|
groupBoxBackend,
|
|
@@ -377,26 +384,29 @@ export async function prepareTurnToolRuntime(deps: PrepareTurnToolRuntimeDeps) {
|
|
|
377
384
|
throwIfTurnCancelled,
|
|
378
385
|
} = deps;
|
|
379
386
|
|
|
387
|
+
const accountRoutes = expandMcpAccountRoutes({
|
|
388
|
+
settings: canonicalRunSettings,
|
|
389
|
+
tools: canonicalTurnTools,
|
|
390
|
+
bindings: turn.mcpAccountBindings,
|
|
391
|
+
});
|
|
392
|
+
const runSettings = accountRoutes.settings;
|
|
393
|
+
const turnTools = accountRoutes.tools;
|
|
380
394
|
const toolContextPreparationStartedAt = performance.now();
|
|
381
395
|
throwIfWorkerShuttingDown();
|
|
382
396
|
throwIfTurnCancelled();
|
|
383
|
-
const mcpCredentialRootSessionId =
|
|
384
|
-
connectionCredentials?.mcpCredentials && hostCredentialRootSessionId
|
|
385
|
-
? hostCredentialRootSessionId
|
|
386
|
-
: input.sessionId;
|
|
387
397
|
// Connection credentials and the optional Apps credential are resolved
|
|
388
398
|
// independently. Inference auth is never an Apps fallback.
|
|
389
399
|
const rawResolveCredential = connectionTokenResolverForTurn({
|
|
390
400
|
db,
|
|
391
401
|
settings: runSettings,
|
|
392
|
-
|
|
402
|
+
canonicalMcpServerIds: canonicalRunSettings.mcpServers
|
|
403
|
+
.filter((server) => server.connectionRef)
|
|
404
|
+
.map((server) => server.id),
|
|
393
405
|
accountId: input.accountId,
|
|
394
406
|
workspaceId: input.workspaceId,
|
|
395
407
|
sessionId: input.sessionId,
|
|
396
|
-
rootSessionId: mcpCredentialRootSessionId,
|
|
397
408
|
attemptId: input.attemptId,
|
|
398
409
|
turn,
|
|
399
|
-
observability,
|
|
400
410
|
});
|
|
401
411
|
const personalConnectionDelegations = turn.personalConnectionDelegations;
|
|
402
412
|
const delegatedMembershipChecks = new Map<string, Promise<boolean>>();
|
|
@@ -463,16 +473,24 @@ export async function prepareTurnToolRuntime(deps: PrepareTurnToolRuntimeDeps) {
|
|
|
463
473
|
return;
|
|
464
474
|
}
|
|
465
475
|
await eventing.publish!(
|
|
466
|
-
[
|
|
476
|
+
[
|
|
477
|
+
{
|
|
478
|
+
type: "tool.auth_needed",
|
|
479
|
+
payload: rollingSafeToolAuthNeededPayload(
|
|
480
|
+
accountRouteAuthNeededPayload(payload, turn.mcpAccountBindings),
|
|
481
|
+
),
|
|
482
|
+
},
|
|
483
|
+
],
|
|
467
484
|
true,
|
|
468
485
|
);
|
|
469
486
|
};
|
|
470
|
-
const selectedApiIntegrationServerIds = new Set(turnTools.map((tool) => tool.id));
|
|
471
487
|
const apiIntegrationMcpServers = buildApiIntegrationMcpServers({
|
|
472
488
|
settings: runSettings,
|
|
473
|
-
integrations:
|
|
474
|
-
|
|
475
|
-
|
|
489
|
+
integrations: expandApiIntegrationAccountRoutes({
|
|
490
|
+
integrations: installedApiIntegrations,
|
|
491
|
+
bindings: turn.mcpAccountBindings,
|
|
492
|
+
tools: turnTools,
|
|
493
|
+
}),
|
|
476
494
|
authority: {
|
|
477
495
|
accountId: input.accountId,
|
|
478
496
|
workspaceId: input.workspaceId,
|
|
@@ -608,7 +626,6 @@ export async function prepareTurnToolRuntime(deps: PrepareTurnToolRuntimeDeps) {
|
|
|
608
626
|
});
|
|
609
627
|
const selectedSkills = [
|
|
610
628
|
...bundledSkills,
|
|
611
|
-
...deps.selectedSkillActivations.map((entry) => ({ id: entry.id, artifact: entry.artifact })),
|
|
612
629
|
...session.skills.map((skill) => ({
|
|
613
630
|
id: `session:${session.id}:${skill.name}`,
|
|
614
631
|
artifact: skill,
|
|
@@ -950,6 +967,7 @@ export async function prepareTurnToolRuntime(deps: PrepareTurnToolRuntimeDeps) {
|
|
|
950
967
|
try {
|
|
951
968
|
eventing.preparedTools = await waitForTurnOperation(
|
|
952
969
|
runtime.prepareTools(githubRestMcp.settings, githubRestMcp.tools, {
|
|
970
|
+
mcpAccountLabels: accountRoutes.accountLabels,
|
|
953
971
|
accountId: input.accountId,
|
|
954
972
|
workspaceId: input.workspaceId,
|
|
955
973
|
sessionId: input.sessionId,
|
|
@@ -1002,13 +1020,10 @@ export async function prepareTurnToolRuntime(deps: PrepareTurnToolRuntimeDeps) {
|
|
|
1002
1020
|
localMcpServers,
|
|
1003
1021
|
...(deferNonEagerToolPreparation ? { deferNonEagerUntilToolDemand: true } : {}),
|
|
1004
1022
|
onPreparationPhase: (measurement) => {
|
|
1005
|
-
|
|
1006
|
-
|
|
1023
|
+
recordToolPreparationPhase(observability, {
|
|
1024
|
+
...measurement,
|
|
1007
1025
|
provider: turnExecutionPolicy.providerId,
|
|
1008
1026
|
backend: activeSandboxBackend ?? groupBoxBackend,
|
|
1009
|
-
outcome: measurement.outcome,
|
|
1010
|
-
durationSeconds: measurement.durationSeconds,
|
|
1011
|
-
count: githubRestMcp.tools.length,
|
|
1012
1027
|
});
|
|
1013
1028
|
},
|
|
1014
1029
|
onAttemptToolCatalog: async (catalog) => {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TurnHeartbeatDetails } from "../../op-journal";
|
|
1
2
|
import type { Settings } from "@opengeni/config";
|
|
2
3
|
import type { CodexUsageHeaderSnapshot } from "@opengeni/codex";
|
|
3
4
|
import type { AppendEventInput, ApplySessionTurnSettlementInput } from "@opengeni/db";
|
|
@@ -123,6 +124,7 @@ export type RenewalState = {
|
|
|
123
124
|
};
|
|
124
125
|
|
|
125
126
|
export type EventingState = {
|
|
127
|
+
heartbeatDetails: TurnHeartbeatDetails | null;
|
|
126
128
|
heartbeatTimer: ReturnType<typeof startActivityHeartbeat> | undefined;
|
|
127
129
|
batcher: ReturnType<typeof createRuntimeBatcher> | null;
|
|
128
130
|
preparedTools: Awaited<ReturnType<OpenGeniRuntime["prepareTools"]>> | null;
|
|
@@ -254,6 +256,7 @@ export function createTurnContext(input: {
|
|
|
254
256
|
publishedRunCredentialNotices: new Set(),
|
|
255
257
|
},
|
|
256
258
|
eventing: {
|
|
259
|
+
heartbeatDetails: null,
|
|
257
260
|
heartbeatTimer: undefined,
|
|
258
261
|
batcher: null,
|
|
259
262
|
preparedTools: null,
|
|
@@ -13,6 +13,12 @@ export type ResolvedTurnSandboxAccess = Readonly<{
|
|
|
13
13
|
leaseEpoch: number;
|
|
14
14
|
}>;
|
|
15
15
|
|
|
16
|
+
/** The lazy wrapper remains the agent's owner even after provisioning fills
|
|
17
|
+
* resolvedSandbox with the raw provider handle for capture and release. */
|
|
18
|
+
export function ownedTurnSandboxForAgent(sandboxState: TurnSandboxAccessState) {
|
|
19
|
+
return sandboxState.lazyOwnedSandbox ?? sandboxState.resolvedSandbox?.established;
|
|
20
|
+
}
|
|
21
|
+
|
|
16
22
|
/** Join the turn's canonical sandbox boundary and return its active routed session. */
|
|
17
23
|
export async function resolveTurnSandboxAccess(
|
|
18
24
|
sandboxState: TurnSandboxAccessState,
|
|
@@ -24,7 +30,7 @@ export async function resolveTurnSandboxAccess(
|
|
|
24
30
|
sandbox = await sandboxState.turnSandboxProvisioner.get();
|
|
25
31
|
}
|
|
26
32
|
|
|
27
|
-
const session = (sandboxState
|
|
33
|
+
const session = (ownedTurnSandboxForAgent(sandboxState)?.session ??
|
|
28
34
|
sandbox?.established.session ??
|
|
29
35
|
sdkOwnedSandboxSession) as ChannelASession | null;
|
|
30
36
|
if (!session) throw new Error(unavailableMessage);
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
getWorkspaceOpenRouterCustomModelForExecution,
|
|
23
23
|
listSessionMcpServerMetadata,
|
|
24
24
|
listSessionMcpServersForRun,
|
|
25
|
+
getSessionAttemptMcpApprovalPolicies,
|
|
25
26
|
listWorkspaceGatewayCustomModels,
|
|
26
27
|
listWorkspaceOpenRouterCustomModels,
|
|
27
28
|
workspaceCodexSubscriptionActive,
|
|
@@ -47,10 +48,24 @@ export async function settingsWithSessionMcpServersForRun(
|
|
|
47
48
|
},
|
|
48
49
|
): Promise<Settings> {
|
|
49
50
|
const encryptionKey = environmentsEncryptionKeyBytes(settings);
|
|
51
|
+
const policies = await getSessionAttemptMcpApprovalPolicies(
|
|
52
|
+
db,
|
|
53
|
+
workspaceId,
|
|
54
|
+
sessionId,
|
|
55
|
+
attemptId,
|
|
56
|
+
);
|
|
57
|
+
const policySettings = {
|
|
58
|
+
...settings,
|
|
59
|
+
mcpServers: settings.mcpServers.map((server) =>
|
|
60
|
+
Object.hasOwn(policies, server.id)
|
|
61
|
+
? { ...server, requireApproval: policies[server.id] }
|
|
62
|
+
: server,
|
|
63
|
+
),
|
|
64
|
+
};
|
|
50
65
|
if (!encryptionKey) {
|
|
51
66
|
const metadata = await listSessionMcpServerMetadata(db, workspaceId, sessionId);
|
|
52
67
|
if (metadata.length === 0) {
|
|
53
|
-
return
|
|
68
|
+
return policySettings;
|
|
54
69
|
}
|
|
55
70
|
if (metadata.some((server) => server.headerNames.length > 0)) {
|
|
56
71
|
throw new Error(
|
|
@@ -69,7 +84,7 @@ export async function settingsWithSessionMcpServersForRun(
|
|
|
69
84
|
// overlaid into settings. A session projection read earlier in the turn can
|
|
70
85
|
// be stale after a concurrent mcpCredentialUpdates renewal.
|
|
71
86
|
options?.onResolvedServers?.(servers);
|
|
72
|
-
return settingsWithSessionMcpServers(
|
|
87
|
+
return settingsWithSessionMcpServers(policySettings, servers);
|
|
73
88
|
}
|
|
74
89
|
|
|
75
90
|
export function settingsWithSessionMcpServers(
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
connectionModelAllowed,
|
|
2
3
|
armCodexCapacityWait,
|
|
3
4
|
fetchCodexUsageForAccount,
|
|
4
5
|
getCodexCapacityWaitForSession,
|
|
@@ -90,8 +91,15 @@ export async function signalPendingCodexCapacityWakeTargets(
|
|
|
90
91
|
|
|
91
92
|
export function codexCapacityDecision<TPolicyScope = never, TUnavailableDiagnostic = never>(
|
|
92
93
|
context: CodexCapacitySelectionContext<TPolicyScope, TUnavailableDiagnostic>,
|
|
94
|
+
now = new Date(),
|
|
93
95
|
): ReturnType<Parameters<typeof reconcileCodexCapacityWaitDb>[2]> {
|
|
94
|
-
|
|
96
|
+
context = {
|
|
97
|
+
...context,
|
|
98
|
+
accounts: context.accounts.filter(
|
|
99
|
+
(account) =>
|
|
100
|
+
!context.modelId || connectionModelAllowed(account.allowedModelIds, context.modelId),
|
|
101
|
+
),
|
|
102
|
+
};
|
|
95
103
|
const selected = selectCodexCredentialLeaseForTurn({
|
|
96
104
|
context,
|
|
97
105
|
sessionId: context.sessionId,
|
|
@@ -114,8 +122,12 @@ export function codexCapacityDecision<TPolicyScope = never, TUnavailableDiagnost
|
|
|
114
122
|
credentialId: selected.credentialId,
|
|
115
123
|
diagnostic: {
|
|
116
124
|
connectedCount: context.accounts.length,
|
|
117
|
-
eligibleCount: context.accounts.filter(
|
|
118
|
-
|
|
125
|
+
eligibleCount: context.accounts.filter(
|
|
126
|
+
(account) =>
|
|
127
|
+
(account.id === selected.credentialId ||
|
|
128
|
+
!context.failedCredentialIds?.includes(account.id)) &&
|
|
129
|
+
isCodexCredentialEligible(account, now),
|
|
130
|
+
).length,
|
|
119
131
|
},
|
|
120
132
|
};
|
|
121
133
|
}
|
|
@@ -134,8 +146,7 @@ export function codexCapacityDecision<TPolicyScope = never, TUnavailableDiagnost
|
|
|
134
146
|
account.status === "active" &&
|
|
135
147
|
account.allocatorEnabled &&
|
|
136
148
|
account.exhaustedKind === "quota" &&
|
|
137
|
-
account.exhaustedUntil !== null
|
|
138
|
-
account.exhaustedUntil > now,
|
|
149
|
+
account.exhaustedUntil !== null,
|
|
139
150
|
);
|
|
140
151
|
const policyAccount = capacityAccounts[0] ?? null;
|
|
141
152
|
const mutationOnlyStatusBlock =
|
|
@@ -162,7 +173,7 @@ export function codexCapacityDecision<TPolicyScope = never, TUnavailableDiagnost
|
|
|
162
173
|
kind: "unavailable",
|
|
163
174
|
earliestResetAt: authoritativeReset,
|
|
164
175
|
resetKind:
|
|
165
|
-
selected.decision.kind === "none" ||
|
|
176
|
+
(selected.decision.kind === "none" && !hasReconcilableQuotaCooldown && !authoritativeReset) ||
|
|
166
177
|
selected.decision.kind === "allocatorDisabled" ||
|
|
167
178
|
mutationOnlyStatusBlock
|
|
168
179
|
? "mutation_only"
|
|
@@ -191,6 +202,16 @@ export async function armAndReconcileCodexCapacityWait(
|
|
|
191
202
|
options: { onArmed?: () => void } = {},
|
|
192
203
|
) {
|
|
193
204
|
const armed = await armCodexCapacityWait(services.db, input);
|
|
205
|
+
if (armed.action === "stopped") {
|
|
206
|
+
options.onArmed?.();
|
|
207
|
+
await publishDurableSessionEvents(
|
|
208
|
+
services.bus,
|
|
209
|
+
input.workspaceId,
|
|
210
|
+
input.sessionId,
|
|
211
|
+
armed.events,
|
|
212
|
+
);
|
|
213
|
+
return armed;
|
|
214
|
+
}
|
|
194
215
|
if (armed.action !== "waiting") return armed;
|
|
195
216
|
|
|
196
217
|
options.onArmed?.();
|
|
@@ -219,8 +240,9 @@ export async function armAndReconcileCodexCapacityWait(
|
|
|
219
240
|
async function refreshCapacityMetadata(
|
|
220
241
|
services: ControlActivityServices,
|
|
221
242
|
workspaceId: string,
|
|
243
|
+
turnId: string,
|
|
222
244
|
): Promise<void> {
|
|
223
|
-
const accounts = await listCodexAccountStatuses(services.db, workspaceId).catch(() => []);
|
|
245
|
+
const accounts = await listCodexAccountStatuses(services.db, workspaceId, turnId).catch(() => []);
|
|
224
246
|
const now = new Date();
|
|
225
247
|
const stale = accounts.filter(
|
|
226
248
|
(account) =>
|
|
@@ -231,7 +253,14 @@ async function refreshCapacityMetadata(
|
|
|
231
253
|
await refreshCodexUsageAndRepairCapacityWaiters(
|
|
232
254
|
stale.map(
|
|
233
255
|
(account) => () =>
|
|
234
|
-
fetchCodexUsageForAccount(
|
|
256
|
+
fetchCodexUsageForAccount(
|
|
257
|
+
services.db,
|
|
258
|
+
services.settings,
|
|
259
|
+
workspaceId,
|
|
260
|
+
account.id,
|
|
261
|
+
undefined,
|
|
262
|
+
turnId,
|
|
263
|
+
),
|
|
235
264
|
),
|
|
236
265
|
() => signalPendingCodexCapacityWakeTargets(services, workspaceId),
|
|
237
266
|
);
|
|
@@ -338,7 +367,7 @@ export function createCodexCapacityActivities(services: () => Promise<ControlAct
|
|
|
338
367
|
if (boundedRefreshAttempted) {
|
|
339
368
|
// This is a bounded secret-safe control-plane quota refresh. It creates no
|
|
340
369
|
// turn, model call, user message, schedule, or entitlement action.
|
|
341
|
-
await refreshCapacityMetadata(resolved, input.workspaceId);
|
|
370
|
+
await refreshCapacityMetadata(resolved, input.workspaceId, current.blockedTurnId);
|
|
342
371
|
}
|
|
343
372
|
const result = await reconcileCodexCapacityWaitDb(
|
|
344
373
|
resolved.db,
|
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
CodexLeaseAccountStatus,
|
|
13
13
|
CodexPinSource,
|
|
14
14
|
} from "@opengeni/db";
|
|
15
|
+
import { connectionModelAllowed } from "@opengeni/db";
|
|
15
16
|
|
|
16
17
|
export type CodexRotationAccount = CodexAccountStatus | CodexLeaseAccountStatus;
|
|
17
18
|
|
|
@@ -21,14 +22,12 @@ export function codexAccountNeedsLiveCapacityRefresh(
|
|
|
21
22
|
CodexAccountStatus,
|
|
22
23
|
"primaryUsedPercent" | "secondaryUsedPercent" | "exhaustedUntil" | "exhaustedKind"
|
|
23
24
|
>,
|
|
24
|
-
|
|
25
|
+
_now: Date,
|
|
25
26
|
): boolean {
|
|
26
27
|
return (
|
|
27
28
|
(account.primaryUsedPercent ?? 0) >= CODEX_USAGE_EXHAUSTED_PCT ||
|
|
28
29
|
(account.secondaryUsedPercent ?? 0) >= CODEX_USAGE_EXHAUSTED_PCT ||
|
|
29
|
-
(account.exhaustedKind === "quota" &&
|
|
30
|
-
account.exhaustedUntil !== null &&
|
|
31
|
-
account.exhaustedUntil > now)
|
|
30
|
+
(account.exhaustedKind === "quota" && account.exhaustedUntil !== null)
|
|
32
31
|
);
|
|
33
32
|
}
|
|
34
33
|
|
|
@@ -524,8 +523,16 @@ export function selectCodexCredentialLeaseForTurn<
|
|
|
524
523
|
sessionLastCredentialId: string | null;
|
|
525
524
|
now: Date;
|
|
526
525
|
}): CodexTurnLeaseSelection {
|
|
527
|
-
const {
|
|
526
|
+
const { activeCredentialId, rotationEnabled, rotationStrategy, existingCredentialId } =
|
|
528
527
|
args.context;
|
|
528
|
+
if (args.context.failoverExhausted) {
|
|
529
|
+
return { credentialId: null, decision: { kind: "none" }, advanceActivePointer: false };
|
|
530
|
+
}
|
|
531
|
+
const accounts = args.context.modelId
|
|
532
|
+
? args.context.accounts.filter((account) =>
|
|
533
|
+
connectionModelAllowed(account.allowedModelIds, args.context.modelId!),
|
|
534
|
+
)
|
|
535
|
+
: args.context.accounts;
|
|
529
536
|
const failedCredentialIds = new Set(args.context.failedCredentialIds ?? []);
|
|
530
537
|
// Normalized: rotation-enabled always behaves as sharded (see
|
|
531
538
|
// effectiveRotationStrategy); the stored value is only ever legacy residue.
|
|
@@ -564,6 +571,9 @@ export function selectCodexCredentialLeaseForTurn<
|
|
|
564
571
|
if (!pinned) {
|
|
565
572
|
return { credentialId: null, decision: { kind: "none" }, advanceActivePointer: false };
|
|
566
573
|
}
|
|
574
|
+
if (failedCredentialIds.has(pinned.id)) {
|
|
575
|
+
return { credentialId: null, decision: { kind: "none" }, advanceActivePointer: false };
|
|
576
|
+
}
|
|
567
577
|
if (!pinned.allocatorEnabled) {
|
|
568
578
|
return {
|
|
569
579
|
credentialId: null,
|
|
@@ -633,6 +643,9 @@ export function selectCodexCredentialLeaseForTurn<
|
|
|
633
643
|
if (!active) {
|
|
634
644
|
return { credentialId: null, decision: { kind: "none" }, advanceActivePointer: false };
|
|
635
645
|
}
|
|
646
|
+
if (failedCredentialIds.has(active.id)) {
|
|
647
|
+
return { credentialId: null, decision: { kind: "none" }, advanceActivePointer: false };
|
|
648
|
+
}
|
|
636
649
|
if (!active.allocatorEnabled) {
|
|
637
650
|
return {
|
|
638
651
|
credentialId: null,
|