@springbrand/agent-runtime 0.2.0-alpha.43 → 0.2.0-alpha.45
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/package.json +1 -1
- package/src/adapter/cloudflare/subagent/definition.ts +0 -3
- package/src/adapter/cloudflare/subagent/runner.ts +19 -6
- package/src/db/schema.ts +6 -1
- package/src/db/submission.repo.ts +13 -1
- package/src/index.ts +0 -1
- package/src/kernel/submission-lifecycle.ts +25 -8
- package/src/pi/assembly/context.ts +23 -10
- package/src/pi/assembly/snapshot.ts +14 -8
- package/src/pi/message/projection.ts +47 -21
- package/src/pi/runtime-adapter/assembly.ts +2 -30
- package/src/pi/runtime-adapter/execution.ts +56 -46
- package/src/pi/runtime-adapter/index.ts +2 -8
- package/src/pi/runtime-adapter/models.ts +63 -26
- package/src/pi/runtime-adapter/transcript.ts +6 -3
- package/src/pi/tool/base.ts +10 -15
- package/src/pi/tool/compiler.ts +1 -1
- package/src/pi/tool/core.ts +2 -0
- package/src/pi/tool/schedule.ts +15 -11
- package/src/pi/tool/skill.ts +3 -0
- package/src/pi/tool/subagent.ts +2 -0
- package/src/pi/tool/time.ts +1 -0
- package/src/pi/tool/web-search/api.ts +8 -0
- package/src/pi/tool/web-search/web-search.ts +1 -0
- package/src/pi/tool/workspace-sandbox.ts +3 -0
- package/src/runtime-agent.ts +9 -0
- package/src/runtime-assembler.ts +17 -11
- package/src/runtime.ts +103 -20
|
@@ -2,13 +2,13 @@ import {
|
|
|
2
2
|
PreparedPiTurnAdapter,
|
|
3
3
|
type CreatePreparedPiTurnOptions,
|
|
4
4
|
} from "./execution";
|
|
5
|
-
export { RetryableModelError } from "./execution";
|
|
5
|
+
export { ContextOverflowError, RetryableModelError } from "./execution";
|
|
6
6
|
import {
|
|
7
7
|
uiUserMessageToPi,
|
|
8
8
|
} from "../message";
|
|
9
9
|
import type { UIMessage } from "ai";
|
|
10
10
|
import { createModels, type MutableModels } from "@earendil-works/pi-ai";
|
|
11
|
-
import { configurePiModels, createPiModels
|
|
11
|
+
import { configurePiModels, createPiModels } from "./models";
|
|
12
12
|
export {
|
|
13
13
|
MODEL_STREAM_STALL_TIMEOUT_MS,
|
|
14
14
|
modelRequestUrl,
|
|
@@ -120,12 +120,6 @@ export class PiRuntimeAdapter {
|
|
|
120
120
|
return uiUserMessageToPi(input, privateContext);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
resolveApiKey(
|
|
124
|
-
...args: Parameters<typeof resolvePiApiKey>
|
|
125
|
-
): ReturnType<typeof resolvePiApiKey> {
|
|
126
|
-
return resolvePiApiKey(...args);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
123
|
/**
|
|
130
124
|
* 加载并预检一份可供后续准入使用的 Prepared Runtime。
|
|
131
125
|
*
|
|
@@ -111,6 +111,38 @@ export type PiGenerationLifecycleObserver = (
|
|
|
111
111
|
event: PiGenerationLifecycleEvent,
|
|
112
112
|
) => void;
|
|
113
113
|
|
|
114
|
+
export function structuredOutputStream(
|
|
115
|
+
streamFn: StreamFn,
|
|
116
|
+
model: Model<Api>,
|
|
117
|
+
schema: Record<string, unknown>,
|
|
118
|
+
): StreamFn | undefined {
|
|
119
|
+
if (
|
|
120
|
+
model.api !== "anthropic-messages" ||
|
|
121
|
+
!model.provider.startsWith("model-api-openrouter-messages-")
|
|
122
|
+
) return undefined;
|
|
123
|
+
|
|
124
|
+
return (activeModel, context, options) =>
|
|
125
|
+
streamFn(activeModel, context, {
|
|
126
|
+
...options,
|
|
127
|
+
onPayload: async (payload, payloadModel) => {
|
|
128
|
+
const value = await options?.onPayload?.(payload, payloadModel) ?? payload;
|
|
129
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
130
|
+
throw new Error("Structured model payload must be an object");
|
|
131
|
+
}
|
|
132
|
+
const current = (value as Record<string, unknown>).output_config;
|
|
133
|
+
return {
|
|
134
|
+
...value,
|
|
135
|
+
output_config: {
|
|
136
|
+
...(current && typeof current === "object" && !Array.isArray(current)
|
|
137
|
+
? current
|
|
138
|
+
: {}),
|
|
139
|
+
format: { type: "json_schema", schema },
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
114
146
|
export function isModelStreamStallMessage(message?: string): message is string {
|
|
115
147
|
return message === MODEL_STREAM_STALL_MESSAGE ||
|
|
116
148
|
message?.startsWith(MODEL_STREAM_STALL_DETAILS_PREFIX) === true;
|
|
@@ -534,8 +566,8 @@ export function withProviderRetry(
|
|
|
534
566
|
}
|
|
535
567
|
|
|
536
568
|
// 找出部署把这个模型放在哪个端点,并保留该端点在配置中的位置。
|
|
537
|
-
// resolvePiModel
|
|
538
|
-
//
|
|
569
|
+
// resolvePiModel 在组装运行快照前调用它,调用方只需传部署配置和 modelId。
|
|
570
|
+
// 同一 modelId 若配置多次会取第一个,是否应改为拒绝重复配置仍待确认。
|
|
539
571
|
function endpointFor(
|
|
540
572
|
provider: RuntimeProviderPort,
|
|
541
573
|
modelId: string,
|
|
@@ -585,6 +617,20 @@ function piApiFor(protocol: RuntimeModelProtocol) {
|
|
|
585
617
|
}
|
|
586
618
|
}
|
|
587
619
|
|
|
620
|
+
function anthropicMessagesModelForOpenRouter(
|
|
621
|
+
modelId: string,
|
|
622
|
+
): Model<"anthropic-messages"> | undefined {
|
|
623
|
+
if (!modelId.startsWith("anthropic/")) return;
|
|
624
|
+
const anthropicModelId = modelId
|
|
625
|
+
.slice("anthropic/".length)
|
|
626
|
+
.replace(/:batch$/, "")
|
|
627
|
+
.replace(/-fast$/, "")
|
|
628
|
+
.replaceAll(".", "-");
|
|
629
|
+
return CATALOGS["anthropic-messages"].find(
|
|
630
|
+
(model) => model.id === anthropicModelId,
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
|
|
588
634
|
// 把部署声明的模型改造成可由对应自定义端点执行的 Pi 模型。
|
|
589
635
|
// resolvePiModel 为单个运行快照调用它,configurePiModels 则在注册端点时为端点中的每个 modelId 调用它。
|
|
590
636
|
// Pi 的内置 OpenAI、Anthropic provider 提供上下文窗口、费用和推理能力等元数据;这里保留这些元数据,但以部署协议、provider 标识和 baseURL 覆盖内置路由,未知模型会在初始化阶段直接报错,而不是拖到请求阶段。
|
|
@@ -602,10 +648,10 @@ function configuredModel(
|
|
|
602
648
|
);
|
|
603
649
|
}
|
|
604
650
|
const {
|
|
605
|
-
api:
|
|
651
|
+
api: catalogApi,
|
|
606
652
|
provider: _provider,
|
|
607
653
|
baseUrl: _baseUrl,
|
|
608
|
-
compat,
|
|
654
|
+
compat: catalogCompat,
|
|
609
655
|
headers: catalogHeaders,
|
|
610
656
|
...metadata
|
|
611
657
|
} = catalogModel;
|
|
@@ -614,17 +660,24 @@ function configuredModel(
|
|
|
614
660
|
...endpoint.headers,
|
|
615
661
|
};
|
|
616
662
|
const openRouterProviderPin = endpoint.openRouterProviderPins?.[modelId];
|
|
617
|
-
const
|
|
663
|
+
const configuredApi = apiFor(endpoint.protocol);
|
|
664
|
+
// OpenRouter uses dotted versions and optional transport suffixes while the
|
|
665
|
+
// Anthropic catalog uses dashed base model ids.
|
|
666
|
+
const anthropicMessagesCompat = endpoint.protocol === "openrouter-messages"
|
|
667
|
+
? anthropicMessagesModelForOpenRouter(modelId)?.compat
|
|
668
|
+
: undefined;
|
|
618
669
|
return {
|
|
619
670
|
...metadata,
|
|
620
|
-
api:
|
|
671
|
+
api: configuredApi,
|
|
621
672
|
provider: providerId(endpoint, index),
|
|
622
673
|
baseUrl: endpoint.baseURL,
|
|
623
674
|
...(endpoint.protocol === "openrouter-messages"
|
|
624
675
|
? {
|
|
625
676
|
compat: {
|
|
677
|
+
...anthropicMessagesCompat,
|
|
626
678
|
supportsEagerToolInputStreaming: false,
|
|
627
|
-
supportsStrictTools
|
|
679
|
+
supportsStrictTools:
|
|
680
|
+
anthropicMessagesCompat?.supportsStrictTools ?? false,
|
|
628
681
|
supportsToolReferences: true,
|
|
629
682
|
openRouterRouting: {
|
|
630
683
|
require_parameters: true,
|
|
@@ -637,7 +690,9 @@ function configuredModel(
|
|
|
637
690
|
},
|
|
638
691
|
},
|
|
639
692
|
}
|
|
640
|
-
:
|
|
693
|
+
: catalogApi === configuredApi && catalogCompat
|
|
694
|
+
? { compat: catalogCompat }
|
|
695
|
+
: {}),
|
|
641
696
|
...(Object.keys(headers).length > 0 ? { headers } : {}),
|
|
642
697
|
};
|
|
643
698
|
}
|
|
@@ -661,24 +716,6 @@ export function resolvePiModel(
|
|
|
661
716
|
return configuredModel(endpoint, index, modelId);
|
|
662
717
|
}
|
|
663
718
|
|
|
664
|
-
/**
|
|
665
|
-
* 取出负责指定模型的部署端点密钥。
|
|
666
|
-
*
|
|
667
|
-
* Runtime 在创建一次模型执行或压缩上下文前调用它;辅助的标题生成器也在请求 Pi 时使用它。
|
|
668
|
-
* 调用方应传入与 {@link resolvePiModel} 相同的 provider 和 modelId,并只把返回值用于当前请求。
|
|
669
|
-
*
|
|
670
|
-
* 它与模型解析共用 endpointFor,确保密钥和模型总是选择同一个端点,也让未配置的模型在密钥离开部署边界前失败。
|
|
671
|
-
* 不要另写按协议选密钥的分支,否则同协议的多个端点会拿错凭据。
|
|
672
|
-
*
|
|
673
|
-
* @throws 当部署未配置该 modelId 时抛出错误。
|
|
674
|
-
*/
|
|
675
|
-
export function resolvePiApiKey(
|
|
676
|
-
provider: RuntimeProviderPort,
|
|
677
|
-
modelId: string,
|
|
678
|
-
): string {
|
|
679
|
-
return endpointFor(provider, modelId).endpoint.apiKey;
|
|
680
|
-
}
|
|
681
|
-
|
|
682
719
|
/**
|
|
683
720
|
* 用当前部署端点完整重建一个已有的 Pi 模型集合。
|
|
684
721
|
*
|
|
@@ -440,7 +440,7 @@ export class PiRuntimeTranscript {
|
|
|
440
440
|
/**
|
|
441
441
|
* 在 Pi 请求模型前按 Runtime 阈值压缩当前上下文,并把压缩 entry 写回 Session。
|
|
442
442
|
*
|
|
443
|
-
* `AgentRuntimeKernel.transformPiContext` 作为 Pi 的 `transformContext`
|
|
443
|
+
* `AgentRuntimeKernel.transformPiContext` 作为 Pi 的 `transformContext` 调用它,并提供本次固定的模型和取消信号。
|
|
444
444
|
*
|
|
445
445
|
* 方法先逐条核对调用参数与耐久分支;不一致或压缩不可用时保留原消息,避免把漂移的内存上下文提交成新的规范分支。
|
|
446
446
|
*/
|
|
@@ -448,8 +448,8 @@ export class PiRuntimeTranscript {
|
|
|
448
448
|
messages: AgentMessage[],
|
|
449
449
|
options: {
|
|
450
450
|
compactAfterTokens: number;
|
|
451
|
+
force?: boolean;
|
|
451
452
|
model: Model<Api>;
|
|
452
|
-
apiKey: string;
|
|
453
453
|
signal?: AbortSignal;
|
|
454
454
|
submissionId?: string;
|
|
455
455
|
onCompactionPersisted?: (event: RuntimeModelUsageEvent) => void;
|
|
@@ -474,11 +474,14 @@ export class PiRuntimeTranscript {
|
|
|
474
474
|
const result = await compactPiContext({
|
|
475
475
|
branch,
|
|
476
476
|
compactAfterTokens: options.compactAfterTokens,
|
|
477
|
+
force: options.force,
|
|
477
478
|
models: this.models,
|
|
478
479
|
model: options.model,
|
|
479
|
-
apiKey: options.apiKey,
|
|
480
480
|
signal: options.signal,
|
|
481
481
|
});
|
|
482
|
+
if (options.force && !result.didCompact) {
|
|
483
|
+
throw new Error("Forced context compaction did not reduce the model input");
|
|
484
|
+
}
|
|
482
485
|
if (result.compactionEntry) {
|
|
483
486
|
// 整对象写入:assembly 侧已经造好了完整的 compaction 条目,这里不再拆成位置
|
|
484
487
|
// 参数。旧写法拆 5 个参数喂 pi 的 Session.appendCompaction,pi 0.81 把它加到
|
package/src/pi/tool/base.ts
CHANGED
|
@@ -2,7 +2,12 @@ import type {
|
|
|
2
2
|
AgentTool,
|
|
3
3
|
AgentToolResult,
|
|
4
4
|
} from "@earendil-works/pi-agent-core";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
StringEnum,
|
|
7
|
+
Type,
|
|
8
|
+
type Static,
|
|
9
|
+
type TSchema,
|
|
10
|
+
} from "@earendil-works/pi-ai";
|
|
6
11
|
import { estimateStringTokens } from "agents/experimental/memory/utils";
|
|
7
12
|
import type { RuntimeMemoryPort } from "../../kernel/bindings";
|
|
8
13
|
import type { RuntimeMemoryProfile } from "../../kernel/profile";
|
|
@@ -145,11 +150,7 @@ const updatePlanParameters = Type.Object({
|
|
|
145
150
|
text: Type.String({
|
|
146
151
|
description: "The step, phrased as a short imperative.",
|
|
147
152
|
}),
|
|
148
|
-
status:
|
|
149
|
-
Type.Literal("pending"),
|
|
150
|
-
Type.Literal("in_progress"),
|
|
151
|
-
Type.Literal("done"),
|
|
152
|
-
], {
|
|
153
|
+
status: StringEnum(["pending", "in_progress", "done"] as const, {
|
|
153
154
|
description:
|
|
154
155
|
'Current status. Use "done" for a finished step; never use "completed".',
|
|
155
156
|
}),
|
|
@@ -214,18 +215,12 @@ export function normalizeUpdatePlanArguments(
|
|
|
214
215
|
}
|
|
215
216
|
|
|
216
217
|
const setContextParameters = Type.Object({
|
|
217
|
-
label:
|
|
218
|
-
Type.Literal("memory"),
|
|
219
|
-
Type.Literal("preferences"),
|
|
220
|
-
], {
|
|
218
|
+
label: StringEnum(["memory", "preferences"] as const, {
|
|
221
219
|
description:
|
|
222
220
|
"Context block to update: memory for durable facts and active context, preferences for tone and workflow choices.",
|
|
223
221
|
}),
|
|
224
222
|
content: Type.String({ description: "Text to store in the selected context block." }),
|
|
225
|
-
action: Type.Optional(
|
|
226
|
-
Type.Literal("replace"),
|
|
227
|
-
Type.Literal("append"),
|
|
228
|
-
], {
|
|
223
|
+
action: Type.Optional(StringEnum(["replace", "append"] as const, {
|
|
229
224
|
description: 'Whether to replace the block or append to it. Defaults to "replace".',
|
|
230
225
|
})),
|
|
231
226
|
}, { additionalProperties: false });
|
|
@@ -318,7 +313,7 @@ export function basePiToolCandidates(
|
|
|
318
313
|
name: "update_plan",
|
|
319
314
|
label: "Update plan",
|
|
320
315
|
description:
|
|
321
|
-
"Maintain the user-visible plan for the current task. Call this whenever a task involves 2 or more steps, and again every time the plan or a step's status changes. Always pass the FULL plan — it replaces the previous plan entirely (idempotent overwrite), so omitted steps disappear.",
|
|
316
|
+
"Maintain the user-visible plan for the current task. Required input: steps. Call this whenever a task involves 2 or more steps, and again every time the plan or a step's status changes. Always pass the FULL plan — it replaces the previous plan entirely (idempotent overwrite), so omitted steps disappear.",
|
|
322
317
|
parameters: updatePlanParameters,
|
|
323
318
|
prepareArguments: normalizeUpdatePlanArguments,
|
|
324
319
|
async execute(_toolCallId, input) {
|
package/src/pi/tool/compiler.ts
CHANGED
|
@@ -66,7 +66,7 @@ export interface PiToolCandidate {
|
|
|
66
66
|
* 那是失效不是降级。`deny` 仍然优先,被 deny 的工具根本不会注入。
|
|
67
67
|
*/
|
|
68
68
|
readonly alwaysRequiresApproval?: boolean;
|
|
69
|
-
/** Recovery may replay this Tool Call only when the candidate guarantees convergence. */
|
|
69
|
+
/** Recovery may replay this Tool Call only when the candidate guarantees convergence. Omitted means non-idempotent. */
|
|
70
70
|
readonly retry?: "idempotent" | "non-idempotent";
|
|
71
71
|
readonly summary?: string;
|
|
72
72
|
readonly source?: ApprovalReceipt["source"];
|
package/src/pi/tool/core.ts
CHANGED
|
@@ -196,6 +196,7 @@ export function listExtensionsPiToolCandidate(
|
|
|
196
196
|
return {
|
|
197
197
|
owner: "core:extensions",
|
|
198
198
|
requiredExecutionLevel: "low",
|
|
199
|
+
retry: "idempotent",
|
|
199
200
|
tool,
|
|
200
201
|
};
|
|
201
202
|
}
|
|
@@ -278,6 +279,7 @@ export function codeExecutionPiToolCandidate(
|
|
|
278
279
|
return {
|
|
279
280
|
owner: "core:codemode",
|
|
280
281
|
requiredExecutionLevel: "safe",
|
|
282
|
+
retry: "idempotent",
|
|
281
283
|
outputBudget: { kind: "structure" },
|
|
282
284
|
source: "codemode",
|
|
283
285
|
summary: "Run JavaScript with network and configured connector access",
|
package/src/pi/tool/schedule.ts
CHANGED
|
@@ -92,7 +92,7 @@ function candidate<T extends TSchema>(
|
|
|
92
92
|
options: Partial<
|
|
93
93
|
Pick<
|
|
94
94
|
PiToolCandidate,
|
|
95
|
-
"alwaysRequiresApproval" | "exposureMode" | "owner" | "requiredExecutionLevel" | "summary"
|
|
95
|
+
"alwaysRequiresApproval" | "exposureMode" | "owner" | "requiredExecutionLevel" | "retry" | "summary"
|
|
96
96
|
>
|
|
97
97
|
> = {},
|
|
98
98
|
): PiToolCandidate {
|
|
@@ -106,6 +106,7 @@ function candidate<T extends TSchema>(
|
|
|
106
106
|
? { alwaysRequiresApproval: true }
|
|
107
107
|
: {}),
|
|
108
108
|
...(options.summary ? { summary: options.summary } : {}),
|
|
109
|
+
...(options.retry ? { retry: options.retry } : {}),
|
|
109
110
|
};
|
|
110
111
|
}
|
|
111
112
|
|
|
@@ -138,17 +139,20 @@ export function schedulePiToolCandidates(
|
|
|
138
139
|
summary: "Create a scheduled task",
|
|
139
140
|
},
|
|
140
141
|
),
|
|
141
|
-
candidate(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
signal
|
|
149
|
-
|
|
142
|
+
candidate(
|
|
143
|
+
{
|
|
144
|
+
name: "list_schedules",
|
|
145
|
+
label: "List schedules",
|
|
146
|
+
description:
|
|
147
|
+
"List the user's existing scheduled tasks. Call this when the user asks what they have scheduled, or before cancelling one — you need the id, and the user will refer to a task by what it does, not by its id.",
|
|
148
|
+
parameters: noParameters,
|
|
149
|
+
async execute(_toolCallId, _input, signal) {
|
|
150
|
+
signal?.throwIfAborted();
|
|
151
|
+
return result({ schedules: await schedule.list() });
|
|
152
|
+
},
|
|
150
153
|
},
|
|
151
|
-
|
|
154
|
+
{ retry: "idempotent" },
|
|
155
|
+
),
|
|
152
156
|
candidate({
|
|
153
157
|
name: "update_schedule",
|
|
154
158
|
label: "Update schedule",
|
package/src/pi/tool/skill.ts
CHANGED
|
@@ -534,6 +534,9 @@ export async function skillPiToolCandidates(
|
|
|
534
534
|
...(name === "activate_skill"
|
|
535
535
|
? { exposureMode: "direct" as const }
|
|
536
536
|
: { exposureMode: "codemode" as const }),
|
|
537
|
+
...(name === "activate_skill" || name === "read_skill_resource"
|
|
538
|
+
? { retry: "idempotent" as const }
|
|
539
|
+
: {}),
|
|
537
540
|
tool: adapted,
|
|
538
541
|
};
|
|
539
542
|
});
|
package/src/pi/tool/subagent.ts
CHANGED
|
@@ -182,6 +182,7 @@ export function subagentPiToolCandidates(
|
|
|
182
182
|
owner: `subagent:${type.name}`,
|
|
183
183
|
exposureMode: "direct",
|
|
184
184
|
requiredExecutionLevel: "safe",
|
|
185
|
+
retry: "idempotent",
|
|
185
186
|
tool,
|
|
186
187
|
};
|
|
187
188
|
});
|
|
@@ -265,6 +266,7 @@ export function subagentPiToolCandidates(
|
|
|
265
266
|
owner: "subagent:background",
|
|
266
267
|
exposureMode: "direct",
|
|
267
268
|
requiredExecutionLevel: "low",
|
|
269
|
+
retry: "idempotent",
|
|
268
270
|
summary: "Dispatch a background sub-agent",
|
|
269
271
|
source: "action",
|
|
270
272
|
tool: background,
|
package/src/pi/tool/time.ts
CHANGED
|
@@ -707,6 +707,12 @@ async function callOpenAIStream(
|
|
|
707
707
|
tools: isOpenRouter
|
|
708
708
|
? [{ type: "openrouter:web_search", parameters: { max_uses: 10 } }]
|
|
709
709
|
: [{ type: "web_search" }],
|
|
710
|
+
...(isOpenRouter
|
|
711
|
+
? {
|
|
712
|
+
max_output_tokens: Math.min(model.maxTokens, OPENROUTER_MAX_OUTPUT_TOKENS),
|
|
713
|
+
reasoning: { effort: "none" },
|
|
714
|
+
}
|
|
715
|
+
: {}),
|
|
710
716
|
...(!isOpenRouter
|
|
711
717
|
? {
|
|
712
718
|
include: isCodex
|
|
@@ -1108,6 +1114,8 @@ export function applyCitations(text: string, groundingMetadata: any): { text: st
|
|
|
1108
1114
|
|
|
1109
1115
|
const PROVIDER_ERROR_MAX_LENGTH = 2_048;
|
|
1110
1116
|
const NATIVE_TIMEOUT_MS = 60_000;
|
|
1117
|
+
const OPENROUTER_MAX_OUTPUT_TOKENS = 4_096;
|
|
1118
|
+
export const WEB_SEARCH_MODEL = "deepseek/deepseek-v4-flash";
|
|
1111
1119
|
|
|
1112
1120
|
export interface WebSearchOptions {
|
|
1113
1121
|
endpoint: RuntimeModelEndpoint;
|
|
@@ -402,6 +402,7 @@ export function workspacePiToolCandidates(
|
|
|
402
402
|
{
|
|
403
403
|
owner: "workspace",
|
|
404
404
|
requiredExecutionLevel: "safe" as const,
|
|
405
|
+
retry: "idempotent" as const,
|
|
405
406
|
// read 是大输出外置协议的**出口**:溢出结果的提示语指向它。出口自己再外置,
|
|
406
407
|
// 就会变成 read → 新 artifact → read 的死循环,模型只能靠猜逃出去。
|
|
407
408
|
// 分页上限由 `pageReadResult` 自己保证,不需要外置也不会撑爆一轮。
|
|
@@ -411,6 +412,7 @@ export function workspacePiToolCandidates(
|
|
|
411
412
|
...[write, edit, list, find, grep, remove].map((tool) => ({
|
|
412
413
|
owner: "workspace",
|
|
413
414
|
requiredExecutionLevel: "safe" as const,
|
|
415
|
+
retry: "idempotent" as const,
|
|
414
416
|
tool,
|
|
415
417
|
})),
|
|
416
418
|
];
|
|
@@ -621,6 +623,7 @@ export function sandboxPiToolCandidates(
|
|
|
621
623
|
{
|
|
622
624
|
owner: "sandbox",
|
|
623
625
|
requiredExecutionLevel: "safe",
|
|
626
|
+
retry: "idempotent",
|
|
624
627
|
tool: processLogs,
|
|
625
628
|
},
|
|
626
629
|
{
|
package/src/runtime-agent.ts
CHANGED
|
@@ -301,6 +301,7 @@ export interface RuntimeAgentControls {
|
|
|
301
301
|
requestId?: string,
|
|
302
302
|
reason?: string,
|
|
303
303
|
): Promise<{ ok: boolean }>;
|
|
304
|
+
stopAllSubmissions(reason?: string): Promise<{ ok: boolean }>;
|
|
304
305
|
}
|
|
305
306
|
|
|
306
307
|
export interface RuntimeAgentConfigControls<Command, Change> {
|
|
@@ -525,6 +526,10 @@ export function defineRuntimeAgent<
|
|
|
525
526
|
return super.stopTurn(requestId, reason);
|
|
526
527
|
}
|
|
527
528
|
|
|
529
|
+
stopAllSubmissions(reason?: string): Promise<{ ok: boolean }> {
|
|
530
|
+
return super.stopAllSubmissions(reason);
|
|
531
|
+
}
|
|
532
|
+
|
|
528
533
|
protected override async prepareAgentToolRun(
|
|
529
534
|
input: unknown,
|
|
530
535
|
): Promise<string> {
|
|
@@ -922,6 +927,10 @@ export function defineRuntimeAgent<
|
|
|
922
927
|
callableContext,
|
|
923
928
|
);
|
|
924
929
|
callable()(GeneratedRuntimeAgent.prototype.stopTurn, callableContext);
|
|
930
|
+
callable()(
|
|
931
|
+
GeneratedRuntimeAgent.prototype.stopAllSubmissions,
|
|
932
|
+
callableContext,
|
|
933
|
+
);
|
|
925
934
|
|
|
926
935
|
return GeneratedRuntimeAgent as unknown as RuntimeAgentClass<
|
|
927
936
|
Env,
|
package/src/runtime-assembler.ts
CHANGED
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
codeExecutionPiToolCandidate,
|
|
59
59
|
} from "./pi/tool/core";
|
|
60
60
|
import { skillPiToolCandidates } from "./pi/tool/skill";
|
|
61
|
-
import { createWebSearch } from "./pi/tool/web-search";
|
|
61
|
+
import { createWebSearch, WEB_SEARCH_MODEL } from "./pi/tool/web-search";
|
|
62
62
|
import { subagentPiToolCandidates } from "./pi/tool/subagent";
|
|
63
63
|
import { resolvePiModel } from "./pi/runtime-adapter/models";
|
|
64
64
|
import {
|
|
@@ -170,7 +170,7 @@ interface ToolSurfaceInput {
|
|
|
170
170
|
readonly hostTools: readonly PiToolCandidate[];
|
|
171
171
|
readonly skills: readonly RuntimeSkillSourceBinding[];
|
|
172
172
|
readonly enabledSubagents: readonly string[];
|
|
173
|
-
readonly webSearch
|
|
173
|
+
readonly webSearch?: Parameters<typeof basePiToolCandidates>[0];
|
|
174
174
|
readonly extensions: readonly RuntimeExtensionConfig[];
|
|
175
175
|
readonly policy?: RuntimeToolSurfacePolicy;
|
|
176
176
|
}
|
|
@@ -581,14 +581,20 @@ class RuntimeBuilder {
|
|
|
581
581
|
: `Model is configured by multiple endpoints: ${profile.model}`,
|
|
582
582
|
);
|
|
583
583
|
}
|
|
584
|
-
const
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
584
|
+
const webSearchEndpoint = provider.endpoints.find((endpoint) =>
|
|
585
|
+
endpoint.models.includes(WEB_SEARCH_MODEL)
|
|
586
|
+
);
|
|
587
|
+
const webSearch = webSearchEndpoint
|
|
588
|
+
? (() => {
|
|
589
|
+
const webSearchModel = resolvePiModel(provider, WEB_SEARCH_MODEL);
|
|
590
|
+
return createWebSearch({
|
|
591
|
+
endpoint: webSearchEndpoint,
|
|
592
|
+
model: WEB_SEARCH_MODEL,
|
|
593
|
+
maxTokens: webSearchModel.maxTokens,
|
|
594
|
+
reasoning: webSearchModel.reasoning,
|
|
595
|
+
});
|
|
596
|
+
})()
|
|
597
|
+
: undefined;
|
|
592
598
|
const skillSources = [...this.skillSources.values()];
|
|
593
599
|
const toolSurface = await createToolSurface({
|
|
594
600
|
...(this.codeExecution ? { codeExecution: this.codeExecution } : {}),
|
|
@@ -606,7 +612,7 @@ class RuntimeBuilder {
|
|
|
606
612
|
],
|
|
607
613
|
skills: skillSources,
|
|
608
614
|
enabledSubagents: [...this.enabledSubagents],
|
|
609
|
-
webSearch,
|
|
615
|
+
...(webSearch ? { webSearch } : {}),
|
|
610
616
|
extensions: [...this.extensions.values()],
|
|
611
617
|
...(this.toolPolicy ? { policy: this.toolPolicy } : {}),
|
|
612
618
|
});
|