veryfront 0.1.102 → 0.1.104
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/esm/deno.js +1 -1
- package/esm/src/agent/runtime/ai-stream-handler.d.ts +1 -1
- package/esm/src/agent/runtime/ai-stream-handler.d.ts.map +1 -1
- package/esm/src/agent/runtime/ai-stream-handler.js +16 -1
- package/esm/src/agent/runtime/index.d.ts +2 -2
- package/esm/src/agent/runtime/index.d.ts.map +1 -1
- package/esm/src/agent/runtime/index.js +58 -9
- package/esm/src/agent/runtime/sse-utils.d.ts +1 -0
- package/esm/src/agent/runtime/sse-utils.d.ts.map +1 -1
- package/esm/src/agent/runtime/sse-utils.js +15 -0
- package/esm/src/internal-agents/run-stream.d.ts +1 -1
- package/esm/src/internal-agents/run-stream.d.ts.map +1 -1
- package/esm/src/internal-agents/run-stream.js +1 -1
- package/esm/src/internal-agents/runtime-owner.d.ts +17 -0
- package/esm/src/internal-agents/runtime-owner.d.ts.map +1 -0
- package/esm/src/internal-agents/runtime-owner.js +119 -0
- package/esm/src/server/handlers/request/agent-stream.handler.d.ts +2 -0
- package/esm/src/server/handlers/request/agent-stream.handler.d.ts.map +1 -1
- package/esm/src/server/handlers/request/agent-stream.handler.js +17 -1
- package/esm/src/utils/version.d.ts +1 -1
- package/esm/src/utils/version.js +1 -1
- package/package.json +1 -1
- package/src/deno.js +1 -1
- package/src/src/agent/runtime/ai-stream-handler.ts +25 -0
- package/src/src/agent/runtime/index.ts +65 -6
- package/src/src/agent/runtime/sse-utils.ts +17 -0
- package/src/src/internal-agents/run-stream.ts +6 -0
- package/src/src/internal-agents/runtime-owner.ts +189 -0
- package/src/src/server/handlers/request/agent-stream.handler.ts +28 -2
- package/src/src/utils/version.ts +1 -1
package/esm/deno.js
CHANGED
|
@@ -42,5 +42,5 @@ export declare function createStreamState(): AIStreamState;
|
|
|
42
42
|
* - tool-call → tool-input-available SSE (accumulated input)
|
|
43
43
|
* - finish → captures finishReason and usage
|
|
44
44
|
*/
|
|
45
|
-
export declare function processStream(result: StreamTextResult<ToolSet, never>, state: AIStreamState, controller: ReadableStreamDefaultController, encoder: TextEncoder, textPartId: string | undefined, callbacks?: AIStreamCallbacks): Promise<void>;
|
|
45
|
+
export declare function processStream(result: StreamTextResult<ToolSet, never>, state: AIStreamState, controller: ReadableStreamDefaultController, encoder: TextEncoder, textPartId: string | undefined, callbacks?: AIStreamCallbacks, abortSignal?: AbortSignal): Promise<void>;
|
|
46
46
|
//# sourceMappingURL=ai-stream-handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-stream-handler.d.ts","sourceRoot":"","sources":["../../../../src/src/agent/runtime/ai-stream-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAQpD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAC1C,KAAK,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;CAChF;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE;QAChB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,KAAK,IAAI,CAAC;CACZ;
|
|
1
|
+
{"version":3,"file":"ai-stream-handler.d.ts","sourceRoot":"","sources":["../../../../src/src/agent/runtime/ai-stream-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAQpD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAC1C,KAAK,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;CAChF;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE;QAChB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,KAAK,IAAI,CAAC;CACZ;AAmBD,wBAAgB,iBAAiB,IAAI,aAAa,CAOjD;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,EACxC,KAAK,EAAE,aAAa,EACpB,UAAU,EAAE,+BAA+B,EAC3C,OAAO,EAAE,WAAW,EACpB,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,SAAS,CAAC,EAAE,iBAAiB,EAC7B,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,IAAI,CAAC,CAwHf"}
|
|
@@ -12,6 +12,17 @@ import { isDynamicTool } from "./tool-helpers.js";
|
|
|
12
12
|
import { serverLogger } from "../../utils/index.js";
|
|
13
13
|
import { setActiveSpanAttributes, withSpan } from "../../observability/tracing/otlp-setup.js";
|
|
14
14
|
const logger = serverLogger.component("agent");
|
|
15
|
+
function createAbortError(reason) {
|
|
16
|
+
if (reason instanceof Error) {
|
|
17
|
+
return reason;
|
|
18
|
+
}
|
|
19
|
+
return new DOMException(typeof reason === "string" && reason.length > 0 ? reason : "The operation was aborted", "AbortError");
|
|
20
|
+
}
|
|
21
|
+
function throwIfAborted(abortSignal) {
|
|
22
|
+
if (abortSignal?.aborted) {
|
|
23
|
+
throw createAbortError(abortSignal.reason);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
15
26
|
export function createStreamState() {
|
|
16
27
|
return {
|
|
17
28
|
accumulatedText: "",
|
|
@@ -30,10 +41,12 @@ export function createStreamState() {
|
|
|
30
41
|
* - tool-call → tool-input-available SSE (accumulated input)
|
|
31
42
|
* - finish → captures finishReason and usage
|
|
32
43
|
*/
|
|
33
|
-
export function processStream(result, state, controller, encoder, textPartId, callbacks) {
|
|
44
|
+
export function processStream(result, state, controller, encoder, textPartId, callbacks, abortSignal) {
|
|
34
45
|
return withSpan("agent.runtime.processStream", async () => {
|
|
35
46
|
let eventCount = 0;
|
|
47
|
+
throwIfAborted(abortSignal);
|
|
36
48
|
for await (const part of result.fullStream) {
|
|
49
|
+
throwIfAborted(abortSignal);
|
|
37
50
|
eventCount++;
|
|
38
51
|
switch (part.type) {
|
|
39
52
|
case "text-delta": {
|
|
@@ -124,7 +137,9 @@ export function processStream(result, state, controller, encoder, textPartId, ca
|
|
|
124
137
|
// Ignore other stream parts (source, file, reasoning-*, etc.)
|
|
125
138
|
break;
|
|
126
139
|
}
|
|
140
|
+
throwIfAborted(abortSignal);
|
|
127
141
|
}
|
|
142
|
+
throwIfAborted(abortSignal);
|
|
128
143
|
setActiveSpanAttributes({
|
|
129
144
|
"stream.event_count": eventCount,
|
|
130
145
|
"stream.tool_calls": state.toolCalls.size,
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { type AgentConfig, type AgentResponse, type Message, type ToolCall } from "../types.js";
|
|
14
14
|
import { type Memory } from "../memory/index.js";
|
|
15
|
-
export { generateMessageId, sendSSE } from "./sse-utils.js";
|
|
15
|
+
export { closeSSEStream, generateMessageId, sendSSE } from "./sse-utils.js";
|
|
16
16
|
export { executeConfiguredTool, getAvailableTools, isDynamicTool, parseToolArgs, } from "./tool-helpers.js";
|
|
17
17
|
export type { ParsedToolArgs, ToolConfigEntry } from "./tool-helpers.js";
|
|
18
18
|
export { accumulateUsage, getMaxSteps, normalizeInput } from "./input-utils.js";
|
|
@@ -55,7 +55,7 @@ export declare class AgentRuntime {
|
|
|
55
55
|
onToolCall?: (toolCall: ToolCall) => void;
|
|
56
56
|
onChunk?: (chunk: string) => void;
|
|
57
57
|
onFinish?: (response: AgentResponse) => void;
|
|
58
|
-
}, modelOverride?: string, maxOutputTokensOverride?: number): Promise<ReadableStream<Uint8Array>>;
|
|
58
|
+
}, modelOverride?: string, maxOutputTokensOverride?: number, abortSignal?: AbortSignal): Promise<ReadableStream<Uint8Array>>;
|
|
59
59
|
/**
|
|
60
60
|
* Execute agent loop (with tool calling)
|
|
61
61
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/src/agent/runtime/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EACL,KAAK,WAAW,EAEhB,KAAK,aAAa,EAGlB,KAAK,OAAO,EAEZ,KAAK,QAAQ,EACd,MAAM,aAAa,CAAC;AAIrB,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAe/D,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/src/agent/runtime/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EACL,KAAK,WAAW,EAEhB,KAAK,aAAa,EAGlB,KAAK,OAAO,EAEZ,KAAK,QAAQ,EACd,MAAM,aAAa,CAAC;AAIrB,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAe/D,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC5E,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,aAAa,EACb,aAAa,GACd,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAChF,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC1E,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAClG,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,gBAAgB,CAAC;AAmDxB;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,GAAG,SAAS,CA6BxE;AAED,gEAAgE;AAChE,KAAK,iBAAiB,GAClB;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GACjB;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtC;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,iBAAiB,EAAE,MAAM,EAAE,GAAG,SAAS,EACvC,kBAAkB,EAAE,OAAO,GAC1B,iBAAiB,CAiBnB;AAkCD,qBAAa,YAAY;IACvB,OAAO,CAAC,EAAE,CAAS;IACnB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,MAAM,CAAuB;gBAEzB,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW;IAS3C;;OAEG;IACG,QAAQ,CACZ,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE,EACzB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,aAAa,CAAC,EAAE,MAAM,EACtB,uBAAuB,CAAC,EAAE,MAAM,GAC/B,OAAO,CAAC,aAAa,CAAC;IAsCzB;;;OAGG;IACG,MAAM,CACV,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,SAAS,CAAC,EAAE;QACV,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;QAC1C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;QAClC,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,IAAI,CAAC;KAC9C,EACD,aAAa,CAAC,EAAE,MAAM,EACtB,uBAAuB,CAAC,EAAE,MAAM,EAChC,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;IA4GtC;;OAEG;YACW,gBAAgB;IAkO9B;;;;OAIG;YACW,yBAAyB;IA2OvC;;OAEG;YACW,eAAe;IAqC7B;;OAEG;YACW,mBAAmB;IAOjC;;OAEG;IACH,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,sBAAsB;IAY9B;;OAEG;IACH,SAAS,IAAI,MAAM,CAAC,OAAO,CAAC;IAI5B;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC;QAC9B,aAAa,EAAE,MAAM,CAAC;QACtB,eAAe,EAAE,MAAM,CAAC;QACxB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IAIF;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;CAGnC"}
|
|
@@ -24,19 +24,36 @@ import { MiddlewareChain } from "../middleware/chain.js";
|
|
|
24
24
|
import { generateText, streamText } from "ai";
|
|
25
25
|
import { AGENT_DEFAULTS } from "../ai-defaults.js";
|
|
26
26
|
// Re-export from submodules
|
|
27
|
-
export { generateMessageId, sendSSE } from "./sse-utils.js";
|
|
27
|
+
export { closeSSEStream, generateMessageId, sendSSE } from "./sse-utils.js";
|
|
28
28
|
export { executeConfiguredTool, getAvailableTools, isDynamicTool, parseToolArgs, } from "./tool-helpers.js";
|
|
29
29
|
export { accumulateUsage, getMaxSteps, normalizeInput } from "./input-utils.js";
|
|
30
30
|
export { createStreamState, processStream } from "./ai-stream-handler.js";
|
|
31
31
|
export { DEFAULT_MAX_STEPS, DEFAULT_MAX_TOKENS, DEFAULT_TEMPERATURE, MAX_STREAM_BUFFER_SIZE, } from "./constants.js";
|
|
32
32
|
import { DEFAULT_MAX_TOKENS, DEFAULT_TEMPERATURE } from "./constants.js";
|
|
33
|
-
import { generateMessageId, sendSSE } from "./sse-utils.js";
|
|
33
|
+
import { closeSSEStream, generateMessageId, sendSSE } from "./sse-utils.js";
|
|
34
34
|
import { executeConfiguredTool, getAvailableTools, isDynamicTool, parseToolArgs, } from "./tool-helpers.js";
|
|
35
35
|
import { accumulateUsage, getMaxSteps, normalizeInput } from "./input-utils.js";
|
|
36
36
|
import { filterToolsForSkill, isToolAllowedBySkill, validateAllowedToolPatterns, } from "../../skill/allowed-tools.js";
|
|
37
37
|
import { resolveConfiguredAgentModel } from "./model-resolution.js";
|
|
38
38
|
const logger = serverLogger.component("agent");
|
|
39
39
|
const LOAD_SKILL_TOOL_ID = "load-skill";
|
|
40
|
+
function createAbortError(reason) {
|
|
41
|
+
if (reason instanceof Error) {
|
|
42
|
+
return reason;
|
|
43
|
+
}
|
|
44
|
+
return new DOMException(typeof reason === "string" && reason.length > 0 ? reason : "The operation was aborted", "AbortError");
|
|
45
|
+
}
|
|
46
|
+
function throwIfAborted(abortSignal) {
|
|
47
|
+
if (abortSignal?.aborted) {
|
|
48
|
+
throw createAbortError(abortSignal.reason);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function isAbortError(error, abortSignal) {
|
|
52
|
+
if (abortSignal?.aborted && error === abortSignal.reason) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
return error instanceof DOMException && error.name === "AbortError";
|
|
56
|
+
}
|
|
40
57
|
function getSkillActivationRequiredError(toolName) {
|
|
41
58
|
return `Tool "${toolName}" cannot run before load-skill succeeds in the same step. ` +
|
|
42
59
|
`Call "${LOAD_SKILL_TOOL_ID}" first to establish the active skill context.`;
|
|
@@ -159,7 +176,7 @@ export class AgentRuntime {
|
|
|
159
176
|
* Stream a response
|
|
160
177
|
* Returns a ReadableStream in the veryfront stream event format.
|
|
161
178
|
*/
|
|
162
|
-
async stream(messages, context, callbacks, modelOverride, maxOutputTokensOverride) {
|
|
179
|
+
async stream(messages, context, callbacks, modelOverride, maxOutputTokensOverride, abortSignal) {
|
|
163
180
|
const requestedModel = resolveConfiguredAgentModel(modelOverride || this.config.model);
|
|
164
181
|
// Auto-upgrade local/* to a cloud provider when API keys are available.
|
|
165
182
|
const resolvedModelString = maybeUpgradeLocalModel(requestedModel);
|
|
@@ -168,7 +185,20 @@ export class AgentRuntime {
|
|
|
168
185
|
const memoryMessages = await this.memory.getMessages();
|
|
169
186
|
const systemPrompt = await this.resolveSystemPrompt();
|
|
170
187
|
const encoder = new TextEncoder();
|
|
171
|
-
const
|
|
188
|
+
const streamAbortController = new AbortController();
|
|
189
|
+
const forwardAbort = () => {
|
|
190
|
+
streamAbortController.abort(abortSignal?.reason);
|
|
191
|
+
};
|
|
192
|
+
if (abortSignal) {
|
|
193
|
+
if (abortSignal.aborted) {
|
|
194
|
+
streamAbortController.abort(abortSignal.reason);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
abortSignal.addEventListener("abort", forwardAbort, { once: true });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const streamAbortSignal = streamAbortController.signal;
|
|
201
|
+
const toolContext = { agentId: this.id, abortSignal: streamAbortSignal, ...context };
|
|
172
202
|
const textPartId = generateId("text");
|
|
173
203
|
// Resolve model BEFORE creating the ReadableStream — if this throws
|
|
174
204
|
// (e.g., no_ai_available), the error propagates to the caller who can
|
|
@@ -186,6 +216,7 @@ export class AgentRuntime {
|
|
|
186
216
|
return new ReadableStream({
|
|
187
217
|
start: async (controller) => {
|
|
188
218
|
try {
|
|
219
|
+
throwIfAborted(streamAbortSignal);
|
|
189
220
|
this.status = "streaming";
|
|
190
221
|
const messageId = generateMessageId();
|
|
191
222
|
sendSSE(controller, encoder, { type: "message-start", messageId });
|
|
@@ -203,21 +234,33 @@ export class AgentRuntime {
|
|
|
203
234
|
},
|
|
204
235
|
});
|
|
205
236
|
sendSSE(controller, encoder, { type: "text-start", id: textPartId });
|
|
206
|
-
const response = await this.executeAgentLoopStreaming(systemPrompt, memoryMessages, controller, encoder, callbacks, textPartId, toolContext, resolvedModelString, languageModel, maxOutputTokensOverride);
|
|
237
|
+
const response = await this.executeAgentLoopStreaming(systemPrompt, memoryMessages, controller, encoder, callbacks, textPartId, toolContext, resolvedModelString, languageModel, maxOutputTokensOverride, streamAbortSignal);
|
|
238
|
+
throwIfAborted(streamAbortSignal);
|
|
207
239
|
callbacks?.onFinish?.(response);
|
|
240
|
+
throwIfAborted(streamAbortSignal);
|
|
208
241
|
sendSSE(controller, encoder, { type: "text-end", id: textPartId });
|
|
209
242
|
sendSSE(controller, encoder, { type: "message-finish" });
|
|
210
|
-
controller
|
|
243
|
+
closeSSEStream(controller);
|
|
211
244
|
}
|
|
212
245
|
catch (error) {
|
|
246
|
+
if (isAbortError(error, streamAbortSignal)) {
|
|
247
|
+
closeSSEStream(controller);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
213
250
|
this.status = "error";
|
|
214
251
|
logger.error("Agent stream error", { error });
|
|
215
252
|
sendSSE(controller, encoder, {
|
|
216
253
|
type: "error",
|
|
217
254
|
error: "An internal error occurred",
|
|
218
255
|
});
|
|
219
|
-
controller
|
|
256
|
+
closeSSEStream(controller);
|
|
220
257
|
}
|
|
258
|
+
finally {
|
|
259
|
+
abortSignal?.removeEventListener("abort", forwardAbort);
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
cancel(reason) {
|
|
263
|
+
streamAbortController.abort(reason);
|
|
221
264
|
},
|
|
222
265
|
});
|
|
223
266
|
}
|
|
@@ -414,7 +457,7 @@ export class AgentRuntime {
|
|
|
414
457
|
* Emits veryfront stream events (message-start/message-finish + step-start/step-end)
|
|
415
458
|
* while consuming AI SDK `streamText()` parts internally.
|
|
416
459
|
*/
|
|
417
|
-
async executeAgentLoopStreaming(systemPrompt, messages, controller, encoder, callbacks, textPartId, toolContext, modelString, resolvedModel, maxOutputTokensOverride) {
|
|
460
|
+
async executeAgentLoopStreaming(systemPrompt, messages, controller, encoder, callbacks, textPartId, toolContext, modelString, resolvedModel, maxOutputTokensOverride, abortSignal) {
|
|
418
461
|
const { maxAgentSteps } = getPlatformCapabilities();
|
|
419
462
|
const maxSteps = this.computeMaxSteps(maxAgentSteps);
|
|
420
463
|
const effectiveModel = resolveConfiguredAgentModel(modelString || this.config.model);
|
|
@@ -434,6 +477,7 @@ export class AgentRuntime {
|
|
|
434
477
|
let activeSkillPolicy;
|
|
435
478
|
let finalFinishReason;
|
|
436
479
|
for (let step = 0; step < maxSteps; step++) {
|
|
480
|
+
throwIfAborted(abortSignal);
|
|
437
481
|
sendSSE(controller, encoder, { type: "step-start" });
|
|
438
482
|
let tools = isLocalStreaming ? [] : getAvailableTools(this.config.tools, {
|
|
439
483
|
includeSkillTools: Boolean(this.config.skills),
|
|
@@ -449,12 +493,14 @@ export class AgentRuntime {
|
|
|
449
493
|
tools: convertToolsToAISDK(tools),
|
|
450
494
|
maxOutputTokens: this.resolveMaxOutputTokens(maxOutputTokensOverride),
|
|
451
495
|
temperature: DEFAULT_TEMPERATURE,
|
|
496
|
+
abortSignal,
|
|
452
497
|
});
|
|
453
498
|
const state = createStreamState();
|
|
454
499
|
await processStream(result, state, controller, encoder, textPartId, {
|
|
455
500
|
onChunk: callbacks?.onChunk,
|
|
456
501
|
onUsage: (usage) => accumulateUsage(totalUsage, usage),
|
|
457
|
-
});
|
|
502
|
+
}, abortSignal);
|
|
503
|
+
throwIfAborted(abortSignal);
|
|
458
504
|
finalFinishReason = state.finishReason ?? finalFinishReason;
|
|
459
505
|
const streamParts = [];
|
|
460
506
|
if (state.accumulatedText)
|
|
@@ -492,6 +538,7 @@ export class AgentRuntime {
|
|
|
492
538
|
Boolean(this.config.skills) &&
|
|
493
539
|
streamedToolCalls.some((tc) => tc.name === LOAD_SKILL_TOOL_ID);
|
|
494
540
|
for (const tc of streamedToolCalls) {
|
|
541
|
+
throwIfAborted(abortSignal);
|
|
495
542
|
const { args, error: argError } = parseToolArgs(tc.arguments);
|
|
496
543
|
const toolCall = { id: tc.id, name: tc.name, args, status: "pending" };
|
|
497
544
|
if (argError) {
|
|
@@ -523,6 +570,7 @@ export class AgentRuntime {
|
|
|
523
570
|
toolCallId: tc.id,
|
|
524
571
|
...toolContext,
|
|
525
572
|
});
|
|
573
|
+
throwIfAborted(abortSignal);
|
|
526
574
|
toolCall.status = "completed";
|
|
527
575
|
toolCall.result = result;
|
|
528
576
|
toolCall.executionTime = Date.now() - startTime;
|
|
@@ -560,6 +608,7 @@ export class AgentRuntime {
|
|
|
560
608
|
await this.recordToolError(toolCall, errorStr, controller, encoder, currentMessages, toolCalls);
|
|
561
609
|
}
|
|
562
610
|
}
|
|
611
|
+
throwIfAborted(abortSignal);
|
|
563
612
|
sendSSE(controller, encoder, { type: "step-end" });
|
|
564
613
|
this.status = "thinking";
|
|
565
614
|
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* Formats event as: data: {json}\n\n
|
|
11
11
|
*/
|
|
12
12
|
export declare function sendSSE(controller: ReadableStreamDefaultController, encoder: TextEncoder, event: Record<string, unknown>): void;
|
|
13
|
+
export declare function closeSSEStream(controller: ReadableStreamDefaultController): void;
|
|
13
14
|
/**
|
|
14
15
|
* Generate a unique message ID for streaming.
|
|
15
16
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse-utils.d.ts","sourceRoot":"","sources":["../../../../src/src/agent/runtime/sse-utils.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"sse-utils.d.ts","sourceRoot":"","sources":["../../../../src/src/agent/runtime/sse-utils.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH;;;GAGG;AACH,wBAAgB,OAAO,CACrB,UAAU,EAAE,+BAA+B,EAC3C,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,IAAI,CAEN;AAED,wBAAgB,cAAc,CAAC,UAAU,EAAE,+BAA+B,GAAG,IAAI,CAUhF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C"}
|
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @module ai/agent/runtime/sse-utils
|
|
7
7
|
*/
|
|
8
|
+
function isClosedStreamControllerError(error) {
|
|
9
|
+
return error instanceof TypeError &&
|
|
10
|
+
error.message.includes("The stream controller cannot close or enqueue");
|
|
11
|
+
}
|
|
8
12
|
/**
|
|
9
13
|
* Encode and enqueue a Server-Sent Event (SSE) to the stream controller.
|
|
10
14
|
* Formats event as: data: {json}\n\n
|
|
@@ -12,6 +16,17 @@
|
|
|
12
16
|
export function sendSSE(controller, encoder, event) {
|
|
13
17
|
controller.enqueue(encoder.encode(`data: ${JSON.stringify(event)}\n\n`));
|
|
14
18
|
}
|
|
19
|
+
export function closeSSEStream(controller) {
|
|
20
|
+
try {
|
|
21
|
+
controller.close();
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
if (isClosedStreamControllerError(error)) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
15
30
|
/**
|
|
16
31
|
* Generate a unique message ID for streaming.
|
|
17
32
|
*/
|
|
@@ -7,7 +7,7 @@ export interface RuntimeAgentStreamExecutionDeps {
|
|
|
7
7
|
createRuntime?: (agent: Agent, mergedTools: Agent["config"]["tools"]) => {
|
|
8
8
|
stream: (messages: Message[], context?: Record<string, unknown>, callbacks?: {
|
|
9
9
|
onFinish?: (response: AgentResponse) => void;
|
|
10
|
-
}) => Promise<ReadableStream<Uint8Array>>;
|
|
10
|
+
}, modelOverride?: string, maxOutputTokensOverride?: number, abortSignal?: AbortSignal) => Promise<ReadableStream<Uint8Array>>;
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
13
|
export declare function createRuntimeAgentStreamResponse(input: RuntimeRunAgentInput, agent: Agent, deps: RuntimeAgentStreamExecutionDeps): Promise<dntShim.Response>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-stream.d.ts","sourceRoot":"","sources":["../../../src/src/internal-agents/run-stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EACL,KAAK,KAAK,EACV,KAAK,YAAY,IAAI,OAAO,EAC5B,KAAK,aAAa,EAEnB,MAAM,mBAAmB,CAAC;AAW3B,OAAO,EAA0B,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAIxD,MAAM,WAAW,+BAA+B;IAC9C,cAAc,EAAE,sBAAsB,CAAC;IACvC,aAAa,CAAC,EAAE,CACd,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,KAClC;QACH,MAAM,EAAE,CACN,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,SAAS,CAAC,EAAE;YACV,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,IAAI,CAAC;SAC9C,
|
|
1
|
+
{"version":3,"file":"run-stream.d.ts","sourceRoot":"","sources":["../../../src/src/internal-agents/run-stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EACL,KAAK,KAAK,EACV,KAAK,YAAY,IAAI,OAAO,EAC5B,KAAK,aAAa,EAEnB,MAAM,mBAAmB,CAAC;AAW3B,OAAO,EAA0B,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAIxD,MAAM,WAAW,+BAA+B;IAC9C,cAAc,EAAE,sBAAsB,CAAC;IACvC,aAAa,CAAC,EAAE,CACd,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,KAClC;QACH,MAAM,EAAE,CACN,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,SAAS,CAAC,EAAE;YACV,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,IAAI,CAAC;SAC9C,EACD,aAAa,CAAC,EAAE,MAAM,EACtB,uBAAuB,CAAC,EAAE,MAAM,EAChC,WAAW,CAAC,EAAE,WAAW,KACtB,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;KAC1C,CAAC;CACH;AAsKD,wBAAsB,gCAAgC,CACpD,KAAK,EAAE,oBAAoB,EAC3B,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,+BAA+B,GACpC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAsJ3B"}
|
|
@@ -150,7 +150,7 @@ export async function createRuntimeAgentStreamResponse(input, agent, deps) {
|
|
|
150
150
|
onFinish: (response) => {
|
|
151
151
|
completedResponse = response;
|
|
152
152
|
},
|
|
153
|
-
});
|
|
153
|
+
}, undefined, undefined, abortSignal);
|
|
154
154
|
}
|
|
155
155
|
catch (error) {
|
|
156
156
|
deps.sessionManager.failRun(input.runId);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as dntShim from "../../_dnt.shims.js";
|
|
2
|
+
import { dynamicImport } from "../platform/compat/dynamic-import.js";
|
|
3
|
+
import { getHostEnv } from "../platform/compat/process.js";
|
|
4
|
+
export declare const RUNTIME_OWNER_INVOKE_URL_HEADER = "x-veryfront-runtime-owner-invoke-url";
|
|
5
|
+
type DenoNetworkInterface = {
|
|
6
|
+
address: string;
|
|
7
|
+
family?: string | number;
|
|
8
|
+
internal?: boolean;
|
|
9
|
+
};
|
|
10
|
+
interface RuntimeOwnerResolverDeps {
|
|
11
|
+
getHostEnv?: typeof getHostEnv;
|
|
12
|
+
dynamicImport?: typeof dynamicImport;
|
|
13
|
+
getDenoNetworkInterfaces?: () => ReadonlyArray<DenoNetworkInterface>;
|
|
14
|
+
}
|
|
15
|
+
export declare function resolveRuntimeOwnerInvokeUrl(req: dntShim.Request, deps?: RuntimeOwnerResolverDeps): Promise<string | null>;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=runtime-owner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-owner.d.ts","sourceRoot":"","sources":["../../../src/src/internal-agents/runtime-owner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAI3D,eAAO,MAAM,+BAA+B,yCAAyC,CAAC;AAQtF,KAAK,oBAAoB,GAAG;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAYF,UAAU,wBAAwB;IAChC,UAAU,CAAC,EAAE,OAAO,UAAU,CAAC;IAC/B,aAAa,CAAC,EAAE,OAAO,aAAa,CAAC;IACrC,wBAAwB,CAAC,EAAE,MAAM,aAAa,CAAC,oBAAoB,CAAC,CAAC;CACtE;AAgJD,wBAAsB,4BAA4B,CAChD,GAAG,EAAE,OAAO,CAAC,OAAO,EACpB,IAAI,GAAE,wBAA6B,GAClC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOxB"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import * as dntShim from "../../_dnt.shims.js";
|
|
2
|
+
import { dynamicImport } from "../platform/compat/dynamic-import.js";
|
|
3
|
+
import { getHostEnv } from "../platform/compat/process.js";
|
|
4
|
+
const DEFAULT_PROXY_RUNTIME_PORT = "20000";
|
|
5
|
+
export const RUNTIME_OWNER_INVOKE_URL_HEADER = "x-veryfront-runtime-owner-invoke-url";
|
|
6
|
+
function parsePort(value) {
|
|
7
|
+
if (!value) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
const trimmed = value.trim();
|
|
11
|
+
if (!/^\d+$/.test(trimmed)) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
const port = Number(trimmed);
|
|
15
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
return String(port);
|
|
19
|
+
}
|
|
20
|
+
function isTruthyEnv(value) {
|
|
21
|
+
if (!value) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const normalized = value.trim().toLowerCase();
|
|
25
|
+
return normalized === "1" || normalized === "true" || normalized === "yes";
|
|
26
|
+
}
|
|
27
|
+
function isIpv4Address(value) {
|
|
28
|
+
if (!value) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
const parts = value.split(".");
|
|
32
|
+
if (parts.length !== 4) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return parts.every((part) => {
|
|
36
|
+
if (!/^\d+$/.test(part)) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
const octet = Number(part);
|
|
40
|
+
return octet >= 0 && octet <= 255;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function isLoopbackIpv4(address) {
|
|
44
|
+
return address.startsWith("127.");
|
|
45
|
+
}
|
|
46
|
+
function selectNetworkIpv4Address(candidates) {
|
|
47
|
+
for (const candidate of candidates) {
|
|
48
|
+
if (candidate.internal === true ||
|
|
49
|
+
!isIpv4Address(candidate.address) ||
|
|
50
|
+
isLoopbackIpv4(candidate.address)) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
return candidate.address;
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
function getDenoNetworkInterfacesSafe(deps) {
|
|
58
|
+
try {
|
|
59
|
+
return deps.getDenoNetworkInterfaces?.() ??
|
|
60
|
+
(dntShim.dntGlobalThis.Deno?.networkInterfaces?.() ?? []);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function detectRuntimeOwnerHost(deps) {
|
|
67
|
+
const readHostEnv = deps.getHostEnv ?? getHostEnv;
|
|
68
|
+
const explicitHost = readHostEnv("VERYFRONT_RUNTIME_OWNER_HOST") ??
|
|
69
|
+
readHostEnv("POD_IP") ??
|
|
70
|
+
null;
|
|
71
|
+
if (explicitHost?.trim()) {
|
|
72
|
+
return explicitHost.trim();
|
|
73
|
+
}
|
|
74
|
+
const denoInterfaces = getDenoNetworkInterfacesSafe(deps);
|
|
75
|
+
const denoHost = selectNetworkIpv4Address(denoInterfaces);
|
|
76
|
+
if (denoHost) {
|
|
77
|
+
return denoHost;
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const nodeOs = await (deps.dynamicImport ?? dynamicImport)("node:os");
|
|
81
|
+
const nodeInterfaces = Object.values(nodeOs.networkInterfaces()).flatMap((entries) => entries ?? []);
|
|
82
|
+
const nodeHost = selectNetworkIpv4Address(nodeInterfaces);
|
|
83
|
+
if (nodeHost) {
|
|
84
|
+
return nodeHost;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// Ignore runtime-specific interface lookup failures and fall through.
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
function resolveRuntimeOwnerPort(req, deps) {
|
|
93
|
+
const readHostEnv = deps.getHostEnv ?? getHostEnv;
|
|
94
|
+
return (parsePort(readHostEnv("VERYFRONT_RUNTIME_OWNER_PORT")) ??
|
|
95
|
+
parsePort(readHostEnv("VERYFRONT_SERVER_PORT")) ??
|
|
96
|
+
parsePort(new URL(req.url).port) ??
|
|
97
|
+
parsePort(readHostEnv("PORT")) ??
|
|
98
|
+
(isTruthyEnv(readHostEnv("PROXY_MODE")) ? DEFAULT_PROXY_RUNTIME_PORT : null));
|
|
99
|
+
}
|
|
100
|
+
function buildRuntimeOwnerInvokeUrl(host, port) {
|
|
101
|
+
try {
|
|
102
|
+
const url = new URL("http://127.0.0.1/channels/invoke");
|
|
103
|
+
url.hostname = host;
|
|
104
|
+
if (port) {
|
|
105
|
+
url.port = port;
|
|
106
|
+
}
|
|
107
|
+
return url.toString();
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
export async function resolveRuntimeOwnerInvokeUrl(req, deps = {}) {
|
|
114
|
+
const host = await detectRuntimeOwnerHost(deps);
|
|
115
|
+
if (!host) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
return buildRuntimeOwnerInvokeUrl(host, resolveRuntimeOwnerPort(req, deps));
|
|
119
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import * as dntShim from "../../../../_dnt.shims.js";
|
|
2
2
|
import { type RuntimeAgentDiscoveryDeps } from "../../../channels/control-plane.js";
|
|
3
3
|
import { type RuntimeAgentStreamExecutionDeps } from "../../../internal-agents/run-stream.js";
|
|
4
|
+
import { resolveRuntimeOwnerInvokeUrl } from "../../../internal-agents/runtime-owner.js";
|
|
4
5
|
import { BaseHandler } from "../response/base.js";
|
|
5
6
|
import type { HandlerContext, HandlerMetadata, HandlerResult } from "../types.js";
|
|
6
7
|
export interface AgentStreamHandlerDeps extends RuntimeAgentDiscoveryDeps, RuntimeAgentStreamExecutionDeps {
|
|
8
|
+
resolveRuntimeOwnerInvokeUrl?: typeof resolveRuntimeOwnerInvokeUrl;
|
|
7
9
|
}
|
|
8
10
|
export declare class AgentStreamHandler extends BaseHandler {
|
|
9
11
|
private readonly deps;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-stream.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/request/agent-stream.handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;AAGrD,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AACpF,OAAO,EAEL,KAAK,+BAA+B,EACrC,MAAM,wCAAwC,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-stream.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/request/agent-stream.handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,2BAA2B,CAAC;AAGrD,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AACpF,OAAO,EAEL,KAAK,+BAA+B,EACrC,MAAM,wCAAwC,CAAC;AAChD,OAAO,EACL,4BAA4B,EAE7B,MAAM,2CAA2C,CAAC;AAkBnD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;AAInG,MAAM,WAAW,sBACf,SAAQ,yBAAyB,EAAE,+BAA+B;IAClE,4BAA4B,CAAC,EAAE,OAAO,4BAA4B,CAAC;CACpE;AA2ED,qBAAa,kBAAmB,SAAQ,WAAW;IAOrC,OAAO,CAAC,QAAQ,CAAC,IAAI;IANjC,QAAQ,EAAE,eAAe,CAIvB;gBAE2B,IAAI,GAAE,sBAAoC;IAIvE,OAAO,CAAC,sBAAsB;IAwBxB,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CAmFhF"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as dntShim from "../../../../_dnt.shims.js";
|
|
2
2
|
import { defaultChannelInvokeDeps } from "../../../channels/invoke.js";
|
|
3
3
|
import { createRuntimeAgentStreamResponse, } from "../../../internal-agents/run-stream.js";
|
|
4
|
+
import { resolveRuntimeOwnerInvokeUrl, RUNTIME_OWNER_INVOKE_URL_HEADER, } from "../../../internal-agents/runtime-owner.js";
|
|
4
5
|
import { ControlPlaneRequestError, verifyControlPlaneRequest, } from "../../../internal-agents/control-plane-auth.js";
|
|
5
6
|
import { INTERNAL_AGENT_STREAM_MAX_BODY_BYTES, InternalAgentRequestBodyTooLargeError, readInternalAgentRequestBody, } from "../../../internal-agents/request-body.js";
|
|
6
7
|
import { AgentRunAlreadyExistsError, agentRunSessionManager, } from "../../../internal-agents/session-manager.js";
|
|
@@ -11,6 +12,7 @@ import { getHostEnv } from "../../../platform/compat/process.js";
|
|
|
11
12
|
const defaultDeps = {
|
|
12
13
|
...defaultChannelInvokeDeps,
|
|
13
14
|
sessionManager: agentRunSessionManager,
|
|
15
|
+
resolveRuntimeOwnerInvokeUrl,
|
|
14
16
|
};
|
|
15
17
|
function buildAgentSourceRunOptions(sourceContext) {
|
|
16
18
|
switch (sourceContext.type) {
|
|
@@ -45,6 +47,15 @@ function applyBuilderHeaders(target, source) {
|
|
|
45
47
|
headers,
|
|
46
48
|
});
|
|
47
49
|
}
|
|
50
|
+
function setResponseHeader(target, key, value) {
|
|
51
|
+
const headers = new dntShim.Headers(target.headers);
|
|
52
|
+
headers.set(key, value);
|
|
53
|
+
return new dntShim.Response(target.body, {
|
|
54
|
+
status: target.status,
|
|
55
|
+
statusText: target.statusText,
|
|
56
|
+
headers,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
48
59
|
export class AgentStreamHandler extends BaseHandler {
|
|
49
60
|
deps;
|
|
50
61
|
metadata = {
|
|
@@ -89,7 +100,12 @@ export class AgentStreamHandler extends BaseHandler {
|
|
|
89
100
|
return this.respond(builder.json({ error: "Agent not found" }, 404));
|
|
90
101
|
}
|
|
91
102
|
const response = await createRuntimeAgentStreamResponse(payload, agent, this.deps);
|
|
92
|
-
|
|
103
|
+
const runtimeOwnerInvokeUrl = await this.deps.resolveRuntimeOwnerInvokeUrl?.(req) ??
|
|
104
|
+
null;
|
|
105
|
+
const responseWithOwner = runtimeOwnerInvokeUrl
|
|
106
|
+
? setResponseHeader(response, RUNTIME_OWNER_INVOKE_URL_HEADER, runtimeOwnerInvokeUrl)
|
|
107
|
+
: response;
|
|
108
|
+
return this.respond(applyBuilderHeaders(responseWithOwner, builder.headers));
|
|
93
109
|
});
|
|
94
110
|
}
|
|
95
111
|
catch (error) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const VERSION = "0.1.
|
|
1
|
+
export declare const VERSION = "0.1.104";
|
|
2
2
|
export declare function normalizeVeryfrontVersion(version: string | undefined): string | undefined;
|
|
3
3
|
export declare function resolveRuntimeVersion(options?: {
|
|
4
4
|
veryfrontVersion?: string;
|
package/esm/src/utils/version.js
CHANGED
|
@@ -2,7 +2,7 @@ import denoConfig from "../../deno.js";
|
|
|
2
2
|
import { getEnv } from "../platform/compat/process.js";
|
|
3
3
|
// Keep in sync with deno.json version.
|
|
4
4
|
// scripts/release.ts updates this constant during releases.
|
|
5
|
-
export const VERSION = "0.1.
|
|
5
|
+
export const VERSION = "0.1.104";
|
|
6
6
|
export function normalizeVeryfrontVersion(version) {
|
|
7
7
|
if (!version)
|
|
8
8
|
return undefined;
|
package/package.json
CHANGED
package/src/deno.js
CHANGED
|
@@ -38,6 +38,23 @@ export interface AIStreamCallbacks {
|
|
|
38
38
|
}) => void;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
function createAbortError(reason?: unknown): Error {
|
|
42
|
+
if (reason instanceof Error) {
|
|
43
|
+
return reason;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return new DOMException(
|
|
47
|
+
typeof reason === "string" && reason.length > 0 ? reason : "The operation was aborted",
|
|
48
|
+
"AbortError",
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function throwIfAborted(abortSignal?: AbortSignal): void {
|
|
53
|
+
if (abortSignal?.aborted) {
|
|
54
|
+
throw createAbortError(abortSignal.reason);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
41
58
|
export function createStreamState(): AIStreamState {
|
|
42
59
|
return {
|
|
43
60
|
accumulatedText: "",
|
|
@@ -64,11 +81,15 @@ export function processStream(
|
|
|
64
81
|
encoder: TextEncoder,
|
|
65
82
|
textPartId: string | undefined,
|
|
66
83
|
callbacks?: AIStreamCallbacks,
|
|
84
|
+
abortSignal?: AbortSignal,
|
|
67
85
|
): Promise<void> {
|
|
68
86
|
return withSpan("agent.runtime.processStream", async () => {
|
|
69
87
|
let eventCount = 0;
|
|
70
88
|
|
|
89
|
+
throwIfAborted(abortSignal);
|
|
90
|
+
|
|
71
91
|
for await (const part of result.fullStream) {
|
|
92
|
+
throwIfAborted(abortSignal);
|
|
72
93
|
eventCount++;
|
|
73
94
|
|
|
74
95
|
switch (part.type) {
|
|
@@ -169,8 +190,12 @@ export function processStream(
|
|
|
169
190
|
// Ignore other stream parts (source, file, reasoning-*, etc.)
|
|
170
191
|
break;
|
|
171
192
|
}
|
|
193
|
+
|
|
194
|
+
throwIfAborted(abortSignal);
|
|
172
195
|
}
|
|
173
196
|
|
|
197
|
+
throwIfAborted(abortSignal);
|
|
198
|
+
|
|
174
199
|
setActiveSpanAttributes({
|
|
175
200
|
"stream.event_count": eventCount,
|
|
176
201
|
"stream.tool_calls": state.toolCalls.size,
|
|
@@ -39,7 +39,7 @@ import { generateText, type LanguageModel, streamText } from "ai";
|
|
|
39
39
|
import { AGENT_DEFAULTS } from "../ai-defaults.js";
|
|
40
40
|
|
|
41
41
|
// Re-export from submodules
|
|
42
|
-
export { generateMessageId, sendSSE } from "./sse-utils.js";
|
|
42
|
+
export { closeSSEStream, generateMessageId, sendSSE } from "./sse-utils.js";
|
|
43
43
|
export {
|
|
44
44
|
executeConfiguredTool,
|
|
45
45
|
getAvailableTools,
|
|
@@ -58,7 +58,7 @@ export {
|
|
|
58
58
|
} from "./constants.js";
|
|
59
59
|
|
|
60
60
|
import { DEFAULT_MAX_TOKENS, DEFAULT_TEMPERATURE } from "./constants.js";
|
|
61
|
-
import { generateMessageId, sendSSE } from "./sse-utils.js";
|
|
61
|
+
import { closeSSEStream, generateMessageId, sendSSE } from "./sse-utils.js";
|
|
62
62
|
import {
|
|
63
63
|
executeConfiguredTool,
|
|
64
64
|
getAvailableTools,
|
|
@@ -76,6 +76,31 @@ import { resolveConfiguredAgentModel } from "./model-resolution.js";
|
|
|
76
76
|
const logger = serverLogger.component("agent");
|
|
77
77
|
const LOAD_SKILL_TOOL_ID = "load-skill";
|
|
78
78
|
|
|
79
|
+
function createAbortError(reason?: unknown): Error {
|
|
80
|
+
if (reason instanceof Error) {
|
|
81
|
+
return reason;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return new DOMException(
|
|
85
|
+
typeof reason === "string" && reason.length > 0 ? reason : "The operation was aborted",
|
|
86
|
+
"AbortError",
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function throwIfAborted(abortSignal?: AbortSignal): void {
|
|
91
|
+
if (abortSignal?.aborted) {
|
|
92
|
+
throw createAbortError(abortSignal.reason);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function isAbortError(error: unknown, abortSignal?: AbortSignal): boolean {
|
|
97
|
+
if (abortSignal?.aborted && error === abortSignal.reason) {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return error instanceof DOMException && error.name === "AbortError";
|
|
102
|
+
}
|
|
103
|
+
|
|
79
104
|
function getSkillActivationRequiredError(toolName: string): string {
|
|
80
105
|
return `Tool "${toolName}" cannot run before load-skill succeeds in the same step. ` +
|
|
81
106
|
`Call "${LOAD_SKILL_TOOL_ID}" first to establish the active skill context.`;
|
|
@@ -256,6 +281,7 @@ export class AgentRuntime {
|
|
|
256
281
|
},
|
|
257
282
|
modelOverride?: string,
|
|
258
283
|
maxOutputTokensOverride?: number,
|
|
284
|
+
abortSignal?: AbortSignal,
|
|
259
285
|
): Promise<ReadableStream<Uint8Array>> {
|
|
260
286
|
const requestedModel = resolveConfiguredAgentModel(modelOverride || this.config.model);
|
|
261
287
|
// Auto-upgrade local/* to a cloud provider when API keys are available.
|
|
@@ -267,7 +293,19 @@ export class AgentRuntime {
|
|
|
267
293
|
const systemPrompt = await this.resolveSystemPrompt();
|
|
268
294
|
|
|
269
295
|
const encoder = new TextEncoder();
|
|
270
|
-
const
|
|
296
|
+
const streamAbortController = new AbortController();
|
|
297
|
+
const forwardAbort = () => {
|
|
298
|
+
streamAbortController.abort(abortSignal?.reason);
|
|
299
|
+
};
|
|
300
|
+
if (abortSignal) {
|
|
301
|
+
if (abortSignal.aborted) {
|
|
302
|
+
streamAbortController.abort(abortSignal.reason);
|
|
303
|
+
} else {
|
|
304
|
+
abortSignal.addEventListener("abort", forwardAbort, { once: true });
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
const streamAbortSignal = streamAbortController.signal;
|
|
308
|
+
const toolContext = { agentId: this.id, abortSignal: streamAbortSignal, ...context };
|
|
271
309
|
const textPartId = generateId("text");
|
|
272
310
|
|
|
273
311
|
// Resolve model BEFORE creating the ReadableStream — if this throws
|
|
@@ -289,6 +327,7 @@ export class AgentRuntime {
|
|
|
289
327
|
return new ReadableStream<Uint8Array>({
|
|
290
328
|
start: async (controller) => {
|
|
291
329
|
try {
|
|
330
|
+
throwIfAborted(streamAbortSignal);
|
|
292
331
|
this.status = "streaming";
|
|
293
332
|
|
|
294
333
|
const messageId = generateMessageId();
|
|
@@ -319,22 +358,35 @@ export class AgentRuntime {
|
|
|
319
358
|
resolvedModelString,
|
|
320
359
|
languageModel,
|
|
321
360
|
maxOutputTokensOverride,
|
|
361
|
+
streamAbortSignal,
|
|
322
362
|
);
|
|
363
|
+
throwIfAborted(streamAbortSignal);
|
|
323
364
|
callbacks?.onFinish?.(response);
|
|
365
|
+
throwIfAborted(streamAbortSignal);
|
|
324
366
|
|
|
325
367
|
sendSSE(controller, encoder, { type: "text-end", id: textPartId });
|
|
326
368
|
sendSSE(controller, encoder, { type: "message-finish" });
|
|
327
|
-
controller
|
|
369
|
+
closeSSEStream(controller);
|
|
328
370
|
} catch (error) {
|
|
371
|
+
if (isAbortError(error, streamAbortSignal)) {
|
|
372
|
+
closeSSEStream(controller);
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
|
|
329
376
|
this.status = "error";
|
|
330
377
|
logger.error("Agent stream error", { error });
|
|
331
378
|
sendSSE(controller, encoder, {
|
|
332
379
|
type: "error",
|
|
333
380
|
error: "An internal error occurred",
|
|
334
381
|
});
|
|
335
|
-
controller
|
|
382
|
+
closeSSEStream(controller);
|
|
383
|
+
} finally {
|
|
384
|
+
abortSignal?.removeEventListener("abort", forwardAbort);
|
|
336
385
|
}
|
|
337
386
|
},
|
|
387
|
+
cancel(reason) {
|
|
388
|
+
streamAbortController.abort(reason);
|
|
389
|
+
},
|
|
338
390
|
});
|
|
339
391
|
}
|
|
340
392
|
|
|
@@ -587,6 +639,7 @@ export class AgentRuntime {
|
|
|
587
639
|
modelString?: string,
|
|
588
640
|
resolvedModel?: LanguageModel,
|
|
589
641
|
maxOutputTokensOverride?: number,
|
|
642
|
+
abortSignal?: AbortSignal,
|
|
590
643
|
): Promise<AgentResponse> {
|
|
591
644
|
const { maxAgentSteps } = getPlatformCapabilities();
|
|
592
645
|
const maxSteps = this.computeMaxSteps(maxAgentSteps);
|
|
@@ -613,6 +666,7 @@ export class AgentRuntime {
|
|
|
613
666
|
let finalFinishReason: string | undefined;
|
|
614
667
|
|
|
615
668
|
for (let step = 0; step < maxSteps; step++) {
|
|
669
|
+
throwIfAborted(abortSignal);
|
|
616
670
|
sendSSE(controller, encoder, { type: "step-start" });
|
|
617
671
|
|
|
618
672
|
let tools = isLocalStreaming ? [] : getAvailableTools(this.config.tools, {
|
|
@@ -631,13 +685,15 @@ export class AgentRuntime {
|
|
|
631
685
|
tools: convertToolsToAISDK(tools),
|
|
632
686
|
maxOutputTokens: this.resolveMaxOutputTokens(maxOutputTokensOverride),
|
|
633
687
|
temperature: DEFAULT_TEMPERATURE,
|
|
688
|
+
abortSignal,
|
|
634
689
|
});
|
|
635
690
|
|
|
636
691
|
const state = createStreamState();
|
|
637
692
|
await processStream(result, state, controller, encoder, textPartId, {
|
|
638
693
|
onChunk: callbacks?.onChunk,
|
|
639
694
|
onUsage: (usage) => accumulateUsage(totalUsage, usage),
|
|
640
|
-
});
|
|
695
|
+
}, abortSignal);
|
|
696
|
+
throwIfAborted(abortSignal);
|
|
641
697
|
finalFinishReason = state.finishReason ?? finalFinishReason;
|
|
642
698
|
|
|
643
699
|
const streamParts: MessagePart[] = [];
|
|
@@ -680,6 +736,7 @@ export class AgentRuntime {
|
|
|
680
736
|
streamedToolCalls.some((tc) => tc.name === LOAD_SKILL_TOOL_ID);
|
|
681
737
|
|
|
682
738
|
for (const tc of streamedToolCalls) {
|
|
739
|
+
throwIfAborted(abortSignal);
|
|
683
740
|
const { args, error: argError } = parseToolArgs(tc.arguments);
|
|
684
741
|
const toolCall: ToolCall = { id: tc.id, name: tc.name, args, status: "pending" };
|
|
685
742
|
|
|
@@ -737,6 +794,7 @@ export class AgentRuntime {
|
|
|
737
794
|
...toolContext,
|
|
738
795
|
},
|
|
739
796
|
);
|
|
797
|
+
throwIfAborted(abortSignal);
|
|
740
798
|
|
|
741
799
|
toolCall.status = "completed";
|
|
742
800
|
toolCall.result = result;
|
|
@@ -785,6 +843,7 @@ export class AgentRuntime {
|
|
|
785
843
|
}
|
|
786
844
|
}
|
|
787
845
|
|
|
846
|
+
throwIfAborted(abortSignal);
|
|
788
847
|
sendSSE(controller, encoder, { type: "step-end" });
|
|
789
848
|
this.status = "thinking";
|
|
790
849
|
}
|
|
@@ -6,6 +6,11 @@
|
|
|
6
6
|
* @module ai/agent/runtime/sse-utils
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
function isClosedStreamControllerError(error: unknown): error is TypeError {
|
|
10
|
+
return error instanceof TypeError &&
|
|
11
|
+
error.message.includes("The stream controller cannot close or enqueue");
|
|
12
|
+
}
|
|
13
|
+
|
|
9
14
|
/**
|
|
10
15
|
* Encode and enqueue a Server-Sent Event (SSE) to the stream controller.
|
|
11
16
|
* Formats event as: data: {json}\n\n
|
|
@@ -18,6 +23,18 @@ export function sendSSE(
|
|
|
18
23
|
controller.enqueue(encoder.encode(`data: ${JSON.stringify(event)}\n\n`));
|
|
19
24
|
}
|
|
20
25
|
|
|
26
|
+
export function closeSSEStream(controller: ReadableStreamDefaultController): void {
|
|
27
|
+
try {
|
|
28
|
+
controller.close();
|
|
29
|
+
} catch (error) {
|
|
30
|
+
if (isClosedStreamControllerError(error)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
21
38
|
/**
|
|
22
39
|
* Generate a unique message ID for streaming.
|
|
23
40
|
*/
|
|
@@ -32,6 +32,9 @@ export interface RuntimeAgentStreamExecutionDeps {
|
|
|
32
32
|
callbacks?: {
|
|
33
33
|
onFinish?: (response: AgentResponse) => void;
|
|
34
34
|
},
|
|
35
|
+
modelOverride?: string,
|
|
36
|
+
maxOutputTokensOverride?: number,
|
|
37
|
+
abortSignal?: AbortSignal,
|
|
35
38
|
) => Promise<ReadableStream<Uint8Array>>;
|
|
36
39
|
};
|
|
37
40
|
}
|
|
@@ -234,6 +237,9 @@ export async function createRuntimeAgentStreamResponse(
|
|
|
234
237
|
completedResponse = response;
|
|
235
238
|
},
|
|
236
239
|
},
|
|
240
|
+
undefined,
|
|
241
|
+
undefined,
|
|
242
|
+
abortSignal,
|
|
237
243
|
);
|
|
238
244
|
} catch (error) {
|
|
239
245
|
deps.sessionManager.failRun(input.runId);
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import * as dntShim from "../../_dnt.shims.js";
|
|
2
|
+
import { dynamicImport } from "../platform/compat/dynamic-import.js";
|
|
3
|
+
import { getHostEnv } from "../platform/compat/process.js";
|
|
4
|
+
|
|
5
|
+
const DEFAULT_PROXY_RUNTIME_PORT = "20000";
|
|
6
|
+
|
|
7
|
+
export const RUNTIME_OWNER_INVOKE_URL_HEADER = "x-veryfront-runtime-owner-invoke-url";
|
|
8
|
+
|
|
9
|
+
interface NetworkAddressInfo {
|
|
10
|
+
address?: string;
|
|
11
|
+
family?: string | number;
|
|
12
|
+
internal?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type DenoNetworkInterface = {
|
|
16
|
+
address: string;
|
|
17
|
+
family?: string | number;
|
|
18
|
+
internal?: boolean;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type NodeOsModule = {
|
|
22
|
+
networkInterfaces(): Record<string, NetworkAddressInfo[] | undefined>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type RuntimeOwnerGlobal = typeof dntShim.dntGlobalThis & {
|
|
26
|
+
Deno?: {
|
|
27
|
+
networkInterfaces?: () => ReadonlyArray<DenoNetworkInterface>;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
interface RuntimeOwnerResolverDeps {
|
|
32
|
+
getHostEnv?: typeof getHostEnv;
|
|
33
|
+
dynamicImport?: typeof dynamicImport;
|
|
34
|
+
getDenoNetworkInterfaces?: () => ReadonlyArray<DenoNetworkInterface>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function parsePort(value: string | null | undefined): string | null {
|
|
38
|
+
if (!value) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const trimmed = value.trim();
|
|
43
|
+
if (!/^\d+$/.test(trimmed)) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const port = Number(trimmed);
|
|
48
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return String(port);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isTruthyEnv(value: string | null | undefined): boolean {
|
|
56
|
+
if (!value) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const normalized = value.trim().toLowerCase();
|
|
61
|
+
return normalized === "1" || normalized === "true" || normalized === "yes";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isIpv4Address(value: string | undefined): value is string {
|
|
65
|
+
if (!value) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const parts = value.split(".");
|
|
70
|
+
if (parts.length !== 4) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return parts.every((part) => {
|
|
75
|
+
if (!/^\d+$/.test(part)) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const octet = Number(part);
|
|
80
|
+
return octet >= 0 && octet <= 255;
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function isLoopbackIpv4(address: string): boolean {
|
|
85
|
+
return address.startsWith("127.");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function selectNetworkIpv4Address(
|
|
89
|
+
candidates: ReadonlyArray<NetworkAddressInfo | DenoNetworkInterface>,
|
|
90
|
+
): string | null {
|
|
91
|
+
for (const candidate of candidates) {
|
|
92
|
+
if (
|
|
93
|
+
candidate.internal === true ||
|
|
94
|
+
!isIpv4Address(candidate.address) ||
|
|
95
|
+
isLoopbackIpv4(candidate.address)
|
|
96
|
+
) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return candidate.address;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function getDenoNetworkInterfacesSafe(
|
|
107
|
+
deps: RuntimeOwnerResolverDeps,
|
|
108
|
+
): ReadonlyArray<DenoNetworkInterface> {
|
|
109
|
+
try {
|
|
110
|
+
return deps.getDenoNetworkInterfaces?.() ??
|
|
111
|
+
((dntShim.dntGlobalThis as RuntimeOwnerGlobal).Deno?.networkInterfaces?.() ?? []);
|
|
112
|
+
} catch {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function detectRuntimeOwnerHost(
|
|
118
|
+
deps: RuntimeOwnerResolverDeps,
|
|
119
|
+
): Promise<string | null> {
|
|
120
|
+
const readHostEnv = deps.getHostEnv ?? getHostEnv;
|
|
121
|
+
const explicitHost = readHostEnv("VERYFRONT_RUNTIME_OWNER_HOST") ??
|
|
122
|
+
readHostEnv("POD_IP") ??
|
|
123
|
+
null;
|
|
124
|
+
|
|
125
|
+
if (explicitHost?.trim()) {
|
|
126
|
+
return explicitHost.trim();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const denoInterfaces = getDenoNetworkInterfacesSafe(deps);
|
|
130
|
+
const denoHost = selectNetworkIpv4Address(denoInterfaces);
|
|
131
|
+
if (denoHost) {
|
|
132
|
+
return denoHost;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
const nodeOs = await (deps.dynamicImport ?? dynamicImport)<NodeOsModule>("node:os");
|
|
137
|
+
const nodeInterfaces = Object.values(nodeOs.networkInterfaces()).flatMap(
|
|
138
|
+
(entries) => entries ?? [],
|
|
139
|
+
);
|
|
140
|
+
const nodeHost = selectNetworkIpv4Address(nodeInterfaces);
|
|
141
|
+
if (nodeHost) {
|
|
142
|
+
return nodeHost;
|
|
143
|
+
}
|
|
144
|
+
} catch {
|
|
145
|
+
// Ignore runtime-specific interface lookup failures and fall through.
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function resolveRuntimeOwnerPort(
|
|
152
|
+
req: dntShim.Request,
|
|
153
|
+
deps: RuntimeOwnerResolverDeps,
|
|
154
|
+
): string | null {
|
|
155
|
+
const readHostEnv = deps.getHostEnv ?? getHostEnv;
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
parsePort(readHostEnv("VERYFRONT_RUNTIME_OWNER_PORT")) ??
|
|
159
|
+
parsePort(readHostEnv("VERYFRONT_SERVER_PORT")) ??
|
|
160
|
+
parsePort(new URL(req.url).port) ??
|
|
161
|
+
parsePort(readHostEnv("PORT")) ??
|
|
162
|
+
(isTruthyEnv(readHostEnv("PROXY_MODE")) ? DEFAULT_PROXY_RUNTIME_PORT : null)
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function buildRuntimeOwnerInvokeUrl(host: string, port: string | null): string | null {
|
|
167
|
+
try {
|
|
168
|
+
const url = new URL("http://127.0.0.1/channels/invoke");
|
|
169
|
+
url.hostname = host;
|
|
170
|
+
if (port) {
|
|
171
|
+
url.port = port;
|
|
172
|
+
}
|
|
173
|
+
return url.toString();
|
|
174
|
+
} catch {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export async function resolveRuntimeOwnerInvokeUrl(
|
|
180
|
+
req: dntShim.Request,
|
|
181
|
+
deps: RuntimeOwnerResolverDeps = {},
|
|
182
|
+
): Promise<string | null> {
|
|
183
|
+
const host = await detectRuntimeOwnerHost(deps);
|
|
184
|
+
if (!host) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return buildRuntimeOwnerInvokeUrl(host, resolveRuntimeOwnerPort(req, deps));
|
|
189
|
+
}
|
|
@@ -6,6 +6,10 @@ import {
|
|
|
6
6
|
createRuntimeAgentStreamResponse,
|
|
7
7
|
type RuntimeAgentStreamExecutionDeps,
|
|
8
8
|
} from "../../../internal-agents/run-stream.js";
|
|
9
|
+
import {
|
|
10
|
+
resolveRuntimeOwnerInvokeUrl,
|
|
11
|
+
RUNTIME_OWNER_INVOKE_URL_HEADER,
|
|
12
|
+
} from "../../../internal-agents/runtime-owner.js";
|
|
9
13
|
import {
|
|
10
14
|
ControlPlaneRequestError,
|
|
11
15
|
verifyControlPlaneRequest,
|
|
@@ -29,11 +33,14 @@ import { PRIORITY_MEDIUM_API } from "../../../utils/constants/index.js";
|
|
|
29
33
|
import { getHostEnv } from "../../../platform/compat/process.js";
|
|
30
34
|
|
|
31
35
|
export interface AgentStreamHandlerDeps
|
|
32
|
-
extends RuntimeAgentDiscoveryDeps, RuntimeAgentStreamExecutionDeps {
|
|
36
|
+
extends RuntimeAgentDiscoveryDeps, RuntimeAgentStreamExecutionDeps {
|
|
37
|
+
resolveRuntimeOwnerInvokeUrl?: typeof resolveRuntimeOwnerInvokeUrl;
|
|
38
|
+
}
|
|
33
39
|
|
|
34
40
|
const defaultDeps: AgentStreamHandlerDeps = {
|
|
35
41
|
...defaultChannelInvokeDeps,
|
|
36
42
|
sessionManager: agentRunSessionManager,
|
|
43
|
+
resolveRuntimeOwnerInvokeUrl,
|
|
37
44
|
};
|
|
38
45
|
|
|
39
46
|
type SourceContextFsWrapper = {
|
|
@@ -93,6 +100,16 @@ function applyBuilderHeaders(target: dntShim.Response, source: dntShim.Headers):
|
|
|
93
100
|
});
|
|
94
101
|
}
|
|
95
102
|
|
|
103
|
+
function setResponseHeader(target: dntShim.Response, key: string, value: string): dntShim.Response {
|
|
104
|
+
const headers = new dntShim.Headers(target.headers);
|
|
105
|
+
headers.set(key, value);
|
|
106
|
+
return new dntShim.Response(target.body, {
|
|
107
|
+
status: target.status,
|
|
108
|
+
statusText: target.statusText,
|
|
109
|
+
headers,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
96
113
|
export class AgentStreamHandler extends BaseHandler {
|
|
97
114
|
metadata: HandlerMetadata = {
|
|
98
115
|
name: "AgentStreamHandler",
|
|
@@ -165,7 +182,16 @@ export class AgentStreamHandler extends BaseHandler {
|
|
|
165
182
|
agent as Agent,
|
|
166
183
|
this.deps,
|
|
167
184
|
);
|
|
168
|
-
|
|
185
|
+
const runtimeOwnerInvokeUrl = await this.deps.resolveRuntimeOwnerInvokeUrl?.(req) ??
|
|
186
|
+
null;
|
|
187
|
+
const responseWithOwner = runtimeOwnerInvokeUrl
|
|
188
|
+
? setResponseHeader(
|
|
189
|
+
response,
|
|
190
|
+
RUNTIME_OWNER_INVOKE_URL_HEADER,
|
|
191
|
+
runtimeOwnerInvokeUrl,
|
|
192
|
+
)
|
|
193
|
+
: response;
|
|
194
|
+
return this.respond(applyBuilderHeaders(responseWithOwner, builder.headers));
|
|
169
195
|
},
|
|
170
196
|
);
|
|
171
197
|
} catch (error) {
|
package/src/src/utils/version.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { getEnv } from "../platform/compat/process.js";
|
|
|
3
3
|
|
|
4
4
|
// Keep in sync with deno.json version.
|
|
5
5
|
// scripts/release.ts updates this constant during releases.
|
|
6
|
-
export const VERSION = "0.1.
|
|
6
|
+
export const VERSION = "0.1.104";
|
|
7
7
|
|
|
8
8
|
export function normalizeVeryfrontVersion(version: string | undefined): string | undefined {
|
|
9
9
|
if (!version) return undefined;
|