@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,9 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
|
-
import { realpathSync } from "node:fs";
|
|
3
|
+
import { mkdirSync, realpathSync } from "node:fs";
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { isDeepStrictEqual } from "node:util";
|
|
7
|
+
import { assertExecutionEnvironmentCurrent } from "../runtime/executionEnvironment.js";
|
|
8
|
+
import { runPurposeAdmitsTaskState } from "../agentRun/agentRun.js";
|
|
9
|
+
import { taskOwnsManagedWorkspace } from "../task/task.js";
|
|
7
10
|
import { configuredAgentToDefinition, resolveAgentEnvironment } from "../agent/agent.js";
|
|
8
11
|
import { NATIVE_AGENT_ENVIRONMENT_NAMES, nativeAgentEnvironmentNames, operationalAgentEnvironment, selectEnvironment } from "../agent/launchEnvironment.js";
|
|
9
12
|
import { activeRoleAgentBinding } from "../role/role.js";
|
|
@@ -12,15 +15,15 @@ import { compileRoleSessionContext, roleSessionKind } from "../context/roleSessi
|
|
|
12
15
|
import { materializeSessionBootstrap } from "../context/sessionBootstrapManifest.js";
|
|
13
16
|
import { resolveAgentAdapter } from "./agentAdapter.js";
|
|
14
17
|
import { resolveTaskRoleSessionTitle } from "../runtime/sessionTitle.js";
|
|
15
|
-
import { nativeSessionIdForLaunch } from "../runtime/preallocatedNativeSession.js";
|
|
16
18
|
import { classifyWorkspacePreflight, formatWorkspacePreflightError } from "./workspacePreflightClassification.js";
|
|
17
|
-
import { activeLiveRoleAgentSession } from "./agentExecutor.js";
|
|
19
|
+
import { activeLiveRoleAgentSession, taskRoleControlTarget } from "./agentExecutor.js";
|
|
18
20
|
import { roleSessionMayContinue, effectiveRoleForLaunch, resolveEffectiveLaunch } from "./effectiveLaunch.js";
|
|
19
21
|
import { parseTaskRuntimeIsolationDescriptor, taskRuntimeIsolationEnvironment } from "../runtime/taskRuntimeIsolation.js";
|
|
20
22
|
import { ResourceRegistrar } from "../resources/resourceRegistrar.js";
|
|
21
23
|
import { builtinAgentDriverRegistry, builtinDriverIdForAdapter } from "../runtime/builtinAgentDrivers.js";
|
|
22
24
|
import { managedRuntimeAdmission } from "../runtime/agentDriver.js";
|
|
23
|
-
import {
|
|
25
|
+
import { builtinAgentEndpointImplementation, validateAgentEndpointImplementation } from "../runtime/agentEndpointIdentity.js";
|
|
26
|
+
import { assertProviderConversationReplaceable } from "../runtime/providerRuntimeIdentity.js";
|
|
24
27
|
import { assertCodexLaunchOverridesAvailable, inspectCodexLaunchConfig } from "./codexConfigConflict.js";
|
|
25
28
|
/** Builds managed native Agent launches from the authoritative Task records. */
|
|
26
29
|
export class FileRoleLaunchPlanner {
|
|
@@ -64,42 +67,83 @@ export class FileRoleLaunchPlanner {
|
|
|
64
67
|
this.#agentEnvironment = patchEnvironment(this.#agentEnvironment, refresh.sourceNames, refresh.sources);
|
|
65
68
|
this.#nativeAgentEnvironment = patchEnvironment(this.#nativeAgentEnvironment, refresh.nativeNames, refresh.nativeSources);
|
|
66
69
|
}
|
|
67
|
-
/**
|
|
68
|
-
*
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
|
|
70
|
+
/** Recovery addresses the recorded Agent/Session, independent of delivery
|
|
71
|
+
* admission, a missing worktree, or the old Endpoint code generation. */
|
|
72
|
+
planNativeControl(taskId, roleName) {
|
|
73
|
+
const set = this.store.getTaskRoleSessionSet(taskId, roleName);
|
|
74
|
+
const session = taskRoleControlTarget(set);
|
|
75
|
+
if (session?.adapterId !== "codex")
|
|
76
|
+
throw new Error("Native metadata control requires a recorded Codex Session.");
|
|
77
|
+
const configured = this.store.getConfiguredAgent(session.agentId);
|
|
78
|
+
if (configured === null || configured.adapterId !== session.adapterId) {
|
|
79
|
+
throw new Error("The recorded native Agent connection is unavailable.");
|
|
80
|
+
}
|
|
81
|
+
const agent = configuredAgentToDefinition(configured);
|
|
82
|
+
const connection = this.store.listEvents(taskId).find(event => event.type === "runtime.native-connection-bound"
|
|
83
|
+
&& event.payload.roleName === roleName && event.payload.agentId === session.agentId
|
|
84
|
+
&& event.payload.nativeSessionId === session.nativeSessionId)?.payload;
|
|
85
|
+
return {
|
|
86
|
+
command: configured.command, args: [...agent.baseArgs, "app-server", "proxy"], cwd: this.home,
|
|
87
|
+
expectedAccountHome: connection?.nativeAccountHome,
|
|
88
|
+
environment: {
|
|
89
|
+
...operationalAgentEnvironment("codex", { ...this.#operationalEnvironment, ...this.#nativeAgentEnvironment }),
|
|
90
|
+
...resolveAgentEnvironment(agent, this.#agentEnvironment),
|
|
91
|
+
...(connection === undefined ? {} : { HOME: connection.home, CODEX_HOME: connection.codexHome })
|
|
92
|
+
}
|
|
93
|
+
};
|
|
76
94
|
}
|
|
77
95
|
plan(input) {
|
|
78
96
|
const task = this.store.getTask(input.taskId);
|
|
79
97
|
if (task === null)
|
|
80
98
|
throw new Error(`Task not found: ${input.taskId}.`);
|
|
81
|
-
if (task.status !== "active" || task.executionGate.state !== "enabled") {
|
|
82
|
-
throw new Error(`Task execution is not enabled: ${input.taskId}.`);
|
|
83
|
-
}
|
|
84
99
|
const role = this.store.getRole(input.taskId, input.roleName);
|
|
85
100
|
if (role === null)
|
|
86
101
|
throw new Error(`Role not found: ${input.taskId}/${input.roleName}.`);
|
|
87
|
-
const
|
|
88
|
-
if (input.
|
|
89
|
-
throw new Error(`Role
|
|
102
|
+
const activeRun = this.store.getActiveRun(task.id, role.name);
|
|
103
|
+
if (input.runId !== undefined && activeRun?.id !== input.runId) {
|
|
104
|
+
throw new Error(`Role AgentRun is no longer current: ${input.runId}.`);
|
|
90
105
|
}
|
|
91
|
-
const
|
|
106
|
+
const purpose = activeRun?.purpose
|
|
107
|
+
?? (task.status === "draft" && role.name === "leader" ? "planning" : "execution");
|
|
108
|
+
if (!runPurposeAdmitsTaskState(purpose, task)) {
|
|
109
|
+
throw new Error(`Task execution is not enabled: ${input.taskId}.`);
|
|
110
|
+
}
|
|
111
|
+
// A launch owns no managed workspace in two cases: a Draft planning
|
|
112
|
+
// conversation, and a Task activated with an empty environment plan. Both
|
|
113
|
+
// run with no Project entries, so there is nothing to preflight and no
|
|
114
|
+
// worktree to wait for. Every other fence — live Session, replaceable
|
|
115
|
+
// Conversation, configured Agent, Context protocol identity, adopted
|
|
116
|
+
// execution environment — still applies.
|
|
117
|
+
const planningDraft = purpose === "planning" && task.status === "draft";
|
|
118
|
+
// A planning cwd is a disposable per-Task runtime resource, not a delivery
|
|
119
|
+
// workspace. Materialize only the exact directory selected at creation.
|
|
120
|
+
if (planningDraft && resolve(role.workspace) === resolve(`${this.home}.task-runtimes`, "planning", task.id)) {
|
|
121
|
+
mkdirSync(role.workspace, { recursive: true, mode: 0o700 });
|
|
122
|
+
}
|
|
123
|
+
const workspaceFree = planningDraft || !taskOwnsManagedWorkspace(task);
|
|
124
|
+
// An empty resource plan is a legal Task shape, but it does not make a
|
|
125
|
+
// shared directory a legal cwd. Once such a Task is active it can name the
|
|
126
|
+
// directory it means through the existing environment plan, so require that
|
|
127
|
+
// instead of silently running in whatever workspace the Role inherited with
|
|
128
|
+
// isolation skipped.
|
|
129
|
+
if (workspaceFree && !planningDraft && role.executionEnvironment === undefined) {
|
|
130
|
+
throw new Error(`Task ${task.id} owns no workspace and no adopted execution environment, so Role `
|
|
131
|
+
+ `${role.name} has no directory of its own to run in. Request activation with a `
|
|
132
|
+
+ "`scratch` or `local` environment plan, or bind an adopted environment with "
|
|
133
|
+
+ "`environment.bind`, instead of inheriting a shared workspace.");
|
|
134
|
+
}
|
|
135
|
+
const runWorkspace = activeRun?.workspace;
|
|
92
136
|
const main = this.store.getTaskWorkspace(task.id);
|
|
93
137
|
// Quick Win (EXE-04/EXE-08): classify workspace preflight failures so
|
|
94
138
|
// split-brain state is never reported as a transient Provider failure.
|
|
95
|
-
const preflight = classifyWorkspacePreflight(this.store, task, input.roleName,
|
|
139
|
+
const preflight = workspaceFree ? null : classifyWorkspacePreflight(this.store, task, input.roleName, activeRun === null ? null : { id: activeRun.id, workspace: activeRun.workspace }, this.#inspectWorkspacePhysicalState);
|
|
96
140
|
if (preflight !== null) {
|
|
97
141
|
throw new Error(formatWorkspacePreflightError(preflight));
|
|
98
142
|
}
|
|
99
143
|
const assignedWorkItem = this.store.listWorkItems(task.id).find((item) => item.assignee === role.name
|
|
100
|
-
&& !["
|
|
101
|
-
// A
|
|
102
|
-
// a Reviewer
|
|
144
|
+
&& !["accepted", "retired"].includes(item.status));
|
|
145
|
+
// A AgentRun snapshot is authoritative for the live launch. In particular,
|
|
146
|
+
// a Reviewer AgentRun must launch from its ReviewRound-owned workspace rather
|
|
103
147
|
// than falling back to the WorkItem Develop workspace. Without an
|
|
104
148
|
// active snapshot, resolve the normal Role/WorkItem assignment.
|
|
105
149
|
const workspace = runWorkspace !== undefined
|
|
@@ -109,7 +153,12 @@ export class FileRoleLaunchPlanner {
|
|
|
109
153
|
: assignedWorkItem === undefined
|
|
110
154
|
? main
|
|
111
155
|
: this.store.getWorkItemWorkspace(task.id, assignedWorkItem.id);
|
|
112
|
-
if (
|
|
156
|
+
if (workspaceFree) {
|
|
157
|
+
if (workspace !== null && !isDeepStrictEqual(workspace, main)) {
|
|
158
|
+
throw new Error(`Role workspace is not ready: ${input.taskId}/${input.roleName}.`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
else if (task.projectBindings.length === 0) {
|
|
113
162
|
if (workspace === null || !isDeepStrictEqual(workspace, main)) {
|
|
114
163
|
throw new Error(`Role workspace is not ready: ${input.taskId}/${input.roleName}.`);
|
|
115
164
|
}
|
|
@@ -126,9 +175,9 @@ export class FileRoleLaunchPlanner {
|
|
|
126
175
|
&& workspace.owner.type === "work-item"
|
|
127
176
|
&& isolatedWorkItem !== null
|
|
128
177
|
&& (isolatedWorkItem.assignee === undefined || isolatedWorkItem.assignee === role.name)
|
|
129
|
-
&& !["
|
|
130
|
-
&& (
|
|
131
|
-
||
|
|
178
|
+
&& !["accepted", "retired"].includes(isolatedWorkItem.status)
|
|
179
|
+
&& (activeRun === null
|
|
180
|
+
|| activeRun.workItemId === workspace.owner.workItemId)
|
|
132
181
|
&& sameWorkspaceProjects(workspace, task.projectBindings.map(({ projectId }) => projectId))
|
|
133
182
|
&& sameWritableProjects(workspace, isolatedWorkItem.writeProjectIds);
|
|
134
183
|
const runScoped = runWorkspace !== undefined
|
|
@@ -136,29 +185,29 @@ export class FileRoleLaunchPlanner {
|
|
|
136
185
|
&& sameWorkspaceProjects(runWorkspace, task.projectBindings.map(({ projectId }) => projectId))
|
|
137
186
|
&& (runWorkspace.owner.type === "task"
|
|
138
187
|
|| (runWorkspace.owner.type === "work-item"
|
|
139
|
-
&& runWorkspace.owner.workItemId ===
|
|
188
|
+
&& runWorkspace.owner.workItemId === activeRun?.workItemId)
|
|
140
189
|
|| (runWorkspace.owner.type === "review-round"
|
|
141
|
-
&&
|
|
142
|
-
&& runWorkspace.owner.reviewRoundId ===
|
|
190
|
+
&& activeRun?.purpose === "review"
|
|
191
|
+
&& runWorkspace.owner.reviewRoundId === activeRun.reviewRoundId)
|
|
143
192
|
|| (runWorkspace.owner.type === "execution-lane"
|
|
144
|
-
&& runWorkspace.owner.executionGroupId ===
|
|
145
|
-
&& runWorkspace.owner.executionLaneId ===
|
|
146
|
-
&& ((runWorkspace.owner.purpose === "review" &&
|
|
147
|
-
|| (runWorkspace.owner.purpose === "execution" &&
|
|
193
|
+
&& runWorkspace.owner.executionGroupId === activeRun?.executionGroupId
|
|
194
|
+
&& runWorkspace.owner.executionLaneId === activeRun?.executionLaneId
|
|
195
|
+
&& ((runWorkspace.owner.purpose === "review" && activeRun?.purpose === "review")
|
|
196
|
+
|| (runWorkspace.owner.purpose === "execution" && activeRun?.purpose === "execution"))));
|
|
148
197
|
if (!runScoped && !sharedMain && !isolated) {
|
|
149
198
|
throw new Error(`Role workspace is not ready: ${input.taskId}/${input.roleName}.`);
|
|
150
199
|
}
|
|
151
200
|
}
|
|
152
201
|
const sessionSet = this.store.getTaskRoleSessionSet(task.id, role.name);
|
|
153
|
-
const resolvedEffective =
|
|
202
|
+
const resolvedEffective = activeRun?.effective
|
|
154
203
|
?? activeLiveRoleAgentSession(sessionSet)?.effective
|
|
155
|
-
?? resolveTaskRoleEffectiveLaunch(this.store, role);
|
|
204
|
+
?? resolveTaskRoleEffectiveLaunch(this.store, role, planningDraft ? "planning" : "execution");
|
|
156
205
|
if (input.effective !== undefined
|
|
157
206
|
&& !isDeepStrictEqual(resolvedEffective, input.effective)) {
|
|
158
|
-
throw new Error(`Role launch effective
|
|
207
|
+
throw new Error(`Role launch effective AgentRun snapshot changed: ${input.taskId}/${input.roleName}.`);
|
|
159
208
|
}
|
|
160
|
-
if (
|
|
161
|
-
throw new Error(`Role launch is missing the effective
|
|
209
|
+
if (activeRun !== null && input.effective === undefined) {
|
|
210
|
+
throw new Error(`Role launch is missing the effective AgentRun snapshot: ${input.taskId}/${input.roleName}.`);
|
|
162
211
|
}
|
|
163
212
|
const effective = input.effective ?? resolvedEffective;
|
|
164
213
|
const existing = sessionSet?.sessions[effective.agentId];
|
|
@@ -179,7 +228,7 @@ export class FileRoleLaunchPlanner {
|
|
|
179
228
|
assertProviderConversationReplaceable(sessionSet.providerBinding);
|
|
180
229
|
}
|
|
181
230
|
return this.#compile(role, input, { scope: "task", taskId: task.id }, resolveTaskRoleSessionTitle(input.mode === "resume" ? existing?.title : undefined, task, role.name), input.mode === "resume" && compatibleExisting ? existing.nativeSessionId : undefined, runWorkspace, effective, {
|
|
182
|
-
purpose
|
|
231
|
+
purpose
|
|
183
232
|
});
|
|
184
233
|
}
|
|
185
234
|
planGlobalRole(input) {
|
|
@@ -206,14 +255,40 @@ export class FileRoleLaunchPlanner {
|
|
|
206
255
|
const launchRole = effectiveRoleForLaunch(role, effective);
|
|
207
256
|
const binding = activeRoleAgentBinding(launchRole);
|
|
208
257
|
if (binding.agentId !== input.agentId || binding.adapterId !== input.adapterId) {
|
|
209
|
-
throw new Error(`Role runtime
|
|
210
|
-
}
|
|
258
|
+
throw new Error(`Role runtime identity changed: ${role.name}.`);
|
|
259
|
+
}
|
|
260
|
+
const existingSession = owner.scope === "task"
|
|
261
|
+
? this.store.getTaskRoleSessionSet(owner.taskId, role.name)?.sessions[input.agentId]
|
|
262
|
+
: this.store.getGlobalRoleSessionSet(role.name)?.sessions[input.agentId];
|
|
263
|
+
// A resume carries the Session's recorded generation forward unchanged. This
|
|
264
|
+
// planner runs in the Controller, which after an upgrade is already the new
|
|
265
|
+
// code, so it cannot speak for the still-running Agent Host that owns the
|
|
266
|
+
// Session: rejecting here would end live Sessions on Controller upgrade,
|
|
267
|
+
// while rewriting to current code would silently move a Session onto code it
|
|
268
|
+
// never started on. The Host that actually executes the implementation makes
|
|
269
|
+
// the decision — a reused live Host accepts its own generation, and a newly
|
|
270
|
+
// started Host running different code fails closed
|
|
271
|
+
// (`agentEndpointOwnership.pin` -> `requireBuiltinAgentEndpointImplementation`).
|
|
272
|
+
const endpointImplementation = input.mode === "resume"
|
|
273
|
+
? validateAgentEndpointImplementation(existingSession.endpointImplementation)
|
|
274
|
+
: builtinAgentEndpointImplementation(binding.adapterId);
|
|
211
275
|
const configured = this.store.getConfiguredAgent(input.agentId);
|
|
212
276
|
if (configured === null)
|
|
213
277
|
throw new Error(`Configured Agent not found: ${input.agentId}.`);
|
|
214
278
|
if (configured.adapterId !== binding.adapterId) {
|
|
215
279
|
throw new Error(`Configured Agent adapter changed: ${input.agentId}.`);
|
|
216
280
|
}
|
|
281
|
+
// The command below comes from the Agent record while the product identity
|
|
282
|
+
// stamped onto this Session comes from the pinned snapshot. Checking only
|
|
283
|
+
// the adapter lets those disagree whenever two products share a plan, which
|
|
284
|
+
// is exactly the ACP case: the launch would run the newly configured
|
|
285
|
+
// executable and label it with the product the Session was pinned to. The
|
|
286
|
+
// update path refuses this change on a referenced Agent, so reaching here
|
|
287
|
+
// means the record was altered some other way — still not something to
|
|
288
|
+
// launch through.
|
|
289
|
+
if (configured.component !== binding.component) {
|
|
290
|
+
throw new Error(`Configured Agent execution component changed: ${input.agentId}.`);
|
|
291
|
+
}
|
|
217
292
|
const agent = configuredAgentToDefinition(configured);
|
|
218
293
|
const agentSourceEnvironment = input.environment ?? this.#agentEnvironment;
|
|
219
294
|
const operationalSourceEnvironment = input.environment ?? {
|
|
@@ -228,8 +303,20 @@ export class FileRoleLaunchPlanner {
|
|
|
228
303
|
const launchEnvironment = { ...inheritedLaunchEnvironment };
|
|
229
304
|
const adapter = resolveAgentAdapter(binding.adapterId);
|
|
230
305
|
const effectiveWorkspace = effective.workspace.root;
|
|
231
|
-
|
|
232
|
-
|
|
306
|
+
if (effective.executionEnvironment !== undefined) {
|
|
307
|
+
if (owner.scope !== "task")
|
|
308
|
+
throw new Error("Global Roles cannot adopt a Task execution environment.");
|
|
309
|
+
assertExecutionEnvironmentCurrent(this.store, owner.taskId, effective.executionEnvironment);
|
|
310
|
+
if ((binding.config.advanced?.rawArgs?.length ?? 0) > 0) {
|
|
311
|
+
throw new Error("Adopted environments do not accept raw Agent arguments; use structured configuration.");
|
|
312
|
+
}
|
|
313
|
+
if ((binding.config.additionalDirectories?.length ?? 0) > 0) {
|
|
314
|
+
throw new Error("Adopted environments cannot implicitly include additional directories.");
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
const agentWorkspace = effective.executionEnvironment?.directory.path
|
|
318
|
+
?? nativeAgentWorkspace(effective.workspace);
|
|
319
|
+
if (adapter.id === "codex" && (owner.scope !== "task" || input.runId === undefined)) {
|
|
233
320
|
const codexConfig = inspectCodexLaunchConfig({
|
|
234
321
|
environment: launchEnvironment,
|
|
235
322
|
workspace: agentWorkspace,
|
|
@@ -245,10 +332,7 @@ export class FileRoleLaunchPlanner {
|
|
|
245
332
|
const runtimeIsolation = input.runtimeIsolation === undefined
|
|
246
333
|
? undefined
|
|
247
334
|
: parseTaskRuntimeIsolationDescriptor(JSON.stringify(input.runtimeIsolation));
|
|
248
|
-
if (runtimeIsolation !== undefined && (owner.scope !== "task"
|
|
249
|
-
|| runtimeIsolation.taskId !== owner.taskId
|
|
250
|
-
|| runtimeIsolation.workspace.root !== effectiveWorkspace
|
|
251
|
-
|| runtimeIsolation.generation.runtimeGenerationId !== input.runtimeGenerationId)) {
|
|
335
|
+
if (runtimeIsolation !== undefined && (owner.scope !== "task" || runtimeIsolation.taskId !== owner.taskId || runtimeIsolation.workspace.root !== effectiveWorkspace)) {
|
|
252
336
|
throw new Error("Role launch does not match its Task runtime isolation descriptor.");
|
|
253
337
|
}
|
|
254
338
|
Object.assign(launchEnvironment, runtimeIsolation === undefined
|
|
@@ -276,23 +360,25 @@ export class FileRoleLaunchPlanner {
|
|
|
276
360
|
sessionManifestDigest: bootstrap.manifest.digest,
|
|
277
361
|
sessionCliPath: bootstrap.sessionCliPath
|
|
278
362
|
};
|
|
279
|
-
const managedRun = owner.scope === "task" && input.
|
|
280
|
-
? this.store.
|
|
363
|
+
const managedRun = owner.scope === "task" && input.runId !== undefined
|
|
364
|
+
? this.store.getRun(owner.taskId, input.runId)
|
|
281
365
|
: null;
|
|
282
366
|
const driver = builtinAgentDriverRegistry().require(builtinDriverIdForAdapter(configured.adapterId));
|
|
283
|
-
if (owner.scope === "task" && input.
|
|
367
|
+
if (owner.scope === "task" && input.runId !== undefined) {
|
|
284
368
|
const admission = managedRuntimeAdmission(driver.capabilities);
|
|
285
369
|
if (!admission.admitted) {
|
|
286
|
-
throw new Error(`Agent Driver ${driver.id} cannot host managed
|
|
370
|
+
throw new Error(`Agent Driver ${driver.id} cannot host managed AgentRuns; missing capabilities: `
|
|
287
371
|
+ admission.missing.join(", "));
|
|
288
372
|
}
|
|
289
373
|
}
|
|
290
374
|
const roleConfig = binding.config.adapterId === "claude"
|
|
291
375
|
&& owner.scope === "task"
|
|
292
|
-
&& input.
|
|
293
|
-
? managedClaudeControlPlaneConfig(binding.config, owner.taskId, managedRun?.workItemId, input.
|
|
376
|
+
&& input.runId !== undefined
|
|
377
|
+
? managedClaudeControlPlaneConfig(binding.config, owner.taskId, managedRun?.workItemId, input.runId)
|
|
294
378
|
: binding.config;
|
|
295
|
-
const effectiveConfig = withNativeProjectDirectories(roleConfig,
|
|
379
|
+
const effectiveConfig = withNativeProjectDirectories(roleConfig, effective.executionEnvironment === undefined
|
|
380
|
+
? nativeAdditionalDirectories(effective.workspace, agentWorkspace)
|
|
381
|
+
: []);
|
|
296
382
|
const compileInput = {
|
|
297
383
|
agent,
|
|
298
384
|
config: effectiveConfig,
|
|
@@ -307,27 +393,20 @@ export class FileRoleLaunchPlanner {
|
|
|
307
393
|
}
|
|
308
394
|
// A previous attempt may have persisted a preallocated/discovered ID
|
|
309
395
|
// before its receipt was committed. Reuse that fixed session rather than
|
|
310
|
-
// allocating a second native session for the same durable
|
|
396
|
+
// allocating a second native session for the same durable AgentRun.
|
|
311
397
|
const resumeNativeSessionId = input.mode === "resume"
|
|
312
398
|
? requireText(input.nativeSessionId, "Native session id")
|
|
313
399
|
: knownNativeSessionId;
|
|
314
400
|
const launchMode = resumeNativeSessionId === undefined
|
|
315
401
|
? "new"
|
|
316
402
|
: "resume";
|
|
317
|
-
const managedControl = owner.scope === "task"
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
// identity for provider requests while clientInfo still identifies Yui.
|
|
323
|
-
CODEX_INTERNAL_ORIGINATOR_OVERRIDE: "codex_exec"
|
|
324
|
-
}
|
|
325
|
-
: {};
|
|
326
|
-
const preallocatedNativeSessionId = binding.adapterId === "claude"
|
|
403
|
+
const managedControl = owner.scope === "task";
|
|
404
|
+
// Only an Agent that accepts a caller-chosen Session id can have one
|
|
405
|
+
// preallocated. Keying this on the adapter name instead of the declared
|
|
406
|
+
// capability meant every non-Claude adapter was assumed to accept one.
|
|
407
|
+
const preallocatedNativeSessionId = adapter.capabilities.nativeSessionDiscovery === "preallocated"
|
|
327
408
|
&& resumeNativeSessionId === undefined
|
|
328
|
-
? requireText(
|
|
329
|
-
? this.#createNativeSessionId()
|
|
330
|
-
: nativeSessionIdForLaunch(this.home, input.runtimeGenerationId, input.agentId, input.adapterId), "Native session id")
|
|
409
|
+
? requireText(this.#createNativeSessionId(), "Native session id")
|
|
331
410
|
: resumeNativeSessionId;
|
|
332
411
|
const managedCompiled = managedControl
|
|
333
412
|
? adapter.compileManagedControl(compileInput, launchMode, preallocatedNativeSessionId)
|
|
@@ -363,17 +442,14 @@ export class FileRoleLaunchPlanner {
|
|
|
363
442
|
}
|
|
364
443
|
if (binding.adapterId === "codex") {
|
|
365
444
|
// Interactive Task sessions may use notify for presentation.
|
|
366
|
-
// Managed
|
|
367
|
-
// subscription, avoiding a second Hook channel for the same
|
|
368
|
-
if (owner.scope === "task" && input.turnId === undefined) {
|
|
369
|
-
args = addCodexSessionNotify(args, launchMode, this.#cliPath);
|
|
370
|
-
}
|
|
445
|
+
// Managed AgentRuns receive lifecycle facts through their ordinary App Server
|
|
446
|
+
// subscription, avoiding a second Hook channel for the same AgentRun.
|
|
371
447
|
// Managed Codex Turns use disposable proxy clients against the shared
|
|
372
448
|
// native App Server. Their Session Manifest points at the Yui Skills;
|
|
373
449
|
// no Yui Hook config is installed.
|
|
374
|
-
if (owner.scope === "task" && input.
|
|
450
|
+
if (owner.scope === "task" && input.runId !== undefined) {
|
|
375
451
|
if (managedRun === null || managedRun.status !== "active") {
|
|
376
|
-
throw new Error(`Managed Codex
|
|
452
|
+
throw new Error(`Managed Codex AgentRun is no longer active: ${input.runId}.`);
|
|
377
453
|
}
|
|
378
454
|
}
|
|
379
455
|
session = launchMode === "resume"
|
|
@@ -381,41 +457,41 @@ export class FileRoleLaunchPlanner {
|
|
|
381
457
|
: null;
|
|
382
458
|
}
|
|
383
459
|
else if (launchMode === "new") {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
args.
|
|
389
|
-
|
|
460
|
+
if (adapter.capabilities.nativeSessionDiscovery === "preallocated") {
|
|
461
|
+
const nativeSessionId = requireText(preallocatedNativeSessionId, "Native session id");
|
|
462
|
+
if (!managedControl)
|
|
463
|
+
args.push("--session-id", nativeSessionId);
|
|
464
|
+
else if (!args.includes("--session-id"))
|
|
465
|
+
args.push("--session-id", nativeSessionId);
|
|
466
|
+
session = readySession(input.agentId, binding.adapterId, nativeSessionId, effective);
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
// A runtime-discovered Session id does not exist until the Agent
|
|
470
|
+
// answers, so there is no ready Session to record at plan time and no
|
|
471
|
+
// id to pass on the command line. Same shape as a new Codex launch.
|
|
472
|
+
session = null;
|
|
473
|
+
}
|
|
390
474
|
}
|
|
391
475
|
else {
|
|
392
476
|
session = readySession(input.agentId, binding.adapterId, resumeNativeSessionId, effective);
|
|
393
477
|
}
|
|
394
478
|
const managedClaudeRun = binding.adapterId === "claude"
|
|
395
479
|
&& owner.scope === "task"
|
|
396
|
-
&& input.
|
|
480
|
+
&& input.runId !== undefined;
|
|
397
481
|
if (managedClaudeRun && (managedRun === null || managedRun.status !== "active")) {
|
|
398
|
-
throw new Error(`Managed Claude
|
|
482
|
+
throw new Error(`Managed Claude AgentRun is no longer active: ${input.runId}.`);
|
|
399
483
|
}
|
|
400
|
-
//
|
|
401
|
-
// every task-scope launch. A reused live Conversation keeps the key that
|
|
402
|
-
// its process inherited; a new Host or a Conversation replacement commits
|
|
403
|
-
// the fresh candidate only after Provider dispatch is observed.
|
|
404
|
-
let jobCallerKey;
|
|
405
|
-
if (owner.scope === "task" && (input.mode === "new" || input.mode === "resume")) {
|
|
406
|
-
jobCallerKey = randomBytes(32).toString("hex");
|
|
407
|
-
}
|
|
408
|
-
// Session lifecycle and Turn submission are separate atomic operations.
|
|
484
|
+
// Session lifecycle and AgentRun submission are separate atomic operations.
|
|
409
485
|
// Planning only starts or restores the exact native Session; delivery
|
|
410
|
-
// submits the
|
|
411
|
-
if (managedControl && (managedRun === null || managedRun.status !== "active")) {
|
|
412
|
-
throw new Error(`Managed
|
|
486
|
+
// submits the AgentRun input after that Session fact is durable.
|
|
487
|
+
if (managedControl && input.runId !== undefined && (managedRun === null || managedRun.status !== "active")) {
|
|
488
|
+
throw new Error(`Managed AgentRun is no longer active: ${input.runId}.`);
|
|
413
489
|
}
|
|
414
490
|
const providerAuthority = managedControl
|
|
415
|
-
? this.#providerAuthorityForLaunch(owner.taskId, role.name, input.
|
|
491
|
+
? this.#providerAuthorityForLaunch(owner.taskId, role.name, input.mode)
|
|
416
492
|
: undefined;
|
|
417
|
-
const
|
|
418
|
-
? this.#
|
|
493
|
+
const providerOwnedRun = managedControl && input.mode === "resume" && input.runId !== undefined
|
|
494
|
+
? this.#providerOwnedRunForLaunch(owner.taskId, role.name, input.runId)
|
|
419
495
|
: undefined;
|
|
420
496
|
const providerNativeSessionId = binding.adapterId === "claude"
|
|
421
497
|
? preallocatedNativeSessionId
|
|
@@ -426,22 +502,35 @@ export class FileRoleLaunchPlanner {
|
|
|
426
502
|
? {
|
|
427
503
|
schemaVersion: 1,
|
|
428
504
|
adapterId: binding.adapterId,
|
|
505
|
+
// The product identity is pinned by the snapshot this launch was
|
|
506
|
+
// resolved from, and the check above proves the Agent record still
|
|
507
|
+
// names it. Carrying only the adapter would leave the runtime with
|
|
508
|
+
// two products sharing one plan and no way to tell them apart.
|
|
509
|
+
component: binding.component,
|
|
429
510
|
transport: managedCompiled.transport,
|
|
511
|
+
endpointImplementation,
|
|
430
512
|
kind: "restore",
|
|
513
|
+
sessionOnly: input.runId === undefined,
|
|
431
514
|
mode: "resume",
|
|
432
515
|
nativeSessionId: requireText(providerNativeSessionId, "Managed Provider restore native session id"),
|
|
433
516
|
...(sessionTitle === undefined ? {} : { sessionTitle }),
|
|
434
517
|
...(managedCompiled.codexThread === undefined
|
|
435
518
|
? {}
|
|
436
519
|
: { codexThread: managedCompiled.codexThread }),
|
|
437
|
-
...(
|
|
520
|
+
...(providerOwnedRun === undefined ? {} : { ownedTurn: providerOwnedRun }),
|
|
521
|
+
...(managedCompiled.acpSession === undefined
|
|
522
|
+
? {}
|
|
523
|
+
: { acpSession: managedCompiled.acpSession }),
|
|
438
524
|
authority: providerAuthority
|
|
439
525
|
}
|
|
440
526
|
: {
|
|
441
527
|
schemaVersion: 1,
|
|
442
528
|
adapterId: binding.adapterId,
|
|
529
|
+
component: binding.component,
|
|
443
530
|
transport: managedCompiled.transport,
|
|
531
|
+
endpointImplementation,
|
|
444
532
|
kind: "start",
|
|
533
|
+
sessionOnly: input.runId === undefined,
|
|
445
534
|
mode: "new",
|
|
446
535
|
...(providerNativeSessionId === undefined
|
|
447
536
|
? {}
|
|
@@ -450,15 +539,23 @@ export class FileRoleLaunchPlanner {
|
|
|
450
539
|
...(managedCompiled.codexThread === undefined
|
|
451
540
|
? {}
|
|
452
541
|
: { codexThread: managedCompiled.codexThread }),
|
|
542
|
+
...(managedCompiled.acpSession === undefined
|
|
543
|
+
? {}
|
|
544
|
+
: { acpSession: managedCompiled.acpSession }),
|
|
453
545
|
authority: providerAuthority
|
|
454
546
|
};
|
|
455
547
|
const launch = {
|
|
456
548
|
command,
|
|
457
549
|
args,
|
|
550
|
+
...(effective.executionEnvironment === undefined ? {} : {
|
|
551
|
+
executionEnvironment: structuredClone(effective.executionEnvironment)
|
|
552
|
+
}),
|
|
458
553
|
...(providerControl === undefined ? {} : { providerControl }),
|
|
554
|
+
...(owner.scope === "global" && binding.adapterId === "codex"
|
|
555
|
+
? { interactiveCodexThread: compiled.codexThread }
|
|
556
|
+
: {}),
|
|
459
557
|
env: {
|
|
460
558
|
...launchEnvironment,
|
|
461
|
-
...managedProviderEnvironment,
|
|
462
559
|
YUI_HOME: resolve(this.home),
|
|
463
560
|
YUI_SESSION_SCOPE: owner.scope,
|
|
464
561
|
...(owner.scope === "task" ? { YUI_TASK_ID: owner.taskId } : {}),
|
|
@@ -469,10 +566,9 @@ export class FileRoleLaunchPlanner {
|
|
|
469
566
|
YUI_WORKSPACE: effectiveWorkspace,
|
|
470
567
|
YUI_SESSION_MANIFEST: sessionContext.sessionManifestPath,
|
|
471
568
|
YUI_SESSION_CLI: sessionContext.sessionCliPath,
|
|
472
|
-
...(
|
|
569
|
+
...(configured.adapterId === "codex"
|
|
473
570
|
? { YUI_AGENT_BASE_ARGS: JSON.stringify(configured.baseArgs) }
|
|
474
571
|
: {}),
|
|
475
|
-
...(jobCallerKey === undefined ? {} : { YUI_JOB_CALLER_KEY: jobCallerKey }),
|
|
476
572
|
...(sessionTitle === undefined
|
|
477
573
|
? {}
|
|
478
574
|
: {
|
|
@@ -484,13 +580,10 @@ export class FileRoleLaunchPlanner {
|
|
|
484
580
|
}
|
|
485
581
|
: {})
|
|
486
582
|
}),
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
//
|
|
491
|
-
// outlives its Turn, so a Turn id frozen here would be stale for every
|
|
492
|
-
// later Turn. The Agent Host sets it explicitly per spawned Provider
|
|
493
|
-
// Turn, and every durable decision reads the active Turn from state.
|
|
583
|
+
// No AgentRun is exported into the Session environment. A native pane
|
|
584
|
+
// outlives its AgentRun, so a AgentRun id frozen here would be stale for every
|
|
585
|
+
// later AgentRun. The Agent Host sets it explicitly per spawned Provider
|
|
586
|
+
// AgentRun, and every durable decision reads the active AgentRun from state.
|
|
494
587
|
...(session === null
|
|
495
588
|
? {}
|
|
496
589
|
: { YUI_NATIVE_SESSION_ID: session.nativeSessionId })
|
|
@@ -518,24 +611,17 @@ export class FileRoleLaunchPlanner {
|
|
|
518
611
|
...(sessionTitle === undefined ? {} : { sessionTitle })
|
|
519
612
|
};
|
|
520
613
|
}
|
|
521
|
-
#providerAuthorityForLaunch(taskId, roleName,
|
|
522
|
-
const activationId = requireText(runtimeGenerationId, "Managed Provider Activation id");
|
|
614
|
+
#providerAuthorityForLaunch(taskId, roleName, mode) {
|
|
523
615
|
const binding = this.store.getTaskRoleSessionSet(taskId, roleName)?.providerBinding;
|
|
524
616
|
if (binding === null || binding === undefined) {
|
|
525
|
-
return { epoch: 1, owner: "controller", holderId:
|
|
617
|
+
return { epoch: 1, owner: "controller", holderId: "controller" };
|
|
526
618
|
}
|
|
527
619
|
if (binding.authority.owner === "controller") {
|
|
528
620
|
if (mode === "new") {
|
|
529
|
-
const activation = currentProviderActivation(binding);
|
|
530
|
-
if (activation === null || binding.authority.holderId !== activation.activationId) {
|
|
531
|
-
throw new Error(`Provider Activation is not exact: ${taskId}/${roleName}.`);
|
|
532
|
-
}
|
|
533
|
-
// An actor-requested switch first ends the idle old Activation (+1)
|
|
534
|
-
// and then binds the replacement Activation (+1).
|
|
535
621
|
return {
|
|
536
|
-
epoch: binding.authority.epoch +
|
|
622
|
+
epoch: binding.authority.epoch + 1,
|
|
537
623
|
owner: "controller",
|
|
538
|
-
holderId:
|
|
624
|
+
holderId: "controller"
|
|
539
625
|
};
|
|
540
626
|
}
|
|
541
627
|
return {
|
|
@@ -551,32 +637,32 @@ export class FileRoleLaunchPlanner {
|
|
|
551
637
|
return {
|
|
552
638
|
epoch: binding.authority.epoch + 1,
|
|
553
639
|
owner: "controller",
|
|
554
|
-
holderId:
|
|
640
|
+
holderId: "controller"
|
|
555
641
|
};
|
|
556
642
|
}
|
|
557
643
|
throw new Error(`Provider writer authority is unknown: ${taskId}/${roleName}.`);
|
|
558
644
|
}
|
|
559
|
-
#
|
|
645
|
+
#providerOwnedRunForLaunch(taskId, roleName, runId) {
|
|
560
646
|
const binding = this.store.getTaskRoleSessionSet(taskId, roleName)?.providerBinding;
|
|
561
647
|
if (binding === null || binding === undefined
|
|
562
|
-
|| binding.
|
|
648
|
+
|| binding.run?.runId !== runId)
|
|
563
649
|
return undefined;
|
|
564
|
-
const
|
|
565
|
-
if (
|
|
650
|
+
const run = binding.run;
|
|
651
|
+
if (run === null || run.status !== "accepted")
|
|
566
652
|
return undefined;
|
|
567
|
-
if (
|
|
568
|
-
throw new Error(`Active Provider
|
|
653
|
+
if (run.nativeTurnId === undefined) {
|
|
654
|
+
throw new Error(`Active Provider AgentRun has no native identity: ${taskId}/${roleName}.`);
|
|
569
655
|
}
|
|
570
|
-
return { attemptId:
|
|
656
|
+
return { attemptId: run.attemptId, turnId: run.nativeTurnId };
|
|
571
657
|
}
|
|
572
658
|
#applyWorkspaceScope(taskId, role, launch, workspaceOverride) {
|
|
573
659
|
const workspace = workspaceOverride
|
|
574
660
|
?? (role.name === "leader"
|
|
575
661
|
? this.store.getTaskWorkspace(taskId)
|
|
576
662
|
: this.store.listWorkItems(taskId)
|
|
577
|
-
.find((item) => item.assignee === role.name && item.status === "
|
|
663
|
+
.find((item) => item.assignee === role.name && item.status === "open") === undefined
|
|
578
664
|
? this.store.getTaskWorkspace(taskId)
|
|
579
|
-
: this.store.getWorkItemWorkspace(taskId, this.store.listWorkItems(taskId).find((item) => item.assignee === role.name && item.status === "
|
|
665
|
+
: this.store.getWorkItemWorkspace(taskId, this.store.listWorkItems(taskId).find((item) => item.assignee === role.name && item.status === "open").id));
|
|
580
666
|
if (workspace === null || workspace === undefined)
|
|
581
667
|
return launch;
|
|
582
668
|
return {
|
|
@@ -622,9 +708,14 @@ export function withNativeProjectDirectories(config, projectDirectories) {
|
|
|
622
708
|
])]
|
|
623
709
|
};
|
|
624
710
|
}
|
|
625
|
-
function resolveTaskRoleEffectiveLaunch(store, role) {
|
|
711
|
+
function resolveTaskRoleEffectiveLaunch(store, role, purpose = "execution") {
|
|
712
|
+
if (purpose === "planning") {
|
|
713
|
+
// Planning has no WorkItem assignment and no managed workspace, so it
|
|
714
|
+
// resolves the Role's own configured workspace with no writable Project.
|
|
715
|
+
return resolveEffectiveLaunch({ role, purpose: "planning" });
|
|
716
|
+
}
|
|
626
717
|
const item = store.listWorkItems(role.taskId).find((candidate) => (candidate.assignee === role.name
|
|
627
|
-
&& !["
|
|
718
|
+
&& !["accepted", "retired"].includes(candidate.status))) ?? null;
|
|
628
719
|
const workspace = (item === null
|
|
629
720
|
? store.getTaskWorkspace(role.taskId)
|
|
630
721
|
: store.getWorkItemWorkspace(role.taskId, item.id))
|
|
@@ -684,11 +775,11 @@ function ensureClaudeLifecyclePlugin(home, cliPath) {
|
|
|
684
775
|
}, null, 2)}\n`);
|
|
685
776
|
return root;
|
|
686
777
|
}
|
|
687
|
-
function managedClaudeControlPlaneConfig(config, taskId, workItemId,
|
|
778
|
+
function managedClaudeControlPlaneConfig(config, taskId, workItemId, runId) {
|
|
688
779
|
if (config.permission.strategy !== "configured")
|
|
689
780
|
return config;
|
|
690
781
|
const managed = [
|
|
691
|
-
`Bash(yui task
|
|
782
|
+
`Bash(yui task run context ${taskId}/${runId}:*)`,
|
|
692
783
|
`Bash(yui --json task context ${taskId})`,
|
|
693
784
|
`Bash(yui --json task work list ${taskId})`,
|
|
694
785
|
...(workItemId === undefined
|
|
@@ -751,22 +842,6 @@ function patchEnvironment(current, names, values) {
|
|
|
751
842
|
next[name] = value;
|
|
752
843
|
return next;
|
|
753
844
|
}
|
|
754
|
-
/**
|
|
755
|
-
* Codex invokes this argv after each completed turn and appends one JSON
|
|
756
|
-
* payload. JSON arrays are valid TOML arrays for the managed config override.
|
|
757
|
-
*/
|
|
758
|
-
export function codexSessionNotifyConfig(cliPath) {
|
|
759
|
-
return `notify=${JSON.stringify([process.execPath, cliPath, "internal", "session-notify"])}`;
|
|
760
|
-
}
|
|
761
|
-
function addCodexSessionNotify(args, mode, cliPath) {
|
|
762
|
-
const managed = ["--config", codexSessionNotifyConfig(cliPath)];
|
|
763
|
-
if (mode === "new")
|
|
764
|
-
return [...args, ...managed];
|
|
765
|
-
if (args.length < 2 || args.at(-2) !== "resume") {
|
|
766
|
-
throw new Error("Codex resume launch shape is invalid.");
|
|
767
|
-
}
|
|
768
|
-
return [...args.slice(0, -2), ...managed, ...args.slice(-2)];
|
|
769
|
-
}
|
|
770
845
|
/**
|
|
771
846
|
* Global Codex keeps its native TUI, but the TUI is only a client attachment
|
|
772
847
|
* to the user's shared App Server. The daemon owns the Thread and its writer,
|
|
@@ -798,7 +873,7 @@ function codexShellEnvironmentConfig(environment) {
|
|
|
798
873
|
/**
|
|
799
874
|
* Codex 0.145 discovers lifecycle hooks from its effective config. Keep Yui's
|
|
800
875
|
* two handlers invocation-local: this avoids mutating CODEX_HOME or the Task
|
|
801
|
-
* workspace, while the exact launch environment supplies the durable
|
|
876
|
+
* workspace, while the exact launch environment supplies the durable AgentRun fence.
|
|
802
877
|
*/
|
|
803
878
|
function codexLifecycleHooksConfig(cliPath) {
|
|
804
879
|
const command = [
|