@tangle-network/sandbox 0.30.0 → 0.30.2-develop.20260820100122.dc85046
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/agent/index.d.ts +3 -3
- package/dist/agent/index.js +3 -3
- package/dist/agent-run-outcome-TwIC-Ccd.d.ts +27 -0
- package/dist/{client-DyFI5GnE.d.ts → client-1OK1sjCh.d.ts} +19 -8
- package/dist/{client-BnhE_gUq.js → client-CCKEVUaD.js} +12 -2
- package/dist/core.d.ts +3 -3
- package/dist/core.js +2 -2
- package/dist/{index-ldDRk8Jh.d.ts → index-DAxioMfr.d.ts} +2 -2
- package/dist/index.d.ts +6 -5
- package/dist/index.js +5 -5
- package/dist/openai/index.d.ts +24 -2
- package/dist/openai/index.js +147 -67
- package/dist/{runtime-api-CK-4S7oA.js → runtime-api-TVLbV10b.js} +388 -2
- package/dist/runtime.d.ts +3 -2
- package/dist/runtime.js +2 -2
- package/dist/{sandbox-B49ZGQv7.d.ts → sandbox-Caev7isY.d.ts} +6 -8
- package/dist/{sandbox-BwIaQcE7.js → sandbox-DM-VD8ja.js} +207 -326
- package/dist/session-gateway/index.d.ts +56 -2
- package/dist/session-gateway/index.js +25 -3
- package/dist/tangle/index.d.ts +1 -1
- package/dist/tangle/index.js +1 -1
- package/dist/{tangle-CGzEfYFL.js → tangle-lzUyvhKp.js} +1 -1
- package/dist/{types-Qn3x7R8O.d.ts → types-D_ykdqn6.d.ts} +11 -7
- package/package.json +4 -3
package/dist/openai/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
1
2
|
//#region src/openai/hooks.ts
|
|
2
3
|
/**
|
|
3
4
|
* Sequential hook composer. Hooks run in registration order; `block` and
|
|
@@ -379,6 +380,101 @@ var InMemoryResponseStore = class {
|
|
|
379
380
|
}
|
|
380
381
|
};
|
|
381
382
|
//#endregion
|
|
383
|
+
//#region src/openai/terminal.ts
|
|
384
|
+
const SUCCESS_OUTCOMES = new Set([
|
|
385
|
+
"success",
|
|
386
|
+
"succeeded",
|
|
387
|
+
"completed",
|
|
388
|
+
"done"
|
|
389
|
+
]);
|
|
390
|
+
const ResponseErrorCodeSchema = z.enum([
|
|
391
|
+
"empty_image_file",
|
|
392
|
+
"failed_to_download_image",
|
|
393
|
+
"image_content_policy_violation",
|
|
394
|
+
"image_file_not_found",
|
|
395
|
+
"image_file_too_large",
|
|
396
|
+
"image_parse_error",
|
|
397
|
+
"image_too_large",
|
|
398
|
+
"image_too_small",
|
|
399
|
+
"invalid_base64_image",
|
|
400
|
+
"invalid_image",
|
|
401
|
+
"invalid_image_format",
|
|
402
|
+
"invalid_image_mode",
|
|
403
|
+
"invalid_image_url",
|
|
404
|
+
"invalid_prompt",
|
|
405
|
+
"rate_limit_exceeded",
|
|
406
|
+
"server_error",
|
|
407
|
+
"unsupported_image_media_type",
|
|
408
|
+
"vector_store_timeout"
|
|
409
|
+
]);
|
|
410
|
+
const OPENAI_MISSING_TERMINAL_REASON = "stream ended without a terminal event";
|
|
411
|
+
const OPENAI_MALFORMED_TERMINAL_REASON = "malformed terminal event";
|
|
412
|
+
const OpenAITerminalEventSchema = z.object({
|
|
413
|
+
type: z.enum(["done", "error"]),
|
|
414
|
+
outcome: z.string().optional(),
|
|
415
|
+
status: z.string().optional(),
|
|
416
|
+
success: z.boolean().optional(),
|
|
417
|
+
error: z.string().optional(),
|
|
418
|
+
message: z.string().optional()
|
|
419
|
+
}).passthrough();
|
|
420
|
+
function responseError(reason) {
|
|
421
|
+
const normalized = reason.trim().toLowerCase().replace(/[\s-]+/g, "_");
|
|
422
|
+
const parsedCode = ResponseErrorCodeSchema.safeParse(normalized);
|
|
423
|
+
return {
|
|
424
|
+
code: parsedCode.success ? parsedCode.data : normalized === "rate_limit" || normalized === "rate_limited" ? "rate_limit_exceeded" : normalized === "invalid_prompt_format" ? "invalid_prompt" : "server_error",
|
|
425
|
+
message: reason
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
function failure(reason) {
|
|
429
|
+
return {
|
|
430
|
+
status: "failed",
|
|
431
|
+
error: responseError(reason),
|
|
432
|
+
reason
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
function terminalOutcome(event) {
|
|
436
|
+
if (event.outcome?.trim()) return event.outcome.trim().toLowerCase();
|
|
437
|
+
if (event.status?.trim()) return event.status.trim().toLowerCase();
|
|
438
|
+
if (event.success === false) return "failure";
|
|
439
|
+
return "success";
|
|
440
|
+
}
|
|
441
|
+
function terminalReason(event, fallback) {
|
|
442
|
+
if (event.error && event.error.length > 0) return event.error;
|
|
443
|
+
if (event.message && event.message.length > 0) return event.message;
|
|
444
|
+
return fallback;
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Classify one top-level OpenAI-adapter event.
|
|
448
|
+
*
|
|
449
|
+
* `TangleSSEEvent` is not the nested `SandboxEvent` protocol. This function
|
|
450
|
+
* reads only fields on the supplied event itself.
|
|
451
|
+
*/
|
|
452
|
+
function classifyOpenAITerminal(event) {
|
|
453
|
+
if (event.type !== "done" && event.type !== "error") return null;
|
|
454
|
+
const parsed = OpenAITerminalEventSchema.safeParse(event);
|
|
455
|
+
if (!parsed.success) return failure(OPENAI_MALFORMED_TERMINAL_REASON);
|
|
456
|
+
if (parsed.data.type === "done") {
|
|
457
|
+
const outcome = terminalOutcome(parsed.data);
|
|
458
|
+
return SUCCESS_OUTCOMES.has(outcome) ? {
|
|
459
|
+
status: "completed",
|
|
460
|
+
error: null
|
|
461
|
+
} : failure(terminalReason(parsed.data, outcome));
|
|
462
|
+
}
|
|
463
|
+
return failure(terminalReason(parsed.data, "stream error"));
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Classify a complete collected stream. Any failure takes precedence.
|
|
467
|
+
*/
|
|
468
|
+
function classifyOpenAITerminalEvents(events) {
|
|
469
|
+
let terminal = null;
|
|
470
|
+
for (const event of events) {
|
|
471
|
+
const current = classifyOpenAITerminal(event);
|
|
472
|
+
if (!current) continue;
|
|
473
|
+
if (current.status === "failed" || terminal === null) terminal = current;
|
|
474
|
+
}
|
|
475
|
+
return terminal;
|
|
476
|
+
}
|
|
477
|
+
//#endregion
|
|
382
478
|
//#region src/openai/runs.ts
|
|
383
479
|
const TERMINAL_STATUSES = new Set([
|
|
384
480
|
"completed",
|
|
@@ -425,21 +521,6 @@ function readToolCallFromFrame(event) {
|
|
|
425
521
|
requiresAction: false
|
|
426
522
|
};
|
|
427
523
|
}
|
|
428
|
-
function readTerminal(event) {
|
|
429
|
-
if (event.type === "done") {
|
|
430
|
-
const outcome = typeof event.outcome === "string" && event.outcome || "success";
|
|
431
|
-
if (outcome === "success" || outcome === "completed" || outcome === "succeeded" || outcome === "done") return { status: "completed" };
|
|
432
|
-
return {
|
|
433
|
-
status: "failed",
|
|
434
|
-
reason: typeof event.error === "string" ? event.error : outcome
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
if (event.type === "error") return {
|
|
438
|
-
status: "failed",
|
|
439
|
-
reason: typeof event.message === "string" && event.message || typeof event.error === "string" && event.error || "stream error"
|
|
440
|
-
};
|
|
441
|
-
return null;
|
|
442
|
-
}
|
|
443
524
|
/**
|
|
444
525
|
* Internal queue helper: an async iterable backed by a bounded
|
|
445
526
|
* pending-list with a settle promise that resolves whenever a new
|
|
@@ -649,7 +730,7 @@ var Run = class {
|
|
|
649
730
|
if (handled === "requires_action") return;
|
|
650
731
|
continue;
|
|
651
732
|
}
|
|
652
|
-
const terminal =
|
|
733
|
+
const terminal = classifyOpenAITerminal(event);
|
|
653
734
|
if (terminal) {
|
|
654
735
|
if (terminal.status === "completed") {
|
|
655
736
|
this.transition("completed");
|
|
@@ -658,7 +739,7 @@ var Run = class {
|
|
|
658
739
|
this.transition("failed");
|
|
659
740
|
this.queue.push({
|
|
660
741
|
type: "failed",
|
|
661
|
-
reason: terminal.reason
|
|
742
|
+
reason: terminal.reason
|
|
662
743
|
});
|
|
663
744
|
}
|
|
664
745
|
this.queue.close();
|
|
@@ -666,8 +747,11 @@ var Run = class {
|
|
|
666
747
|
}
|
|
667
748
|
}
|
|
668
749
|
if (!isTerminal(this._status)) {
|
|
669
|
-
this.transition("
|
|
670
|
-
this.queue.push({
|
|
750
|
+
this.transition("failed");
|
|
751
|
+
this.queue.push({
|
|
752
|
+
type: "failed",
|
|
753
|
+
reason: OPENAI_MISSING_TERMINAL_REASON
|
|
754
|
+
});
|
|
671
755
|
this.queue.close();
|
|
672
756
|
}
|
|
673
757
|
} catch (err) {
|
|
@@ -1038,6 +1122,43 @@ function usageFromTokens(usage) {
|
|
|
1038
1122
|
output_tokens_details: { reasoning_tokens: 0 }
|
|
1039
1123
|
};
|
|
1040
1124
|
}
|
|
1125
|
+
function terminalResponse(event, ctx, terminal) {
|
|
1126
|
+
const tokens = event.type === "done" ? readUsage(event) : null;
|
|
1127
|
+
return {
|
|
1128
|
+
id: ctx.runId,
|
|
1129
|
+
object: "response",
|
|
1130
|
+
created_at: ctx.createdAt,
|
|
1131
|
+
output_text: "",
|
|
1132
|
+
error: terminal.error,
|
|
1133
|
+
incomplete_details: null,
|
|
1134
|
+
instructions: null,
|
|
1135
|
+
metadata: null,
|
|
1136
|
+
model: ctx.modelId,
|
|
1137
|
+
output: [],
|
|
1138
|
+
parallel_tool_calls: false,
|
|
1139
|
+
temperature: null,
|
|
1140
|
+
tool_choice: "auto",
|
|
1141
|
+
tools: [],
|
|
1142
|
+
top_p: null,
|
|
1143
|
+
status: terminal.status,
|
|
1144
|
+
usage: tokens ? usageFromTokens(tokens) : emptyResponseUsage()
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1147
|
+
function terminalResponseEvent(event, ctx, extras) {
|
|
1148
|
+
const terminal = classifyOpenAITerminal(event);
|
|
1149
|
+
if (!terminal) return null;
|
|
1150
|
+
const response = terminalResponse(event, ctx, terminal);
|
|
1151
|
+
if (terminal.status === "failed") return {
|
|
1152
|
+
type: "response.failed",
|
|
1153
|
+
sequence_number: nextSeq(extras),
|
|
1154
|
+
response
|
|
1155
|
+
};
|
|
1156
|
+
return {
|
|
1157
|
+
type: "response.completed",
|
|
1158
|
+
sequence_number: nextSeq(extras),
|
|
1159
|
+
response
|
|
1160
|
+
};
|
|
1161
|
+
}
|
|
1041
1162
|
function sandboxEventToResponsesEvent(event, ctx) {
|
|
1042
1163
|
const extras = getResponsesExtras(ctx);
|
|
1043
1164
|
switch (event.type) {
|
|
@@ -1089,48 +1210,8 @@ function sandboxEventToResponsesEvent(event, ctx) {
|
|
|
1089
1210
|
sequence_number: nextSeq(extras)
|
|
1090
1211
|
};
|
|
1091
1212
|
}
|
|
1092
|
-
case "error":
|
|
1093
|
-
|
|
1094
|
-
return {
|
|
1095
|
-
type: "response.output_item.done",
|
|
1096
|
-
item: {
|
|
1097
|
-
type: "message",
|
|
1098
|
-
id: `msg_${ctx.runId}`,
|
|
1099
|
-
role: "assistant",
|
|
1100
|
-
status: "incomplete",
|
|
1101
|
-
content: []
|
|
1102
|
-
},
|
|
1103
|
-
output_index: outputIndex,
|
|
1104
|
-
sequence_number: nextSeq(extras)
|
|
1105
|
-
};
|
|
1106
|
-
}
|
|
1107
|
-
case "done": {
|
|
1108
|
-
const tokens = readUsage(event);
|
|
1109
|
-
const usage = tokens ? usageFromTokens(tokens) : emptyResponseUsage();
|
|
1110
|
-
return {
|
|
1111
|
-
type: "response.completed",
|
|
1112
|
-
sequence_number: nextSeq(extras),
|
|
1113
|
-
response: {
|
|
1114
|
-
id: ctx.runId,
|
|
1115
|
-
object: "response",
|
|
1116
|
-
created_at: ctx.createdAt,
|
|
1117
|
-
output_text: "",
|
|
1118
|
-
error: null,
|
|
1119
|
-
incomplete_details: null,
|
|
1120
|
-
instructions: null,
|
|
1121
|
-
metadata: null,
|
|
1122
|
-
model: ctx.modelId,
|
|
1123
|
-
output: [],
|
|
1124
|
-
parallel_tool_calls: false,
|
|
1125
|
-
temperature: null,
|
|
1126
|
-
tool_choice: "auto",
|
|
1127
|
-
tools: [],
|
|
1128
|
-
top_p: null,
|
|
1129
|
-
status: "completed",
|
|
1130
|
-
usage
|
|
1131
|
-
}
|
|
1132
|
-
};
|
|
1133
|
-
}
|
|
1213
|
+
case "error":
|
|
1214
|
+
case "done": return terminalResponseEvent(event, ctx, extras);
|
|
1134
1215
|
default: return null;
|
|
1135
1216
|
}
|
|
1136
1217
|
}
|
|
@@ -1705,11 +1786,10 @@ function normalizeResponsesWebSearchForTangleRouter(req, options = {}) {
|
|
|
1705
1786
|
} : req;
|
|
1706
1787
|
}
|
|
1707
1788
|
function readNativeWebSearchParameters(tool) {
|
|
1708
|
-
const input = tool;
|
|
1709
1789
|
const result = {};
|
|
1710
|
-
const searchContextSize =
|
|
1790
|
+
const searchContextSize = ("search_context_size" in tool ? tool.search_context_size : void 0) ?? ("searchContextSize" in tool ? tool.searchContextSize : void 0);
|
|
1711
1791
|
if (searchContextSize === "low" || searchContextSize === "medium" || searchContextSize === "high") result.search_context_size = searchContextSize;
|
|
1712
|
-
const userLocation =
|
|
1792
|
+
const userLocation = ("user_location" in tool ? tool.user_location : void 0) ?? ("userLocation" in tool ? tool.userLocation : void 0);
|
|
1713
1793
|
if (userLocation && typeof userLocation === "object") result.user_location = userLocation;
|
|
1714
1794
|
return result;
|
|
1715
1795
|
}
|
|
@@ -1726,7 +1806,7 @@ function buildResponseShell(args) {
|
|
|
1726
1806
|
object: "response",
|
|
1727
1807
|
created_at: args.createdAt,
|
|
1728
1808
|
output_text: outputText,
|
|
1729
|
-
error: null,
|
|
1809
|
+
error: args.terminal?.error ?? null,
|
|
1730
1810
|
incomplete_details: null,
|
|
1731
1811
|
instructions: args.instructions ?? null,
|
|
1732
1812
|
metadata: null,
|
|
@@ -1737,7 +1817,7 @@ function buildResponseShell(args) {
|
|
|
1737
1817
|
tool_choice: "auto",
|
|
1738
1818
|
tools: [],
|
|
1739
1819
|
top_p: null,
|
|
1740
|
-
status: "completed",
|
|
1820
|
+
status: args.terminal?.status ?? "completed",
|
|
1741
1821
|
usage: args.usage,
|
|
1742
1822
|
previous_response_id: args.previousResponseId ?? null
|
|
1743
1823
|
};
|
|
@@ -1757,4 +1837,4 @@ function createTranslatorContext(init) {
|
|
|
1757
1837
|
};
|
|
1758
1838
|
}
|
|
1759
1839
|
//#endregion
|
|
1760
|
-
export { EmbeddingValidationError, HookChain, InMemoryResponseStore, ResponsesValidationError, Run, actionRateLimitHook, assembleResponseOutput, auditLogHook, buildResponseShell, costCapHook, createTranslatorContext, destructiveActionGuardHook, egressPolicyHook, normalizeResponsesWebSearchForTangleRouter, openaiMessagesToSandboxInput, outcomeToFinishReason, resolvePreviousResponseId, runEvents, sandboxEventToChatChunk, sandboxEventToCompletionChunk, sandboxEventToResponsesEvent, screenshotRedactionHook, usageFromEvents, validateEmbeddingRequest, validateResponsesRequest };
|
|
1840
|
+
export { EmbeddingValidationError, HookChain, InMemoryResponseStore, ResponsesValidationError, Run, actionRateLimitHook, assembleResponseOutput, auditLogHook, buildResponseShell, classifyOpenAITerminal, classifyOpenAITerminalEvents, costCapHook, createTranslatorContext, destructiveActionGuardHook, egressPolicyHook, normalizeResponsesWebSearchForTangleRouter, openaiMessagesToSandboxInput, outcomeToFinishReason, resolvePreviousResponseId, runEvents, sandboxEventToChatChunk, sandboxEventToCompletionChunk, sandboxEventToResponsesEvent, screenshotRedactionHook, usageFromEvents, validateEmbeddingRequest, validateResponsesRequest };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as NetworkError, c as QuotaError, f as TimeoutError, l as SandboxError, m as parseErrorResponse, n as CapabilityError, p as ValidationError, u as ServerError } from "./errors-C6kn-3zt.js";
|
|
2
|
-
import { AgentExactRunControlRefSchema, agentProfileSchema, harnessTypeSchema } from "@tangle-network/agent-interface";
|
|
2
|
+
import { AgentExactRunControlRefSchema, AgentExecutionOutcomeSchema, DurablePlanSchema, InteractionRequestSchema, agentProfileSchema, harnessTypeSchema } from "@tangle-network/agent-interface";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
//#region ../../../packages/runtime-contracts/dist/backend-types.js
|
|
5
5
|
/**
|
|
@@ -57,6 +57,392 @@ function parseBackendType(value) {
|
|
|
57
57
|
return value;
|
|
58
58
|
}
|
|
59
59
|
//#endregion
|
|
60
|
+
//#region src/agent-run-outcome-inspection.ts
|
|
61
|
+
const HUB_APPROVAL_REQUIRED = "HUB_APPROVAL_REQUIRED";
|
|
62
|
+
const MAX_SERIALIZED_TOOL_VALUE_BYTES = 4 * 1024 * 1024;
|
|
63
|
+
function parseInteraction(value) {
|
|
64
|
+
const parsed = InteractionRequestSchema.safeParse(value);
|
|
65
|
+
return parsed.success ? parsed.data : void 0;
|
|
66
|
+
}
|
|
67
|
+
function parsePlan(value) {
|
|
68
|
+
const parsed = DurablePlanSchema.safeParse(value);
|
|
69
|
+
return parsed.success ? parsed.data : void 0;
|
|
70
|
+
}
|
|
71
|
+
function parseToolInvocations(value) {
|
|
72
|
+
if (!Array.isArray(value)) return void 0;
|
|
73
|
+
const result = [];
|
|
74
|
+
for (const entry of value) {
|
|
75
|
+
const tool = plainRecord(entry);
|
|
76
|
+
const toolName = readString(tool?.toolName);
|
|
77
|
+
if (!tool || !toolName || tool.isError !== void 0 && typeof tool.isError !== "boolean") return;
|
|
78
|
+
const invocation = { toolName };
|
|
79
|
+
const input = tool.input ?? tool.arguments;
|
|
80
|
+
if (input !== void 0) {
|
|
81
|
+
const copied = copyJsonValue(input);
|
|
82
|
+
if (!copied.success) return void 0;
|
|
83
|
+
invocation.input = copied.value;
|
|
84
|
+
}
|
|
85
|
+
if (tool.result !== void 0) {
|
|
86
|
+
const copied = copyJsonValue(tool.result);
|
|
87
|
+
if (!copied.success) return void 0;
|
|
88
|
+
invocation.result = copied.value;
|
|
89
|
+
}
|
|
90
|
+
if (typeof tool.isError === "boolean") invocation.isError = tool.isError;
|
|
91
|
+
result.push(invocation);
|
|
92
|
+
}
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
function inspectToolOutcomes(toolInvocations) {
|
|
96
|
+
let approval;
|
|
97
|
+
for (const tool of toolInvocations ?? []) {
|
|
98
|
+
if (tool.isError !== true) continue;
|
|
99
|
+
const inspection = inspectToolResult(tool.result);
|
|
100
|
+
if (!inspection.approvalCode) continue;
|
|
101
|
+
if (approval) return { malformed: true };
|
|
102
|
+
approval = { message: inspection.message ?? "Hub action requires approval" };
|
|
103
|
+
if (inspection.approvalId) approval.approvalId = inspection.approvalId;
|
|
104
|
+
if (inspection.connectionId) approval.connectionId = inspection.connectionId;
|
|
105
|
+
}
|
|
106
|
+
const result = { malformed: false };
|
|
107
|
+
if (approval) result.approval = approval;
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
function inspectToolResult(value) {
|
|
111
|
+
let root = value;
|
|
112
|
+
if (typeof value === "string") try {
|
|
113
|
+
root = JSON.parse(value);
|
|
114
|
+
} catch {
|
|
115
|
+
return { approvalCode: false };
|
|
116
|
+
}
|
|
117
|
+
const record = plainRecord(root);
|
|
118
|
+
if (!record) return { approvalCode: false };
|
|
119
|
+
const structuredContent = plainRecord(record.structuredContent);
|
|
120
|
+
const envelope = record.code === HUB_APPROVAL_REQUIRED ? record : structuredContent?.code === HUB_APPROVAL_REQUIRED ? structuredContent : void 0;
|
|
121
|
+
if (!envelope) return { approvalCode: false };
|
|
122
|
+
const approval = plainRecord(plainRecord(envelope.details)?.approval) ?? plainRecord(envelope.approval);
|
|
123
|
+
const result = { approvalCode: true };
|
|
124
|
+
const message = readString(envelope.message);
|
|
125
|
+
const approvalId = readString(approval?.id);
|
|
126
|
+
const connectionId = readString(approval?.connectionId);
|
|
127
|
+
if (message) result.message = message;
|
|
128
|
+
if (approvalId) result.approvalId = approvalId;
|
|
129
|
+
if (connectionId) result.connectionId = connectionId;
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
/** Copy a provider value into a bounded JSON-only value safe for public output. */
|
|
133
|
+
function copyJsonValue(value) {
|
|
134
|
+
try {
|
|
135
|
+
const serialized = JSON.stringify(value);
|
|
136
|
+
if (serialized === void 0 || new TextEncoder().encode(serialized).byteLength > MAX_SERIALIZED_TOOL_VALUE_BYTES) return { success: false };
|
|
137
|
+
return {
|
|
138
|
+
success: true,
|
|
139
|
+
value: JSON.parse(serialized)
|
|
140
|
+
};
|
|
141
|
+
} catch {
|
|
142
|
+
return { success: false };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function questionFromInteraction(request) {
|
|
146
|
+
return {
|
|
147
|
+
questionId: request.id,
|
|
148
|
+
questions: request.answerSpec.fields.map((field) => field.type === "select" ? {
|
|
149
|
+
question: field.label,
|
|
150
|
+
options: field.options.map((option) => {
|
|
151
|
+
const value = { label: option.label };
|
|
152
|
+
if (option.description) value.description = option.description;
|
|
153
|
+
return value;
|
|
154
|
+
}),
|
|
155
|
+
multiSelect: field.multi === true
|
|
156
|
+
} : { question: field.label })
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function normalizeStatus(value) {
|
|
160
|
+
return readString(value)?.toLowerCase().replaceAll("-", "_");
|
|
161
|
+
}
|
|
162
|
+
function readString(value) {
|
|
163
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
164
|
+
}
|
|
165
|
+
function readError(value) {
|
|
166
|
+
if (!value) return void 0;
|
|
167
|
+
const direct = readString(value.error);
|
|
168
|
+
if (direct) return direct;
|
|
169
|
+
return readString(plainRecord(value.error)?.message) ?? readString(value.message);
|
|
170
|
+
}
|
|
171
|
+
function plainRecord(value) {
|
|
172
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
173
|
+
}
|
|
174
|
+
//#endregion
|
|
175
|
+
//#region src/agent-run-outcome.ts
|
|
176
|
+
const FAILURE_STATUSES = new Set([
|
|
177
|
+
"canceled",
|
|
178
|
+
"cancelled",
|
|
179
|
+
"error",
|
|
180
|
+
"errored",
|
|
181
|
+
"failed",
|
|
182
|
+
"failure",
|
|
183
|
+
"timed_out",
|
|
184
|
+
"timeout"
|
|
185
|
+
]);
|
|
186
|
+
const SUCCESS_STATUSES = new Set([
|
|
187
|
+
"completed",
|
|
188
|
+
"done",
|
|
189
|
+
"ok",
|
|
190
|
+
"success"
|
|
191
|
+
]);
|
|
192
|
+
const WAIT_STATUSES = new Map([
|
|
193
|
+
["awaiting_interaction", "interaction"],
|
|
194
|
+
["awaiting_plan_decision", "plan"],
|
|
195
|
+
["awaiting_question", "question"],
|
|
196
|
+
["blocked_on_approval", "approval"]
|
|
197
|
+
]);
|
|
198
|
+
/** Track one run and settle its outcome only after its event stream ends. */
|
|
199
|
+
function createAgentRunOutcomeTracker() {
|
|
200
|
+
const state = createOutcomeState();
|
|
201
|
+
let result;
|
|
202
|
+
return {
|
|
203
|
+
observe(event) {
|
|
204
|
+
if (result) throw new Error("Agent run outcome tracker is already finished");
|
|
205
|
+
observeEvent(state, event);
|
|
206
|
+
},
|
|
207
|
+
finish() {
|
|
208
|
+
result ??= settleOutcome(state);
|
|
209
|
+
return result;
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
/** Settle a complete in-memory event sequence with the streaming tracker. */
|
|
214
|
+
function deriveAgentRunOutcome(events) {
|
|
215
|
+
const tracker = createAgentRunOutcomeTracker();
|
|
216
|
+
for (const event of events) tracker.observe(event);
|
|
217
|
+
return tracker.finish();
|
|
218
|
+
}
|
|
219
|
+
/** Settle one completed result record without treating live result frames as terminal. */
|
|
220
|
+
function deriveAgentResultOutcome(result) {
|
|
221
|
+
const state = createOutcomeState();
|
|
222
|
+
observeOutcomeFrame(state, "result", result, true);
|
|
223
|
+
return settleOutcome(state);
|
|
224
|
+
}
|
|
225
|
+
function createOutcomeState() {
|
|
226
|
+
return {
|
|
227
|
+
terminalReached: false,
|
|
228
|
+
terminalStatuses: /* @__PURE__ */ new Set(),
|
|
229
|
+
malformed: false
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
function observeEvent(state, event) {
|
|
233
|
+
try {
|
|
234
|
+
if (!event || typeof event !== "object") {
|
|
235
|
+
state.malformed = true;
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const type = typeof event.type === "string" ? event.type : "";
|
|
239
|
+
const data = plainRecord(event.data);
|
|
240
|
+
if (!type || !data) {
|
|
241
|
+
state.malformed = true;
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
if (type === "error") {
|
|
245
|
+
state.runError ??= readError(data) ?? "Agent execution failed";
|
|
246
|
+
state.runErrorCode ??= readString(data.code);
|
|
247
|
+
}
|
|
248
|
+
if (type === "interaction") observeInteraction(state, data);
|
|
249
|
+
if (type === "interaction.cancel") observeInteractionCancel(state, data);
|
|
250
|
+
if (type === "plan.submitted") observePlan(state, data);
|
|
251
|
+
if (type === "result") observeOutcomeFrame(state, type, data, false);
|
|
252
|
+
if (type === "done") observeOutcomeFrame(state, type, data, true);
|
|
253
|
+
} catch {
|
|
254
|
+
state.malformed = true;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function observeInteraction(state, data) {
|
|
258
|
+
const request = parseInteraction(data.request);
|
|
259
|
+
if (!request) {
|
|
260
|
+
state.malformed = true;
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
retainInteraction(state, request);
|
|
264
|
+
}
|
|
265
|
+
function observeInteractionCancel(state, data) {
|
|
266
|
+
const id = readString(data.id);
|
|
267
|
+
if (!id) {
|
|
268
|
+
state.malformed = true;
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
if (state.interaction?.id === id) state.interaction = void 0;
|
|
272
|
+
}
|
|
273
|
+
function observePlan(state, data) {
|
|
274
|
+
const plan = parsePlan(data.plan);
|
|
275
|
+
if (!plan) {
|
|
276
|
+
state.malformed = true;
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
retainPlan(state, plan);
|
|
280
|
+
}
|
|
281
|
+
function observeOutcomeFrame(state, type, data, terminal) {
|
|
282
|
+
if (terminal) state.terminalReached = true;
|
|
283
|
+
if (data.success !== void 0 && typeof data.success !== "boolean") state.malformed = true;
|
|
284
|
+
if (typeof data.success === "boolean") {
|
|
285
|
+
if (state.terminalSuccess !== void 0 && state.terminalSuccess !== data.success) state.malformed = true;
|
|
286
|
+
state.terminalSuccess = data.success;
|
|
287
|
+
}
|
|
288
|
+
if (type === "done" || data.outcome !== void 0) {
|
|
289
|
+
if (!AgentExecutionOutcomeSchema.safeParse(data.outcome).success) state.malformed = true;
|
|
290
|
+
}
|
|
291
|
+
const outcome = plainRecord(data.outcome);
|
|
292
|
+
const nestedResult = plainRecord(data.result);
|
|
293
|
+
for (const value of [
|
|
294
|
+
data.status,
|
|
295
|
+
outcome?.type,
|
|
296
|
+
outcome?.status,
|
|
297
|
+
nestedResult?.status
|
|
298
|
+
]) {
|
|
299
|
+
if (value === void 0) continue;
|
|
300
|
+
const status = normalizeStatus(value);
|
|
301
|
+
if (status) state.terminalStatuses.add(status);
|
|
302
|
+
else state.malformed = true;
|
|
303
|
+
}
|
|
304
|
+
state.terminalError ??= readError(data) ?? readError(outcome) ?? readError(nestedResult);
|
|
305
|
+
state.terminalErrorCode ??= readString(data.errorCode) ?? readString(data.code) ?? readString(outcome?.errorCode) ?? readString(outcome?.code) ?? readString(nestedResult?.errorCode) ?? readString(nestedResult?.code);
|
|
306
|
+
const interactionCandidate = data.interaction ?? nestedResult?.interaction;
|
|
307
|
+
if (interactionCandidate !== void 0) {
|
|
308
|
+
const interaction = parseInteraction(interactionCandidate);
|
|
309
|
+
if (interaction) retainInteraction(state, interaction);
|
|
310
|
+
else state.malformed = true;
|
|
311
|
+
}
|
|
312
|
+
const planCandidate = data.plan ?? (outcome?.type === "awaiting_plan_decision" ? outcome.plan : void 0) ?? (nestedResult?.status === "awaiting_plan_decision" ? nestedResult.plan : void 0);
|
|
313
|
+
if (planCandidate !== void 0) {
|
|
314
|
+
const plan = parsePlan(planCandidate);
|
|
315
|
+
if (plan) retainPlan(state, plan);
|
|
316
|
+
else state.malformed = true;
|
|
317
|
+
}
|
|
318
|
+
const invocationCandidate = data.toolInvocations ?? nestedResult?.toolInvocations;
|
|
319
|
+
if (invocationCandidate !== void 0) {
|
|
320
|
+
const invocations = parseToolInvocations(invocationCandidate);
|
|
321
|
+
if (invocations) retainToolInvocations(state, invocations);
|
|
322
|
+
else state.malformed = true;
|
|
323
|
+
}
|
|
324
|
+
const toolOutcome = inspectToolOutcomes(state.toolInvocations);
|
|
325
|
+
if (toolOutcome.malformed) state.malformed = true;
|
|
326
|
+
state.approval = toolOutcome.approval;
|
|
327
|
+
}
|
|
328
|
+
function settleOutcome(state) {
|
|
329
|
+
const common = outcomeDetails(state);
|
|
330
|
+
if (state.runError) return failed(state.runError, state.runErrorCode, common);
|
|
331
|
+
const statusGroups = classifyStatuses(state.terminalStatuses);
|
|
332
|
+
if (statusGroups.failure) return failed(state.terminalError ?? `Agent execution ended with status ${statusGroups.failure}`, state.terminalErrorCode, common);
|
|
333
|
+
if (!state.terminalReached) return failed("Agent stream ended without a terminal event", void 0, common);
|
|
334
|
+
if (state.malformed || statusGroups.unknown || statusGroups.conflicting) return failed("Agent terminal outcome is malformed", void 0, common);
|
|
335
|
+
const wait = selectWait(state);
|
|
336
|
+
if (wait.error) return failed(wait.error, void 0, common);
|
|
337
|
+
if (statusGroups.wait && statusGroups.wait !== wait.kind) return failed("Agent terminal wait does not match its response request", void 0, common);
|
|
338
|
+
if (state.terminalSuccess === false && !wait.kind) return failed(state.terminalError ?? "Agent execution failed", state.terminalErrorCode, common);
|
|
339
|
+
if (state.terminalError && !statusGroups.wait) return failed(state.terminalError, state.terminalErrorCode, common);
|
|
340
|
+
if (wait.kind) return waitingOutcome(wait.kind, state, common);
|
|
341
|
+
return {
|
|
342
|
+
success: true,
|
|
343
|
+
status: "success",
|
|
344
|
+
...common
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
function classifyStatuses(statuses) {
|
|
348
|
+
let failure;
|
|
349
|
+
let wait;
|
|
350
|
+
let success = false;
|
|
351
|
+
let unknown = false;
|
|
352
|
+
let conflicting = false;
|
|
353
|
+
for (const status of statuses) if (FAILURE_STATUSES.has(status)) failure ??= status;
|
|
354
|
+
else if (SUCCESS_STATUSES.has(status)) success = true;
|
|
355
|
+
else if (WAIT_STATUSES.has(status)) {
|
|
356
|
+
const next = WAIT_STATUSES.get(status);
|
|
357
|
+
if (!next) continue;
|
|
358
|
+
if (wait && wait !== next) conflicting = true;
|
|
359
|
+
wait = next;
|
|
360
|
+
} else unknown = true;
|
|
361
|
+
if (failure && (wait || success)) conflicting = true;
|
|
362
|
+
return {
|
|
363
|
+
failure,
|
|
364
|
+
wait,
|
|
365
|
+
success,
|
|
366
|
+
unknown,
|
|
367
|
+
conflicting
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
function selectWait(state) {
|
|
371
|
+
const kinds = [];
|
|
372
|
+
if (state.plan) kinds.push("plan");
|
|
373
|
+
if (state.approval) kinds.push("approval");
|
|
374
|
+
if (state.interaction) kinds.push(state.interaction.kind === "question" ? "question" : "interaction");
|
|
375
|
+
if (kinds.length > 1) return { error: "Agent terminal outcome contains conflicting response requests" };
|
|
376
|
+
return { kind: kinds[0] };
|
|
377
|
+
}
|
|
378
|
+
function waitingOutcome(kind, state, common) {
|
|
379
|
+
if (kind === "plan") return {
|
|
380
|
+
success: false,
|
|
381
|
+
status: "awaiting_plan_decision",
|
|
382
|
+
error: "Agent is awaiting a durable plan decision",
|
|
383
|
+
...common
|
|
384
|
+
};
|
|
385
|
+
if (kind === "approval") return {
|
|
386
|
+
success: false,
|
|
387
|
+
status: "blocked_on_approval",
|
|
388
|
+
error: state.approval?.message ?? "Agent is awaiting approval",
|
|
389
|
+
...common
|
|
390
|
+
};
|
|
391
|
+
if (kind === "question") return {
|
|
392
|
+
success: false,
|
|
393
|
+
status: "awaiting_question",
|
|
394
|
+
error: "Agent is awaiting an answer",
|
|
395
|
+
...common
|
|
396
|
+
};
|
|
397
|
+
return {
|
|
398
|
+
success: false,
|
|
399
|
+
status: "awaiting_interaction",
|
|
400
|
+
error: "Agent is awaiting a response",
|
|
401
|
+
...common
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function outcomeDetails(state) {
|
|
405
|
+
const question = state.interaction?.kind === "question" ? questionFromInteraction(state.interaction) : void 0;
|
|
406
|
+
const details = {};
|
|
407
|
+
if (state.toolInvocations) details.toolInvocations = state.toolInvocations;
|
|
408
|
+
if (state.approval) details.approval = state.approval;
|
|
409
|
+
if (state.interaction) details.interaction = state.interaction;
|
|
410
|
+
if (question) details.question = question;
|
|
411
|
+
if (state.plan) details.plan = state.plan;
|
|
412
|
+
return details;
|
|
413
|
+
}
|
|
414
|
+
function failed(error, errorCode, common) {
|
|
415
|
+
const outcome = {
|
|
416
|
+
success: false,
|
|
417
|
+
status: "failed",
|
|
418
|
+
error,
|
|
419
|
+
...common
|
|
420
|
+
};
|
|
421
|
+
if (errorCode) outcome.errorCode = errorCode;
|
|
422
|
+
return outcome;
|
|
423
|
+
}
|
|
424
|
+
function retainPlan(state, plan) {
|
|
425
|
+
if (state.plan && (state.plan.id !== plan.id || state.plan.revision !== plan.revision || state.plan.title !== plan.title || state.plan.body !== plan.body || state.plan.submittedAt !== plan.submittedAt)) {
|
|
426
|
+
state.malformed = true;
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
state.plan = plan;
|
|
430
|
+
}
|
|
431
|
+
function retainInteraction(state, interaction) {
|
|
432
|
+
if (state.interaction && state.interaction.requestDigest !== interaction.requestDigest) {
|
|
433
|
+
state.malformed = true;
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
state.interaction = interaction;
|
|
437
|
+
}
|
|
438
|
+
function retainToolInvocations(state, invocations) {
|
|
439
|
+
if (state.toolInvocations && JSON.stringify(state.toolInvocations) !== JSON.stringify(invocations)) {
|
|
440
|
+
state.malformed = true;
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
state.toolInvocations = invocations;
|
|
444
|
+
}
|
|
445
|
+
//#endregion
|
|
60
446
|
//#region src/backend-config.ts
|
|
61
447
|
function assertCurrentBackendType(value, field = "backend.type") {
|
|
62
448
|
const parsed = backendTypeSchema.safeParse(value);
|
|
@@ -825,4 +1211,4 @@ function normalizeSessionInfo(raw) {
|
|
|
825
1211
|
};
|
|
826
1212
|
}
|
|
827
1213
|
//#endregion
|
|
828
|
-
export { uploadChunked as a, combineAbortSignals as c, applyRequestedModel as d, assertCurrentBackendType as f,
|
|
1214
|
+
export { backendTypeSchema as _, uploadChunked as a, combineAbortSignals as c, applyRequestedModel as d, assertCurrentBackendType as f, deriveAgentRunOutcome as g, deriveAgentResultOutcome as h, sessionHeaders as i, encodePromptForWire as l, createAgentRunOutcomeTracker as m, normalizeSessionInfo as n, SANDBOX_PROXY_REQUEST_MAX_BYTES as o, normalizeRuntimeBackendConfig as p, appendSessionIdQuery as r, FILE_DECODED_WRITE_MAX_BYTES as s, SandboxRuntimeApi as t, encodeTextForWire as u, parseBackendType as v };
|