@zq-silk/yui 0.15.7 → 0.15.8
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 +192 -399
- package/README.md +127 -1149
- package/dist/agent/adapterCatalog.js +15 -2
- package/dist/agent/agent.js +23 -3
- package/dist/agent/argumentPolicy.js +7 -1
- package/dist/agent/connectionPlan.js +62 -0
- package/dist/agent/executionComponents.js +158 -0
- package/dist/agent/launchEnvironment.js +31 -3
- package/dist/agent/managedRuntimeEnvironment.js +3 -5
- package/dist/{turn/turn.js → agentRun/agentRun.js} +166 -109
- package/dist/{turn/turnIdentity.js → agentRun/runIdentity.js} +4 -4
- package/dist/brief/taskBrief.js +12 -0
- package/dist/cli/agentConfigurationPicker.js +13 -0
- package/dist/cli/commandCatalog.js +157 -70
- package/dist/cli/interactionCandidates.js +5 -5
- package/dist/cli/interactionPolicy.js +38 -8
- package/dist/cli/invocationRouter.js +1 -1
- package/dist/cli/managedDiagnostics.js +28 -0
- package/dist/cli/operatorWizard.js +1 -7
- package/dist/cli/roleOptionOrder.js +27 -0
- package/dist/cli/roleWizard.js +50 -14
- package/dist/cli/updateOrchestrator.js +1 -1
- package/dist/cli/updatePorts.js +3 -4
- package/dist/cli.js +178 -95
- package/dist/commands/agentCommands.js +72 -14
- package/dist/commands/capabilityCommands.js +9 -6
- package/dist/commands/configCommands.js +20 -20
- package/dist/commands/deliveryGuardPreflight.js +2 -2
- package/dist/commands/executionAuditCommands.js +24 -24
- package/dist/commands/globalRoleCommands.js +1 -1
- package/dist/commands/grantCommands.js +4 -4
- package/dist/commands/operatorCommands.js +1 -7
- package/dist/commands/projectCommands.js +4 -4
- package/dist/commands/resourcesCommands.js +2 -2
- package/dist/commands/roleConfiguration.js +25 -5
- package/dist/commands/roleRuntimeGuard.js +4 -5
- package/dist/commands/sessionCommands.js +3 -7
- package/dist/commands/taskActivationCommands.js +259 -0
- package/dist/commands/taskActor.js +28 -49
- package/dist/commands/taskCommands.js +1188 -710
- package/dist/commands/taskContextCommand.js +39 -583
- package/dist/commands/taskExecutionCommands.js +32 -32
- package/dist/commands/taskInputCommands.js +40 -104
- package/dist/commands/taskIntegrationCommands.js +3 -2
- package/dist/commands/taskIntegrationQueueCommands.js +1 -1
- package/dist/commands/taskNextActionCommand.js +8 -8
- package/dist/commands/taskOverviewCommand.js +33 -45
- package/dist/commands/taskRemoteDeliveryCommand.js +2 -2
- package/dist/commands/taskRoleRuntimeStatus.js +133 -102
- package/dist/commands/telemetryCommands.js +36 -38
- package/dist/config/configCatalog.js +4 -4
- package/dist/config/yuiConfig.js +8 -8
- package/dist/context/contextSnapshot.js +10 -10
- package/dist/context/dispatchContext.js +11 -11
- package/dist/context/roleSessionContext.js +6 -3
- package/dist/context/{turnContextPack.js → runContextPack.js} +146 -81
- package/dist/context/{turnInputContract.js → runInputContract.js} +73 -60
- package/dist/context/sessionBootstrapManifest.js +21 -2
- package/dist/context/sourceRunContext.js +30 -0
- package/dist/context/taskContext.js +458 -0
- package/dist/context/wakeNotification.js +27 -27
- package/dist/controller/agentRuntimeObserver.js +21 -24
- package/dist/controller/capabilityBridge.js +17 -6
- package/dist/controller/clientRuntime.js +65 -92
- package/dist/controller/controller.js +114 -188
- package/dist/controller/fileSchedulerStoreAdapter.js +787 -887
- package/dist/controller/jobControl.js +54 -85
- package/dist/controller/resourceInventory.js +8 -27
- package/dist/controller/resourceInventoryLinux.js +12 -13
- package/dist/controller/runtime.js +529 -479
- package/dist/controller/runtimeEventInbox.js +55 -25
- package/dist/controller/runtimeEventProcessor.js +22 -31
- package/dist/controller/{runtimeHookTurnFence.js → runtimeHookRunFence.js} +91 -115
- package/dist/controller/runtimeLaunchCoordinator.js +80 -426
- package/dist/controller/runtimeObservationHook.js +14 -18
- package/dist/controller/sessionNotify.js +16 -24
- package/dist/controller/sessionOwnerReconciliation.js +168 -50
- package/dist/controller/structuredProviderObservation.js +138 -99
- package/dist/coordination/workMailbox.js +3 -3
- package/dist/coordination/workMailboxQueue.js +36 -33
- package/dist/core/boundedRpc.js +8 -1
- package/dist/core/controllerClient.js +20 -1
- package/dist/core/controllerServer.js +4 -4
- package/dist/doctor/doctor.js +13 -2
- package/dist/domain/agentResultTransport.js +9 -9
- package/dist/execution/codexThreadNaming.js +2 -8
- package/dist/execution/executionHealth.js +51 -63
- package/dist/execution/reviewMainRun.js +137 -0
- package/dist/execution/workItemExecution.js +28 -29
- package/dist/execution/workItemExecutionProjection.js +99 -107
- package/dist/execution/workItemMainRun.js +141 -0
- package/dist/executor/agentAdapter.js +227 -20
- package/dist/executor/agentConfigurationCatalog.js +126 -4
- package/dist/executor/agentConfigurationProbe.js +162 -4
- package/dist/executor/agentExecutor.js +79 -78
- package/dist/executor/effectiveLaunch.js +105 -18
- package/dist/executor/executorRegistry.js +29 -44
- package/dist/executor/fileRoleLaunchPlanner.js +229 -154
- package/dist/executor/workspacePreflightClassification.js +16 -16
- package/dist/grant/capabilityGrant.js +6 -3
- package/dist/input/inputRequest.js +12 -10
- package/dist/integration/gitIntegrationService.js +4 -11
- package/dist/integration/integrationQueueService.js +4 -4
- package/dist/interaction/operatorPresentation.js +1 -1
- package/dist/kernel/builtinCapabilities.js +247 -12
- package/dist/kernel/capabilityRegistry.js +64 -18
- package/dist/kernel/instanceHost.js +12 -1
- package/dist/kernel/kernelPorts.js +2 -2
- package/dist/lifecycle/canonicalLifecycleEvent.js +44 -49
- package/dist/lifecycle/exactRunTerminalization.js +449 -0
- package/dist/message/message.js +62 -6
- package/dist/message/messageContinuation.js +204 -0
- package/dist/observability/executionAudit.js +70 -72
- package/dist/observability/faultClassification.js +2 -2
- package/dist/observability/orchestrationMetrics.js +8 -8
- package/dist/operator/operatorSessionHistory.js +1 -7
- package/dist/output/agentConfigurationPresentation.js +8 -3
- package/dist/output/agentRunConfigurationPresentation.js +128 -0
- package/dist/output/rolePresentation.js +54 -3
- package/dist/plugins/pluginChild.js +104 -0
- package/dist/plugins/pluginIntent.js +26 -0
- package/dist/plugins/pluginInterpreter.js +43 -0
- package/dist/plugins/pluginPackage.js +101 -0
- package/dist/plugins/pluginProcess.js +112 -0
- package/dist/plugins/pluginService.js +380 -0
- package/dist/profile/agentProfile.js +1 -1
- package/dist/repository/gitWorkspace.js +26 -4
- package/dist/repository/project.js +19 -4
- package/dist/repository/taskBaseFreshness.js +13 -13
- package/dist/repository/taskWorkspaceCoordinator.js +20 -27
- package/dist/repository/taskWorkspacePreparer.js +344 -83
- package/dist/resources/autoResourceGc.js +3 -3
- package/dist/resources/liveReferences.js +3 -3
- package/dist/resources/projectResource.js +123 -0
- package/dist/resources/projectResourceService.js +421 -0
- package/dist/resources/resourceDiscovery.js +6 -6
- package/dist/resources/resourceGc.js +1 -1
- package/dist/resources/resourceRegistrar.js +1 -1
- package/dist/resources/resourceTypes.js +1 -1
- package/dist/review/deltaRecheck.js +3 -3
- package/dist/review/reviewAcceptance.js +16 -16
- package/dist/review/reviewDecision.js +7 -7
- package/dist/review/reviewRound.js +21 -20
- package/dist/review/reviewerAvailability.js +2 -2
- package/dist/role/role.js +51 -7
- package/dist/role/taskRoleUpdate.js +30 -0
- package/dist/runtime/acpProtocol.js +425 -0
- package/dist/runtime/acpSession.js +731 -0
- package/dist/runtime/acpSessionConfiguration.js +260 -0
- package/dist/runtime/agentDriver.js +30 -11
- package/dist/runtime/agentEndpoint.js +278 -0
- package/dist/runtime/agentEndpointIdentity.js +86 -0
- package/dist/runtime/agentEndpointOwnership.js +239 -0
- package/dist/runtime/agentError.js +2 -10
- package/dist/runtime/agentHost.js +565 -314
- package/dist/runtime/agentRunConfiguration.js +258 -0
- package/dist/runtime/builtinAgentDrivers.js +134 -18
- package/dist/runtime/builtinAgentErrorMappers.js +55 -3
- package/dist/runtime/builtinTranscriptUsage.js +1 -1
- package/dist/runtime/claude-process-owner +0 -0
- package/dist/runtime/codexAppServerRuntime.js +38 -30
- package/dist/runtime/codexInteractiveHost.js +41 -6
- package/dist/runtime/continuationManager.js +2 -6
- package/dist/runtime/executionEnvironment.js +30 -0
- package/dist/runtime/firstProgressAdvisory.js +11 -11
- package/dist/runtime/index.js +4 -3
- package/dist/runtime/jsonLineChannel.js +109 -0
- package/dist/runtime/launchBroker.js +91 -16
- package/dist/runtime/launchDiagnostics.js +2 -2
- package/dist/runtime/lifecycleReservation.js +10 -18
- package/dist/runtime/managedCaller.js +61 -17
- package/dist/runtime/nativeSessionControl.js +102 -0
- package/dist/runtime/ports.js +6 -21
- package/dist/runtime/processExitObservation.js +8 -7
- package/dist/runtime/promptEnvelope.js +17 -6
- package/dist/runtime/providerContinuation.js +3 -9
- package/dist/runtime/providerContinuationReconciliationService.js +4 -13
- package/dist/runtime/providerControl.js +2 -7
- package/dist/runtime/providerRuntimeIdentity.js +110 -222
- package/dist/runtime/providerRuntimeReconciler.js +5 -9
- package/dist/runtime/runtimeBinding.js +0 -1
- package/dist/runtime/runtimeContinuationProjection.js +4 -7
- package/dist/runtime/runtimeDeadlines.js +9 -0
- package/dist/runtime/runtimeHealthPolicy.js +1 -1
- package/dist/runtime/runtimeObservation.js +29 -65
- package/dist/runtime/runtimeProjection.js +43 -51
- package/dist/runtime/runtimeSessionCandidate.js +1 -3
- package/dist/runtime/sessionLaunchRequest.js +3 -7
- package/dist/runtime/sessionOwnerIdentity.js +7 -54
- package/dist/runtime/sessionOwnerRegistry.js +22 -17
- package/dist/runtime/sessionReconciliation.js +4 -8
- package/dist/runtime/sessionTerminationGuard.js +70 -259
- package/dist/runtime/sessionTokenMetrics.js +5 -16
- package/dist/runtime/structuredProviderHost.js +237 -117
- package/dist/runtime/taskRuntimeIsolation.js +39 -122
- package/dist/runtime/tmuxAdapters.js +39 -86
- package/dist/scheduler/activeRoleRunDelivery.js +354 -0
- package/dist/scheduler/leaderWakeupProcessor.js +75 -266
- package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
- package/dist/scheduler/ports.js +80 -9
- package/dist/scheduler/{roleTurnLiveness.js → roleRunLiveness.js} +26 -30
- package/dist/scheduler/{roleTurnStall.js → roleRunStall.js} +128 -139
- package/dist/scheduler/taskExecutionProjection.js +120 -124
- package/dist/scheduler/taskObservabilityProjection.js +29 -29
- package/dist/scheduler/taskWake.js +11 -4
- package/dist/scheduler/wakeReason.js +9 -1
- package/dist/setup/setupCommand.js +1 -0
- package/dist/storage/migrations/agentRunContract.js +159 -0
- package/dist/storage/migrations/removeRuntimeGeneration.js +207 -0
- package/dist/storage/sqliteSchema.js +431 -5
- package/dist/storage/sqliteStore.js +375 -220
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +10 -5
- package/dist/storage/taskStore.js +13 -11
- package/dist/storage/upgrade/upgradeOrchestrator.js +5 -7
- package/dist/surface/surfaceContributions.js +102 -0
- package/dist/task/completionReadiness.js +32 -6
- package/dist/task/deliveryGuard.js +16 -16
- package/dist/task/draftPlan.js +72 -12
- package/dist/task/nextAction.js +144 -128
- package/dist/task/remoteDelivery.js +6 -6
- package/dist/task/task.js +184 -18
- package/dist/task/taskActivation.js +301 -0
- package/dist/task/taskActivationService.js +392 -0
- package/dist/task/taskRecordReference.js +5 -4
- package/dist/task/taskRecordRetirement.js +1 -1
- package/dist/telemetry/sqliteTelemetryStore.js +55 -68
- package/dist/telemetry/telemetryConfig.js +14 -14
- package/dist/telemetry/telemetryWiring.js +2 -2
- package/dist/web/assets/assetManifest.js +2 -0
- package/dist/web/assets/client/app.js +120 -20
- package/dist/web/assets/client/components.js +87 -54
- package/dist/web/assets/client/i18n.js +83 -41
- package/dist/web/assets/client/markdown.js +1 -1
- package/dist/web/assets/client/taskSurface.js +353 -0
- package/dist/web/assets/client/view.js +49 -44
- package/dist/web/assets/shell.js +1 -1
- package/dist/web/assets/styles/cards.js +22 -4
- package/dist/web/controllerWeb.js +60 -0
- package/dist/web/webMutation.js +28 -0
- package/dist/web/webServer.js +118 -8
- package/dist/web/webSnapshot.js +81 -74
- package/dist/web/webTaskSurface.js +64 -0
- package/dist/workItem/dependencyGate.js +1 -1
- package/dist/workItem/workItem.js +80 -48
- package/dist/workspace/workItemChangeSetManager.js +16 -9
- package/docs/agent-result-consumption.md +94 -0
- package/docs/agent-runtime-drivers.md +91 -0
- package/docs/architecture/README.md +38 -0
- package/docs/architecture/capabilities-and-resources.md +79 -0
- package/docs/managed-turn-and-session-runtime.md +222 -0
- package/docs/observability/README.md +81 -0
- package/docs/plugin-sdk.md +290 -0
- package/docs/provider-runtime.md +163 -0
- package/docs/release-workflow.md +303 -0
- package/docs/roles-and-configuration.md +113 -0
- package/docs/sqlite-control-plane-design.md +76 -0
- package/docs/task-dag-semantics.md +57 -0
- package/docs/task-delivery.md +103 -0
- package/docs/task-local-identity.md +6 -6
- package/docs/testing/verification-levels.md +86 -0
- package/i18n/README.zh-CN.md +98 -739
- package/package.json +2 -2
- package/skills/yui-leader/SKILL.md +130 -103
- package/skills/yui-leader/references/integration.md +39 -0
- package/skills/yui-leader/references/replicated-execution.md +42 -0
- package/skills/yui-leader/references/task-plugins.md +33 -0
- package/skills/yui-operator/SKILL.md +30 -59
- package/skills/yui-reviewer/SKILL.md +35 -36
- package/skills/yui-runtime/SKILL.md +88 -24
- package/skills/yui-runtime/references/publication.md +22 -0
- package/skills/yui-runtime/references/recovery.md +64 -0
- package/skills/yui-worker/SKILL.md +37 -39
- package/dist/cli/roleOptionCatalog.js +0 -68
- package/dist/context/sourceTurnContext.js +0 -30
- package/dist/execution/reviewMainTurn.js +0 -161
- package/dist/execution/workItemMainTurn.js +0 -164
- package/dist/lifecycle/exactTurnTerminalization.js +0 -407
- package/dist/runtime/preallocatedNativeSession.js +0 -13
- package/dist/runtime/runtimeStopReceipt.js +0 -42
- package/dist/scheduler/activeRoleTurnDelivery.js +0 -315
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
-
import { lstatSync, mkdirSync, readFileSync,
|
|
2
|
+
import { lstatSync, mkdirSync, readFileSync, renameSync, rmdirSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
4
|
import { validateManagedWorkspace } from "../worktree/managedWorkspace.js";
|
|
5
5
|
import { ResourceRegistrar } from "../resources/resourceRegistrar.js";
|
|
@@ -8,7 +8,7 @@ export const YUI_TASK_RUNTIME_SERVICE_NAMESPACE = "YUI_TASK_RUNTIME_SERVICE_NAME
|
|
|
8
8
|
const MARKER_FILE = ".yui-task-runtime-owner.json";
|
|
9
9
|
/**
|
|
10
10
|
* One project-neutral implementation owns descriptor construction, preflight,
|
|
11
|
-
* exact
|
|
11
|
+
* exact runtime activation, and exact runtime cleanup. It never scans by
|
|
12
12
|
* process name, Role, PID, age, or an ambient Home.
|
|
13
13
|
*/
|
|
14
14
|
export class FileTaskRuntimeIsolation {
|
|
@@ -40,7 +40,7 @@ export class FileTaskRuntimeIsolation {
|
|
|
40
40
|
pathLayout: this.#pathLayout,
|
|
41
41
|
controlPlane: this.#controlPlane,
|
|
42
42
|
resources: [
|
|
43
|
-
...
|
|
43
|
+
...inspectRuntimeRoot(descriptor, fingerprint),
|
|
44
44
|
...(this.#inspectResources?.(descriptor) ?? [])
|
|
45
45
|
],
|
|
46
46
|
allowExactActive: input.allowExactActive === true
|
|
@@ -53,13 +53,13 @@ export class FileTaskRuntimeIsolation {
|
|
|
53
53
|
}
|
|
54
54
|
activate(preparation) {
|
|
55
55
|
const { descriptor, fingerprint } = validatePreparation(preparation);
|
|
56
|
-
const root = descriptor.roots.
|
|
57
|
-
const existing =
|
|
56
|
+
const root = descriptor.roots.runtime;
|
|
57
|
+
const existing = inspectRuntimeRoot(descriptor, fingerprint);
|
|
58
58
|
if (existing.length > 0) {
|
|
59
59
|
const [resource] = existing;
|
|
60
60
|
if (resource?.ownership !== "owned"
|
|
61
61
|
|| resource.descriptorFingerprint !== fingerprint) {
|
|
62
|
-
throw new Error(`Task runtime
|
|
62
|
+
throw new Error(`Task runtime workspace is not exactly owned: ${root}.`);
|
|
63
63
|
}
|
|
64
64
|
ensureOwnedDirectories(descriptor);
|
|
65
65
|
this.#resourceRegistrar().registerTaskRuntimeIsolation(descriptor);
|
|
@@ -82,7 +82,7 @@ export class FileTaskRuntimeIsolation {
|
|
|
82
82
|
this.#resourceRegistrar().registerTaskRuntimeIsolation(descriptor);
|
|
83
83
|
}
|
|
84
84
|
catch (error) {
|
|
85
|
-
const current =
|
|
85
|
+
const current = inspectRuntimeRoot(descriptor, fingerprint);
|
|
86
86
|
if (current[0]?.ownership === "owned")
|
|
87
87
|
throw error;
|
|
88
88
|
// The directory was created by this exact activation but never acquired
|
|
@@ -96,21 +96,21 @@ export class FileTaskRuntimeIsolation {
|
|
|
96
96
|
requireCleanupReason(reason);
|
|
97
97
|
const { descriptor, fingerprint } = validatePreparation(preparation);
|
|
98
98
|
const resources = [
|
|
99
|
-
...
|
|
99
|
+
...inspectRuntimeRoot(descriptor, fingerprint),
|
|
100
100
|
...(this.#inspectResources?.(descriptor) ?? [])
|
|
101
101
|
];
|
|
102
102
|
if (resources.length === 0)
|
|
103
103
|
return;
|
|
104
104
|
planTaskRuntimeCleanup(descriptor, reason, resources);
|
|
105
105
|
// Re-read the sole durable marker immediately before deletion. A missing,
|
|
106
|
-
// replaced, symlinked, or mismatched
|
|
107
|
-
const current =
|
|
106
|
+
// replaced, symlinked, or mismatched runtime is never cleanup authority.
|
|
107
|
+
const current = inspectRuntimeRoot(descriptor, fingerprint);
|
|
108
108
|
if (current.length !== 1
|
|
109
109
|
|| current[0]?.ownership !== "owned"
|
|
110
110
|
|| current[0].descriptorFingerprint !== fingerprint) {
|
|
111
111
|
throw new Error("Task runtime resources changed since cleanup preflight.");
|
|
112
112
|
}
|
|
113
|
-
const root = descriptor.roots.
|
|
113
|
+
const root = descriptor.roots.runtime;
|
|
114
114
|
const claimed = `${root}.cleanup-${randomBytes(16).toString("hex")}`;
|
|
115
115
|
renameSync(root, claimed);
|
|
116
116
|
try {
|
|
@@ -121,7 +121,7 @@ export class FileTaskRuntimeIsolation {
|
|
|
121
121
|
}
|
|
122
122
|
rmSync(claimed, { recursive: true });
|
|
123
123
|
this.#resourceRegistrar().markPathsDeleted([
|
|
124
|
-
descriptor.roots.
|
|
124
|
+
descriptor.roots.runtime,
|
|
125
125
|
descriptor.roots.data,
|
|
126
126
|
descriptor.roots.cache,
|
|
127
127
|
descriptor.roots.temporary
|
|
@@ -129,7 +129,7 @@ export class FileTaskRuntimeIsolation {
|
|
|
129
129
|
}
|
|
130
130
|
catch (error) {
|
|
131
131
|
// Preserve a claimed-but-unverified resource. Restore its exact path only
|
|
132
|
-
// when no concurrent
|
|
132
|
+
// when no concurrent runtime has appeared there; never delete it.
|
|
133
133
|
try {
|
|
134
134
|
renameSync(claimed, root);
|
|
135
135
|
}
|
|
@@ -139,84 +139,14 @@ export class FileTaskRuntimeIsolation {
|
|
|
139
139
|
throw error;
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
|
-
cleanupTaskLaunch(input) {
|
|
143
|
-
const taskId = requireIdentity(input.taskId, "Task id");
|
|
144
|
-
const runtimeGenerationId = requireIdentity(input.runtimeGenerationId, "Runtime generation id");
|
|
145
|
-
requireCleanupReason(input.reason);
|
|
146
|
-
const taskRoot = taskRuntimeInventoryRoot(this.#runtimeRoot, taskId, this.#pathLayout);
|
|
147
|
-
let entries;
|
|
148
|
-
try {
|
|
149
|
-
const metadata = lstatSync(taskRoot);
|
|
150
|
-
if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
|
|
151
|
-
throw new Error("Task runtime Task root is ambiguous.");
|
|
152
|
-
}
|
|
153
|
-
entries = readdirSync(taskRoot, { withFileTypes: true });
|
|
154
|
-
}
|
|
155
|
-
catch (error) {
|
|
156
|
-
if (isNodeCode(error, "ENOENT"))
|
|
157
|
-
return "absent";
|
|
158
|
-
throw error;
|
|
159
|
-
}
|
|
160
|
-
const launchDigest = taskRuntimeLaunchDigest(taskId, runtimeGenerationId, this.#pathLayout);
|
|
161
|
-
const matches = [];
|
|
162
|
-
for (const entry of entries) {
|
|
163
|
-
if (!entry.isDirectory() || entry.isSymbolicLink()) {
|
|
164
|
-
throw new Error("Task runtime owner inventory is ambiguous.");
|
|
165
|
-
}
|
|
166
|
-
const generationRoot = join(taskRoot, entry.name, launchDigest);
|
|
167
|
-
let metadata;
|
|
168
|
-
try {
|
|
169
|
-
metadata = lstatSync(generationRoot);
|
|
170
|
-
}
|
|
171
|
-
catch (error) {
|
|
172
|
-
if (isNodeCode(error, "ENOENT"))
|
|
173
|
-
continue;
|
|
174
|
-
throw new Error("Task runtime generation inventory is ambiguous.", { cause: error });
|
|
175
|
-
}
|
|
176
|
-
if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
|
|
177
|
-
throw new Error("Task runtime generation inventory is ambiguous.");
|
|
178
|
-
}
|
|
179
|
-
let marker;
|
|
180
|
-
try {
|
|
181
|
-
marker = parseMarker(readFileSync(join(generationRoot, MARKER_FILE), "utf8"));
|
|
182
|
-
}
|
|
183
|
-
catch (error) {
|
|
184
|
-
throw new Error("Task runtime generation is unmarked or invalid.", {
|
|
185
|
-
cause: error
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
const descriptor = marker.descriptor;
|
|
189
|
-
const fingerprint = taskRuntimeIsolationFingerprint(descriptor);
|
|
190
|
-
if (marker.fingerprint !== fingerprint
|
|
191
|
-
|| descriptor.taskId !== taskId
|
|
192
|
-
|| descriptor.generation.runtimeGenerationId !== runtimeGenerationId
|
|
193
|
-
|| descriptor.roots.generation !== generationRoot) {
|
|
194
|
-
throw new Error("Task runtime generation ownership is mismatched.");
|
|
195
|
-
}
|
|
196
|
-
matches.push({
|
|
197
|
-
descriptor,
|
|
198
|
-
fingerprint,
|
|
199
|
-
environment: taskRuntimeIsolationEnvironment(descriptor)
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
if (matches.length === 0)
|
|
203
|
-
return "absent";
|
|
204
|
-
if (matches.length !== 1) {
|
|
205
|
-
throw new Error("Task runtime generation ownership is ambiguous.");
|
|
206
|
-
}
|
|
207
|
-
this.cleanup(matches[0], input.reason);
|
|
208
|
-
return "cleaned";
|
|
209
|
-
}
|
|
210
142
|
}
|
|
211
143
|
export function createTaskRuntimeIsolationDescriptor(input) {
|
|
212
144
|
const workspace = validateManagedWorkspace(input.workspace);
|
|
213
145
|
const owner = taskRuntimeWorkspaceOwner(workspace.owner);
|
|
214
146
|
const taskId = requireIdentity(owner.taskId, "Task id");
|
|
215
|
-
const runtimeGenerationId = requireIdentity(input.runtimeGenerationId, "Runtime generation id");
|
|
216
|
-
const generationId = requireIdentity(input.generationId, "Generation id");
|
|
217
147
|
const runtimeRoot = canonicalPath(input.runtimeRoot, "Task runtime root");
|
|
218
148
|
const pathLayout = taskRuntimePathLayout(input.pathLayout);
|
|
219
|
-
const
|
|
149
|
+
const runtime = taskRuntimeWorkspaceRoot(runtimeRoot, taskId, owner, pathLayout);
|
|
220
150
|
const declared = capabilities(input.policy?.declaredExternalCapabilities ?? [], "Declared external capability");
|
|
221
151
|
const requested = capabilities(input.policy?.requestedExternalCapabilities ?? [], "Requested external capability");
|
|
222
152
|
const portPreference = ports(input.policy?.portPreference ?? [], "Port preference");
|
|
@@ -227,19 +157,15 @@ export function createTaskRuntimeIsolationDescriptor(input) {
|
|
|
227
157
|
taskId,
|
|
228
158
|
workspace: Object.freeze({ owner, root: canonicalPath(workspace.root, "Workspace root") }),
|
|
229
159
|
roots: Object.freeze({
|
|
230
|
-
|
|
231
|
-
data: join(
|
|
232
|
-
cache: join(
|
|
233
|
-
temporary: join(
|
|
160
|
+
runtime,
|
|
161
|
+
data: join(runtime, "data"),
|
|
162
|
+
cache: join(runtime, "cache"),
|
|
163
|
+
temporary: join(runtime, "tmp")
|
|
234
164
|
}),
|
|
235
|
-
serviceNamespace: taskRuntimeServiceNamespace(taskId, owner,
|
|
165
|
+
serviceNamespace: taskRuntimeServiceNamespace(taskId, owner, runtime),
|
|
236
166
|
portPreference,
|
|
237
167
|
portAllocations,
|
|
238
168
|
externalCapabilities: Object.freeze({ declared, requested }),
|
|
239
|
-
generation: Object.freeze({
|
|
240
|
-
runtimeGenerationId,
|
|
241
|
-
generationId
|
|
242
|
-
})
|
|
243
169
|
});
|
|
244
170
|
}
|
|
245
171
|
export function parseTaskRuntimeIsolationDescriptor(serialized) {
|
|
@@ -258,7 +184,6 @@ export function parseTaskRuntimeIsolationDescriptor(serialized) {
|
|
|
258
184
|
}
|
|
259
185
|
const workspace = requireRecord(value.workspace, "Task runtime workspace");
|
|
260
186
|
const roots = requireRecord(value.roots, "Task runtime roots");
|
|
261
|
-
const generation = requireRecord(value.generation, "Task runtime generation");
|
|
262
187
|
const external = requireRecord(value.externalCapabilities, "Task runtime external capabilities");
|
|
263
188
|
const owner = taskRuntimeWorkspaceOwner(requireRecord(workspace.owner, "Task runtime workspace owner"));
|
|
264
189
|
const descriptor = {
|
|
@@ -270,7 +195,7 @@ export function parseTaskRuntimeIsolationDescriptor(serialized) {
|
|
|
270
195
|
root: canonicalPath(workspace.root, "Workspace root")
|
|
271
196
|
},
|
|
272
197
|
roots: {
|
|
273
|
-
|
|
198
|
+
runtime: canonicalPath(roots.runtime, "Task runtime workspace root"),
|
|
274
199
|
data: canonicalPath(roots.data, "Task runtime data root"),
|
|
275
200
|
cache: canonicalPath(roots.cache, "Task runtime cache root"),
|
|
276
201
|
temporary: canonicalPath(roots.temporary, "Task runtime temporary root")
|
|
@@ -282,10 +207,6 @@ export function parseTaskRuntimeIsolationDescriptor(serialized) {
|
|
|
282
207
|
declared: capabilities(external.declared, "Declared external capability"),
|
|
283
208
|
requested: capabilities(external.requested, "Requested external capability")
|
|
284
209
|
},
|
|
285
|
-
generation: {
|
|
286
|
-
runtimeGenerationId: requireIdentity(generation.runtimeGenerationId, "Runtime generation id"),
|
|
287
|
-
generationId: requireIdentity(generation.generationId, "Generation id")
|
|
288
|
-
}
|
|
289
210
|
};
|
|
290
211
|
return Object.freeze(descriptor);
|
|
291
212
|
}
|
|
@@ -315,22 +236,22 @@ export function assertTaskRuntimeIsolationPreflight(input) {
|
|
|
315
236
|
}
|
|
316
237
|
const runtimeRoot = canonicalPath(input.runtimeRoot, "Task runtime root");
|
|
317
238
|
const pathLayout = taskRuntimePathLayout(input.pathLayout);
|
|
318
|
-
const
|
|
319
|
-
if (descriptor.roots.
|
|
320
|
-
|| descriptor.roots.data !== join(
|
|
321
|
-
|| descriptor.roots.cache !== join(
|
|
322
|
-
|| descriptor.roots.temporary !== join(
|
|
323
|
-
throw new Error("Task runtime roots do not match the exact Task, owner, and runtime
|
|
239
|
+
const expectedRuntimeRoot = taskRuntimeWorkspaceRoot(runtimeRoot, descriptor.taskId, expectedOwner, pathLayout);
|
|
240
|
+
if (descriptor.roots.runtime !== expectedRuntimeRoot
|
|
241
|
+
|| descriptor.roots.data !== join(expectedRuntimeRoot, "data")
|
|
242
|
+
|| descriptor.roots.cache !== join(expectedRuntimeRoot, "cache")
|
|
243
|
+
|| descriptor.roots.temporary !== join(expectedRuntimeRoot, "tmp")) {
|
|
244
|
+
throw new Error("Task runtime roots do not match the exact Task, owner, and runtime workspace identity.");
|
|
324
245
|
}
|
|
325
|
-
if (descriptor.serviceNamespace !== taskRuntimeServiceNamespace(descriptor.taskId, expectedOwner, descriptor.
|
|
326
|
-
throw new Error("Task runtime service namespace does not match its exact Task runtime
|
|
246
|
+
if (descriptor.serviceNamespace !== taskRuntimeServiceNamespace(descriptor.taskId, expectedOwner, descriptor.roots.runtime)) {
|
|
247
|
+
throw new Error("Task runtime service namespace does not match its exact Task runtime workspace.");
|
|
327
248
|
}
|
|
328
|
-
if (!isWithin(runtimeRoot, descriptor.roots.
|
|
329
|
-
throw new Error("Task runtime
|
|
249
|
+
if (!isWithin(runtimeRoot, descriptor.roots.runtime)) {
|
|
250
|
+
throw new Error("Task runtime workspace is outside its runtime root.");
|
|
330
251
|
}
|
|
331
252
|
for (const [name, root] of Object.entries(descriptor.roots)) {
|
|
332
|
-
if (name !== "
|
|
333
|
-
throw new Error(`Task runtime ${name} root is outside its exact
|
|
253
|
+
if (name !== "runtime" && !isWithin(descriptor.roots.runtime, root)) {
|
|
254
|
+
throw new Error(`Task runtime ${name} root is outside its exact runtime.`);
|
|
334
255
|
}
|
|
335
256
|
}
|
|
336
257
|
const control = normalizeControlBoundary(input.controlPlane);
|
|
@@ -394,8 +315,8 @@ export function planTaskRuntimeCleanup(descriptor, reason, resources) {
|
|
|
394
315
|
}
|
|
395
316
|
return Object.freeze([...ids].sort());
|
|
396
317
|
}
|
|
397
|
-
function
|
|
398
|
-
const root = descriptor.roots.
|
|
318
|
+
function inspectRuntimeRoot(descriptor, expectedFingerprint) {
|
|
319
|
+
const root = descriptor.roots.runtime;
|
|
399
320
|
let metadata;
|
|
400
321
|
try {
|
|
401
322
|
metadata = lstatSync(root);
|
|
@@ -453,7 +374,7 @@ function ensureOwnedDirectories(descriptor) {
|
|
|
453
374
|
join(descriptor.roots.data, "state"),
|
|
454
375
|
join(descriptor.roots.temporary, "runtime")
|
|
455
376
|
]) {
|
|
456
|
-
ensureDirectoryChain(descriptor.roots.
|
|
377
|
+
ensureDirectoryChain(descriptor.roots.runtime, path);
|
|
457
378
|
}
|
|
458
379
|
}
|
|
459
380
|
function ensureDirectoryChain(boundary, target) {
|
|
@@ -494,8 +415,8 @@ function validatePreparation(preparation) {
|
|
|
494
415
|
function taskRuntimeWorkspaceOwner(owner) {
|
|
495
416
|
return owner;
|
|
496
417
|
}
|
|
497
|
-
function
|
|
498
|
-
return join(taskRuntimeInventoryRoot(runtimeRoot, taskId, pathLayout), taskRuntimeOwnerDigest(taskId, owner, pathLayout),
|
|
418
|
+
function taskRuntimeWorkspaceRoot(runtimeRoot, taskId, owner, pathLayout = "hierarchical") {
|
|
419
|
+
return join(taskRuntimeInventoryRoot(runtimeRoot, taskId, pathLayout), taskRuntimeOwnerDigest(taskId, owner, pathLayout), "runtime");
|
|
499
420
|
}
|
|
500
421
|
function taskRuntimeInventoryRoot(runtimeRoot, taskId, pathLayout) {
|
|
501
422
|
return pathLayout === "compact" ? runtimeRoot : join(runtimeRoot, taskId);
|
|
@@ -505,9 +426,6 @@ function taskRuntimeOwnerDigest(taskId, owner, pathLayout) {
|
|
|
505
426
|
? digest(JSON.stringify([taskId, owner])).slice(0, 20)
|
|
506
427
|
: digest(JSON.stringify(owner)).slice(0, 24);
|
|
507
428
|
}
|
|
508
|
-
function taskRuntimeLaunchDigest(taskId, runtimeGenerationId, pathLayout) {
|
|
509
|
-
return digest(JSON.stringify([taskId, runtimeGenerationId])).slice(0, pathLayout === "compact" ? 20 : 24);
|
|
510
|
-
}
|
|
511
429
|
function taskRuntimePathLayout(value) {
|
|
512
430
|
const pathLayout = value ?? "hierarchical";
|
|
513
431
|
if (pathLayout !== "hierarchical" && pathLayout !== "compact") {
|
|
@@ -515,12 +433,11 @@ function taskRuntimePathLayout(value) {
|
|
|
515
433
|
}
|
|
516
434
|
return pathLayout;
|
|
517
435
|
}
|
|
518
|
-
function taskRuntimeServiceNamespace(taskId, owner,
|
|
436
|
+
function taskRuntimeServiceNamespace(taskId, owner, runtimeRoot) {
|
|
519
437
|
return `yui-task-${digest(JSON.stringify([
|
|
520
438
|
taskId,
|
|
521
439
|
owner,
|
|
522
|
-
|
|
523
|
-
generationId
|
|
440
|
+
runtimeRoot
|
|
524
441
|
])).slice(0, 24)}`;
|
|
525
442
|
}
|
|
526
443
|
function normalizeControlBoundary(value) {
|
|
@@ -3,12 +3,12 @@ import { fileURLToPath } from "node:url";
|
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { createRuntimeBinding } from "./runtimeBinding.js";
|
|
5
5
|
import { normalizeRuntimeOwner } from "./runtimeOwner.js";
|
|
6
|
-
import {
|
|
6
|
+
import { RuntimeHostContentionError, RuntimeHostUnavailableError, promptPushOutcome } from "./ports.js";
|
|
7
7
|
import { providerDeliveryFailureFrom } from "./agentError.js";
|
|
8
8
|
import { toRuntimeLaunchFailure } from "./launchDiagnostics.js";
|
|
9
9
|
import { requireSafeIdentity } from "./validation.js";
|
|
10
10
|
import { launchBrokerForHome } from "./launchBroker.js";
|
|
11
|
-
import { AGENT_HOST_CONTROL_PROTOCOL, sendAgentHostLaunchControl,
|
|
11
|
+
import { AGENT_HOST_CONTROL_PROTOCOL, sendAgentHostLaunchControl, sendAgentHostRunControl, sendAgentHostSteerControl, waitForAgentHostLaunchAck } from "./agentHost.js";
|
|
12
12
|
const DEFAULT_INACTIVITY_TIMEOUT_MS = 300_000;
|
|
13
13
|
/**
|
|
14
14
|
* Runtime lifecycle adapter for the current tmux host. The returned hostRef is
|
|
@@ -143,8 +143,8 @@ export class TmuxSessionHost {
|
|
|
143
143
|
const key = hostId;
|
|
144
144
|
const previous = this.#launchTails.get(key) ?? Promise.resolve();
|
|
145
145
|
let release;
|
|
146
|
-
const
|
|
147
|
-
const tail = previous.then(() =>
|
|
146
|
+
const run = new Promise((resolve) => { release = resolve; });
|
|
147
|
+
const tail = previous.then(() => run);
|
|
148
148
|
this.#launchTails.set(key, tail);
|
|
149
149
|
await previous;
|
|
150
150
|
try {
|
|
@@ -160,7 +160,7 @@ export class TmuxSessionHost {
|
|
|
160
160
|
// Validate generated identity before starting an external process.
|
|
161
161
|
const bindingId = requireSafeIdentity(this.#createBindingId(), "Runtime binding id");
|
|
162
162
|
const writableHumanAttached = request.owner.scope === "task"
|
|
163
|
-
&& request.
|
|
163
|
+
&& request.runId !== undefined
|
|
164
164
|
&& (this.tmux.hasWritableClientAsync !== undefined
|
|
165
165
|
? await this.tmux.hasWritableClientAsync(hostId, request.owner.roleName)
|
|
166
166
|
: this.tmux.hasWritableClient?.(hostId, request.owner.roleName) === true);
|
|
@@ -172,9 +172,8 @@ export class TmuxSessionHost {
|
|
|
172
172
|
agentId: request.agentId,
|
|
173
173
|
adapterId: request.adapterId,
|
|
174
174
|
effective: request.effective,
|
|
175
|
-
runtimeGenerationId: request.runtimeGenerationId,
|
|
176
175
|
mode: request.mode,
|
|
177
|
-
...(request.
|
|
176
|
+
...(request.runId === undefined ? {} : { runId: request.runId }),
|
|
178
177
|
...(request.runtimeIsolation === undefined
|
|
179
178
|
? {}
|
|
180
179
|
: { runtimeIsolation: request.runtimeIsolation }),
|
|
@@ -237,8 +236,7 @@ export class TmuxSessionHost {
|
|
|
237
236
|
?? (request.mode === "resume" ? request.nativeSessionId : undefined);
|
|
238
237
|
beforeHostStart?.({
|
|
239
238
|
owner: request.owner,
|
|
240
|
-
|
|
241
|
-
...(request.turnId === undefined ? {} : { turnId: request.turnId }),
|
|
239
|
+
...(request.runId === undefined ? {} : { runId: request.runId }),
|
|
242
240
|
agentId: request.agentId,
|
|
243
241
|
adapterId: request.adapterId,
|
|
244
242
|
effective: request.effective,
|
|
@@ -256,8 +254,8 @@ export class TmuxSessionHost {
|
|
|
256
254
|
|| (!interactiveCodex && (yuiHome === undefined
|
|
257
255
|
|| childLifecycle === undefined
|
|
258
256
|
|| planned.launch.providerControl === undefined))) {
|
|
259
|
-
if (request.owner.scope === "task" && request.
|
|
260
|
-
throw new Error("Managed Task
|
|
257
|
+
if (request.owner.scope === "task" && request.runId !== undefined) {
|
|
258
|
+
throw new Error("Managed Task AgentRun is missing its structured Agent Host contract.");
|
|
261
259
|
}
|
|
262
260
|
let hostCreated;
|
|
263
261
|
try {
|
|
@@ -271,7 +269,6 @@ export class TmuxSessionHost {
|
|
|
271
269
|
}
|
|
272
270
|
let binding = createRuntimeBinding({
|
|
273
271
|
id: bindingId,
|
|
274
|
-
runtimeGenerationId: request.runtimeGenerationId,
|
|
275
272
|
owner: request.owner,
|
|
276
273
|
agentId: request.agentId,
|
|
277
274
|
adapterId: request.adapterId,
|
|
@@ -290,7 +287,7 @@ export class TmuxSessionHost {
|
|
|
290
287
|
}
|
|
291
288
|
if (request.mode === "new"
|
|
292
289
|
&& request.owner.scope === "task"
|
|
293
|
-
&& request.
|
|
290
|
+
&& request.runId !== undefined
|
|
294
291
|
&& this.#waitForNativeSession !== undefined) {
|
|
295
292
|
binding = createRuntimeBinding({
|
|
296
293
|
...binding,
|
|
@@ -307,22 +304,27 @@ export class TmuxSessionHost {
|
|
|
307
304
|
}
|
|
308
305
|
const broker = launchBrokerForHome(yuiHome);
|
|
309
306
|
const sessionManifest = planned.launch.env.YUI_SESSION_MANIFEST;
|
|
310
|
-
if (request.owner.scope === "task" && request.
|
|
307
|
+
if (request.owner.scope === "task" && request.runId !== undefined
|
|
311
308
|
&& sessionManifest === undefined) {
|
|
312
309
|
throw new Error("Managed Task Agent Host launch is missing its Session Manifest.");
|
|
313
310
|
}
|
|
314
311
|
const reservation = broker.reserve(Object.freeze({
|
|
315
312
|
schemaVersion: 2,
|
|
316
|
-
runtimeGenerationId: request.runtimeGenerationId,
|
|
317
313
|
command: planned.launch.command,
|
|
318
314
|
args: [...planned.launch.args],
|
|
319
315
|
environment: { ...planned.launch.env },
|
|
320
316
|
cwd: planned.role.cwd ?? planned.role.workspace,
|
|
317
|
+
...(planned.launch.executionEnvironment === undefined ? {} : {
|
|
318
|
+
executionEnvironment: structuredClone(planned.launch.executionEnvironment)
|
|
319
|
+
}),
|
|
321
320
|
childLifecycle,
|
|
322
321
|
startMode: planned.launch.deferProviderStart === true ? "idle" : "provider",
|
|
323
322
|
...(planned.launch.providerControl === undefined
|
|
324
323
|
? {}
|
|
325
|
-
: { providerControl: planned.launch.providerControl })
|
|
324
|
+
: { providerControl: planned.launch.providerControl }),
|
|
325
|
+
...(planned.launch.interactiveCodexThread === undefined
|
|
326
|
+
? {}
|
|
327
|
+
: { interactiveCodexThread: planned.launch.interactiveCodexThread })
|
|
326
328
|
}));
|
|
327
329
|
const hostLaunch = {
|
|
328
330
|
command: process.execPath,
|
|
@@ -330,7 +332,6 @@ export class TmuxSessionHost {
|
|
|
330
332
|
fileURLToPath(new URL("../cli.js", import.meta.url)),
|
|
331
333
|
"internal",
|
|
332
334
|
"agent-host",
|
|
333
|
-
reservation.runtimeGenerationId,
|
|
334
335
|
reservation.ticket
|
|
335
336
|
],
|
|
336
337
|
env: {
|
|
@@ -338,7 +339,6 @@ export class TmuxSessionHost {
|
|
|
338
339
|
YUI_SESSION_SCOPE: request.owner.scope,
|
|
339
340
|
...(request.owner.scope === "task" ? { YUI_TASK_ID: request.owner.taskId } : {}),
|
|
340
341
|
YUI_ROLE: request.owner.roleName,
|
|
341
|
-
YUI_RUNTIME_GENERATION_ID: request.runtimeGenerationId,
|
|
342
342
|
...(planned.launch.env.YUI_AGENT_ID === undefined
|
|
343
343
|
? {}
|
|
344
344
|
: { YUI_AGENT_ID: planned.launch.env.YUI_AGENT_ID }),
|
|
@@ -354,7 +354,6 @@ export class TmuxSessionHost {
|
|
|
354
354
|
}
|
|
355
355
|
};
|
|
356
356
|
let hostCreated = false;
|
|
357
|
-
let providerDispatchObserved = false;
|
|
358
357
|
let providerSnapshot;
|
|
359
358
|
try {
|
|
360
359
|
hostCreated = await ensureRoleWindow(this.tmux, hostId, planned.role, hostLaunch);
|
|
@@ -372,8 +371,7 @@ export class TmuxSessionHost {
|
|
|
372
371
|
scope: request.owner.scope,
|
|
373
372
|
...(request.owner.scope === "task" ? { taskId: request.owner.taskId } : {}),
|
|
374
373
|
roleName: request.owner.roleName,
|
|
375
|
-
|
|
376
|
-
requireTurnAck: false,
|
|
374
|
+
requireRunAck: false,
|
|
377
375
|
...(interactiveCodex ? { assertHostRunning: assertInteractivePane } : {})
|
|
378
376
|
});
|
|
379
377
|
if (interactiveCodex) {
|
|
@@ -383,7 +381,6 @@ export class TmuxSessionHost {
|
|
|
383
381
|
requireSafeIdentity(providerSnapshot.nativeSessionId, "Codex Thread identity");
|
|
384
382
|
await assertInteractivePane();
|
|
385
383
|
}
|
|
386
|
-
providerDispatchObserved = true;
|
|
387
384
|
}
|
|
388
385
|
if (!hostCreated) {
|
|
389
386
|
if (interactiveCodex) {
|
|
@@ -397,74 +394,32 @@ export class TmuxSessionHost {
|
|
|
397
394
|
control: {
|
|
398
395
|
protocol: AGENT_HOST_CONTROL_PROTOCOL,
|
|
399
396
|
type: "launch",
|
|
400
|
-
runtimeGenerationId: reservation.runtimeGenerationId,
|
|
401
397
|
ticket: reservation.ticket
|
|
402
398
|
}
|
|
403
399
|
});
|
|
404
|
-
if (controlResult.outcome === "
|
|
405
|
-
broker.revoke(
|
|
400
|
+
if (controlResult.outcome === "busy") {
|
|
401
|
+
broker.revoke(reservation.ticket);
|
|
406
402
|
throw new RuntimeHostContentionError("provider-child-active", `The persistent Agent Host for ${request.owner.roleName} still owns another Provider Turn.`);
|
|
407
403
|
}
|
|
408
|
-
if (controlResult.outcome === "active-same-generation") {
|
|
409
|
-
broker.revoke(request.runtimeGenerationId);
|
|
410
|
-
}
|
|
411
|
-
// Identity and readiness are separate facts. Only a genuinely
|
|
412
|
-
// different generation is a conflict that must fail closed, not
|
|
413
|
-
// permission to stop the Host; a matching unsettled generation is this
|
|
414
|
-
// exact activation still coming up, and stopping it would destroy a
|
|
415
|
-
// healthy Session (and any Turn it is carrying).
|
|
416
|
-
const observedGeneration = controlResult.snapshot.runtimeGenerationId;
|
|
417
|
-
if (observedGeneration !== reservation.runtimeGenerationId) {
|
|
418
|
-
broker.revoke(request.runtimeGenerationId);
|
|
419
|
-
throw new RuntimeGenerationMismatchError(reservation.runtimeGenerationId, observedGeneration, controlResult.snapshot.state, `Agent Host acknowledgement generation mismatch for ${reservation.runtimeGenerationId}; observed=${observedGeneration ?? "none"}; `
|
|
420
|
-
+ `state=${controlResult.snapshot.state}${describeHostFailure(controlResult)}.`);
|
|
421
|
-
}
|
|
422
404
|
if (!["idle", "ready", "busy"].includes(controlResult.snapshot.state)) {
|
|
423
|
-
broker.revoke(
|
|
405
|
+
broker.revoke(reservation.ticket);
|
|
424
406
|
if (["starting", "settling", "delivery-unknown"].includes(controlResult.snapshot.state)) {
|
|
425
|
-
//
|
|
426
|
-
|
|
427
|
-
// the caller retry rather than terminalizing the Turn.
|
|
428
|
-
throw new RuntimeHostContentionError("provider-child-active", `The Agent Host for ${request.owner.roleName} is still ${controlResult.snapshot.state} on this exact generation${describeHostFailure(controlResult)}.`);
|
|
407
|
+
// A temporarily unavailable Session preserves the pending input.
|
|
408
|
+
throw new RuntimeHostContentionError("provider-child-active", `The Agent Host for ${request.owner.roleName} is still ${controlResult.snapshot.state}${describeHostFailure(controlResult)}.`);
|
|
429
409
|
}
|
|
430
410
|
// This launch is unusable. It did not create this Host, so failure
|
|
431
411
|
// is not authority to clean its resources or unknown execution.
|
|
432
|
-
throw new RuntimeHostUnavailableError(
|
|
412
|
+
throw new RuntimeHostUnavailableError(controlResult.snapshot.state, `Agent Host reached ${controlResult.snapshot.state}${describeHostFailure(controlResult)}.`, { cause: controlResult.failure ?? controlResult.snapshot });
|
|
433
413
|
}
|
|
434
414
|
providerSnapshot = controlResult.snapshot;
|
|
435
|
-
providerDispatchObserved = true;
|
|
436
415
|
}
|
|
437
416
|
}
|
|
438
417
|
catch (error) {
|
|
439
|
-
broker.revoke(
|
|
440
|
-
if (hostCreated && !providerDispatchObserved) {
|
|
441
|
-
try {
|
|
442
|
-
await stopExactRole(this.tmux, hostId, request.owner.roleName);
|
|
443
|
-
}
|
|
444
|
-
catch (stopError) {
|
|
445
|
-
throw new Error(`Managed Provider launch failed and its disposable Agent Host could not be stopped: ${stopError instanceof Error ? stopError.message : String(stopError)}`, { cause: stopError });
|
|
446
|
-
}
|
|
447
|
-
}
|
|
418
|
+
broker.revoke(reservation.ticket);
|
|
448
419
|
throw error;
|
|
449
420
|
}
|
|
450
|
-
if (providerDispatchObserved
|
|
451
|
-
// A fresh Host always starts a Provider process. mode=new also starts
|
|
452
|
-
// one inside a reused Host by replacing (or creating) the native
|
|
453
|
-
// Conversation. Same-Conversation resume must retain its inherited key.
|
|
454
|
-
&& (hostCreated || request.mode === "new")
|
|
455
|
-
&& request.owner.scope === "task"
|
|
456
|
-
&& planned.launch.env.YUI_JOB_CALLER_KEY !== undefined
|
|
457
|
-
&& this.planner.commitTaskCallerKey !== undefined) {
|
|
458
|
-
this.planner.commitTaskCallerKey({
|
|
459
|
-
taskId: request.owner.taskId,
|
|
460
|
-
roleName: request.owner.roleName,
|
|
461
|
-
agentId: request.agentId,
|
|
462
|
-
callerKey: planned.launch.env.YUI_JOB_CALLER_KEY
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
421
|
const binding = createRuntimeBinding({
|
|
466
422
|
id: bindingId,
|
|
467
|
-
runtimeGenerationId: request.runtimeGenerationId,
|
|
468
423
|
owner: request.owner,
|
|
469
424
|
agentId: request.agentId,
|
|
470
425
|
adapterId: request.adapterId,
|
|
@@ -519,12 +474,7 @@ export class TmuxSessionHost {
|
|
|
519
474
|
return await Promise.race([discovery, monitor]);
|
|
520
475
|
}
|
|
521
476
|
catch (error) {
|
|
522
|
-
|
|
523
|
-
await stopExactRole(this.tmux, hostId, request.owner.roleName);
|
|
524
|
-
}
|
|
525
|
-
catch {
|
|
526
|
-
// Preserve the discovery failure; durable cleanup owns later retries.
|
|
527
|
-
}
|
|
477
|
+
// Missing discovery is not evidence that the running process is unwanted.
|
|
528
478
|
throw toRuntimeLaunchFailure(error, "native-session-discovery", context);
|
|
529
479
|
}
|
|
530
480
|
finally {
|
|
@@ -565,7 +515,7 @@ function describeHostFailure(result) {
|
|
|
565
515
|
const detail = result.failure?.detail ?? result.snapshot.detail;
|
|
566
516
|
return detail === undefined ? "" : `; detail=${detail}`;
|
|
567
517
|
}
|
|
568
|
-
/** Structured managed-
|
|
518
|
+
/** Structured managed-AgentRun input; tmux remains presentation/liveness only. */
|
|
569
519
|
export class AgentHostPromptPushAdapter {
|
|
570
520
|
home;
|
|
571
521
|
constructor(home) {
|
|
@@ -579,7 +529,7 @@ export class AgentHostPromptPushAdapter {
|
|
|
579
529
|
return promptPushOutcome("unavailable");
|
|
580
530
|
}
|
|
581
531
|
try {
|
|
582
|
-
const result = await
|
|
532
|
+
const result = await sendAgentHostRunControl({
|
|
583
533
|
home: this.home,
|
|
584
534
|
scope: "task",
|
|
585
535
|
taskId: ref.hostId,
|
|
@@ -587,11 +537,11 @@ export class AgentHostPromptPushAdapter {
|
|
|
587
537
|
control: {
|
|
588
538
|
protocol: AGENT_HOST_CONTROL_PROTOCOL,
|
|
589
539
|
type: "submit-turn",
|
|
590
|
-
runtimeGenerationId: request.binding.runtimeGenerationId,
|
|
591
540
|
nativeSessionId: request.binding.nativeSessionId,
|
|
592
|
-
|
|
541
|
+
...(request.envelope.source.kind === "notification"
|
|
542
|
+
? {} : { runId: request.envelope.source.localId }),
|
|
593
543
|
authority: request.binding.providerAuthority,
|
|
594
|
-
|
|
544
|
+
run: {
|
|
595
545
|
attemptId: request.envelope.id,
|
|
596
546
|
boundedText: request.envelope.text
|
|
597
547
|
}
|
|
@@ -599,6 +549,8 @@ export class AgentHostPromptPushAdapter {
|
|
|
599
549
|
});
|
|
600
550
|
// The Host attaches its own structured cause to every non-delivery.
|
|
601
551
|
const failure = result.failure;
|
|
552
|
+
if (result.outcome === "pending")
|
|
553
|
+
return promptPushOutcome("pending");
|
|
602
554
|
if (result.snapshot.state === "delivery-unknown") {
|
|
603
555
|
return promptPushOutcome("delivery-unknown", failure);
|
|
604
556
|
}
|
|
@@ -612,7 +564,7 @@ export class AgentHostPromptPushAdapter {
|
|
|
612
564
|
: promptPushOutcome("unavailable", failure);
|
|
613
565
|
}
|
|
614
566
|
if (result.snapshot.state === "ready")
|
|
615
|
-
return promptPushOutcome("delivered");
|
|
567
|
+
return promptPushOutcome(result.snapshot.inputAcceptance === "provider" ? "delivered" : "pending");
|
|
616
568
|
return result.snapshot.state === "starting" || result.snapshot.state === "settling"
|
|
617
569
|
? promptPushOutcome("busy", failure)
|
|
618
570
|
: promptPushOutcome("unavailable", failure);
|
|
@@ -631,20 +583,21 @@ export class AgentHostPromptPushAdapter {
|
|
|
631
583
|
control: {
|
|
632
584
|
protocol: AGENT_HOST_CONTROL_PROTOCOL,
|
|
633
585
|
type: "steer-turn",
|
|
634
|
-
runtimeGenerationId: request.runtimeGenerationId,
|
|
635
586
|
nativeSessionId: request.nativeSessionId,
|
|
636
587
|
nativeTurnId: request.nativeTurnId,
|
|
637
588
|
authority: request.providerAuthority,
|
|
638
|
-
|
|
589
|
+
run: {
|
|
639
590
|
attemptId: request.envelope.id,
|
|
640
591
|
boundedText: request.envelope.text
|
|
641
592
|
}
|
|
642
593
|
}
|
|
643
594
|
});
|
|
595
|
+
if (result.outcome === "pending")
|
|
596
|
+
return promptPushOutcome("pending");
|
|
644
597
|
if (result.outcome === "accepted")
|
|
645
598
|
return promptPushOutcome("delivered");
|
|
646
599
|
const failure = result.failure;
|
|
647
|
-
if (result.snapshot.state === "delivery-unknown") {
|
|
600
|
+
if (failure?.inputDisposition === "unknown" || result.snapshot.state === "delivery-unknown") {
|
|
648
601
|
return promptPushOutcome("delivery-unknown", failure);
|
|
649
602
|
}
|
|
650
603
|
if (result.snapshot.state === "busy")
|