@zq-silk/yui 0.15.12 → 0.16.0
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 +3 -2
- package/ARCHITECTURE.zh-CN.md +3 -2
- package/dist/artifacts/artifactCommitLock.js +16 -55
- package/dist/artifacts/taskArtifactRepository.js +3 -3
- package/dist/cli/agentConfigurationPicker.js +1 -1
- package/dist/cli/commandCatalog.js +57 -41
- package/dist/cli/completion.js +18 -18
- package/dist/cli/completionWizard.js +1 -1
- package/dist/cli/dynamicCompletion.js +1 -1
- package/dist/cli/interactionPolicy.js +7 -3
- package/dist/cli/invocationAuthority.js +64 -0
- package/dist/cli/managedDiagnostics.js +1 -1
- package/dist/cli/parseRepeatable.js +35 -0
- package/dist/cli/updatePorts.js +26 -5
- package/dist/cli.js +1280 -1243
- package/dist/commands/agentCommands.js +4 -4
- package/dist/commands/capabilityCommands.js +1 -1
- package/dist/commands/configCommands.js +6 -57
- package/dist/commands/configOverview.js +2 -2
- package/dist/commands/durableJobCommands.js +12 -6
- package/dist/commands/globalRoleCommands.js +53 -29
- package/dist/commands/grantCommands.js +0 -3
- package/dist/commands/jobCommands.js +1 -4
- package/dist/commands/operatorCommands.js +3 -3
- package/dist/commands/projectCommands.js +1 -10
- package/dist/commands/resourcesCommands.js +10 -40
- package/dist/commands/roleConfiguration.js +2 -6
- package/dist/commands/taskActivationCommands.js +4 -8
- package/dist/commands/taskAgentCapabilities.js +9 -0
- package/dist/commands/taskCommandSupport.js +155 -0
- package/dist/commands/taskCommandTypes.js +1 -0
- package/dist/commands/taskCommands.js +271 -710
- package/dist/commands/taskCompletionGate.js +1 -1
- package/dist/commands/taskExecutionCommands.js +1 -1
- package/dist/commands/taskFactCommands.js +325 -0
- package/dist/commands/taskInputCommands.js +12 -42
- package/dist/commands/taskIntegrationCommands.js +17 -57
- package/dist/commands/taskNextActionCommand.js +7 -6
- package/dist/commands/taskPublicationAdoptCommand.js +3 -3
- package/dist/commands/taskPublicationCommands.js +1 -1
- package/dist/commands/taskPublicationVerifyCommand.js +1 -1
- package/dist/commands/taskRemoteDeliveryCommand.js +2 -94
- package/dist/commands/taskUpstreamCommands.js +19 -8
- package/dist/commands/workflowCommands.js +6 -1
- package/dist/completion/completionInstaller.js +26 -26
- package/dist/completion/completionState.js +26 -26
- package/dist/completion/fileCompletionManager.js +6 -11
- package/dist/config/configCatalog.js +0 -2
- package/dist/config/timeZone.js +14 -0
- package/dist/config/yuiConfig.js +0 -29
- package/dist/context/runContextPack.js +2 -3
- package/dist/context/taskCatalog.js +3 -5
- package/dist/context/taskContext.js +139 -151
- package/dist/context/wakeRunReferences.js +11 -0
- package/dist/controller/agentCapabilities.js +58 -0
- package/dist/controller/agentRuntimeObserver.js +6 -11
- package/dist/controller/clientRuntime.js +22 -14
- package/dist/controller/controller.js +9 -22
- package/dist/controller/fileSchedulerStoreAdapter.js +117 -212
- package/dist/controller/jobClient.js +4 -4
- package/dist/controller/jobControl.js +60 -25
- package/dist/controller/jobSupervisor.js +4 -4
- package/dist/controller/providerRetryDelivery.js +7 -7
- package/dist/controller/runtime.js +51 -44
- package/dist/controller/runtimeEventProcessor.js +0 -5
- package/dist/controller/sessionOwnerReconciliation.js +5 -0
- package/dist/controller/taskAgentError.js +35 -0
- package/dist/coordination/workMailboxQueue.js +2 -24
- package/dist/core/controllerClient.js +2 -2
- package/dist/core/fileLockOwner.js +74 -0
- package/dist/decision/decision.js +17 -0
- package/dist/doctor/doctor.js +18 -9
- package/dist/event/taskEvent.js +12 -0
- package/dist/execution/workItemExecutionProjection.js +2 -2
- package/dist/executor/agentCapabilityConfig.js +43 -0
- package/dist/executor/agentConfigurationCatalog.js +49 -11
- package/dist/executor/agentExecutor.js +5 -8
- package/dist/executor/fileRoleLaunchPlanner.js +11 -53
- package/dist/executor/taskAgentCapabilities.js +64 -0
- package/dist/integration/deliveryObligation.js +1 -69
- package/dist/integration/gitIntegrationService.js +60 -84
- package/dist/integration/integrationAttempt.js +3 -14
- package/dist/integration/integrationSourceApplication.js +4 -12
- package/dist/integration/manifestTags.js +2 -2
- package/dist/job/durableJob.js +4 -25
- package/dist/job/jobAssignmentScope.js +22 -0
- package/dist/job/jobOperation.js +16 -0
- package/dist/job/jobRunner.js +2 -1
- package/dist/job/stepDirectory.js +15 -0
- package/dist/kernel/builtinCapabilities.js +15 -11
- package/dist/kernel/kernelPorts.js +1 -17
- package/dist/lifecycle/exactRunTerminalization.js +1 -9
- package/dist/message/inputControlResolution.js +3 -4
- package/dist/message/message.js +17 -20
- package/dist/message/messageContinuation.js +1 -1
- package/dist/milestone/milestone.js +11 -0
- package/dist/observability/orchestrationMetrics.js +24 -1
- package/dist/observability/runtimeIdentity.js +1 -17
- package/dist/output/agentConfigurationPresentation.js +2 -0
- package/dist/output/timePresentation.js +1 -14
- package/dist/plugins/pluginService.js +12 -4
- package/dist/release/releaseWorkflowPorts.js +45 -96
- package/dist/release/runtimeRelease.js +9 -15
- package/dist/repository/projectMaintenanceLock.js +23 -64
- package/dist/repository/taskBaseFreshness.js +1 -1
- package/dist/repository/taskWorkspaceCoordinator.js +13 -22
- package/dist/repository/taskWorkspacePreparer.js +29 -25
- package/dist/repository/workspaceCleanupInspection.js +5 -5
- package/dist/resources/autoResourceGc.js +4 -77
- package/dist/resources/liveReferences.js +7 -54
- package/dist/resources/resourceDiscovery.js +5 -118
- package/dist/resources/resourceGc.js +233 -181
- package/dist/resources/resourceRegistrar.js +5 -4
- package/dist/resources/resourceRegistry.js +3 -37
- package/dist/resources/resourceRegistryStore.js +13 -36
- package/dist/resources/sqliteResourceRegistry.js +43 -26
- package/dist/review/deltaRecheck.js +1 -1
- package/dist/review/reviewAcceptance.js +1 -1
- package/dist/review/reviewDecision.js +1 -1
- package/dist/review/reviewRound.js +8 -7
- package/dist/review/taskFinalReviewContractResolution.js +1 -1
- package/dist/runtime/acpProtocol.js +4 -51
- package/dist/runtime/acpSession.js +10 -75
- package/dist/runtime/acpSessionConfiguration.js +1 -8
- package/dist/runtime/agentEndpoint.js +3 -2
- package/dist/runtime/agentFailureContext.js +43 -0
- package/dist/runtime/agentHost.js +15 -15
- package/dist/runtime/codexInteractiveHost.js +2 -2
- package/dist/runtime/index.js +0 -1
- package/dist/runtime/managedCaller.js +21 -1
- package/dist/runtime/providerErrors.js +38 -0
- package/dist/runtime/runtimeObservation.js +0 -5
- package/dist/runtime/runtimeSessionCandidate.js +2 -3
- package/dist/runtime/sessionOwnerIdentity.js +1 -1
- package/dist/runtime/structuredProviderHost.js +4 -41
- package/dist/runtime/tmuxAdapters.js +6 -8
- package/dist/scheduler/activeRoleRunDelivery.js +7 -7
- package/dist/scheduler/leaderWakeupProcessor.js +19 -6
- package/dist/scheduler/operatorEvent.js +12 -20
- package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
- package/dist/scheduler/ports.js +5 -8
- package/dist/scheduler/roleRunLiveness.js +4 -4
- package/dist/scheduler/roleRunStall.js +16 -23
- package/dist/scheduler/taskExecutionProjection.js +25 -33
- package/dist/scheduler/taskObservabilityProjection.js +0 -1
- package/dist/scheduler/taskWake.js +5 -10
- package/dist/scheduler/wakeReason.js +2 -0
- package/dist/scheduler/wakeupQueue.js +2 -4
- package/dist/setup/setupCommand.js +1 -1
- package/dist/storage/contextRecords.js +106 -0
- package/dist/storage/currentTaskStore.js +1 -1
- package/dist/storage/homeLayout.js +13 -17
- package/dist/storage/migrations/agentFailureContext.js +22 -0
- package/dist/storage/migrations/currentInputContract.js +86 -0
- package/dist/storage/migrations/currentRuntimeContract.js +228 -0
- package/dist/storage/migrations/historicalVerificationPlan.js +35 -0
- package/dist/storage/migrations/integrationContinuation.js +5 -4
- package/dist/storage/migrations/narrowAgentFailureContext.js +65 -0
- package/dist/storage/migrations/notificationOnlyWakes.js +74 -0
- package/dist/storage/migrations/verificationPlanV1.js +162 -0
- package/dist/storage/migrations/verificationPolicy.js +74 -0
- package/dist/storage/migrations/workItemHistory.js +46 -0
- package/dist/storage/persistenceWorker.js +12 -4
- package/dist/storage/recordValidation.js +87 -0
- package/dist/storage/sqliteSchema.js +142 -1
- package/dist/storage/sqliteStore.js +156 -162
- package/dist/storage/storageSchema.js +2 -14
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +15 -7
- package/dist/storage/taskStore.js +3 -7
- package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
- package/dist/task/completionReadiness.js +5 -24
- package/dist/task/draftPlan.js +0 -53
- package/dist/task/nextAction.js +7 -13
- package/dist/task/remoteDeliveryService.js +89 -0
- package/dist/task/taskActivation.js +2 -25
- package/dist/task/taskActivationService.js +0 -2
- package/dist/task/taskRecordReference.js +0 -1
- package/dist/task/taskSubmission.js +3 -14
- package/dist/telemetry/sqliteTelemetryBatch.js +29 -0
- package/dist/telemetry/sqliteTelemetryStore.js +49 -49
- package/dist/telemetry/telemetryWiring.js +4 -3
- package/dist/tmux/tmuxManager.js +29 -20
- package/dist/verification/gateArtifact.js +15 -24
- package/dist/verification/gateArtifactStore.js +14 -7
- package/dist/verification/verificationGateService.js +29 -88
- package/dist/verification/verificationPlan.js +27 -72
- package/dist/web/assets/client/app.js +1 -1
- package/dist/web/assets/client/taskSurface.js +4 -4
- package/dist/web/webServer.js +4 -6
- package/dist/web/webSnapshot.js +3 -83
- package/dist/web/webTaskSurface.js +17 -46
- package/dist/workItem/workItem.js +1 -12
- package/docs/architecture/capabilities-and-resources.md +14 -2
- package/docs/architecture/capabilities-and-resources.zh-CN.md +11 -1
- package/docs/managed-turn-and-session-runtime.md +83 -10
- package/docs/managed-turn-and-session-runtime.zh-CN.md +65 -11
- package/docs/plugin-sdk.md +4 -2
- package/docs/provider-runtime.md +3 -1
- package/docs/provider-runtime.zh-CN.md +4 -2
- package/docs/release-workflow.md +190 -7
- package/docs/release-workflow.zh-CN.md +139 -5
- package/docs/roles-and-configuration.md +29 -0
- package/docs/roles-and-configuration.zh-CN.md +21 -0
- package/docs/sqlite-control-plane-design.md +16 -3
- package/docs/sqlite-control-plane-design.zh-CN.md +13 -2
- package/docs/task-dag-semantics.md +3 -3
- package/docs/task-dag-semantics.zh-CN.md +2 -2
- package/docs/task-delivery.md +91 -5
- package/docs/task-delivery.zh-CN.md +72 -4
- package/docs/task-discovery.md +10 -11
- package/docs/task-discovery.zh-CN.md +8 -9
- package/docs/testing/verification-levels.md +145 -9
- package/docs/testing/verification-levels.zh-CN.md +103 -8
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +5 -0
- package/skills/yui-leader/references/integration.md +9 -5
- package/skills/yui-leader/references/planning.md +9 -3
- package/skills/yui-operator/SKILL.md +15 -10
- package/skills/yui-runtime/SKILL.md +6 -6
- package/skills/yui-runtime/references/recovery.md +47 -0
- package/dist/agent/agentRegistry.js +0 -10
- package/dist/agentRun/runIdentity.js +0 -22
- package/dist/commands/deliveryGuardPreflight.js +0 -30
- package/dist/commands/taskIntegrationQueueCommands.js +0 -226
- package/dist/commands/taskOverviewCommand.js +0 -377
- package/dist/completion/completionWizard.js +0 -125
- package/dist/context/dispatchContext.js +0 -110
- package/dist/context/wakeNotification.js +0 -144
- package/dist/integration/integrationCheckEvidenceReuse.js +0 -53
- package/dist/integration/integrationQueueEntry.js +0 -191
- package/dist/integration/integrationQueueService.js +0 -810
- package/dist/release/fakeReleasePorts.js +0 -55
- package/dist/runtime/sessionOwnerRegistry.js +0 -137
- package/dist/task/deliveryGuard.js +0 -226
- /package/dist/{commands/taskActor.js → task/taskAuthority.js} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { resolveJobCaller } from "../
|
|
1
|
+
import { resolveJobCaller } from "../task/taskAuthority.js";
|
|
2
2
|
import { callFileTaskController } from "./clientRuntime.js";
|
|
3
3
|
export async function startDurableJob(home, params, clientOptions = {}) {
|
|
4
4
|
// rr6/f2: Route through callFileTaskController so an Integration-issued
|
|
@@ -7,8 +7,8 @@ export async function startDurableJob(home, params, clientOptions = {}) {
|
|
|
7
7
|
const result = await callFileTaskController(home, "job.start", params, clientOptions);
|
|
8
8
|
return parseStartResult(result);
|
|
9
9
|
}
|
|
10
|
-
export async function getDurableJob(home, taskId, jobId, clientOptions = {}) {
|
|
11
|
-
const result = await callFileTaskController(home, "job.get", { taskId, jobId }, clientOptions);
|
|
10
|
+
export async function getDurableJob(home, taskId, jobId, caller, clientOptions = {}) {
|
|
11
|
+
const result = await callFileTaskController(home, "job.get", { taskId, jobId, caller }, clientOptions);
|
|
12
12
|
return parseJobResult(result);
|
|
13
13
|
}
|
|
14
14
|
export async function acknowledgeDurableJob(home, taskId, jobId, caller, clientOptions = {}) {
|
|
@@ -89,7 +89,7 @@ export function createControllerIntegrationJobPort(home, clientOptions = {}) {
|
|
|
89
89
|
return job;
|
|
90
90
|
},
|
|
91
91
|
async getJob(taskId, jobId) {
|
|
92
|
-
return getDurableJob(home, taskId, jobId, clientOptions);
|
|
92
|
+
return getDurableJob(home, taskId, jobId, resolveJobCaller(clientOptions.environment, taskId), clientOptions);
|
|
93
93
|
},
|
|
94
94
|
async cancelJob(taskId, jobId) {
|
|
95
95
|
// rr8/rr12: Bind the cancel request to the caller's managed identity.
|
|
@@ -3,18 +3,22 @@
|
|
|
3
3
|
* record is created or cancel-requested. The Controller socket layer calls
|
|
4
4
|
* this port for `job.*` requests; nothing else writes queued jobs.
|
|
5
5
|
*
|
|
6
|
-
* Creation is idempotent per
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* Creation is idempotent per authenticated actor and explicit request ID.
|
|
7
|
+
* Repeating that request with identical inputs returns the original Job;
|
|
8
|
+
* changing its input is a conflict. Integration owner identity additionally
|
|
9
|
+
* preserves its admitted Job across authorized Session replacement.
|
|
9
10
|
*/
|
|
10
11
|
import { execSync } from "node:child_process";
|
|
11
12
|
import { createHash } from "node:crypto";
|
|
12
13
|
import { resolve, sep } from "node:path";
|
|
13
|
-
import { acknowledgeUnknownDurableJob, createDurableJob, durableJobIdempotencyKey, isDurableJobTerminal, requestDurableJobCancel, retryDurableJobIdempotencyKey } from "../job/durableJob.js";
|
|
14
14
|
import { activeLiveRoleAgentSession } from "../executor/agentExecutor.js";
|
|
15
|
+
import { acknowledgeUnknownDurableJob, createDurableJob, durableJobIdempotencyKey, isDurableJobTerminal, requestDurableJobCancel, retryDurableJobIdempotencyKey } from "../job/durableJob.js";
|
|
15
16
|
import { CallAuthority } from "../kernel/callAuthority.js";
|
|
16
17
|
import { redactLaunchText } from "../runtime/launchDiagnostics.js";
|
|
17
|
-
import { requireManagedTaskCaller } from "../runtime/managedCaller.js";
|
|
18
|
+
import { requireManagedTaskCaller, ManagedRuntimeDriftError } from "../runtime/managedCaller.js";
|
|
19
|
+
import { CliError } from "../errors/cliError.js";
|
|
20
|
+
import { assertJobAssignmentScope, JobAssignmentScopeError } from "../job/jobAssignmentScope.js";
|
|
21
|
+
import { assertContextRecordReadable } from "../context/taskContext.js";
|
|
18
22
|
export function createDurableJobControl(store) {
|
|
19
23
|
const authority = createJobCallAuthority(store);
|
|
20
24
|
return {
|
|
@@ -25,6 +29,7 @@ export function createDurableJobControl(store) {
|
|
|
25
29
|
// startJob with the same key create a duplicate job.
|
|
26
30
|
return store.transaction((tx) => {
|
|
27
31
|
const context = authority.authenticate(Object.freeze({ ...params.caller }), params.taskId);
|
|
32
|
+
requireJobAssignment(tx, params, params.caller);
|
|
28
33
|
assertNonSecretJobInput(params);
|
|
29
34
|
const baseKey = durableJobIdempotencyKey({
|
|
30
35
|
owner: params.owner,
|
|
@@ -37,8 +42,7 @@ export function createDurableJobControl(store) {
|
|
|
37
42
|
const inputDigest = params.retryOf === undefined
|
|
38
43
|
? baseKey
|
|
39
44
|
: retryDurableJobIdempotencyKey(baseKey, params.retryOf);
|
|
40
|
-
const requestId = params.requestId
|
|
41
|
-
: requiredId(params.requestId, "job.start requestId");
|
|
45
|
+
const requestId = requiredId(params.requestId, "job.start requestId");
|
|
42
46
|
// An IntegrationAttempt already is a durable operation identity.
|
|
43
47
|
// Recovery by another authorized Role must find its original Job,
|
|
44
48
|
// including the window before Integration persisted the returned id.
|
|
@@ -68,12 +72,6 @@ export function createDurableJobControl(store) {
|
|
|
68
72
|
}
|
|
69
73
|
return { job: existing, created: false };
|
|
70
74
|
}
|
|
71
|
-
// A historical content-addressed request has no attributable caller.
|
|
72
|
-
// Do not silently execute it again under a newly attributed identity.
|
|
73
|
-
const historical = tx.findDurableJobByIdempotencyKey(params.taskId, inputDigest);
|
|
74
|
-
if (params.requestId === undefined && historical !== null) {
|
|
75
|
-
throw jobDomainError(`Historical request already exists: ${historical.id}; inspect it or select an explicit new requestId.`);
|
|
76
|
-
}
|
|
77
75
|
authority.authorize(context, params.taskId);
|
|
78
76
|
validateStartParams(tx, params);
|
|
79
77
|
const id = tx.nextDurableJobId(params.taskId);
|
|
@@ -97,7 +95,21 @@ export function createDurableJobControl(store) {
|
|
|
97
95
|
return { job, created: true };
|
|
98
96
|
});
|
|
99
97
|
},
|
|
100
|
-
getJob(taskId, jobId) {
|
|
98
|
+
getJob(taskId, jobId, caller) {
|
|
99
|
+
if (caller === undefined)
|
|
100
|
+
throw jobControlError("UNAUTHORIZED", "Job reads require an explicit caller.");
|
|
101
|
+
try {
|
|
102
|
+
assertContextRecordReadable(store, taskId, "job", jobId, caller.scope === "user" ? {} : {
|
|
103
|
+
YUI_SESSION_SCOPE: caller.scope, YUI_TASK_ID: caller.taskId, YUI_ROLE: caller.role,
|
|
104
|
+
YUI_AGENT_ID: caller.agentId, YUI_ADAPTER_ID: caller.adapterId, YUI_NATIVE_SESSION_ID: caller.nativeSessionId
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
if (error instanceof ManagedRuntimeDriftError || (error instanceof CliError && error.code === "USAGE_ERROR")) {
|
|
109
|
+
throw jobControlError("UNAUTHORIZED", error.message);
|
|
110
|
+
}
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
101
113
|
return store.getDurableJob(taskId, jobId);
|
|
102
114
|
},
|
|
103
115
|
cancelJob(taskId, jobId, now, caller) {
|
|
@@ -108,6 +120,7 @@ export function createDurableJobControl(store) {
|
|
|
108
120
|
// rr8: Bind the cancel request to the caller's managed identity. The
|
|
109
121
|
// same rules as job.start apply, checked against the job's owner.
|
|
110
122
|
assertCallerAuthorized(tx, caller, taskId);
|
|
123
|
+
requireJobAssignment(tx, current, caller);
|
|
111
124
|
const next = requestDurableJobCancel(current, now);
|
|
112
125
|
if (next !== current)
|
|
113
126
|
tx.saveDurableJob(taskId, next);
|
|
@@ -120,6 +133,7 @@ export function createDurableJobControl(store) {
|
|
|
120
133
|
if (current === null)
|
|
121
134
|
return null;
|
|
122
135
|
assertCallerAuthorized(tx, caller, taskId);
|
|
136
|
+
requireJobAssignment(tx, current, caller);
|
|
123
137
|
const next = acknowledgeUnknownDurableJob(current, now);
|
|
124
138
|
if (next !== current)
|
|
125
139
|
tx.saveDurableJob(taskId, next);
|
|
@@ -152,8 +166,19 @@ export function authorizeJobStart(store, job) {
|
|
|
152
166
|
if (jobAuthorityBinding(store, scope, role, job.taskId) !== job.operation.authorityRef) {
|
|
153
167
|
throw jobDomainError("Job caller binding was revoked; no execution was started.");
|
|
154
168
|
}
|
|
169
|
+
requireJobAssignment(store, job, { scope, role });
|
|
155
170
|
validateJobTarget(store, job);
|
|
156
171
|
}
|
|
172
|
+
function requireJobAssignment(store, target, caller) {
|
|
173
|
+
try {
|
|
174
|
+
assertJobAssignmentScope(store, target, caller);
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
if (error instanceof JobAssignmentScopeError)
|
|
178
|
+
throw jobControlError("UNAUTHORIZED", error.message);
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
157
182
|
function jobAuthorityBinding(store, scope, roleName, taskId) {
|
|
158
183
|
// A Host detach/reattach preserves the native Session and its queued work.
|
|
159
184
|
// Authenticate the live launch at ingress, but bind accepted Jobs to the
|
|
@@ -328,8 +353,8 @@ function validateJobTarget(store, params) {
|
|
|
328
353
|
* - `task` + mismatched taskId: rejected.
|
|
329
354
|
* - A non-Leader Task caller requires its current active Assignment.
|
|
330
355
|
* - A Leader Task caller requires its current, unrevoked native Session.
|
|
331
|
-
* -
|
|
332
|
-
*
|
|
356
|
+
* - Non-Leader Task callers additionally pass the shared exact Assignment
|
|
357
|
+
* check for every Job operation and before a queued Job starts.
|
|
333
358
|
*/
|
|
334
359
|
/**
|
|
335
360
|
* Verify the caller's current native Session and Task scope. This local Home
|
|
@@ -470,28 +495,26 @@ export function parseDurableJobStartParams(value) {
|
|
|
470
495
|
env,
|
|
471
496
|
steps,
|
|
472
497
|
caller,
|
|
473
|
-
|
|
474
|
-
requestId: requiredId(record.requestId, "job.start requestId")
|
|
475
|
-
}),
|
|
498
|
+
requestId: requiredId(record.requestId, "job.start requestId"),
|
|
476
499
|
...(retryOf === undefined ? {} : { retryOf })
|
|
477
500
|
};
|
|
478
501
|
}
|
|
479
502
|
export function parseDurableJobRefParams(value) {
|
|
480
503
|
if (typeof value !== "object" || value === null || Array.isArray(value)
|
|
481
|
-
|| Object.keys(value).length !==
|
|
504
|
+
|| Object.keys(value).length !== 3) {
|
|
482
505
|
throw jobControlError("INVALID_PARAMS", "DurableJob ref params are invalid.");
|
|
483
506
|
}
|
|
484
507
|
const record = value;
|
|
485
508
|
return {
|
|
486
509
|
taskId: requiredId(record.taskId, "DurableJob taskId"),
|
|
487
|
-
jobId: requiredId(record.jobId, "DurableJob jobId")
|
|
510
|
+
jobId: requiredId(record.jobId, "DurableJob jobId"),
|
|
511
|
+
caller: parseCaller(record.caller)
|
|
488
512
|
};
|
|
489
513
|
}
|
|
490
514
|
/**
|
|
491
515
|
* rr8: `job.cancel` params carry the caller identity so the Controller can
|
|
492
|
-
* bind the cancel request to the caller's managed scope.
|
|
493
|
-
*
|
|
494
|
-
* third `caller` key.
|
|
516
|
+
* bind the cancel request to the caller's managed scope. Reads also carry
|
|
517
|
+
* an explicit caller but use Context's read authority rather than write authority.
|
|
495
518
|
*/
|
|
496
519
|
export function parseDurableJobCancelParams(value) {
|
|
497
520
|
if (typeof value !== "object" || value === null || Array.isArray(value)
|
|
@@ -551,7 +574,7 @@ function parseSteps(value) {
|
|
|
551
574
|
throw jobControlError("INVALID_PARAMS", "job.start steps are invalid.");
|
|
552
575
|
}
|
|
553
576
|
const record = entry;
|
|
554
|
-
const allowed = new Set(["name", "command", "timeoutMs"]);
|
|
577
|
+
const allowed = new Set(["name", "command", "argv", "cwd", "env", "timeoutMs"]);
|
|
555
578
|
for (const key of Object.keys(record)) {
|
|
556
579
|
if (!allowed.has(key)) {
|
|
557
580
|
throw jobControlError("INVALID_PARAMS", "job.start steps are invalid.");
|
|
@@ -559,6 +582,15 @@ function parseSteps(value) {
|
|
|
559
582
|
}
|
|
560
583
|
const name = requiredId(record.name, "job.start step name");
|
|
561
584
|
const command = requiredId(record.command, "job.start step command");
|
|
585
|
+
const argv = record.argv === undefined ? undefined : record.argv;
|
|
586
|
+
if (argv !== undefined && (!Array.isArray(argv) || argv.length === 0
|
|
587
|
+
|| argv.some(value => typeof value !== "string" || value.length === 0 || value.includes("\0")))) {
|
|
588
|
+
throw jobControlError("INVALID_PARAMS", `job.start step argv is invalid: ${name}.`);
|
|
589
|
+
}
|
|
590
|
+
const cwd = record.cwd === undefined ? undefined : requiredId(record.cwd, "job.start step cwd");
|
|
591
|
+
if (cwd !== undefined && !cwd.startsWith("/")) {
|
|
592
|
+
throw jobControlError("INVALID_PARAMS", `job.start step cwd must be absolute: ${name}.`);
|
|
593
|
+
}
|
|
562
594
|
if (names.has(name)) {
|
|
563
595
|
throw jobControlError("INVALID_PARAMS", `job.start step names must be unique: ${name}.`);
|
|
564
596
|
}
|
|
@@ -572,6 +604,9 @@ function parseSteps(value) {
|
|
|
572
604
|
const step = {
|
|
573
605
|
name,
|
|
574
606
|
command,
|
|
607
|
+
...(argv === undefined ? {} : { argv: argv }),
|
|
608
|
+
...(cwd === undefined ? {} : { cwd }),
|
|
609
|
+
...(record.env === undefined ? {} : { env: parseStringMap(record.env, "job.start step env") }),
|
|
575
610
|
...(record.timeoutMs === undefined ? {} : { timeoutMs: record.timeoutMs })
|
|
576
611
|
};
|
|
577
612
|
steps.push(step);
|
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
* cancelRequestedAt -> write cancel fence + SIGTERM
|
|
13
13
|
*/
|
|
14
14
|
import { spawn } from "node:child_process";
|
|
15
|
-
import { chmodSync, existsSync, mkdirSync, openSync,
|
|
15
|
+
import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, statSync } from "node:fs";
|
|
16
16
|
import { join } from "node:path";
|
|
17
17
|
import { fileURLToPath } from "node:url";
|
|
18
|
-
import { writeTextFileAtomically } from "../storage/durableFile.js";
|
|
19
18
|
import { cancelQueuedDurableJob, completeDurableJob, isDurableJobTerminal, markDurableJobUnknown, markDurableJobWakeupNotified, rejectQueuedDurableJob, startDurableJob, touchDurableJobHeartbeat } from "../job/durableJob.js";
|
|
20
|
-
import { readLinuxProcessStartIdentity } from "./domainIdentity.js";
|
|
21
|
-
import { wakeReason } from "../scheduler/wakeReason.js";
|
|
22
19
|
import { recordOperationEvidence } from "../kernel/operationFacts.js";
|
|
20
|
+
import { wakeReason } from "../scheduler/wakeReason.js";
|
|
21
|
+
import { writeTextFileAtomically } from "../storage/durableFile.js";
|
|
22
|
+
import { readLinuxProcessStartIdentity } from "./domainIdentity.js";
|
|
23
23
|
const DEFAULT_STEP_TIMEOUT_MS = 30 * 60_000;
|
|
24
24
|
const HEARTBEAT_STALE_MS = 2 * 60_000;
|
|
25
25
|
const SIGKILL_GRACE_MS = 30_000;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { performance } from "node:perf_hooks";
|
|
2
2
|
import { runTaskCommand } from "../commands/taskCommands.js";
|
|
3
|
-
import { retryIntentBlocker } from "./providerRetryAdmission.js";
|
|
4
|
-
import { cancelProviderRetry, providerRetryAttemptId, providerRetryPending } from "../runtime/providerRetry.js";
|
|
5
|
-
import { currentProviderConversation } from "../runtime/providerRuntimeIdentity.js";
|
|
6
|
-
import { runtimeLifecycleTarget, hasRuntimeCleanupObligation } from "../runtime/lifecycleReservation.js";
|
|
7
|
-
import { sendAgentHostRunControl, inspectAgentHostSocket, agentHostControlSocketPath, AGENT_HOST_CONTROL_PROTOCOL } from "../runtime/agentHost.js";
|
|
8
|
-
import { redactAgentErrorText } from "../runtime/agentError.js";
|
|
9
|
-
import { createTaskEvent } from "../event/taskEvent.js";
|
|
10
3
|
import { enqueueWork } from "../coordination/workMailboxQueue.js";
|
|
4
|
+
import { createTaskEvent } from "../event/taskEvent.js";
|
|
11
5
|
import { settleGlobalRetryInput } from "../message/globalProviderRetry.js";
|
|
6
|
+
import { redactAgentErrorText } from "../runtime/agentError.js";
|
|
7
|
+
import { AGENT_HOST_CONTROL_PROTOCOL, agentHostControlSocketPath, inspectAgentHostSocket, sendAgentHostRunControl } from "../runtime/agentHost.js";
|
|
8
|
+
import { hasRuntimeCleanupObligation, runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
|
|
9
|
+
import { cancelProviderRetry, providerRetryAttemptId, providerRetryPending } from "../runtime/providerRetry.js";
|
|
10
|
+
import { currentProviderConversation } from "../runtime/providerRuntimeIdentity.js";
|
|
11
|
+
import { retryIntentBlocker } from "./providerRetryAdmission.js";
|
|
12
12
|
/** Hooks in the existing Controller pass and nearest-deadline timer. SQLite's
|
|
13
13
|
* current Provider input is the only pending intent; nothing is queued here. */
|
|
14
14
|
export function createProviderRetryHooks(home, store, ports = {}) {
|
|
@@ -1,59 +1,60 @@
|
|
|
1
|
-
import { reconciliationIntervalMilliseconds, resolveAgentLaunchInactivityTimeoutSeconds, resolveControllerTaskConcurrency, resolveDeliveryTimeoutSeconds, resolveRuntimeHealth, resolveTmuxBin, resolveTmuxHistoryLimit } from "../config/yuiConfig.js";
|
|
2
1
|
import { isDeepStrictEqual } from "node:util";
|
|
3
|
-
import { controllerSocketPath } from "../core/controllerEndpoint.js";
|
|
4
2
|
import { AGENT_OPERATIONAL_ENVIRONMENT_NAMES, NATIVE_AGENT_ENVIRONMENT_NAMES, nativeAgentEnvironmentNames, YUI_MANAGED_RUNTIME_ENVIRONMENT_NAMES } from "../agent/launchEnvironment.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
3
|
+
import { runPurposeAdmitsTaskState } from "../agentRun/agentRun.js";
|
|
4
|
+
import { reconciliationIntervalMilliseconds, resolveAgentLaunchInactivityTimeoutSeconds, resolveControllerTaskConcurrency, resolveDeliveryTimeoutSeconds, resolveRuntimeHealth, resolveTmuxBin, resolveTmuxHistoryLimit } from "../config/yuiConfig.js";
|
|
5
|
+
import { controllerSocketPath } from "../core/controllerEndpoint.js";
|
|
6
|
+
import { createTaskEvent } from "../event/taskEvent.js";
|
|
9
7
|
import { AgentConfigurationCatalogService, validateAgentLaunchConfiguration } from "../executor/agentConfigurationCatalog.js";
|
|
10
|
-
import {
|
|
8
|
+
import { activeLiveRoleAgentSession, roleAgentSessionResumeMode } from "../executor/agentExecutor.js";
|
|
9
|
+
import { effectiveLaunchConfig, resolveEffectiveLaunch, roleSessionMayContinue } from "../executor/effectiveLaunch.js";
|
|
10
|
+
import { agentProcessReadinessProbe, ExecutorRegistry } from "../executor/executorRegistry.js";
|
|
11
11
|
import { FileRoleLaunchPlanner } from "../executor/fileRoleLaunchPlanner.js";
|
|
12
|
+
import { createKernelPorts } from "../kernel/kernelPorts.js";
|
|
13
|
+
import { createGlobalRoleMessage } from "../message/message.js";
|
|
14
|
+
import { FileTaskWorkspacePreparer } from "../repository/taskWorkspacePreparer.js";
|
|
15
|
+
import { createResourceAutoGc } from "../resources/autoResourceGc.js";
|
|
16
|
+
import { builtinAgentDriverRegistry } from "../runtime/builtinAgentDrivers.js";
|
|
17
|
+
import { assertExecutionEnvironmentCurrent } from "../runtime/executionEnvironment.js";
|
|
18
|
+
import { appendGlobalProcessExitObservation } from "../runtime/globalProcessExitStore.js";
|
|
19
|
+
import { AgentHostPromptPushAdapter, FileTaskRuntimeIsolation, ProviderContinuationReconciliationService, TmuxSessionHost } from "../runtime/index.js";
|
|
20
|
+
import { launchBrokerForHome } from "../runtime/launchBroker.js";
|
|
21
|
+
import { hasRuntimeCleanupObligation, runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
|
|
22
|
+
import { classifyRuntimeProcessExit, validateRuntimeProcessExitObservation } from "../runtime/processExitObservation.js";
|
|
23
|
+
import { replayRuntimeProcessExitOutbox } from "../runtime/processExitOutbox.js";
|
|
24
|
+
import { providerRetryPrompt } from "../runtime/providerRetry.js";
|
|
25
|
+
import { createRuntimeObservation, runtimeObservationFromTaskEvent } from "../runtime/runtimeObservation.js";
|
|
12
26
|
import { openCurrentTaskStore } from "../storage/currentTaskStore.js";
|
|
13
27
|
import { managedRuntimeRoot } from "../storage/homeLayout.js";
|
|
14
28
|
import { SqliteTaskStore } from "../storage/sqliteStore.js";
|
|
15
29
|
import { AsyncTaskStoreClient, resolveStoreWorkerEnabledForHome } from "../storage/storeRpc.js";
|
|
16
|
-
import {
|
|
30
|
+
import { SurfaceContributions } from "../surface/surfaceContributions.js";
|
|
31
|
+
import { taskOwnsManagedWorkspace } from "../task/task.js";
|
|
32
|
+
import { openSchedulerTelemetry } from "../telemetry/telemetryWiring.js";
|
|
17
33
|
import { NodeCommandExecutor } from "../tmux/commandExecutor.js";
|
|
18
34
|
import { TmuxManager, yuiTmuxServerName } from "../tmux/tmuxManager.js";
|
|
19
|
-
import { AgentHostPromptPushAdapter, FileTaskRuntimeIsolation, TmuxSessionHost, ProviderContinuationReconciliationService } from "../runtime/index.js";
|
|
20
|
-
import { startFileTaskController } from "./controller.js";
|
|
21
|
-
import { AgentHostProviderTurnFenceError, AgentHostProviderSessionBusyError, FileSchedulerStoreAdapter } from "./fileSchedulerStoreAdapter.js";
|
|
22
|
-
import { openSchedulerTelemetry } from "../telemetry/telemetryWiring.js";
|
|
23
|
-
import { createFileArtifactPort, createLinuxProcessPort, DurableJobSupervisor } from "./jobSupervisor.js";
|
|
24
|
-
import { authorizeJobStart } from "./jobControl.js";
|
|
25
|
-
import { createKernelPorts } from "../kernel/kernelPorts.js";
|
|
26
|
-
import { createCapabilityDispatcher } from "./capabilityBridge.js";
|
|
27
35
|
import { createControllerWeb } from "../web/controllerWeb.js";
|
|
28
|
-
import { createWebTaskSurface } from "../web/webTaskSurface.js";
|
|
29
|
-
import { SurfaceContributions } from "../surface/surfaceContributions.js";
|
|
30
36
|
import { TmuxWebTerminalService } from "../web/tmuxWebTerminal.js";
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
37
|
+
import { createWebTaskSurface } from "../web/webTaskSurface.js";
|
|
38
|
+
import { isTaskOwnedWorkspace, sameManagedWorkspaceIdentity } from "../worktree/managedWorkspace.js";
|
|
33
39
|
import { AgentRuntimeObserver } from "./agentRuntimeObserver.js";
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
40
|
+
import { createCapabilityDispatcher } from "./capabilityBridge.js";
|
|
41
|
+
import { FileTaskWorkflowRuntime } from "./clientRuntime.js";
|
|
42
|
+
import { startFileTaskController } from "./controller.js";
|
|
36
43
|
import { ephemeralDomainFromEnvironment, recordEphemeralTmuxTarget } from "./domainIdentity.js";
|
|
37
44
|
import { createEphemeralResourceReaper } from "./ephemeralResourceReaper.js";
|
|
45
|
+
import { AgentHostProviderSessionBusyError, AgentHostProviderTurnFenceError, FileSchedulerStoreAdapter } from "./fileSchedulerStoreAdapter.js";
|
|
46
|
+
import { deliverGlobalInputs } from "./globalInputDelivery.js";
|
|
47
|
+
import { authorizeJobStart } from "./jobControl.js";
|
|
48
|
+
import { createFileArtifactPort, createLinuxProcessPort, DurableJobSupervisor } from "./jobSupervisor.js";
|
|
49
|
+
import { createProviderRetryHooks } from "./providerRetryDelivery.js";
|
|
50
|
+
import { createRuntimeResourceActivityTracker } from "./resourceInventory.js";
|
|
38
51
|
import { scanControllerResourceInventory } from "./resourceInventoryLinux.js";
|
|
39
52
|
import { ResourceInventoryClient } from "./resourceInventoryRpc.js";
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
53
|
+
import { FileRuntimeEventInbox } from "./runtimeEventInbox.js";
|
|
54
|
+
import { AsyncRuntimeEventProcessor, createAsyncRuntimeObserver, FileRuntimeEventProcessor } from "./runtimeEventProcessor.js";
|
|
55
|
+
import { RuntimeLaunchCoordinator } from "./runtimeLaunchCoordinator.js";
|
|
42
56
|
import { SessionOwnerReconciliation } from "./sessionOwnerReconciliation.js";
|
|
43
|
-
import {
|
|
44
|
-
import { assertExecutionEnvironmentCurrent } from "../runtime/executionEnvironment.js";
|
|
45
|
-
import { classifyRuntimeProcessExit, validateRuntimeProcessExitObservation } from "../runtime/processExitObservation.js";
|
|
46
|
-
import { replayRuntimeProcessExitOutbox } from "../runtime/processExitOutbox.js";
|
|
47
|
-
import { appendGlobalProcessExitObservation } from "../runtime/globalProcessExitStore.js";
|
|
48
|
-
import { deliverGlobalInputs } from "./globalInputDelivery.js";
|
|
49
|
-
import { providerRetryPrompt } from "../runtime/providerRetry.js";
|
|
50
|
-
import { createProviderRetryHooks } from "./providerRetryDelivery.js";
|
|
51
|
-
import { createGlobalRoleMessage } from "../message/message.js";
|
|
52
|
-
import { builtinAgentDriverRegistry } from "../runtime/builtinAgentDrivers.js";
|
|
53
|
-
import { createRuntimeObservation, runtimeObservationFromTaskEvent } from "../runtime/runtimeObservation.js";
|
|
54
|
-
import { createTaskEvent } from "../event/taskEvent.js";
|
|
55
|
-
import { runPurposeAdmitsTaskState } from "../agentRun/agentRun.js";
|
|
56
|
-
import { taskOwnsManagedWorkspace } from "../task/task.js";
|
|
57
|
+
import { readTaskAgentCapabilities } from "./agentCapabilities.js";
|
|
57
58
|
/** Production composition root for the current SQLite TaskStore + tmux Controller. */
|
|
58
59
|
export async function startFileTaskControllerRuntime(home, options = {}) {
|
|
59
60
|
// The current Home always uses SQLite. The persistence worker is enabled by
|
|
@@ -86,9 +87,13 @@ export async function startFileTaskControllerRuntime(home, options = {}) {
|
|
|
86
87
|
: undefined;
|
|
87
88
|
let closeKernel = async () => { };
|
|
88
89
|
let closeWeb = async () => { };
|
|
90
|
+
let closeTelemetry = async () => { };
|
|
89
91
|
try {
|
|
92
|
+
const telemetry = options.schedulerStore === undefined
|
|
93
|
+
? openSchedulerTelemetry(home, store.getConfig(), asyncStoreClient) : null;
|
|
94
|
+
closeTelemetry = () => telemetry?.sink.close() ?? Promise.resolve();
|
|
90
95
|
const schedulerStore = options.schedulerStore
|
|
91
|
-
?? new FileSchedulerStoreAdapter(store,
|
|
96
|
+
?? new FileSchedulerStoreAdapter(store, telemetry);
|
|
92
97
|
const domainIdentity = options.domainIdentity
|
|
93
98
|
?? ephemeralDomainFromEnvironment(options.environment ?? process.env);
|
|
94
99
|
const planner = options.planner ?? new FileRoleLaunchPlanner(home, store, {
|
|
@@ -387,7 +392,6 @@ export async function startFileTaskControllerRuntime(home, options = {}) {
|
|
|
387
392
|
environment: options.environment ?? process.env, onError: options.onError
|
|
388
393
|
});
|
|
389
394
|
const webSurface = createWebTaskSurface(store, { runtime: {
|
|
390
|
-
notifyStateChanged: (taskId) => runningRuntime?.signal(`task:${taskId}`),
|
|
391
395
|
notifyMailboxChanged: (target) => {
|
|
392
396
|
if (target.kind === "role")
|
|
393
397
|
runningRuntime?.signal(`role:${target.taskId}/${target.roleName}`);
|
|
@@ -454,8 +458,10 @@ export async function startFileTaskControllerRuntime(home, options = {}) {
|
|
|
454
458
|
const continuationReconciler = options.continuationMetadata === undefined
|
|
455
459
|
? undefined
|
|
456
460
|
: new ProviderContinuationReconciliationService(store, schedulerStore, options.continuationMetadata);
|
|
457
|
-
const running = await startFileTaskController(home, schedulerStore, delivery, (method, params) => method === "
|
|
458
|
-
?
|
|
461
|
+
const running = await startFileTaskController(home, schedulerStore, delivery, (method, params) => method === "task.role-capabilities"
|
|
462
|
+
? readTaskAgentCapabilities(store, catalogs, params)
|
|
463
|
+
: method === "web.start" || method === "web.stop" || method === "web.status"
|
|
464
|
+
? web.dispatch(method, params) : lifecycleDispatcher(method, params), {
|
|
459
465
|
intervalMs: options.intervalMs
|
|
460
466
|
?? reconciliationIntervalMilliseconds(store.getConfig().reconciliationIntervalSeconds),
|
|
461
467
|
signalWindowMs: options.signalWindowMs,
|
|
@@ -543,12 +549,12 @@ export async function startFileTaskControllerRuntime(home, options = {}) {
|
|
|
543
549
|
}
|
|
544
550
|
let resourceClose;
|
|
545
551
|
const closeResources = () => {
|
|
546
|
-
resourceClose ??= Promise.all([
|
|
552
|
+
resourceClose ??= closeTelemetry().finally(() => Promise.all([
|
|
547
553
|
web.close(),
|
|
548
554
|
kernel.close(),
|
|
549
555
|
asyncStoreClient?.close() ?? Promise.resolve(),
|
|
550
556
|
inventoryClient?.close() ?? Promise.resolve()
|
|
551
|
-
]).then(() => undefined).finally(() => {
|
|
557
|
+
])).then(() => undefined).finally(() => {
|
|
552
558
|
ownedStore?.close();
|
|
553
559
|
});
|
|
554
560
|
return resourceClose;
|
|
@@ -583,6 +589,7 @@ export async function startFileTaskControllerRuntime(home, options = {}) {
|
|
|
583
589
|
catch (error) {
|
|
584
590
|
// The socket may never have opened. Startup failure must still release
|
|
585
591
|
// this attempt's workers/instances instead of leaving an uncallable process.
|
|
592
|
+
await closeTelemetry().catch(() => undefined);
|
|
586
593
|
await Promise.allSettled([
|
|
587
594
|
closeWeb(),
|
|
588
595
|
closeKernel(),
|
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
import { isRuntimeTokenEvidence } from "../runtime/runtimeObservation.js";
|
|
2
|
-
import { builtinAgentDriverRegistry } from "../runtime/builtinAgentDrivers.js";
|
|
3
2
|
const DEFAULT_MAX_RUNTIME_EVENTS_PER_DRAIN = 64;
|
|
4
3
|
/** Folds immutable Hook facts in one bounded transaction before acknowledging them. */
|
|
5
4
|
export class FileRuntimeEventProcessor {
|
|
6
5
|
inbox;
|
|
7
6
|
observer;
|
|
8
7
|
options;
|
|
9
|
-
drivers;
|
|
10
8
|
drainLaneCursor = 0;
|
|
11
9
|
constructor(inbox, observer, options = {}) {
|
|
12
10
|
this.inbox = inbox;
|
|
13
11
|
this.observer = observer;
|
|
14
12
|
this.options = options;
|
|
15
|
-
this.drivers = options.drivers ?? builtinAgentDriverRegistry();
|
|
16
13
|
}
|
|
17
14
|
drain(now) {
|
|
18
15
|
const acknowledgedEventIds = [];
|
|
@@ -540,13 +537,11 @@ export class AsyncRuntimeEventProcessor {
|
|
|
540
537
|
inbox;
|
|
541
538
|
observer;
|
|
542
539
|
options;
|
|
543
|
-
drivers;
|
|
544
540
|
drainLaneCursor = 0;
|
|
545
541
|
constructor(inbox, observer, options = {}) {
|
|
546
542
|
this.inbox = inbox;
|
|
547
543
|
this.observer = observer;
|
|
548
544
|
this.options = options;
|
|
549
|
-
this.drivers = options.drivers ?? builtinAgentDriverRegistry();
|
|
550
545
|
}
|
|
551
546
|
async drainAsync(now) {
|
|
552
547
|
const acknowledgedEventIds = [];
|
|
@@ -132,6 +132,11 @@ export class SessionOwnerReconciliation {
|
|
|
132
132
|
}
|
|
133
133
|
const result = await terminateSessionOwners(owner, records, this.#terminationPorts(), options);
|
|
134
134
|
if (result.outcome === "stop-confirmed") {
|
|
135
|
+
// A signalled Host exits into a retained tmux pane. Explicit release
|
|
136
|
+
// removes that window too, after physical exit proof, not on observation.
|
|
137
|
+
if (records.length > 0) {
|
|
138
|
+
this.#tmux?.killRole(owner.scope === "task" ? owner.taskId : "operator", owner.roleName);
|
|
139
|
+
}
|
|
135
140
|
for (const record of result.confirmed) {
|
|
136
141
|
this.#store.removeSessionOwner(sessionOwnerProcessKey(record));
|
|
137
142
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createTaskEvent } from "../event/taskEvent.js";
|
|
2
|
+
import { redactAgentErrorText } from "../runtime/agentError.js";
|
|
3
|
+
import { captureAgentFailureContext } from "../runtime/agentFailureContext.js";
|
|
4
|
+
/** One persistence boundary. Ingress owns Provider classification, execution
|
|
5
|
+
* fences and routing; this writer never invents an execution or recovery plan. */
|
|
6
|
+
export function recordTaskAgentError(store, input, now) {
|
|
7
|
+
return store.transaction(tx => {
|
|
8
|
+
const error = input.error;
|
|
9
|
+
const payload = {
|
|
10
|
+
...Object.fromEntries(Object.entries(input.evidence ?? {}).filter((entry) => typeof entry[1] === "string" && (entry[0] === "lastOutput" || entry[1].trim().length > 0))),
|
|
11
|
+
sourceEventId: input.sourceEventId, roleName: input.roleName,
|
|
12
|
+
agentId: input.agentId, adapterId: input.adapterId, driverId: input.driverId,
|
|
13
|
+
source: error.source, phase: error.phase, category: error.category, code: error.code,
|
|
14
|
+
message: redactAgentErrorText(error.message), raw: redactAgentErrorText(error.raw),
|
|
15
|
+
inputDisposition: error.inputDisposition, sessionDisposition: error.sessionDisposition,
|
|
16
|
+
...(error.retryAfterMs === undefined ? {} : { retryAfterMs: String(error.retryAfterMs) })
|
|
17
|
+
};
|
|
18
|
+
const identity = ["sourceEventId", "roleName", "agentId", "adapterId", "driverId",
|
|
19
|
+
"source", "phase", "category", "code", "inputDisposition", "sessionDisposition",
|
|
20
|
+
"runId", "attemptId", "nativeSessionId", "nativeTurnId", "observationEventId",
|
|
21
|
+
"message", "errorName", "causeName", "hostState", "registrationDisposition",
|
|
22
|
+
"retryAfterMs", "lastOutput", ...(payload.attemptId === undefined ? ["raw"] : [])];
|
|
23
|
+
const duplicate = [...tx.listEventsByType(input.taskId, ["runtime.agent-error"])].reverse().find(event => identity.every(key => event.payload[key] === payload[key]));
|
|
24
|
+
// Keep the first original raw cause/configuration for a fixed failure.
|
|
25
|
+
// Changed normalized facts are distinct evidence, not a replay.
|
|
26
|
+
if (duplicate !== undefined)
|
|
27
|
+
return { event: duplicate, created: false };
|
|
28
|
+
const event = createTaskEvent(tx.nextEventId(input.taskId), input.taskId, "runtime.agent-error", {
|
|
29
|
+
...payload,
|
|
30
|
+
capabilityContext: captureAgentFailureContext(input.effective, input.effective === undefined ? null : tx.getConfiguredAgent(input.effective.agentId))
|
|
31
|
+
}, now);
|
|
32
|
+
tx.saveEvent(input.taskId, event);
|
|
33
|
+
return { event, created: true };
|
|
34
|
+
});
|
|
35
|
+
}
|
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
import { completeProcessing, consumePendingBatch, createWorkMailbox, enqueueSignal, mailboxBatches, mailboxEntityRefKey } from "./workMailbox.js";
|
|
2
|
-
const LEGACY_ROLE_RUN_DISPATCH_REASONS = new Set([
|
|
3
|
-
"turn-dispatched",
|
|
4
|
-
"turn-retried",
|
|
5
|
-
"review-requested",
|
|
6
|
-
"workitem-synthesis-ready",
|
|
7
|
-
"review-synthesis-ready"
|
|
8
|
-
]);
|
|
9
2
|
/** Atomically useful when called inside the caller's TaskStore transaction. */
|
|
10
3
|
export function enqueueWork(store, target, reason, occurredAt, refs = [], metadata = {}) {
|
|
11
4
|
const mailbox = store.getWorkMailbox(target) ?? createWorkMailbox(target);
|
|
12
5
|
// Archive is an admission boundary, not an acknowledgement of old input.
|
|
13
6
|
// Runtime cleanup mailboxes remain usable for exact-owner recovery.
|
|
14
7
|
if ((target.kind === "task" || target.kind === "role")
|
|
15
|
-
&& store.getTask
|
|
8
|
+
&& store.getTask(target.taskId)?.status === "archived")
|
|
16
9
|
return mailbox;
|
|
17
10
|
const queued = enqueueSignal(mailbox, {
|
|
18
11
|
reason,
|
|
@@ -79,26 +72,11 @@ export function captureRoleRunDispatch(mailbox, input) {
|
|
|
79
72
|
};
|
|
80
73
|
}
|
|
81
74
|
}
|
|
82
|
-
// Valid earlier dispatches used sequence-generated dedupe keys and could
|
|
83
|
-
// include a companion WorkItem ref. Consume that complete legacy batch once
|
|
84
|
-
// the exact AgentRun reaches an accepted or terminal boundary.
|
|
85
|
-
if (pending.requestCount === 1
|
|
86
|
-
&& pending.sources.length === 1
|
|
87
|
-
&& pending.sources[0] === "yui"
|
|
88
|
-
&& pending.reasons.some((reason) => LEGACY_ROLE_RUN_DISPATCH_REASONS.has(reason))
|
|
89
|
-
&& pending.refs.some((ref) => (mailboxEntityRefKey(ref) === mailboxEntityRefKey(exactRef)))) {
|
|
90
|
-
return {
|
|
91
|
-
kind: "pending",
|
|
92
|
-
fromSequence: pending.fromSequence,
|
|
93
|
-
toSequence: pending.toSequence
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
75
|
return null;
|
|
97
76
|
}
|
|
98
77
|
/**
|
|
99
78
|
* Settles one exact ordinary Role AgentRun dispatch. Provider acceptance is the
|
|
100
|
-
* normal boundary; terminalization
|
|
101
|
-
* unaccepted and valid earlier dispatches.
|
|
79
|
+
* normal boundary; terminalization uses the same exact dispatch identity.
|
|
102
80
|
*/
|
|
103
81
|
export function settleRoleRunDispatch(store, input, expected) {
|
|
104
82
|
if (expected === null)
|
|
@@ -2,10 +2,10 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { lstat, readFile } from "node:fs/promises";
|
|
3
3
|
import { createConnection } from "node:net";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
-
import { CONTROLLER_DISCOVERY_PATH, FILE_TASK_CONTROLLER_PROTOCOL_VERSION, MAX_CONTROLLER_MESSAGE_BYTES, ControllerProtocolError, encodeControllerRequest, parseControllerDiscovery, parseControllerResponse } from "./protocol.js";
|
|
6
5
|
import { isControllerSocketPathForHome } from "./controllerEndpoint.js";
|
|
7
|
-
import { readHomeFilesystemId } from "./homeFilesystemIdentity.js";
|
|
8
6
|
import { findLiveControllerProcessForHome, inspectLiveControllerProcess } from "./controllerProcessIdentity.js";
|
|
7
|
+
import { readHomeFilesystemId } from "./homeFilesystemIdentity.js";
|
|
8
|
+
import { CONTROLLER_DISCOVERY_PATH, ControllerProtocolError, encodeControllerRequest, FILE_TASK_CONTROLLER_PROTOCOL_VERSION, MAX_CONTROLLER_MESSAGE_BYTES, parseControllerDiscovery, parseControllerResponse } from "./protocol.js";
|
|
9
9
|
export class ControllerClientError extends Error {
|
|
10
10
|
code;
|
|
11
11
|
constructor(code, message) {
|