@opengeni/worker-bundle 1.0.0-canary.1 → 1.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-run-admission.d.ts +3 -1
- package/dist/activities/agent-turn/admission.d.ts +24 -3
- package/dist/activities/agent-turn/agent-build.d.ts +4 -0
- package/dist/activities/agent-turn/claude-usage-observer.d.ts +58 -0
- package/dist/activities/agent-turn/code-search.d.ts +60 -0
- package/dist/activities/agent-turn/codex-capacity.d.ts +13 -0
- package/dist/activities/agent-turn/codex-plan-entitlement.d.ts +105 -0
- package/dist/activities/agent-turn/codex.d.ts +3 -2
- package/dist/activities/agent-turn/compaction-prep.d.ts +21 -5
- package/dist/activities/agent-turn/credential-leases.d.ts +19 -33
- package/dist/activities/agent-turn/credential-restriction.d.ts +3 -0
- package/dist/activities/agent-turn/errors.d.ts +82 -11
- package/dist/activities/agent-turn/failure-settlement.d.ts +6 -3
- package/dist/activities/agent-turn/final-reply.d.ts +18 -0
- package/dist/activities/agent-turn/finalization-monitor.d.ts +17 -0
- package/dist/activities/agent-turn/finalization.d.ts +4 -2
- package/dist/activities/agent-turn/github-repository-bindings.d.ts +2 -0
- package/dist/activities/agent-turn/governance-model.d.ts +9 -2
- package/dist/activities/agent-turn/history-sink.d.ts +2 -0
- package/dist/activities/agent-turn/history.d.ts +2 -0
- package/dist/activities/agent-turn/input-wait-reply.d.ts +41 -0
- package/dist/activities/agent-turn/media-artifacts.d.ts +5 -3
- package/dist/activities/agent-turn/model-call-admission.d.ts +15 -0
- package/dist/activities/agent-turn/model-usage.d.ts +6 -0
- package/dist/activities/agent-turn/model-wire-dispatch.d.ts +15 -0
- package/dist/activities/agent-turn/optional-repositories.d.ts +59 -0
- package/dist/activities/agent-turn/provider-dispatch-barrier.d.ts +12 -1
- package/dist/activities/agent-turn/quiescence.d.ts +7 -7
- package/dist/activities/agent-turn/recovery-warning.d.ts +9 -0
- package/dist/activities/agent-turn/refresh-credentials.d.ts +6 -0
- package/dist/activities/agent-turn/run-credentials.d.ts +8 -3
- package/dist/activities/agent-turn/sandbox-establish.d.ts +2 -1
- package/dist/activities/agent-turn/sandbox-provision.d.ts +3 -1
- package/dist/activities/agent-turn/sandbox-route.d.ts +3 -20
- package/dist/activities/agent-turn/sandbox-runtime.d.ts +2 -2
- package/dist/activities/agent-turn/session-title.d.ts +34 -2
- package/dist/activities/agent-turn/skill-checkout.d.ts +25 -6
- package/dist/activities/agent-turn/skill-read.d.ts +70 -2
- package/dist/activities/agent-turn/skill-remove.d.ts +14 -0
- package/dist/activities/agent-turn/skill-search.d.ts +3 -1
- package/dist/activities/agent-turn/skill-tools.d.ts +15 -2
- package/dist/activities/agent-turn/skill-transfer.d.ts +18 -6
- package/dist/activities/agent-turn/stream-attempt.d.ts +7 -3
- package/dist/activities/agent-turn/subscription-lease.d.ts +38 -0
- package/dist/activities/agent-turn/tool-environment.d.ts +56 -6
- package/dist/activities/agent-turn/tool-policy.d.ts +43 -0
- package/dist/activities/agent-turn/turn-context.d.ts +29 -0
- package/dist/activities/agent-turn/turn-sandbox-access.d.ts +3 -0
- package/dist/activities/agent-turn/xai-capacity.d.ts +2 -1
- package/dist/activities/automations.d.ts +3 -1
- package/dist/activities/browser-deadline-checkpoint.d.ts +29 -0
- package/dist/activities/codemode-dispatcher.d.ts +9 -2
- package/dist/activities/codex-capacity.d.ts +18 -2
- package/dist/activities/codex-rotation.d.ts +7 -1
- package/dist/activities/context-compaction.d.ts +16 -2
- package/dist/activities/gmail-files.d.ts +32 -0
- package/dist/activities/goals.d.ts +8 -0
- package/dist/activities/knowledge-indexing.d.ts +19 -0
- package/dist/activities/mcp-account-routes.d.ts +3 -0
- package/dist/activities/mcp-credentials.d.ts +12 -13
- package/dist/activities/mcp-operation-authority.d.ts +0 -5
- package/dist/activities/model-call-fact-reconciler.d.ts +34 -0
- package/dist/activities/open-suffix-resume.d.ts +13 -0
- package/dist/activities/parent-wake.d.ts +6 -12
- package/dist/activities/retained-screenshots.d.ts +7 -2
- package/dist/activities/rig-verification.d.ts +2 -3
- package/dist/activities/run-credential-renewal.d.ts +1 -0
- package/dist/activities/run-credentials.d.ts +15 -3
- package/dist/activities/sandbox-images.d.ts +1 -1
- package/dist/activities/sandbox-lease.d.ts +47 -9
- package/dist/activities/scheduled-tasks.d.ts +4 -35
- package/dist/activities/session-state.d.ts +70 -3
- package/dist/activities/types.d.ts +55 -6
- package/dist/activities/workspace-capture.d.ts +2 -0
- package/dist/activities/workspace-credential-provider.d.ts +46 -0
- package/dist/{activities-control-HMFGSKC5.js → activities-control-BNPTRTOE.js} +1896 -418
- package/dist/activities-control-BNPTRTOE.js.map +1 -0
- package/dist/activities-control.d.ts +182 -4
- package/dist/{activities-turn-AP67JADS.js → activities-turn-5LGZOSVU.js} +4476 -1327
- package/dist/activities-turn-5LGZOSVU.js.map +1 -0
- package/dist/activities.d.ts +182 -4
- package/dist/artifact-materializer-entry.js +5 -2
- package/dist/artifact-materializer-entry.js.map +1 -1
- package/dist/artifact-outbox-entry.js +5 -2
- package/dist/artifact-outbox-entry.js.map +1 -1
- package/dist/browser-deadline-checkpoint-contract.d.ts +2 -0
- package/dist/{chunk-A4XQWVZK.js → chunk-3ZTRWDMN.js} +307 -4
- package/dist/chunk-3ZTRWDMN.js.map +1 -0
- package/dist/{chunk-RAMNYSAW.js → chunk-4ER3YXOT.js} +22 -4
- package/dist/chunk-4ER3YXOT.js.map +1 -0
- package/dist/{chunk-CYITU5UH.js → chunk-LJX7Z2KP.js} +1445 -343
- package/dist/chunk-LJX7Z2KP.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/host-export-pump.d.ts +3 -1
- package/dist/host-export.d.ts +1 -1
- package/dist/host-export.js +1 -1
- package/dist/http.d.ts +1 -1
- package/dist/index.d.ts +95 -3
- package/dist/index.js +137 -43
- package/dist/index.js.map +1 -1
- package/dist/observability-metrics.d.ts +118 -8
- package/dist/retained-process-diagnostics.d.ts +4 -0
- package/dist/retained-process-retry.d.ts +7 -0
- package/dist/sandbox-resume.d.ts +55 -13
- package/dist/sandbox-routing.d.ts +5 -3
- package/dist/sandbox-snapshot-diagnostics.d.ts +20 -0
- package/dist/session-event-fanout.d.ts +6 -0
- package/dist/warm-capture-holder.d.ts +3 -0
- package/dist/worker-service-lifecycle.d.ts +12 -0
- package/dist/workflow-bundle.js +273 -41
- package/dist/workflows/activities.d.ts +1 -1
- package/dist/workflows/browser-deadline-checkpoint.d.ts +9 -0
- package/dist/workflows/scheduled-tasks.d.ts +4 -0
- package/dist/workflows/session.d.ts +36 -0
- package/dist/workflows.d.ts +1 -0
- package/package.json +21 -19
- package/src/activities/agent-run-admission.ts +17 -1
- package/src/activities/agent-turn/admission.ts +66 -3
- package/src/activities/agent-turn/agent-build.ts +84 -10
- package/src/activities/agent-turn/claim.ts +186 -43
- package/src/activities/agent-turn/claude-usage-observer.ts +221 -0
- package/src/activities/agent-turn/code-search.ts +275 -0
- package/src/activities/agent-turn/codex-capacity.ts +172 -8
- package/src/activities/agent-turn/codex-plan-entitlement.ts +254 -0
- package/src/activities/agent-turn/codex.ts +13 -5
- package/src/activities/agent-turn/compaction-prep.ts +170 -102
- package/src/activities/agent-turn/credential-leases.ts +122 -216
- package/src/activities/agent-turn/credential-restriction.ts +14 -0
- package/src/activities/agent-turn/errors.ts +647 -48
- package/src/activities/agent-turn/failure-settlement.ts +682 -130
- package/src/activities/agent-turn/final-reply.ts +46 -0
- package/src/activities/agent-turn/finalization-monitor.ts +120 -0
- package/src/activities/agent-turn/finalization.ts +146 -43
- package/src/activities/agent-turn/github-repository-bindings.ts +3 -1
- package/src/activities/agent-turn/governance-model.ts +69 -28
- package/src/activities/agent-turn/history-sink.ts +16 -2
- package/src/activities/agent-turn/history.ts +38 -11
- package/src/activities/agent-turn/input-wait-reply.ts +103 -0
- package/src/activities/agent-turn/knowledge-source-tools.ts +2 -0
- package/src/activities/agent-turn/media-artifacts.ts +2 -1
- package/src/activities/agent-turn/model-call-admission.ts +94 -0
- package/src/activities/agent-turn/model-usage.ts +64 -3
- package/src/activities/agent-turn/model-wire-dispatch.ts +27 -0
- package/src/activities/agent-turn/optional-repositories.ts +224 -0
- package/src/activities/agent-turn/provider-dispatch-barrier.ts +22 -0
- package/src/activities/agent-turn/quiescence.ts +10 -7
- package/src/activities/agent-turn/recovery-warning.ts +16 -0
- package/src/activities/agent-turn/refresh-credentials.ts +44 -0
- package/src/activities/agent-turn/run-credentials.ts +116 -55
- package/src/activities/agent-turn/run.ts +320 -93
- package/src/activities/agent-turn/sandbox-establish.ts +84 -8
- package/src/activities/agent-turn/sandbox-provision.ts +3 -1
- package/src/activities/agent-turn/sandbox-route.ts +4 -21
- package/src/activities/agent-turn/sandbox-runtime.ts +343 -18
- package/src/activities/agent-turn/session-title.ts +81 -5
- package/src/activities/agent-turn/skill-checkout.ts +111 -24
- package/src/activities/agent-turn/skill-install.ts +1 -1
- package/src/activities/agent-turn/skill-read.ts +374 -28
- 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 +10 -1
- package/src/activities/agent-turn/skill-selection.ts +4 -0
- package/src/activities/agent-turn/skill-tools.ts +112 -18
- package/src/activities/agent-turn/skill-transfer.ts +68 -23
- package/src/activities/agent-turn/stream-attempt.ts +313 -77
- package/src/activities/agent-turn/subscription-lease.ts +115 -0
- package/src/activities/agent-turn/tool-environment.ts +316 -167
- package/src/activities/agent-turn/tool-policy.ts +80 -1
- package/src/activities/agent-turn/turn-context.ts +35 -0
- package/src/activities/agent-turn/turn-sandbox-access.ts +7 -1
- package/src/activities/agent-turn/xai-capacity.ts +94 -51
- package/src/activities/automations.ts +76 -4
- package/src/activities/browser-deadline-checkpoint.ts +279 -0
- package/src/activities/capabilities.ts +108 -11
- package/src/activities/codemode-dispatcher.ts +36 -8
- package/src/activities/codex-capacity.ts +60 -25
- package/src/activities/codex-rotation.ts +39 -17
- package/src/activities/context-compaction.ts +75 -18
- package/src/activities/gmail-files.ts +88 -0
- package/src/activities/goals.ts +116 -121
- package/src/activities/google-drive-publication.ts +1 -3
- package/src/activities/image-generation-operation.ts +1 -1
- package/src/activities/knowledge-indexing.ts +269 -15
- package/src/activities/knowledge-source-sync.ts +10 -0
- package/src/activities/mcp-account-routes.ts +17 -0
- package/src/activities/mcp-credentials.ts +83 -194
- package/src/activities/mcp-operation-authority.ts +16 -41
- package/src/activities/model-call-fact-reconciler.ts +112 -0
- package/src/activities/open-suffix-resume.ts +15 -2
- package/src/activities/parent-wake.ts +46 -21
- package/src/activities/retained-screenshots.ts +82 -11
- package/src/activities/rig-verification.ts +38 -37
- package/src/activities/run-credential-renewal.ts +13 -1
- package/src/activities/run-credentials.ts +59 -17
- package/src/activities/run-input.ts +25 -16
- package/src/activities/sandbox-images.ts +0 -1
- package/src/activities/sandbox-lease.ts +798 -82
- package/src/activities/scheduled-tasks.ts +550 -178
- package/src/activities/session-state.ts +245 -6
- package/src/activities/site-auth-maintenance.ts +3 -0
- package/src/activities/types.ts +72 -5
- package/src/activities/workflow-wake.ts +2 -17
- package/src/activities/workspace-capture.ts +13 -2
- package/src/activities/workspace-credential-provider.ts +283 -0
- package/src/activities-control.ts +4 -0
- package/src/activities-turn.ts +5 -0
- package/src/activities.ts +13 -0
- package/src/activity-services.ts +16 -4
- package/src/browser-deadline-checkpoint-contract.ts +5 -0
- package/src/editable-artifact-hint-broker.ts +3 -0
- package/src/editable-artifact-materializer-service.ts +7 -3
- package/src/editable-artifact-outbox-service.ts +4 -4
- package/src/github-rest-mcp.ts +1 -2
- package/src/host-export-pump.ts +26 -4
- package/src/host-export.ts +3 -0
- package/src/http.ts +4 -2
- package/src/index.ts +99 -37
- package/src/observability-metrics.ts +510 -5
- package/src/personal-github-git-credentials.ts +2 -1
- package/src/pr-review-credentials.ts +1 -3
- package/src/retained-process-diagnostics.ts +50 -0
- package/src/retained-process-retry.ts +36 -0
- package/src/sandbox-resume.ts +388 -101
- package/src/sandbox-routing.ts +150 -25
- package/src/sandbox-snapshot-diagnostics.ts +112 -0
- package/src/session-event-fanout.ts +23 -0
- package/src/warm-capture-holder.ts +15 -0
- package/src/worker-service-lifecycle.ts +53 -0
- package/src/workflows/activities.ts +16 -1
- package/src/workflows/browser-deadline-checkpoint.ts +50 -0
- package/src/workflows/file-upload-reaper.ts +2 -0
- package/src/workflows/scheduled-tasks.ts +26 -13
- package/src/workflows/session.ts +242 -22
- package/src/workflows.ts +4 -0
- 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/dist/chunk-RAMNYSAW.js.map +0 -1
- package/src/activities/agent-turn/knowledge-sources.ts +0 -86
- package/src/activities/packs.ts +0 -166
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import type { AttemptToolDefinition } from "@opengeni/codemode";
|
|
2
|
+
import { usableJevApiKey, type Settings } from "@opengeni/config";
|
|
3
|
+
import {
|
|
4
|
+
CODE_SEARCH_TOOL_DESCRIPTION,
|
|
5
|
+
CODE_SEARCH_TOOL_NAME,
|
|
6
|
+
CodeSearchArgumentError,
|
|
7
|
+
CodeSearchRipgrepMissingError,
|
|
8
|
+
CodeSearchWorkspaceError,
|
|
9
|
+
JevCircuitBreaker,
|
|
10
|
+
JevClient,
|
|
11
|
+
JevRequestError,
|
|
12
|
+
JevUnavailableError,
|
|
13
|
+
codeSearchInputSchema,
|
|
14
|
+
parseCodeSearchArguments,
|
|
15
|
+
renderCodeSearchError,
|
|
16
|
+
runCodeSearch,
|
|
17
|
+
type CodeSearchWorkspace,
|
|
18
|
+
type JevCircuitLease,
|
|
19
|
+
} from "@opengeni/jev";
|
|
20
|
+
import type { Observability } from "@opengeni/observability";
|
|
21
|
+
import {
|
|
22
|
+
ChannelANotFoundError,
|
|
23
|
+
ChannelAUnavailableError,
|
|
24
|
+
ChannelAUnsupportedError,
|
|
25
|
+
ChannelAValidationError,
|
|
26
|
+
isWindowsConnectedMachinePath,
|
|
27
|
+
type SandboxChannelAService,
|
|
28
|
+
} from "@opengeni/runtime/sandbox";
|
|
29
|
+
import { recordCodeSearchCall, type CodeSearchCallOutcome } from "../../observability-metrics";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* One breaker per worker process. Repeated Jev outages (including an exhausted
|
|
33
|
+
* account) make `code_search` calls on this worker fail at once for a cooldown
|
|
34
|
+
* instead of each running a full retry cycle. The breaker never changes which
|
|
35
|
+
* tools a turn is offered: the tool list and instructions are the start of the
|
|
36
|
+
* model's cached prompt, and sessions move between workers whose breakers
|
|
37
|
+
* disagree.
|
|
38
|
+
*/
|
|
39
|
+
export const codeSearchCircuitBreaker = new JevCircuitBreaker();
|
|
40
|
+
|
|
41
|
+
/** Ripgrep output kept on the box per call before framing. */
|
|
42
|
+
const CODE_SEARCH_RIPGREP_MAX_BYTES = 32 * 1024 * 1024;
|
|
43
|
+
|
|
44
|
+
type CodeSearchChannel = Pick<
|
|
45
|
+
SandboxChannelAService,
|
|
46
|
+
"codeSearchRipgrep" | "codeSearchPathKinds" | "fsRead"
|
|
47
|
+
>;
|
|
48
|
+
|
|
49
|
+
/** Adapt the turn's sandbox or Connected Machine to the code search engine. */
|
|
50
|
+
export function codeSearchWorkspaceFromChannel(channel: CodeSearchChannel): CodeSearchWorkspace {
|
|
51
|
+
return {
|
|
52
|
+
ripgrep: async (args, options) => {
|
|
53
|
+
options.signal?.throwIfAborted();
|
|
54
|
+
const outcome = await channel.codeSearchRipgrep(args, {
|
|
55
|
+
timeoutMs: options.timeoutMs,
|
|
56
|
+
maxBytes: CODE_SEARCH_RIPGREP_MAX_BYTES,
|
|
57
|
+
});
|
|
58
|
+
if (!outcome.available) throw new CodeSearchRipgrepMissingError();
|
|
59
|
+
return {
|
|
60
|
+
stdout: outcome.stdout,
|
|
61
|
+
exitCode: outcome.exitCode,
|
|
62
|
+
truncated: outcome.truncated,
|
|
63
|
+
timedOut: outcome.timedOut,
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
readText: async (path, options) => {
|
|
67
|
+
options.signal?.throwIfAborted();
|
|
68
|
+
try {
|
|
69
|
+
const read = await channel.fsRead({ path, encoding: "utf8", maxBytes: options.maxBytes });
|
|
70
|
+
return { text: read.content, truncated: read.truncated, binary: read.isBinary };
|
|
71
|
+
} catch (error) {
|
|
72
|
+
if (error instanceof ChannelANotFoundError) return null;
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
pathKinds: async (paths, options) => {
|
|
77
|
+
options.signal?.throwIfAborted();
|
|
78
|
+
return await channel.codeSearchPathKinds(paths);
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Jev work done by one completed `code_search` call, for per-workspace usage records. */
|
|
84
|
+
export type CodeSearchUsage = {
|
|
85
|
+
operationId: string;
|
|
86
|
+
jevRequests: number;
|
|
87
|
+
jevInputTokens: number;
|
|
88
|
+
jevCostUsd: number;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
function textResult(text: string, isError: boolean) {
|
|
92
|
+
return { isError, content: [{ type: "text" as const, text }] };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* The model-facing `code_search` tool. Jev scores candidates inside the worker;
|
|
97
|
+
* the sandbox only runs read-only ripgrep and file reads, so the Jev key never
|
|
98
|
+
* leaves this process. A Jev failure is reported to the model instead of
|
|
99
|
+
* degrading to keyword-only ranking, which lowered answer quality in testing.
|
|
100
|
+
*/
|
|
101
|
+
export function createCodeSearchAttemptToolDefinition(input: {
|
|
102
|
+
settings: Pick<Settings, "jevApiKey" | "jevBaseUrl" | "jevModel" | "jevRequestTimeoutMs">;
|
|
103
|
+
apiKey: string;
|
|
104
|
+
workspace: () => Promise<CodeSearchWorkspace>;
|
|
105
|
+
observability: Observability;
|
|
106
|
+
/** Records Jev usage against the workspace. Failures are logged, never surfaced. */
|
|
107
|
+
recordUsage?: (usage: CodeSearchUsage) => Promise<void>;
|
|
108
|
+
breaker?: JevCircuitBreaker;
|
|
109
|
+
fetch?: typeof fetch;
|
|
110
|
+
}): AttemptToolDefinition {
|
|
111
|
+
const breaker = input.breaker ?? codeSearchCircuitBreaker;
|
|
112
|
+
const jev = new JevClient({
|
|
113
|
+
apiKey: input.apiKey,
|
|
114
|
+
baseUrl: input.settings.jevBaseUrl,
|
|
115
|
+
model: input.settings.jevModel,
|
|
116
|
+
timeoutMs: input.settings.jevRequestTimeoutMs,
|
|
117
|
+
...(input.fetch ? { fetch: input.fetch } : {}),
|
|
118
|
+
});
|
|
119
|
+
return {
|
|
120
|
+
identity: { serverId: "opengeni", toolName: CODE_SEARCH_TOOL_NAME },
|
|
121
|
+
modelName: CODE_SEARCH_TOOL_NAME,
|
|
122
|
+
codemodePath: ["opengeni", CODE_SEARCH_TOOL_NAME],
|
|
123
|
+
title: "Search code",
|
|
124
|
+
description: CODE_SEARCH_TOOL_DESCRIPTION,
|
|
125
|
+
inputSchema: codeSearchInputSchema,
|
|
126
|
+
annotations: {
|
|
127
|
+
title: "Search code",
|
|
128
|
+
readOnlyHint: true,
|
|
129
|
+
destructiveHint: false,
|
|
130
|
+
idempotentHint: true,
|
|
131
|
+
openWorldHint: false,
|
|
132
|
+
},
|
|
133
|
+
source: "opengeni",
|
|
134
|
+
approval: "none",
|
|
135
|
+
execute: async (args, context) => {
|
|
136
|
+
const startedAt = performance.now();
|
|
137
|
+
let outcome: CodeSearchCallOutcome = "failed";
|
|
138
|
+
let jevRequests = 0;
|
|
139
|
+
let jevCostUsd = 0;
|
|
140
|
+
// The breaker lease (the single trial while half-open) is settled exactly
|
|
141
|
+
// once: by what Jev did, or released when Jev was never judged. Settling
|
|
142
|
+
// takes it, so the finally block releases only a lease still held.
|
|
143
|
+
let lease: JevCircuitLease | null = null;
|
|
144
|
+
try {
|
|
145
|
+
const request = parseCodeSearchArguments(args);
|
|
146
|
+
lease = breaker.tryAcquire(Date.now());
|
|
147
|
+
if (!lease) {
|
|
148
|
+
outcome = "breaker_open";
|
|
149
|
+
return textResult(
|
|
150
|
+
renderCodeSearchError(
|
|
151
|
+
new JevUnavailableError("Jev is not responding; calls are paused for a few minutes"),
|
|
152
|
+
),
|
|
153
|
+
true,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
const workspace = await input.workspace();
|
|
157
|
+
const result = await runCodeSearch({
|
|
158
|
+
...request,
|
|
159
|
+
workspace,
|
|
160
|
+
jev,
|
|
161
|
+
...(context.signal ? { signal: context.signal } : {}),
|
|
162
|
+
});
|
|
163
|
+
const held = lease;
|
|
164
|
+
lease = null;
|
|
165
|
+
// A pack whose final status check hit an outage still counts against Jev.
|
|
166
|
+
if (result.statusCheckError instanceof JevUnavailableError) {
|
|
167
|
+
breaker.recordFailure(result.statusCheckError, Date.now(), held);
|
|
168
|
+
} else if (result.stats.jev.requests > 0) {
|
|
169
|
+
breaker.recordSuccess(held);
|
|
170
|
+
} else {
|
|
171
|
+
breaker.release(held);
|
|
172
|
+
}
|
|
173
|
+
outcome = "completed";
|
|
174
|
+
jevRequests = result.stats.jev.requests;
|
|
175
|
+
jevCostUsd = result.stats.jev.costUsd;
|
|
176
|
+
if (input.recordUsage && jevRequests > 0) {
|
|
177
|
+
await input
|
|
178
|
+
.recordUsage({
|
|
179
|
+
operationId: context.operationId,
|
|
180
|
+
jevRequests,
|
|
181
|
+
jevInputTokens: result.stats.jev.inputTokens,
|
|
182
|
+
jevCostUsd,
|
|
183
|
+
})
|
|
184
|
+
.catch((error: unknown) => {
|
|
185
|
+
input.observability.warn("code_search usage record failed", {
|
|
186
|
+
error: error instanceof Error ? error.message : String(error),
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
return textResult(result.text, false);
|
|
191
|
+
} catch (error) {
|
|
192
|
+
if (context.signal?.aborted) {
|
|
193
|
+
outcome = "cancelled";
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
196
|
+
if (error instanceof CodeSearchArgumentError) {
|
|
197
|
+
outcome = "invalid_arguments";
|
|
198
|
+
return textResult(error.message, true);
|
|
199
|
+
}
|
|
200
|
+
if (error instanceof JevUnavailableError) {
|
|
201
|
+
const held = lease;
|
|
202
|
+
lease = null;
|
|
203
|
+
breaker.recordFailure(error, Date.now(), held);
|
|
204
|
+
outcome = "jev_unavailable";
|
|
205
|
+
return textResult(renderCodeSearchError(error), true);
|
|
206
|
+
}
|
|
207
|
+
if (error instanceof JevRequestError) {
|
|
208
|
+
outcome = "jev_rejected";
|
|
209
|
+
return textResult(renderCodeSearchError(error), true);
|
|
210
|
+
}
|
|
211
|
+
if (error instanceof CodeSearchWorkspaceError) {
|
|
212
|
+
outcome = "workspace_unavailable";
|
|
213
|
+
return textResult(renderCodeSearchError(error), true);
|
|
214
|
+
}
|
|
215
|
+
if (
|
|
216
|
+
error instanceof ChannelAUnavailableError ||
|
|
217
|
+
error instanceof ChannelAUnsupportedError ||
|
|
218
|
+
error instanceof ChannelAValidationError
|
|
219
|
+
) {
|
|
220
|
+
outcome = "workspace_unavailable";
|
|
221
|
+
return textResult(
|
|
222
|
+
renderCodeSearchError(new CodeSearchWorkspaceError(error.message)),
|
|
223
|
+
true,
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
throw error;
|
|
227
|
+
} finally {
|
|
228
|
+
if (lease) breaker.release(lease);
|
|
229
|
+
recordCodeSearchCall(input.observability, {
|
|
230
|
+
outcome,
|
|
231
|
+
durationSeconds: (performance.now() - startedAt) / 1_000,
|
|
232
|
+
jevRequests,
|
|
233
|
+
jevCostUsd,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* The `code_search` definition for one turn, or none. It is offered only when
|
|
242
|
+
* the session's frozen decision and the deployment enable it, a usable Jev key
|
|
243
|
+
* exists, and the turn has compute that can run its POSIX shell commands (not
|
|
244
|
+
* a Windows Connected Machine). Every input is durable, so the tool list stays
|
|
245
|
+
* the same from turn to turn and on every worker. Transient Jev health never
|
|
246
|
+
* hides the tool; the breaker only refuses calls.
|
|
247
|
+
*/
|
|
248
|
+
export function codeSearchToolDefinitions(input: {
|
|
249
|
+
enabled: boolean;
|
|
250
|
+
settings: Pick<Settings, "jevApiKey" | "jevBaseUrl" | "jevModel" | "jevRequestTimeoutMs">;
|
|
251
|
+
backend: Settings["sandboxBackend"];
|
|
252
|
+
/** The turn's Connected Machine workspace root, when a machine is primary. */
|
|
253
|
+
machineWorkspaceRoot?: string | null;
|
|
254
|
+
observability: Observability;
|
|
255
|
+
workspace: () => Promise<CodeSearchWorkspace>;
|
|
256
|
+
recordUsage?: (usage: CodeSearchUsage) => Promise<void>;
|
|
257
|
+
breaker?: JevCircuitBreaker;
|
|
258
|
+
}): AttemptToolDefinition[] {
|
|
259
|
+
const apiKey = usableJevApiKey(input.settings);
|
|
260
|
+
const breaker = input.breaker ?? codeSearchCircuitBreaker;
|
|
261
|
+
if (!input.enabled || !apiKey || input.backend === "none") return [];
|
|
262
|
+
if (input.machineWorkspaceRoot && isWindowsConnectedMachinePath(input.machineWorkspaceRoot)) {
|
|
263
|
+
return [];
|
|
264
|
+
}
|
|
265
|
+
return [
|
|
266
|
+
createCodeSearchAttemptToolDefinition({
|
|
267
|
+
settings: input.settings,
|
|
268
|
+
apiKey,
|
|
269
|
+
workspace: input.workspace,
|
|
270
|
+
observability: input.observability,
|
|
271
|
+
...(input.recordUsage ? { recordUsage: input.recordUsage } : {}),
|
|
272
|
+
breaker,
|
|
273
|
+
}),
|
|
274
|
+
];
|
|
275
|
+
}
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
connectionModelAllowed,
|
|
3
3
|
getSessionGoal,
|
|
4
4
|
acquireCodexCredentialLease,
|
|
5
|
+
recheckCodexCredentialPlan,
|
|
5
6
|
CodexCredentialLeaseAttemptFencedError,
|
|
6
7
|
CodexCredentialFailoverExhaustedError,
|
|
7
8
|
CODEX_CREDENTIAL_LEASE_TTL_MS,
|
|
@@ -14,7 +15,7 @@ import {
|
|
|
14
15
|
type CodexCredentialLeaseSelectionContext,
|
|
15
16
|
} from "@opengeni/db";
|
|
16
17
|
import { type Settings } from "@opengeni/config";
|
|
17
|
-
import { CodexReloginRequired } from "@opengeni/codex";
|
|
18
|
+
import { CodexReloginRequired, codexPlanKey } from "@opengeni/codex";
|
|
18
19
|
import { publishDurableSessionEvents } from "@opengeni/events";
|
|
19
20
|
import {
|
|
20
21
|
authoritativeCodexCapacityResetAt,
|
|
@@ -43,8 +44,15 @@ import { recordTurnStartupPhase } from "../../observability-metrics";
|
|
|
43
44
|
import { createTurnCredentialLeases } from "./credential-leases";
|
|
44
45
|
import { deliverFailedChildTurnToParent } from "../parent-wake";
|
|
45
46
|
import { randomUUID } from "node:crypto";
|
|
47
|
+
import { createLogThrottle, type LogThrottle } from "@opengeni/observability";
|
|
46
48
|
|
|
47
49
|
import { refreshCappedCodexUsageRows } from "./codex";
|
|
50
|
+
import {
|
|
51
|
+
CodexPlanEntitlementError,
|
|
52
|
+
codexAccountDisplayLabel,
|
|
53
|
+
codexPlanEntitlementAdmissionBlock,
|
|
54
|
+
codexPlanEntitlementFailurePayload,
|
|
55
|
+
} from "./codex-plan-entitlement";
|
|
48
56
|
import { codexUsageLimitFailurePayload, CODEX_USAGE_LIMIT_MAX_RESUME_MS } from "./errors";
|
|
49
57
|
import type { ClaimTurnOk } from "./claim";
|
|
50
58
|
import type {
|
|
@@ -56,6 +64,39 @@ import type {
|
|
|
56
64
|
TurnControlState,
|
|
57
65
|
} from "./turn-context";
|
|
58
66
|
|
|
67
|
+
/** The eligible-pool gauge and `opengeni_codex_pool_low_total` stay per turn;
|
|
68
|
+
* the warning line is the first observation per workspace pool depth, then at
|
|
69
|
+
* most one per interval with the count it hid. The public log projection drops
|
|
70
|
+
* the identifiers and counts, so the closed `reason` keeps the depth visible. */
|
|
71
|
+
export const CODEX_POOL_LOW_WARNING_INTERVAL_MS = 10 * 60_000;
|
|
72
|
+
const codexPoolLowWarningThrottle = createLogThrottle({
|
|
73
|
+
intervalMs: CODEX_POOL_LOW_WARNING_INTERVAL_MS,
|
|
74
|
+
maxKeys: 1_024,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
export function warnCodexPoolLow(
|
|
78
|
+
observability: Pick<ActivityServices["observability"], "warn">,
|
|
79
|
+
input: {
|
|
80
|
+
workspaceKey: string;
|
|
81
|
+
workspaceId: string;
|
|
82
|
+
eligibleCount: number;
|
|
83
|
+
connectedCount: number;
|
|
84
|
+
depth: "zero" | "one";
|
|
85
|
+
},
|
|
86
|
+
throttle: LogThrottle = codexPoolLowWarningThrottle,
|
|
87
|
+
): void {
|
|
88
|
+
const admission = throttle.admit(`${input.workspaceKey}:${input.depth}`);
|
|
89
|
+
if (!admission) return;
|
|
90
|
+
observability.warn("Codex eligible credential pool is low", {
|
|
91
|
+
workspaceId: input.workspaceId,
|
|
92
|
+
eligibleCount: input.eligibleCount,
|
|
93
|
+
connectedCount: input.connectedCount,
|
|
94
|
+
depth: input.depth,
|
|
95
|
+
reason: input.depth === "zero" ? "eligible_pool_zero" : "eligible_pool_one",
|
|
96
|
+
...(admission.suppressedCount > 0 ? { suppressedCount: admission.suppressedCount } : {}),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
59
100
|
export type CapacityPhaseDeps = {
|
|
60
101
|
input: RunAgentTurnInput;
|
|
61
102
|
settings: Settings;
|
|
@@ -142,7 +183,13 @@ export async function selectCodexTurnCapacity(
|
|
|
142
183
|
)
|
|
143
184
|
throw new Error("This model is disabled for the pinned Codex subscription");
|
|
144
185
|
return selectCodexCredentialLeaseForTurn({
|
|
145
|
-
|
|
186
|
+
// The accepted product model also scopes proven plan entitlement:
|
|
187
|
+
// an account whose current plan excludes it is not a candidate.
|
|
188
|
+
context: {
|
|
189
|
+
...context,
|
|
190
|
+
accounts: allowed,
|
|
191
|
+
modelId: deps.turnExecutionPolicy.productModelId,
|
|
192
|
+
},
|
|
146
193
|
sessionId: input.sessionId,
|
|
147
194
|
sessionPinnedCredentialId: lockedSessionCodexState.pinnedCredentialId,
|
|
148
195
|
sessionPinSource: lockedSessionCodexState.pinSource,
|
|
@@ -173,10 +220,17 @@ export async function selectCodexTurnCapacity(
|
|
|
173
220
|
);
|
|
174
221
|
if (leased.decision.kind === "allCapped") {
|
|
175
222
|
// Bounded self-heal of stale usage cache, then ONE new atomic selection.
|
|
176
|
-
await refreshCappedCodexUsageRows(
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
223
|
+
await refreshCappedCodexUsageRows(
|
|
224
|
+
db,
|
|
225
|
+
settings,
|
|
226
|
+
input.workspaceId,
|
|
227
|
+
leased.accounts,
|
|
228
|
+
{
|
|
229
|
+
signalCodexCapacityWorkflow,
|
|
230
|
+
wakeSessionWorkflow,
|
|
231
|
+
},
|
|
232
|
+
turn.id,
|
|
233
|
+
);
|
|
180
234
|
leaseAcquisitionStartedAtMs = performance.now();
|
|
181
235
|
leased = await acquireCodexCredentialLease(
|
|
182
236
|
db,
|
|
@@ -197,6 +251,99 @@ export async function selectCodexTurnCapacity(
|
|
|
197
251
|
selectForTurn,
|
|
198
252
|
);
|
|
199
253
|
}
|
|
254
|
+
// No account can serve the model because the only candidates' CURRENT
|
|
255
|
+
// plans were proven not to include it. Re-read those plans once (an
|
|
256
|
+
// upgrade may not have been observed yet), then either select again or
|
|
257
|
+
// fail with typed copy instead of an indefinite capacity wait.
|
|
258
|
+
const productModelId = deps.turnExecutionPolicy.productModelId;
|
|
259
|
+
const planBlock = (current: typeof leased) =>
|
|
260
|
+
codexPlanEntitlementAdmissionBlock({
|
|
261
|
+
accounts: current.accounts,
|
|
262
|
+
modelId: productModelId,
|
|
263
|
+
credentialId: current.credentialId,
|
|
264
|
+
rotationEnabled: current.rotationEnabled,
|
|
265
|
+
activeCredentialId: current.activeCredentialId,
|
|
266
|
+
pinnedCredentialId: current.sessionCodexState.pinnedCredentialId,
|
|
267
|
+
pinSource: current.sessionCodexState.pinSource,
|
|
268
|
+
now: new Date(),
|
|
269
|
+
});
|
|
270
|
+
const blocked = planBlock(leased);
|
|
271
|
+
if (blocked) {
|
|
272
|
+
const rechecks = await Promise.all(
|
|
273
|
+
blocked.slice(0, 4).map((account) =>
|
|
274
|
+
recheckCodexCredentialPlan(db, settings, input.workspaceId, account.id, {
|
|
275
|
+
turnId,
|
|
276
|
+
purpose: "capacity_refresh",
|
|
277
|
+
}).catch(() => null),
|
|
278
|
+
),
|
|
279
|
+
);
|
|
280
|
+
const planMoved = rechecks.some(
|
|
281
|
+
(recheck) =>
|
|
282
|
+
recheck?.planType != null &&
|
|
283
|
+
codexPlanKey(recheck.planType) !== codexPlanKey(recheck.previousPlanType),
|
|
284
|
+
);
|
|
285
|
+
if (planMoved) {
|
|
286
|
+
leaseAcquisitionStartedAtMs = performance.now();
|
|
287
|
+
leased = await acquireCodexCredentialLease(
|
|
288
|
+
db,
|
|
289
|
+
{
|
|
290
|
+
accountId: input.accountId,
|
|
291
|
+
workspaceId: input.workspaceId,
|
|
292
|
+
sessionId: input.sessionId,
|
|
293
|
+
turnId,
|
|
294
|
+
attemptId: input.attemptId,
|
|
295
|
+
executionGeneration: attempt.executionGeneration,
|
|
296
|
+
workflowId: input.workflowId,
|
|
297
|
+
workflowRunId: input.workflowRunId,
|
|
298
|
+
dispatchId,
|
|
299
|
+
expectedRedispatches: attempt.redispatchesAtDispatch,
|
|
300
|
+
holderId,
|
|
301
|
+
advanceActivePointer: true,
|
|
302
|
+
},
|
|
303
|
+
selectForTurn,
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
const stillBlocked = planMoved ? planBlock(leased) : blocked;
|
|
307
|
+
if (stillBlocked) {
|
|
308
|
+
const account = stillBlocked[0]!;
|
|
309
|
+
// Name the plan only when this admission just observed it.
|
|
310
|
+
const observedPlan =
|
|
311
|
+
rechecks[blocked.findIndex((candidate) => candidate.id === account.id)]?.planType ??
|
|
312
|
+
null;
|
|
313
|
+
const payload = codexPlanEntitlementFailurePayload({
|
|
314
|
+
accountLabel: stillBlocked.length === 1 ? codexAccountDisplayLabel(account) : null,
|
|
315
|
+
planType: observedPlan,
|
|
316
|
+
planChanged: false,
|
|
317
|
+
modelId: productModelId,
|
|
318
|
+
});
|
|
319
|
+
if (turn.source === "compaction") {
|
|
320
|
+
if (
|
|
321
|
+
!(await eventing.settle!({
|
|
322
|
+
events: [
|
|
323
|
+
{
|
|
324
|
+
type: "turn.cancelled",
|
|
325
|
+
payload: {
|
|
326
|
+
maintenance: "context_compaction",
|
|
327
|
+
reason: payload.code,
|
|
328
|
+
requestPreserved: true,
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
{ type: "session.status.changed", payload: { status: "idle" } },
|
|
332
|
+
],
|
|
333
|
+
turnStatus: "cancelled",
|
|
334
|
+
sessionStatus: "idle",
|
|
335
|
+
activeTurnId: null,
|
|
336
|
+
}))
|
|
337
|
+
) {
|
|
338
|
+
return { exit: claimedResult({ status: "cancelled" }) };
|
|
339
|
+
}
|
|
340
|
+
control.turnMetricOutcome = "cancelled";
|
|
341
|
+
control.activityStatus = "idle";
|
|
342
|
+
return { exit: claimedResult({ status: "idle", deferredUntilWake: true }) };
|
|
343
|
+
}
|
|
344
|
+
throw new CodexPlanEntitlementError(payload);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
200
347
|
const lockedSessionCodexState = leased.sessionCodexState;
|
|
201
348
|
providerTurn.codexPolicySnapshot = leased.codexPolicySnapshot;
|
|
202
349
|
const sessionPin = lockedSessionCodexState.pinnedCredentialId;
|
|
@@ -276,6 +423,7 @@ export async function selectCodexTurnCapacity(
|
|
|
276
423
|
}
|
|
277
424
|
providerTurn.effectiveCodexCredentialId = leased.credentialId;
|
|
278
425
|
providerTurn.codexCredentialFailoverLimit = leased.failoverLimit;
|
|
426
|
+
providerTurn.codexProductModelId = deps.turnExecutionPolicy.productModelId;
|
|
279
427
|
leases.codex.generation = leased.generation;
|
|
280
428
|
leases.codex.confirmedUntilMs =
|
|
281
429
|
leased.leasedUntil && leaseAcquisitionStartedAtMs !== null
|
|
@@ -395,11 +543,12 @@ export async function selectCodexTurnCapacity(
|
|
|
395
543
|
help: "Alert signal emitted when the eligible Codex pool is zero or one.",
|
|
396
544
|
labels: { workspace_key: codexWorkspaceKey, depth: poolDepth },
|
|
397
545
|
});
|
|
398
|
-
observability
|
|
546
|
+
warnCodexPoolLow(observability, {
|
|
547
|
+
workspaceKey: codexWorkspaceKey,
|
|
399
548
|
workspaceId: input.workspaceId,
|
|
400
549
|
eligibleCount,
|
|
401
550
|
connectedCount: leased.accounts.length,
|
|
402
|
-
depth: poolDepth,
|
|
551
|
+
depth: poolDepth === "zero" ? "zero" : "one",
|
|
403
552
|
});
|
|
404
553
|
}
|
|
405
554
|
|
|
@@ -466,6 +615,11 @@ export async function selectCodexTurnCapacity(
|
|
|
466
615
|
},
|
|
467
616
|
},
|
|
468
617
|
);
|
|
618
|
+
if (evaluated.action === "stopped") {
|
|
619
|
+
control.turnMetricOutcome = "failed";
|
|
620
|
+
control.activityStatus = evaluated.sessionStatus === "queued" ? "idle" : "failed";
|
|
621
|
+
return { exit: claimedResult({ status: control.activityStatus }) };
|
|
622
|
+
}
|
|
469
623
|
if (evaluated.action === "resumed") {
|
|
470
624
|
control.turnMetricOutcome = "recovering";
|
|
471
625
|
control.activityStatus = "recovering";
|
|
@@ -583,6 +737,11 @@ export async function selectCodexTurnCapacity(
|
|
|
583
737
|
},
|
|
584
738
|
},
|
|
585
739
|
);
|
|
740
|
+
if (evaluated.action === "stopped") {
|
|
741
|
+
control.turnMetricOutcome = "failed";
|
|
742
|
+
control.activityStatus = evaluated.sessionStatus === "queued" ? "idle" : "failed";
|
|
743
|
+
return { exit: claimedResult({ status: control.activityStatus }) };
|
|
744
|
+
}
|
|
586
745
|
if (evaluated.action === "resumed") {
|
|
587
746
|
control.turnMetricOutcome = "recovering";
|
|
588
747
|
control.activityStatus = "recovering";
|
|
@@ -682,6 +841,11 @@ export async function selectCodexTurnCapacity(
|
|
|
682
841
|
failurePayload,
|
|
683
842
|
},
|
|
684
843
|
);
|
|
844
|
+
if (evaluated.action === "stopped") {
|
|
845
|
+
control.turnMetricOutcome = "failed";
|
|
846
|
+
control.activityStatus = evaluated.sessionStatus === "queued" ? "idle" : "failed";
|
|
847
|
+
return { exit: claimedResult({ status: control.activityStatus }) };
|
|
848
|
+
}
|
|
685
849
|
if (evaluated.action === "resumed") {
|
|
686
850
|
control.turnMetricOutcome = "recovering";
|
|
687
851
|
control.activityStatus = "recovering";
|