@zq-silk/yui 0.15.12 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +3 -2
- package/ARCHITECTURE.zh-CN.md +3 -2
- package/dist/artifacts/artifactCommitLock.js +16 -55
- package/dist/artifacts/taskArtifactRepository.js +3 -3
- package/dist/cli/agentConfigurationPicker.js +1 -1
- package/dist/cli/commandCatalog.js +57 -41
- package/dist/cli/completion.js +18 -18
- package/dist/cli/completionWizard.js +1 -1
- package/dist/cli/dynamicCompletion.js +1 -1
- package/dist/cli/interactionPolicy.js +7 -3
- package/dist/cli/invocationAuthority.js +64 -0
- package/dist/cli/managedDiagnostics.js +1 -1
- package/dist/cli/parseRepeatable.js +35 -0
- package/dist/cli/updatePorts.js +26 -5
- package/dist/cli.js +1280 -1243
- package/dist/commands/agentCommands.js +4 -4
- package/dist/commands/capabilityCommands.js +1 -1
- package/dist/commands/configCommands.js +6 -57
- package/dist/commands/configOverview.js +2 -2
- package/dist/commands/durableJobCommands.js +12 -6
- package/dist/commands/globalRoleCommands.js +53 -29
- package/dist/commands/grantCommands.js +0 -3
- package/dist/commands/jobCommands.js +1 -4
- package/dist/commands/operatorCommands.js +3 -3
- package/dist/commands/projectCommands.js +1 -10
- package/dist/commands/resourcesCommands.js +10 -40
- package/dist/commands/roleConfiguration.js +2 -6
- package/dist/commands/taskActivationCommands.js +4 -8
- package/dist/commands/taskAgentCapabilities.js +9 -0
- package/dist/commands/taskCommandSupport.js +155 -0
- package/dist/commands/taskCommandTypes.js +1 -0
- package/dist/commands/taskCommands.js +271 -710
- package/dist/commands/taskCompletionGate.js +1 -1
- package/dist/commands/taskExecutionCommands.js +1 -1
- package/dist/commands/taskFactCommands.js +325 -0
- package/dist/commands/taskInputCommands.js +12 -42
- package/dist/commands/taskIntegrationCommands.js +17 -57
- package/dist/commands/taskNextActionCommand.js +7 -6
- package/dist/commands/taskPublicationAdoptCommand.js +3 -3
- package/dist/commands/taskPublicationCommands.js +1 -1
- package/dist/commands/taskPublicationVerifyCommand.js +1 -1
- package/dist/commands/taskRemoteDeliveryCommand.js +2 -94
- package/dist/commands/taskUpstreamCommands.js +19 -8
- package/dist/commands/workflowCommands.js +6 -1
- package/dist/completion/completionInstaller.js +26 -26
- package/dist/completion/completionState.js +26 -26
- package/dist/completion/fileCompletionManager.js +6 -11
- package/dist/config/configCatalog.js +0 -2
- package/dist/config/timeZone.js +14 -0
- package/dist/config/yuiConfig.js +0 -29
- package/dist/context/runContextPack.js +2 -3
- package/dist/context/taskCatalog.js +3 -5
- package/dist/context/taskContext.js +139 -151
- package/dist/context/wakeRunReferences.js +11 -0
- package/dist/controller/agentCapabilities.js +58 -0
- package/dist/controller/agentRuntimeObserver.js +6 -11
- package/dist/controller/clientRuntime.js +22 -14
- package/dist/controller/controller.js +9 -22
- package/dist/controller/fileSchedulerStoreAdapter.js +117 -212
- package/dist/controller/jobClient.js +4 -4
- package/dist/controller/jobControl.js +60 -25
- package/dist/controller/jobSupervisor.js +4 -4
- package/dist/controller/providerRetryDelivery.js +7 -7
- package/dist/controller/runtime.js +51 -44
- package/dist/controller/runtimeEventProcessor.js +0 -5
- package/dist/controller/sessionOwnerReconciliation.js +5 -0
- package/dist/controller/taskAgentError.js +35 -0
- package/dist/coordination/workMailboxQueue.js +2 -24
- package/dist/core/controllerClient.js +2 -2
- package/dist/core/fileLockOwner.js +74 -0
- package/dist/decision/decision.js +17 -0
- package/dist/doctor/doctor.js +18 -9
- package/dist/event/taskEvent.js +12 -0
- package/dist/execution/workItemExecutionProjection.js +2 -2
- package/dist/executor/agentCapabilityConfig.js +43 -0
- package/dist/executor/agentConfigurationCatalog.js +49 -11
- package/dist/executor/agentExecutor.js +5 -8
- package/dist/executor/fileRoleLaunchPlanner.js +11 -53
- package/dist/executor/taskAgentCapabilities.js +64 -0
- package/dist/integration/deliveryObligation.js +1 -69
- package/dist/integration/gitIntegrationService.js +60 -84
- package/dist/integration/integrationAttempt.js +3 -14
- package/dist/integration/integrationSourceApplication.js +4 -12
- package/dist/integration/manifestTags.js +2 -2
- package/dist/job/durableJob.js +4 -25
- package/dist/job/jobAssignmentScope.js +22 -0
- package/dist/job/jobOperation.js +16 -0
- package/dist/job/jobRunner.js +2 -1
- package/dist/job/stepDirectory.js +15 -0
- package/dist/kernel/builtinCapabilities.js +15 -11
- package/dist/kernel/kernelPorts.js +1 -17
- package/dist/lifecycle/exactRunTerminalization.js +1 -9
- package/dist/message/inputControlResolution.js +3 -4
- package/dist/message/message.js +17 -20
- package/dist/message/messageContinuation.js +1 -1
- package/dist/milestone/milestone.js +11 -0
- package/dist/observability/orchestrationMetrics.js +24 -1
- package/dist/observability/runtimeIdentity.js +1 -17
- package/dist/output/agentConfigurationPresentation.js +2 -0
- package/dist/output/timePresentation.js +1 -14
- package/dist/plugins/pluginService.js +12 -4
- package/dist/release/releaseWorkflowPorts.js +45 -96
- package/dist/release/runtimeRelease.js +9 -15
- package/dist/repository/projectMaintenanceLock.js +23 -64
- package/dist/repository/taskBaseFreshness.js +1 -1
- package/dist/repository/taskWorkspaceCoordinator.js +13 -22
- package/dist/repository/taskWorkspacePreparer.js +29 -25
- package/dist/repository/workspaceCleanupInspection.js +5 -5
- package/dist/resources/autoResourceGc.js +4 -77
- package/dist/resources/liveReferences.js +7 -54
- package/dist/resources/resourceDiscovery.js +5 -118
- package/dist/resources/resourceGc.js +233 -181
- package/dist/resources/resourceRegistrar.js +5 -4
- package/dist/resources/resourceRegistry.js +3 -37
- package/dist/resources/resourceRegistryStore.js +13 -36
- package/dist/resources/sqliteResourceRegistry.js +43 -26
- package/dist/review/deltaRecheck.js +1 -1
- package/dist/review/reviewAcceptance.js +1 -1
- package/dist/review/reviewDecision.js +1 -1
- package/dist/review/reviewRound.js +8 -7
- package/dist/review/taskFinalReviewContractResolution.js +1 -1
- package/dist/runtime/acpProtocol.js +4 -51
- package/dist/runtime/acpSession.js +10 -75
- package/dist/runtime/acpSessionConfiguration.js +1 -8
- package/dist/runtime/agentEndpoint.js +3 -2
- package/dist/runtime/agentFailureContext.js +43 -0
- package/dist/runtime/agentHost.js +15 -15
- package/dist/runtime/codexInteractiveHost.js +2 -2
- package/dist/runtime/index.js +0 -1
- package/dist/runtime/managedCaller.js +21 -1
- package/dist/runtime/providerErrors.js +38 -0
- package/dist/runtime/runtimeObservation.js +0 -5
- package/dist/runtime/runtimeSessionCandidate.js +2 -3
- package/dist/runtime/sessionOwnerIdentity.js +1 -1
- package/dist/runtime/structuredProviderHost.js +4 -41
- package/dist/runtime/tmuxAdapters.js +6 -8
- package/dist/scheduler/activeRoleRunDelivery.js +7 -7
- package/dist/scheduler/leaderWakeupProcessor.js +19 -6
- package/dist/scheduler/operatorEvent.js +12 -20
- package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
- package/dist/scheduler/ports.js +5 -8
- package/dist/scheduler/roleRunLiveness.js +4 -4
- package/dist/scheduler/roleRunStall.js +16 -23
- package/dist/scheduler/taskExecutionProjection.js +25 -33
- package/dist/scheduler/taskObservabilityProjection.js +0 -1
- package/dist/scheduler/taskWake.js +5 -10
- package/dist/scheduler/wakeReason.js +2 -0
- package/dist/scheduler/wakeupQueue.js +2 -4
- package/dist/setup/setupCommand.js +1 -1
- package/dist/storage/contextRecords.js +106 -0
- package/dist/storage/currentTaskStore.js +1 -1
- package/dist/storage/homeLayout.js +13 -17
- package/dist/storage/migrations/agentFailureContext.js +22 -0
- package/dist/storage/migrations/currentInputContract.js +86 -0
- package/dist/storage/migrations/currentRuntimeContract.js +228 -0
- package/dist/storage/migrations/historicalVerificationPlan.js +35 -0
- package/dist/storage/migrations/integrationContinuation.js +5 -4
- package/dist/storage/migrations/narrowAgentFailureContext.js +65 -0
- package/dist/storage/migrations/notificationOnlyWakes.js +74 -0
- package/dist/storage/migrations/verificationPlanV1.js +162 -0
- package/dist/storage/migrations/verificationPolicy.js +74 -0
- package/dist/storage/migrations/workItemHistory.js +46 -0
- package/dist/storage/persistenceWorker.js +12 -4
- package/dist/storage/recordValidation.js +87 -0
- package/dist/storage/sqliteSchema.js +142 -1
- package/dist/storage/sqliteStore.js +156 -162
- package/dist/storage/storageSchema.js +2 -14
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +15 -7
- package/dist/storage/taskStore.js +3 -7
- package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
- package/dist/task/completionReadiness.js +5 -24
- package/dist/task/draftPlan.js +0 -53
- package/dist/task/nextAction.js +7 -13
- package/dist/task/remoteDeliveryService.js +89 -0
- package/dist/task/taskActivation.js +2 -25
- package/dist/task/taskActivationService.js +0 -2
- package/dist/task/taskRecordReference.js +0 -1
- package/dist/task/taskSubmission.js +3 -14
- package/dist/telemetry/sqliteTelemetryBatch.js +29 -0
- package/dist/telemetry/sqliteTelemetryStore.js +49 -49
- package/dist/telemetry/telemetryWiring.js +4 -3
- package/dist/tmux/tmuxManager.js +29 -20
- package/dist/verification/gateArtifact.js +15 -24
- package/dist/verification/gateArtifactStore.js +14 -7
- package/dist/verification/verificationGateService.js +29 -88
- package/dist/verification/verificationPlan.js +27 -72
- package/dist/web/assets/client/app.js +1 -1
- package/dist/web/assets/client/taskSurface.js +4 -4
- package/dist/web/webServer.js +4 -6
- package/dist/web/webSnapshot.js +3 -83
- package/dist/web/webTaskSurface.js +17 -46
- package/dist/workItem/workItem.js +1 -12
- package/docs/architecture/capabilities-and-resources.md +14 -2
- package/docs/architecture/capabilities-and-resources.zh-CN.md +11 -1
- package/docs/managed-turn-and-session-runtime.md +83 -10
- package/docs/managed-turn-and-session-runtime.zh-CN.md +65 -11
- package/docs/plugin-sdk.md +4 -2
- package/docs/provider-runtime.md +3 -1
- package/docs/provider-runtime.zh-CN.md +4 -2
- package/docs/release-workflow.md +190 -7
- package/docs/release-workflow.zh-CN.md +139 -5
- package/docs/roles-and-configuration.md +29 -0
- package/docs/roles-and-configuration.zh-CN.md +21 -0
- package/docs/sqlite-control-plane-design.md +16 -3
- package/docs/sqlite-control-plane-design.zh-CN.md +13 -2
- package/docs/task-dag-semantics.md +3 -3
- package/docs/task-dag-semantics.zh-CN.md +2 -2
- package/docs/task-delivery.md +91 -5
- package/docs/task-delivery.zh-CN.md +72 -4
- package/docs/task-discovery.md +10 -11
- package/docs/task-discovery.zh-CN.md +8 -9
- package/docs/testing/verification-levels.md +145 -9
- package/docs/testing/verification-levels.zh-CN.md +103 -8
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +5 -0
- package/skills/yui-leader/references/integration.md +9 -5
- package/skills/yui-leader/references/planning.md +9 -3
- package/skills/yui-operator/SKILL.md +15 -10
- package/skills/yui-runtime/SKILL.md +6 -6
- package/skills/yui-runtime/references/recovery.md +47 -0
- package/dist/agent/agentRegistry.js +0 -10
- package/dist/agentRun/runIdentity.js +0 -22
- package/dist/commands/deliveryGuardPreflight.js +0 -30
- package/dist/commands/taskIntegrationQueueCommands.js +0 -226
- package/dist/commands/taskOverviewCommand.js +0 -377
- package/dist/completion/completionWizard.js +0 -125
- package/dist/context/dispatchContext.js +0 -110
- package/dist/context/wakeNotification.js +0 -144
- package/dist/integration/integrationCheckEvidenceReuse.js +0 -53
- package/dist/integration/integrationQueueEntry.js +0 -191
- package/dist/integration/integrationQueueService.js +0 -810
- package/dist/release/fakeReleasePorts.js +0 -55
- package/dist/runtime/sessionOwnerRegistry.js +0 -137
- package/dist/task/deliveryGuard.js +0 -226
- /package/dist/{commands/taskActor.js → task/taskAuthority.js} +0 -0
|
@@ -1,31 +1,30 @@
|
|
|
1
1
|
import { execFile, spawn } from "node:child_process";
|
|
2
|
-
import { createHash } from "node:crypto";
|
|
3
|
-
import { existsSync } from "node:fs";
|
|
4
2
|
import { lstat, mkdir, open, rm, stat } from "node:fs/promises";
|
|
5
3
|
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
6
4
|
import { promisify } from "node:util";
|
|
7
5
|
import { selectEnvironment } from "../agent/launchEnvironment.js";
|
|
8
6
|
import { controllerSocketPath } from "../core/controllerEndpoint.js";
|
|
9
7
|
import { durableJobIdempotencyKey } from "../job/durableJob.js";
|
|
10
|
-
import {
|
|
11
|
-
import { planBootstrapJobSteps, planL2JobSteps } from "../verification/verificationPlan.js";
|
|
12
|
-
import { assertNoAdHocFullSuiteChecks, checkResultsFromGateArtifact, checkResultsFromGateJob, gateIdentityForCandidate, lookupReusableGateArtifact, recordGateArtifactFromJob, recordGateArtifactFromStepOutcomes, resolveVerificationGate, runGateStepsInProcess } from "../verification/verificationGateService.js";
|
|
13
|
-
import { recordGateArtifactPotentialReuse, recordGateArtifactReuse } from "../verification/gateArtifact.js";
|
|
14
|
-
import { touchGateArtifact } from "../verification/gateArtifactStore.js";
|
|
8
|
+
import { readRuntimeIdentity } from "../release/runtimeRelease.js";
|
|
15
9
|
import { NodeGitWorkspace } from "../repository/gitWorkspace.js";
|
|
16
|
-
import { integrationWorkspaceRoot } from "../repository/taskWorkspacePreparer.js";
|
|
17
10
|
import { acquireProjectMaintenanceLocks } from "../repository/projectMaintenanceLock.js";
|
|
18
11
|
import { taskWorkspaceRefSegment } from "../repository/taskWorkspaceIdentity.js";
|
|
12
|
+
import { integrationWorkspaceRoot } from "../repository/taskWorkspacePreparer.js";
|
|
13
|
+
import { ResourceRegistrar } from "../resources/resourceRegistrar.js";
|
|
19
14
|
import { FileTaskRuntimeIsolation } from "../runtime/taskRuntimeIsolation.js";
|
|
20
|
-
import { managedIntegrationRuntimeRoot } from "../storage/homeLayout.js";
|
|
15
|
+
import { integrationTmuxSocketRoot, managedIntegrationRuntimeRoot } from "../storage/homeLayout.js";
|
|
21
16
|
import { advanceTaskProjectCommit } from "../task/task.js";
|
|
22
17
|
import { yuiTmuxServerName } from "../tmux/tmuxManager.js";
|
|
23
|
-
import {
|
|
18
|
+
import { recordGateArtifactReuse } from "../verification/gateArtifact.js";
|
|
19
|
+
import { touchGateArtifact } from "../verification/gateArtifactStore.js";
|
|
20
|
+
import { beginGateVerification, checkResultsFromGateArtifact, checkResultsFromGateJob, gateIdentityForCandidate, lookupReusableGateArtifact, recordGateArtifactFromJob, recordGateArtifactFromStepOutcomes, resolveVerificationGate, runGateStepsInProcess } from "../verification/verificationGateService.js";
|
|
21
|
+
import { planBootstrapJobSteps, planL2JobSteps } from "../verification/verificationPlan.js";
|
|
24
22
|
import { createManagedWorkspace } from "../worktree/managedWorkspace.js";
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import { findReusableIntegrationCheckEvidence, INTEGRATION_RUNTIME_RELEASE_ENV } from "./integrationCheckEvidenceReuse.js";
|
|
23
|
+
import { recordIntegrationCheckJob, updateIntegrationAttempt } from "./integrationAttempt.js";
|
|
24
|
+
import { applyIntegrationSource, assertIntegrationCandidate, assertRecordedSourceCandidate } from "./integrationSourceApplication.js";
|
|
28
25
|
const executeFile = promisify(execFile);
|
|
26
|
+
/** Preserve the exact execution-environment fence in durable Job input digests. */
|
|
27
|
+
const INTEGRATION_RUNTIME_RELEASE_ENV = "YUI_INTEGRATION_RUNTIME_RELEASE_ID";
|
|
29
28
|
const INTEGRATION_OPERATIONAL_ENVIRONMENT_NAMES = [
|
|
30
29
|
"PATH",
|
|
31
30
|
"USER",
|
|
@@ -421,26 +420,7 @@ export class GitIntegrationService {
|
|
|
421
420
|
}
|
|
422
421
|
await assertIntegrationCandidate(path, attempt.candidateCommit, workspace.branch);
|
|
423
422
|
await assertTargetReadyForChecks(repositoryPath, attempt.targetRef, attempt.beforeCommit);
|
|
424
|
-
const { runtime, head, steps, environment, inputDigest
|
|
425
|
-
const ownedJobs = this.store.listDurableJobs(attempt.taskId).filter(job => job.owner.kind === "integration-attempt" && job.owner.integrationAttemptId === attempt.id);
|
|
426
|
-
if (gate === undefined && releaseId !== null && attempt.checkInputDigest === undefined
|
|
427
|
-
&& ownedJobs.length === 0 && attempt.jobId === undefined) {
|
|
428
|
-
const reusable = findReusableIntegrationCheckEvidence({
|
|
429
|
-
taskId: attempt.taskId,
|
|
430
|
-
projectId: attempt.projectId,
|
|
431
|
-
currentAttemptId: attempt.id,
|
|
432
|
-
candidateCommit: head,
|
|
433
|
-
checkCommands: attempt.checkCommands,
|
|
434
|
-
runtimeReleaseId: releaseId,
|
|
435
|
-
attempts: this.store.listIntegrationAttempts(attempt.taskId),
|
|
436
|
-
jobs: this.store.listDurableJobs(attempt.taskId),
|
|
437
|
-
logExists: (homeRelativePath) => existsSync(join(this.home, homeRelativePath)),
|
|
438
|
-
logPathFor: (job, relativeLogPath) => (join(job.artifactsLocator, "logs", relativeLogPath))
|
|
439
|
-
});
|
|
440
|
-
if (reusable !== null) {
|
|
441
|
-
return this.#finalizeGateSuccess(updateIntegrationAttempt(attempt, { checkInputDigest: inputDigest }, this.now()), workspace, repositoryPath, head, [...reusable.checks]);
|
|
442
|
-
}
|
|
443
|
-
}
|
|
423
|
+
const { runtime, head, steps, environment, inputDigest } = await this.#checkSpecification(attempt, managedWorkspace, gate);
|
|
444
424
|
if (attempt.checkInputDigest !== undefined && attempt.checkInputDigest !== inputDigest) {
|
|
445
425
|
throw new Error("Integration check conditions changed since admission; inspect the original Job, then abort or start a new attempt.");
|
|
446
426
|
}
|
|
@@ -457,6 +437,14 @@ export class GitIntegrationService {
|
|
|
457
437
|
})
|
|
458
438
|
}, this.now());
|
|
459
439
|
this.store.saveIntegrationAttempt(attempt.taskId, persisted);
|
|
440
|
+
if (gate !== undefined) {
|
|
441
|
+
// Resumption can enter here after persisting admission but before the
|
|
442
|
+
// original call withdrew old proof or recorded its Job id.
|
|
443
|
+
beginGateVerification(this.store, gateIdentityForCandidate({
|
|
444
|
+
projectId: attempt.projectId, gate, level: "L2", commit: head,
|
|
445
|
+
targetRef: attempt.targetRef, baseHead: attempt.beforeCommit
|
|
446
|
+
}), gate.plan, this.now());
|
|
447
|
+
}
|
|
460
448
|
// All domain admission and durable candidate identity precede Job effects.
|
|
461
449
|
this.runtimeIsolation.activate(runtime);
|
|
462
450
|
await prepareIntegrationRuntimeHome(runtime, this.home);
|
|
@@ -520,7 +508,7 @@ export class GitIntegrationService {
|
|
|
520
508
|
baseHead: attempt.beforeCommit
|
|
521
509
|
});
|
|
522
510
|
try {
|
|
523
|
-
const artifact = await recordGateArtifactFromJob(this.store, this.home, identity, gate.plan, job, this.now());
|
|
511
|
+
const artifact = await this.#publishGateEvidence(workspace, job.head, () => recordGateArtifactFromJob(this.store, this.home, identity, gate.plan, job, this.now()));
|
|
524
512
|
if (checks.every((check) => check.outcome !== "failed")) {
|
|
525
513
|
checks.push(...checkResultsFromGateArtifact(artifact));
|
|
526
514
|
}
|
|
@@ -562,11 +550,10 @@ export class GitIntegrationService {
|
|
|
562
550
|
/**
|
|
563
551
|
* Issue 08: the VerificationPlan gate for a configured Project.
|
|
564
552
|
*
|
|
565
|
-
*
|
|
566
|
-
* returns an existing successful artifact for the same identity tuple
|
|
553
|
+
* Returns an existing successful artifact for the same identity tuple
|
|
567
554
|
* (project + exact commit + plan digest + toolchain digest + target
|
|
568
|
-
* boundary)
|
|
569
|
-
*
|
|
555
|
+
* boundary), unless this attempt explicitly requests fresh checks.
|
|
556
|
+
* The gate itself runs as bootstrap + L2 DurableJob steps (or
|
|
570
557
|
* in-process when no Controller job port is available) and records a
|
|
571
558
|
* self-contained GateArtifact. The final CAS in
|
|
572
559
|
* {@link #finalizeGateSuccess} still fences a target that moves during the
|
|
@@ -574,19 +561,26 @@ export class GitIntegrationService {
|
|
|
574
561
|
*/
|
|
575
562
|
async #runVerificationGate(attempt, workspace, path, managedWorkspace, repositoryPath, gate) {
|
|
576
563
|
const spec = await this.#checkSpecification(attempt, managedWorkspace, gate);
|
|
564
|
+
const unsettled = this.store.listIntegrationAttempts(attempt.taskId).find(other => {
|
|
565
|
+
if (other.id === attempt.id || other.projectId !== attempt.projectId
|
|
566
|
+
|| other.targetRef !== attempt.targetRef || other.beforeCommit !== attempt.beforeCommit
|
|
567
|
+
|| other.candidateCommit !== attempt.candidateCommit
|
|
568
|
+
|| other.gatePlanDigest !== gate.planDigest || other.gateToolchainDigest !== gate.toolchainDigest)
|
|
569
|
+
return false;
|
|
570
|
+
if (other.status === "running" || other.status === "validating")
|
|
571
|
+
return true;
|
|
572
|
+
const job = other.jobId === undefined ? null : this.store.getDurableJob(other.taskId, other.jobId);
|
|
573
|
+
return job !== null && (job.status === "queued" || job.status === "running"
|
|
574
|
+
|| (job.status === "unknown-needs-attention" && job.acknowledgedAt === undefined));
|
|
575
|
+
});
|
|
576
|
+
if (unsettled !== undefined) {
|
|
577
|
+
throw new Error(`Exact verification is unsettled in ${unsettled.taskId}/${unsettled.id}; continue or settle its original Job before requesting another check.`);
|
|
578
|
+
}
|
|
577
579
|
attempt = updateIntegrationAttempt(attempt, {
|
|
578
580
|
checkInputDigest: spec.inputDigest,
|
|
579
581
|
gatePlanDigest: gate.planDigest, gateToolchainDigest: gate.toolchainDigest
|
|
580
582
|
}, this.now());
|
|
581
583
|
this.store.saveIntegrationAttempt(attempt.taskId, attempt);
|
|
582
|
-
if (gate.mode === "enforce") {
|
|
583
|
-
try {
|
|
584
|
-
assertNoAdHocFullSuiteChecks(gate.plan, attempt.checkCommands);
|
|
585
|
-
}
|
|
586
|
-
catch (error) {
|
|
587
|
-
return this.#fail(attempt, error instanceof Error ? error : new Error(String(error)), "verification-plan", workspace);
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
584
|
const candidateCommit = await gitLine(["-C", path, "rev-parse", "HEAD^{commit}"]);
|
|
591
585
|
const identity = gateIdentityForCandidate({
|
|
592
586
|
projectId: attempt.projectId,
|
|
@@ -596,7 +590,7 @@ export class GitIntegrationService {
|
|
|
596
590
|
targetRef: attempt.targetRef,
|
|
597
591
|
baseHead: attempt.beforeCommit
|
|
598
592
|
});
|
|
599
|
-
if (
|
|
593
|
+
if (!attempt.rerunChecks && attempt.checkCommands.length === 0) {
|
|
600
594
|
const existing = await lookupReusableGateArtifact(this.store, identity);
|
|
601
595
|
if (existing !== null) {
|
|
602
596
|
touchGateArtifact(this.store, recordGateArtifactReuse(existing, this.now()));
|
|
@@ -604,31 +598,22 @@ export class GitIntegrationService {
|
|
|
604
598
|
return this.#finalizeGateSuccess(attempt, workspace, repositoryPath, candidateCommit, checks);
|
|
605
599
|
}
|
|
606
600
|
}
|
|
607
|
-
else {
|
|
608
|
-
// Record mode: observe the potential reuse without skipping the gate.
|
|
609
|
-
const existing = await lookupReusableGateArtifact(this.store, identity);
|
|
610
|
-
if (existing !== null) {
|
|
611
|
-
touchGateArtifact(this.store, recordGateArtifactPotentialReuse(existing, this.now()));
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
601
|
if (this.jobPort !== undefined) {
|
|
615
602
|
return this.#startCheckJob(attempt, workspace, path, managedWorkspace, repositoryPath, gate);
|
|
616
603
|
}
|
|
617
|
-
|
|
604
|
+
beginGateVerification(this.store, identity, gate.plan, this.now());
|
|
605
|
+
// An explicit local caller without a Controller Job port runs the
|
|
618
606
|
// plan gate in-process and record the artifact directly.
|
|
619
607
|
const runtime = this.#runtimePreparation(attempt, managedWorkspace);
|
|
620
608
|
this.runtimeIsolation.activate(runtime);
|
|
621
609
|
let cleanupReason = "failure";
|
|
622
610
|
try {
|
|
623
611
|
await prepareIntegrationRuntimeHome(runtime, this.home);
|
|
624
|
-
const steps =
|
|
625
|
-
...planBootstrapJobSteps(gate.plan),
|
|
626
|
-
...planL2JobSteps(gate.plan)
|
|
627
|
-
];
|
|
612
|
+
const steps = spec.steps;
|
|
628
613
|
const outcomes = await runGateStepsInProcess(path, steps, spec.environment, integrationCheckDirectory(this.home, attempt.taskId, attempt.id), candidateCommit);
|
|
629
614
|
const succeeded = outcomes.length === steps.length
|
|
630
615
|
&& outcomes.every((outcome) => outcome.exitCode === 0 && outcome.signal === null && !outcome.timedOut);
|
|
631
|
-
const artifact = await recordGateArtifactFromStepOutcomes(this.store, identity, gate.plan, outcomes, succeeded, this.now());
|
|
616
|
+
const artifact = await this.#publishGateEvidence(workspace, candidateCommit, () => recordGateArtifactFromStepOutcomes(this.store, identity, gate.plan, outcomes, succeeded, this.now()));
|
|
632
617
|
const checks = checkResultsFromGateArtifact(artifact);
|
|
633
618
|
cleanupReason = succeeded ? "completion" : "failure";
|
|
634
619
|
if (!succeeded) {
|
|
@@ -642,6 +627,13 @@ export class GitIntegrationService {
|
|
|
642
627
|
this.runtimeIsolation.cleanup(runtime, cleanupReason);
|
|
643
628
|
}
|
|
644
629
|
}
|
|
630
|
+
/** Both executors publish proof only for the exact clean candidate they
|
|
631
|
+
* checked. A later delivery/CAS failure is separate from this evidence, but
|
|
632
|
+
* a mutated candidate must never enter the reusable-success cache. */
|
|
633
|
+
async #publishGateEvidence(workspace, candidateCommit, publish) {
|
|
634
|
+
await assertIntegrationCandidate(workspace.path, candidateCommit, workspace.branch);
|
|
635
|
+
return publish();
|
|
636
|
+
}
|
|
645
637
|
async #finalizeGateSuccess(attempt, workspace, repositoryPath, candidateCommit, checks) {
|
|
646
638
|
await assertIntegrationCandidate(workspace.path, candidateCommit, workspace.branch);
|
|
647
639
|
const validating = updateIntegrationAttempt(attempt, {
|
|
@@ -654,7 +646,7 @@ export class GitIntegrationService {
|
|
|
654
646
|
const committed = this.#recordCommitted(validating);
|
|
655
647
|
return this.#terminalResult("committed", committed, workspace);
|
|
656
648
|
}
|
|
657
|
-
#runtimePreparation(
|
|
649
|
+
#runtimePreparation(_attempt, workspace) {
|
|
658
650
|
return this.runtimeIsolation.preflight({
|
|
659
651
|
workspace,
|
|
660
652
|
allowExactActive: true
|
|
@@ -668,11 +660,12 @@ export class GitIntegrationService {
|
|
|
668
660
|
const head = attempt.candidateCommit;
|
|
669
661
|
if (head === undefined)
|
|
670
662
|
throw new Error("Integration check specification requires a candidate.");
|
|
671
|
-
const
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
: [...planBootstrapJobSteps(gate.plan
|
|
663
|
+
const explicitChecks = attempt.checkCommands.map((command, index) => ({
|
|
664
|
+
name: `check-${index + 1}`, command, timeoutMs: 30 * 60_000
|
|
665
|
+
}));
|
|
666
|
+
const steps = gate === undefined ? explicitChecks
|
|
667
|
+
: [...planBootstrapJobSteps(gate.plan, managedWorkspace.root),
|
|
668
|
+
...planL2JobSteps(gate.plan, managedWorkspace.root), ...explicitChecks]
|
|
676
669
|
.map(step => ({ ...step, timeoutMs: 30 * 60_000 }));
|
|
677
670
|
const releaseId = integrationRuntimeReleaseIdentity(this.home);
|
|
678
671
|
const environment = Object.freeze({
|
|
@@ -684,7 +677,7 @@ export class GitIntegrationService {
|
|
|
684
677
|
projectId: attempt.projectId, head, workspace: managedWorkspace.root,
|
|
685
678
|
env: environment, steps
|
|
686
679
|
});
|
|
687
|
-
return { runtime, head, steps, environment, inputDigest
|
|
680
|
+
return { runtime, head, steps, environment, inputDigest };
|
|
688
681
|
}
|
|
689
682
|
async #recoverValidating(attempt, workspace, managedWorkspace, repositoryPath) {
|
|
690
683
|
if (attempt.candidateCommit === undefined || attempt.checks === undefined) {
|
|
@@ -805,11 +798,6 @@ function integrationSummary(attempt) {
|
|
|
805
798
|
? `Upstream ${attempt.source.remoteCommit} was already represented; Task head was unchanged.`
|
|
806
799
|
: `Rebased Task changes onto upstream ${attempt.source.remoteCommit}.`;
|
|
807
800
|
}
|
|
808
|
-
if (attempt.source.kind === "historical-change-sets") {
|
|
809
|
-
return unchanged
|
|
810
|
-
? "Historical Integration was already represented; Task head was unchanged."
|
|
811
|
-
: "Historical Integration advanced the Task head.";
|
|
812
|
-
}
|
|
813
801
|
if (attempt.source.strategy === "manual") {
|
|
814
802
|
const decision = attempt.resolution;
|
|
815
803
|
if (decision?.action !== "manual-resolution") {
|
|
@@ -1036,18 +1024,6 @@ function defaultIntegrationRuntimeIsolation(home, homeId) {
|
|
|
1036
1024
|
}
|
|
1037
1025
|
});
|
|
1038
1026
|
}
|
|
1039
|
-
/**
|
|
1040
|
-
* Short `/tmp` directory that holds ONLY the integration check's tmux socket
|
|
1041
|
-
* endpoint. tmux uses a `sockaddr_un` path whose length budget cannot absorb a
|
|
1042
|
-
* deep Home path, so this single IPC endpoint is the one approved exception to
|
|
1043
|
-
* the unified-Home contract. All other integration runtime state (data, cache,
|
|
1044
|
-
* ordinary temp) lives in the Home partition from `managedIntegrationRuntimeRoot`.
|
|
1045
|
-
*/
|
|
1046
|
-
function integrationTmuxSocketRoot(home) {
|
|
1047
|
-
const uid = typeof process.getuid === "function" ? process.getuid() : 0;
|
|
1048
|
-
const homeDigest = createHash("sha256").update(resolve(home)).digest("hex").slice(0, 16);
|
|
1049
|
-
return join("/tmp", `yi-${uid.toString(36)}-${homeDigest}`);
|
|
1050
|
-
}
|
|
1051
1027
|
/**
|
|
1052
1028
|
* Reuse is enabled only for an immutable installed release. Development
|
|
1053
1029
|
* checkouts have no content-addressed release identity, so they return.
|
|
@@ -12,6 +12,7 @@ export function createIntegrationAttempt(input, now) {
|
|
|
12
12
|
source: input.source,
|
|
13
13
|
beforeCommit: input.beforeCommit,
|
|
14
14
|
checkCommands: normalizedUniqueText(input.checkCommands ?? [], "Integration check command"),
|
|
15
|
+
rerunChecks: input.rerunChecks ?? false,
|
|
15
16
|
status: "running",
|
|
16
17
|
createdAt: timestamp,
|
|
17
18
|
updatedAt: timestamp
|
|
@@ -143,6 +144,8 @@ export function validateIntegrationAttempt(attempt) {
|
|
|
143
144
|
requireCommit(attempt.beforeCommit, "Integration before commit");
|
|
144
145
|
validateIntegrationSource(attempt.taskId, attempt.source);
|
|
145
146
|
normalizedUniqueText(attempt.checkCommands, "Integration check command");
|
|
147
|
+
if (typeof attempt.rerunChecks !== "boolean")
|
|
148
|
+
throw new Error("Integration rerunChecks must be explicit.");
|
|
146
149
|
if (attempt.candidateCommit !== undefined) {
|
|
147
150
|
requireCommit(attempt.candidateCommit, "Integration candidate commit");
|
|
148
151
|
}
|
|
@@ -239,20 +242,6 @@ function validateIntegrationSource(taskId, source) {
|
|
|
239
242
|
}
|
|
240
243
|
return;
|
|
241
244
|
}
|
|
242
|
-
if (source.kind === "historical-change-sets") {
|
|
243
|
-
if (!Array.isArray(source.changeSetIds) || source.changeSetIds.length === 0) {
|
|
244
|
-
throw new Error("Historical Integration source requires ChangeSet ids.");
|
|
245
|
-
}
|
|
246
|
-
const seen = new Set();
|
|
247
|
-
for (const changeSetId of source.changeSetIds) {
|
|
248
|
-
validateTaskRecordReference({ taskId, localId: changeSetId }, "changeSet");
|
|
249
|
-
if (seen.has(changeSetId)) {
|
|
250
|
-
throw new Error(`Historical Integration ChangeSet is duplicated: ${changeSetId}.`);
|
|
251
|
-
}
|
|
252
|
-
seen.add(changeSetId);
|
|
253
|
-
}
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
245
|
throw new Error("Integration source is invalid.");
|
|
257
246
|
}
|
|
258
247
|
function normalizeConflictReport(report) {
|
|
@@ -33,9 +33,6 @@ export async function applyIntegrationSource(input) {
|
|
|
33
33
|
});
|
|
34
34
|
return pending;
|
|
35
35
|
};
|
|
36
|
-
if (source.kind === "historical-change-sets") {
|
|
37
|
-
throw new Error("Historical Integration source has no current application proof; preserve evidence and abort explicitly.");
|
|
38
|
-
}
|
|
39
36
|
if (source.kind === "work-item" && source.strategy === "manual") {
|
|
40
37
|
if (current.candidateCommit !== undefined) {
|
|
41
38
|
await assertIntegrationCandidate(path, current.candidateCommit);
|
|
@@ -61,15 +58,11 @@ export async function applyIntegrationSource(input) {
|
|
|
61
58
|
const branch = workspace.branch.startsWith("refs/") ? workspace.branch : `refs/heads/${workspace.branch}`;
|
|
62
59
|
const active = await activeGitOperation(path);
|
|
63
60
|
if (current.sourceProgress === undefined) {
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
// without a receipt are deliberately not inferred from a clean tree.
|
|
61
|
+
// Only a pristine candidate can establish this attempt's first receipt.
|
|
62
|
+
// An unfinished operation without that receipt belongs to explicit recovery.
|
|
67
63
|
const head = await line(path, "rev-parse", "HEAD");
|
|
68
64
|
if (active !== undefined) {
|
|
69
|
-
|
|
70
|
-
throw new Error("Historical cherry-pick has no durable completed-step cursor; preserve it and choose explicit recovery.");
|
|
71
|
-
}
|
|
72
|
-
await assertOperation(path, kind, commits[0], current.beforeCommit, branch, source);
|
|
65
|
+
throw new Error("Active Git operation has no attempt-owned progress receipt; preserve it and choose explicit recovery.");
|
|
73
66
|
}
|
|
74
67
|
else if (head !== current.beforeCommit || current.status === "conflicted") {
|
|
75
68
|
throw new Error("Integration source completion is unproven: no attempt-owned Git receipt. Preserve the candidate; do not replay.");
|
|
@@ -79,8 +72,7 @@ export async function applyIntegrationSource(input) {
|
|
|
79
72
|
}
|
|
80
73
|
save({ sourceProgress: {
|
|
81
74
|
workspace: resolve(path), branch, sourceDigest, completedSteps: 0,
|
|
82
|
-
head: current.beforeCommit
|
|
83
|
-
...(active === undefined ? {} : { activeAction: actionId() })
|
|
75
|
+
head: current.beforeCommit
|
|
84
76
|
} });
|
|
85
77
|
}
|
|
86
78
|
let progress = current.sourceProgress;
|
|
@@ -77,7 +77,7 @@ function isCommandPath(lowerPath, segments, lowerFile) {
|
|
|
77
77
|
|| lowerFile.startsWith("cli")
|
|
78
78
|
|| lowerPath.includes("/bin/");
|
|
79
79
|
}
|
|
80
|
-
function isContractPath(lowerPath,
|
|
80
|
+
function isContractPath(lowerPath, _segments, lowerFile) {
|
|
81
81
|
return lowerFile === "index.ts"
|
|
82
82
|
|| lowerFile === "index.js"
|
|
83
83
|
|| lowerFile === "index.mjs"
|
|
@@ -87,7 +87,7 @@ function isContractPath(lowerPath, segments, lowerFile) {
|
|
|
87
87
|
|| lowerPath.includes("/contract/")
|
|
88
88
|
|| lowerPath.includes("/contracts/");
|
|
89
89
|
}
|
|
90
|
-
function isTestPath(
|
|
90
|
+
function isTestPath(_lowerPath, segments, lowerFile) {
|
|
91
91
|
if (segments.includes("test") || segments.includes("tests") || segments.includes("__tests__")) {
|
|
92
92
|
return true;
|
|
93
93
|
}
|
package/dist/job/durableJob.js
CHANGED
|
@@ -16,27 +16,9 @@ export function isDurableJobTerminal(status) {
|
|
|
16
16
|
}
|
|
17
17
|
export function createDurableJob(input, now) {
|
|
18
18
|
const timestamp = now.toISOString();
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
projectId: input.projectId,
|
|
23
|
-
head: input.head,
|
|
24
|
-
steps: input.steps,
|
|
25
|
-
workspace: input.workspace,
|
|
26
|
-
env: input.env
|
|
27
|
-
})
|
|
28
|
-
: retryDurableJobIdempotencyKey(durableJobIdempotencyKey({
|
|
29
|
-
owner: input.owner,
|
|
30
|
-
projectId: input.projectId,
|
|
31
|
-
head: input.head,
|
|
32
|
-
steps: input.steps,
|
|
33
|
-
workspace: input.workspace,
|
|
34
|
-
env: input.env
|
|
35
|
-
}), input.retryOf);
|
|
36
|
-
const idempotencyKey = input.operation === undefined ? contentKey
|
|
37
|
-
: createHash("sha256").update(JSON.stringify([
|
|
38
|
-
input.operation.actorId, input.operation.requestId
|
|
39
|
-
])).digest("hex");
|
|
19
|
+
const idempotencyKey = createHash("sha256").update(JSON.stringify([
|
|
20
|
+
input.operation.actorId, input.operation.requestId
|
|
21
|
+
])).digest("hex");
|
|
40
22
|
return validateDurableJob({
|
|
41
23
|
schemaVersion: CURRENT_DURABLE_JOB_SCHEMA_VERSION,
|
|
42
24
|
id: input.id,
|
|
@@ -49,10 +31,7 @@ export function createDurableJob(input, now) {
|
|
|
49
31
|
steps: input.steps.map((step) => ({ ...step })),
|
|
50
32
|
idempotencyKey,
|
|
51
33
|
operation: {
|
|
52
|
-
|
|
53
|
-
inputDigest: input.operation?.inputDigest ?? idempotencyKey,
|
|
54
|
-
actorId: input.operation?.actorId ?? "internal:job",
|
|
55
|
-
authorityRef: input.operation?.authorityRef ?? "internal:job",
|
|
34
|
+
...input.operation,
|
|
56
35
|
targetId: input.workspace,
|
|
57
36
|
capability: "job.start",
|
|
58
37
|
implementation: JOB_RUNNER_IMPLEMENTATION,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
export class JobAssignmentScopeError extends Error {
|
|
3
|
+
}
|
|
4
|
+
/** Authentication identifies the current Session; it does not grant another
|
|
5
|
+
* Assignment's workspace. Share this check across admission, management and
|
|
6
|
+
* the last pre-spawn boundary. Existing running Jobs settle independently. */
|
|
7
|
+
export function assertJobAssignmentScope(store, target, caller) {
|
|
8
|
+
if (caller.scope !== "task" || caller.role === "leader")
|
|
9
|
+
return;
|
|
10
|
+
const run = caller.role === undefined ? null : store.getActiveRun(target.taskId, caller.role);
|
|
11
|
+
const workspace = run?.workspace ?? run?.effective.workspace;
|
|
12
|
+
const item = run?.workItemId === undefined ? null : store.getWorkItem(target.taskId, run.workItemId);
|
|
13
|
+
if (run?.status !== "active" || run.reviewRoundId !== undefined
|
|
14
|
+
|| target.owner.kind !== "work-item" || target.owner.workItemId !== run.workItemId
|
|
15
|
+
|| item?.status !== "open" || item.assignee !== caller.role
|
|
16
|
+
|| !item.writeProjectIds.includes(target.projectId)
|
|
17
|
+
|| !run.effective.writeProjectIds.includes(target.projectId)
|
|
18
|
+
|| workspace === undefined || resolve(workspace.root) !== resolve(target.workspace)
|
|
19
|
+
|| !workspace.entries.some(entry => entry.projectId === target.projectId && entry.access === "write")) {
|
|
20
|
+
throw new JobAssignmentScopeError("Job is outside the caller's current Assignment. Use the exact assigned WorkItem workspace and writable Project; this Job owner cannot represent a Review or replica Lane.");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** A read model over the one Job, never separately persisted.
|
|
2
|
+
* confirmed means the selected runner was observed, not that every action
|
|
3
|
+
* performed by an arbitrary command succeeded. Inspect original step receipts.
|
|
4
|
+
*/
|
|
5
|
+
export function inspectJobOperation(job) {
|
|
6
|
+
return {
|
|
7
|
+
operationRef: { taskId: job.taskId, jobId: job.id },
|
|
8
|
+
...job.operation,
|
|
9
|
+
state: job.status === "queued" ? "pending"
|
|
10
|
+
: job.status === "running" ? "running"
|
|
11
|
+
: job.status === "unknown-needs-attention" ? "unknown" : "finished",
|
|
12
|
+
outcome: job.result?.outcome,
|
|
13
|
+
result: job.result,
|
|
14
|
+
checkpoint: job.checkpoint
|
|
15
|
+
};
|
|
16
|
+
}
|
package/dist/job/jobRunner.js
CHANGED
|
@@ -11,6 +11,7 @@ import { spawn } from "node:child_process";
|
|
|
11
11
|
import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, utimesSync, writeFileSync } from "node:fs";
|
|
12
12
|
import { join, resolve } from "node:path";
|
|
13
13
|
import { fileURLToPath } from "node:url";
|
|
14
|
+
import { jobStepDirectory } from "./stepDirectory.js";
|
|
14
15
|
import { writeTextFileAtomically } from "../storage/durableFile.js";
|
|
15
16
|
import { readLinuxProcessStartIdentity } from "../controller/domainIdentity.js";
|
|
16
17
|
const DEFAULT_STEP_TIMEOUT_MS = 30 * 60_000;
|
|
@@ -129,7 +130,7 @@ export async function runDurableJobRunner(specPath) {
|
|
|
129
130
|
// Issue 08: a structured argv step executes without a shell so its
|
|
130
131
|
// tokens can never be reinterpreted; a legacy command step keeps the
|
|
131
132
|
// shell form. A per-step cwd/env override applies to either form.
|
|
132
|
-
const stepCwd = step.cwd
|
|
133
|
+
const stepCwd = jobStepDirectory(spec.workspace, step.cwd);
|
|
133
134
|
const stepEnv = step.env === undefined
|
|
134
135
|
? spec.env
|
|
135
136
|
: { ...spec.env, ...step.env };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { realpathSync } from "node:fs";
|
|
2
|
+
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
3
|
+
/** Recheck at execution, including directories created by a bootstrap step. */
|
|
4
|
+
export function jobStepDirectory(workspace, override) {
|
|
5
|
+
const root = realpathSync(workspace);
|
|
6
|
+
const cwd = override ?? workspace;
|
|
7
|
+
if (!isAbsolute(cwd))
|
|
8
|
+
throw new Error("Job step cwd must be absolute.");
|
|
9
|
+
const actual = realpathSync(cwd);
|
|
10
|
+
const nested = relative(root, actual);
|
|
11
|
+
if (nested === ".." || nested.startsWith(`..${sep}`) || isAbsolute(nested)) {
|
|
12
|
+
throw new Error(`Job step cwd is outside its managed workspace: ${resolve(cwd)}.`);
|
|
13
|
+
}
|
|
14
|
+
return actual;
|
|
15
|
+
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { listArtifactsCapability, readArtifactCapability, saveArtifactCapability } from "../artifacts/artifactCapability.js";
|
|
2
2
|
import { runConfigCommand } from "../commands/configCommands.js";
|
|
3
|
-
import {
|
|
3
|
+
import { sendTaskMessageCommand, updateTaskMetadataCommand } from "../commands/taskCommands.js";
|
|
4
4
|
import { CONFIG_DOMAINS } from "../config/configCatalog.js";
|
|
5
|
+
import { inspectTaskContext, readTaskContext, readTaskContextDelta, withContextObservations } from "../context/taskContext.js";
|
|
5
6
|
import { createJobCallAuthority, parseDurableJobStartParams } from "../controller/jobControl.js";
|
|
6
|
-
import { inspectJobOperation } from "
|
|
7
|
-
import {
|
|
8
|
-
import { createProjectResources } from "../resources/projectResourceService.js";
|
|
9
|
-
import { saveArtifactCapability, readArtifactCapability, listArtifactsCapability } from "../artifacts/artifactCapability.js";
|
|
7
|
+
import { inspectJobOperation } from "../job/jobOperation.js";
|
|
8
|
+
import { assertJobAssignmentScope } from "../job/jobAssignmentScope.js";
|
|
10
9
|
import { createPluginService } from "../plugins/pluginService.js";
|
|
10
|
+
import { createProjectResources } from "../resources/projectResourceService.js";
|
|
11
|
+
import { CapabilityRegistry, } from "./capabilityRegistry.js";
|
|
11
12
|
const text = { type: "string", minLength: 1 };
|
|
12
13
|
const strings = { type: "object", additionalProperties: { type: "string" } };
|
|
13
14
|
const object = (properties, required = Object.keys(properties)) => ({
|
|
@@ -26,7 +27,6 @@ const definitions = [
|
|
|
26
27
|
effect: "local-mutation", requiredPermissions: ["task:read"], source: "sendTaskMessageCommand",
|
|
27
28
|
inputSchema: object({ taskId: text, body: text,
|
|
28
29
|
intent: { enum: ["record", "discuss", "develop"] },
|
|
29
|
-
wakePolicy: { enum: ["leader", "none"] },
|
|
30
30
|
recipient: object({ roleName: text, workItemId: text, reviewRoundId: text }, ["roleName"])
|
|
31
31
|
}, ["taskId", "body"]), outputSchema: recordOutput
|
|
32
32
|
},
|
|
@@ -170,7 +170,8 @@ const definitions = [
|
|
|
170
170
|
] },
|
|
171
171
|
env: strings,
|
|
172
172
|
steps: { type: "array", minItems: 1, items: object({
|
|
173
|
-
name: text, command: text, timeoutMs: { type: "integer" }
|
|
173
|
+
name: text, command: text, timeoutMs: { type: "integer" },
|
|
174
|
+
argv: { type: "array", minItems: 1, items: text }, cwd: text, env: strings
|
|
174
175
|
}, ["name", "command"]) },
|
|
175
176
|
retryOf: text
|
|
176
177
|
}, ["taskId", "projectId", "head", "workspace", "owner", "env", "steps"]),
|
|
@@ -247,7 +248,7 @@ export function createBuiltinCapabilities(host, store, jobs, signal = () => unde
|
|
|
247
248
|
// gains develop from an intent argument; only a global Operator Session
|
|
248
249
|
// (user-authority) may carry one. sendTaskMessageCommand enforces this,
|
|
249
250
|
// so the capability just forwards the optional intent unchanged.
|
|
250
|
-
const result = sendTaskMessageCommand(store, taskId, params.body,
|
|
251
|
+
const result = sendTaskMessageCommand(store, taskId, params.body, { environment: callerEnvironment(caller) }, params.recipient, params.intent);
|
|
251
252
|
signal(taskId);
|
|
252
253
|
return result.message;
|
|
253
254
|
}
|
|
@@ -338,11 +339,11 @@ export function createBuiltinCapabilities(host, store, jobs, signal = () => unde
|
|
|
338
339
|
...(patch.tags?.length === 0 ? { tags: null } : {})
|
|
339
340
|
}, {
|
|
340
341
|
environment: callerEnvironment(caller),
|
|
341
|
-
runtime: {
|
|
342
|
+
runtime: { notifyMailboxChanged: () => signal(taskId), reconcileTask: signal }
|
|
342
343
|
});
|
|
343
344
|
}
|
|
344
345
|
if (name === "job.get") {
|
|
345
|
-
const job = jobs.getJob(taskId, params.jobId);
|
|
346
|
+
const job = jobs.getJob(taskId, params.jobId, caller);
|
|
346
347
|
if (!job)
|
|
347
348
|
throw new Error(`Job not found: ${taskId}/${params.jobId}.`);
|
|
348
349
|
const operation = inspectJobOperation(job);
|
|
@@ -379,6 +380,9 @@ export function createBuiltinCapabilities(host, store, jobs, signal = () => unde
|
|
|
379
380
|
if (sessions?.sessions[sessions.activeAgentId]?.effective.executionAuthority !== "delivery") {
|
|
380
381
|
throw new Error("Planning Sessions cannot start delivery Jobs.");
|
|
381
382
|
}
|
|
383
|
+
if (descriptor?.name === "job.start" && input !== undefined) {
|
|
384
|
+
assertJobAssignmentScope(store, input, caller);
|
|
385
|
+
}
|
|
382
386
|
}
|
|
383
387
|
if (permission === "task:read" || permission === "job:start")
|
|
384
388
|
continue;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createDurableJobControl } from "../controller/jobControl.js";
|
|
2
2
|
import { JOB_RUNNER_IMPLEMENTATION } from "../job/durableJob.js";
|
|
3
|
-
import { InstanceHost } from "./instanceHost.js";
|
|
4
3
|
import { createBuiltinCapabilities } from "./builtinCapabilities.js";
|
|
4
|
+
import { InstanceHost } from "./instanceHost.js";
|
|
5
5
|
/** Called once by the existing Controller root. Does not open a Store, start
|
|
6
6
|
* another Controller, or provide arbitrary persistence to plugin code.
|
|
7
7
|
* T02 registers contributions on this Host and wraps this same Job control.
|
|
@@ -29,19 +29,3 @@ export function createKernelPorts(store, runner, signal = () => undefined, conte
|
|
|
29
29
|
}
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
|
-
/** A read model over the one Job, never separately persisted.
|
|
33
|
-
* confirmed means the selected runner was observed, not that every action
|
|
34
|
-
* performed by an arbitrary command succeeded. Inspect original step receipts.
|
|
35
|
-
*/
|
|
36
|
-
export function inspectJobOperation(job) {
|
|
37
|
-
return {
|
|
38
|
-
operationRef: { taskId: job.taskId, jobId: job.id },
|
|
39
|
-
...job.operation,
|
|
40
|
-
state: job.status === "queued" ? "pending"
|
|
41
|
-
: job.status === "running" ? "running"
|
|
42
|
-
: job.status === "unknown-needs-attention" ? "unknown" : "finished",
|
|
43
|
-
outcome: job.result?.outcome,
|
|
44
|
-
result: job.result,
|
|
45
|
-
checkpoint: job.checkpoint
|
|
46
|
-
};
|
|
47
|
-
}
|
|
@@ -8,7 +8,6 @@ import { boundedRunFailureDiagnostic } from "../domain/agentResultTransport.js";
|
|
|
8
8
|
import { updateExecutionLane, updateWorkItemExecutionLane } from "../execution/workItemExecution.js";
|
|
9
9
|
import { managedProviderTurnId } from "../runtime/providerRuntimeIdentity.js";
|
|
10
10
|
import { latestRunDurableProgressAt } from "../scheduler/roleRunStall.js";
|
|
11
|
-
import { markTaskWakeConsumed } from "../scheduler/taskWake.js";
|
|
12
11
|
import { workItemExecutionGroupById, updateWorkItemExecutionGroup } from "../workItem/workItem.js";
|
|
13
12
|
/**
|
|
14
13
|
* Validate every immutable identity and frozen Project head needed before a
|
|
@@ -81,7 +80,7 @@ export function validateExactRunReviewRound(store, run, options = {}) {
|
|
|
81
80
|
}
|
|
82
81
|
}
|
|
83
82
|
const task = store.getTask(run.taskId);
|
|
84
|
-
const taskScope =
|
|
83
|
+
const taskScope = round.scope === "task";
|
|
85
84
|
const item = taskScope || round.workItemId === undefined
|
|
86
85
|
? null
|
|
87
86
|
: store.getWorkItem(run.taskId, round.workItemId);
|
|
@@ -410,13 +409,6 @@ export function terminalizeExactTaskRun(store, input, now) {
|
|
|
410
409
|
roleName: terminal.roleName
|
|
411
410
|
}), dispatchIdentity);
|
|
412
411
|
settleRoleRunDispatch(store, dispatchIdentity, dispatchToken);
|
|
413
|
-
if (terminal.roleName === "leader") {
|
|
414
|
-
const wake = store.listTaskWakes(input.taskId)
|
|
415
|
-
.find((candidate) => candidate.runId === terminal.id && candidate.status === "dispatched");
|
|
416
|
-
if (wake !== undefined) {
|
|
417
|
-
store.saveTaskWake(input.taskId, markTaskWakeConsumed(wake, now));
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
412
|
if (terminal.executionGroupId !== undefined && terminal.executionLaneId !== undefined) {
|
|
421
413
|
store.clearActiveExecutionLaneRun(input.taskId, terminal.executionGroupId, terminal.executionLaneId);
|
|
422
414
|
}
|
|
@@ -36,9 +36,8 @@ function resolveInputControl(sessions, kind, expectedTarget, roleName) {
|
|
|
36
36
|
detail: `Agent plan '${active.adapterId}' has no native interrupt; Yui will not `
|
|
37
37
|
+ "stop the owned process, kill, restart, or detach it." };
|
|
38
38
|
}
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
const binding = "providerBinding" in sessions ? sessions.providerBinding ?? null : null;
|
|
39
|
+
// Both scopes use the same explicit null for an uncontrolled Session.
|
|
40
|
+
const binding = sessions.providerBinding;
|
|
42
41
|
const turn = binding?.run ?? null;
|
|
43
42
|
if (binding === null || turn === null || !PRESENT_TURN_STATUSES.has(turn.status)) {
|
|
44
43
|
return { outcome: "no-active-turn", code: "NO_ACTIVE_TURN",
|
|
@@ -78,7 +77,7 @@ function resolveInputControl(sessions, kind, expectedTarget, roleName) {
|
|
|
78
77
|
...(turn.nativeTurnId === undefined ? {} : { nativeTurnId: turn.nativeTurnId }),
|
|
79
78
|
attemptId: turn.attemptId,
|
|
80
79
|
authority: { epoch: binding.authority.epoch, owner: "controller",
|
|
81
|
-
holderId: binding.authority.holderId
|
|
80
|
+
holderId: binding.authority.holderId }
|
|
82
81
|
} };
|
|
83
82
|
}
|
|
84
83
|
/**
|