@dsh-enhanced/plugin-control-plane 0.1.65 → 0.1.66
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/README.md +81 -5
- package/lib/attestation.d.ts.map +1 -1
- package/lib/attestation.js +33 -1
- package/lib/attestation.js.map +1 -1
- package/lib/creation-capability-empty-handoff.d.ts +57 -0
- package/lib/creation-capability-empty-handoff.d.ts.map +1 -0
- package/lib/creation-capability-empty-handoff.js +147 -0
- package/lib/creation-capability-empty-handoff.js.map +1 -0
- package/lib/creation-capability-execution.d.ts +9 -0
- package/lib/creation-capability-execution.d.ts.map +1 -0
- package/lib/creation-capability-execution.js +70 -0
- package/lib/creation-capability-execution.js.map +1 -0
- package/lib/creation-capability-feedback.d.ts +58 -1
- package/lib/creation-capability-feedback.d.ts.map +1 -1
- package/lib/creation-capability-feedback.js +532 -50
- package/lib/creation-capability-feedback.js.map +1 -1
- package/lib/creation-capability-handoff-common.d.ts +60 -0
- package/lib/creation-capability-handoff-common.d.ts.map +1 -0
- package/lib/creation-capability-handoff-common.js +195 -0
- package/lib/creation-capability-handoff-common.js.map +1 -0
- package/lib/creation-capability-journal.d.ts +19 -0
- package/lib/creation-capability-journal.d.ts.map +1 -1
- package/lib/creation-capability-journal.js +62 -21
- package/lib/creation-capability-journal.js.map +1 -1
- package/lib/creation-capability-runtime.d.ts +18 -0
- package/lib/creation-capability-runtime.d.ts.map +1 -1
- package/lib/creation-capability-runtime.js +76 -18
- package/lib/creation-capability-runtime.js.map +1 -1
- package/lib/creation-capability-sealed-audit.d.ts +19 -0
- package/lib/creation-capability-sealed-audit.d.ts.map +1 -0
- package/lib/creation-capability-sealed-audit.js +114 -0
- package/lib/creation-capability-sealed-audit.js.map +1 -0
- package/lib/creation-capability-sealed-handoff.d.ts +160 -0
- package/lib/creation-capability-sealed-handoff.d.ts.map +1 -0
- package/lib/creation-capability-sealed-handoff.js +695 -0
- package/lib/creation-capability-sealed-handoff.js.map +1 -0
- package/lib/creation-capability-trend-snapshot.d.ts +70 -0
- package/lib/creation-capability-trend-snapshot.d.ts.map +1 -0
- package/lib/creation-capability-trend-snapshot.js +117 -0
- package/lib/creation-capability-trend-snapshot.js.map +1 -0
- package/lib/creation-capability-types.d.ts +254 -4
- package/lib/creation-capability-types.d.ts.map +1 -1
- package/lib/in-process-reload.d.ts +191 -0
- package/lib/in-process-reload.d.ts.map +1 -0
- package/lib/in-process-reload.js +284 -0
- package/lib/in-process-reload.js.map +1 -0
- package/lib/index.d.ts +12 -4
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +8 -2
- package/lib/index.js.map +1 -1
- package/lib/linux-source-build.d.ts +16 -0
- package/lib/linux-source-build.d.ts.map +1 -0
- package/lib/linux-source-build.js +268 -0
- package/lib/linux-source-build.js.map +1 -0
- package/lib/linux-source-preflight.d.ts +31 -0
- package/lib/linux-source-preflight.d.ts.map +1 -0
- package/lib/linux-source-preflight.js +101 -0
- package/lib/linux-source-preflight.js.map +1 -0
- package/lib/live-qualification-runtime.d.ts +6 -2
- package/lib/live-qualification-runtime.d.ts.map +1 -1
- package/lib/live-qualification-runtime.js +65 -11
- package/lib/live-qualification-runtime.js.map +1 -1
- package/lib/owner-task-gap-types.d.ts +23 -0
- package/lib/owner-task-gap-types.d.ts.map +1 -1
- package/lib/owner-task-gaps.d.ts +4 -0
- package/lib/owner-task-gaps.d.ts.map +1 -1
- package/lib/owner-task-gaps.js +35 -1
- package/lib/owner-task-gaps.js.map +1 -1
- package/lib/scanner-owner-runtime.d.ts +34 -0
- package/lib/scanner-owner-runtime.d.ts.map +1 -0
- package/lib/scanner-owner-runtime.js +99 -0
- package/lib/scanner-owner-runtime.js.map +1 -0
- package/lib/service.d.ts +82 -5
- package/lib/service.d.ts.map +1 -1
- package/lib/service.js +450 -45
- package/lib/service.js.map +1 -1
- package/lib/source-approval-authority.d.ts.map +1 -1
- package/lib/source-approval-authority.js +25 -7
- package/lib/source-approval-authority.js.map +1 -1
- package/lib/source-build-diagnostics.d.ts +31 -0
- package/lib/source-build-diagnostics.d.ts.map +1 -0
- package/lib/source-build-diagnostics.js +110 -0
- package/lib/source-build-diagnostics.js.map +1 -0
- package/lib/source-build.d.ts +25 -2
- package/lib/source-build.d.ts.map +1 -1
- package/lib/source-build.js +145 -26
- package/lib/source-build.js.map +1 -1
- package/lib/source-creation.d.ts +6 -2
- package/lib/source-creation.d.ts.map +1 -1
- package/lib/source-creation.js +11 -2
- package/lib/source-creation.js.map +1 -1
- package/lib/source-job-types.d.ts +3 -1
- package/lib/source-job-types.d.ts.map +1 -1
- package/lib/source-jobs.d.ts +28 -1
- package/lib/source-jobs.d.ts.map +1 -1
- package/lib/source-jobs.js +117 -26
- package/lib/source-jobs.js.map +1 -1
- package/lib/sqlite.d.ts +1 -1
- package/lib/sqlite.d.ts.map +1 -1
- package/lib/sqlite.js +36 -1
- package/lib/sqlite.js.map +1 -1
- package/lib/store.d.ts +21 -1
- package/lib/store.d.ts.map +1 -1
- package/lib/store.js +470 -174
- package/lib/store.js.map +1 -1
- package/lib/systemd-host-authority.d.ts.map +1 -1
- package/lib/systemd-host-authority.js +4 -0
- package/lib/systemd-host-authority.js.map +1 -1
- package/lib/task-observation-runtime.d.ts +6 -2
- package/lib/task-observation-runtime.d.ts.map +1 -1
- package/lib/task-observation-runtime.js +69 -11
- package/lib/task-observation-runtime.js.map +1 -1
- package/lib/types.d.ts +47 -1
- package/lib/types.d.ts.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/lib/work-item-creation-contract.d.ts +17 -0
- package/lib/work-item-creation-contract.d.ts.map +1 -0
- package/lib/work-item-creation-contract.js +61 -0
- package/lib/work-item-creation-contract.js.map +1 -0
- package/package.json +10 -10
package/lib/service.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
+
import { cleanedLinuxSourceBuildFailure, cleanedLinuxSourcePreflight, consumeCleanedLinuxSourceBuildFailure, consumeCleanedLinuxSourcePreflight, consumeLinuxSourceBuildFailed, consumeLinuxSourceNotStarted, isLinuxSourceBuildFailed } from './linux-source-preflight.js';
|
|
1
2
|
import { AdoptionCoordinatorRuntime, validateAdoptionCoordinatorConfig } from './adoption-coordinator.js';
|
|
2
|
-
import { CreationCapabilityJournal, creationCapabilityPublicKey, validateCreationCapabilityConfig } from './creation-capability-journal.js';
|
|
3
|
+
import { CreationCapabilityJournal, creationCapabilityAuthorityDigest, creationCapabilityPublicKey, validateCreationCapabilityConfig } from './creation-capability-journal.js';
|
|
3
4
|
import { CreationCapabilityRuntime } from './creation-capability-runtime.js';
|
|
4
|
-
import { inspectCreationCapabilityTaskAssociations } from './creation-capability-feedback.js';
|
|
5
|
+
import { inspectCreationCapabilityAvailabilityAttribution, inspectCreationCapabilityOrdinaryUseReport, inspectCreationCapabilityOrdinaryUseTrend, inspectCreationCapabilityTaskAssociations } from './creation-capability-feedback.js';
|
|
5
6
|
import { captureCreationCapabilitySource } from './creation-capability-source.js';
|
|
6
7
|
import { lstat, readFile, realpath } from 'node:fs/promises';
|
|
7
8
|
import { realpathSync } from 'node:fs';
|
|
8
9
|
import { basename, dirname, isAbsolute, join, resolve } from 'node:path';
|
|
9
10
|
import { createHash, createPublicKey, randomBytes } from 'node:crypto';
|
|
10
|
-
import { isSourceOwnerContinuation, sourceGrowthRunDigest, validateCreationAcceptanceAuthorityRef, verifyPluginCreationVerificationCertificate, validateRevisionAcceptanceAuthorityRef, verifyPluginRevisionVerificationCertificate, validateRevisionRegressionAcceptanceAuthorityRef, verifyPluginRevisionRegressionCertificate
|
|
11
|
+
import { isSourceOwnerContinuation, anySourceGrowthRunDigest as sourceGrowthRunDigest, validateAnySourceGrowthRunBinding as validateSourceGrowthRunBinding, validateWorkItemGrowthAuthorityPins, validateCreationAcceptanceAuthorityRef, verifyPluginCreationVerificationCertificate, validateRevisionAcceptanceAuthorityRef, verifyPluginRevisionVerificationCertificate, validateRevisionRegressionAcceptanceAuthorityRef, verifyPluginRevisionRegressionCertificate } from '@dsh-enhanced/assistant-growth-contract';
|
|
11
12
|
import { TaskObservationRuntime, validateTaskObservationConfig } from './task-observation-runtime.js';
|
|
12
13
|
import { LiveQualificationRuntime, validateLiveQualificationConfig } from './live-qualification-runtime.js';
|
|
13
14
|
import { rollbackPluginWatch } from './cli.js';
|
|
15
|
+
import { assertWorkItemRunReference, inertCreationData, WORK_ITEM_ENGINEERING_PROTOCOL } from './work-item-creation-contract.js';
|
|
14
16
|
import { OwnerTaskFailureGaps } from './owner-task-gaps.js';
|
|
15
17
|
import { Context, Service } from '@deepseek-ai/cordis';
|
|
16
18
|
import Schema from '@deepseek-ai/schemastery';
|
|
@@ -27,7 +29,8 @@ import { Ed25519ApprovalAuthority } from './approval.js';
|
|
|
27
29
|
import { ControlPlaneStore, MODIFY_GENERATOR_DIGEST, controlPlaneDigest } from './store.js';
|
|
28
30
|
import { runDockerPreparedChecks, validateSourceBuildConfig } from './source-build.js';
|
|
29
31
|
import { runNativePreparedChecks } from './native-source-build.js';
|
|
30
|
-
import {
|
|
32
|
+
import { LINUX_PREPARED_SOURCE_BUILD_SCRIPT, LINUX_SOURCE_PACK_PATH, runLinuxPreparedChecks } from './linux-source-build.js';
|
|
33
|
+
import { LinuxRepositoryRunner, NativeMacVerifierRunner, linuxRuntimeDigest, verifyLinuxRuntime } from '@dsh-enhanced/assistant-isolation';
|
|
31
34
|
import { NATIVE_SOURCE_COMPILER } from './native-source-build-worker.js';
|
|
32
35
|
import { awaitSourceSignal, inspectSourceContext, inspectSourceTargetsContext } from './source-context.js';
|
|
33
36
|
import { inspectSourceRevisionContext, prepareRevisedPluginWorkspace, verifyRevisedPluginWorkspace } from './source-revision.js';
|
|
@@ -35,7 +38,7 @@ import { inspectSourceCreationContext, prepareCreatedPluginWorkspace, validateSo
|
|
|
35
38
|
import { resolveSourceBaseline } from './source-baseline.js';
|
|
36
39
|
import { inheritedEnvironment, loadTrustConfig, resolveTrustKey } from './trust.js';
|
|
37
40
|
import { registerPluginControlTools } from './tools.js';
|
|
38
|
-
import { SourceGrowthRunUnavailableError, SourceJobRuntime,
|
|
41
|
+
import { SourceGrowthRunUnavailableError, SourceJobRuntime, normalizeSourceJobsConfig } from './source-jobs.js';
|
|
39
42
|
import { installRuntimeObserver, validateRuntimeObserverConfig } from './runtime-observer.js';
|
|
40
43
|
import { installReplayEndpoint, validateReplayEndpointConfig } from './replay-endpoint.js';
|
|
41
44
|
import { readPrivateRuntimeObserverKey } from './runtime-observer-protocol.js';
|
|
@@ -136,13 +139,21 @@ export function normalizeControlPlaneConfig(input) {
|
|
|
136
139
|
validateSourceApprovalClientConfig(config.sourceApprovals);
|
|
137
140
|
if (config.sourceBuild !== undefined)
|
|
138
141
|
validateSourceBuildConfig(config.sourceBuild);
|
|
139
|
-
if (config.sourceBuild !== undefined && 'backend' in config.sourceBuild) {
|
|
142
|
+
if (config.sourceBuild !== undefined && 'backend' in config.sourceBuild && config.sourceBuild.backend === 'macos-seatbelt') {
|
|
140
143
|
if (!config.sourceJobs?.allowedModes || config.sourceJobs.allowedModes.some(mode => mode === 'modify')
|
|
141
144
|
|| config.sourceReleases !== undefined || config.sourceReleaseExecution !== undefined
|
|
142
145
|
|| config.sourceAdoptions !== undefined || config.sourceApprovals !== undefined) {
|
|
143
146
|
throw new Error('plugin-control-plane: native source build only supports Host-scaffolded create/revise-created');
|
|
144
147
|
}
|
|
145
148
|
}
|
|
149
|
+
if (config.sourceBuild !== undefined && 'backend' in config.sourceBuild && config.sourceBuild.backend === 'linux-native') {
|
|
150
|
+
if (!config.sourceJobs)
|
|
151
|
+
throw new Error('plugin-control-plane: Linux repository preparation requires durable sourceJobs');
|
|
152
|
+
if ((config.creationVerifications || config.creationCapabilities || config.revisionVerifications || config.revisionRegressions)
|
|
153
|
+
&& (!config.sourceBuild.userNamespace || !config.sourceBuild.systemSnapshot)) {
|
|
154
|
+
throw new Error('plugin-control-plane: Linux independent verification requires the dual-UID repository boundary');
|
|
155
|
+
}
|
|
156
|
+
}
|
|
146
157
|
if (config.sourceReleases !== undefined) {
|
|
147
158
|
validateSourceReleaseClientConfig(config.sourceReleases);
|
|
148
159
|
if (!config.sourceApprovals || config.sourceBuild?.versioning !== 'patch')
|
|
@@ -168,7 +179,7 @@ export function normalizeControlPlaneConfig(input) {
|
|
|
168
179
|
}
|
|
169
180
|
}
|
|
170
181
|
if (config.sourceJobs !== undefined) {
|
|
171
|
-
|
|
182
|
+
config.sourceJobs = normalizeSourceJobsConfig(config.sourceJobs, config.sourceBuild);
|
|
172
183
|
if (realpathSync(config.sourceJobs.repository) !== config.sourceJobs.repository)
|
|
173
184
|
throw new Error('plugin-control-plane: sourceJobs.repository must be canonical');
|
|
174
185
|
}
|
|
@@ -227,9 +238,19 @@ export function normalizeControlPlaneConfig(input) {
|
|
|
227
238
|
}
|
|
228
239
|
if (config.creationCapabilities !== undefined) {
|
|
229
240
|
validateCreationCapabilityConfig(config.creationCapabilities);
|
|
241
|
+
if (config.creationCapabilities.workItemFailures === false) {
|
|
242
|
+
config.creationCapabilities = { ...config.creationCapabilities };
|
|
243
|
+
delete config.creationCapabilities.workItemFailures;
|
|
244
|
+
}
|
|
230
245
|
const capability = config.creationCapabilities;
|
|
231
246
|
const verification = config.creationVerifications;
|
|
232
247
|
const jobs = config.sourceJobs;
|
|
248
|
+
const build = config.sourceBuild;
|
|
249
|
+
const buildBackend = build && 'backend' in build ? build.backend : 'docker';
|
|
250
|
+
const adoptionBackend = 'backend' in capability.runner ? capability.runner.backend : 'docker';
|
|
251
|
+
if (!build || buildBackend !== adoptionBackend) {
|
|
252
|
+
throw new Error('plugin-control-plane: creation build, verification and adoption require matching execution backends');
|
|
253
|
+
}
|
|
233
254
|
if (!verification || !jobs?.creation || capability.namePrefix !== jobs.creation.namePrefix
|
|
234
255
|
|| capability.owner.authorityId !== jobs.ownerRouteId || capability.owner.principalId !== jobs.principalId
|
|
235
256
|
|| capability.owner.workspace !== jobs.workspace || capability.owner.agentPreset !== jobs.preset
|
|
@@ -269,6 +290,7 @@ export class PluginControlPlaneService extends Service {
|
|
|
269
290
|
creationCapabilityReconcileRequested = false;
|
|
270
291
|
growthRunProducer;
|
|
271
292
|
growthProviderDraining = false;
|
|
293
|
+
workItemCreationFrame;
|
|
272
294
|
constructor(ctx, input) {
|
|
273
295
|
super(ctx, 'pluginControlPlane');
|
|
274
296
|
this.config = normalizeControlPlaneConfig(input);
|
|
@@ -437,9 +459,14 @@ export class PluginControlPlaneService extends Service {
|
|
|
437
459
|
},
|
|
438
460
|
delivery: {
|
|
439
461
|
validateOwnerRoute: input => delivery().validateOwnerRoute(input),
|
|
462
|
+
inspectOwnerRouteState: input => delivery().inspectOwnerRouteState(input),
|
|
440
463
|
inspectOwnerForegroundLearningTask: input => delivery().inspectOwnerForegroundLearningTask(input),
|
|
441
464
|
},
|
|
442
465
|
automations: {
|
|
466
|
+
inspectSystemOwnedRetirement: input => automations().inspectSystemOwnedRetirement(input),
|
|
467
|
+
onSchedulerWake: listener => automations().onSchedulerWake(listener),
|
|
468
|
+
retireSystemOwned: input => automations().retireSystemOwned(input),
|
|
469
|
+
inspectSystemOwned: input => automations().inspectSystemOwned(input),
|
|
443
470
|
registerHostExecutor: input => automations().registerHostExecutor(input),
|
|
444
471
|
reconcileSystem: input => automations().reconcileSystem(input),
|
|
445
472
|
inspectSystemOwnedActivation: input => automations().inspectSystemOwnedActivation(input),
|
|
@@ -495,9 +522,14 @@ export class PluginControlPlaneService extends Service {
|
|
|
495
522
|
},
|
|
496
523
|
delivery: {
|
|
497
524
|
validateOwnerRoute: input => delivery().validateOwnerRoute(input),
|
|
525
|
+
inspectOwnerRouteState: input => delivery().inspectOwnerRouteState(input),
|
|
498
526
|
inspectOwnerForegroundLearningTask: input => delivery().inspectOwnerForegroundLearningTask(input),
|
|
499
527
|
},
|
|
500
528
|
automations: {
|
|
529
|
+
inspectSystemOwnedRetirement: input => automations().inspectSystemOwnedRetirement(input),
|
|
530
|
+
onSchedulerWake: listener => automations().onSchedulerWake(listener),
|
|
531
|
+
retireSystemOwned: input => automations().retireSystemOwned(input),
|
|
532
|
+
inspectSystemOwned: input => automations().inspectSystemOwned(input),
|
|
501
533
|
registerHostExecutor: input => automations().registerHostExecutor(input),
|
|
502
534
|
reconcileSystem: input => automations().reconcileSystem(input),
|
|
503
535
|
inspectSystemOwnedActivation: input => automations().inspectSystemOwnedActivation(input),
|
|
@@ -558,10 +590,14 @@ export class PluginControlPlaneService extends Service {
|
|
|
558
590
|
if (Object.keys(owner).some(key => prepared.reference.owner[key] !== owner[key])) {
|
|
559
591
|
throw new Error('created capability owner differs from the finite grant');
|
|
560
592
|
}
|
|
561
|
-
return { certificate, artifact: prepared.artifact, owner };
|
|
593
|
+
return { certificate, artifact: prepared.artifact, owner, ...(prepared.protocol === 'dsh-prepared-creation/v2' ? { sourceRun: prepared.plan.creation.growthRun } : {}) };
|
|
562
594
|
},
|
|
563
595
|
recheck: async (planId, signal) => {
|
|
564
596
|
await this.inspectPreparedCreationReviewContext(planId, signal);
|
|
597
|
+
if (this.config.sourceBuild && 'backend' in this.config.sourceBuild
|
|
598
|
+
&& this.config.sourceBuild.backend === 'linux-native') {
|
|
599
|
+
await this.inspectPreparedCreationLinuxBuild(planId, signal);
|
|
600
|
+
}
|
|
565
601
|
if (!this.inspectVerifiedCreation(planId))
|
|
566
602
|
throw new Error('created capability independent certificate changed');
|
|
567
603
|
},
|
|
@@ -584,7 +620,7 @@ export class PluginControlPlaneService extends Service {
|
|
|
584
620
|
return source;
|
|
585
621
|
},
|
|
586
622
|
inspectRetained: record => this.withRetainedCreatedCapabilityCurrent(record, () => ({
|
|
587
|
-
certificate: record.certificate, artifact: record.artifact, owner: config.owner
|
|
623
|
+
certificate: record.certificate, artifact: record.artifact, owner: config.owner, ...(this.store.getRetainedPreparedCreation(record.planId).plan.creation?.growthRun?.protocol === 'assistant-growth/source-run/v2' ? { sourceRun: this.store.getRetainedPreparedCreation(record.planId).plan.creation.growthRun } : {})
|
|
588
624
|
})),
|
|
589
625
|
recheckRetained: async (record, signal) => {
|
|
590
626
|
signal.throwIfAborted();
|
|
@@ -613,9 +649,6 @@ export class PluginControlPlaneService extends Service {
|
|
|
613
649
|
},
|
|
614
650
|
} });
|
|
615
651
|
this.creationCapabilityRuntimes.add(capabilities);
|
|
616
|
-
await capabilities.start();
|
|
617
|
-
this.abort.signal.throwIfAborted();
|
|
618
|
-
this.creationCapabilityRuntime = capabilities;
|
|
619
652
|
unsubscribeTaskChanges = evaluation.onTrustedTaskChange(() => this.reconcileCreatedCapabilities());
|
|
620
653
|
}
|
|
621
654
|
catch (error) {
|
|
@@ -632,11 +665,24 @@ export class PluginControlPlaneService extends Service {
|
|
|
632
665
|
const sourceBuild = this.config.sourceBuild;
|
|
633
666
|
let nativeRunner;
|
|
634
667
|
try {
|
|
635
|
-
nativeRunner = 'backend' in sourceBuild ?
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
668
|
+
nativeRunner = !('backend' in sourceBuild) ? undefined : sourceBuild.backend === 'macos-seatbelt'
|
|
669
|
+
? { backend: 'macos-seatbelt', runner: new NativeMacVerifierRunner({ backend: 'macos-seatbelt',
|
|
670
|
+
stateRoot: sourceBuild.stateRoot, authorityDigest: sourceBuild.authorityDigest,
|
|
671
|
+
runtime: sourceBuild.runtime, source: NATIVE_SOURCE_COMPILER, expiresAt: sourceBuild.expiresAt,
|
|
672
|
+
maxRuns: sourceBuild.maxRuns, maxTotalDurationMs: sourceBuild.maxTotalDurationMs,
|
|
673
|
+
maxDurationMs: sourceBuild.timeoutMs, maxOutputBytes: sourceBuild.outputBytes }) }
|
|
674
|
+
: { backend: 'linux-native', runner: new LinuxRepositoryRunner({ backend: 'linux-native',
|
|
675
|
+
stateRoot: sourceBuild.stateRoot, authorityDigest: sourceBuild.authorityDigest,
|
|
676
|
+
runtime: sourceBuild.runtime, cgroupRoots: sourceBuild.cgroupRoots, expiresAt: sourceBuild.expiresAt,
|
|
677
|
+
maxRuns: sourceBuild.maxRuns, maxTotalDurationMs: sourceBuild.maxTotalDurationMs,
|
|
678
|
+
...(sourceBuild.stateSeal === undefined ? {} : { stateSeal: sourceBuild.stateSeal }),
|
|
679
|
+
...(sourceBuild.userNamespace === undefined ? {} : { userNamespace: sourceBuild.userNamespace,
|
|
680
|
+
systemSnapshot: sourceBuild.systemSnapshot }),
|
|
681
|
+
limits: { maxDurationMs: sourceBuild.timeoutMs, maxInputBytes: 64 * 1024 ** 2,
|
|
682
|
+
maxOutputBytes: sourceBuild.outputBytes, maxArtifactBytes: 32 * 1024 ** 2, maxFiles: 128,
|
|
683
|
+
memoryMiB: sourceBuild.memoryMiB, cpus: sourceBuild.cpus, pidsLimit: sourceBuild.pidsLimit,
|
|
684
|
+
workspaceMiB: sourceBuild.workspaceMiB, workspaceInodes: sourceBuild.workspaceInodes,
|
|
685
|
+
tmpMiB: sourceBuild.temporaryMiB, tmpInodes: sourceBuild.temporaryInodes, inputMiB: 65, inputInodes: 16 } }) };
|
|
640
686
|
}
|
|
641
687
|
catch (error) {
|
|
642
688
|
unsubscribeTaskChanges?.();
|
|
@@ -648,7 +694,12 @@ export class PluginControlPlaneService extends Service {
|
|
|
648
694
|
}
|
|
649
695
|
this.nativeSourceRunner = nativeRunner;
|
|
650
696
|
let runtime;
|
|
697
|
+
const preparationGeneration = {};
|
|
651
698
|
try {
|
|
699
|
+
// Restored fresh adoptions read their original Linux build receipt.
|
|
700
|
+
// Their source runner must exist before capability reconciliation.
|
|
701
|
+
await capabilities?.start();
|
|
702
|
+
this.abort.signal.throwIfAborted();
|
|
652
703
|
runtime = new SourceJobRuntime({ config: this.config.sourceJobs, build: sourceBuild, statePath: this.config.statePath, store: this.store,
|
|
653
704
|
ports: {
|
|
654
705
|
automations: {
|
|
@@ -661,6 +712,7 @@ export class PluginControlPlaneService extends Service {
|
|
|
661
712
|
},
|
|
662
713
|
delivery: { validateOwnerRoute: request => current('delivery').validateOwnerRoute(request) },
|
|
663
714
|
}, withGapSourceFence: (gapId, owner, callback) => this.taskGaps.withCurrent(gapId, owner, callback),
|
|
715
|
+
withWorkItemGrowthRunFence: (run, gapId, owner, generation, callback) => this.withWorkItemGrowthFrame(run, gapId, owner, generation, callback),
|
|
664
716
|
assertGrowthRun: (run, gapId, owner, generation) => this.assertSourceGrowthRun(run, gapId, owner, generation),
|
|
665
717
|
...(this.config.revisionVerifications ? { inspectRevisionParent: this.inspectRevisionParent,
|
|
666
718
|
verifyPreparedRevision: (job, signal) => this.verifyPreparedRevision(job, signal, () => jobsCtx.get('assistantVerifier')) } : {}),
|
|
@@ -694,14 +746,16 @@ export class PluginControlPlaneService extends Service {
|
|
|
694
746
|
throw new Error('source job has no released plan');
|
|
695
747
|
await this.adoptOwnerSourceRelease({ sourcePlanId: job.planId, signal, expectedTrustDigest: job.intent.trustDigest, assertCurrent });
|
|
696
748
|
} } : {}),
|
|
697
|
-
trust: () => this.boundTrust(), prepare: (job, signal, assertCurrent) => this.prepareSourceJob(job, signal, assertCurrent),
|
|
698
|
-
...(nativeRunner ? {
|
|
749
|
+
trust: () => this.boundTrust(), prepare: (job, signal, assertCurrent) => this.prepareSourceJob(job, signal, assertCurrent, nativeRunner?.backend === 'linux-native' ? { runner: nativeRunner.runner, generation: preparationGeneration } : undefined),
|
|
750
|
+
...(nativeRunner?.backend === 'linux-native' ? { isPreparationNotStarted: (error, job) => consumeCleanedLinuxSourcePreflight(error, job, preparationGeneration),
|
|
751
|
+
deterministicBuildFailure: (error, job) => consumeCleanedLinuxSourceBuildFailure(error, job, preparationGeneration) } : {}),
|
|
752
|
+
...(nativeRunner ? { inspectNativeQuiescence: (key) => nativeRunner.runner.inspectQuiescence(key) } : {}),
|
|
699
753
|
});
|
|
700
754
|
}
|
|
701
755
|
catch (error) {
|
|
702
756
|
if (this.nativeSourceRunner === nativeRunner)
|
|
703
757
|
this.nativeSourceRunner = undefined;
|
|
704
|
-
await nativeRunner?.close();
|
|
758
|
+
await nativeRunner?.runner.close();
|
|
705
759
|
unsubscribeTaskChanges?.();
|
|
706
760
|
if (capabilities) {
|
|
707
761
|
await capabilities.close();
|
|
@@ -710,6 +764,7 @@ export class PluginControlPlaneService extends Service {
|
|
|
710
764
|
throw error;
|
|
711
765
|
}
|
|
712
766
|
this.sourceRuntimes.add(runtime);
|
|
767
|
+
this.creationCapabilityRuntime = capabilities;
|
|
713
768
|
const close = async () => {
|
|
714
769
|
unsubscribeTaskChanges?.();
|
|
715
770
|
if (this.sourceRuntime === runtime)
|
|
@@ -726,7 +781,7 @@ export class PluginControlPlaneService extends Service {
|
|
|
726
781
|
this.sourceRuntimes.delete(runtime);
|
|
727
782
|
if (this.creationCapabilityRuntime === capabilities)
|
|
728
783
|
this.creationCapabilityRuntime = undefined;
|
|
729
|
-
const results = await Promise.allSettled([nativeRunner?.close(), capabilities?.close()]);
|
|
784
|
+
const results = await Promise.allSettled([nativeRunner?.runner.close(), capabilities?.close()]);
|
|
730
785
|
if (capabilities)
|
|
731
786
|
this.creationCapabilityRuntimes.delete(capabilities);
|
|
732
787
|
const failures = results.filter((result) => result.status === 'rejected');
|
|
@@ -738,6 +793,10 @@ export class PluginControlPlaneService extends Service {
|
|
|
738
793
|
runtime.start();
|
|
739
794
|
this.abort.signal.throwIfAborted();
|
|
740
795
|
this.sourceRuntime = runtime;
|
|
796
|
+
// Producer/binding readiness can arrive while capabilities.start() is
|
|
797
|
+
// awaiting its source checks, before these runtime fields are public.
|
|
798
|
+
// Reconcile once after publication so that notification is not lost.
|
|
799
|
+
this.reconcileCreatedCapabilities();
|
|
741
800
|
return close;
|
|
742
801
|
}
|
|
743
802
|
catch (error) {
|
|
@@ -782,6 +841,10 @@ export class PluginControlPlaneService extends Service {
|
|
|
782
841
|
return discover(loaded.catalog, capability);
|
|
783
842
|
}
|
|
784
843
|
/** Host only: reread the exact source; no caller text or ratings are admitted. */
|
|
844
|
+
recordOwnerWorkItemFailureGap = (source) => {
|
|
845
|
+
this.abort.signal.throwIfAborted();
|
|
846
|
+
return this.taskGaps.recordWorkItem(source);
|
|
847
|
+
};
|
|
785
848
|
recordOwnerTaskFailureGap = (source) => {
|
|
786
849
|
this.abort.signal.throwIfAborted();
|
|
787
850
|
return this.taskGaps.record(source);
|
|
@@ -1021,7 +1084,8 @@ export class PluginControlPlaneService extends Service {
|
|
|
1021
1084
|
/** Host-only durable Usage reader; the caller's binding is always an expectation. */
|
|
1022
1085
|
registerSourceGrowthRunProducer = (producer) => {
|
|
1023
1086
|
this.abort.signal.throwIfAborted();
|
|
1024
|
-
if (producer
|
|
1087
|
+
if (!['assistant-growth-source-run-producer/v1', 'assistant-growth-source-run-producer/v2'].includes(producer?.protocol) || typeof producer.inspect !== 'function'
|
|
1088
|
+
|| producer.protocol === 'assistant-growth-source-run-producer/v2' && typeof producer.withWorkItemRunFence !== 'function'
|
|
1025
1089
|
|| this.growthRunProducer !== undefined || this.growthProviderDraining)
|
|
1026
1090
|
throw new Error('plugin-control-plane: source growth run producer is invalid or already registered');
|
|
1027
1091
|
this.growthRunProducer = producer;
|
|
@@ -1142,29 +1206,109 @@ export class PluginControlPlaneService extends Service {
|
|
|
1142
1206
|
};
|
|
1143
1207
|
/** Host-only current association of exact adopted call versions with authenticated task heads. */
|
|
1144
1208
|
inspectCreatedCapabilityTaskAssociations = (planId) => {
|
|
1209
|
+
const input = this.createdCapabilityFeedbackInput(planId);
|
|
1210
|
+
if (!input)
|
|
1211
|
+
return [];
|
|
1212
|
+
try {
|
|
1213
|
+
return inspectCreationCapabilityTaskAssociations(input());
|
|
1214
|
+
}
|
|
1215
|
+
catch {
|
|
1216
|
+
return [];
|
|
1217
|
+
}
|
|
1218
|
+
};
|
|
1219
|
+
/** Host-only descriptive ordinary-use counts over the same fenced associations; never causal or adoption evidence. */
|
|
1220
|
+
inspectCreatedCapabilityOrdinaryUseReport = (planId) => {
|
|
1221
|
+
const input = this.createdCapabilityFeedbackInput(planId);
|
|
1222
|
+
if (!input)
|
|
1223
|
+
return undefined;
|
|
1224
|
+
try {
|
|
1225
|
+
return inspectCreationCapabilityOrdinaryUseReport(input());
|
|
1226
|
+
}
|
|
1227
|
+
catch {
|
|
1228
|
+
return undefined;
|
|
1229
|
+
}
|
|
1230
|
+
};
|
|
1231
|
+
/** Host-only observational before/after attribution of one adoption over the owner's certified tasks; never causal. */
|
|
1232
|
+
inspectCreatedCapabilityAvailabilityAttribution = (planId, options = {}) => {
|
|
1233
|
+
const input = this.createdCapabilityFeedbackInput(planId);
|
|
1234
|
+
if (!input)
|
|
1235
|
+
return undefined;
|
|
1236
|
+
try {
|
|
1237
|
+
return inspectCreationCapabilityAvailabilityAttribution({ ...input(),
|
|
1238
|
+
otherAdoptedAt: this.creationCapabilityRuntime?.inspectOtherAdoptionTimes(planId) ?? [],
|
|
1239
|
+
...(options.windowMs === undefined ? {} : { windowMs: options.windowMs }) });
|
|
1240
|
+
}
|
|
1241
|
+
catch {
|
|
1242
|
+
return undefined;
|
|
1243
|
+
}
|
|
1244
|
+
};
|
|
1245
|
+
/**
|
|
1246
|
+
* Host-only longitudinal ordinary-use series of one adoption in fixed windows with a same-scope session-cohort
|
|
1247
|
+
* control and a model-stratified difference-in-differences estimate; observational, never causal. Pure read.
|
|
1248
|
+
*/
|
|
1249
|
+
inspectCreatedCapabilityOrdinaryUseTrend = (planId, options = {}) => {
|
|
1250
|
+
const input = this.createdCapabilityFeedbackInput(planId);
|
|
1251
|
+
if (!input)
|
|
1252
|
+
return undefined;
|
|
1253
|
+
try {
|
|
1254
|
+
return inspectCreationCapabilityOrdinaryUseTrend({ ...input(),
|
|
1255
|
+
otherAdoptedAt: this.creationCapabilityRuntime?.inspectOtherAdoptionTimes(planId) ?? [],
|
|
1256
|
+
...(options.windowMs === undefined ? {} : { windowMs: options.windowMs }),
|
|
1257
|
+
...(options.preWindows === undefined ? {} : { preWindows: options.preWindows }),
|
|
1258
|
+
...(options.maxPostWindows === undefined ? {} : { maxPostWindows: options.maxPostWindows }) });
|
|
1259
|
+
}
|
|
1260
|
+
catch {
|
|
1261
|
+
return undefined;
|
|
1262
|
+
}
|
|
1263
|
+
};
|
|
1264
|
+
/**
|
|
1265
|
+
* Host-only, owner-invoked: seal the current ordinary-use trend of one adoption into the append-only,
|
|
1266
|
+
* content-addressed snapshot ledger. Re-sealing an unchanged trend returns the existing row (`created: false`);
|
|
1267
|
+
* rows are never rewritten. Still observational (`causal: false`); never adoption or revocation evidence.
|
|
1268
|
+
*/
|
|
1269
|
+
sealCreatedCapabilityOrdinaryUseTrendSnapshot = (planId, options = {}) => {
|
|
1270
|
+
const trend = this.inspectCreatedCapabilityOrdinaryUseTrend(planId, options);
|
|
1271
|
+
if (!trend)
|
|
1272
|
+
return undefined;
|
|
1273
|
+
try {
|
|
1274
|
+
return this.store.sealCreatedCapabilityTrendSnapshot(trend);
|
|
1275
|
+
}
|
|
1276
|
+
catch {
|
|
1277
|
+
return undefined;
|
|
1278
|
+
}
|
|
1279
|
+
};
|
|
1280
|
+
/** Host-only bounded newest-first read of sealed trend snapshots; undefined on invalid query or any tampered row. */
|
|
1281
|
+
inspectCreatedCapabilityOrdinaryUseTrendSnapshots = (query = {}) => {
|
|
1282
|
+
try {
|
|
1283
|
+
this.abort.signal.throwIfAborted();
|
|
1284
|
+
return this.store.listCreatedCapabilityTrendSnapshots(query);
|
|
1285
|
+
}
|
|
1286
|
+
catch {
|
|
1287
|
+
return undefined;
|
|
1288
|
+
}
|
|
1289
|
+
};
|
|
1290
|
+
/** The historical read is deferred so callers keep it inside their own failure boundary. */
|
|
1291
|
+
createdCapabilityFeedbackInput(planId) {
|
|
1145
1292
|
this.abort.signal.throwIfAborted();
|
|
1146
1293
|
const snapshot = this.creationCapabilityRuntime?.inspectAssociationEvidence(planId);
|
|
1147
1294
|
if (!snapshot || !this.config.creationCapabilities)
|
|
1148
|
-
return
|
|
1295
|
+
return undefined;
|
|
1149
1296
|
const owner = this.config.creationCapabilities.owner;
|
|
1150
1297
|
const evaluation = this.ctx.get('assistantEvaluation', false);
|
|
1151
1298
|
const delivery = this.ctx.get('assistantDelivery', false);
|
|
1152
1299
|
if (!evaluation || !delivery)
|
|
1153
|
-
return
|
|
1154
|
-
|
|
1300
|
+
return undefined;
|
|
1301
|
+
return () => {
|
|
1155
1302
|
const historical = this.store.getRetainedPreparedCreation(planId);
|
|
1156
|
-
return
|
|
1303
|
+
return { ...snapshot, owner, evaluation, delivery,
|
|
1157
1304
|
triggerInboxId: historical.reference.projection.subjectRef,
|
|
1158
1305
|
readSourceCurrent: () => {
|
|
1159
1306
|
this.assertCreatedCapabilityAssociationSource(snapshot.record);
|
|
1160
1307
|
const current = this.taskGaps.inspectCurrent(historical.plan.gapId, historical.reference.owner);
|
|
1161
1308
|
return { projection: current.canonical.projection, scopeWatermark: current.canonical.scopeWatermark };
|
|
1162
|
-
} }
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
return [];
|
|
1166
|
-
}
|
|
1167
|
-
};
|
|
1309
|
+
} };
|
|
1310
|
+
};
|
|
1311
|
+
}
|
|
1168
1312
|
/** Exact historical certificate and source check without nesting an Evaluation writer fence. */
|
|
1169
1313
|
assertCreatedCapabilityAssociationSource(record) {
|
|
1170
1314
|
this.abort.signal.throwIfAborted();
|
|
@@ -1252,11 +1396,113 @@ export class PluginControlPlaneService extends Service {
|
|
|
1252
1396
|
!== ownerGrant[key])) {
|
|
1253
1397
|
throw new Error('created capability retained source binding changed');
|
|
1254
1398
|
}
|
|
1399
|
+
if (growth.protocol === 'assistant-growth/source-run/v2') {
|
|
1400
|
+
if (reference.schemaVersion !== 2 || growth.ownerDigest !== controlPlaneDigest(reference.owner))
|
|
1401
|
+
throw new Error('retained work item reference changed');
|
|
1402
|
+
assertWorkItemRunReference(growth, reference);
|
|
1403
|
+
this.assertWorkItemPinsLocal(growth, 'retained');
|
|
1404
|
+
return callback(growth);
|
|
1405
|
+
}
|
|
1255
1406
|
return callback();
|
|
1256
1407
|
});
|
|
1257
1408
|
}
|
|
1409
|
+
inspectWorkItemPreparedCreationProtocol = () => {
|
|
1410
|
+
return !this.abort.signal.aborted && this.config.sourceJobs?.creation?.workItemFailures === true && this.sourceRuntime?.available() ? 'dsh-prepared-creation/v2' : undefined;
|
|
1411
|
+
};
|
|
1412
|
+
inspectWorkItemCreationReadiness = (input) => {
|
|
1413
|
+
try {
|
|
1414
|
+
this.abort.signal.throwIfAborted();
|
|
1415
|
+
const owner = inertCreationData(input).owner, config = this.config, source = this.sourceRuntime?.inspectWorkItemReadiness(owner);
|
|
1416
|
+
if (!source || !config.sourceJobs || !config.sourceBuild || config.sourceJobs.creation?.workItemFailures !== true || config.creationCapabilities?.workItemFailures !== true || !config.creationVerifications)
|
|
1417
|
+
return undefined;
|
|
1418
|
+
const delivery = this.ctx.get('assistantDelivery', false);
|
|
1419
|
+
const verifier = this.ctx.get('assistantVerifier', false);
|
|
1420
|
+
if (typeof delivery?.withOwnerWorkItemEngineeringContextFence !== 'function' || typeof verifier?.inspectWorkItemCreationReviewReadiness !== 'function' || typeof this.creationCapabilityRuntime?.inspectWorkItemAdoptionReadiness !== 'function')
|
|
1421
|
+
return undefined;
|
|
1422
|
+
const actual = delivery.validateOwnerRoute({ authorityId: owner.authorityId, principalId: owner.principalId, workspace: owner.workspace, agentPreset: owner.agentPreset });
|
|
1423
|
+
if (!isSourceOwnerContinuation(actual, owner) || Object.entries(config.creationCapabilities.owner).some(([key, value]) => actual[key] !== value))
|
|
1424
|
+
return undefined;
|
|
1425
|
+
const review = verifier.inspectWorkItemCreationReviewReadiness(), adoption = this.creationCapabilityRuntime.inspectWorkItemAdoptionReadiness();
|
|
1426
|
+
if (!review || !adoption || review.protocol !== 'assistant-growth/work-item-review-readiness/v1' || adoption.protocol !== 'assistant-growth/work-item-adoption-readiness/v1'
|
|
1427
|
+
|| review.sourceProtocol !== WORK_ITEM_ENGINEERING_PROTOCOL || adoption.sourceProtocol !== WORK_ITEM_ENGINEERING_PROTOCOL || review.runProtocol !== 'assistant-growth/source-run/v2' || adoption.runProtocol !== 'assistant-growth/source-run/v2'
|
|
1428
|
+
|| review.workItemFailures !== true || adoption.workItemFailures !== true || controlPlaneDigest(review.authority) !== controlPlaneDigest(config.creationVerifications.authority)
|
|
1429
|
+
|| !createPublicKey(review.publicKey).export({ type: 'spki', format: 'der' }).equals(createPublicKey(config.creationVerifications.publicKey).export({ type: 'spki', format: 'der' }))
|
|
1430
|
+
|| adoption.authority.authorityDigest !== creationCapabilityAuthorityDigest({ path: join(config.statePath, 'creation-adoptions.sqlite'), config: config.creationCapabilities })
|
|
1431
|
+
|| adoption.authority.authorityId !== config.creationCapabilities.authorityId || adoption.authority.expiresAt !== config.creationCapabilities.expiresAt)
|
|
1432
|
+
return undefined;
|
|
1433
|
+
const pins = { protocol: 'assistant-growth/work-item-authorities/v1', sourceJobs: { authorityId: source.authority.id, authorityDigest: source.authority.digest, expiresAt: source.authority.expiresAt },
|
|
1434
|
+
creationGrant: { id: source.creation.id, digest: controlPlaneDigest(source.creation), expiresAt: source.creation.expiresAt }, creationAcceptance: structuredClone(review.authority), adoption: structuredClone(adoption.authority) };
|
|
1435
|
+
validateWorkItemGrowthAuthorityPins(pins);
|
|
1436
|
+
if (source.authority.digest !== controlPlaneDigest({ config: config.sourceJobs, build: config.sourceBuild }) || [pins.sourceJobs.expiresAt, pins.creationGrant.expiresAt, pins.creationAcceptance.expiresAt, pins.adoption.expiresAt].some(expiry => Date.now() >= expiry))
|
|
1437
|
+
return undefined;
|
|
1438
|
+
const remaining = { ...source.remaining, verifications: review.remainingVerifications, adoptions: adoption.remainingAdoptions };
|
|
1439
|
+
if (Object.values(remaining).some(count => !Number.isSafeInteger(count) || count < 0))
|
|
1440
|
+
return undefined;
|
|
1441
|
+
return { protocol: 'assistant-growth/work-item-creation-readiness/v1', sourceProtocol: WORK_ITEM_ENGINEERING_PROTOCOL, runProtocol: 'assistant-growth/source-run/v2', workItemFailures: true, pins, remaining, available: Object.values(remaining).every(count => count > 0) && review.available && adoption.available };
|
|
1442
|
+
}
|
|
1443
|
+
catch {
|
|
1444
|
+
return undefined;
|
|
1445
|
+
}
|
|
1446
|
+
};
|
|
1447
|
+
assertWorkItemPinsLocal(run, phase = 'current') {
|
|
1448
|
+
const jobs = this.config.sourceJobs, verification = this.config.creationVerifications, adoption = this.config.creationCapabilities;
|
|
1449
|
+
if (!jobs || !this.config.sourceBuild || jobs.creation?.workItemFailures !== true || !verification || adoption?.workItemFailures !== true
|
|
1450
|
+
|| run.authorities.sourceJobs.authorityId !== jobs.authorityId || run.authorities.sourceJobs.authorityDigest !== controlPlaneDigest({ config: jobs, build: this.config.sourceBuild }) || run.authorities.sourceJobs.expiresAt !== jobs.expiresAt
|
|
1451
|
+
|| run.authorities.creationGrant.id !== jobs.creation.id || run.authorities.creationGrant.digest !== controlPlaneDigest(jobs.creation) || run.authorities.creationGrant.expiresAt !== jobs.creation.expiresAt
|
|
1452
|
+
|| controlPlaneDigest(run.creationAcceptance) !== controlPlaneDigest(verification.authority) || controlPlaneDigest(run.authorities.creationAcceptance) !== controlPlaneDigest(verification.authority)
|
|
1453
|
+
|| run.authorities.adoption.authorityId !== adoption.authorityId || run.authorities.adoption.authorityDigest !== creationCapabilityAuthorityDigest({ path: join(this.config.statePath, 'creation-adoptions.sqlite'), config: adoption }) || run.authorities.adoption.expiresAt !== adoption.expiresAt
|
|
1454
|
+
|| (phase === 'current' ? [run.authorities.sourceJobs.expiresAt, run.authorities.creationGrant.expiresAt, run.authorities.creationAcceptance.expiresAt, run.authorities.adoption.expiresAt] : [run.authorities.adoption.expiresAt]).some(expiry => Date.now() >= expiry))
|
|
1455
|
+
throw new Error('work item creation authority pins changed or expired');
|
|
1456
|
+
}
|
|
1457
|
+
assertWorkItemRunLocal(run, gapId, owner, generation) {
|
|
1458
|
+
const reference = this.store.getOwnerTaskFailureReference(gapId);
|
|
1459
|
+
if (!reference || reference.schemaVersion !== 2 || run.ownerDigest !== controlPlaneDigest(reference.owner) || !isSourceOwnerContinuation(owner, reference.owner))
|
|
1460
|
+
throw new Error('work item creation owner reference changed');
|
|
1461
|
+
assertWorkItemRunReference(run, reference);
|
|
1462
|
+
this.assertWorkItemPinsLocal(run);
|
|
1463
|
+
if (Date.now() >= run.expiresAt || generation && Date.now() >= run.generationDeadlineAt)
|
|
1464
|
+
throw new Error('work item source run expired');
|
|
1465
|
+
if (run.modelOrigin === 'inherited-owner-task' && this.workItemCreationFrame && controlPlaneDigest(this.workItemCreationFrame.source.task.proofIdentity.supplier) !== controlPlaneDigest(run.model))
|
|
1466
|
+
throw new Error('work item source supplier changed');
|
|
1467
|
+
}
|
|
1468
|
+
withWorkItemGrowthFrame(run, gapId, owner, generation, callback) {
|
|
1469
|
+
this.abort.signal.throwIfAborted();
|
|
1470
|
+
if (this.workItemCreationFrame) {
|
|
1471
|
+
if (this.workItemCreationFrame.gapId !== gapId || sourceGrowthRunDigest(this.workItemCreationFrame.run) !== sourceGrowthRunDigest(run))
|
|
1472
|
+
throw new Error('work item growth frame reentry');
|
|
1473
|
+
this.assertWorkItemRunLocal(run, gapId, owner, generation);
|
|
1474
|
+
return callback();
|
|
1475
|
+
}
|
|
1476
|
+
const producer = this.growthRunProducer;
|
|
1477
|
+
if (!producer || producer.protocol !== 'assistant-growth-source-run-producer/v2')
|
|
1478
|
+
throw new SourceGrowthRunUnavailableError('work item growth producer unavailable');
|
|
1479
|
+
const readiness = this.inspectWorkItemCreationReadiness({ owner });
|
|
1480
|
+
if (!readiness)
|
|
1481
|
+
throw new SourceGrowthRunUnavailableError('work item growth peers unavailable');
|
|
1482
|
+
if (controlPlaneDigest(readiness.pins) !== controlPlaneDigest(run.authorities))
|
|
1483
|
+
throw new Error('work item growth authority pins changed');
|
|
1484
|
+
const source = this.taskGaps.engineeringSnapshot(gapId, owner);
|
|
1485
|
+
return producer.withWorkItemRunFence({ runId: run.runId, intentDigest: run.intentDigest, expectedGrowthRunDigest: sourceGrowthRunDigest(run) }, actual => {
|
|
1486
|
+
if (this.growthRunProducer !== producer || sourceGrowthRunDigest(actual) !== sourceGrowthRunDigest(run) || this.workItemCreationFrame)
|
|
1487
|
+
throw new Error('work item producer changed its true binding');
|
|
1488
|
+
this.workItemCreationFrame = { gapId, run: actual, source };
|
|
1489
|
+
try {
|
|
1490
|
+
return this.store.withOwnerTaskFailureGapAdmission(gapId, () => { this.assertWorkItemRunLocal(actual, gapId, owner, generation); return callback(); });
|
|
1491
|
+
}
|
|
1492
|
+
finally {
|
|
1493
|
+
this.workItemCreationFrame = undefined;
|
|
1494
|
+
}
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1258
1497
|
assertSourceGrowthRun(expected, gapId, owner, generation) {
|
|
1259
1498
|
validateSourceGrowthRunBinding(expected);
|
|
1499
|
+
if (expected.protocol === 'assistant-growth/source-run/v2') {
|
|
1500
|
+
if (this.workItemCreationFrame) {
|
|
1501
|
+
this.assertWorkItemRunLocal(expected, gapId, owner, generation);
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1504
|
+
return this.withWorkItemGrowthFrame(expected, gapId, owner, generation, () => { });
|
|
1505
|
+
}
|
|
1260
1506
|
const producer = this.growthRunProducer;
|
|
1261
1507
|
if (!producer)
|
|
1262
1508
|
throw new SourceGrowthRunUnavailableError('source growth run producer unavailable');
|
|
@@ -1264,6 +1510,8 @@ export class PluginControlPlaneService extends Service {
|
|
|
1264
1510
|
if (!actual)
|
|
1265
1511
|
throw new Error('source growth run is absent or no longer current');
|
|
1266
1512
|
validateSourceGrowthRunBinding(actual);
|
|
1513
|
+
if (actual.protocol !== 'assistant-growth/source-run/v1')
|
|
1514
|
+
throw new Error('foreground source cannot use a work item run');
|
|
1267
1515
|
if (sourceGrowthRunDigest(actual) !== sourceGrowthRunDigest(expected)
|
|
1268
1516
|
|| expected.ownerDigest !== controlPlaneDigest(owner))
|
|
1269
1517
|
throw new Error('source growth run immutable binding changed');
|
|
@@ -1282,7 +1530,7 @@ export class PluginControlPlaneService extends Service {
|
|
|
1282
1530
|
throw new Error('source growth run creation acceptance policy changed or was not pinned before authoring');
|
|
1283
1531
|
}
|
|
1284
1532
|
const reference = this.store.getOwnerTaskFailureReference(gapId);
|
|
1285
|
-
if (!reference || expected.source.outcomeId !== reference.outcomeId
|
|
1533
|
+
if (!reference || reference.schemaVersion !== 1 || expected.source.outcomeId !== reference.outcomeId
|
|
1286
1534
|
|| expected.source.sourceDigest !== reference.sourceDigest
|
|
1287
1535
|
|| controlPlaneDigest(expected.source.projection) !== controlPlaneDigest(reference.projection)
|
|
1288
1536
|
|| expected.ownerDigest !== controlPlaneDigest(reference.owner))
|
|
@@ -1306,6 +1554,12 @@ export class PluginControlPlaneService extends Service {
|
|
|
1306
1554
|
|| Date.now() >= plan.expiresAt || !plan.creation || Date.now() >= plan.creation.grant.expiresAt) {
|
|
1307
1555
|
throw new Error('prepared creation package is unavailable for independent verification');
|
|
1308
1556
|
}
|
|
1557
|
+
if (reference.schemaVersion === 2) {
|
|
1558
|
+
const run = plan.creation.growthRun;
|
|
1559
|
+
if (run?.protocol !== 'assistant-growth/source-run/v2')
|
|
1560
|
+
throw new Error('work item prepared creation run unavailable');
|
|
1561
|
+
return this.withWorkItemGrowthFrame(run, plan.gapId, reference.owner, false, () => ({ protocol: 'dsh-prepared-creation/v2', plan: structuredClone(this.store.getSourcePlan(plan.id)), job: structuredClone(this.store.getPreparedSourceJob(plan.id)), reference: structuredClone(reference), source: structuredClone(this.workItemCreationFrame.source), artifact: this.store.readPreparedSourceArtifact(plan.id) }));
|
|
1562
|
+
}
|
|
1309
1563
|
return this.taskGaps.withCurrent(plan.gapId, reference.owner, () => {
|
|
1310
1564
|
this.abort.signal.throwIfAborted();
|
|
1311
1565
|
const job = this.store.getPreparedSourceJob(plan.id);
|
|
@@ -1339,6 +1593,17 @@ export class PluginControlPlaneService extends Service {
|
|
|
1339
1593
|
const reference = this.store.getOwnerTaskFailureReference(plan.gapId);
|
|
1340
1594
|
if (!reference)
|
|
1341
1595
|
throw new Error('creation verification source unavailable');
|
|
1596
|
+
if (reference.schemaVersion === 2) {
|
|
1597
|
+
const run = plan.creation?.growthRun;
|
|
1598
|
+
if (run?.protocol !== 'assistant-growth/source-run/v2')
|
|
1599
|
+
throw new Error('work item verification run unavailable');
|
|
1600
|
+
return this.withWorkItemGrowthFrame(run, plan.gapId, reference.owner, false, () => {
|
|
1601
|
+
const current = this.store.getSourcePlan(input.planId), job = this.store.getPreparedSourceJob(input.planId);
|
|
1602
|
+
if (current.digest !== input.planDigest || current.preparedEvidence?.pack.sha256 !== input.artifactSha256 || controlPlaneDigest(reference) !== input.referenceDigest || sourceGrowthRunDigest(run) !== input.growthRunDigest || !job.intent.creation?.growthRun || sourceGrowthRunDigest(job.intent.creation.growthRun) !== input.growthRunDigest || createHash('sha256').update(this.store.readPreparedSourceArtifact(input.planId)).digest('hex') !== input.artifactSha256)
|
|
1603
|
+
throw new Error('work item prepared verification exact binding changed');
|
|
1604
|
+
return callback(run);
|
|
1605
|
+
});
|
|
1606
|
+
}
|
|
1342
1607
|
return this.taskGaps.withCurrent(plan.gapId, reference.owner, () => {
|
|
1343
1608
|
this.abort.signal.throwIfAborted();
|
|
1344
1609
|
const current = this.store.getSourcePlan(input.planId);
|
|
@@ -1382,6 +1647,105 @@ export class PluginControlPlaneService extends Service {
|
|
|
1382
1647
|
current();
|
|
1383
1648
|
return review;
|
|
1384
1649
|
};
|
|
1650
|
+
/** Host-only read of the original successful build; never admits or replays a job. */
|
|
1651
|
+
inspectPreparedCreationLinuxBuild = async (planId, signal) => {
|
|
1652
|
+
const combined = AbortSignal.any([this.abort.signal, signal, AbortSignal.timeout(120_000)]);
|
|
1653
|
+
combined.throwIfAborted();
|
|
1654
|
+
const prepared = this.inspectPreparedCreation(planId), build = this.config.sourceBuild;
|
|
1655
|
+
const native = this.nativeSourceRunner, evidence = prepared.plan.preparedEvidence;
|
|
1656
|
+
if (!build || !('backend' in build) || build.backend !== 'linux-native' || !build.userNamespace || !build.systemSnapshot
|
|
1657
|
+
|| native?.backend !== 'linux-native'
|
|
1658
|
+
|| evidence?.schemaVersion !== 3 || evidence.boundary !== 'linux-native-repository-v1'
|
|
1659
|
+
|| !('backend' in prepared.job.intent.build) || prepared.job.intent.build.backend !== 'linux-native'
|
|
1660
|
+
|| prepared.job.intent.nativeResource?.backend !== 'linux-native' || prepared.job.intent.nativeResource.key !== prepared.job.id
|
|
1661
|
+
|| evidence.runtimeDigest !== linuxRuntimeDigest(build.runtime)
|
|
1662
|
+
|| evidence.runtimeDigest !== native.runner.runtimeDigest || evidence.executionContractDigest !== native.runner.contractDigest
|
|
1663
|
+
|| evidence.buildScriptSha256 !== createHash('sha256').update(LINUX_PREPARED_SOURCE_BUILD_SCRIPT).digest('hex')) {
|
|
1664
|
+
throw new Error('creation verification Linux build binding unavailable');
|
|
1665
|
+
}
|
|
1666
|
+
const evidenceDigest = controlPlaneDigest(evidence);
|
|
1667
|
+
const current = () => this.withPreparedCreationFence({ planId, planDigest: prepared.plan.digest,
|
|
1668
|
+
artifactSha256: evidence.pack.sha256, growthRunDigest: sourceGrowthRunDigest(prepared.plan.creation.growthRun),
|
|
1669
|
+
referenceDigest: controlPlaneDigest(prepared.reference) }, () => {
|
|
1670
|
+
combined.throwIfAborted();
|
|
1671
|
+
if (this.nativeSourceRunner !== native || this.config.sourceBuild !== build || Date.now() >= build.expiresAt
|
|
1672
|
+
|| controlPlaneDigest(this.store.getSourcePlan(planId).preparedEvidence) !== evidenceDigest
|
|
1673
|
+
|| controlPlaneDigest(this.store.getPreparedSourceJob(planId)) !== controlPlaneDigest(prepared.job)) {
|
|
1674
|
+
throw new Error('creation verification Linux build authority changed');
|
|
1675
|
+
}
|
|
1676
|
+
});
|
|
1677
|
+
current();
|
|
1678
|
+
if (controlPlaneDigest(await this.boundTrust()) !== prepared.job.intent.trustDigest)
|
|
1679
|
+
throw new Error('creation verification trust changed');
|
|
1680
|
+
await verifyLinuxRuntime(build.runtime, combined);
|
|
1681
|
+
current();
|
|
1682
|
+
const execution = await native.runner.inspectSuccessfulExecution(prepared.job.id, combined);
|
|
1683
|
+
current();
|
|
1684
|
+
const artifact = execution?.artifacts[0];
|
|
1685
|
+
if (!execution || execution.runtimeDigest !== evidence.runtimeDigest || execution.contractDigest !== evidence.executionContractDigest
|
|
1686
|
+
|| execution.result.status !== 'succeeded' || !execution.result.quiescent || execution.result.exitCode !== 0
|
|
1687
|
+
|| !execution.receiptBinding || controlPlaneDigest(execution.receiptBinding) !== controlPlaneDigest(evidence.receipt)
|
|
1688
|
+
|| execution.artifacts.length !== 1 || artifact?.path !== LINUX_SOURCE_PACK_PATH || artifact.sha256 !== evidence.pack.sha256
|
|
1689
|
+
|| artifact.bytes.length !== evidence.pack.sizeBytes || !Buffer.from(artifact.bytes).equals(prepared.artifact)
|
|
1690
|
+
|| createHash('sha256').update((execution.result.stdout ?? '') + '\0' + (execution.result.stderr ?? '')).digest('hex') !== evidence.logDigest) {
|
|
1691
|
+
throw new Error('creation verification original Linux build receipt or artifact changed');
|
|
1692
|
+
}
|
|
1693
|
+
if (controlPlaneDigest(await this.boundTrust()) !== prepared.job.intent.trustDigest)
|
|
1694
|
+
throw new Error('creation verification trust changed');
|
|
1695
|
+
await verifyLinuxRuntime(build.runtime, combined);
|
|
1696
|
+
current();
|
|
1697
|
+
return { runtime: structuredClone(build.runtime), evidenceDigest };
|
|
1698
|
+
};
|
|
1699
|
+
/** Host-only read of the original successful revision build; never admits or replays a job. */
|
|
1700
|
+
inspectPreparedRevisionLinuxBuild = async (planId, signal) => {
|
|
1701
|
+
const combined = AbortSignal.any([this.abort.signal, signal, AbortSignal.timeout(120_000)]);
|
|
1702
|
+
combined.throwIfAborted();
|
|
1703
|
+
const prepared = this.inspectPreparedRevision(planId), build = this.config.sourceBuild;
|
|
1704
|
+
const native = this.nativeSourceRunner, evidence = prepared.plan.preparedEvidence;
|
|
1705
|
+
if (!build || !('backend' in build) || build.backend !== 'linux-native' || !build.userNamespace || !build.systemSnapshot
|
|
1706
|
+
|| native?.backend !== 'linux-native'
|
|
1707
|
+
|| evidence?.schemaVersion !== 3 || evidence.boundary !== 'linux-native-repository-v1'
|
|
1708
|
+
|| !('backend' in prepared.job.intent.build) || prepared.job.intent.build.backend !== 'linux-native'
|
|
1709
|
+
|| prepared.job.intent.nativeResource?.backend !== 'linux-native' || prepared.job.intent.nativeResource.key !== prepared.job.id
|
|
1710
|
+
|| evidence.runtimeDigest !== linuxRuntimeDigest(build.runtime)
|
|
1711
|
+
|| evidence.runtimeDigest !== native.runner.runtimeDigest || evidence.executionContractDigest !== native.runner.contractDigest
|
|
1712
|
+
|| evidence.buildScriptSha256 !== createHash('sha256').update(LINUX_PREPARED_SOURCE_BUILD_SCRIPT).digest('hex')) {
|
|
1713
|
+
throw new Error('revision verification Linux build binding unavailable');
|
|
1714
|
+
}
|
|
1715
|
+
const sourceRevision = prepared.plan.sourceRevision;
|
|
1716
|
+
const evidenceDigest = controlPlaneDigest(evidence);
|
|
1717
|
+
const current = () => this.withPreparedRevisionFence({ planId, planDigest: prepared.plan.digest,
|
|
1718
|
+
artifactSha256: evidence.pack.sha256, growthRunDigest: sourceGrowthRunDigest(sourceRevision.growthRun),
|
|
1719
|
+
referenceDigest: controlPlaneDigest(prepared.reference), parentDigest: controlPlaneDigest(sourceRevision.parent) }, () => {
|
|
1720
|
+
combined.throwIfAborted();
|
|
1721
|
+
if (this.nativeSourceRunner !== native || this.config.sourceBuild !== build || Date.now() >= build.expiresAt
|
|
1722
|
+
|| controlPlaneDigest(this.store.getSourcePlan(planId).preparedEvidence) !== evidenceDigest
|
|
1723
|
+
|| controlPlaneDigest(this.store.getPreparedRevisionJob(planId)) !== controlPlaneDigest(prepared.job)) {
|
|
1724
|
+
throw new Error('revision verification Linux build authority changed');
|
|
1725
|
+
}
|
|
1726
|
+
});
|
|
1727
|
+
current();
|
|
1728
|
+
if (controlPlaneDigest(await this.boundTrust()) !== prepared.job.intent.trustDigest)
|
|
1729
|
+
throw new Error('revision verification trust changed');
|
|
1730
|
+
await verifyLinuxRuntime(build.runtime, combined);
|
|
1731
|
+
current();
|
|
1732
|
+
const execution = await native.runner.inspectSuccessfulExecution(prepared.job.id, combined);
|
|
1733
|
+
current();
|
|
1734
|
+
const artifact = execution?.artifacts[0];
|
|
1735
|
+
if (!execution || execution.runtimeDigest !== evidence.runtimeDigest || execution.contractDigest !== evidence.executionContractDigest
|
|
1736
|
+
|| execution.result.status !== 'succeeded' || !execution.result.quiescent || execution.result.exitCode !== 0
|
|
1737
|
+
|| !execution.receiptBinding || controlPlaneDigest(execution.receiptBinding) !== controlPlaneDigest(evidence.receipt)
|
|
1738
|
+
|| execution.artifacts.length !== 1 || artifact?.path !== LINUX_SOURCE_PACK_PATH || artifact.sha256 !== evidence.pack.sha256
|
|
1739
|
+
|| artifact.bytes.length !== evidence.pack.sizeBytes || !Buffer.from(artifact.bytes).equals(prepared.artifact)
|
|
1740
|
+
|| createHash('sha256').update((execution.result.stdout ?? '') + '\0' + (execution.result.stderr ?? '')).digest('hex') !== evidence.logDigest) {
|
|
1741
|
+
throw new Error('revision verification original Linux build receipt or artifact changed');
|
|
1742
|
+
}
|
|
1743
|
+
if (controlPlaneDigest(await this.boundTrust()) !== prepared.job.intent.trustDigest)
|
|
1744
|
+
throw new Error('revision verification trust changed');
|
|
1745
|
+
await verifyLinuxRuntime(build.runtime, combined);
|
|
1746
|
+
current();
|
|
1747
|
+
return { runtime: structuredClone(build.runtime), evidenceDigest };
|
|
1748
|
+
};
|
|
1385
1749
|
/** Signed behavior evidence over stored pack/checks. Adoption must await async review-context recheck. */
|
|
1386
1750
|
inspectVerifiedCreation = (planId) => {
|
|
1387
1751
|
const certificate = this.store.getCreationVerification(planId);
|
|
@@ -1691,13 +2055,21 @@ export class PluginControlPlaneService extends Service {
|
|
|
1691
2055
|
throw new Error('plugin-control-plane: durable source jobs unavailable or still draining');
|
|
1692
2056
|
return this.sourceRuntime.reconcileUnknown(input);
|
|
1693
2057
|
};
|
|
1694
|
-
async prepareSourceJob(job, signal, assertCurrent) {
|
|
2058
|
+
async prepareSourceJob(job, signal, assertCurrent, preflight) {
|
|
1695
2059
|
this.abort.signal.throwIfAborted();
|
|
1696
|
-
if (this.sourceBuilds.size !== 0 || this.sourceInspections.size !== 0)
|
|
1697
|
-
|
|
2060
|
+
if (this.sourceBuilds.size !== 0 || this.sourceInspections.size !== 0) {
|
|
2061
|
+
const error = new Error('plugin-control-plane: another source operation is draining');
|
|
2062
|
+
// This call has not created an operation/worktree or called a runner.
|
|
2063
|
+
if (preflight && job.intent.nativeResource?.backend === 'linux-native' && job.intent.nativeResource.key === job.id
|
|
2064
|
+
&& controlPlaneDigest(job.intent) === job.intentDigest) {
|
|
2065
|
+
throw cleanedLinuxSourcePreflight(error, job, preflight.generation, () => !this.abort.signal.aborted
|
|
2066
|
+
&& this.nativeSourceRunner?.backend === 'linux-native' && this.nativeSourceRunner.runner === preflight.runner);
|
|
2067
|
+
}
|
|
2068
|
+
throw error;
|
|
2069
|
+
}
|
|
1698
2070
|
const intent = job.intent;
|
|
1699
2071
|
const operation = this.prepareSourcePlanOwned({ gapId: intent.gapId, name: intent.name, repository: intent.repository, files: intent.files,
|
|
1700
|
-
idempotencyKey: `source-job-plan:${job.id}`, expectedBaseCommit: intent.baseCommit, ttlMs: intent.ttlMs, timeoutMs: intent.build.timeoutMs, offline: true, signal, assertCurrent }, job);
|
|
2072
|
+
idempotencyKey: `source-job-plan:${job.id}`, expectedBaseCommit: intent.baseCommit, ttlMs: intent.ttlMs, timeoutMs: intent.build.timeoutMs, offline: true, signal, assertCurrent }, job, preflight);
|
|
1701
2073
|
this.sourceBuilds.add(operation);
|
|
1702
2074
|
try {
|
|
1703
2075
|
return await operation;
|
|
@@ -2016,7 +2388,7 @@ export class PluginControlPlaneService extends Service {
|
|
|
2016
2388
|
void operation.then(() => this.sourceBuilds.delete(operation), () => this.sourceBuilds.delete(operation));
|
|
2017
2389
|
return operation;
|
|
2018
2390
|
}
|
|
2019
|
-
async prepareSourcePlanOwned(input, sourceJob) {
|
|
2391
|
+
async prepareSourcePlanOwned(input, sourceJob, preflight) {
|
|
2020
2392
|
const signal = input.signal === undefined ? this.abort.signal : AbortSignal.any([this.abort.signal, input.signal]);
|
|
2021
2393
|
const gapOwner = sourceJob?.intent.owner ?? input.owner;
|
|
2022
2394
|
const creating = sourceJob?.intent.mode === 'create';
|
|
@@ -2065,8 +2437,10 @@ export class PluginControlPlaneService extends Service {
|
|
|
2065
2437
|
if (!Number.isSafeInteger(ttlMs) || ttlMs < 900_000 || ttlMs > 86_400_000) {
|
|
2066
2438
|
throw new ControlPlaneCliError('INVALID_ARGUMENT', 'ttlMs must be an integer within 900000..86400000');
|
|
2067
2439
|
}
|
|
2068
|
-
const
|
|
2069
|
-
|
|
2440
|
+
const buildConfig = this.config.sourceBuild;
|
|
2441
|
+
const repositoryBuild = buildConfig !== undefined && ('backend' in buildConfig
|
|
2442
|
+
? buildConfig.backend === 'linux-native' : ['repository', 'portable-repository'].includes(buildConfig.profile ?? ''));
|
|
2443
|
+
const maximumTimeoutMs = repositoryBuild ? 1_800_000 : 240_000;
|
|
2070
2444
|
const timeoutMs = input.timeoutMs ?? 180_000;
|
|
2071
2445
|
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 60_000 || timeoutMs > maximumTimeoutMs) {
|
|
2072
2446
|
throw new ControlPlaneCliError('INVALID_ARGUMENT', `timeoutMs must be an integer within 60000..${maximumTimeoutMs}`);
|
|
@@ -2087,6 +2461,7 @@ export class PluginControlPlaneService extends Service {
|
|
|
2087
2461
|
const stateRoot = join(this.config.statePath, 'source-worktrees');
|
|
2088
2462
|
const isolated = await createIsolatedWorktree({ stateRoot, repository, baseCommit, environment,
|
|
2089
2463
|
...(sourceJob === undefined ? {} : { worktreeName: basename(sourceJob.intent.worktree) }) });
|
|
2464
|
+
let linuxBuildInvoked = false;
|
|
2090
2465
|
try {
|
|
2091
2466
|
const generated = revision !== undefined ? await prepareRevisedPluginWorkspace({ worktree: isolated.worktree,
|
|
2092
2467
|
baseCommit, name, files: input.files, environment, signal, assertCurrent, revision, parentSource: parent.source, parentCertificate: parent.certificate })
|
|
@@ -2116,12 +2491,21 @@ export class PluginControlPlaneService extends Service {
|
|
|
2116
2491
|
parentCertificate: parent.certificate, files: input.files } }), ...(capturePack ? { capturePack: true } : {}) };
|
|
2117
2492
|
const checked = 'backend' in configured
|
|
2118
2493
|
? await (async () => {
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2494
|
+
const native = this.nativeSourceRunner;
|
|
2495
|
+
if (sourceJob === undefined || native === undefined || native.backend !== configured.backend
|
|
2496
|
+
|| sourceJob.intent.nativeResource?.backend !== configured.backend || sourceJob.intent.nativeResource.key !== sourceJob.id) {
|
|
2497
|
+
throw new ControlPlaneCliError('SOURCE_BOUNDARY', 'native source preparation requires its bound durable source job');
|
|
2498
|
+
}
|
|
2499
|
+
if (configured.backend === 'linux-native' && native.backend === 'linux-native') {
|
|
2500
|
+
linuxBuildInvoked = true;
|
|
2501
|
+
return runLinuxPreparedChecks({ ...common, config: { ...configured, timeoutMs: Math.min(timeoutMs, configured.timeoutMs) },
|
|
2502
|
+
runner: native.runner, sourceJob: { id: sourceJob.id } });
|
|
2503
|
+
}
|
|
2504
|
+
if (configured.backend !== 'macos-seatbelt' || native.backend !== 'macos-seatbelt' || !creating && !revising) {
|
|
2505
|
+
throw new ControlPlaneCliError('SOURCE_BOUNDARY', 'macOS source preparation requires a Host-scaffolded creation job');
|
|
2122
2506
|
}
|
|
2123
2507
|
return runNativePreparedChecks({ ...common, config: { ...configured, timeoutMs: Math.min(timeoutMs, configured.timeoutMs) },
|
|
2124
|
-
runner:
|
|
2508
|
+
runner: native.runner, sourceJob: { id: sourceJob.id } });
|
|
2125
2509
|
})()
|
|
2126
2510
|
: await runDockerPreparedChecks({ ...common, config: { ...configured, timeoutMs: Math.min(timeoutMs, configured.timeoutMs) },
|
|
2127
2511
|
...(sourceJob === undefined ? {} : { sourceJob: { id: sourceJob.id, containerName: sourceJob.intent.containerName } }) });
|
|
@@ -2145,7 +2529,15 @@ export class PluginControlPlaneService extends Service {
|
|
|
2145
2529
|
}
|
|
2146
2530
|
// The worktree survives success: the owner recomputes its digests on this
|
|
2147
2531
|
// exact directory during `source verify-prepared`.
|
|
2148
|
-
|
|
2532
|
+
const commitSource = (callback) => {
|
|
2533
|
+
if (creation?.growthRun?.protocol === 'assistant-growth/source-run/v2') {
|
|
2534
|
+
if (!gapOwner || !('receiptVersion' in gapOwner))
|
|
2535
|
+
throw new Error('work item creation owner unavailable');
|
|
2536
|
+
return this.withWorkItemGrowthFrame(creation.growthRun, input.gapId, gapOwner, false, callback);
|
|
2537
|
+
}
|
|
2538
|
+
return this.taskGaps.withCurrent(input.gapId, gapOwner, callback);
|
|
2539
|
+
};
|
|
2540
|
+
return commitSource(() => this.store.createSourcePlan({ gapId: input.gapId, repository, worktree: isolated.worktree, baseCommit,
|
|
2149
2541
|
name, generatorDigest: generated?.generatorDigest ?? MODIFY_GENERATOR_DIGEST,
|
|
2150
2542
|
scope: generated?.scope ?? [`plugins/${name}`], mode: creating ? 'prepared-create' : revising ? 'prepared-revise' : 'modify', ttlMs,
|
|
2151
2543
|
...(creation === undefined ? {} : { creation }), ...(revision === undefined ? {} : { revision }),
|
|
@@ -2156,6 +2548,19 @@ export class PluginControlPlaneService extends Service {
|
|
|
2156
2548
|
}
|
|
2157
2549
|
catch (error) {
|
|
2158
2550
|
await isolated.remove();
|
|
2551
|
+
if (preflight && sourceJob && sourceJob.intent.nativeResource?.backend === 'linux-native'
|
|
2552
|
+
&& sourceJob.intent.nativeResource.key === sourceJob.id && controlPlaneDigest(sourceJob.intent) === sourceJob.intentDigest) {
|
|
2553
|
+
// Preserve native authority checks across asynchronous worktree cleanup.
|
|
2554
|
+
// The runtime consumes the complete chain immediately before its original claim CAS.
|
|
2555
|
+
if (linuxBuildInvoked && isLinuxSourceBuildFailed(error)) {
|
|
2556
|
+
throw cleanedLinuxSourceBuildFailure(error, sourceJob, preflight.generation, () => !this.abort.signal.aborted && this.nativeSourceRunner?.backend === 'linux-native'
|
|
2557
|
+
&& this.nativeSourceRunner.runner === preflight.runner
|
|
2558
|
+
? consumeLinuxSourceBuildFailed(error, { runner: preflight.runner, key: sourceJob.id }) : undefined);
|
|
2559
|
+
}
|
|
2560
|
+
throw cleanedLinuxSourcePreflight(error, sourceJob, preflight.generation, () => !this.abort.signal.aborted && this.nativeSourceRunner?.backend === 'linux-native'
|
|
2561
|
+
&& this.nativeSourceRunner.runner === preflight.runner
|
|
2562
|
+
&& (!linuxBuildInvoked || consumeLinuxSourceNotStarted(error, { runner: preflight.runner, key: sourceJob.id })));
|
|
2563
|
+
}
|
|
2159
2564
|
throw error;
|
|
2160
2565
|
}
|
|
2161
2566
|
}
|