@opengeni/worker-bundle 0.28.3 → 0.28.4-canary.1
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-selection-diagnostics.d.ts +1 -0
- package/dist/activities/agent-turn/mcp-operation-read-tool.d.ts +16 -0
- package/dist/activities/codex-fleet-shadow.d.ts +1 -1
- package/dist/activities/mcp-operation-authority.d.ts +16 -0
- package/dist/activities/mcp-operation-observer.d.ts +14 -0
- package/dist/activities/mcp-operation-reader.d.ts +45 -0
- package/dist/activities/mcp-operation-store.d.ts +6 -0
- package/dist/activities/sandbox-lease.d.ts +12 -4
- package/dist/{activities-control-UXGPMDL3.js → activities-control-MKGEOBXT.js} +90 -71
- package/dist/activities-control-MKGEOBXT.js.map +1 -0
- package/dist/{activities-turn-NOYDE7PX.js → activities-turn-5MZZ6HQA.js} +15691 -815
- package/dist/activities-turn-5MZZ6HQA.js.map +1 -0
- package/dist/artifact-materializer-entry.js +2 -0
- package/dist/artifact-materializer-entry.js.map +1 -1
- package/dist/artifact-outbox-entry.js +2 -0
- package/dist/artifact-outbox-entry.js.map +1 -1
- package/dist/{chunk-BARTLVBK.js → chunk-A4XQWVZK.js} +25 -25
- package/dist/chunk-A4XQWVZK.js.map +1 -0
- package/dist/{chunk-NNXLW6V7.js → chunk-OA5CSY5N.js} +147 -112
- package/dist/chunk-OA5CSY5N.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/host-export.js +1 -0
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/observability-metrics.d.ts +12 -4
- package/dist/sandbox-archive-storage.d.ts +15 -3
- package/dist/sandbox-resume.d.ts +1 -1
- package/dist/sandbox-routing.d.ts +6 -1
- package/package.json +18 -17
- package/src/activities/agent-turn/claim.ts +3 -3
- package/src/activities/agent-turn/codex-capacity.ts +31 -53
- package/src/activities/agent-turn/codex-selection-diagnostics.ts +1 -0
- package/src/activities/agent-turn/finalization.ts +10 -5
- package/src/activities/agent-turn/mcp-operation-read-tool.ts +61 -0
- package/src/activities/agent-turn/sandbox-establish.ts +2 -0
- package/src/activities/agent-turn/skill-search.ts +2 -16
- package/src/activities/agent-turn/stream-attempt.ts +1 -1
- package/src/activities/agent-turn/tool-environment.ts +65 -0
- package/src/activities/codex-fleet-shadow.ts +3 -0
- package/src/activities/mcp-credentials.ts +94 -40
- package/src/activities/mcp-operation-authority.ts +76 -0
- package/src/activities/mcp-operation-observer.ts +86 -0
- package/src/activities/mcp-operation-reader.ts +108 -0
- package/src/activities/mcp-operation-store.ts +98 -0
- package/src/activities/sandbox-lease.ts +111 -73
- package/src/observability-metrics.ts +28 -24
- package/src/sandbox-archive-storage.ts +82 -18
- package/src/sandbox-resume.ts +79 -63
- package/src/sandbox-routing.ts +5 -1
- package/dist/activities-control-UXGPMDL3.js.map +0 -1
- package/dist/activities-turn-NOYDE7PX.js.map +0 -1
- package/dist/chunk-BARTLVBK.js.map +0 -1
- package/dist/chunk-NNXLW6V7.js.map +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ObjectStorage } from "@opengeni/storage";
|
|
2
|
+
import type { VerifiedHostWorkspaceArchive } from "@opengeni/runtime/sandbox";
|
|
2
3
|
import { type WorkspaceArchiveDescriptor, type WorkspaceArchiveObjectRef } from "@opengeni/contracts";
|
|
3
4
|
export declare class WorkspaceArchiveObjectStorageRequiredError extends Error {
|
|
4
5
|
readonly code: "workspace_archive_object_storage_required";
|
|
@@ -13,15 +14,26 @@ export declare function putTarWorkspaceArchiveObject(input: {
|
|
|
13
14
|
archive: {
|
|
14
15
|
bytes: Uint8Array;
|
|
15
16
|
descriptor: WorkspaceArchiveDescriptor;
|
|
16
|
-
};
|
|
17
|
+
} | VerifiedHostWorkspaceArchive;
|
|
17
18
|
}): Promise<WorkspaceArchiveObjectRef>;
|
|
19
|
+
/** The caller owns a fresh candidate returned by this module. A thrown or lost
|
|
20
|
+
* database response is UNKNOWN: it may have committed, so never delete on catch.
|
|
21
|
+
* Only the transaction's exact candidate disposition can authorize cleanup. */
|
|
22
|
+
export declare function persistWorkspaceArchiveCandidate<T extends {
|
|
23
|
+
wrote: boolean;
|
|
24
|
+
candidateDisposition?: "adopted" | "already_referenced" | "unused";
|
|
25
|
+
}>(input: {
|
|
26
|
+
objectStorage?: ObjectStorage | null;
|
|
27
|
+
ref?: WorkspaceArchiveObjectRef;
|
|
28
|
+
persist: () => Promise<T>;
|
|
29
|
+
}): Promise<T>;
|
|
18
30
|
export declare function putVersion1TarArchiveOrInline(input: {
|
|
19
31
|
backend: string;
|
|
20
32
|
objectStorage?: ObjectStorage | null | undefined;
|
|
21
33
|
accountId: string;
|
|
22
34
|
workspaceId: string;
|
|
23
35
|
sandboxGroupId: string;
|
|
24
|
-
archive: {
|
|
36
|
+
archive: VerifiedHostWorkspaceArchive | {
|
|
25
37
|
bytes: Uint8Array;
|
|
26
38
|
descriptor: WorkspaceArchiveDescriptor;
|
|
27
39
|
base64: string;
|
package/dist/sandbox-resume.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Settings } from "@opengeni/config";
|
|
2
2
|
import { readLease, reinstateTurnLeaseHolder, SandboxLeaseSupersededError, type Database, type LeaseHolderKind } from "@opengeni/db";
|
|
3
3
|
import { establishSandboxSessionFromEnvelope, type EstablishedSandboxSession, type RuntimeMetricsHooks } from "@opengeni/runtime";
|
|
4
|
-
import type
|
|
4
|
+
import { type ObjectStorage } from "@opengeni/storage";
|
|
5
5
|
import type { Observability } from "@opengeni/observability";
|
|
6
6
|
export type ResumeHolderKind = LeaseHolderKind;
|
|
7
7
|
/**
|
|
@@ -2,7 +2,7 @@ import { type Settings } from "@opengeni/config";
|
|
|
2
2
|
import { type Database, type EnrollmentRecord } from "@opengeni/db";
|
|
3
3
|
import type { OpStreamOutputFrame } from "@opengeni/runtime/sandbox";
|
|
4
4
|
import type { EventBus } from "@opengeni/events";
|
|
5
|
-
import { type EstablishedSandboxSession, type RoutingSandboxFirstOperationObserver, type RoutingSandboxOperationObserver, type SelfhostedOpObserver, type SelfhostedRelayConfig, type OpStreamJournal } from "@opengeni/runtime";
|
|
5
|
+
import { type EstablishedSandboxSession, type RoutingSandboxFirstOperationObserver, type RoutingSandboxOperationObserver, type SelfhostedOpObserver, type SelfhostedConnectionBinding, type SelfhostedRelayConfig, type OpStreamJournal } from "@opengeni/runtime";
|
|
6
6
|
export type RoutingWiringServices = {
|
|
7
7
|
db: Database;
|
|
8
8
|
settings: Settings;
|
|
@@ -179,6 +179,8 @@ export type SelfhostedTurnSessionArgs = {
|
|
|
179
179
|
operationResourcePolicy: EnrollmentRecord["operationPolicy"];
|
|
180
180
|
operationResourcePolicySupported: boolean;
|
|
181
181
|
operationCpuQuotaSupported: boolean;
|
|
182
|
+
/** Feature advertisement only; refreshed by exact live admission for each operation. */
|
|
183
|
+
transactionalFsWriteSupported?: boolean;
|
|
182
184
|
/** The active pointer's epoch — the control-op fence echoed to the agent. */
|
|
183
185
|
epoch: number;
|
|
184
186
|
/** The run's declared sandbox environment (the SAME object fed to buildAgent +
|
|
@@ -197,6 +199,9 @@ export type SelfhostedTurnSessionArgs = {
|
|
|
197
199
|
sessionId: string;
|
|
198
200
|
};
|
|
199
201
|
};
|
|
202
|
+
/** Project one enrollment read into one exact runtime route. Capability and
|
|
203
|
+
* process identity must be taken from the same snapshot. */
|
|
204
|
+
export declare function connectionBindingFor(services: RoutingWiringServices, enrollment: EnrollmentRecord | null): SelfhostedConnectionBinding | null;
|
|
200
205
|
export declare function establishSelfhostedTurnSession(services: RoutingWiringServices, args: SelfhostedTurnSessionArgs): Promise<EstablishedSandboxSession>;
|
|
201
206
|
/** Whether the routing proxy should wrap the turn box: the hot-swap feature is
|
|
202
207
|
* gated by the selfhosted flag (the active pointer + swap tools are only
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/worker-bundle",
|
|
3
|
-
"version": "0.28.
|
|
3
|
+
"version": "0.28.4-canary.1",
|
|
4
4
|
"description": "OpenGeni worker entry and reusable embedded lifecycle, shipped with a release-coherent pre-bundled Temporal workflow artifact.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -54,22 +54,23 @@
|
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@llamaindex/liteparse": "2.14.2",
|
|
57
|
-
"@opengeni/agent-proto": "^0.5.1",
|
|
58
|
-
"@opengeni/capabilities": "^0.3.3",
|
|
59
|
-
"@opengeni/codemode": "^0.5.
|
|
60
|
-
"@opengeni/codex": "^0.2.22",
|
|
61
|
-
"@opengeni/config": "^1.1.1",
|
|
62
|
-
"@opengeni/contracts": "^3.0.1",
|
|
63
|
-
"@opengeni/core": "^2.9.
|
|
64
|
-
"@opengeni/db": "^4.3.
|
|
65
|
-
"@opengeni/documents": "^0.8.
|
|
66
|
-
"@opengeni/events": "^0.4.
|
|
67
|
-
"@opengeni/github": "^0.7.
|
|
68
|
-
"@opengeni/network": "^0.3.1",
|
|
69
|
-
"@opengeni/observability": "^0.8.
|
|
70
|
-
"@opengeni/runtime": "^2.5.
|
|
71
|
-
"@opengeni/storage": "^0.2.
|
|
72
|
-
"@opengeni/
|
|
57
|
+
"@opengeni/agent-proto": "^0.5.1-canary.27",
|
|
58
|
+
"@opengeni/capabilities": "^0.3.3-canary.9",
|
|
59
|
+
"@opengeni/codemode": "^0.5.6-canary.1",
|
|
60
|
+
"@opengeni/codex": "^0.2.22-canary.9",
|
|
61
|
+
"@opengeni/config": "^1.1.2-canary.1",
|
|
62
|
+
"@opengeni/contracts": "^3.0.2-canary.1",
|
|
63
|
+
"@opengeni/core": "^2.9.4-canary.1",
|
|
64
|
+
"@opengeni/db": "^4.3.3-canary.1",
|
|
65
|
+
"@opengeni/documents": "^0.8.27-canary.1",
|
|
66
|
+
"@opengeni/events": "^0.4.25-canary.1",
|
|
67
|
+
"@opengeni/github": "^0.7.10-canary.1",
|
|
68
|
+
"@opengeni/network": "^0.3.1-canary.9",
|
|
69
|
+
"@opengeni/observability": "^0.8.26-canary.1",
|
|
70
|
+
"@opengeni/runtime": "^2.5.3-canary.1",
|
|
71
|
+
"@opengeni/storage": "^0.2.127-canary.1",
|
|
72
|
+
"@opengeni/tool-gateway": "^0.1.7-canary.1",
|
|
73
|
+
"@opengeni/xai-subscription": "^0.1.4-canary.9",
|
|
73
74
|
"@temporalio/activity": "^1.17.0",
|
|
74
75
|
"@temporalio/client": "^1.17.0",
|
|
75
76
|
"@temporalio/worker": "^1.17.0",
|
|
@@ -347,7 +347,7 @@ export async function claimTurnAttempt(deps: ClaimTurnDeps): Promise<ClaimTurnOu
|
|
|
347
347
|
});
|
|
348
348
|
return attached.action === "claimed" && attached.turn.id === turn.id;
|
|
349
349
|
};
|
|
350
|
-
turnLifecycleMetricsFor(observability).start(
|
|
350
|
+
turnLifecycleMetricsFor(observability).start({ attemptId: input.attemptId });
|
|
351
351
|
// §7.5 P3 — pass the accepted billing attribution (externally funded turns
|
|
352
352
|
// bypass OpenGeni credit/token gates)
|
|
353
353
|
// AND the optional host `entitlements` port (when bound, its admitRun replaces
|
|
@@ -451,7 +451,7 @@ export async function claimTurnAttempt(deps: ClaimTurnDeps): Promise<ClaimTurnOu
|
|
|
451
451
|
}
|
|
452
452
|
recordCanonicalStartupMilestones(appended.canonicalStartupMilestones);
|
|
453
453
|
if (inputs.length > 0) {
|
|
454
|
-
turnLifecycleMetricsFor(observability).progress(
|
|
454
|
+
turnLifecycleMetricsFor(observability).progress({ attemptId: input.attemptId });
|
|
455
455
|
}
|
|
456
456
|
activityContext?.heartbeat({
|
|
457
457
|
...heartbeatDetails,
|
|
@@ -515,7 +515,7 @@ export async function claimTurnAttempt(deps: ClaimTurnDeps): Promise<ClaimTurnOu
|
|
|
515
515
|
return false;
|
|
516
516
|
}
|
|
517
517
|
recordCanonicalStartupMilestones(result.canonicalStartupMilestones);
|
|
518
|
-
turnLifecycleMetricsFor(observability).progress(
|
|
518
|
+
turnLifecycleMetricsFor(observability).progress({ attemptId: input.attemptId });
|
|
519
519
|
await publishDurableSessionEvents(bus, input.workspaceId, input.sessionId, result.events);
|
|
520
520
|
if (inputSettlement.turnStatus === "requires_action") {
|
|
521
521
|
// The settlement transaction committed the parent's child_requires_action
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
CodexCredentialLeaseAttemptFencedError,
|
|
6
6
|
CodexCredentialFailoverExhaustedError,
|
|
7
7
|
CODEX_CREDENTIAL_LEASE_TTL_MS,
|
|
8
|
-
|
|
8
|
+
recordSessionCodexSelectionForTurnAttempt,
|
|
9
9
|
setSessionCodexPinInTransaction,
|
|
10
10
|
settleCodexCredentialFailover,
|
|
11
11
|
withSessionCodexCapacityMutation,
|
|
@@ -288,19 +288,40 @@ export async function selectCodexTurnCapacity(
|
|
|
288
288
|
leases.codex.confirmedUntilMs !== null;
|
|
289
289
|
if (leases.codex.held) leases.codex.startHeartbeat();
|
|
290
290
|
|
|
291
|
+
const eligibleCount = leased.accounts.filter((account) =>
|
|
292
|
+
isCodexCredentialEligible(account, new Date()),
|
|
293
|
+
).length;
|
|
294
|
+
const selectionReceipt = providerTurn.effectiveCodexCredentialId
|
|
295
|
+
? await recordSessionCodexSelectionForTurnAttempt(db, {
|
|
296
|
+
workspaceId: input.workspaceId,
|
|
297
|
+
sessionId: input.sessionId,
|
|
298
|
+
turnId,
|
|
299
|
+
attemptId: input.attemptId,
|
|
300
|
+
executionGeneration: attempt.executionGeneration,
|
|
301
|
+
credentialId: providerTurn.effectiveCodexCredentialId,
|
|
302
|
+
strategy: leased.rotationStrategy,
|
|
303
|
+
reusedLease: leased.reused,
|
|
304
|
+
pinnedCredentialId: sessionPin,
|
|
305
|
+
pinSource: sessionPinSource,
|
|
306
|
+
eligibleCount,
|
|
307
|
+
connectedCount: leased.accounts.length,
|
|
308
|
+
})
|
|
309
|
+
: null;
|
|
310
|
+
if (selectionReceipt)
|
|
311
|
+
await publishDurableSessionEvents(
|
|
312
|
+
bus,
|
|
313
|
+
input.workspaceId,
|
|
314
|
+
input.sessionId,
|
|
315
|
+
selectionReceipt.events,
|
|
316
|
+
);
|
|
317
|
+
const selectionDiagnostics = selectionReceipt?.diagnostics ?? null;
|
|
291
318
|
const actualOutcome = providerTurn.effectiveCodexCredentialId
|
|
292
319
|
? "selected"
|
|
293
320
|
: rotationDecision.kind === "allCapped" || rotationDecision.kind === "allocatorDisabled"
|
|
294
321
|
? "waiting"
|
|
295
322
|
: "none";
|
|
296
|
-
const actualReason =
|
|
297
|
-
?
|
|
298
|
-
? "lease_reused"
|
|
299
|
-
: sessionPin === providerTurn.effectiveCodexCredentialId
|
|
300
|
-
? "pin"
|
|
301
|
-
: rotationDecision.kind === "active" && rotationDecision.moved
|
|
302
|
-
? "rotation"
|
|
303
|
-
: "active"
|
|
323
|
+
const actualReason = selectionDiagnostics
|
|
324
|
+
? selectionDiagnostics.reason
|
|
304
325
|
: rotationDecision.kind === "allCapped"
|
|
305
326
|
? "all_capped"
|
|
306
327
|
: rotationDecision.kind === "allocatorDisabled"
|
|
@@ -362,9 +383,6 @@ export async function selectCodexTurnCapacity(
|
|
|
362
383
|
});
|
|
363
384
|
}
|
|
364
385
|
|
|
365
|
-
const eligibleCount = leased.accounts.filter((account) =>
|
|
366
|
-
isCodexCredentialEligible(account, new Date()),
|
|
367
|
-
).length;
|
|
368
386
|
const poolDepth = eligibleCount === 0 ? "zero" : eligibleCount === 1 ? "one" : "many";
|
|
369
387
|
observability.incrementCounter({
|
|
370
388
|
name: "opengeni_codex_pool_observations_total",
|
|
@@ -690,34 +708,7 @@ export async function selectCodexTurnCapacity(
|
|
|
690
708
|
return { exit: claimedResult({ status: "cancelled" }) };
|
|
691
709
|
}
|
|
692
710
|
if (providerTurn.effectiveCodexCredentialId) {
|
|
693
|
-
const
|
|
694
|
-
if (priorAccountId !== providerTurn.effectiveCodexCredentialId) {
|
|
695
|
-
await recordSessionActiveCodexCredential(
|
|
696
|
-
db,
|
|
697
|
-
input.workspaceId,
|
|
698
|
-
input.sessionId,
|
|
699
|
-
providerTurn.effectiveCodexCredentialId,
|
|
700
|
-
);
|
|
701
|
-
const rotated = rotationDecision.kind === "active" && rotationDecision.moved;
|
|
702
|
-
await eventing.publish([
|
|
703
|
-
{
|
|
704
|
-
type: "codex.account.switched",
|
|
705
|
-
payload: {
|
|
706
|
-
fromAccountId: priorAccountId,
|
|
707
|
-
toAccountId: providerTurn.effectiveCodexCredentialId,
|
|
708
|
-
reason: rotated ? "rotation" : "manual",
|
|
709
|
-
},
|
|
710
|
-
},
|
|
711
|
-
]);
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
const selectionReason = leased.reused
|
|
715
|
-
? "lease_reused"
|
|
716
|
-
: sessionPin === providerTurn.effectiveCodexCredentialId
|
|
717
|
-
? "pin"
|
|
718
|
-
: rotationDecision.kind === "active" && rotationDecision.moved
|
|
719
|
-
? "rotation"
|
|
720
|
-
: "active";
|
|
711
|
+
const selectionReason = selectionDiagnostics!.reason;
|
|
721
712
|
observability.incrementCounter({
|
|
722
713
|
name: "opengeni_codex_credential_selections_total",
|
|
723
714
|
help: "Codex credential selections by strategy and reason.",
|
|
@@ -727,19 +718,6 @@ export async function selectCodexTurnCapacity(
|
|
|
727
718
|
reason: selectionReason,
|
|
728
719
|
},
|
|
729
720
|
});
|
|
730
|
-
await eventing.publish([
|
|
731
|
-
{
|
|
732
|
-
type: "codex.credential.selected",
|
|
733
|
-
payload: {
|
|
734
|
-
credentialId: providerTurn.effectiveCodexCredentialId,
|
|
735
|
-
strategy: leased.rotationStrategy,
|
|
736
|
-
reason: selectionReason,
|
|
737
|
-
eligibleCount,
|
|
738
|
-
connectedCount: leased.accounts.length,
|
|
739
|
-
reused: leased.reused,
|
|
740
|
-
},
|
|
741
|
-
},
|
|
742
|
-
]);
|
|
743
721
|
}
|
|
744
722
|
} catch (error) {
|
|
745
723
|
credentialSelectionOutcome = "failed";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { codexSelectionDiagnostics } from "@opengeni/db";
|
|
@@ -745,12 +745,17 @@ export async function finalizeTurnAttempt(deps: TurnFinalizationDeps): Promise<v
|
|
|
745
745
|
status: finalizationError ? "cleanup_failed" : control.activityStatus,
|
|
746
746
|
durationSeconds,
|
|
747
747
|
});
|
|
748
|
-
if (attempt.turnId
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
748
|
+
if (attempt.turnId) {
|
|
749
|
+
// This registry describes physical activity ownership, not durable turn
|
|
750
|
+
// settlement. Always clear the exact attempt when its activity unwinds,
|
|
751
|
+
// even when failure settlement could not classify an outcome. Otherwise
|
|
752
|
+
// a recovered successor can make progress while the predecessor leaves a
|
|
753
|
+
// process-local false stuck-turn alert behind.
|
|
754
|
+
turnLifecycleMetricsFor(observability).finish({
|
|
755
|
+
attemptId: input.attemptId,
|
|
756
|
+
outcome: control.turnMetricOutcome,
|
|
752
757
|
durationSeconds,
|
|
753
|
-
);
|
|
758
|
+
});
|
|
754
759
|
}
|
|
755
760
|
activitySpan.end({
|
|
756
761
|
attributes: {
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { AttemptToolDefinition } from "@opengeni/codemode";
|
|
2
|
+
import { AttemptToolResult } from "@opengeni/contracts";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
const selectorSchema = z.union([
|
|
6
|
+
z.object({ operationId: z.string().uuid() }).strict(),
|
|
7
|
+
z.object({ sourceTurnId: z.string().uuid(), sourceCallId: z.string().min(1) }).strict(),
|
|
8
|
+
]);
|
|
9
|
+
|
|
10
|
+
export type McpOperationReadSelector = z.infer<typeof selectorSchema>;
|
|
11
|
+
|
|
12
|
+
/** Attempt-local adapter. The supplied read closure owns canonical session
|
|
13
|
+
* access, current provider authority, immutable binding checks and settlement.
|
|
14
|
+
* No caller-controlled provider parameters cross this tool boundary. */
|
|
15
|
+
export function createOperationReadAttemptToolDefinition(input: {
|
|
16
|
+
read: (selector: McpOperationReadSelector) => Promise<Record<string, unknown>>;
|
|
17
|
+
}): AttemptToolDefinition {
|
|
18
|
+
return {
|
|
19
|
+
identity: { serverId: "opengeni", toolName: "operation_read" },
|
|
20
|
+
modelName: "operation_read",
|
|
21
|
+
codemodePath: ["opengeni", "operation_read"],
|
|
22
|
+
title: "Read an existing MCP operation outcome",
|
|
23
|
+
description:
|
|
24
|
+
"Read a retained MCP operation and, when supported and currently authorized, observe its provider receipt. " +
|
|
25
|
+
"Use the operationId from the original call, or its exact sourceTurnId and sourceCallId if that response was lost. " +
|
|
26
|
+
"This never repeats the original mutation. Unknown means the outcome is not proven, not that the operation failed. " +
|
|
27
|
+
"Provider support and renewed authorization may be required after reconnecting.",
|
|
28
|
+
inputSchema: {
|
|
29
|
+
type: "object",
|
|
30
|
+
properties: {
|
|
31
|
+
operationId: { type: "string", format: "uuid" },
|
|
32
|
+
sourceTurnId: { type: "string", format: "uuid" },
|
|
33
|
+
sourceCallId: { type: "string", minLength: 1 },
|
|
34
|
+
},
|
|
35
|
+
additionalProperties: false,
|
|
36
|
+
oneOf: [
|
|
37
|
+
{
|
|
38
|
+
required: ["operationId"],
|
|
39
|
+
not: { anyOf: [{ required: ["sourceTurnId"] }, { required: ["sourceCallId"] }] },
|
|
40
|
+
},
|
|
41
|
+
{ required: ["sourceTurnId", "sourceCallId"], not: { required: ["operationId"] } },
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
annotations: {
|
|
45
|
+
readOnlyHint: true,
|
|
46
|
+
destructiveHint: false,
|
|
47
|
+
idempotentHint: true,
|
|
48
|
+
openWorldHint: true,
|
|
49
|
+
},
|
|
50
|
+
source: "opengeni",
|
|
51
|
+
approval: "none",
|
|
52
|
+
execute: async (args) => {
|
|
53
|
+
const selector = selectorSchema.parse(args);
|
|
54
|
+
const receipt = await input.read(selector);
|
|
55
|
+
return AttemptToolResult.parse({
|
|
56
|
+
content: [{ type: "text", text: JSON.stringify(receipt) }],
|
|
57
|
+
structuredContent: receipt,
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
@@ -475,6 +475,8 @@ export async function establishTurnSandbox(deps: EstablishTurnSandboxDeps): Prom
|
|
|
475
475
|
liveMachineEnrollment?.agentCapabilities.operationResourcePolicy === true,
|
|
476
476
|
operationCpuQuotaSupported:
|
|
477
477
|
liveMachineEnrollment?.agentCapabilities.operationCpuQuota === true,
|
|
478
|
+
transactionalFsWriteSupported:
|
|
479
|
+
liveMachineEnrollment?.agentCapabilities.transactionalFsWrite === true,
|
|
478
480
|
...(activeSandboxRecord!.scope === "user" && fileAuthoritySubjectId
|
|
479
481
|
? {
|
|
480
482
|
personalMachineAttempt: {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { AttemptToolDefinition } from "@opengeni/codemode";
|
|
2
2
|
import { PublicSkillSearchError, type PublicSkillSearchClient } from "@opengeni/core";
|
|
3
|
-
import { listSkillLibraryEntries } from "@opengeni/runtime/skill-library";
|
|
4
3
|
|
|
5
4
|
export type WorkspaceSkillSearchEntry = Readonly<{
|
|
6
5
|
id: string;
|
|
@@ -23,7 +22,7 @@ export function createSkillSearchAttemptToolDefinition(input: {
|
|
|
23
22
|
codemodePath: ["opengeni", "skill_search"],
|
|
24
23
|
title: "Search Skills",
|
|
25
24
|
description:
|
|
26
|
-
"Find installed workspace Skills or available
|
|
25
|
+
"Find installed workspace Skills or available public Skills. Returns identifiers and install sources, not file contents. Search never installs or starts a sandbox. Scope defaults to all; use installed to avoid external search.",
|
|
27
26
|
inputSchema: {
|
|
28
27
|
type: "object",
|
|
29
28
|
properties: {
|
|
@@ -60,19 +59,6 @@ export function createSkillSearchAttemptToolDefinition(input: {
|
|
|
60
59
|
source: entry.source ?? "workspace",
|
|
61
60
|
installed: !entry.source || entry.source === "workspace",
|
|
62
61
|
}));
|
|
63
|
-
const libraryHits =
|
|
64
|
-
scope === "installed"
|
|
65
|
-
? []
|
|
66
|
-
: listSkillLibraryEntries()
|
|
67
|
-
.filter(matches)
|
|
68
|
-
.slice(0, limit)
|
|
69
|
-
.map((entry) => ({
|
|
70
|
-
id: `library:${entry.id}`,
|
|
71
|
-
name: entry.name,
|
|
72
|
-
description: entry.description,
|
|
73
|
-
source: "library" as const,
|
|
74
|
-
libraryId: entry.id,
|
|
75
|
-
}));
|
|
76
62
|
let publicResult: Awaited<ReturnType<PublicSkillSearchClient["search"]>> | null = null;
|
|
77
63
|
let publicError: {
|
|
78
64
|
source: "skills_sh";
|
|
@@ -93,7 +79,7 @@ export function createSkillSearchAttemptToolDefinition(input: {
|
|
|
93
79
|
}
|
|
94
80
|
const output = {
|
|
95
81
|
workspace: workspaceHits,
|
|
96
|
-
library:
|
|
82
|
+
library: [], // Retained response field for older clients.
|
|
97
83
|
public: publicResult?.items.map((entry) => ({ ...entry })) ?? [],
|
|
98
84
|
// A provider outage is not an empty search. Keep local results useful
|
|
99
85
|
// and expose partial failure explicitly, without leaking network details.
|
|
@@ -1062,7 +1062,7 @@ export async function runTurnStreamAttempt(
|
|
|
1062
1062
|
currentToolBatchCallIds = new Set<string>();
|
|
1063
1063
|
currentToolBatchCompletedCallIds = new Set<string>();
|
|
1064
1064
|
await historySink.reconcileConversationTruth();
|
|
1065
|
-
turnLifecycleMetricsFor(observability).progress(
|
|
1065
|
+
turnLifecycleMetricsFor(observability).progress({ attemptId: input.attemptId });
|
|
1066
1066
|
modelCheckpointMemoryCollector.schedule(observability);
|
|
1067
1067
|
try {
|
|
1068
1068
|
await ensureRunAllowed(
|
|
@@ -37,6 +37,11 @@ import {
|
|
|
37
37
|
resolveGoogleDrivePublicationTarget,
|
|
38
38
|
} from "../google-drive-publication";
|
|
39
39
|
import { connectionTokenResolverForTurn } from "../mcp-credentials";
|
|
40
|
+
import { createMcpOperationPersistence } from "@opengeni/db/mcp-operations";
|
|
41
|
+
import { createMcpOperationReadStore } from "../mcp-operation-store";
|
|
42
|
+
import { createMcpOperationObserverResolver } from "../mcp-operation-observer";
|
|
43
|
+
import { readMcpOperation } from "../mcp-operation-reader";
|
|
44
|
+
import { createOperationReadAttemptToolDefinition } from "./mcp-operation-read-tool";
|
|
40
45
|
import { buildGitHubRestMcpForTurn } from "../../github-rest-mcp";
|
|
41
46
|
import { materializeConnectorAttachmentsInChannel } from "../connector-attachments";
|
|
42
47
|
import { allowedFirstPartyMcpToolsForSession, type Settings } from "@opengeni/config";
|
|
@@ -668,7 +673,66 @@ export async function prepareTurnToolRuntime(deps: PrepareTurnToolRuntimeDeps) {
|
|
|
668
673
|
});
|
|
669
674
|
},
|
|
670
675
|
});
|
|
676
|
+
const operationRecoveryEnabled = githubRestMcp.settings.mcpServers.some(
|
|
677
|
+
(server) =>
|
|
678
|
+
server.operationRecovery &&
|
|
679
|
+
Object.keys(server.operationRecovery).length > 0 &&
|
|
680
|
+
githubRestMcp.tools.some((tool) => tool.kind === "mcp" && tool.id === server.id),
|
|
681
|
+
);
|
|
682
|
+
const operationAttempt = {
|
|
683
|
+
accountId: input.accountId,
|
|
684
|
+
workspaceId: input.workspaceId,
|
|
685
|
+
sessionId: input.sessionId,
|
|
686
|
+
turnId: turn.id,
|
|
687
|
+
attemptId: input.attemptId,
|
|
688
|
+
executionGeneration: attempt.executionGeneration,
|
|
689
|
+
};
|
|
690
|
+
const operationPersistence = operationRecoveryEnabled
|
|
691
|
+
? createMcpOperationPersistence(db, operationAttempt)
|
|
692
|
+
: undefined;
|
|
693
|
+
const operationReadStore = operationRecoveryEnabled
|
|
694
|
+
? createMcpOperationReadStore(db, operationAttempt)
|
|
695
|
+
: undefined;
|
|
696
|
+
const operationObserver = createMcpOperationObserverResolver({
|
|
697
|
+
settings: githubRestMcp.settings,
|
|
698
|
+
workspaceId: input.workspaceId,
|
|
699
|
+
...(credentialSubjectId ? { credentialSubjectId } : {}),
|
|
700
|
+
resolveCredential,
|
|
701
|
+
assertAttempt: async () => {
|
|
702
|
+
throwIfWorkerShuttingDown();
|
|
703
|
+
throwIfTurnCancelled();
|
|
704
|
+
const current = await getSessionTurnForAttempt(
|
|
705
|
+
db,
|
|
706
|
+
input.workspaceId,
|
|
707
|
+
input.sessionId,
|
|
708
|
+
input.attemptId,
|
|
709
|
+
);
|
|
710
|
+
if (
|
|
711
|
+
!current ||
|
|
712
|
+
current.id !== turn.id ||
|
|
713
|
+
current.executionGeneration !== attempt.executionGeneration
|
|
714
|
+
) {
|
|
715
|
+
throw new Error("MCP operation reader no longer owns the executing attempt");
|
|
716
|
+
}
|
|
717
|
+
},
|
|
718
|
+
getEnvironment: async () => {
|
|
719
|
+
const prepared = eventing.preparedTools;
|
|
720
|
+
if (!prepared) return null;
|
|
721
|
+
return ((await prepared.ready) ?? prepared).attemptToolEnvironment;
|
|
722
|
+
},
|
|
723
|
+
});
|
|
671
724
|
const attemptToolDefinitions = [
|
|
725
|
+
...(operationReadStore
|
|
726
|
+
? [
|
|
727
|
+
createOperationReadAttemptToolDefinition({
|
|
728
|
+
read: async (selector) =>
|
|
729
|
+
await readMcpOperation(selector, {
|
|
730
|
+
...operationReadStore,
|
|
731
|
+
resolveObserver: operationObserver,
|
|
732
|
+
}),
|
|
733
|
+
}),
|
|
734
|
+
]
|
|
735
|
+
: []),
|
|
672
736
|
...skillTools,
|
|
673
737
|
createListModelsAttemptToolDefinition({
|
|
674
738
|
currentModelId: turnExecutionPolicy.productModelId,
|
|
@@ -876,6 +940,7 @@ export async function prepareTurnToolRuntime(deps: PrepareTurnToolRuntimeDeps) {
|
|
|
876
940
|
...(credentialSubjectId ? { credentialSubjectId } : {}),
|
|
877
941
|
...(codexAppsAuth ? { codexAppsAuth } : {}),
|
|
878
942
|
resolveCredential,
|
|
943
|
+
...(operationPersistence ? { mcpOperationPersistence: operationPersistence } : {}),
|
|
879
944
|
...(linkedAuthority
|
|
880
945
|
? {
|
|
881
946
|
authorizeAttemptExecution: async () => {
|