@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
|
@@ -2,7 +2,7 @@ import { usageError } from "../errors/cliError.js";
|
|
|
2
2
|
import { activeLiveRoleAgentSession } from "../executor/agentExecutor.js";
|
|
3
3
|
import { hasRuntimeLifecycleWork, runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Explicit runtime cleanup is an ownership obligation for a Role
|
|
6
6
|
* identity. Mutating or recreating that identity while either obligation is
|
|
7
7
|
* queued can make the controller act on a different launch configuration.
|
|
8
8
|
*/
|
|
@@ -17,7 +17,7 @@ export const LIVE_SESSION_ACKNOWLEDGEMENT_OPTION = "--yes";
|
|
|
17
17
|
* the desired configuration is therefore a decision about the next Host
|
|
18
18
|
* activation, and the Agent making the change is the one that knows whether the
|
|
19
19
|
* running Session should keep going or be replaced. Report that fact once, here,
|
|
20
|
-
* instead of refusing the
|
|
20
|
+
* instead of refusing the AgentRun that later resumes the Session.
|
|
21
21
|
*/
|
|
22
22
|
export function assertLiveRoleSessionAcknowledged(input) {
|
|
23
23
|
if (input.acknowledged)
|
|
@@ -28,12 +28,11 @@ export function assertLiveRoleSessionAcknowledged(input) {
|
|
|
28
28
|
throw usageError([
|
|
29
29
|
input.endsSession === true
|
|
30
30
|
? `Role ${input.roleName} has a live native Session that cannot continue after this `
|
|
31
|
-
+ "change; its next
|
|
31
|
+
+ "change; its next AgentRun will need a new Session."
|
|
32
32
|
: `Role ${input.roleName} has a live native Session, so this change applies to its `
|
|
33
|
-
+ "next Host
|
|
33
|
+
+ "next Host process instead of the running one.",
|
|
34
34
|
` Agent: ${session.agentId} (${session.adapterId})`,
|
|
35
35
|
` Native Session: ${session.nativeSessionId}`,
|
|
36
|
-
` Host activation: ${session.runtimeGenerationId ?? "not recorded"}`,
|
|
37
36
|
` Session launched from desired revision r${session.effective.sourceDesiredRevision}; `
|
|
38
37
|
+ `current desired revision r${input.desiredRevision}`,
|
|
39
38
|
`Re-run with ${LIVE_SESSION_ACKNOWLEDGEMENT_OPTION} to record the change and keep the Session.`,
|
|
@@ -28,7 +28,7 @@ export async function runSessionStopCommand(input) {
|
|
|
28
28
|
// lifecycle work that may exist without a current Session candidate.
|
|
29
29
|
await input.runtime.drainController();
|
|
30
30
|
await input.runtime.stopController();
|
|
31
|
-
// Re-read the authoritative facts after the drain because a
|
|
31
|
+
// Re-read the authoritative facts after the drain because a AgentRun that was
|
|
32
32
|
// already inside a pass may have changed state meanwhile.
|
|
33
33
|
const settled = input.runtime.snapshot();
|
|
34
34
|
const blockedAfterDrain = blockedSessions(settled);
|
|
@@ -80,7 +80,7 @@ function blockedSessions(input) {
|
|
|
80
80
|
});
|
|
81
81
|
}
|
|
82
82
|
function blockedStopResult(blocked) {
|
|
83
|
-
const details = blocked.map(({ session }) => (`- ${renderSessionOwner(session)} (an active
|
|
83
|
+
const details = blocked.map(({ session }) => (`- ${renderSessionOwner(session)} (an active AgentRun or lifecycle operation is still pending)`));
|
|
84
84
|
return {
|
|
85
85
|
output: [
|
|
86
86
|
`Cannot stop managed Sessions: ${blocked.length} Session(s) are still busy.`,
|
|
@@ -101,11 +101,7 @@ function sameStopCandidate(session, candidate) {
|
|
|
101
101
|
&& session.owner.taskId !== candidate.owner.taskId) {
|
|
102
102
|
return false;
|
|
103
103
|
}
|
|
104
|
-
return session.agentId === candidate.agentId
|
|
105
|
-
&& session.adapterId === candidate.adapterId
|
|
106
|
-
&& session.nativeSessionId === candidate.nativeSessionId
|
|
107
|
-
&& session.runtimeGenerationId === candidate.runtimeGenerationId
|
|
108
|
-
&& session.sessionUpdatedAt === candidate.sessionUpdatedAt;
|
|
104
|
+
return session.agentId === candidate.agentId && session.adapterId === candidate.adapterId && session.nativeSessionId === candidate.nativeSessionId && session.sessionUpdatedAt === candidate.sessionUpdatedAt;
|
|
109
105
|
}
|
|
110
106
|
function renderSessionOwner(session) {
|
|
111
107
|
return session.owner.scope === "task"
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { usageError } from "../errors/cliError.js";
|
|
3
|
+
import { currentManagedRuntime } from "../runtime/managedCaller.js";
|
|
4
|
+
import { enqueueWork } from "../coordination/workMailboxQueue.js";
|
|
5
|
+
import { SYSTEM_LEADER_ROLE } from "../role/systemRoles.js";
|
|
6
|
+
import { describeEnvironmentPlan } from "../task/taskActivation.js";
|
|
7
|
+
import { cancelTaskActivation, requestTaskActivation, taskActivationOperationRef } from "../task/taskActivationService.js";
|
|
8
|
+
import { taskLocalActor } from "./taskActor.js";
|
|
9
|
+
/**
|
|
10
|
+
* Explicit Activation requests.
|
|
11
|
+
*
|
|
12
|
+
* Requesting is not activating: this family only records durable intent and
|
|
13
|
+
* returns its operation reference. Adoption stays at the single activation
|
|
14
|
+
* boundary (`yui task activate`, or the Controller once a deferred request is
|
|
15
|
+
* released), where the Task status change and the resource facts commit
|
|
16
|
+
* together.
|
|
17
|
+
*/
|
|
18
|
+
export function runTaskActivationCommand(args, store, options) {
|
|
19
|
+
const [command, ...rest] = args;
|
|
20
|
+
switch (command) {
|
|
21
|
+
case "request": return requestActivation(rest, store, options);
|
|
22
|
+
case "cancel": return cancelActivation(rest, store, options);
|
|
23
|
+
case "show": return showActivation(rest, store);
|
|
24
|
+
default:
|
|
25
|
+
throw usageError(command === undefined
|
|
26
|
+
? "Task activation command is required."
|
|
27
|
+
: `Unknown command: task activation ${command}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const REQUEST_USAGE = "Task activation request usage: yui task activation request <task> "
|
|
31
|
+
+ "--request-id <id> --environment <empty|scratch|local:<resource>:<read|write>>.";
|
|
32
|
+
/**
|
|
33
|
+
* Records the request and returns its reference immediately.
|
|
34
|
+
*
|
|
35
|
+
* When the caller is the Leader's own running planning Turn the request is
|
|
36
|
+
* stored as deferred and this command still returns right away: a synchronous
|
|
37
|
+
* tool must never wait for the Turn it is running inside. The Turn keeps going
|
|
38
|
+
* and the request is adopted after it terminates, against the facts that are
|
|
39
|
+
* current then.
|
|
40
|
+
*/
|
|
41
|
+
function requestActivation(args, store, options) {
|
|
42
|
+
const parsed = parseTail(args, new Set(["--request-id", "--environment"]), REQUEST_USAGE);
|
|
43
|
+
exactPositionals(parsed.positionals, 1, REQUEST_USAGE);
|
|
44
|
+
const taskId = parsed.positionals[0];
|
|
45
|
+
const requestId = requiredOption(parsed.options, "--request-id", REQUEST_USAGE);
|
|
46
|
+
const environmentPlan = parseEnvironmentPlan(requiredOption(parsed.options, "--environment", REQUEST_USAGE), REQUEST_USAGE);
|
|
47
|
+
const now = options.now?.() ?? new Date();
|
|
48
|
+
const caller = activationCaller(store, options, taskId);
|
|
49
|
+
const result = attempt(REQUEST_USAGE, () => requestTaskActivation(store, {
|
|
50
|
+
taskId,
|
|
51
|
+
requestId,
|
|
52
|
+
actorId: caller.actorId,
|
|
53
|
+
authorityRef: caller.authorityRef,
|
|
54
|
+
environmentPlan,
|
|
55
|
+
...(caller.planningRunId === undefined ? {} : { callerRunId: caller.planningRunId })
|
|
56
|
+
}, now));
|
|
57
|
+
// An immediate request has nothing left to wait for, so ask the Controller to
|
|
58
|
+
// pick it up. A deferred one is released by its planning Turn's termination.
|
|
59
|
+
// The signal is only promptness: the durable request is what is authoritative.
|
|
60
|
+
if (result.created && result.startMode === "immediate") {
|
|
61
|
+
store.transaction((tx) => {
|
|
62
|
+
enqueueWork(tx, { kind: "task", taskId }, "activation-requested", now, [{ type: "task", id: taskId }]);
|
|
63
|
+
});
|
|
64
|
+
void options.runtime?.notifyMailboxChanged?.({ kind: "task", taskId });
|
|
65
|
+
}
|
|
66
|
+
const request = result.request;
|
|
67
|
+
const lines = [
|
|
68
|
+
`${result.created ? "Requested" : "Existing"} activation ${result.operationRef}`,
|
|
69
|
+
`Task: ${taskId} (draft)`,
|
|
70
|
+
`Start: ${result.startMode}${result.afterPlanningRun === undefined ? "" : ` after ${taskId}/${result.afterPlanningRun}`}`,
|
|
71
|
+
`Environment: ${describeEnvironmentPlan(request.environmentPlan)}`,
|
|
72
|
+
`Disposition: ${request.disposition}`,
|
|
73
|
+
result.startMode === "after-planning-turn"
|
|
74
|
+
? "This reference is returned now; the request is adopted after the planning Turn ends."
|
|
75
|
+
: `Adopt it with: yui task activate ${taskId}`
|
|
76
|
+
];
|
|
77
|
+
return output(`${lines.join("\n")}\n`, {
|
|
78
|
+
operationRef: result.operationRef,
|
|
79
|
+
created: result.created,
|
|
80
|
+
startMode: result.startMode,
|
|
81
|
+
...(result.afterPlanningRun === undefined
|
|
82
|
+
? {}
|
|
83
|
+
: { afterPlanningRun: result.afterPlanningRun }),
|
|
84
|
+
request
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/** A cancelled request is never adopted, even if the same id is retried. */
|
|
88
|
+
function cancelActivation(args, store, options) {
|
|
89
|
+
const usage = "Task activation cancel usage: yui task activation cancel <task> "
|
|
90
|
+
+ "--request-id <id> --reason <text>.";
|
|
91
|
+
const parsed = parseTail(args, new Set(["--request-id", "--reason"]), usage);
|
|
92
|
+
exactPositionals(parsed.positionals, 1, usage);
|
|
93
|
+
const taskId = parsed.positionals[0];
|
|
94
|
+
const requestId = requiredOption(parsed.options, "--request-id", usage);
|
|
95
|
+
const reason = requiredOption(parsed.options, "--reason", usage);
|
|
96
|
+
// Cancelling is a Task-local decision: the same authority that may request
|
|
97
|
+
// activation may withdraw it, and withdrawal is always the safe direction.
|
|
98
|
+
taskLocalActor(store, options.environment, taskId);
|
|
99
|
+
const now = options.now?.() ?? new Date();
|
|
100
|
+
const request = attempt(usage, () => cancelTaskActivation(store, taskId, requestId, reason, now));
|
|
101
|
+
return output(`Cancelled activation ${taskActivationOperationRef(taskId, requestId)}\n`
|
|
102
|
+
+ `Reason: ${request.outcome ?? reason}\n`, { request });
|
|
103
|
+
}
|
|
104
|
+
function showActivation(args, store) {
|
|
105
|
+
const usage = "Task activation show usage: yui task activation show <task>.";
|
|
106
|
+
const parsed = parseTail(args, new Set(), usage);
|
|
107
|
+
exactPositionals(parsed.positionals, 1, usage);
|
|
108
|
+
const task = requireTask(store, parsed.positionals[0]);
|
|
109
|
+
const request = task.activationRequest;
|
|
110
|
+
// The recent terminal requests the Task still displays. The refusal itself is
|
|
111
|
+
// enforced from the durable activation event ledger, not this bounded list;
|
|
112
|
+
// showing it just lets an Operator diagnosing a refusal see the recent
|
|
113
|
+
// outcomes at a glance instead of only the latest request. An id can be
|
|
114
|
+
// refused without appearing here once it has aged out of the display bound.
|
|
115
|
+
const settled = task.settledActivationRequests ?? [];
|
|
116
|
+
const settledLines = settled.length === 0 ? [] : [
|
|
117
|
+
"",
|
|
118
|
+
`Earlier requests (${settled.length}, oldest first):`,
|
|
119
|
+
...settled.map((entry) => (` ${entry.operation.requestId}: ${entry.disposition}`
|
|
120
|
+
+ ` / effect ${entry.operation.effect}`
|
|
121
|
+
+ ` / ${describeEnvironmentPlan(entry.environmentPlan)}`
|
|
122
|
+
+ `${entry.outcome === undefined ? "" : ` — ${entry.outcome}`}`))
|
|
123
|
+
];
|
|
124
|
+
if (request === undefined) {
|
|
125
|
+
return output(`No activation request exists for ${task.id} (${task.status}).\n`
|
|
126
|
+
+ (settledLines.length === 0 ? "" : `${settledLines.join("\n")}\n`), { task, request: null, settledRequests: settled });
|
|
127
|
+
}
|
|
128
|
+
const lines = [
|
|
129
|
+
`Activation ${taskActivationOperationRef(task.id, request.operation.requestId)}`,
|
|
130
|
+
`Task: ${task.id} (${task.status})`,
|
|
131
|
+
`Start: ${request.startMode}${request.afterPlanningRun === undefined ? "" : ` after ${task.id}/${request.afterPlanningRun}`}`,
|
|
132
|
+
`Environment: ${describeEnvironmentPlan(request.environmentPlan)}`,
|
|
133
|
+
`Disposition: ${request.disposition}`,
|
|
134
|
+
`Effect: ${request.operation.effect}`,
|
|
135
|
+
...(request.preparationId === undefined
|
|
136
|
+
? []
|
|
137
|
+
: [`Adopted environment: ${request.preparationId}`]),
|
|
138
|
+
...(request.outcome === undefined ? [] : [`Outcome: ${request.outcome}`]),
|
|
139
|
+
`Requested at: ${request.requestedAt}`,
|
|
140
|
+
...settledLines
|
|
141
|
+
];
|
|
142
|
+
return output(`${lines.join("\n")}\n`, { task, request, settledRequests: settled });
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Resolves who is requesting, and whether they are running inside this Task's
|
|
146
|
+
* own planning Turn.
|
|
147
|
+
*
|
|
148
|
+
* The start mode is derived from that fact rather than accepted from the
|
|
149
|
+
* caller: a request can only defer to a Turn the caller is actually in, and a
|
|
150
|
+
* Turn that is not an active planning Turn is simply not a deferral target.
|
|
151
|
+
*/
|
|
152
|
+
function activationCaller(store, options, taskId) {
|
|
153
|
+
const actor = taskLocalActor(store, options.environment, taskId);
|
|
154
|
+
if (actor === "leader") {
|
|
155
|
+
const runtime = currentManagedRuntime(store, options.environment, taskId, SYSTEM_LEADER_ROLE);
|
|
156
|
+
if (runtime === undefined) {
|
|
157
|
+
throw usageError(`Task-local Leader authority requires the current Provider AgentRun: ${taskId}.`);
|
|
158
|
+
}
|
|
159
|
+
const current = runtime.currentRunId === undefined
|
|
160
|
+
? null
|
|
161
|
+
: store.getRun(taskId, runtime.currentRunId);
|
|
162
|
+
return {
|
|
163
|
+
actorId: `task:${taskId}/role:${SYSTEM_LEADER_ROLE}`,
|
|
164
|
+
// Non-secret binding fingerprint of the live Session, matching the Job
|
|
165
|
+
// boundary: reattaching the same Session keeps it, replacing it does not.
|
|
166
|
+
authorityRef: fingerprint([
|
|
167
|
+
runtime.agentId,
|
|
168
|
+
runtime.adapterId,
|
|
169
|
+
runtime.nativeSessionId
|
|
170
|
+
]),
|
|
171
|
+
...(current?.status === "active" && current.purpose === "planning"
|
|
172
|
+
? { planningRunId: current.id }
|
|
173
|
+
: {})
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
if (actor === "operator") {
|
|
177
|
+
return {
|
|
178
|
+
actorId: "global:operator",
|
|
179
|
+
authorityRef: fingerprint([
|
|
180
|
+
"operator",
|
|
181
|
+
store.getGlobalRole("operator")?.activeAgentId ?? "operator"
|
|
182
|
+
])
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
return { actorId: "user:local", authorityRef: fingerprint(["user", "local-terminal"]) };
|
|
186
|
+
}
|
|
187
|
+
function parseEnvironmentPlan(value, usage) {
|
|
188
|
+
if (value === "empty")
|
|
189
|
+
return { kind: "empty" };
|
|
190
|
+
if (value === "scratch")
|
|
191
|
+
return { kind: "scratch" };
|
|
192
|
+
const local = /^local:(?<resourceId>[^:]+):(?<access>read|write)$/u.exec(value);
|
|
193
|
+
if (local?.groups !== undefined) {
|
|
194
|
+
return {
|
|
195
|
+
kind: "local",
|
|
196
|
+
resourceId: local.groups.resourceId,
|
|
197
|
+
access: local.groups.access
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
throw usageError(`Environment plan is invalid: ${value}. Use empty, scratch, or local:<resource>:<read|write>.`, usage);
|
|
201
|
+
}
|
|
202
|
+
function fingerprint(parts) {
|
|
203
|
+
return createHash("sha256").update(JSON.stringify(parts)).digest("hex");
|
|
204
|
+
}
|
|
205
|
+
/** Domain rejections are caller mistakes here, so they carry the usage text. */
|
|
206
|
+
function attempt(usage, action) {
|
|
207
|
+
try {
|
|
208
|
+
return action();
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
throw usageError(error instanceof Error ? error.message : String(error), usage);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function requireTask(store, taskId) {
|
|
215
|
+
const task = store.getTask(taskId);
|
|
216
|
+
if (task === null)
|
|
217
|
+
throw usageError(`Task not found: ${taskId}.`);
|
|
218
|
+
return task;
|
|
219
|
+
}
|
|
220
|
+
function parseTail(args, valueOptions, usage) {
|
|
221
|
+
const positionals = [];
|
|
222
|
+
const options = new Map();
|
|
223
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
224
|
+
const value = args[index];
|
|
225
|
+
if (!value.startsWith("--")) {
|
|
226
|
+
positionals.push(value);
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (!valueOptions.has(value))
|
|
230
|
+
throw usageError(`Unsupported option: ${value}.`, usage);
|
|
231
|
+
if (options.has(value)) {
|
|
232
|
+
throw usageError(`Option may only be specified once: ${value}.`, usage);
|
|
233
|
+
}
|
|
234
|
+
const optionValue = args[index + 1];
|
|
235
|
+
if (optionValue === undefined || optionValue.startsWith("--")) {
|
|
236
|
+
throw usageError(`${value} is required.`, usage);
|
|
237
|
+
}
|
|
238
|
+
options.set(value, optionValue);
|
|
239
|
+
index += 1;
|
|
240
|
+
}
|
|
241
|
+
return { positionals, options };
|
|
242
|
+
}
|
|
243
|
+
function exactPositionals(values, count, usage) {
|
|
244
|
+
if (values.length !== count || values.some((value) => value.trim().length === 0)) {
|
|
245
|
+
throw usageError(usage);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function requiredOption(options, name, usage) {
|
|
249
|
+
const value = options.get(name)?.trim();
|
|
250
|
+
if (value === undefined || value.length === 0) {
|
|
251
|
+
throw usageError(`${name} is required.`, usage);
|
|
252
|
+
}
|
|
253
|
+
return value;
|
|
254
|
+
}
|
|
255
|
+
function output(value, data) {
|
|
256
|
+
return data === undefined
|
|
257
|
+
? { kind: "output", output: value }
|
|
258
|
+
: { kind: "output", output: value, data };
|
|
259
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { usageError } from "../errors/cliError.js";
|
|
2
|
-
import {
|
|
2
|
+
import { MANAGED_NATIVE_SESSION_ENV, currentManagedRuntime } from "../runtime/managedCaller.js";
|
|
3
3
|
const LEADER_ROLE = "leader";
|
|
4
4
|
export function taskActor(environment, taskId) {
|
|
5
5
|
const env = environment ?? {};
|
|
@@ -18,7 +18,7 @@ export function taskActor(environment, taskId) {
|
|
|
18
18
|
}
|
|
19
19
|
if (env.YUI_ROLE !== undefined
|
|
20
20
|
|| env.YUI_AGENT_ID !== undefined
|
|
21
|
-
|| env[
|
|
21
|
+
|| env[MANAGED_NATIVE_SESSION_ENV] !== undefined) {
|
|
22
22
|
throw usageError("Managed Agent identity is incomplete; refusing to infer user authority.");
|
|
23
23
|
}
|
|
24
24
|
return "user";
|
|
@@ -27,15 +27,28 @@ export function taskActor(environment, taskId) {
|
|
|
27
27
|
* Resolve authority for a recoverable Task-local mutation. A managed Leader
|
|
28
28
|
* does not gain that authority from long-lived process environment alone. Its
|
|
29
29
|
* process proves only that Yui launched it for this Task Role; whether it is
|
|
30
|
-
* still the current
|
|
31
|
-
*
|
|
30
|
+
* still the current Session is read from durable state at command time.
|
|
31
|
+
* Execution occupancy is not management authority.
|
|
32
32
|
*/
|
|
33
33
|
export function taskLocalActor(store, environment, taskId) {
|
|
34
34
|
const actor = taskActor(environment, taskId);
|
|
35
35
|
if (actor !== "leader")
|
|
36
36
|
return actor;
|
|
37
|
-
if (
|
|
38
|
-
throw usageError(`Task-local Leader authority requires the current
|
|
37
|
+
if (currentManagedRuntime(store, environment, taskId, LEADER_ROLE) === undefined) {
|
|
38
|
+
throw usageError(`Task-local Leader authority requires the current native Session: ${taskId}.`);
|
|
39
|
+
}
|
|
40
|
+
return actor;
|
|
41
|
+
}
|
|
42
|
+
/** Planning conversations may save facts; delivery checks the immutable
|
|
43
|
+
* native Session authority, not the Task's newly activated status.
|
|
44
|
+
*/
|
|
45
|
+
export function assertTaskDeliveryAuthority(store, environment, taskId) {
|
|
46
|
+
const actor = taskLocalActor(store, environment, taskId);
|
|
47
|
+
if (actor !== "leader")
|
|
48
|
+
return actor;
|
|
49
|
+
const caller = currentManagedRuntime(store, environment, taskId, LEADER_ROLE);
|
|
50
|
+
if (caller?.executionAuthority !== "delivery") {
|
|
51
|
+
throw usageError("This native Session has planning authority, not delivery authority. Use a delivery Session for this operation.");
|
|
39
52
|
}
|
|
40
53
|
return actor;
|
|
41
54
|
}
|
|
@@ -50,31 +63,15 @@ export function projectActor(environment) {
|
|
|
50
63
|
}
|
|
51
64
|
if (env.YUI_ROLE !== undefined
|
|
52
65
|
|| env.YUI_AGENT_ID !== undefined
|
|
53
|
-
|| env[
|
|
66
|
+
|| env[MANAGED_NATIVE_SESSION_ENV] !== undefined) {
|
|
54
67
|
throw usageError("Managed Agent identity is incomplete; refusing to infer user authority.");
|
|
55
68
|
}
|
|
56
69
|
return "user";
|
|
57
70
|
}
|
|
58
71
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
* The identity is Controller-verified rather than self-reported. A managed
|
|
64
|
-
* Task Session reports only its immutable self-identity (Task, Role, caller
|
|
65
|
-
* key); the Controller resolves the current Turn from durable state, so no
|
|
66
|
-
* Turn is ever carried across Turns in a long-lived process environment.
|
|
67
|
-
*
|
|
68
|
-
* rr13: A managed Task Session also carries `callerKey` — the
|
|
69
|
-
* `YUI_JOB_CALLER_KEY` injected at its native Session launch. The Controller
|
|
70
|
-
* hashes it and compares against the durable `jobCallerKeyHashes` map, so a
|
|
71
|
-
* client that reads durable state cannot replay the caller. A `user`-scope
|
|
72
|
-
* caller is rejected outright for job.start/job.cancel (fail-closed); a
|
|
73
|
-
* managed global Agent carries its own durable Session identity.
|
|
74
|
-
*
|
|
75
|
-
* A managed Task Session may only start jobs for its own Task. An incomplete
|
|
76
|
-
* managed identity (role/agent/caller-key vars without a scope) is rejected
|
|
77
|
-
* rather than silently downgraded to user authority.
|
|
72
|
+
* Resolve the caller's Task, Role and native Session identity. The Controller
|
|
73
|
+
* verifies it against durable state and supplies the current AgentRun. A Task
|
|
74
|
+
* caller cannot control another Task; an incomplete identity is not a user.
|
|
78
75
|
*/
|
|
79
76
|
export function resolveJobCaller(environment, taskId) {
|
|
80
77
|
const env = environment ?? {};
|
|
@@ -83,17 +80,14 @@ export function resolveJobCaller(environment, taskId) {
|
|
|
83
80
|
throw usageError("A managed Task Session may not start Jobs for a different Task.");
|
|
84
81
|
}
|
|
85
82
|
const role = env.YUI_ROLE;
|
|
86
|
-
|
|
87
|
-
//
|
|
88
|
-
// Controller boundary.
|
|
89
|
-
const callerKey = env[MANAGED_CALLER_KEY_ENV];
|
|
90
|
-
// The Turn is deliberately absent: the Controller reads the current Turn
|
|
83
|
+
const nativeSessionId = env.CODEX_THREAD_ID ?? env.YUI_NATIVE_SESSION_ID;
|
|
84
|
+
// The AgentRun is deliberately absent: the Controller reads the current AgentRun
|
|
91
85
|
// for this Task Role from durable state when it authorizes the request.
|
|
92
86
|
return {
|
|
93
87
|
scope: "task",
|
|
94
88
|
taskId,
|
|
95
89
|
role,
|
|
96
|
-
...(
|
|
90
|
+
...(nativeSessionId === undefined ? {} : { nativeSessionId })
|
|
97
91
|
};
|
|
98
92
|
}
|
|
99
93
|
if (env.YUI_SESSION_SCOPE === "global") {
|
|
@@ -102,28 +96,13 @@ export function resolveJobCaller(environment, taskId) {
|
|
|
102
96
|
role: env.YUI_ROLE,
|
|
103
97
|
agentId: env.YUI_AGENT_ID,
|
|
104
98
|
adapterId: env.YUI_ADAPTER_ID,
|
|
105
|
-
|
|
106
|
-
nativeSessionId: env.YUI_NATIVE_SESSION_ID
|
|
99
|
+
nativeSessionId: env.CODEX_THREAD_ID ?? env.YUI_NATIVE_SESSION_ID
|
|
107
100
|
};
|
|
108
101
|
}
|
|
109
102
|
if (env.YUI_ROLE !== undefined
|
|
110
103
|
|| env.YUI_AGENT_ID !== undefined
|
|
111
|
-
|| env[
|
|
104
|
+
|| env[MANAGED_NATIVE_SESSION_ENV] !== undefined) {
|
|
112
105
|
throw usageError("Managed Agent identity is incomplete; refusing to infer user authority.");
|
|
113
106
|
}
|
|
114
107
|
return { scope: "user" };
|
|
115
108
|
}
|
|
116
|
-
/**
|
|
117
|
-
* Resolve the current Task Leader Turn for event attribution and Task-local
|
|
118
|
-
* Leader authority.
|
|
119
|
-
*
|
|
120
|
-
* A long-lived managed process cannot carry this: its environment is frozen at
|
|
121
|
-
* launch, so any Turn it holds is stale the moment Yui advances the Task. The
|
|
122
|
-
* Turn is therefore read from durable state, and the process only has to prove
|
|
123
|
-
* that it is still the current runtime of the Leader Role through its
|
|
124
|
-
* per-Session caller key. When the Role has no active Turn, or the process has
|
|
125
|
-
* been superseded, there is simply no Leader action window.
|
|
126
|
-
*/
|
|
127
|
-
export function taskLeaderActionTurnId(store, taskId, environment) {
|
|
128
|
-
return currentManagedRuntime(store, environment, taskId, LEADER_ROLE)?.currentTurnId;
|
|
129
|
-
}
|