@zq-silk/yui 0.13.8 → 0.13.9
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/README.md +9 -8
- package/dist/cli/commandCatalog.js +21 -2
- package/dist/cli.js +42 -13
- package/dist/commands/agentCommands.js +1 -1
- package/dist/commands/configCommands.js +1 -86
- package/dist/commands/executionAuditCommands.js +17 -16
- package/dist/commands/globalRoleCommands.js +4 -4
- package/dist/commands/sessionCommands.js +2 -6
- package/dist/commands/taskActor.js +1 -2
- package/dist/commands/taskCommands.js +92 -9
- package/dist/commands/taskRoleRuntimeStatus.js +3 -3
- package/dist/config/configCatalog.js +1 -6
- package/dist/config/yuiConfig.js +0 -79
- package/dist/controller/clientRuntime.js +40 -3
- package/dist/controller/controller.js +29 -52
- package/dist/controller/fileSchedulerStoreAdapter.js +305 -1056
- package/dist/controller/runtime.js +12 -5
- package/dist/controller/runtimeHookRunFence.js +3 -9
- package/dist/controller/runtimeLaunchCoordinator.js +44 -67
- package/dist/controller/structuredProviderObservation.js +18 -5
- package/dist/coordination/workMailbox.js +4 -4
- package/dist/execution/executionHealth.js +1 -1
- package/dist/executor/agentExecutor.js +92 -68
- package/dist/executor/executorRegistry.js +8 -20
- package/dist/executor/fileRoleLaunchPlanner.js +24 -90
- package/dist/executor/turnCompletion.js +5 -5
- package/dist/lifecycle/exactRunTerminalization.js +1 -1
- package/dist/observability/executionAudit.js +40 -94
- package/dist/operator/operatorSessionHistory.js +7 -5
- package/dist/role/role.js +1 -1
- package/dist/run/agentRun.js +4 -54
- package/dist/runtime/agentDriver.js +2 -0
- package/dist/runtime/agentError.js +114 -0
- package/dist/runtime/agentHost.js +55 -82
- package/dist/runtime/builtinAgentDrivers.js +21 -9
- package/dist/runtime/builtinAgentErrorMappers.js +150 -0
- package/dist/runtime/exactControlPlane.js +6 -12
- package/dist/runtime/index.js +0 -1
- package/dist/runtime/launchBroker.js +5 -19
- package/dist/runtime/lifecycleReservation.js +20 -4
- package/dist/runtime/providerRuntimeIdentity.js +3 -2
- package/dist/runtime/runtimeBinding.js +0 -27
- package/dist/runtime/runtimeObservation.js +7 -16
- package/dist/runtime/runtimeSessionCandidate.js +3 -10
- package/dist/runtime/sessionLaunchRequest.js +1 -2
- package/dist/runtime/sessionReconciliation.js +2 -2
- package/dist/runtime/structuredProviderHost.js +44 -79
- package/dist/runtime/taskRuntimeIsolation.js +0 -7
- package/dist/runtime/tmuxAdapters.js +6 -49
- package/dist/scheduler/activeRoleRunDelivery.js +218 -168
- package/dist/scheduler/leaderWakeupProcessor.js +126 -86
- package/dist/scheduler/roleRunLiveness.js +4 -1
- package/dist/scheduler/roleRunStall.js +7 -11
- package/dist/scheduler/wakeReason.js +4 -0
- package/dist/storage/migration/productionRegistry.js +332 -0
- package/dist/storage/sqliteSchema.js +54 -2
- package/dist/storage/sqliteStore.js +11 -44
- package/dist/storage/taskStore.js +7 -35
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +30 -8
- package/skills/yui-operator/SKILL.md +14 -6
- package/skills/yui-runtime/SKILL.md +6 -4
- package/dist/lifecycle/providerErrorClass.js +0 -152
- package/dist/run/providerRetry.js +0 -226
- package/dist/run/providerRetryConfig.js +0 -27
- package/dist/runtime/providerErrorCodes.js +0 -278
- package/dist/runtime/providerRecoveryDecision.js +0 -55
|
@@ -6,7 +6,7 @@ import { validateConfiguredAgent } from "../agent/agent.js";
|
|
|
6
6
|
import { validateCapabilityGrant } from "../grant/capabilityGrant.js";
|
|
7
7
|
import { validateReleaseWorkflow } from "../release/releaseWorkflow.js";
|
|
8
8
|
import { publicationExternalKey, validatePublicationReference } from "../task/publicationReference.js";
|
|
9
|
-
import { reconciliationIntervalMilliseconds, resolveAgentLaunchInactivityTimeoutSeconds, resolveControllerTaskConcurrency, resolveContextBudget, resolveDeliveryTimeoutSeconds, resolveLeaderNextActionMode, resolveLeaderSemanticBudgetTurns,
|
|
9
|
+
import { reconciliationIntervalMilliseconds, resolveAgentLaunchInactivityTimeoutSeconds, resolveControllerTaskConcurrency, resolveContextBudget, resolveDeliveryTimeoutSeconds, resolveLeaderNextActionMode, resolveLeaderSemanticBudgetTurns, resolveResourcesGcAutoQuarantine, resolveResourcesGcMode, resolveResourcesQuarantineTtlHours, resolveRuntimeHealth, resolveTelemetryEnabled, resolveTelemetryRunCap, resolveTelemetryTerminalKeep, resolveTmuxBin, resolveTmuxHistoryLimit } from "../config/yuiConfig.js";
|
|
10
10
|
import { resolveTimeZone } from "../output/timePresentation.js";
|
|
11
11
|
import { mailboxBatches, consumePendingBatch, mailboxHasWork, mailboxTargetKey, pendingLane, validateWorkMailbox } from "../coordination/workMailbox.js";
|
|
12
12
|
import { validateContextSnapshot } from "../context/contextSnapshot.js";
|
|
@@ -15,7 +15,6 @@ import { validateInputRequest } from "../input/inputRequest.js";
|
|
|
15
15
|
import { validateRoleSessionSet } from "../executor/agentExecutor.js";
|
|
16
16
|
import { validateTaskMessage } from "../message/message.js";
|
|
17
17
|
import { agentRunDeliveryReceiptId, validateAgentRun } from "../run/agentRun.js";
|
|
18
|
-
import { providerRetryWakeAt } from "../run/providerRetry.js";
|
|
19
18
|
import { compareRuntimeSessionCandidates, projectRuntimeSessionCandidate } from "../runtime/runtimeSessionCandidate.js";
|
|
20
19
|
import { FileSessionOwnerRegistry } from "../runtime/sessionOwnerRegistry.js";
|
|
21
20
|
import { validateReviewConfig } from "../review/reviewConfig.js";
|
|
@@ -45,7 +44,7 @@ import { CURRENT_AGGREGATE_SCHEMA_VERSION, requireCompatibleStorageSchema, requi
|
|
|
45
44
|
export const STORAGE_STATE_FILE = "state.json";
|
|
46
45
|
/** The root StorageState schema is the persisted aggregate document version. */
|
|
47
46
|
export const CURRENT_STORAGE_STATE_SCHEMA_VERSION = CURRENT_AGGREGATE_SCHEMA_VERSION;
|
|
48
|
-
export const CURRENT_CONFIG_SCHEMA_VERSION =
|
|
47
|
+
export const CURRENT_CONFIG_SCHEMA_VERSION = 4;
|
|
49
48
|
export const CURRENT_HOME_IDENTITY_SCHEMA_VERSION = 1;
|
|
50
49
|
export const CURRENT_ACTIVE_RUN_POINTER_SCHEMA_VERSION = 3;
|
|
51
50
|
/**
|
|
@@ -60,7 +59,7 @@ export const CURRENT_CONFIGURED_AGENT_SCHEMA_VERSION = 2;
|
|
|
60
59
|
export const CURRENT_PROJECT_SCHEMA_VERSION = 5;
|
|
61
60
|
export const CURRENT_AGENT_PROFILE_SCHEMA_VERSION = 2;
|
|
62
61
|
export const CURRENT_GLOBAL_ROLE_SCHEMA_VERSION = 3;
|
|
63
|
-
export const CURRENT_GLOBAL_ROLE_SESSION_SET_SCHEMA_VERSION =
|
|
62
|
+
export const CURRENT_GLOBAL_ROLE_SESSION_SET_SCHEMA_VERSION = 4;
|
|
64
63
|
export const CURRENT_TASK_SCHEMA_VERSION = 6;
|
|
65
64
|
export const CURRENT_TASK_BRIEF_SCHEMA_VERSION = 2;
|
|
66
65
|
export const CURRENT_CONTEXT_SNAPSHOT_SCHEMA_VERSION = 1;
|
|
@@ -77,9 +76,9 @@ export const CURRENT_MILESTONE_SCHEMA_VERSION = 2;
|
|
|
77
76
|
export const CURRENT_EVENT_SCHEMA_VERSION = 2;
|
|
78
77
|
export const CURRENT_CAPABILITY_GRANT_SCHEMA_VERSION = 1;
|
|
79
78
|
export const CURRENT_RELEASE_WORKFLOW_SCHEMA_VERSION = 1;
|
|
80
|
-
export const CURRENT_WORK_MAILBOX_SCHEMA_VERSION =
|
|
79
|
+
export const CURRENT_WORK_MAILBOX_SCHEMA_VERSION = 3;
|
|
81
80
|
export const CURRENT_PUBLICATION_REFERENCE_SCHEMA_VERSION = 1;
|
|
82
|
-
export const CURRENT_ROLE_AGENT_SESSION_SCHEMA_VERSION =
|
|
81
|
+
export const CURRENT_ROLE_AGENT_SESSION_SCHEMA_VERSION = 4;
|
|
83
82
|
export const CURRENT_PENDING_WAKEUP_SCHEMA_VERSION = 1;
|
|
84
83
|
const STORAGE_LOCK_DIRECTORY = ".state.lock";
|
|
85
84
|
const LOCK_TIMEOUT_MS = 5_000;
|
|
@@ -118,13 +117,13 @@ export const CURRENT_STORED_TASK_SCHEMA_VERSION = 18;
|
|
|
118
117
|
* Keep these named at the storage boundary so the upgrade record-axis map can
|
|
119
118
|
* assert it is classifying the same bytes the store reads and writes.
|
|
120
119
|
*/
|
|
121
|
-
export const CURRENT_TASK_ROLE_SESSION_SET_SCHEMA_VERSION =
|
|
120
|
+
export const CURRENT_TASK_ROLE_SESSION_SET_SCHEMA_VERSION = 8;
|
|
122
121
|
/**
|
|
123
122
|
* v7 combines optional Issue 04 retry/receipt fields and Issue 05 Leader
|
|
124
123
|
* actionability fields. All are optional, so the v6→v7 migration is a
|
|
125
124
|
* version-only rewrite.
|
|
126
125
|
*/
|
|
127
|
-
export const CURRENT_AGENT_RUN_SCHEMA_VERSION =
|
|
126
|
+
export const CURRENT_AGENT_RUN_SCHEMA_VERSION = 10;
|
|
128
127
|
export const CURRENT_INTEGRATION_QUEUE_SCHEMA_VERSION = 1;
|
|
129
128
|
export class FileTaskStore {
|
|
130
129
|
rootDir;
|
|
@@ -991,29 +990,6 @@ export class FileTaskStore {
|
|
|
991
990
|
}
|
|
992
991
|
getAgentRun(taskId, id) { return optional(this.#state().tasks[taskId]?.agentRuns[id]); }
|
|
993
992
|
listAgentRuns(taskId) { return values(this.#requireTask(taskId).agentRuns, "id"); }
|
|
994
|
-
listPendingProviderRetries(taskIds) {
|
|
995
|
-
// The legacy File store can answer the empty case without a scan fallback.
|
|
996
|
-
// If durable retry state exists, the db-only capability must fail closed
|
|
997
|
-
// instead of silently losing the Controller's wake deadline.
|
|
998
|
-
const tasks = taskIds === undefined
|
|
999
|
-
? this.listTasks()
|
|
1000
|
-
: [...new Set(taskIds)].sort(numericCompare).flatMap((taskId) => {
|
|
1001
|
-
const task = this.getTask(taskId);
|
|
1002
|
-
return task === null ? [] : [task];
|
|
1003
|
-
});
|
|
1004
|
-
for (const task of tasks) {
|
|
1005
|
-
if (task.status !== "active")
|
|
1006
|
-
continue;
|
|
1007
|
-
for (const run of this.listAgentRuns(task.id)) {
|
|
1008
|
-
if (run.status === "active"
|
|
1009
|
-
&& run.providerRetry !== undefined
|
|
1010
|
-
&& providerRetryWakeAt(run.providerRetry) !== null) {
|
|
1011
|
-
throw new StorageRecordError("Provider retry in place requires the SQLite backend; run `yui update` to migrate this Home.");
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
return [];
|
|
1016
|
-
}
|
|
1017
993
|
saveAgentRun(run) {
|
|
1018
994
|
const stored = identified(run, CURRENT_AGENT_RUN_SCHEMA_VERSION, "id", run.id, "Agent run");
|
|
1019
995
|
validateAgentRun(stored);
|
|
@@ -2498,10 +2474,6 @@ export function validateYuiConfig(config) {
|
|
|
2498
2474
|
resolveResourcesGcMode(config.resourcesGcMode);
|
|
2499
2475
|
resolveResourcesGcAutoQuarantine(config.resourcesGcAutoQuarantine);
|
|
2500
2476
|
resolveResourcesQuarantineTtlHours(config.resourcesQuarantineTtlHours);
|
|
2501
|
-
resolveProviderRetryMode(config.providerRetryMode);
|
|
2502
|
-
resolveProviderRetryAdapters(config.providerRetryAdapters);
|
|
2503
|
-
resolveProviderRetryDelaysSeconds(config.providerRetryDelaysSeconds);
|
|
2504
|
-
resolveProviderRetryMaxWindowSeconds(config.providerRetryMaxWindowSeconds);
|
|
2505
2477
|
resolveRuntimeHealth(config.runtimeHealth);
|
|
2506
2478
|
resolveControllerTaskConcurrency(config.controllerTaskConcurrency);
|
|
2507
2479
|
resolveAgentLaunchInactivityTimeoutSeconds(config.agentLaunchInactivityTimeoutSeconds);
|
package/package.json
CHANGED
|
@@ -122,8 +122,8 @@ or yield merely to preserve that native wait.
|
|
|
122
122
|
Before the first durable Leader action, Yui observes fresh native generations
|
|
123
123
|
that produce no WorkItem, Review, Integration, or Leader-attributed durable
|
|
124
124
|
event. Two such generations create a non-blocking orchestration advisory for
|
|
125
|
-
Leader and Operator judgment; they do not fail the Role
|
|
126
|
-
|
|
125
|
+
Leader and Operator judgment; they do not fail the Role or prevent another
|
|
126
|
+
useful generation. Read the evidence
|
|
127
127
|
before retrying, then choose whether to continue, change the configured Leader,
|
|
128
128
|
or perform direct maintenance without manufacturing protocol records merely to
|
|
129
129
|
silence the advisory.
|
|
@@ -703,13 +703,35 @@ newer WorkItem. If the original execution Session cannot be resumed, surface
|
|
|
703
703
|
the recovery decision to the user; do not silently discard its context by
|
|
704
704
|
creating a replacement.
|
|
705
705
|
|
|
706
|
-
|
|
707
|
-
inspect the Run and partial work, then retry only a confirmed failed Run:
|
|
706
|
+
For a Role runtime failure, inspect the exact error and runtime identities first:
|
|
708
707
|
|
|
709
708
|
```sh
|
|
710
|
-
yui task
|
|
709
|
+
yui task event show <task> <agent-error-event>
|
|
710
|
+
yui task role session inspect <task> <role>
|
|
711
711
|
```
|
|
712
712
|
|
|
713
|
+
When a Provider-accepted Turn fails with availability, `429`, capacity, or a
|
|
714
|
+
recoverable transport error and the Session remains usable, retain the Run and
|
|
715
|
+
Session; a recovery action adds a new Turn on that same native Session. A
|
|
716
|
+
Session preparation failure or Driver rejection before input acceptance fails
|
|
717
|
+
the exact Run once; inspect its error and explicitly retry that failed Run when
|
|
718
|
+
another attempt is useful. Core will not redispatch it on a scheduler tick.
|
|
719
|
+
|
|
720
|
+
If the Driver proves that the Session cannot continue, settle or retire the
|
|
721
|
+
exact active Run, stop only that Role Session, then retry the failed Run so the
|
|
722
|
+
next dispatch starts a new Session:
|
|
723
|
+
|
|
724
|
+
```sh
|
|
725
|
+
yui task role session stop <task> <role> --reason "<error decision>"
|
|
726
|
+
yui task run retry <task>/<failed-run>
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
The new Run context contains the prior Agent, adapter, Run, Host activation,
|
|
730
|
+
native Session/Turn identities, and complete raw error through the referenced
|
|
731
|
+
Task event. Inspect recent `runtime.agent-error` events before another fresh
|
|
732
|
+
Session; after repeated fresh-Session failures, report the evidence and bounded
|
|
733
|
+
options to the user instead of inventing another automatic loop.
|
|
734
|
+
|
|
713
735
|
## Request a decision
|
|
714
736
|
|
|
715
737
|
When a real user choice, new authority, or unavailable external fact is
|
|
@@ -789,8 +811,8 @@ yui task complete <task-id> --summary "<outcome, validation, and remaining risks
|
|
|
789
811
|
|
|
790
812
|
Retire obsolete WorkItems with `yui task work retire <task>/<work> --summary
|
|
791
813
|
"..."`, optionally using `--replacement`. If the current Provider Conversation
|
|
792
|
-
cannot continue,
|
|
793
|
-
`yui task role session
|
|
794
|
-
|
|
814
|
+
cannot continue, settle its Run and stop the exact idle Session with
|
|
815
|
+
`yui task role session stop`; the next explicit Run dispatch creates the
|
|
816
|
+
replacement. Archiving is a
|
|
795
817
|
separate global Operator lifecycle action. It performs the final Task-owned
|
|
796
818
|
runtime and clean-worktree teardown, including this Leader.
|
|
@@ -349,12 +349,20 @@ the workflow without claiming that version was delivered.
|
|
|
349
349
|
treat it as a permission boundary. The Operator may make code, semantic,
|
|
350
350
|
requirement, acceptance, recovery, and integration decisions and must leave
|
|
351
351
|
the real actor and rationale in durable Task state.
|
|
352
|
-
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
352
|
+
- Inspect `runtime.agent-error` and `yui task role session inspect` before a
|
|
353
|
+
recovery. When a Provider-accepted Turn fails with availability, `429`,
|
|
354
|
+
capacity, or a recoverable transport error and the Session remains usable,
|
|
355
|
+
add a new Turn to that Session. A Session preparation failure or Driver
|
|
356
|
+
rejection before input acceptance fails the exact Run once; explicitly retry
|
|
357
|
+
that failed Run when another attempt is useful. Core does not redispatch it
|
|
358
|
+
on a scheduler tick. If the Driver proves the Session cannot continue, settle
|
|
359
|
+
or retire its exact Run, stop that one idle Session with `yui task role
|
|
360
|
+
session stop <task> <role> --reason "..."`, then retry the failed Run. The
|
|
361
|
+
replacement Run receives the old Agent, adapter, Run, Host, Session, Turn,
|
|
362
|
+
and complete raw-error facts through Task context.
|
|
363
|
+
- Inspect recent errors before creating another fresh Session. After repeated
|
|
364
|
+
fresh-Session failures, summarize the evidence and bounded options to the
|
|
365
|
+
user; do not hide them behind an automatic replacement counter or loop.
|
|
358
366
|
- Retry only an explicitly failed recovery Job.
|
|
359
367
|
- When a Leader first-progress advisory is reported, inspect its native
|
|
360
368
|
generations and absence of durable progress. It is cost evidence rather than
|
|
@@ -61,7 +61,9 @@ supported checkpoint/yield command as the final control-plane action, then stop
|
|
|
61
61
|
immediately. If that direct command is denied or stale, report the blocker once
|
|
62
62
|
and stop; do not wrap, retry, broaden permissions, or target another Run.
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
After a failed Provider Turn, read the referenced `runtime.agent-error` fact.
|
|
65
|
+
The failed Turn is immutable; a recovery is always a new Turn. Continue on the
|
|
66
|
+
same native Session when it remains recoverable, and load only the current Run
|
|
67
|
+
delta instead of replaying its original Assignment. A new Host process does not
|
|
68
|
+
imply a new Session, and a new Session must never be substituted silently for
|
|
69
|
+
the persisted native Session id.
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Issue 04 — Provider error classification.
|
|
3
|
-
*
|
|
4
|
-
* Provider failures arrive at the driver boundary as opaque free text (Claude
|
|
5
|
-
* StopFailure `error`/`errorDetails`, Codex turn-completion summaries). Each
|
|
6
|
-
* driver parses its own Provider's format into a structured
|
|
7
|
-
* {@link ProviderErrorCode} at the driver boundary. This module maps those
|
|
8
|
-
* codes to provider-neutral error classes by lookup, falling back to text
|
|
9
|
-
* matching only when the driver could not produce a structured code.
|
|
10
|
-
*
|
|
11
|
-
* The retry-in-place coordinator needs a stable, provider-neutral error class
|
|
12
|
-
* before it can decide whether the original Session may be retried.
|
|
13
|
-
*
|
|
14
|
-
* Classes (Issue 04 §2):
|
|
15
|
-
* - `transient-provider` — 500/502/504, connection reset, backend capacity;
|
|
16
|
-
* the original Session is retried in place.
|
|
17
|
-
* - `transport-uncertain` — the request may have been accepted but the
|
|
18
|
-
* response was lost; native facts are consulted
|
|
19
|
-
* before any resend.
|
|
20
|
-
* - `policy-denied` — cyber_policy / permission boundary; never retried
|
|
21
|
-
* automatically, never worked around by switching
|
|
22
|
-
* Session or widening permission.
|
|
23
|
-
* - `session-dead` — the process/tmux/native identity is gone; in-place
|
|
24
|
-
* retry stops and a replacement blocker is raised.
|
|
25
|
-
* - `invalid-request` — deterministic parameter/protocol error; fail fast,
|
|
26
|
-
* never call the Provider again.
|
|
27
|
-
* - `unclassified` — no conservative match; behaves like
|
|
28
|
-
* `invalid-request` for retry purposes (old
|
|
29
|
-
* terminalize-immediately behavior) while remaining
|
|
30
|
-
* observable in shadow metrics.
|
|
31
|
-
*/
|
|
32
|
-
import { PROVIDER_ERROR_CODE_CLASS } from "../runtime/providerErrorCodes.js";
|
|
33
|
-
/** Classes for which the original Session may be retried in place. */
|
|
34
|
-
export const RETRYABLE_PROVIDER_ERROR_CLASSES = [
|
|
35
|
-
"transient-provider",
|
|
36
|
-
"transport-uncertain"
|
|
37
|
-
];
|
|
38
|
-
export function isRetryableProviderErrorClass(errorClass) {
|
|
39
|
-
return RETRYABLE_PROVIDER_ERROR_CLASSES.includes(errorClass);
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Ordered pattern tables. The first class whose pattern matches wins, so the
|
|
43
|
-
* table order is the precedence order. Patterns are matched case-insensitively
|
|
44
|
-
* against the concatenation of every available text field.
|
|
45
|
-
*/
|
|
46
|
-
const SESSION_DEAD_PATTERNS = [
|
|
47
|
-
{ pattern: /session not found/iu, label: "session-not-found" },
|
|
48
|
-
{ pattern: /no such (session|thread)/iu, label: "no-such-session" },
|
|
49
|
-
{ pattern: /thread not found/iu, label: "thread-not-found" },
|
|
50
|
-
{ pattern: /session (has )?expired/iu, label: "session-expired" },
|
|
51
|
-
{ pattern: /session (has )?ended/iu, label: "session-ended" },
|
|
52
|
-
{ pattern: /session (is )?dead/iu, label: "session-dead" },
|
|
53
|
-
{ pattern: /session terminated/iu, label: "session-terminated" },
|
|
54
|
-
];
|
|
55
|
-
const CONTEXT_CAPACITY_PATTERNS = [
|
|
56
|
-
{ pattern: /maximum context length/iu, label: "maximum-context-length" },
|
|
57
|
-
{ pattern: /context length exceeded/iu, label: "context-length-exceeded" },
|
|
58
|
-
{ pattern: /context window (is )?(full|exceeded)/iu, label: "context-window-exceeded" },
|
|
59
|
-
{ pattern: /prompt (is )?too long/iu, label: "prompt-too-long" },
|
|
60
|
-
{ pattern: /too many tokens/iu, label: "too-many-tokens" }
|
|
61
|
-
];
|
|
62
|
-
const POLICY_DENIED_PATTERNS = [
|
|
63
|
-
{ pattern: /cyber[_-]?policy/iu, label: "cyber-policy" },
|
|
64
|
-
{ pattern: /policy[_-]?violation/iu, label: "policy-violation" },
|
|
65
|
-
{ pattern: /usage[_-]?policy/iu, label: "usage-policy" },
|
|
66
|
-
{ pattern: /content[_-]?policy/iu, label: "content-policy" },
|
|
67
|
-
{ pattern: /safety[_-]?policy/iu, label: "safety-policy" },
|
|
68
|
-
{ pattern: /policy denial/iu, label: "policy-denial" }
|
|
69
|
-
];
|
|
70
|
-
const INVALID_REQUEST_PATTERNS = [
|
|
71
|
-
{ pattern: /invalid[_-]?request/iu, label: "invalid-request" },
|
|
72
|
-
{ pattern: /validation error/iu, label: "validation-error" },
|
|
73
|
-
{ pattern: /bad request/iu, label: "bad-request" },
|
|
74
|
-
{ pattern: /\b400\b/u, label: "http-400" },
|
|
75
|
-
{ pattern: /unknown (flag|tool|argument)/iu, label: "unknown-argument" },
|
|
76
|
-
{ pattern: /unexpected argument/iu, label: "unexpected-argument" },
|
|
77
|
-
{ pattern: /invalid schema/iu, label: "invalid-schema" }
|
|
78
|
-
];
|
|
79
|
-
const TRANSIENT_PROVIDER_PATTERNS = [
|
|
80
|
-
{ pattern: /\b50[024]\b/u, label: "http-5xx" },
|
|
81
|
-
{ pattern: /server[\s_-]?error/iu, label: "server-error" },
|
|
82
|
-
{ pattern: /internal server error/iu, label: "internal-server-error" },
|
|
83
|
-
// HTTP/2 RST_STREAM / gRPC status carried by Claude Code and Codex streams
|
|
84
|
-
// (Task-27: "stream error: stream ID …; INTERNAL_ERROR; received from peer").
|
|
85
|
-
{ pattern: /\binternal[\s_-]?error\b/iu, label: "internal-error" },
|
|
86
|
-
{ pattern: /connection lost/iu, label: "connection-lost" },
|
|
87
|
-
{ pattern: /connection reset/iu, label: "connection-reset" },
|
|
88
|
-
{ pattern: /econnreset/iu, label: "econnreset" },
|
|
89
|
-
{ pattern: /socket hang up/iu, label: "socket-hang-up" },
|
|
90
|
-
{ pattern: /kv[_-]?cache[_-]?allocate[_-]?failed/iu, label: "kv-cache-allocate-failed" },
|
|
91
|
-
{ pattern: /overloaded/iu, label: "overloaded" },
|
|
92
|
-
{ pattern: /\b429\b/u, label: "http-429" },
|
|
93
|
-
{ pattern: /rate[_-]?limit/iu, label: "rate-limit" },
|
|
94
|
-
{ pattern: /upstream/iu, label: "upstream" },
|
|
95
|
-
{ pattern: /bad gateway/iu, label: "bad-gateway" },
|
|
96
|
-
{ pattern: /gateway timeout/iu, label: "gateway-timeout" },
|
|
97
|
-
{ pattern: /service unavailable/iu, label: "service-unavailable" },
|
|
98
|
-
{ pattern: /temporarily unavailable/iu, label: "temporarily-unavailable" },
|
|
99
|
-
{ pattern: /try again/iu, label: "try-again" }
|
|
100
|
-
];
|
|
101
|
-
const TRANSPORT_UNCERTAIN_PATTERNS = [
|
|
102
|
-
{ pattern: /timed?[ -]?out/iu, label: "timeout" },
|
|
103
|
-
{ pattern: /etimedout/iu, label: "etimedout" },
|
|
104
|
-
{ pattern: /response lost/iu, label: "response-lost" },
|
|
105
|
-
{ pattern: /lost response/iu, label: "lost-response" },
|
|
106
|
-
// A stream-level failure means the response may have been cut mid-turn;
|
|
107
|
-
// the retry path consults durable completion facts before any resend.
|
|
108
|
-
{ pattern: /stream error/iu, label: "stream-error" },
|
|
109
|
-
{ pattern: /stream interrupted/iu, label: "stream-interrupted" },
|
|
110
|
-
{ pattern: /interrupted function/iu, label: "interrupted-function" },
|
|
111
|
-
{ pattern: /controller timeout/iu, label: "controller-timeout" },
|
|
112
|
-
{ pattern: /delivery (unconfirmed|uncertain|not confirmed)/iu, label: "delivery-unconfirmed" },
|
|
113
|
-
{ pattern: /unconfirmed delivery/iu, label: "unconfirmed-delivery" },
|
|
114
|
-
{ pattern: /no response/iu, label: "no-response" }
|
|
115
|
-
];
|
|
116
|
-
const CLASS_TABLE = [
|
|
117
|
-
{ errorClass: "session-dead", patterns: SESSION_DEAD_PATTERNS },
|
|
118
|
-
{ errorClass: "policy-denied", patterns: POLICY_DENIED_PATTERNS },
|
|
119
|
-
{ errorClass: "context-capacity", patterns: CONTEXT_CAPACITY_PATTERNS },
|
|
120
|
-
{ errorClass: "invalid-request", patterns: INVALID_REQUEST_PATTERNS },
|
|
121
|
-
{ errorClass: "transient-provider", patterns: TRANSIENT_PROVIDER_PATTERNS },
|
|
122
|
-
{ errorClass: "transport-uncertain", patterns: TRANSPORT_UNCERTAIN_PATTERNS }
|
|
123
|
-
];
|
|
124
|
-
/**
|
|
125
|
-
* Classifies one provider failure. When the driver produced a structured
|
|
126
|
-
* {@link ProviderErrorCode}, the class is looked up directly. Otherwise the
|
|
127
|
-
* raw text fields are matched against the fallback pattern tables. Every
|
|
128
|
-
* available text field is concatenated so a class can be recognized
|
|
129
|
-
* regardless of which field carried it.
|
|
130
|
-
*/
|
|
131
|
-
export function classifyProviderError(input) {
|
|
132
|
-
// Structured path: the driver already parsed the Provider's error format.
|
|
133
|
-
if (input.errorCode !== undefined) {
|
|
134
|
-
const errorClass = PROVIDER_ERROR_CODE_CLASS[input.errorCode];
|
|
135
|
-
if (errorClass !== undefined) {
|
|
136
|
-
return { errorClass, matched: input.errorCode, basis: "structured" };
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
// Text fallback: for drivers that cannot yet produce a structured code.
|
|
140
|
-
const text = [input.error, input.errorDetails, input.summary]
|
|
141
|
-
.filter((value) => typeof value === "string" && value.length > 0)
|
|
142
|
-
.join("\n");
|
|
143
|
-
if (text.length === 0)
|
|
144
|
-
return { errorClass: "unclassified", matched: "none", basis: "text" };
|
|
145
|
-
for (const { errorClass, patterns } of CLASS_TABLE) {
|
|
146
|
-
for (const { pattern, label } of patterns) {
|
|
147
|
-
if (pattern.test(text))
|
|
148
|
-
return { errorClass, matched: label, basis: "text" };
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
return { errorClass: "unclassified", matched: "none", basis: "text" };
|
|
152
|
-
}
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { DEFAULT_PROVIDER_RETRY_DELAYS_SECONDS, DEFAULT_PROVIDER_RETRY_MAX_WINDOW_SECONDS, MAX_PROVIDER_RETRY_ATTEMPTS } from "../config/yuiConfig.js";
|
|
3
|
-
import { requireIdentity, requireText, requireTimestamp } from "../domain/validation.js";
|
|
4
|
-
export const PROVIDER_RETRY_DELAYS_MS = Object.freeze(DEFAULT_PROVIDER_RETRY_DELAYS_SECONDS.map((seconds) => seconds * 1_000));
|
|
5
|
-
export const PROVIDER_RETRY_EPISODE_WINDOW_MS = DEFAULT_PROVIDER_RETRY_MAX_WINDOW_SECONDS * 1_000;
|
|
6
|
-
export function nextProviderRetryDelayMs(retryIndex, delaysMs = PROVIDER_RETRY_DELAYS_MS) {
|
|
7
|
-
if (!Number.isSafeInteger(retryIndex)
|
|
8
|
-
|| retryIndex < 1
|
|
9
|
-
|| retryIndex > delaysMs.length) {
|
|
10
|
-
throw new Error(`Provider retry index is out of range: ${String(retryIndex)}.`);
|
|
11
|
-
}
|
|
12
|
-
return delaysMs[retryIndex - 1];
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* True when the retry lineage has used its total wall-clock budget. The
|
|
16
|
-
* budget is measured from the first classified failure, so repeated failures
|
|
17
|
-
* never extend it.
|
|
18
|
-
*/
|
|
19
|
-
export function providerRetryBudgetExhausted(value, now, maxWindowMs = PROVIDER_RETRY_EPISODE_WINDOW_MS) {
|
|
20
|
-
if (!Number.isSafeInteger(maxWindowMs) || maxWindowMs <= 0) {
|
|
21
|
-
throw new Error(`Provider retry max window must be a positive integer: ${String(maxWindowMs)}.`);
|
|
22
|
-
}
|
|
23
|
-
return Math.min(Date.parse(value.episodeDeadlineAt), Date.parse(value.firstFailureAt) + maxWindowMs) <= now.getTime();
|
|
24
|
-
}
|
|
25
|
-
export function validateAgentRunProviderRetry(value) {
|
|
26
|
-
if (value.schemaVersion !== 2) {
|
|
27
|
-
throw new Error("Agent run providerRetry must use schemaVersion 2.");
|
|
28
|
-
}
|
|
29
|
-
if (!["scheduled", "dispatching", "awaiting-progress", "blocked"].includes(value.state)) {
|
|
30
|
-
throw new Error("Agent run providerRetry state is invalid.");
|
|
31
|
-
}
|
|
32
|
-
requireIdentity(value.episodeId, "Agent run providerRetry episodeId");
|
|
33
|
-
requireIdentity(value.failureEventId, "Agent run providerRetry failureEventId");
|
|
34
|
-
if (value.policyVersion !== 1)
|
|
35
|
-
throw new Error("Provider retry policy version is invalid.");
|
|
36
|
-
if (!Number.isSafeInteger(value.consecutiveFailures) || value.consecutiveFailures < 1) {
|
|
37
|
-
throw new Error("Agent run providerRetry consecutiveFailures must be positive.");
|
|
38
|
-
}
|
|
39
|
-
if (!Number.isSafeInteger(value.dispatchedRetries)
|
|
40
|
-
|| value.dispatchedRetries < 0
|
|
41
|
-
|| value.dispatchedRetries > value.maxRetries) {
|
|
42
|
-
throw new Error("Agent run providerRetry dispatchedRetries is invalid.");
|
|
43
|
-
}
|
|
44
|
-
if (!Number.isSafeInteger(value.maxRetries)
|
|
45
|
-
|| value.maxRetries < 1
|
|
46
|
-
|| value.maxRetries > MAX_PROVIDER_RETRY_ATTEMPTS) {
|
|
47
|
-
throw new Error("Agent run providerRetry maxRetries is invalid.");
|
|
48
|
-
}
|
|
49
|
-
requireTimestamp(value.firstFailureAt, "Agent run providerRetry firstFailureAt");
|
|
50
|
-
requireTimestamp(value.lastFailureAt, "Agent run providerRetry lastFailureAt");
|
|
51
|
-
requireTimestamp(value.episodeDeadlineAt, "Agent run providerRetry episodeDeadlineAt");
|
|
52
|
-
if (Date.parse(value.lastFailureAt) < Date.parse(value.firstFailureAt)) {
|
|
53
|
-
throw new Error("Agent run providerRetry lastFailureAt precedes firstFailureAt.");
|
|
54
|
-
}
|
|
55
|
-
if (Date.parse(value.episodeDeadlineAt) <= Date.parse(value.firstFailureAt)) {
|
|
56
|
-
throw new Error("Agent run providerRetry deadline must follow firstFailureAt.");
|
|
57
|
-
}
|
|
58
|
-
if ((value.state === "scheduled") !== (value.nextAttemptAt !== undefined)) {
|
|
59
|
-
throw new Error("Only a scheduled providerRetry may carry nextAttemptAt.");
|
|
60
|
-
}
|
|
61
|
-
if (value.nextAttemptAt !== undefined) {
|
|
62
|
-
requireTimestamp(value.nextAttemptAt, "Agent run providerRetry nextAttemptAt");
|
|
63
|
-
if (Date.parse(value.nextAttemptAt) > Date.parse(value.episodeDeadlineAt)) {
|
|
64
|
-
throw new Error("Agent run providerRetry next attempt exceeds its episode deadline.");
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
if ((value.state === "dispatching" || value.state === "awaiting-progress")
|
|
68
|
-
!== (value.lastRetryReceiptId !== undefined)) {
|
|
69
|
-
throw new Error("An in-flight providerRetry requires one retry receipt identity.");
|
|
70
|
-
}
|
|
71
|
-
if (value.lastRetryReceiptId !== undefined) {
|
|
72
|
-
requireIdentity(value.lastRetryReceiptId, "Agent run providerRetry receipt id");
|
|
73
|
-
}
|
|
74
|
-
if (value.launchId !== undefined)
|
|
75
|
-
requireIdentity(value.launchId, "Agent run providerRetry launchId");
|
|
76
|
-
if (value.nativeSessionId !== undefined) {
|
|
77
|
-
requireIdentity(value.nativeSessionId, "Agent run providerRetry nativeSessionId");
|
|
78
|
-
}
|
|
79
|
-
if (value.failedNativeTurnId !== undefined) {
|
|
80
|
-
requireIdentity(value.failedNativeTurnId, "Agent run providerRetry failed native Turn id");
|
|
81
|
-
}
|
|
82
|
-
requireText(value.lastErrorSummary, "Agent run providerRetry lastErrorSummary");
|
|
83
|
-
return value;
|
|
84
|
-
}
|
|
85
|
-
/** Advance one failure episode without ever changing the native Session. */
|
|
86
|
-
export function scheduleProviderRetry(previous, input, now, policy = {
|
|
87
|
-
delaysMs: PROVIDER_RETRY_DELAYS_MS,
|
|
88
|
-
maxWindowMs: PROVIDER_RETRY_EPISODE_WINDOW_MS
|
|
89
|
-
}) {
|
|
90
|
-
validateRetrySchedulePolicy(policy);
|
|
91
|
-
const at = now.toISOString();
|
|
92
|
-
const firstFailureAt = previous?.firstFailureAt ?? at;
|
|
93
|
-
const episodeDeadlineAt = previous?.episodeDeadlineAt
|
|
94
|
-
?? new Date(now.getTime() + policy.maxWindowMs).toISOString();
|
|
95
|
-
if (now.getTime() >= Date.parse(episodeDeadlineAt)) {
|
|
96
|
-
return Object.freeze({ outcome: "exhausted", reason: "window" });
|
|
97
|
-
}
|
|
98
|
-
const consecutiveFailures = (previous?.consecutiveFailures ?? 0) + 1;
|
|
99
|
-
const dispatchedRetries = previous?.dispatchedRetries ?? 0;
|
|
100
|
-
const schedule = input.scheduleNextAttempt ?? true;
|
|
101
|
-
if (schedule && dispatchedRetries >= policy.delaysMs.length) {
|
|
102
|
-
return Object.freeze({ outcome: "exhausted", reason: "attempts" });
|
|
103
|
-
}
|
|
104
|
-
const state = schedule ? "scheduled" : "blocked";
|
|
105
|
-
const retryAfterMs = input.retryAfterMs;
|
|
106
|
-
if (retryAfterMs !== undefined && (!Number.isSafeInteger(retryAfterMs) || retryAfterMs <= 0)) {
|
|
107
|
-
throw new Error("Provider retry Retry-After must be a positive safe integer.");
|
|
108
|
-
}
|
|
109
|
-
const delayMs = schedule
|
|
110
|
-
? Math.max(nextProviderRetryDelayMs(dispatchedRetries + 1, policy.delaysMs), retryAfterMs ?? 0)
|
|
111
|
-
: undefined;
|
|
112
|
-
const nextAttemptAt = delayMs === undefined
|
|
113
|
-
? undefined
|
|
114
|
-
: new Date(now.getTime() + delayMs).toISOString();
|
|
115
|
-
if (nextAttemptAt !== undefined && Date.parse(nextAttemptAt) > Date.parse(episodeDeadlineAt)) {
|
|
116
|
-
return Object.freeze({
|
|
117
|
-
outcome: "exhausted",
|
|
118
|
-
reason: retryAfterMs === undefined ? "window" : "retry-after-window"
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
const retry = validateAgentRunProviderRetry({
|
|
122
|
-
schemaVersion: 2,
|
|
123
|
-
episodeId: previous?.episodeId ?? createHash("sha256")
|
|
124
|
-
.update(`${firstFailureAt}\0${input.nativeSessionId ?? "unknown"}\0${input.launchId ?? "unknown"}`)
|
|
125
|
-
.digest("hex"),
|
|
126
|
-
failureEventId: requireIdentity(input.failureEventId, "Provider failure event id"),
|
|
127
|
-
policyVersion: 1,
|
|
128
|
-
state,
|
|
129
|
-
errorClass: input.errorClass,
|
|
130
|
-
consecutiveFailures,
|
|
131
|
-
dispatchedRetries,
|
|
132
|
-
maxRetries: policy.delaysMs.length,
|
|
133
|
-
firstFailureAt,
|
|
134
|
-
lastFailureAt: at,
|
|
135
|
-
episodeDeadlineAt,
|
|
136
|
-
...(nextAttemptAt === undefined ? {} : { nextAttemptAt }),
|
|
137
|
-
...(input.launchId === undefined ? {} : { launchId: input.launchId }),
|
|
138
|
-
...(input.nativeSessionId === undefined ? {} : { nativeSessionId: input.nativeSessionId }),
|
|
139
|
-
...(input.failedNativeTurnId === undefined
|
|
140
|
-
? {}
|
|
141
|
-
: { failedNativeTurnId: input.failedNativeTurnId }),
|
|
142
|
-
lastErrorSummary: input.lastErrorSummary
|
|
143
|
-
});
|
|
144
|
-
return Object.freeze({ outcome: schedule ? "scheduled" : "blocked", retry });
|
|
145
|
-
}
|
|
146
|
-
function validateRetrySchedulePolicy(policy) {
|
|
147
|
-
if (!Number.isSafeInteger(policy.maxWindowMs) || policy.maxWindowMs < 1) {
|
|
148
|
-
throw new Error("Provider retry max window must be a positive safe integer.");
|
|
149
|
-
}
|
|
150
|
-
if (policy.delaysMs.length < 1 || policy.delaysMs.length > MAX_PROVIDER_RETRY_ATTEMPTS
|
|
151
|
-
|| policy.delaysMs.some((delay) => !Number.isSafeInteger(delay) || delay < 1)) {
|
|
152
|
-
throw new Error(`Provider retry delay schedule must contain 1-${MAX_PROVIDER_RETRY_ATTEMPTS} positive safe integers.`);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
/** Mark that one short continuation request was dispatched and now awaits any correlated progress. */
|
|
156
|
-
export function prepareProviderRetryDispatch(value, receiptId, now) {
|
|
157
|
-
if (value.state !== "scheduled" || value.nextAttemptAt === undefined)
|
|
158
|
-
return value;
|
|
159
|
-
if (now.getTime() > Date.parse(value.episodeDeadlineAt)) {
|
|
160
|
-
throw new Error("Provider retry episode expired before dispatch.");
|
|
161
|
-
}
|
|
162
|
-
const { nextAttemptAt: _nextAttemptAt, ...rest } = value;
|
|
163
|
-
return validateAgentRunProviderRetry({
|
|
164
|
-
...rest,
|
|
165
|
-
state: "dispatching",
|
|
166
|
-
lastRetryReceiptId: requireIdentity(receiptId, "Provider retry receipt id")
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
export function markProviderRetryDispatched(value) {
|
|
170
|
-
if (value.state !== "dispatching")
|
|
171
|
-
return value;
|
|
172
|
-
return validateAgentRunProviderRetry({
|
|
173
|
-
...value,
|
|
174
|
-
state: "awaiting-progress",
|
|
175
|
-
dispatchedRetries: value.dispatchedRetries + 1
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
export function providerRetryIsDue(value, now) {
|
|
179
|
-
return value.state === "scheduled"
|
|
180
|
-
&& value.nextAttemptAt !== undefined
|
|
181
|
-
&& Date.parse(value.nextAttemptAt) <= now.getTime();
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Next Controller wake for an active automatic retry episode. Scheduled
|
|
185
|
-
* retries wake to dispatch; an in-flight retry wakes only at the episode
|
|
186
|
-
* deadline so silence cannot strand the Run forever. Blocked states are
|
|
187
|
-
* intentionally excluded because they require native/user evidence rather
|
|
188
|
-
* than an automatic lifecycle transition.
|
|
189
|
-
*/
|
|
190
|
-
export function providerRetryWakeAt(value) {
|
|
191
|
-
if (value.state === "scheduled")
|
|
192
|
-
return value.nextAttemptAt ?? null;
|
|
193
|
-
if (value.state === "dispatching" || value.state === "awaiting-progress") {
|
|
194
|
-
return value.episodeDeadlineAt;
|
|
195
|
-
}
|
|
196
|
-
return null;
|
|
197
|
-
}
|
|
198
|
-
/** Delay a control-plane recovery gap without changing either failure counter. */
|
|
199
|
-
export function deferProviderRetry(value, now) {
|
|
200
|
-
const deadline = Date.parse(value.episodeDeadlineAt);
|
|
201
|
-
if (now.getTime() >= deadline)
|
|
202
|
-
return null;
|
|
203
|
-
const { lastRetryReceiptId: _receipt, nextAttemptAt: _next, ...rest } = value;
|
|
204
|
-
return validateAgentRunProviderRetry({
|
|
205
|
-
...rest,
|
|
206
|
-
state: "scheduled",
|
|
207
|
-
nextAttemptAt: new Date(Math.min(now.getTime() + 15_000, deadline)).toISOString()
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
/** Short recovery instruction; it cannot repeat the Task Assignment body. */
|
|
211
|
-
export function serializeProviderRetryEnvelope(input) {
|
|
212
|
-
validateAgentRunProviderRetry(input.retry);
|
|
213
|
-
const retryOrdinal = input.retry.state === "dispatching"
|
|
214
|
-
? input.retry.dispatchedRetries + 1
|
|
215
|
-
: input.retry.dispatchedRetries;
|
|
216
|
-
return [
|
|
217
|
-
"Yui managed in-Session continuation retry.",
|
|
218
|
-
`task=${requireIdentity(input.taskId, "Provider retry task id")} run=${requireIdentity(input.runId, "Provider retry run id")} role=${requireIdentity(input.roleName, "Provider retry role")}`,
|
|
219
|
-
`episode=${input.retry.episodeId} retry=${retryOrdinal}/${input.retry.maxRetries} receipt=${input.retry.lastRetryReceiptId ?? "pending"}`,
|
|
220
|
-
`failureEvent=${input.retry.failureEventId}`,
|
|
221
|
-
...(input.retry.failedNativeTurnId === undefined
|
|
222
|
-
? []
|
|
223
|
-
: [`retryOfTurn=${input.retry.failedNativeTurnId}`]),
|
|
224
|
-
"Continue the existing native conversation from its latest accepted state. Do not repeat completed work; load exact Run deltas if needed."
|
|
225
|
-
].join("\n");
|
|
226
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { resolveProviderRetryAdapters, resolveProviderRetryDelaysSeconds, resolveProviderRetryMaxWindowSeconds, resolveProviderRetryMode } from "../config/yuiConfig.js";
|
|
2
|
-
/**
|
|
3
|
-
* Resolves the retry flags from the durable Yui config. Homes without the
|
|
4
|
-
* fields get the safe defaults: enforce mode, all supported adapters, receipt
|
|
5
|
-
* replay on, 10-minute budget.
|
|
6
|
-
*/
|
|
7
|
-
export function providerRetryConfig(config) {
|
|
8
|
-
const mode = resolveProviderRetryMode(config.providerRetryMode);
|
|
9
|
-
const adapters = resolveProviderRetryAdapters(config.providerRetryAdapters);
|
|
10
|
-
return {
|
|
11
|
-
mode: adapters.length === 0 ? "off" : mode,
|
|
12
|
-
adapters,
|
|
13
|
-
delaysMs: resolveProviderRetryDelaysSeconds(config.providerRetryDelaysSeconds)
|
|
14
|
-
.map((seconds) => seconds * 1_000),
|
|
15
|
-
maxWindowMs: resolveProviderRetryMaxWindowSeconds(config.providerRetryMaxWindowSeconds)
|
|
16
|
-
* 1_000
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
/** Whether the adapter has in-place retry enabled in the given mode. */
|
|
20
|
-
export function providerRetryEnabledForAdapter(config, adapterId, mode) {
|
|
21
|
-
return config.mode === mode
|
|
22
|
-
&& providerRetryAdapterEnabled(config, adapterId);
|
|
23
|
-
}
|
|
24
|
-
/** Default admission is capability-driven, not a hard-coded Provider list. */
|
|
25
|
-
export function providerRetryAdapterEnabled(config, adapterId) {
|
|
26
|
-
return config.adapters === "all-capable" || config.adapters.includes(adapterId);
|
|
27
|
-
}
|