@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
|
@@ -21,9 +21,9 @@ import {
|
|
|
21
21
|
type OpenGeniRuntime,
|
|
22
22
|
} from "@opengeni/runtime";
|
|
23
23
|
import { settingsWithResolvedModelContext, type Settings } from "@opengeni/config";
|
|
24
|
+
import { projectReasoningConfigurations, supportsReasoningConfiguration } from "@opengeni/codex";
|
|
24
25
|
import { settingsWithSessionMcpServersForRun } from "../capabilities";
|
|
25
26
|
import { resolveRigProviderImageForRun } from "@opengeni/core";
|
|
26
|
-
import { resolveWorkspacePackRuntime, settingsWithPackSandboxImage } from "../packs";
|
|
27
27
|
import { createModelHistoryAttachmentProjector } from "../run-input";
|
|
28
28
|
import type {
|
|
29
29
|
TurnActivityServices as ActivityServices,
|
|
@@ -84,7 +84,6 @@ export type GovernanceModelDeps = {
|
|
|
84
84
|
|
|
85
85
|
export type GovernanceModelOk = {
|
|
86
86
|
runtimePreparationStartedAt: number;
|
|
87
|
-
packRuntime: Awaited<ReturnType<typeof resolveWorkspacePackRuntime>>;
|
|
88
87
|
rigVersion:
|
|
89
88
|
| NonNullable<Awaited<ReturnType<typeof materializeRigVersionForAttempt>>>["version"]
|
|
90
89
|
| null;
|
|
@@ -189,17 +188,15 @@ export async function prepareGovernanceAndModel(
|
|
|
189
188
|
attemptId: input.attemptId,
|
|
190
189
|
executionGeneration: turn.executionGeneration,
|
|
191
190
|
};
|
|
192
|
-
// Independent workspace reads after the personal-resource fence.
|
|
193
|
-
//
|
|
191
|
+
// Independent workspace reads after the personal-resource fence. The
|
|
192
|
+
// frozen rig, governance snapshots, and model policy do
|
|
194
193
|
// not depend on each other. Company-brain selection still waits on the
|
|
195
194
|
// snapshots below so its receipt stays exact.
|
|
196
195
|
const [
|
|
197
|
-
packRuntime,
|
|
198
196
|
rigMaterialization,
|
|
199
197
|
[workspace, companyProfileSnapshot, instructionPolicySnapshot, preferenceSnapshot],
|
|
200
198
|
workspaceModelPolicy,
|
|
201
199
|
] = await Promise.all([
|
|
202
|
-
resolveWorkspacePackRuntime(db, input.workspaceId),
|
|
203
200
|
session.rigId && session.rigVersionId
|
|
204
201
|
? (async () =>
|
|
205
202
|
await materializeRigVersionForAttempt(db, {
|
|
@@ -280,14 +277,7 @@ export async function prepareGovernanceAndModel(
|
|
|
280
277
|
// Contribution telemetry must never change model execution semantics.
|
|
281
278
|
}
|
|
282
279
|
// A Rig is always a setup/check layer over the deployment platform sandbox.
|
|
283
|
-
|
|
284
|
-
const logicalSandboxSettings = rigVersion
|
|
285
|
-
? capabilitySettings
|
|
286
|
-
: settingsWithPackSandboxImage(
|
|
287
|
-
capabilitySettings,
|
|
288
|
-
packRuntime.sandboxImage,
|
|
289
|
-
packRuntime.sandboxProviderImages,
|
|
290
|
-
);
|
|
280
|
+
const logicalSandboxSettings = capabilitySettings;
|
|
291
281
|
const providerImageSelection = await resolveRigProviderImageForRun(
|
|
292
282
|
logicalSandboxSettings,
|
|
293
283
|
rigVersion,
|
|
@@ -299,9 +289,8 @@ export async function prepareGovernanceAndModel(
|
|
|
299
289
|
? (providerImageSelection.imageId ?? undefined)
|
|
300
290
|
: undefined;
|
|
301
291
|
const baseRunSettings = {
|
|
302
|
-
//
|
|
303
|
-
//
|
|
304
|
-
// provider-native ID is then applied only to fresh creation without
|
|
292
|
+
// A Rig uses the deployment platform base. A matching verified
|
|
293
|
+
// provider-native ID is applied only to fresh creation without
|
|
305
294
|
// changing the logical lease image.
|
|
306
295
|
...providerImageSettings,
|
|
307
296
|
openaiModel: turn.model,
|
|
@@ -365,12 +354,20 @@ export async function prepareGovernanceAndModel(
|
|
|
365
354
|
fileIds,
|
|
366
355
|
}),
|
|
367
356
|
);
|
|
357
|
+
const useReasoningUpdates =
|
|
358
|
+
runSettings.reasoningConfigurationUpdatesEnabled &&
|
|
359
|
+
providerApi === "responses" &&
|
|
360
|
+
(resolvedModel?.provider.id === "codex" || resolvedModel?.provider.id === "openai") &&
|
|
361
|
+
supportsReasoningConfiguration(turnExecutionPolicy.upstreamModelId, turn.reasoningEffort);
|
|
368
362
|
const modelHistoryProjector = async (
|
|
369
363
|
items: Array<Record<string, unknown>>,
|
|
370
364
|
projectionOptions?: Parameters<typeof attachmentProjector>[1],
|
|
371
365
|
) =>
|
|
372
366
|
projectModelInputForCapabilities(
|
|
373
|
-
|
|
367
|
+
projectReasoningConfigurations(
|
|
368
|
+
await attachmentProjector(items, projectionOptions),
|
|
369
|
+
useReasoningUpdates,
|
|
370
|
+
),
|
|
374
371
|
modelInputPolicy,
|
|
375
372
|
);
|
|
376
373
|
const generatedImageHistoryProjector = async (
|
|
@@ -460,7 +457,6 @@ export async function prepareGovernanceAndModel(
|
|
|
460
457
|
return {
|
|
461
458
|
ok: {
|
|
462
459
|
runtimePreparationStartedAt,
|
|
463
|
-
packRuntime,
|
|
464
460
|
rigVersion,
|
|
465
461
|
rigName,
|
|
466
462
|
agentHumanInputEnabled,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
normalizeProtocolJsonValue,
|
|
3
|
+
TURN_OPERATIONAL_NOTICE_PREFIX,
|
|
3
4
|
sanitizeHistoryItemsForModel,
|
|
4
5
|
toolCallIdFromSdkItem,
|
|
5
6
|
} from "@opengeni/runtime";
|
|
@@ -31,15 +32,20 @@ export function historyRowsToAppend(
|
|
|
31
32
|
}
|
|
32
33
|
// Canonical model-facing inputs (including machine-input batches) are
|
|
33
34
|
// persisted before inference and therefore live inside the prefix represented
|
|
34
|
-
// by persistedHistoryCount.
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
// positions to actual model/tool output.
|
|
35
|
+
// by persistedHistoryCount. Explicitly turn-scoped operational notices remain
|
|
36
|
+
// at their original positions: deleting them rewrites the model-visible prefix
|
|
37
|
+
// on the next turn and during compaction. Unscoped synthetic system messages
|
|
38
|
+
// still cannot silently become permanent conversation instructions.
|
|
39
39
|
const rows: Array<{ position: number; item: Record<string, unknown> }> = [];
|
|
40
40
|
for (const [offset, item] of modelReady.slice(persistedHistoryCount).entries()) {
|
|
41
41
|
if (item.type === "message" && item.role === "system") {
|
|
42
|
-
|
|
42
|
+
const content =
|
|
43
|
+
typeof item.content === "string"
|
|
44
|
+
? item.content
|
|
45
|
+
: Array.isArray(item.content)
|
|
46
|
+
? item.content.map((part: { text?: string }) => part.text ?? "").join("")
|
|
47
|
+
: "";
|
|
48
|
+
if (!content.startsWith(TURN_OPERATIONAL_NOTICE_PREFIX)) continue;
|
|
43
49
|
}
|
|
44
50
|
rows.push({
|
|
45
51
|
position: nextPosition + rows.length,
|
|
@@ -149,10 +155,18 @@ export function pendingToolCallFromSdkEvent(event: unknown): {
|
|
|
149
155
|
return null;
|
|
150
156
|
}
|
|
151
157
|
const raw = item.rawItem as Record<string, unknown>;
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
|
|
158
|
+
// Provider-executed search/image calls carry their outcome in this item,
|
|
159
|
+
// not a later function result. Registering them as pending leaves an
|
|
160
|
+
// impossible receipt behind even after the turn completes. Hosted MCP
|
|
161
|
+
// approval requests are different: they still require a response.
|
|
162
|
+
const providerData = raw.providerData as Record<string, unknown> | undefined;
|
|
163
|
+
if (
|
|
164
|
+
raw.type === "hosted_tool_call" &&
|
|
165
|
+
raw.name !== "mcp_approval_request" &&
|
|
166
|
+
providerData?.type !== "mcp_approval_request" &&
|
|
167
|
+
(raw.status === "completed" || raw.name === "image_generation_call")
|
|
168
|
+
)
|
|
169
|
+
return null;
|
|
156
170
|
const callId = toolCallIdFromSdkItem(raw) ?? raw.id;
|
|
157
171
|
const callType = raw.type;
|
|
158
172
|
if (typeof callId !== "string" || callId.length === 0 || typeof callType !== "string") {
|
|
@@ -198,13 +198,16 @@ export async function drainAttemptOwnedSandboxWriters(input: {
|
|
|
198
198
|
gitCredentialRenewals: readonly Pick<GitCredentialRenewalController, "stop">[];
|
|
199
199
|
codemodeTokenRenewal: Pick<CodemodeTokenRenewalController, "stop"> | null;
|
|
200
200
|
runCredentialRenewal: Pick<RunCredentialRenewalController, "stop"> | null;
|
|
201
|
+
onStage?: (stage: "tool_writers" | "credential_renewals") => void;
|
|
201
202
|
}): Promise<void> {
|
|
203
|
+
input.onStage?.("tool_writers");
|
|
202
204
|
if (input.toolCancellationFence) {
|
|
203
205
|
input.toolCancellationFence.cancel(
|
|
204
206
|
input.cancellationReason ?? new Error("TURN_ATTEMPT_FENCED"),
|
|
205
207
|
);
|
|
206
208
|
await input.toolCancellationFence.waitForQuiescence();
|
|
207
209
|
}
|
|
210
|
+
input.onStage?.("credential_renewals");
|
|
208
211
|
await Promise.all(input.gitCredentialRenewals.map(async (renewal) => await renewal.stop()));
|
|
209
212
|
await input.codemodeTokenRenewal?.stop();
|
|
210
213
|
await input.runCredentialRenewal?.stop();
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { getSandboxRecoveryDiscontinuity, type Database } from "@opengeni/db";
|
|
2
|
+
|
|
3
|
+
/** Opt-in belongs to this warning-aware worker implementation, not the DB
|
|
4
|
+
* client, role defaults or deployment configuration. Claim stamps it locally. */
|
|
5
|
+
export const FILESYSTEM_DISCONTINUITY_PROTOCOL = 1 as const;
|
|
6
|
+
|
|
7
|
+
export async function recoveryAwareSessionInstructions(
|
|
8
|
+
db: Database,
|
|
9
|
+
workspaceId: string,
|
|
10
|
+
session: { id: string; instructions?: string | null },
|
|
11
|
+
): Promise<string> {
|
|
12
|
+
const filesystemDiscontinuity = await getSandboxRecoveryDiscontinuity(
|
|
13
|
+
db,
|
|
14
|
+
workspaceId,
|
|
15
|
+
session.id,
|
|
16
|
+
);
|
|
17
|
+
return [session.instructions, filesystemDiscontinuity].filter(Boolean).join("\n\n");
|
|
18
|
+
}
|
|
@@ -287,13 +287,10 @@ export async function prepareRunCredentials(deps: PrepareRunCredentialsDeps) {
|
|
|
287
287
|
// provider may supply it; unset still self-mints GitHub from settings.
|
|
288
288
|
// gitToken/gitTokens are undefined on the selfhosted skip path (the machine
|
|
289
289
|
// uses its own git creds).
|
|
290
|
-
// Git
|
|
291
|
-
//
|
|
292
|
-
// roots for the same execution merely because the call sites are far apart.
|
|
290
|
+
// Git credential providers share one lineage snapshot for this turn.
|
|
291
|
+
// MCP access uses the native accepted connection context independently.
|
|
293
292
|
const needsHostCredentialRoot = Boolean(
|
|
294
|
-
connectionCredentials?.gitCredentials ||
|
|
295
|
-
personalGitHubCredentials ||
|
|
296
|
-
connectionCredentials?.mcpCredentials,
|
|
293
|
+
connectionCredentials?.gitCredentials || personalGitHubCredentials,
|
|
297
294
|
);
|
|
298
295
|
const hostCredentialRootSessionId = needsHostCredentialRoot
|
|
299
296
|
? await getSessionRootId(db, input.workspaceId, input.sessionId)
|
|
@@ -748,7 +745,6 @@ export async function prepareRunCredentials(deps: PrepareRunCredentialsDeps) {
|
|
|
748
745
|
initialRunCredentialMaterial,
|
|
749
746
|
runCredentialsNote,
|
|
750
747
|
authorizeGitHubTokenMint,
|
|
751
|
-
hostCredentialRootSessionId,
|
|
752
748
|
gitCredentialAuthority,
|
|
753
749
|
codemodeAuthority,
|
|
754
750
|
sandboxArtifactRuntime,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { getSessionAuthorityProjection } from "@opengeni/db";
|
|
1
2
|
import { createKnowledgeSourceSyncActivities } from "../knowledge-source-sync";
|
|
2
|
-
import { prepareTurnKnowledgeSources } from "./knowledge-sources";
|
|
3
3
|
import {
|
|
4
4
|
assertModelConnectionAllowsTurn,
|
|
5
5
|
freezeAgentLearningPolicy,
|
|
@@ -11,7 +11,11 @@ import {
|
|
|
11
11
|
getMaterializedSandboxFileResources,
|
|
12
12
|
markSandboxFileResourcesMaterialized,
|
|
13
13
|
} from "@opengeni/db";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
sandboxOperationMetricObserver,
|
|
16
|
+
turnExecutionTelemetryKey,
|
|
17
|
+
withTraceContext,
|
|
18
|
+
} from "@opengeni/observability";
|
|
15
19
|
import {
|
|
16
20
|
REMOTE_COMPACTION_V2_BETA_FEATURE,
|
|
17
21
|
REMOTE_COMPACTION_V2_IMPLEMENTATION,
|
|
@@ -152,7 +156,11 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
|
|
|
152
156
|
// final model checkpoint happens while the activity still owns its complete
|
|
153
157
|
// turn graph and must not suppress collection after that graph is released.
|
|
154
158
|
const turnCompletionMemoryCollector = createModelCheckpointMemoryCollector();
|
|
155
|
-
|
|
159
|
+
const runAgentTurn = async (
|
|
160
|
+
input: RunAgentTurnInput,
|
|
161
|
+
resolvedServices: ActivityServices,
|
|
162
|
+
activitySpan: ReturnType<ActivityServices["observability"]["startSpan"]>,
|
|
163
|
+
): Promise<RunAgentTurnResult> => {
|
|
156
164
|
const {
|
|
157
165
|
settings,
|
|
158
166
|
catalogSourceSettings = settings,
|
|
@@ -169,7 +177,7 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
|
|
|
169
177
|
connectionCredentials,
|
|
170
178
|
personalGitHubCredentials,
|
|
171
179
|
startVideoGenerationWorkflow,
|
|
172
|
-
} =
|
|
180
|
+
} = resolvedServices;
|
|
173
181
|
const activityContext = currentActivityContext();
|
|
174
182
|
const cancellationSignal = activityContext?.cancellationSignal;
|
|
175
183
|
// Temporal cancellation is not the only way an activity loses ownership:
|
|
@@ -207,11 +215,6 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
|
|
|
207
215
|
});
|
|
208
216
|
const dispatchId = activityContext?.info.activityId ?? randomUUID();
|
|
209
217
|
const activityStarted = performance.now();
|
|
210
|
-
const activitySpan = observability.startSpan("worker.run_agent_segment", {
|
|
211
|
-
"opengeni.session_id": input.sessionId,
|
|
212
|
-
"opengeni.workflow_id": input.workflowId,
|
|
213
|
-
"opengeni.trigger_kind": input.trigger.kind,
|
|
214
|
-
});
|
|
215
218
|
const acknowledgeLostAttemptOwnership = (): void => {
|
|
216
219
|
// A stale terminal/recovery settlement can lose either to a benign
|
|
217
220
|
// successor or to Pause/Steer closing this exact attempt. Only the
|
|
@@ -434,8 +437,26 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
|
|
|
434
437
|
executionGeneration: attempt.executionGeneration,
|
|
435
438
|
},
|
|
436
439
|
});
|
|
440
|
+
const attachmentAuthority = await getSessionAuthorityProjection(
|
|
441
|
+
db,
|
|
442
|
+
input.workspaceId,
|
|
443
|
+
input.sessionId,
|
|
444
|
+
);
|
|
445
|
+
if (!attachmentAuthority) throw new Error("Session attachment authority unavailable");
|
|
437
446
|
return await withSessionRlsActorContext(
|
|
438
447
|
{
|
|
448
|
+
sessionAttachmentReadAccess: {
|
|
449
|
+
sessionId: input.sessionId,
|
|
450
|
+
authorityEpoch: attachmentAuthority.authorityEpoch,
|
|
451
|
+
actor: {
|
|
452
|
+
kind: "agent_attempt",
|
|
453
|
+
subjectId: "service:agent-turn",
|
|
454
|
+
callerSessionId: input.sessionId,
|
|
455
|
+
turnId: attempt.turnId,
|
|
456
|
+
attemptId: input.attemptId,
|
|
457
|
+
executionGeneration: attempt.executionGeneration,
|
|
458
|
+
},
|
|
459
|
+
},
|
|
439
460
|
subjectId: "service:agent-turn",
|
|
440
461
|
initiatingHumanSubjectId: fileAuthoritySubjectId,
|
|
441
462
|
privateFileOwnerSubjectId:
|
|
@@ -511,7 +532,6 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
|
|
|
511
532
|
});
|
|
512
533
|
const {
|
|
513
534
|
runtimePreparationStartedAt,
|
|
514
|
-
packRuntime,
|
|
515
535
|
rigVersion,
|
|
516
536
|
rigName,
|
|
517
537
|
agentHumanInputEnabled,
|
|
@@ -556,6 +576,12 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
|
|
|
556
576
|
runSettings,
|
|
557
577
|
input.workspaceId,
|
|
558
578
|
providerTurn.effectiveCodexCredentialId ?? "",
|
|
579
|
+
undefined,
|
|
580
|
+
{
|
|
581
|
+
turnId: turn.id,
|
|
582
|
+
holderId: leases.codex.holderId!,
|
|
583
|
+
generation: leases.codex.generation!,
|
|
584
|
+
},
|
|
559
585
|
);
|
|
560
586
|
const resolveTrackedToken = async (
|
|
561
587
|
resolve: () => ReturnType<typeof resolver.getToken>,
|
|
@@ -1030,44 +1056,7 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
|
|
|
1030
1056
|
knowledgeRecoveryNote =
|
|
1031
1057
|
"[OpenGeni confirmation recovery unavailable] Do not assume an earlier remember confirmation was published. Inspect the current Knowledge or instruction review.";
|
|
1032
1058
|
}
|
|
1033
|
-
const
|
|
1034
|
-
settings: runSettings,
|
|
1035
|
-
scope: {
|
|
1036
|
-
accountId: input.accountId,
|
|
1037
|
-
workspaceId: input.workspaceId,
|
|
1038
|
-
sessionId: input.sessionId,
|
|
1039
|
-
turnId: turn.id,
|
|
1040
|
-
attemptId: input.attemptId,
|
|
1041
|
-
executionGeneration: attempt.executionGeneration,
|
|
1042
|
-
},
|
|
1043
|
-
// Only attachments on this accepted turn are new source inputs.
|
|
1044
|
-
// Session-level file receipts remain historical context and must
|
|
1045
|
-
// never trigger metadata reads, downloads, or re-preparation.
|
|
1046
|
-
resources: turn.resources,
|
|
1047
|
-
selectedTools: session.firstPartyMcpTools,
|
|
1048
|
-
permissions: session.firstPartyMcpPermissions,
|
|
1049
|
-
learningMode: learning.effective.knowledge,
|
|
1050
|
-
signal: cancellationSignal,
|
|
1051
|
-
onOutcome: async (outcome) => {
|
|
1052
|
-
if (outcome.status === "disabled") return;
|
|
1053
|
-
await eventing.publish!(
|
|
1054
|
-
[
|
|
1055
|
-
{
|
|
1056
|
-
type:
|
|
1057
|
-
outcome.status === "failed"
|
|
1058
|
-
? "knowledge.source.failed"
|
|
1059
|
-
: "knowledge.source.prepared",
|
|
1060
|
-
payload: outcome,
|
|
1061
|
-
},
|
|
1062
|
-
],
|
|
1063
|
-
true,
|
|
1064
|
-
);
|
|
1065
|
-
},
|
|
1066
|
-
});
|
|
1067
|
-
|
|
1068
|
-
const knowledgeSourcePreparationNote =
|
|
1069
|
-
[knowledgeRecoveryNote, attachmentPreparationNote].filter(Boolean).join("\n\n") ||
|
|
1070
|
-
undefined;
|
|
1059
|
+
const knowledgeSourcePreparationNote = knowledgeRecoveryNote;
|
|
1071
1060
|
|
|
1072
1061
|
const sandboxRoute = await resolveSandboxRoute({
|
|
1073
1062
|
input,
|
|
@@ -1148,7 +1137,6 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
|
|
|
1148
1137
|
establishPolicy,
|
|
1149
1138
|
initialRunCredentialMaterial,
|
|
1150
1139
|
runCredentialsNote,
|
|
1151
|
-
hostCredentialRootSessionId,
|
|
1152
1140
|
codemodeAuthority,
|
|
1153
1141
|
sandboxArtifactRuntime,
|
|
1154
1142
|
sandboxEnvironment,
|
|
@@ -1268,7 +1256,6 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
|
|
|
1268
1256
|
];
|
|
1269
1257
|
const toolRuntime = await prepareTurnToolRuntime({
|
|
1270
1258
|
fetchKnowledgeSource: sourceActivities.runKnowledgeSourceSyncBatch,
|
|
1271
|
-
selectedSkillActivations: packRuntime.skillActivations,
|
|
1272
1259
|
input,
|
|
1273
1260
|
catalogSourceSettings,
|
|
1274
1261
|
db,
|
|
@@ -1277,7 +1264,6 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
|
|
|
1277
1264
|
objectStorage,
|
|
1278
1265
|
observability,
|
|
1279
1266
|
cancellationSignal,
|
|
1280
|
-
connectionCredentials,
|
|
1281
1267
|
eventing,
|
|
1282
1268
|
attempt,
|
|
1283
1269
|
sandboxState,
|
|
@@ -1295,7 +1281,6 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
|
|
|
1295
1281
|
lazyToolTransport,
|
|
1296
1282
|
turnTools,
|
|
1297
1283
|
connectionScope,
|
|
1298
|
-
hostCredentialRootSessionId,
|
|
1299
1284
|
sandboxArtifactRuntime,
|
|
1300
1285
|
activeSandboxBackend,
|
|
1301
1286
|
groupBoxBackend,
|
|
@@ -1771,4 +1756,35 @@ export function createRunAgentTurnActivity(services: () => Promise<ActivityServi
|
|
|
1771
1756
|
});
|
|
1772
1757
|
}
|
|
1773
1758
|
};
|
|
1759
|
+
return async (input: RunAgentTurnInput): Promise<RunAgentTurnResult> => {
|
|
1760
|
+
const resolvedServices = await services();
|
|
1761
|
+
const correlationId = turnExecutionTelemetryKey(
|
|
1762
|
+
input.workspaceId,
|
|
1763
|
+
input.sessionId,
|
|
1764
|
+
input.attemptId,
|
|
1765
|
+
);
|
|
1766
|
+
const span = resolvedServices.observability.startSpan(
|
|
1767
|
+
"worker.run_agent_segment",
|
|
1768
|
+
{
|
|
1769
|
+
"opengeni.trigger_kind": input.trigger.kind,
|
|
1770
|
+
correlationId,
|
|
1771
|
+
},
|
|
1772
|
+
{ parent: null },
|
|
1773
|
+
);
|
|
1774
|
+
try {
|
|
1775
|
+
return await withTraceContext(span, () => {
|
|
1776
|
+
try {
|
|
1777
|
+
resolvedServices.observability.info("worker execution started", { correlationId });
|
|
1778
|
+
} catch {
|
|
1779
|
+
// Correlation diagnostics never affect execution or admission.
|
|
1780
|
+
}
|
|
1781
|
+
return runAgentTurn(input, resolvedServices, span);
|
|
1782
|
+
});
|
|
1783
|
+
} catch (error) {
|
|
1784
|
+
span.end({ error });
|
|
1785
|
+
throw error;
|
|
1786
|
+
} finally {
|
|
1787
|
+
span.end();
|
|
1788
|
+
}
|
|
1789
|
+
};
|
|
1774
1790
|
}
|
|
@@ -6,7 +6,10 @@ import {
|
|
|
6
6
|
assertPersonalMachineForAttempt,
|
|
7
7
|
type SandboxRecord,
|
|
8
8
|
} from "@opengeni/db";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
sandboxOperationMetricObserver,
|
|
11
|
+
sandboxCaptureWaitMetricObserver,
|
|
12
|
+
} from "@opengeni/observability";
|
|
10
13
|
import {
|
|
11
14
|
withRunCredentialsSession,
|
|
12
15
|
runOwnedSandboxSetup,
|
|
@@ -29,7 +32,7 @@ import {
|
|
|
29
32
|
type MintedRunGitCredentials,
|
|
30
33
|
type SandboxCodemodeAuthority,
|
|
31
34
|
} from "../environment";
|
|
32
|
-
import { rigProviderImageSourceImage } from "../
|
|
35
|
+
import { rigProviderImageSourceImage } from "../sandbox-images";
|
|
33
36
|
import type { TurnActivityServices as ActivityServices, RunAgentTurnInput } from "../types";
|
|
34
37
|
import type { currentActivityContext } from "../streaming";
|
|
35
38
|
import { resumeBoxForTurn, type ResumedTurnSandbox } from "../../sandbox-resume";
|
|
@@ -444,6 +447,7 @@ export async function establishTurnSandbox(deps: EstablishTurnSandboxDeps): Prom
|
|
|
444
447
|
bus,
|
|
445
448
|
onOp: machineOpObserver.observer,
|
|
446
449
|
onSandboxOperation: sandboxOperationObserver,
|
|
450
|
+
onSandboxCaptureWait: sandboxCaptureWaitMetricObserver(observability),
|
|
447
451
|
opJournal,
|
|
448
452
|
},
|
|
449
453
|
{
|
|
@@ -515,6 +519,7 @@ export async function establishTurnSandbox(deps: EstablishTurnSandboxDeps): Prom
|
|
|
515
519
|
opJournal,
|
|
516
520
|
onOp: machineOpObserver.observer,
|
|
517
521
|
onSandboxOperation: sandboxOperationObserver,
|
|
522
|
+
onSandboxCaptureWait: sandboxCaptureWaitMetricObserver(observability),
|
|
518
523
|
onHomeSandboxRebound,
|
|
519
524
|
...(runtimeCancellationSignal ? { waitSignal: runtimeCancellationSignal } : {}),
|
|
520
525
|
},
|
|
@@ -755,6 +760,7 @@ export async function establishTurnSandbox(deps: EstablishTurnSandboxDeps): Prom
|
|
|
755
760
|
bus,
|
|
756
761
|
opJournal,
|
|
757
762
|
onSandboxOperation: sandboxOperationObserver,
|
|
763
|
+
onSandboxCaptureWait: sandboxCaptureWaitMetricObserver(observability),
|
|
758
764
|
onHomeSandboxLost: publishSandboxLost,
|
|
759
765
|
onHomeSandboxRebound,
|
|
760
766
|
...(runtimeCancellationSignal ? { waitSignal: runtimeCancellationSignal } : {}),
|
|
@@ -1004,6 +1010,7 @@ export async function bindLazySandboxProvisioner(
|
|
|
1004
1010
|
{
|
|
1005
1011
|
settings: runSettings,
|
|
1006
1012
|
environment: sandboxEnvironment,
|
|
1013
|
+
recordLazyManifest: true,
|
|
1007
1014
|
onRuntimeEvent: async (event) => {
|
|
1008
1015
|
await eventing.publish?.([{ type: event.type, payload: event.payload }], true);
|
|
1009
1016
|
},
|
|
@@ -1122,6 +1129,7 @@ export async function bindLazySandboxProvisioner(
|
|
|
1122
1129
|
opJournal,
|
|
1123
1130
|
onOp: machineOpObserver.observer,
|
|
1124
1131
|
onSandboxOperation: sandboxOperationObserver,
|
|
1132
|
+
onSandboxCaptureWait: sandboxCaptureWaitMetricObserver(observability),
|
|
1125
1133
|
onHomeSandboxLost: publishSandboxLost,
|
|
1126
1134
|
onHomeSandboxRebound,
|
|
1127
1135
|
...(runtimeCancellationSignal ? { waitSignal: runtimeCancellationSignal } : {}),
|
|
@@ -268,15 +268,6 @@ export async function reconcileActiveSandboxPointer(
|
|
|
268
268
|
return { pointer: reread, record: rereadRecord };
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
-
/**
|
|
272
|
-
* Warm the Modal private-registry image for the image ref this turn actually
|
|
273
|
-
* resolved, not only the deployment-global OPENGENI_MODAL_IMAGE_REF warmed at
|
|
274
|
-
* worker boot. A provider-native modalImageId bypasses registry import and is
|
|
275
|
-
* resolved by ModalImageSelector.fromId during create. Otherwise packs can
|
|
276
|
-
* override `modalImageRef` per workspace/turn, so a private pack image must be
|
|
277
|
-
* resolved before sandbox creation or Modal falls back to the unauthenticated
|
|
278
|
-
* `fromTag` path.
|
|
279
|
-
*/
|
|
280
271
|
export async function ensureTurnModalRegistryImage(
|
|
281
272
|
runSettings: Settings,
|
|
282
273
|
sandboxCreationBackend: Settings["sandboxBackend"] | undefined,
|
|
@@ -302,17 +293,6 @@ export type SandboxArtifactRuntimeAdmission = Readonly<{
|
|
|
302
293
|
environment: Readonly<Record<string, string>>;
|
|
303
294
|
}>;
|
|
304
295
|
|
|
305
|
-
/**
|
|
306
|
-
* Admit the optional native standalone-file runtime only for the deployment's
|
|
307
|
-
* exact base sandbox image contract. A pack/rig image override is an
|
|
308
|
-
* independent filesystem and therefore fails closed even when the deployment
|
|
309
|
-
* base image is capable. Collaborative artifact skills are admitted separately
|
|
310
|
-
* from the frozen canonical tool catalog.
|
|
311
|
-
*
|
|
312
|
-
* This keeps lazy provisioning intact: CI/release proves the image closure,
|
|
313
|
-
* while a before-agent-start doctor verifies the actual box before any model
|
|
314
|
-
* call. No speculative sandbox is created merely to populate the skill index.
|
|
315
|
-
*/
|
|
316
296
|
export function sandboxArtifactRuntimeAdmission(
|
|
317
297
|
deploymentSettings: Settings,
|
|
318
298
|
runSettings: Settings,
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
} from "@opengeni/db";
|
|
9
9
|
import {
|
|
10
10
|
RoutingMutationOutcomeUnknownError,
|
|
11
|
+
runManagedCodemodeClientHook,
|
|
11
12
|
type EstablishedSandboxSession,
|
|
12
13
|
} from "@opengeni/runtime";
|
|
13
14
|
import {
|
|
@@ -18,7 +19,10 @@ import {
|
|
|
18
19
|
import { createRuntimeBatcher, currentActivityContext } from "../streaming";
|
|
19
20
|
import type { TurnActivityServices as ActivityServices, RunAgentTurnInput } from "../types";
|
|
20
21
|
import { maybePersistWarmWorkspaceSnapshot, type ResumedTurnSandbox } from "../../sandbox-resume";
|
|
21
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
recordCreditMicros,
|
|
24
|
+
runtimeMetricsHooksForObservability,
|
|
25
|
+
} from "../../observability-metrics";
|
|
22
26
|
import { ChannelAPartialMutationError } from "@opengeni/runtime/sandbox";
|
|
23
27
|
|
|
24
28
|
import { safeErrorDiagnostic } from "./errors";
|
|
@@ -124,16 +128,37 @@ export function createSandboxTurnRuntime(deps: SandboxTurnRuntimeDeps) {
|
|
|
124
128
|
// alive. Keep setup/snapshot persistence on the rebound raw session while
|
|
125
129
|
// preserving the SDK-owned routing proxy for eager turns. Lazy turns hold the
|
|
126
130
|
// proxy separately, so their worker-side handle may replace its raw session.
|
|
127
|
-
const onHomeSandboxRebound = (rebound: {
|
|
131
|
+
const onHomeSandboxRebound = async (rebound: {
|
|
128
132
|
established: EstablishedSandboxSession;
|
|
129
133
|
leaseEpoch: number;
|
|
130
|
-
}): void => {
|
|
134
|
+
}): Promise<void> => {
|
|
131
135
|
const current = sandboxState.resolvedSandbox;
|
|
136
|
+
if (!current) throw new Error("Home sandbox rebound before turn sandbox admission");
|
|
137
|
+
if (
|
|
138
|
+
rebound.established.backendId !== "selfhosted" &&
|
|
139
|
+
rebound.established.instanceId !== current.established.instanceId
|
|
140
|
+
) {
|
|
141
|
+
// An older archive may lack this worker release's selected client. Only
|
|
142
|
+
// prepare that client, never replay turn-start hooks/credentials/resources.
|
|
143
|
+
// Fence the replacement itself, not the stale turn-start handle. Both
|
|
144
|
+
// mutation admission and settlement precede raw-state/route publication.
|
|
145
|
+
const commandFence = eventing.toolCancellationFenceRef.current;
|
|
146
|
+
await runWorkspaceMutationForSandbox(
|
|
147
|
+
{ ...current, established: rebound.established, leaseEpoch: rebound.leaseEpoch },
|
|
148
|
+
"homeSandboxClientPreparation",
|
|
149
|
+
() =>
|
|
150
|
+
runManagedCodemodeClientHook(rebound.established.session as never, {
|
|
151
|
+
environment: {},
|
|
152
|
+
...(commandFence
|
|
153
|
+
? { commandRunner: commandFence.runSandboxCommand.bind(commandFence) }
|
|
154
|
+
: {}),
|
|
155
|
+
}),
|
|
156
|
+
);
|
|
157
|
+
}
|
|
132
158
|
const previousSession = current?.established.session;
|
|
133
159
|
const preserveRoutingProxy =
|
|
134
160
|
current !== null && previousSession !== sandboxState.setupBoxSession;
|
|
135
161
|
sandboxState.setupBoxSession = rebound.established.session;
|
|
136
|
-
if (!current) return;
|
|
137
162
|
current.leaseEpoch = rebound.leaseEpoch;
|
|
138
163
|
current.established = preserveRoutingProxy
|
|
139
164
|
? {
|
|
@@ -539,8 +564,10 @@ export function createSandboxTurnRuntime(deps: SandboxTurnRuntimeDeps) {
|
|
|
539
564
|
// MID-SESSION snapshot (sandbox-file-persistence): while the turn holds
|
|
540
565
|
// the box, fold a fresh /workspace snapshot onto the lease every
|
|
541
566
|
// sandboxSnapshotIntervalMs, so a box death the reaper never sees
|
|
542
|
-
// (Modal hard timeout mid-busy, OOM, infra)
|
|
543
|
-
//
|
|
567
|
+
// (Modal hard timeout mid-busy, OOM, infra) has a recent recovery point
|
|
568
|
+
// when capture succeeds. This interval is not a maximum recovery-point
|
|
569
|
+
// age: live writers and failed captures can defer publication. A legit
|
|
570
|
+
// multi-day turn is otherwise completely unprotected
|
|
544
571
|
// (the reaper only drain-persists IDLE leases). Uses the UN-proxied box
|
|
545
572
|
// session (setupBoxSession): the routing veneer could swap mid-op and a
|
|
546
573
|
// selfhosted target has no persistWorkspace anyway. Best-effort +
|
|
@@ -558,7 +585,13 @@ export function createSandboxTurnRuntime(deps: SandboxTurnRuntimeDeps) {
|
|
|
558
585
|
})
|
|
559
586
|
) {
|
|
560
587
|
const snapshot = maybePersistWarmWorkspaceSnapshot(
|
|
561
|
-
{
|
|
588
|
+
{
|
|
589
|
+
db,
|
|
590
|
+
settings,
|
|
591
|
+
objectStorage,
|
|
592
|
+
sandboxMetrics: runtimeMetricsHooksForObservability(observability),
|
|
593
|
+
observability,
|
|
594
|
+
},
|
|
562
595
|
{
|
|
563
596
|
accountId: input.accountId,
|
|
564
597
|
workspaceId: input.workspaceId,
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { AttemptToolDefinition } from "@opengeni/codemode";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import type { SkillWriteReceipt } from "@opengeni/contracts";
|
|
4
|
+
|
|
5
|
+
export const SkillRemoveRequest = z
|
|
6
|
+
.object({
|
|
7
|
+
operationId: z.uuid(),
|
|
8
|
+
skillId: z.uuid(),
|
|
9
|
+
expectedRevisionId: z.uuid().nullable(),
|
|
10
|
+
expectedScopeVersion: z.number().int().positive(),
|
|
11
|
+
reason: z.string().min(1).max(2000),
|
|
12
|
+
})
|
|
13
|
+
.strict();
|
|
14
|
+
|
|
15
|
+
export function createSkillRemoveAttemptToolDefinition(input: {
|
|
16
|
+
authorize: () => Promise<void>;
|
|
17
|
+
remove: (request: z.infer<typeof SkillRemoveRequest>) => Promise<SkillWriteReceipt>;
|
|
18
|
+
}): AttemptToolDefinition {
|
|
19
|
+
return {
|
|
20
|
+
identity: { serverId: "opengeni", toolName: "skill_remove" },
|
|
21
|
+
modelName: "skill_remove",
|
|
22
|
+
codemodePath: ["opengeni", "skill_remove"],
|
|
23
|
+
title: "Permanently remove Skill",
|
|
24
|
+
description:
|
|
25
|
+
"Permanently delete a saved Skill and ALL its stored revisions, not a recoverable hide. Conversations remain unchanged. Read the exact Skill first; supply its current active revision and scope version. Built-in, repository and inline session Skills cannot be removed. Automatic applies deletion; Review first retains an explicitly labeled deletion proposal for human review in Knowledge > Needs review; Off refuses changes. Reuse the same operationId and exact arguments after an uncertain result. Never report a pending proposal as deleted.",
|
|
26
|
+
inputSchema: JSON.parse(JSON.stringify(z.toJSONSchema(SkillRemoveRequest))),
|
|
27
|
+
annotations: {
|
|
28
|
+
readOnlyHint: false,
|
|
29
|
+
destructiveHint: true,
|
|
30
|
+
idempotentHint: true,
|
|
31
|
+
openWorldHint: false,
|
|
32
|
+
},
|
|
33
|
+
source: "opengeni",
|
|
34
|
+
approval: "none",
|
|
35
|
+
execute: async (args) => {
|
|
36
|
+
await input.authorize();
|
|
37
|
+
const output = await input.remove(SkillRemoveRequest.parse(args));
|
|
38
|
+
const text = JSON.stringify(output);
|
|
39
|
+
return {
|
|
40
|
+
isError: false,
|
|
41
|
+
content: [{ type: "text", text }],
|
|
42
|
+
structuredContent: JSON.parse(text),
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|