@springbrand/agent-runtime 0.2.0-alpha.56 → 0.2.0-alpha.57
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
CHANGED
|
@@ -45,6 +45,7 @@ import {
|
|
|
45
45
|
} from "./assembly";
|
|
46
46
|
import {
|
|
47
47
|
isRecoverableAssistantError,
|
|
48
|
+
isOpenRouterServerToolTimeout,
|
|
48
49
|
isModelStreamStallMessage,
|
|
49
50
|
withProviderRetry,
|
|
50
51
|
type PiGenerationLifecycleObserver,
|
|
@@ -490,7 +491,7 @@ export interface PiTurnDurability {
|
|
|
490
491
|
export interface CreatePreparedPiTurnOptions {
|
|
491
492
|
readonly prepared: PreparedPiRuntime;
|
|
492
493
|
readonly pinnedDescriptor: string;
|
|
493
|
-
/**
|
|
494
|
+
/** Session 当前的稳定 Provider 路由身份;终态 Provider 故障可在下一 Submission 前旋转它。 */
|
|
494
495
|
readonly modelSessionId: string;
|
|
495
496
|
readonly submission: {
|
|
496
497
|
readonly id: string;
|
|
@@ -609,6 +610,7 @@ export type PiCanonicalMessageCommit =
|
|
|
609
610
|
export interface PiTurnTerminalIntent {
|
|
610
611
|
readonly outcome: "succeeded" | "failed" | "aborted";
|
|
611
612
|
readonly message?: string;
|
|
613
|
+
readonly resetModelSession?: boolean;
|
|
612
614
|
}
|
|
613
615
|
|
|
614
616
|
export interface PiPreparedTurnRunOptions {
|
|
@@ -1134,6 +1136,9 @@ export class PreparedPiTurnAdapter {
|
|
|
1134
1136
|
);
|
|
1135
1137
|
this.terminalIntent = {
|
|
1136
1138
|
outcome: terminal.outcome,
|
|
1139
|
+
...(isOpenRouterServerToolTimeout(authoritativeMessage)
|
|
1140
|
+
? { resetModelSession: true }
|
|
1141
|
+
: {}),
|
|
1137
1142
|
...(authoritativeMessage.errorMessage
|
|
1138
1143
|
? { message: authoritativeMessage.errorMessage }
|
|
1139
1144
|
: terminal.message
|
|
@@ -182,6 +182,32 @@ export function readModelStreamStallDetails(
|
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
+
export function isOpenRouterServerToolTimeout(
|
|
186
|
+
message: AssistantMessage,
|
|
187
|
+
): boolean {
|
|
188
|
+
if (
|
|
189
|
+
message.stopReason !== "error" ||
|
|
190
|
+
!message.provider.startsWith("model-api-openrouter-messages-") ||
|
|
191
|
+
!message.errorMessage
|
|
192
|
+
) return false;
|
|
193
|
+
try {
|
|
194
|
+
const payload = JSON.parse(message.errorMessage) as {
|
|
195
|
+
type?: unknown;
|
|
196
|
+
error?: {
|
|
197
|
+
type?: unknown;
|
|
198
|
+
message?: unknown;
|
|
199
|
+
error_type?: unknown;
|
|
200
|
+
};
|
|
201
|
+
};
|
|
202
|
+
return payload.type === "error" &&
|
|
203
|
+
payload.error?.type === "timeout_error" &&
|
|
204
|
+
payload.error.message === "Server tool request failed" &&
|
|
205
|
+
payload.error.error_type === "timeout";
|
|
206
|
+
} catch {
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
185
211
|
export function isRecoverableAssistantError(
|
|
186
212
|
message: AssistantMessage,
|
|
187
213
|
): boolean {
|
|
@@ -189,6 +215,7 @@ export function isRecoverableAssistantError(
|
|
|
189
215
|
message.stopReason === "error" &&
|
|
190
216
|
/thought[_ ]signature/i.test(message.errorMessage ?? "")
|
|
191
217
|
) return false;
|
|
218
|
+
if (isOpenRouterServerToolTimeout(message)) return false;
|
|
192
219
|
return isRetryableAssistantError(message) ||
|
|
193
220
|
(message.stopReason === "error" &&
|
|
194
221
|
/upstream http\/2 stream failed/i.test(message.errorMessage ?? ""));
|
package/src/runtime.ts
CHANGED
|
@@ -134,6 +134,7 @@ import {
|
|
|
134
134
|
const SCHEDULED_STABLE_TIMEOUT_MS = 30_000;
|
|
135
135
|
const TURN_EVENT_RETRY_SECONDS = 10;
|
|
136
136
|
const RUNTIME_EVENT_MAX_ATTEMPTS = 5;
|
|
137
|
+
const MODEL_SESSION_ID_STORAGE_KEY = "universal-agent:model-session-id";
|
|
137
138
|
|
|
138
139
|
// 计划续跑丢失多久后由看门狗接手。必须显著长于一个正常执行片的时长,
|
|
139
140
|
// 否则会在健康的长执行片上误判并派发出重复的一片。
|
|
@@ -442,6 +443,8 @@ export abstract class AgentRuntimeKernel<
|
|
|
442
443
|
private readonly approvals: ApprovalLifecycle<StoredSubmission>;
|
|
443
444
|
private readonly interactions: InteractionLifecycle<StoredSubmission>;
|
|
444
445
|
private runtimeLoadTracker?: RuntimeLoadTracker;
|
|
446
|
+
private modelSessionId?: string;
|
|
447
|
+
private readonly modelSessionIdReady?: Promise<void>;
|
|
445
448
|
private runtimeEventDrainTail?: Promise<void>;
|
|
446
449
|
private readonly streamBySubmission = new Map<string, string>();
|
|
447
450
|
private migratedSubmissionIds?: Set<string>;
|
|
@@ -521,6 +524,11 @@ export abstract class AgentRuntimeKernel<
|
|
|
521
524
|
snapshotKey: "__cfPiFiberSnapshot",
|
|
522
525
|
snapshotKind: "pi-turn",
|
|
523
526
|
});
|
|
527
|
+
this.modelSessionIdReady = ctx.storage.get<string>(
|
|
528
|
+
MODEL_SESSION_ID_STORAGE_KEY,
|
|
529
|
+
).then((stored) => {
|
|
530
|
+
this.modelSessionId = stored;
|
|
531
|
+
});
|
|
524
532
|
this.piAdapter = pi;
|
|
525
533
|
this.db = new RuntimeDatabase(
|
|
526
534
|
this.sql.bind(this),
|
|
@@ -734,6 +742,7 @@ export abstract class AgentRuntimeKernel<
|
|
|
734
742
|
}
|
|
735
743
|
|
|
736
744
|
protected async initCandidate(candidate: RuntimeCandidate): Promise<void> {
|
|
745
|
+
await this.modelSessionIdReady;
|
|
737
746
|
const previous = this.activeRuntime;
|
|
738
747
|
const candidateSnapshot = candidate.snapshot;
|
|
739
748
|
this.runtimeLoad.advance("assembly");
|
|
@@ -870,6 +879,7 @@ export abstract class AgentRuntimeKernel<
|
|
|
870
879
|
terminal: {
|
|
871
880
|
outcome: "succeeded" | "failed" | "aborted";
|
|
872
881
|
message?: string;
|
|
882
|
+
resetModelSession?: boolean;
|
|
873
883
|
},
|
|
874
884
|
) => void | Promise<void>;
|
|
875
885
|
} = {},
|
|
@@ -909,7 +919,7 @@ export abstract class AgentRuntimeKernel<
|
|
|
909
919
|
return this.pi.createTurn({
|
|
910
920
|
prepared: this.preparedPi(),
|
|
911
921
|
pinnedDescriptor: submission.assemblyDescriptor,
|
|
912
|
-
modelSessionId: this.name,
|
|
922
|
+
modelSessionId: this.modelSessionId ?? this.name,
|
|
913
923
|
submission: {
|
|
914
924
|
id: submission.submissionId,
|
|
915
925
|
requestId: submission.requestId,
|
|
@@ -3038,6 +3048,7 @@ export abstract class AgentRuntimeKernel<
|
|
|
3038
3048
|
| {
|
|
3039
3049
|
outcome: "succeeded" | "failed" | "aborted";
|
|
3040
3050
|
message?: string;
|
|
3051
|
+
resetModelSession?: boolean;
|
|
3041
3052
|
}
|
|
3042
3053
|
| undefined;
|
|
3043
3054
|
const startedAt = ready.createdAt;
|
|
@@ -3050,13 +3061,20 @@ export abstract class AgentRuntimeKernel<
|
|
|
3050
3061
|
this.migratedSubmissions.has(submissionId)
|
|
3051
3062
|
? undefined
|
|
3052
3063
|
: this.persistAndBroadcastRecord(turn, record),
|
|
3053
|
-
onTerminal: (intent) => {
|
|
3064
|
+
onTerminal: async (intent) => {
|
|
3054
3065
|
terminalIntent = intent;
|
|
3055
3066
|
this.appendTerminalIntent(
|
|
3056
3067
|
submission,
|
|
3057
3068
|
intent.outcome,
|
|
3058
3069
|
intent.message,
|
|
3059
3070
|
);
|
|
3071
|
+
if (intent.resetModelSession) {
|
|
3072
|
+
this.modelSessionId = crypto.randomUUID();
|
|
3073
|
+
await this.ctx.storage.put(
|
|
3074
|
+
MODEL_SESSION_ID_STORAGE_KEY,
|
|
3075
|
+
this.modelSessionId,
|
|
3076
|
+
);
|
|
3077
|
+
}
|
|
3060
3078
|
},
|
|
3061
3079
|
});
|
|
3062
3080
|
for (const pending of this.db.steers.listForSubmission(submissionId)) {
|