@zq-silk/yui 0.2.0 → 0.4.2
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/ARCHITECTURE.md +603 -133
- package/README.md +806 -31
- package/dist/agent/agent.js +2 -1
- package/dist/agent/argumentPolicy.js +3 -1
- package/dist/agent/launchEnvironment.js +106 -0
- package/dist/agent/managedRuntimeEnvironment.js +34 -0
- package/dist/brief/taskBrief.js +11 -1
- package/dist/cli/agentConfigurationPicker.js +287 -0
- package/dist/cli/commandCatalog.js +488 -60
- package/dist/cli/completion.js +146 -22
- package/dist/cli/helpRenderer.js +3 -1
- package/dist/cli/interactionCandidates.js +53 -15
- package/dist/cli/interactionPolicy.js +267 -30
- package/dist/cli/interactiveSelection.js +6 -2
- package/dist/cli/invocationRouter.js +5 -1
- package/dist/cli/operatorWizard.js +87 -0
- package/dist/cli/roleOptionCatalog.js +1 -0
- package/dist/cli/roleWizard.js +185 -21
- package/dist/cli/updateCommand.js +62 -19
- package/dist/cli/updateOrchestrator.js +539 -0
- package/dist/cli/updatePorts.js +1119 -0
- package/dist/cli/upgradeCommand.js +112 -0
- package/dist/cli.js +1420 -86
- package/dist/commands/agentCommands.js +146 -3
- package/dist/commands/configCommands.js +126 -0
- package/dist/commands/controllerCommands.js +365 -0
- package/dist/commands/globalRoleCommands.js +168 -126
- package/dist/commands/jobCommands.js +18 -8
- package/dist/commands/operatorCommands.js +159 -9
- package/dist/commands/profileCommands.js +203 -0
- package/dist/commands/projectCommands.js +650 -0
- package/dist/commands/roleConfiguration.js +85 -24
- package/dist/commands/roleRuntimeGuard.js +12 -0
- package/dist/commands/roleSkillValidation.js +47 -0
- package/dist/commands/taskActor.js +127 -0
- package/dist/commands/taskCommands.js +4201 -313
- package/dist/commands/taskCompletionGate.js +131 -0
- package/dist/commands/taskContextCommand.js +244 -30
- package/dist/commands/taskInputCommands.js +177 -59
- package/dist/commands/taskIntegrationCommands.js +303 -0
- package/dist/commands/taskOverviewCommand.js +363 -0
- package/dist/commands/taskRoleRuntimeStatus.js +125 -19
- package/dist/commands/textInput.js +15 -0
- package/dist/completion/completionInstaller.js +26 -22
- package/dist/config/yuiConfig.js +4 -3
- package/dist/context/dispatchContext.js +90 -38
- package/dist/context/roleSessionContext.js +119 -0
- package/dist/controller/claudeLifecycleHook.js +203 -0
- package/dist/controller/clientRuntime.js +408 -56
- package/dist/controller/codexLifecycleHook.js +108 -0
- package/dist/controller/controller.js +1089 -32
- package/dist/controller/domainIdentity.js +505 -0
- package/dist/controller/ephemeralResourceReaper.js +131 -0
- package/dist/controller/fileSchedulerStoreAdapter.js +2153 -103
- package/dist/controller/providerHookRunFence.js +127 -0
- package/dist/controller/resourceCleanupLinux.js +286 -0
- package/dist/controller/resourceInventory.js +531 -0
- package/dist/controller/resourceInventoryLinux.js +610 -0
- package/dist/controller/runtime.js +629 -10
- package/dist/controller/runtimeEventInbox.js +564 -0
- package/dist/controller/runtimeEventProcessor.js +248 -0
- package/dist/controller/runtimeLaunchCoordinator.js +477 -0
- package/dist/controller/sessionNotify.js +121 -78
- package/dist/coordination/deadlineScheduler.js +15 -0
- package/dist/coordination/mailboxScheduler.js +108 -0
- package/dist/coordination/workMailbox.js +329 -0
- package/dist/coordination/workMailboxQueue.js +86 -0
- package/dist/core/controllerClient.js +19 -5
- package/dist/core/controllerEndpoint.js +37 -0
- package/dist/core/controllerServer.js +218 -10
- package/dist/core/protocol.js +6 -2
- package/dist/decision/decision.js +2 -1
- package/dist/doctor/doctor.js +681 -32
- package/dist/domain/validation.js +53 -0
- package/dist/errors/cliError.js +5 -3
- package/dist/event/taskEvent.js +7 -3
- package/dist/execution/codexThreadNaming.js +160 -0
- package/dist/execution/executionGroup.js +579 -0
- package/dist/executor/agentAdapter.js +255 -40
- package/dist/executor/agentConfigurationCatalog.js +326 -0
- package/dist/executor/agentConfigurationProbe.js +506 -0
- package/dist/executor/agentExecutor.js +625 -10
- package/dist/executor/codexConfigConflict.js +290 -0
- package/dist/executor/effectiveLaunch.js +340 -0
- package/dist/executor/executorRegistry.js +238 -36
- package/dist/executor/fileRoleLaunchPlanner.js +550 -40
- package/dist/executor/turnCompletion.js +126 -0
- package/dist/input/inputRequest.js +30 -9
- package/dist/integration/changeSet.js +36 -0
- package/dist/integration/checkResult.js +24 -0
- package/dist/integration/gitIntegrationService.js +695 -0
- package/dist/integration/integrationAttempt.js +142 -0
- package/dist/interaction/operatorPresentation.js +96 -0
- package/dist/lifecycle/canonicalLifecycleEvent.js +342 -0
- package/dist/lifecycle/exactRunTerminalization.js +572 -0
- package/dist/lifecycle/providerLifecycleMapping.js +190 -0
- package/dist/lifecycle/taskRoleSessionReset.js +124 -0
- package/dist/message/message.js +23 -7
- package/dist/milestone/milestone.js +2 -1
- package/dist/operator/operatorSessionHistory.js +124 -0
- package/dist/output/agentConfigurationPresentation.js +43 -0
- package/dist/output/rolePresentation.js +34 -10
- package/dist/output/terminal.js +8 -0
- package/dist/output/timePresentation.js +55 -0
- package/dist/profile/agentProfile.js +128 -0
- package/dist/repository/gitWorkspace.js +578 -24
- package/dist/repository/project.js +213 -0
- package/dist/repository/taskWorkspaceCoordinator.js +392 -0
- package/dist/repository/taskWorkspacePreparer.js +1688 -191
- package/dist/review/reviewConfig.js +11 -0
- package/dist/review/reviewRound.js +399 -0
- package/dist/review/taskFinalReviewContract.js +90 -0
- package/dist/role/role.js +124 -23
- package/dist/run/agentRun.js +155 -12
- package/dist/run/runIdentity.js +82 -0
- package/dist/runtime/exactControlPlane.js +472 -0
- package/dist/runtime/index.js +8 -0
- package/dist/runtime/lifecycleReservation.js +38 -0
- package/dist/runtime/ports.js +11 -0
- package/dist/runtime/preallocatedNativeSession.js +13 -0
- package/dist/runtime/promptEnvelope.js +30 -0
- package/dist/runtime/runtimeBinding.js +31 -0
- package/dist/runtime/runtimeOwner.js +14 -0
- package/dist/runtime/sessionLaunchRequest.js +62 -0
- package/dist/runtime/sessionTitle.js +54 -0
- package/dist/runtime/taskRuntimeIsolation.js +643 -0
- package/dist/runtime/tmuxAdapters.js +315 -0
- package/dist/runtime/turnCompletion.js +3 -0
- package/dist/runtime/validation.js +23 -0
- package/dist/scheduler/activeRoleRunDelivery.js +342 -32
- package/dist/scheduler/activeTaskProgress.js +63 -0
- package/dist/scheduler/leaderFailure.js +2 -1
- package/dist/scheduler/leaderWakeupProcessor.js +307 -66
- package/dist/scheduler/operatorInputNotificationProcessor.js +109 -46
- package/dist/scheduler/operatorNotification.js +44 -2
- package/dist/scheduler/ports.js +28 -1
- package/dist/scheduler/roleRunLiveness.js +131 -25
- package/dist/scheduler/roleRunStall.js +951 -0
- package/dist/scheduler/taskExecutionProjection.js +544 -0
- package/dist/scheduler/wakeupQueue.js +3 -0
- package/dist/setup/setupCommand.js +302 -52
- package/dist/storage/compatibleTaskStore.js +102 -0
- package/dist/storage/migration/baseline.js +78 -0
- package/dist/storage/migration/classifier.js +51 -0
- package/dist/storage/migration/compatibleCodec.js +53 -0
- package/dist/storage/migration/engine.js +147 -0
- package/dist/storage/migration/index.js +33 -0
- package/dist/storage/migration/planner.js +154 -0
- package/dist/storage/migration/productionRegistry.js +486 -0
- package/dist/storage/migration/registry.js +169 -0
- package/dist/storage/migration/report.js +54 -0
- package/dist/storage/migration/types.js +31 -0
- package/dist/storage/storageSchema.js +147 -123
- package/dist/storage/storageVersions.js +11 -0
- package/dist/storage/taskStore.js +1793 -197
- package/dist/storage/upgrade/homeClassification.js +156 -0
- package/dist/storage/upgrade/homeMigrationTarget.js +595 -0
- package/dist/storage/upgrade/offlineUpgradeInventory.js +315 -0
- package/dist/storage/upgrade/productionMigrationRegistry.js +6 -0
- package/dist/storage/upgrade/recordVersionScan.js +176 -0
- package/dist/storage/upgrade/recordVersions.js +159 -0
- package/dist/storage/upgrade/switchProgress.js +80 -0
- package/dist/storage/upgrade/upgradeOrchestrator.js +948 -0
- package/dist/storage/upgrade/upgradeReceipt.js +161 -0
- package/dist/storage/upgradeCoordination.js +186 -0
- package/dist/storage/upgradeFence.js +366 -0
- package/dist/task/task.js +132 -26
- package/dist/task/taskRecordReference.js +66 -0
- package/dist/tmux/commandExecutor.js +75 -2
- package/dist/tmux/tmuxManager.js +747 -49
- package/dist/version.js +23 -0
- package/dist/web/assets/assetManifest.js +62 -0
- package/dist/web/assets/client/app.js +631 -0
- package/dist/web/assets/client/components.js +605 -0
- package/dist/web/assets/client/dom.js +14 -0
- package/dist/web/assets/client/format.js +28 -0
- package/dist/web/assets/client/i18n.js +494 -0
- package/dist/web/assets/client/markdown.js +114 -0
- package/dist/web/assets/client/theme.js +32 -0
- package/dist/web/assets/client/view.js +458 -0
- package/dist/web/assets/fontData.js +12 -0
- package/dist/web/assets/fonts.js +12 -0
- package/dist/web/assets/shell.js +114 -0
- package/dist/web/assets/styles/cards.js +135 -0
- package/dist/web/assets/styles/layout.js +47 -0
- package/dist/web/assets/styles/markdown.js +29 -0
- package/dist/web/assets/styles/responsive.js +39 -0
- package/dist/web/assets/styles/tokens.js +101 -0
- package/dist/web/assets/styles/widgets.js +147 -0
- package/dist/web/tmuxWebTerminal.js +158 -0
- package/dist/web/webServer.js +463 -0
- package/dist/web/webSnapshot.js +148 -0
- package/dist/workItem/workItem.js +642 -23
- package/dist/workspace/gitChangeSetCapture.js +86 -0
- package/dist/workspace/workItemChangeSetManager.js +445 -0
- package/dist/worktree/managedWorkspace.js +202 -0
- package/docs/task-local-identity.md +62 -0
- package/i18n/README.zh-CN.md +406 -31
- package/package.json +10 -2
- package/skills/yui-leader/SKILL.md +601 -39
- package/skills/yui-operator/SKILL.md +255 -34
- package/skills/yui-reviewer/SKILL.md +57 -0
- package/skills/yui-worker/SKILL.md +214 -17
- package/dist/commands/repositoryCommands.js +0 -86
- package/dist/operator/operatorContext.js +0 -66
- package/dist/repository/repository.js +0 -55
- package/dist/scheduler/archivedTaskRuntime.js +0 -12
- package/dist/worktree/roleWorkspace.js +0 -62
|
@@ -1,20 +1,31 @@
|
|
|
1
|
+
import { selectedSchedulerRoles, selectedSchedulerTasks } from "./ports.js";
|
|
2
|
+
import { isSchedulerTaskWorkspaceReady } from "./ports.js";
|
|
3
|
+
import { formatAgentRunReceiptId } from "../task/taskRecordReference.js";
|
|
4
|
+
import { effectiveLaunchSnapshotsCompatible } from "../executor/effectiveLaunch.js";
|
|
5
|
+
import { RuntimeLaunchError } from "../runtime/ports.js";
|
|
6
|
+
import { isPreInputReadinessSupported } from "../lifecycle/canonicalLifecycleEvent.js";
|
|
7
|
+
import { preInputReadinessCapability } from "../lifecycle/providerLifecycleMapping.js";
|
|
1
8
|
/**
|
|
2
9
|
* Delivers durable Work AgentRuns before liveness reconciliation. Task command
|
|
3
10
|
* handlers only record intent; this Controller path is the sole automated
|
|
4
11
|
* route into the Agent terminal, through tmux receipt-backed delivery.
|
|
5
12
|
*/
|
|
6
|
-
export async function processActiveRoleRunDeliveries(store, delivery, now) {
|
|
13
|
+
export async function processActiveRoleRunDeliveries(store, delivery, now, selection) {
|
|
7
14
|
const results = [];
|
|
8
|
-
for (const task of store
|
|
15
|
+
for (const task of selectedSchedulerTasks(store, selection)) {
|
|
9
16
|
if (task.status !== "active")
|
|
10
17
|
continue;
|
|
11
|
-
for (const role of store
|
|
18
|
+
for (const role of selectedSchedulerRoles(store, task.id, selection)) {
|
|
12
19
|
const run = store.getActiveAgentRun(task.id, role.name);
|
|
13
20
|
// A crash after a Leader wake is durably claimed but before tmux input
|
|
14
|
-
// is recoverable through the same receipt-backed delivery path.
|
|
15
|
-
|
|
21
|
+
// is recoverable through the same receipt-backed delivery path. The
|
|
22
|
+
// re-push guard keys on pushedAt (transport), not deliveredAt (provider
|
|
23
|
+
// acceptance): a pushed-but-unaccepted Run must never be pushed twice —
|
|
24
|
+
// no duplicate Enter while acceptance is still pending.
|
|
25
|
+
if (run === null || run.pushedAt !== undefined)
|
|
16
26
|
continue;
|
|
17
|
-
|
|
27
|
+
const taskWorkspace = store.getTaskWorkspace(task.id);
|
|
28
|
+
if (!isSchedulerTaskWorkspaceReady(task, taskWorkspace)) {
|
|
18
29
|
results.push({
|
|
19
30
|
taskId: task.id,
|
|
20
31
|
roleName: role.name,
|
|
@@ -24,66 +35,355 @@ export async function processActiveRoleRunDeliveries(store, delivery, now) {
|
|
|
24
35
|
});
|
|
25
36
|
continue;
|
|
26
37
|
}
|
|
27
|
-
const existingSession = store.getRoleSession(task.id, role.name);
|
|
28
|
-
const receiptId =
|
|
38
|
+
const existingSession = store.getRoleSession(task.id, role.name, run.effective.agentId);
|
|
39
|
+
const receiptId = formatAgentRunReceiptId(task.id, run.id);
|
|
40
|
+
const target = { kind: "role", taskId: task.id, roleName: role.name };
|
|
41
|
+
const claim = store.claimWorkMailbox({
|
|
42
|
+
target,
|
|
43
|
+
batchId: receiptId,
|
|
44
|
+
owner: "controller",
|
|
45
|
+
now,
|
|
46
|
+
executionRef: { type: "run", taskId: task.id, id: run.id }
|
|
47
|
+
});
|
|
48
|
+
if (claim.status === "empty") {
|
|
49
|
+
results.push({
|
|
50
|
+
taskId: task.id,
|
|
51
|
+
roleName: role.name,
|
|
52
|
+
runId: run.id,
|
|
53
|
+
status: "skipped",
|
|
54
|
+
reason: "mailbox-empty"
|
|
55
|
+
});
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const processing = claim.processing;
|
|
59
|
+
if (processing.executionRef?.type !== "run"
|
|
60
|
+
|| processing.executionRef.taskId !== task.id
|
|
61
|
+
|| processing.executionRef.id !== run.id) {
|
|
62
|
+
results.push({
|
|
63
|
+
taskId: task.id,
|
|
64
|
+
roleName: role.name,
|
|
65
|
+
runId: run.id,
|
|
66
|
+
status: "skipped",
|
|
67
|
+
reason: "mailbox-busy"
|
|
68
|
+
});
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
let prepared;
|
|
72
|
+
let preparedSession = existingSession;
|
|
73
|
+
let preStartFencePersisted = false;
|
|
74
|
+
let deliveryAttempted = false;
|
|
29
75
|
try {
|
|
30
76
|
const nativeSessionId = run.mode === "resume"
|
|
31
|
-
? requireResumeSession(role, existingSession)
|
|
77
|
+
? requireResumeSession(role, run.effective, existingSession)
|
|
32
78
|
: undefined;
|
|
33
|
-
|
|
79
|
+
prepared = await delivery.prepareRoleSession({
|
|
34
80
|
taskId: task.id,
|
|
35
81
|
roleName: role.name,
|
|
36
|
-
agentId:
|
|
37
|
-
adapterId:
|
|
82
|
+
agentId: run.effective.agentId,
|
|
83
|
+
adapterId: run.effective.adapterId,
|
|
84
|
+
effective: run.effective,
|
|
85
|
+
workspace: run.effective.workspace.root,
|
|
86
|
+
...(run.workspace === undefined
|
|
87
|
+
? {}
|
|
88
|
+
: { managedWorkspace: run.workspace }),
|
|
38
89
|
mode: run.mode,
|
|
39
|
-
|
|
90
|
+
runId: run.id,
|
|
91
|
+
...(nativeSessionId === undefined ? {} : { nativeSessionId }),
|
|
92
|
+
beforeHostStart: (preflight) => {
|
|
93
|
+
persistPreStartFence(store, task, role, run, existingSession, preflight, now);
|
|
94
|
+
preparedSession = preflightSession(role, run.effective, existingSession, run.mode, preflight);
|
|
95
|
+
preStartFencePersisted = true;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
// A fresh Codex host may already carry the exact Run prompt in its
|
|
99
|
+
// launch argv. Once preparation returns that transport fact, any
|
|
100
|
+
// later readiness or aggregate-write failure is delivery uncertainty,
|
|
101
|
+
// not a launch failure: preserve the Run and its reservation for the
|
|
102
|
+
// matching provider Hook instead of terminalizing it.
|
|
103
|
+
deliveryAttempted = prepared.inputSubmittedAtLaunch === true;
|
|
104
|
+
// Preparation may already have an exact native Session identity while
|
|
105
|
+
// the provider is still starting. Persist that Session + the Run fence
|
|
106
|
+
// before awaiting readiness so a pre-input provider Hook can validate
|
|
107
|
+
// the complete generation (including receipt) instead of racing an
|
|
108
|
+
// in-memory boundary. A delivery adapter that cannot expose a
|
|
109
|
+
// pre-readiness Session falls back to the existing durable Session;
|
|
110
|
+
// fresh runtime-discovered providers intentionally persist `null`.
|
|
111
|
+
if (!preStartFencePersisted) {
|
|
112
|
+
const preparedFenceSession = prepared.session === undefined
|
|
113
|
+
? existingSession
|
|
114
|
+
: validateReadySession(role, run.effective, existingSession, run.mode, { prepared, session: prepared.session });
|
|
115
|
+
preparedSession = preparedFenceSession;
|
|
116
|
+
store.saveRoleRunPrepared({
|
|
117
|
+
task,
|
|
118
|
+
role,
|
|
119
|
+
run,
|
|
120
|
+
session: preparedFenceSession,
|
|
121
|
+
...(prepared.launchId === undefined ? {} : { launchId: prepared.launchId }),
|
|
122
|
+
now
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
const ready = await delivery.waitUntilReady(prepared);
|
|
126
|
+
deliveryAttempted = deliveryAttempted
|
|
127
|
+
|| ready.prepared.inputSubmittedAtLaunch === true;
|
|
128
|
+
const session = validateReadySession(role, run.effective, existingSession, run.mode, ready);
|
|
129
|
+
preparedSession = session;
|
|
130
|
+
store.saveRoleRunPrepared({
|
|
131
|
+
task,
|
|
132
|
+
role,
|
|
133
|
+
run,
|
|
134
|
+
session,
|
|
135
|
+
...(ready.prepared.launchId === undefined
|
|
136
|
+
? {}
|
|
137
|
+
: { launchId: ready.prepared.launchId }),
|
|
138
|
+
now
|
|
40
139
|
});
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
140
|
+
if (ready.prepared.inputSubmittedAtLaunch === true) {
|
|
141
|
+
// The exact Run prompt was already carried by the newly-created
|
|
142
|
+
// Provider command. Persist transport success without writing any
|
|
143
|
+
// terminal bytes; only the later matching Provider Hook may mark
|
|
144
|
+
// the Run delivered/accepted.
|
|
145
|
+
store.saveRoleRunDelivery({
|
|
146
|
+
task,
|
|
147
|
+
role,
|
|
148
|
+
run,
|
|
149
|
+
session,
|
|
150
|
+
...(ready.prepared.launchId === undefined
|
|
151
|
+
? {}
|
|
152
|
+
: { launchId: ready.prepared.launchId }),
|
|
153
|
+
now
|
|
154
|
+
});
|
|
155
|
+
delivery.forgetPrepared?.({
|
|
156
|
+
taskId: task.id,
|
|
157
|
+
roleName: role.name,
|
|
158
|
+
runId: run.id,
|
|
159
|
+
...(ready.prepared.launchId === undefined
|
|
160
|
+
? {}
|
|
161
|
+
: { launchId: ready.prepared.launchId })
|
|
162
|
+
});
|
|
163
|
+
results.push({
|
|
164
|
+
taskId: task.id,
|
|
165
|
+
roleName: role.name,
|
|
166
|
+
runId: run.id,
|
|
167
|
+
status: "delivered"
|
|
168
|
+
});
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
// Pre-input readiness gate. For an adapter whose capability proves a
|
|
172
|
+
// native event fires before the first prompt (e.g. Claude SessionStart),
|
|
173
|
+
// a freshly-started host must not be pushed until that provider-ready
|
|
174
|
+
// fact has been folded. Unsupported adapters (e.g. Codex) have no
|
|
175
|
+
// pre-input event, so their push proceeds and acceptance is confirmed
|
|
176
|
+
// only by the later exact provider-accepted fold. The gate reads the
|
|
177
|
+
// adapter capability and the durable ready projection — never a sleep,
|
|
178
|
+
// screen scrape, or pane/PID inference — and fails closed for a
|
|
179
|
+
// supported adapter whose readiness cannot be confirmed.
|
|
180
|
+
if (ready.prepared.sessionStarted
|
|
181
|
+
&& isPreInputReadinessSupported(preInputReadinessCapability(run.effective.adapterId))
|
|
182
|
+
&& !providerReadyForPush(store, {
|
|
183
|
+
taskId: task.id,
|
|
184
|
+
roleName: role.name,
|
|
185
|
+
agentId: run.effective.agentId,
|
|
186
|
+
...(ready.prepared.launchId === undefined ? {} : { launchId: ready.prepared.launchId }),
|
|
187
|
+
...(session?.nativeSessionId === undefined
|
|
188
|
+
? {}
|
|
189
|
+
: { nativeSessionId: session.nativeSessionId })
|
|
190
|
+
})) {
|
|
191
|
+
results.push({
|
|
192
|
+
taskId: task.id,
|
|
193
|
+
roleName: role.name,
|
|
194
|
+
runId: run.id,
|
|
195
|
+
status: "skipped",
|
|
196
|
+
reason: "not-ready"
|
|
197
|
+
});
|
|
198
|
+
continue;
|
|
54
199
|
}
|
|
55
|
-
|
|
200
|
+
deliveryAttempted = true;
|
|
201
|
+
const outcome = await delivery.sendOnce({
|
|
202
|
+
delivery: ready,
|
|
203
|
+
receiptId,
|
|
204
|
+
text: run.input
|
|
205
|
+
});
|
|
206
|
+
if (outcome === "busy" || outcome === "unavailable") {
|
|
207
|
+
results.push({
|
|
208
|
+
taskId: task.id,
|
|
209
|
+
roleName: role.name,
|
|
210
|
+
runId: run.id,
|
|
211
|
+
status: "skipped",
|
|
212
|
+
reason: outcome === "busy" ? "not-ready" : "runtime-unavailable",
|
|
213
|
+
terminalFailure: roleRunDeliveryFailure(run, processing.batchId, session, ready.prepared.launchId)
|
|
214
|
+
});
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
const status = outcome === "sent" ? "delivered" : "already-delivered";
|
|
218
|
+
store.saveRoleRunDelivery({
|
|
219
|
+
task,
|
|
220
|
+
role,
|
|
221
|
+
run,
|
|
222
|
+
session,
|
|
223
|
+
...(ready.prepared.launchId === undefined
|
|
224
|
+
? {}
|
|
225
|
+
: { launchId: ready.prepared.launchId }),
|
|
226
|
+
now
|
|
227
|
+
});
|
|
56
228
|
results.push({ taskId: task.id, roleName: role.name, runId: run.id, status });
|
|
57
229
|
}
|
|
58
230
|
catch (error) {
|
|
231
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
232
|
+
if (error instanceof RuntimeLaunchError) {
|
|
233
|
+
const terminalFailure = roleRunDeliveryFailure(run, processing.batchId, existingSession, error.launchId);
|
|
234
|
+
if (error.retryable) {
|
|
235
|
+
results.push({
|
|
236
|
+
taskId: task.id,
|
|
237
|
+
roleName: role.name,
|
|
238
|
+
runId: run.id,
|
|
239
|
+
status: "skipped",
|
|
240
|
+
reason: "runtime-unavailable",
|
|
241
|
+
error: message,
|
|
242
|
+
terminalFailure
|
|
243
|
+
});
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
const persisted = store.saveRoleRunDeliveryFailure({
|
|
247
|
+
...terminalFailure,
|
|
248
|
+
now
|
|
249
|
+
});
|
|
250
|
+
if (persisted === "failed") {
|
|
251
|
+
delivery.forgetPrepared?.({
|
|
252
|
+
taskId: task.id,
|
|
253
|
+
roleName: role.name,
|
|
254
|
+
runId: run.id,
|
|
255
|
+
launchId: error.launchId
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
results.push({
|
|
259
|
+
taskId: task.id,
|
|
260
|
+
roleName: role.name,
|
|
261
|
+
runId: run.id,
|
|
262
|
+
status: "failed",
|
|
263
|
+
reason: "generation-lost",
|
|
264
|
+
error: persisted === "state-changed"
|
|
265
|
+
? "Run state changed during exact launch generation failure."
|
|
266
|
+
: message,
|
|
267
|
+
terminalized: persisted === "failed"
|
|
268
|
+
});
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
if (!deliveryAttempted) {
|
|
272
|
+
delivery.forgetPrepared?.({
|
|
273
|
+
taskId: task.id,
|
|
274
|
+
roleName: role.name,
|
|
275
|
+
runId: run.id,
|
|
276
|
+
...(prepared?.launchId === undefined
|
|
277
|
+
? {}
|
|
278
|
+
: { launchId: prepared.launchId })
|
|
279
|
+
});
|
|
280
|
+
const persisted = store.saveExitedRoleRun({
|
|
281
|
+
task,
|
|
282
|
+
role,
|
|
283
|
+
run,
|
|
284
|
+
session: existingSession,
|
|
285
|
+
summary: `Role Run could not start: ${message}`,
|
|
286
|
+
now
|
|
287
|
+
});
|
|
288
|
+
results.push({
|
|
289
|
+
taskId: task.id,
|
|
290
|
+
roleName: role.name,
|
|
291
|
+
runId: run.id,
|
|
292
|
+
status: "failed",
|
|
293
|
+
reason: "launch-failed",
|
|
294
|
+
error: persisted === "state-changed" ? "Run state changed during launch failure." : message,
|
|
295
|
+
terminalized: persisted === "failed"
|
|
296
|
+
});
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
59
299
|
results.push({
|
|
60
300
|
taskId: task.id,
|
|
61
301
|
roleName: role.name,
|
|
62
302
|
runId: run.id,
|
|
63
303
|
status: "failed",
|
|
64
|
-
|
|
304
|
+
reason: "delivery-uncertain",
|
|
305
|
+
error: message,
|
|
306
|
+
terminalFailure: roleRunDeliveryFailure(run, processing.batchId, preparedSession, prepared?.launchId)
|
|
65
307
|
});
|
|
66
308
|
}
|
|
67
309
|
}
|
|
68
310
|
}
|
|
69
311
|
return results;
|
|
70
312
|
}
|
|
71
|
-
function
|
|
313
|
+
function roleRunDeliveryFailure(run, mailboxBatchId, session, launchId) {
|
|
314
|
+
return {
|
|
315
|
+
taskId: run.taskId,
|
|
316
|
+
roleName: run.roleName,
|
|
317
|
+
agentId: run.effective.agentId,
|
|
318
|
+
adapterId: run.effective.adapterId,
|
|
319
|
+
runId: run.id,
|
|
320
|
+
mailboxBatchId,
|
|
321
|
+
...(session?.nativeSessionId === undefined
|
|
322
|
+
? {}
|
|
323
|
+
: { nativeSessionId: session.nativeSessionId }),
|
|
324
|
+
...(launchId === undefined ? {} : { launchId })
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
function requireResumeSession(role, effective, session) {
|
|
72
328
|
if (session === null || !hasText(session.nativeSessionId)) {
|
|
73
329
|
throw new Error(`Role resume has no fixed native session: ${role.taskId}/${role.name}.`);
|
|
74
330
|
}
|
|
331
|
+
if (!effectiveLaunchSnapshotsCompatible(session.effective, effective)) {
|
|
332
|
+
throw new Error(`Role resume effective snapshot drifted: ${role.taskId}/${role.name}.`);
|
|
333
|
+
}
|
|
75
334
|
return session.nativeSessionId;
|
|
76
335
|
}
|
|
77
|
-
function
|
|
78
|
-
|
|
336
|
+
function persistPreStartFence(store, task, role, run, existingSession, preflight, now) {
|
|
337
|
+
if (preflight.owner.scope !== "task"
|
|
338
|
+
|| preflight.owner.taskId !== task.id
|
|
339
|
+
|| preflight.owner.roleName !== role.name
|
|
340
|
+
|| preflight.runId !== run.id
|
|
341
|
+
|| preflight.launchId.trim().length === 0) {
|
|
342
|
+
throw new Error(`Pre-start launch fence changed the active Role Run: ${task.id}/${role.name}.`);
|
|
343
|
+
}
|
|
344
|
+
const session = preflightSession(role, run.effective, existingSession, run.mode, preflight);
|
|
345
|
+
store.saveRoleRunPrepared({
|
|
346
|
+
task,
|
|
347
|
+
role,
|
|
348
|
+
run,
|
|
349
|
+
session,
|
|
350
|
+
launchId: preflight.launchId,
|
|
351
|
+
now
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
function preflightSession(role, effective, existing, mode, preflight) {
|
|
355
|
+
const session = preflight.nativeSessionId === undefined
|
|
356
|
+
? null
|
|
357
|
+
: {
|
|
358
|
+
agentId: preflight.agentId,
|
|
359
|
+
adapterId: preflight.adapterId,
|
|
360
|
+
nativeSessionId: preflight.nativeSessionId,
|
|
361
|
+
launchId: preflight.launchId,
|
|
362
|
+
status: "ready",
|
|
363
|
+
effective: preflight.effective
|
|
364
|
+
};
|
|
365
|
+
return validateRoleSession(role, effective, existing, mode, session);
|
|
366
|
+
}
|
|
367
|
+
function validateReadySession(role, effective, existing, mode, ready) {
|
|
368
|
+
return validateRoleSession(role, effective, existing, mode, ready.session);
|
|
369
|
+
}
|
|
370
|
+
function validateRoleSession(role, effective, existing, mode, session) {
|
|
79
371
|
if (mode === "new" && session === null)
|
|
80
372
|
return null;
|
|
81
373
|
if (session === null || !hasText(session.nativeSessionId)) {
|
|
82
374
|
throw new Error(`Ready Role session has no native session id: ${role.taskId}/${role.name}.`);
|
|
83
375
|
}
|
|
84
|
-
if (session.agentId !==
|
|
376
|
+
if (session.agentId !== effective.agentId || session.adapterId !== effective.adapterId) {
|
|
85
377
|
throw new Error(`Ready Role session identity changed: ${role.taskId}/${role.name}.`);
|
|
86
378
|
}
|
|
379
|
+
if (!effectiveLaunchSnapshotsCompatible(session.effective, effective)) {
|
|
380
|
+
throw new Error(`Ready Role session effective snapshot changed: ${role.taskId}/${role.name}.`);
|
|
381
|
+
}
|
|
382
|
+
if (existing?.nativeSessionId !== undefined
|
|
383
|
+
&& effectiveLaunchSnapshotsCompatible(existing.effective, effective)
|
|
384
|
+
&& session.nativeSessionId !== existing.nativeSessionId) {
|
|
385
|
+
throw new Error(`Ready Role session changed the fixed native session id: ${role.taskId}/${role.name}.`);
|
|
386
|
+
}
|
|
87
387
|
if (mode === "resume" && session.nativeSessionId !== existing?.nativeSessionId) {
|
|
88
388
|
throw new Error(`Role resume changed the fixed native session id: ${role.taskId}/${role.name}.`);
|
|
89
389
|
}
|
|
@@ -92,3 +392,13 @@ function validateReadySession(role, existing, mode, ready) {
|
|
|
92
392
|
function hasText(value) {
|
|
93
393
|
return typeof value === "string" && value.trim().length > 0;
|
|
94
394
|
}
|
|
395
|
+
/**
|
|
396
|
+
* Fail-closed readiness check for a supported-readiness adapter's first push.
|
|
397
|
+
* When the store cannot answer (no implementation), the push is held rather than
|
|
398
|
+
* proceeding blind — a supported adapter must have a proven provider-ready fold.
|
|
399
|
+
*/
|
|
400
|
+
function providerReadyForPush(store, input) {
|
|
401
|
+
if (store.isRoleGenerationProviderReady === undefined)
|
|
402
|
+
return false;
|
|
403
|
+
return store.isRoleGenerationProviderReady(input);
|
|
404
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { selectedSchedulerTasks, isSchedulerTaskWorkspaceReady } from "./ports.js";
|
|
2
|
+
import { queueLeaderWakeup } from "./wakeupQueue.js";
|
|
3
|
+
import { projectTaskExecution } from "./taskExecutionProjection.js";
|
|
4
|
+
/**
|
|
5
|
+
* Repairs an active Task that has no durable owner capable of advancing it.
|
|
6
|
+
* This is a low-frequency safety net; normal transitions enqueue their own
|
|
7
|
+
* Role mailbox signal in the same transaction.
|
|
8
|
+
*/
|
|
9
|
+
export function repairOrphanedActiveTasks(store, now, selection) {
|
|
10
|
+
const repaired = [];
|
|
11
|
+
for (const task of selectedSchedulerTasks(store, selection)) {
|
|
12
|
+
if (task.status !== "active")
|
|
13
|
+
continue;
|
|
14
|
+
const roles = store.listRoles(task.id);
|
|
15
|
+
const activeRuns = roles.flatMap((role) => {
|
|
16
|
+
const run = store.getActiveAgentRun(task.id, role.name);
|
|
17
|
+
return run === null ? [] : [run];
|
|
18
|
+
});
|
|
19
|
+
const hasInFlightTurn = roles.some((role) => store.hasInFlightTurn(task.id, role.name));
|
|
20
|
+
const leaderTarget = { kind: "role", taskId: task.id, roleName: "leader" };
|
|
21
|
+
const leaderMailbox = store.getWorkMailbox(leaderTarget);
|
|
22
|
+
const projection = projectTaskExecution({
|
|
23
|
+
task,
|
|
24
|
+
roles,
|
|
25
|
+
runs: activeRuns,
|
|
26
|
+
pendingWakeup: store.getPendingWakeup(task.id),
|
|
27
|
+
leaderMailbox,
|
|
28
|
+
leaderFailure: store.getLeaderFailure(task.id),
|
|
29
|
+
operatorNotification: store.getOperatorNotification(task.id)
|
|
30
|
+
});
|
|
31
|
+
if (hasInFlightTurn
|
|
32
|
+
|| store.hasOpenInputRequest(task.id)
|
|
33
|
+
|| store.getLeaderFailure(task.id) !== null
|
|
34
|
+
|| store.getOperatorNotification(task.id) !== null
|
|
35
|
+
|| projection.status !== "needs-leader-action"
|
|
36
|
+
|| hasUnclaimedLeaderWork(store, task.id, leaderMailbox)) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const taskWorkspace = store.getTaskWorkspace(task.id);
|
|
40
|
+
if (!isSchedulerTaskWorkspaceReady(task, taskWorkspace)) {
|
|
41
|
+
store.queueTaskProgress(task.id, "task-orphaned", now);
|
|
42
|
+
}
|
|
43
|
+
if (leaderMailbox?.processing !== null && leaderMailbox?.processing !== undefined) {
|
|
44
|
+
const recovered = store.releaseLeaderWakeupAndEnqueue === undefined
|
|
45
|
+
? store.releaseWorkMailbox(leaderTarget, leaderMailbox.processing.batchId)
|
|
46
|
+
&& (queueLeaderWakeup(store, task.id, "task-orphaned", now), true)
|
|
47
|
+
: store.releaseLeaderWakeupAndEnqueue(task.id, leaderMailbox.processing.batchId, "task-orphaned", now);
|
|
48
|
+
if (recovered) {
|
|
49
|
+
repaired.push(task.id);
|
|
50
|
+
}
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
queueLeaderWakeup(store, task.id, "task-orphaned", now);
|
|
54
|
+
repaired.push(task.id);
|
|
55
|
+
}
|
|
56
|
+
return repaired;
|
|
57
|
+
}
|
|
58
|
+
function hasUnclaimedLeaderWork(store, taskId, leaderMailbox) {
|
|
59
|
+
if (leaderMailbox === null)
|
|
60
|
+
return store.getPendingWakeup(taskId) !== null;
|
|
61
|
+
return leaderMailbox?.processing === null
|
|
62
|
+
&& leaderMailbox.pending !== null;
|
|
63
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
export const CURRENT_LEADER_FAILURE_SCHEMA_VERSION = 1;
|
|
1
2
|
export function recordLeaderFailure(taskId, nativeSessionId, message, now, existing) {
|
|
2
3
|
const timestamp = now.toISOString();
|
|
3
4
|
return {
|
|
4
|
-
schemaVersion:
|
|
5
|
+
schemaVersion: CURRENT_LEADER_FAILURE_SCHEMA_VERSION,
|
|
5
6
|
taskId: requiredText(taskId, "Task id"),
|
|
6
7
|
nativeSessionId: requiredText(nativeSessionId, "Native session id"),
|
|
7
8
|
message: requiredText(message, "Leader failure message"),
|