@zq-silk/yui 0.13.8 → 0.13.10
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/dist/storage/upgrade/sqliteStateMigration.js +17 -9
- 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);
|
|
@@ -29,7 +29,7 @@ import { mailboxTargetKey } from "../../coordination/workMailbox.js";
|
|
|
29
29
|
import { managedWorkspaceKey } from "../../worktree/managedWorkspace.js";
|
|
30
30
|
import { SqliteTaskStore } from "../sqliteStore.js";
|
|
31
31
|
import { readStorageSchemaManifest } from "../storageSchema.js";
|
|
32
|
-
import { CURRENT_STORED_TASK_SCHEMA_VERSION } from "../taskStore.js";
|
|
32
|
+
import { CURRENT_STORED_TASK_SCHEMA_VERSION, CURRENT_WORK_MAILBOX_SCHEMA_VERSION } from "../taskStore.js";
|
|
33
33
|
/** The sidecar database filename used during staging. */
|
|
34
34
|
export const STAGED_DATABASE_FILENAME = "yui.db.staged";
|
|
35
35
|
/** The committed database filename. */
|
|
@@ -62,7 +62,8 @@ export function readMigrationSourceStateFromSqlite(home) {
|
|
|
62
62
|
// Physical migration 14 rewrites the WorkMailbox family while adding its
|
|
63
63
|
// current columns. Preserve the source rows before applying it so the
|
|
64
64
|
// logical record migration still sees and transforms v1 exactly once.
|
|
65
|
-
const
|
|
65
|
+
const sourceManifest = readStorageSchemaManifest(home);
|
|
66
|
+
const sourceMailboxes = readWorkMailboxesFromSqlite(snapshotPath, requireWorkMailboxSchemaVersion(sourceManifest.recordVersions?.workMailbox));
|
|
66
67
|
const stagedStore = new SqliteTaskStore(home, {
|
|
67
68
|
databaseFilename: MIGRATION_SOURCE_DATABASE_FILENAME,
|
|
68
69
|
migration: true
|
|
@@ -596,7 +597,7 @@ function hashPayloadTable(db, sql) {
|
|
|
596
597
|
const rows = db.prepare(sql).all();
|
|
597
598
|
return hashRecords(rows.map((row) => JSON.parse(row.payload)));
|
|
598
599
|
}
|
|
599
|
-
export function rowToMailbox(row) {
|
|
600
|
+
export function rowToMailbox(row, schemaVersion) {
|
|
600
601
|
let target;
|
|
601
602
|
switch (row.target_kind) {
|
|
602
603
|
case "operator":
|
|
@@ -623,9 +624,9 @@ export function rowToMailbox(row) {
|
|
|
623
624
|
processing: row.processing === null ? null : JSON.parse(row.processing),
|
|
624
625
|
pending: row.pending === null ? null : JSON.parse(row.pending)
|
|
625
626
|
};
|
|
626
|
-
return
|
|
627
|
+
return schemaVersion > 1
|
|
627
628
|
? {
|
|
628
|
-
schemaVersion
|
|
629
|
+
schemaVersion,
|
|
629
630
|
...common,
|
|
630
631
|
inputDelivery: row.input_delivery === null
|
|
631
632
|
? null
|
|
@@ -633,16 +634,22 @@ export function rowToMailbox(row) {
|
|
|
633
634
|
}
|
|
634
635
|
: { schemaVersion: 1, ...common };
|
|
635
636
|
}
|
|
637
|
+
function requireWorkMailboxSchemaVersion(value) {
|
|
638
|
+
if (value !== 1 && value !== 2 && value !== 3) {
|
|
639
|
+
throw new Error(`WorkMailbox manifest version is invalid: ${String(value)}.`);
|
|
640
|
+
}
|
|
641
|
+
return value;
|
|
642
|
+
}
|
|
636
643
|
function readWorkMailboxRows(db) {
|
|
637
644
|
const hasInputDelivery = db.prepare("PRAGMA table_info(mailboxes)").all().some(({ name }) => name === "input_delivery");
|
|
638
645
|
return db.prepare(`SELECT target_kind, task_id, role_name, next_sequence, processing, pending${hasInputDelivery ? ", input_delivery" : ""} FROM mailboxes ORDER BY target_key`).all();
|
|
639
646
|
}
|
|
640
|
-
function readWorkMailboxesFromSqlite(dbPath) {
|
|
647
|
+
function readWorkMailboxesFromSqlite(dbPath, schemaVersion) {
|
|
641
648
|
const db = new Database(dbPath, { readonly: true });
|
|
642
649
|
try {
|
|
643
650
|
const mailboxes = {};
|
|
644
651
|
for (const row of readWorkMailboxRows(db)) {
|
|
645
|
-
const mailbox = rowToMailbox(row);
|
|
652
|
+
const mailbox = rowToMailbox(row, schemaVersion);
|
|
646
653
|
mailboxes[mailboxTargetKey(mailbox.target)] = mailbox;
|
|
647
654
|
}
|
|
648
655
|
return mailboxes;
|
|
@@ -699,7 +706,7 @@ export function computeDbFamilyChecksums(home, databaseFilename) {
|
|
|
699
706
|
checksums.publicationReference = hashPayloadTable(db, "SELECT payload FROM publication_references");
|
|
700
707
|
// Mailboxes are reconstructed from typed columns (no payload column).
|
|
701
708
|
const mailboxRows = readWorkMailboxRows(db);
|
|
702
|
-
checksums.workMailbox = hashRecords(mailboxRows.map(rowToMailbox));
|
|
709
|
+
checksums.workMailbox = hashRecords(mailboxRows.map((row) => rowToMailbox(row, CURRENT_WORK_MAILBOX_SCHEMA_VERSION)));
|
|
703
710
|
return checksums;
|
|
704
711
|
}
|
|
705
712
|
finally {
|
|
@@ -756,6 +763,7 @@ export function readStateFromSqlite(home, databaseFilename = COMMITTED_DATABASE_
|
|
|
756
763
|
const storedTaskVersion = typeof manifest.recordVersions?.storedTask === "number"
|
|
757
764
|
? manifest.recordVersions.storedTask
|
|
758
765
|
: CURRENT_STORED_TASK_SCHEMA_VERSION;
|
|
766
|
+
const storedWorkMailboxVersion = requireWorkMailboxSchemaVersion(manifest.recordVersions?.workMailbox);
|
|
759
767
|
// Home identity + revision continuity.
|
|
760
768
|
const meta = db.prepare("SELECT home_identity, revision FROM home_meta WHERE id = 1").get();
|
|
761
769
|
const state = {
|
|
@@ -880,7 +888,7 @@ export function readStateFromSqlite(home, databaseFilename = COMMITTED_DATABASE_
|
|
|
880
888
|
const mailboxRows = readWorkMailboxRows(db);
|
|
881
889
|
const mailboxes = state.mailboxes;
|
|
882
890
|
for (const row of mailboxRows) {
|
|
883
|
-
const mailbox = rowToMailbox(row);
|
|
891
|
+
const mailbox = rowToMailbox(row, storedWorkMailboxVersion);
|
|
884
892
|
mailboxes[mailboxTargetKey(mailbox.target)] = mailbox;
|
|
885
893
|
}
|
|
886
894
|
return state;
|
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
|
-
}
|