@dsh-enhanced/plugin-control-plane 0.1.65 → 0.1.67
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 +123 -7
- 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 +13 -0
- package/lib/creation-capability-execution.d.ts.map +1 -0
- package/lib/creation-capability-execution.js +112 -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 +65 -1
- package/lib/creation-capability-runtime.d.ts.map +1 -1
- package/lib/creation-capability-runtime.js +178 -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 +15 -4
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +10 -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/revision-capability-journal.d.ts +103 -0
- package/lib/revision-capability-journal.d.ts.map +1 -0
- package/lib/revision-capability-journal.js +872 -0
- package/lib/revision-capability-journal.js.map +1 -0
- package/lib/revision-capability-runtime.d.ts +74 -0
- package/lib/revision-capability-runtime.d.ts.map +1 -0
- package/lib/revision-capability-runtime.js +562 -0
- package/lib/revision-capability-runtime.js.map +1 -0
- package/lib/revision-capability-types.d.ts +289 -0
- package/lib/revision-capability-types.d.ts.map +1 -0
- package/lib/revision-capability-types.js +2 -0
- package/lib/revision-capability-types.js.map +1 -0
- 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 +107 -6
- package/lib/service.d.ts.map +1 -1
- package/lib/service.js +643 -47
- 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 +33 -1
- package/lib/source-jobs.d.ts.map +1 -1
- package/lib/source-jobs.js +141 -29
- package/lib/source-jobs.js.map +1 -1
- package/lib/source-workspace.d.ts.map +1 -1
- package/lib/source-workspace.js +0 -1
- package/lib/source-workspace.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 +22 -2
- package/lib/store.d.ts.map +1 -1
- package/lib/store.js +477 -177
- 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,20 @@
|
|
|
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 {
|
|
5
|
+
import { RevisionCapabilityJournal, revisionCapabilityPublicKey, validateRevisionCapabilityConfig } from './revision-capability-journal.js';
|
|
6
|
+
import { RevisionCapabilityRuntime } from './revision-capability-runtime.js';
|
|
7
|
+
import { inspectCreationCapabilityAvailabilityAttribution, inspectCreationCapabilityOrdinaryUseReport, inspectCreationCapabilityOrdinaryUseTrend, inspectCreationCapabilityTaskAssociations } from './creation-capability-feedback.js';
|
|
5
8
|
import { captureCreationCapabilitySource } from './creation-capability-source.js';
|
|
6
9
|
import { lstat, readFile, realpath } from 'node:fs/promises';
|
|
7
10
|
import { realpathSync } from 'node:fs';
|
|
8
11
|
import { basename, dirname, isAbsolute, join, resolve } from 'node:path';
|
|
9
12
|
import { createHash, createPublicKey, randomBytes } from 'node:crypto';
|
|
10
|
-
import { isSourceOwnerContinuation, sourceGrowthRunDigest, validateCreationAcceptanceAuthorityRef, verifyPluginCreationVerificationCertificate, validateRevisionAcceptanceAuthorityRef, verifyPluginRevisionVerificationCertificate, validateRevisionRegressionAcceptanceAuthorityRef, verifyPluginRevisionRegressionCertificate
|
|
13
|
+
import { isSourceOwnerContinuation, anySourceGrowthRunDigest as sourceGrowthRunDigest, validateAnySourceGrowthRunBinding as validateSourceGrowthRunBinding, validateWorkItemGrowthAuthorityPins, validateCreationAcceptanceAuthorityRef, verifyPluginCreationVerificationCertificate, validateRevisionAcceptanceAuthorityRef, verifyPluginRevisionVerificationCertificate, validateRevisionRegressionAcceptanceAuthorityRef, verifyPluginRevisionRegressionCertificate } from '@dsh-enhanced/assistant-growth-contract';
|
|
11
14
|
import { TaskObservationRuntime, validateTaskObservationConfig } from './task-observation-runtime.js';
|
|
12
15
|
import { LiveQualificationRuntime, validateLiveQualificationConfig } from './live-qualification-runtime.js';
|
|
13
16
|
import { rollbackPluginWatch } from './cli.js';
|
|
17
|
+
import { assertWorkItemRunReference, inertCreationData, WORK_ITEM_ENGINEERING_PROTOCOL } from './work-item-creation-contract.js';
|
|
14
18
|
import { OwnerTaskFailureGaps } from './owner-task-gaps.js';
|
|
15
19
|
import { Context, Service } from '@deepseek-ai/cordis';
|
|
16
20
|
import Schema from '@deepseek-ai/schemastery';
|
|
@@ -27,7 +31,8 @@ import { Ed25519ApprovalAuthority } from './approval.js';
|
|
|
27
31
|
import { ControlPlaneStore, MODIFY_GENERATOR_DIGEST, controlPlaneDigest } from './store.js';
|
|
28
32
|
import { runDockerPreparedChecks, validateSourceBuildConfig } from './source-build.js';
|
|
29
33
|
import { runNativePreparedChecks } from './native-source-build.js';
|
|
30
|
-
import {
|
|
34
|
+
import { LINUX_PREPARED_SOURCE_BUILD_SCRIPT, LINUX_SOURCE_PACK_PATH, runLinuxPreparedChecks } from './linux-source-build.js';
|
|
35
|
+
import { LinuxRepositoryRunner, NativeMacVerifierRunner, linuxRuntimeDigest, verifyLinuxRuntime } from '@dsh-enhanced/assistant-isolation';
|
|
31
36
|
import { NATIVE_SOURCE_COMPILER } from './native-source-build-worker.js';
|
|
32
37
|
import { awaitSourceSignal, inspectSourceContext, inspectSourceTargetsContext } from './source-context.js';
|
|
33
38
|
import { inspectSourceRevisionContext, prepareRevisedPluginWorkspace, verifyRevisedPluginWorkspace } from './source-revision.js';
|
|
@@ -35,7 +40,7 @@ import { inspectSourceCreationContext, prepareCreatedPluginWorkspace, validateSo
|
|
|
35
40
|
import { resolveSourceBaseline } from './source-baseline.js';
|
|
36
41
|
import { inheritedEnvironment, loadTrustConfig, resolveTrustKey } from './trust.js';
|
|
37
42
|
import { registerPluginControlTools } from './tools.js';
|
|
38
|
-
import { SourceGrowthRunUnavailableError, SourceJobRuntime,
|
|
43
|
+
import { SourceGrowthRunUnavailableError, SourceJobRuntime, normalizeSourceJobsConfig } from './source-jobs.js';
|
|
39
44
|
import { installRuntimeObserver, validateRuntimeObserverConfig } from './runtime-observer.js';
|
|
40
45
|
import { installReplayEndpoint, validateReplayEndpointConfig } from './replay-endpoint.js';
|
|
41
46
|
import { readPrivateRuntimeObserverKey } from './runtime-observer-protocol.js';
|
|
@@ -54,6 +59,7 @@ const schema = Schema.object({
|
|
|
54
59
|
revisionVerifications: Schema.any(),
|
|
55
60
|
revisionRegressions: Schema.any(),
|
|
56
61
|
creationCapabilities: Schema.any(),
|
|
62
|
+
revisionCapabilities: Schema.any(),
|
|
57
63
|
sourceApprovals: Schema.any(),
|
|
58
64
|
sourceReleases: Schema.any(),
|
|
59
65
|
sourceReleaseExecution: Schema.any(),
|
|
@@ -136,13 +142,22 @@ export function normalizeControlPlaneConfig(input) {
|
|
|
136
142
|
validateSourceApprovalClientConfig(config.sourceApprovals);
|
|
137
143
|
if (config.sourceBuild !== undefined)
|
|
138
144
|
validateSourceBuildConfig(config.sourceBuild);
|
|
139
|
-
if (config.sourceBuild !== undefined && 'backend' in config.sourceBuild) {
|
|
145
|
+
if (config.sourceBuild !== undefined && 'backend' in config.sourceBuild && config.sourceBuild.backend === 'macos-seatbelt') {
|
|
140
146
|
if (!config.sourceJobs?.allowedModes || config.sourceJobs.allowedModes.some(mode => mode === 'modify')
|
|
141
147
|
|| config.sourceReleases !== undefined || config.sourceReleaseExecution !== undefined
|
|
142
148
|
|| config.sourceAdoptions !== undefined || config.sourceApprovals !== undefined) {
|
|
143
149
|
throw new Error('plugin-control-plane: native source build only supports Host-scaffolded create/revise-created');
|
|
144
150
|
}
|
|
145
151
|
}
|
|
152
|
+
if (config.sourceBuild !== undefined && 'backend' in config.sourceBuild && config.sourceBuild.backend === 'linux-native') {
|
|
153
|
+
if (!config.sourceJobs)
|
|
154
|
+
throw new Error('plugin-control-plane: Linux repository preparation requires durable sourceJobs');
|
|
155
|
+
if ((config.creationVerifications || config.creationCapabilities || config.revisionVerifications || config.revisionRegressions
|
|
156
|
+
|| config.revisionCapabilities)
|
|
157
|
+
&& (!config.sourceBuild.userNamespace || !config.sourceBuild.systemSnapshot)) {
|
|
158
|
+
throw new Error('plugin-control-plane: Linux independent verification requires the dual-UID repository boundary');
|
|
159
|
+
}
|
|
160
|
+
}
|
|
146
161
|
if (config.sourceReleases !== undefined) {
|
|
147
162
|
validateSourceReleaseClientConfig(config.sourceReleases);
|
|
148
163
|
if (!config.sourceApprovals || config.sourceBuild?.versioning !== 'patch')
|
|
@@ -168,7 +183,7 @@ export function normalizeControlPlaneConfig(input) {
|
|
|
168
183
|
}
|
|
169
184
|
}
|
|
170
185
|
if (config.sourceJobs !== undefined) {
|
|
171
|
-
|
|
186
|
+
config.sourceJobs = normalizeSourceJobsConfig(config.sourceJobs, config.sourceBuild);
|
|
172
187
|
if (realpathSync(config.sourceJobs.repository) !== config.sourceJobs.repository)
|
|
173
188
|
throw new Error('plugin-control-plane: sourceJobs.repository must be canonical');
|
|
174
189
|
}
|
|
@@ -227,9 +242,19 @@ export function normalizeControlPlaneConfig(input) {
|
|
|
227
242
|
}
|
|
228
243
|
if (config.creationCapabilities !== undefined) {
|
|
229
244
|
validateCreationCapabilityConfig(config.creationCapabilities);
|
|
245
|
+
if (config.creationCapabilities.workItemFailures === false) {
|
|
246
|
+
config.creationCapabilities = { ...config.creationCapabilities };
|
|
247
|
+
delete config.creationCapabilities.workItemFailures;
|
|
248
|
+
}
|
|
230
249
|
const capability = config.creationCapabilities;
|
|
231
250
|
const verification = config.creationVerifications;
|
|
232
251
|
const jobs = config.sourceJobs;
|
|
252
|
+
const build = config.sourceBuild;
|
|
253
|
+
const buildBackend = build && 'backend' in build ? build.backend : 'docker';
|
|
254
|
+
const adoptionBackend = 'backend' in capability.runner ? capability.runner.backend : 'docker';
|
|
255
|
+
if (!build || buildBackend !== adoptionBackend) {
|
|
256
|
+
throw new Error('plugin-control-plane: creation build, verification and adoption require matching execution backends');
|
|
257
|
+
}
|
|
233
258
|
if (!verification || !jobs?.creation || capability.namePrefix !== jobs.creation.namePrefix
|
|
234
259
|
|| capability.owner.authorityId !== jobs.ownerRouteId || capability.owner.principalId !== jobs.principalId
|
|
235
260
|
|| capability.owner.workspace !== jobs.workspace || capability.owner.agentPreset !== jobs.preset
|
|
@@ -241,6 +266,39 @@ export function normalizeControlPlaneConfig(input) {
|
|
|
241
266
|
if (adoptionKey.equals(verificationKey))
|
|
242
267
|
throw new Error('plugin-control-plane: adoption and verification signing keys must be separate');
|
|
243
268
|
}
|
|
269
|
+
if (config.revisionCapabilities !== undefined) {
|
|
270
|
+
validateRevisionCapabilityConfig(config.revisionCapabilities);
|
|
271
|
+
const capability = config.revisionCapabilities;
|
|
272
|
+
const creation = config.creationCapabilities;
|
|
273
|
+
const verification = config.revisionVerifications;
|
|
274
|
+
const jobs = config.sourceJobs;
|
|
275
|
+
const build = config.sourceBuild;
|
|
276
|
+
if (!creation || !verification || !jobs?.revision || !build) {
|
|
277
|
+
throw new Error('plugin-control-plane: revision adoption requires creation capabilities, revision verification and finite revision source');
|
|
278
|
+
}
|
|
279
|
+
const buildBackend = 'backend' in build ? build.backend : 'docker';
|
|
280
|
+
const runnerBackend = 'backend' in creation.runner ? creation.runner.backend : 'docker';
|
|
281
|
+
if (buildBackend !== runnerBackend
|
|
282
|
+
|| capability.namePrefix !== creation.namePrefix || capability.namePrefix !== jobs.revision.namePrefix
|
|
283
|
+
|| controlPlaneDigest(capability.owner) !== controlPlaneDigest(creation.owner)
|
|
284
|
+
|| capability.expiresAt > creation.expiresAt) {
|
|
285
|
+
throw new Error('plugin-control-plane: revision adoption authority does not match its authenticated creation lane');
|
|
286
|
+
}
|
|
287
|
+
if (capability.authorityId === creation.authorityId || capability.authorityId === verification.authority.authorityId
|
|
288
|
+
|| capability.rollback.authorityId === capability.authorityId || capability.rollback.authorityId === creation.authorityId
|
|
289
|
+
|| capability.rollback.authorityId === verification.authority.authorityId) {
|
|
290
|
+
throw new Error('plugin-control-plane: revision adoption, rollback, verification and creation authorities must be separate');
|
|
291
|
+
}
|
|
292
|
+
if (capability.keyId === creation.keyId || capability.keyId === verification.authority.keyId) {
|
|
293
|
+
throw new Error('plugin-control-plane: revision adoption signing key must differ from acceptance and creation keys');
|
|
294
|
+
}
|
|
295
|
+
const revisionKey = createPublicKey(revisionCapabilityPublicKey(capability)).export({ format: 'der', type: 'spki' });
|
|
296
|
+
const creationKey = createPublicKey(creationCapabilityPublicKey(creation)).export({ format: 'der', type: 'spki' });
|
|
297
|
+
const verificationKey = createPublicKey(verification.publicKey).export({ format: 'der', type: 'spki' });
|
|
298
|
+
if (revisionKey.equals(creationKey) || revisionKey.equals(verificationKey)) {
|
|
299
|
+
throw new Error('plugin-control-plane: revision adoption signing key material must be separate');
|
|
300
|
+
}
|
|
301
|
+
}
|
|
244
302
|
if (![config.catalogPath, config.statePath, config.trustPath].every(isAbsolute))
|
|
245
303
|
throw new Error('plugin-control-plane: catalogPath, statePath and trustPath must be absolute');
|
|
246
304
|
return config;
|
|
@@ -264,11 +322,13 @@ export class PluginControlPlaneService extends Service {
|
|
|
264
322
|
nativeSourceRunner;
|
|
265
323
|
sourceRuntimes = new Set();
|
|
266
324
|
creationCapabilityRuntime;
|
|
325
|
+
revisionCapabilityRuntime;
|
|
267
326
|
creationCapabilityRuntimes = new Set();
|
|
268
327
|
creationCapabilityFlights = new Set();
|
|
269
328
|
creationCapabilityReconcileRequested = false;
|
|
270
329
|
growthRunProducer;
|
|
271
330
|
growthProviderDraining = false;
|
|
331
|
+
workItemCreationFrame;
|
|
272
332
|
constructor(ctx, input) {
|
|
273
333
|
super(ctx, 'pluginControlPlane');
|
|
274
334
|
this.config = normalizeControlPlaneConfig(input);
|
|
@@ -437,9 +497,14 @@ export class PluginControlPlaneService extends Service {
|
|
|
437
497
|
},
|
|
438
498
|
delivery: {
|
|
439
499
|
validateOwnerRoute: input => delivery().validateOwnerRoute(input),
|
|
500
|
+
inspectOwnerRouteState: input => delivery().inspectOwnerRouteState(input),
|
|
440
501
|
inspectOwnerForegroundLearningTask: input => delivery().inspectOwnerForegroundLearningTask(input),
|
|
441
502
|
},
|
|
442
503
|
automations: {
|
|
504
|
+
inspectSystemOwnedRetirement: input => automations().inspectSystemOwnedRetirement(input),
|
|
505
|
+
onSchedulerWake: listener => automations().onSchedulerWake(listener),
|
|
506
|
+
retireSystemOwned: input => automations().retireSystemOwned(input),
|
|
507
|
+
inspectSystemOwned: input => automations().inspectSystemOwned(input),
|
|
443
508
|
registerHostExecutor: input => automations().registerHostExecutor(input),
|
|
444
509
|
reconcileSystem: input => automations().reconcileSystem(input),
|
|
445
510
|
inspectSystemOwnedActivation: input => automations().inspectSystemOwnedActivation(input),
|
|
@@ -495,9 +560,14 @@ export class PluginControlPlaneService extends Service {
|
|
|
495
560
|
},
|
|
496
561
|
delivery: {
|
|
497
562
|
validateOwnerRoute: input => delivery().validateOwnerRoute(input),
|
|
563
|
+
inspectOwnerRouteState: input => delivery().inspectOwnerRouteState(input),
|
|
498
564
|
inspectOwnerForegroundLearningTask: input => delivery().inspectOwnerForegroundLearningTask(input),
|
|
499
565
|
},
|
|
500
566
|
automations: {
|
|
567
|
+
inspectSystemOwnedRetirement: input => automations().inspectSystemOwnedRetirement(input),
|
|
568
|
+
onSchedulerWake: listener => automations().onSchedulerWake(listener),
|
|
569
|
+
retireSystemOwned: input => automations().retireSystemOwned(input),
|
|
570
|
+
inspectSystemOwned: input => automations().inspectSystemOwned(input),
|
|
501
571
|
registerHostExecutor: input => automations().registerHostExecutor(input),
|
|
502
572
|
reconcileSystem: input => automations().reconcileSystem(input),
|
|
503
573
|
inspectSystemOwnedActivation: input => automations().inspectSystemOwnedActivation(input),
|
|
@@ -558,10 +628,14 @@ export class PluginControlPlaneService extends Service {
|
|
|
558
628
|
if (Object.keys(owner).some(key => prepared.reference.owner[key] !== owner[key])) {
|
|
559
629
|
throw new Error('created capability owner differs from the finite grant');
|
|
560
630
|
}
|
|
561
|
-
return { certificate, artifact: prepared.artifact, owner };
|
|
631
|
+
return { certificate, artifact: prepared.artifact, owner, ...(prepared.protocol === 'dsh-prepared-creation/v2' ? { sourceRun: prepared.plan.creation.growthRun } : {}) };
|
|
562
632
|
},
|
|
563
633
|
recheck: async (planId, signal) => {
|
|
564
634
|
await this.inspectPreparedCreationReviewContext(planId, signal);
|
|
635
|
+
if (this.config.sourceBuild && 'backend' in this.config.sourceBuild
|
|
636
|
+
&& this.config.sourceBuild.backend === 'linux-native') {
|
|
637
|
+
await this.inspectPreparedCreationLinuxBuild(planId, signal);
|
|
638
|
+
}
|
|
565
639
|
if (!this.inspectVerifiedCreation(planId))
|
|
566
640
|
throw new Error('created capability independent certificate changed');
|
|
567
641
|
},
|
|
@@ -584,7 +658,7 @@ export class PluginControlPlaneService extends Service {
|
|
|
584
658
|
return source;
|
|
585
659
|
},
|
|
586
660
|
inspectRetained: record => this.withRetainedCreatedCapabilityCurrent(record, () => ({
|
|
587
|
-
certificate: record.certificate, artifact: record.artifact, owner: config.owner
|
|
661
|
+
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
662
|
})),
|
|
589
663
|
recheckRetained: async (record, signal) => {
|
|
590
664
|
signal.throwIfAborted();
|
|
@@ -613,9 +687,6 @@ export class PluginControlPlaneService extends Service {
|
|
|
613
687
|
},
|
|
614
688
|
} });
|
|
615
689
|
this.creationCapabilityRuntimes.add(capabilities);
|
|
616
|
-
await capabilities.start();
|
|
617
|
-
this.abort.signal.throwIfAborted();
|
|
618
|
-
this.creationCapabilityRuntime = capabilities;
|
|
619
690
|
unsubscribeTaskChanges = evaluation.onTrustedTaskChange(() => this.reconcileCreatedCapabilities());
|
|
620
691
|
}
|
|
621
692
|
catch (error) {
|
|
@@ -629,17 +700,125 @@ export class PluginControlPlaneService extends Service {
|
|
|
629
700
|
throw error;
|
|
630
701
|
}
|
|
631
702
|
}
|
|
703
|
+
let revisionCapabilities;
|
|
704
|
+
if (this.config.revisionCapabilities) {
|
|
705
|
+
const config = this.config.revisionCapabilities;
|
|
706
|
+
const delivery = current('delivery');
|
|
707
|
+
if (typeof delivery.validateOwnerAgentForRoute !== 'function') {
|
|
708
|
+
throw new Error('plugin-control-plane: revision adoption requires authenticated live owner Agent admission');
|
|
709
|
+
}
|
|
710
|
+
const journal = new RevisionCapabilityJournal({ path: join(this.config.statePath, 'revision-adoptions.sqlite'), config });
|
|
711
|
+
try {
|
|
712
|
+
revisionCapabilities = new RevisionCapabilityRuntime({ ctx: jobsCtx, config, journal,
|
|
713
|
+
regressionRequired: this.config.revisionRegressions !== undefined,
|
|
714
|
+
ports: {
|
|
715
|
+
inspect: planId => {
|
|
716
|
+
const certificate = this.inspectVerifiedRevision(planId);
|
|
717
|
+
if (!certificate)
|
|
718
|
+
throw new Error('revision adoption requires a fresh independent certificate');
|
|
719
|
+
const prepared = this.inspectPreparedRevision(planId);
|
|
720
|
+
const source = this.store.readRevisionSource(planId);
|
|
721
|
+
if (!source)
|
|
722
|
+
throw new Error('revision adoption source archive unavailable');
|
|
723
|
+
const regression = this.config.revisionRegressions === undefined
|
|
724
|
+
? undefined : this.inspectVerifiedRevisionRegression(planId);
|
|
725
|
+
return { certificate, artifact: Buffer.from(prepared.artifact), source,
|
|
726
|
+
plan: { id: prepared.plan.id, digest: prepared.plan.digest, name: prepared.plan.name },
|
|
727
|
+
...(regression === undefined ? {} : { regression }), owner: config.owner };
|
|
728
|
+
},
|
|
729
|
+
recheck: async (planId, signal) => {
|
|
730
|
+
await this.inspectPreparedRevisionReviewContext(planId, signal);
|
|
731
|
+
if (this.config.sourceBuild && 'backend' in this.config.sourceBuild
|
|
732
|
+
&& this.config.sourceBuild.backend === 'linux-native') {
|
|
733
|
+
await this.inspectPreparedRevisionLinuxBuild(planId, signal);
|
|
734
|
+
}
|
|
735
|
+
if (!this.inspectVerifiedRevision(planId))
|
|
736
|
+
throw new Error('revision adoption independent certificate changed');
|
|
737
|
+
},
|
|
738
|
+
parent: planId => {
|
|
739
|
+
const adoption = capabilities?.inspectAdoptionVersion(planId);
|
|
740
|
+
if (!adoption)
|
|
741
|
+
throw new Error('revision adoption parent adoption unavailable');
|
|
742
|
+
return adoption;
|
|
743
|
+
},
|
|
744
|
+
recheckParent: async (planId, signal) => {
|
|
745
|
+
if (!capabilities)
|
|
746
|
+
throw new Error('revision adoption parent adoption unavailable');
|
|
747
|
+
await capabilities.recheckAdoptionVersion(planId, signal);
|
|
748
|
+
},
|
|
749
|
+
inFlightParentCalls: planId => capabilities?.inspectInFlightCalls(planId) ?? 0,
|
|
750
|
+
suspendParent: async (planId) => {
|
|
751
|
+
if (!capabilities)
|
|
752
|
+
throw new Error('revision adoption parent adoption unavailable');
|
|
753
|
+
await capabilities.suspendEntry(planId);
|
|
754
|
+
},
|
|
755
|
+
resumeParent: async (planId) => {
|
|
756
|
+
if (!capabilities)
|
|
757
|
+
throw new Error('revision adoption parent adoption unavailable');
|
|
758
|
+
await capabilities.resumeEntry(planId);
|
|
759
|
+
},
|
|
760
|
+
runVersion: input => {
|
|
761
|
+
if (!capabilities)
|
|
762
|
+
throw new Error('revision adoption runner unavailable');
|
|
763
|
+
return capabilities.runRevisionArtifact(input);
|
|
764
|
+
},
|
|
765
|
+
recheckExecution: async (certificate, signal) => {
|
|
766
|
+
if (!capabilities)
|
|
767
|
+
throw new Error('revision adoption runner unavailable');
|
|
768
|
+
await capabilities.verifyArtifactExecution(certificate, signal);
|
|
769
|
+
},
|
|
770
|
+
matchesExecution: certificate => capabilities?.matchesArtifactExecution(certificate) === true,
|
|
771
|
+
assertCaller: execution => {
|
|
772
|
+
if (!execution.agent)
|
|
773
|
+
throw new Error('revision capability requires an authenticated owner Agent');
|
|
774
|
+
const receipt = delivery.validateOwnerAgentForRoute(execution.agent, config.owner.authorityId);
|
|
775
|
+
if (!receipt || Object.keys(config.owner).some(key => receipt[key] !== config.owner[key])) {
|
|
776
|
+
throw new Error('revision capability caller is not the current authorized owner');
|
|
777
|
+
}
|
|
778
|
+
},
|
|
779
|
+
} });
|
|
780
|
+
}
|
|
781
|
+
catch (error) {
|
|
782
|
+
journal.close();
|
|
783
|
+
throw error;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
const closeRevision = async () => {
|
|
787
|
+
const currentRuntime = revisionCapabilities;
|
|
788
|
+
revisionCapabilities = undefined;
|
|
789
|
+
if (this.revisionCapabilityRuntime === currentRuntime)
|
|
790
|
+
this.revisionCapabilityRuntime = undefined;
|
|
791
|
+
if (currentRuntime)
|
|
792
|
+
await currentRuntime.close();
|
|
793
|
+
};
|
|
632
794
|
const sourceBuild = this.config.sourceBuild;
|
|
633
795
|
let nativeRunner;
|
|
634
796
|
try {
|
|
635
|
-
nativeRunner = 'backend' in sourceBuild ?
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
797
|
+
nativeRunner = !('backend' in sourceBuild) ? undefined : sourceBuild.backend === 'macos-seatbelt'
|
|
798
|
+
? { backend: 'macos-seatbelt', runner: new NativeMacVerifierRunner({ backend: 'macos-seatbelt',
|
|
799
|
+
stateRoot: sourceBuild.stateRoot, authorityDigest: sourceBuild.authorityDigest,
|
|
800
|
+
runtime: sourceBuild.runtime, source: NATIVE_SOURCE_COMPILER, expiresAt: sourceBuild.expiresAt,
|
|
801
|
+
maxRuns: sourceBuild.maxRuns, maxTotalDurationMs: sourceBuild.maxTotalDurationMs,
|
|
802
|
+
maxDurationMs: sourceBuild.timeoutMs, maxOutputBytes: sourceBuild.outputBytes }) }
|
|
803
|
+
: { backend: 'linux-native', runner: new LinuxRepositoryRunner({ backend: 'linux-native',
|
|
804
|
+
stateRoot: sourceBuild.stateRoot, authorityDigest: sourceBuild.authorityDigest,
|
|
805
|
+
runtime: sourceBuild.runtime, cgroupRoots: sourceBuild.cgroupRoots, expiresAt: sourceBuild.expiresAt,
|
|
806
|
+
maxRuns: sourceBuild.maxRuns, maxTotalDurationMs: sourceBuild.maxTotalDurationMs,
|
|
807
|
+
...(sourceBuild.stateSeal === undefined ? {} : { stateSeal: sourceBuild.stateSeal }),
|
|
808
|
+
...(sourceBuild.userNamespace === undefined ? {} : { userNamespace: sourceBuild.userNamespace,
|
|
809
|
+
systemSnapshot: sourceBuild.systemSnapshot }),
|
|
810
|
+
limits: { maxDurationMs: sourceBuild.timeoutMs, maxInputBytes: 64 * 1024 ** 2,
|
|
811
|
+
maxOutputBytes: sourceBuild.outputBytes, maxArtifactBytes: 32 * 1024 ** 2, maxFiles: 128,
|
|
812
|
+
memoryMiB: sourceBuild.memoryMiB, cpus: sourceBuild.cpus, pidsLimit: sourceBuild.pidsLimit,
|
|
813
|
+
workspaceMiB: sourceBuild.workspaceMiB, workspaceInodes: sourceBuild.workspaceInodes,
|
|
814
|
+
tmpMiB: sourceBuild.temporaryMiB, tmpInodes: sourceBuild.temporaryInodes, inputMiB: 65, inputInodes: 16 } }) };
|
|
640
815
|
}
|
|
641
816
|
catch (error) {
|
|
642
817
|
unsubscribeTaskChanges?.();
|
|
818
|
+
try {
|
|
819
|
+
await closeRevision();
|
|
820
|
+
}
|
|
821
|
+
catch { /* original error wins */ }
|
|
643
822
|
if (capabilities) {
|
|
644
823
|
await capabilities.close();
|
|
645
824
|
this.creationCapabilityRuntimes.delete(capabilities);
|
|
@@ -648,7 +827,13 @@ export class PluginControlPlaneService extends Service {
|
|
|
648
827
|
}
|
|
649
828
|
this.nativeSourceRunner = nativeRunner;
|
|
650
829
|
let runtime;
|
|
830
|
+
const preparationGeneration = {};
|
|
651
831
|
try {
|
|
832
|
+
// Restored fresh adoptions read their original Linux build receipt.
|
|
833
|
+
// Their source runner must exist before capability reconciliation.
|
|
834
|
+
await capabilities?.start();
|
|
835
|
+
await revisionCapabilities?.start();
|
|
836
|
+
this.abort.signal.throwIfAborted();
|
|
652
837
|
runtime = new SourceJobRuntime({ config: this.config.sourceJobs, build: sourceBuild, statePath: this.config.statePath, store: this.store,
|
|
653
838
|
ports: {
|
|
654
839
|
automations: {
|
|
@@ -661,6 +846,7 @@ export class PluginControlPlaneService extends Service {
|
|
|
661
846
|
},
|
|
662
847
|
delivery: { validateOwnerRoute: request => current('delivery').validateOwnerRoute(request) },
|
|
663
848
|
}, withGapSourceFence: (gapId, owner, callback) => this.taskGaps.withCurrent(gapId, owner, callback),
|
|
849
|
+
withWorkItemGrowthRunFence: (run, gapId, owner, generation, callback) => this.withWorkItemGrowthFrame(run, gapId, owner, generation, callback),
|
|
664
850
|
assertGrowthRun: (run, gapId, owner, generation) => this.assertSourceGrowthRun(run, gapId, owner, generation),
|
|
665
851
|
...(this.config.revisionVerifications ? { inspectRevisionParent: this.inspectRevisionParent,
|
|
666
852
|
verifyPreparedRevision: (job, signal) => this.verifyPreparedRevision(job, signal, () => jobsCtx.get('assistantVerifier')) } : {}),
|
|
@@ -672,6 +858,12 @@ export class PluginControlPlaneService extends Service {
|
|
|
672
858
|
throw new Error('created capability has no prepared source plan');
|
|
673
859
|
await capabilities.adopt(job.planId, signal);
|
|
674
860
|
} } : {}),
|
|
861
|
+
...(revisionCapabilities ? { revisionAdoptionEligible: (planId) => revisionCapabilities.eligible(planId),
|
|
862
|
+
adoptVerifiedRevision: async (job, signal) => {
|
|
863
|
+
if (!job.planId)
|
|
864
|
+
throw new Error('revision adoption has no prepared source plan');
|
|
865
|
+
await revisionCapabilities.adopt(job.planId, signal);
|
|
866
|
+
} } : {}),
|
|
675
867
|
...(this.config.sourceApprovals ? { approvePrepared: async (job, signal) => {
|
|
676
868
|
if (!job.planId)
|
|
677
869
|
throw new Error('source job has no prepared plan');
|
|
@@ -694,15 +886,21 @@ export class PluginControlPlaneService extends Service {
|
|
|
694
886
|
throw new Error('source job has no released plan');
|
|
695
887
|
await this.adoptOwnerSourceRelease({ sourcePlanId: job.planId, signal, expectedTrustDigest: job.intent.trustDigest, assertCurrent });
|
|
696
888
|
} } : {}),
|
|
697
|
-
trust: () => this.boundTrust(), prepare: (job, signal, assertCurrent) => this.prepareSourceJob(job, signal, assertCurrent),
|
|
698
|
-
...(nativeRunner ? {
|
|
889
|
+
trust: () => this.boundTrust(), prepare: (job, signal, assertCurrent) => this.prepareSourceJob(job, signal, assertCurrent, nativeRunner?.backend === 'linux-native' ? { runner: nativeRunner.runner, generation: preparationGeneration } : undefined),
|
|
890
|
+
...(nativeRunner?.backend === 'linux-native' ? { isPreparationNotStarted: (error, job) => consumeCleanedLinuxSourcePreflight(error, job, preparationGeneration),
|
|
891
|
+
deterministicBuildFailure: (error, job) => consumeCleanedLinuxSourceBuildFailure(error, job, preparationGeneration) } : {}),
|
|
892
|
+
...(nativeRunner ? { inspectNativeQuiescence: (key) => nativeRunner.runner.inspectQuiescence(key) } : {}),
|
|
699
893
|
});
|
|
700
894
|
}
|
|
701
895
|
catch (error) {
|
|
702
896
|
if (this.nativeSourceRunner === nativeRunner)
|
|
703
897
|
this.nativeSourceRunner = undefined;
|
|
704
|
-
await nativeRunner?.close();
|
|
898
|
+
await nativeRunner?.runner.close();
|
|
705
899
|
unsubscribeTaskChanges?.();
|
|
900
|
+
try {
|
|
901
|
+
await closeRevision();
|
|
902
|
+
}
|
|
903
|
+
catch { /* original error wins */ }
|
|
706
904
|
if (capabilities) {
|
|
707
905
|
await capabilities.close();
|
|
708
906
|
this.creationCapabilityRuntimes.delete(capabilities);
|
|
@@ -710,6 +908,8 @@ export class PluginControlPlaneService extends Service {
|
|
|
710
908
|
throw error;
|
|
711
909
|
}
|
|
712
910
|
this.sourceRuntimes.add(runtime);
|
|
911
|
+
this.creationCapabilityRuntime = capabilities;
|
|
912
|
+
this.revisionCapabilityRuntime = revisionCapabilities;
|
|
713
913
|
const close = async () => {
|
|
714
914
|
unsubscribeTaskChanges?.();
|
|
715
915
|
if (this.sourceRuntime === runtime)
|
|
@@ -726,18 +926,30 @@ export class PluginControlPlaneService extends Service {
|
|
|
726
926
|
this.sourceRuntimes.delete(runtime);
|
|
727
927
|
if (this.creationCapabilityRuntime === capabilities)
|
|
728
928
|
this.creationCapabilityRuntime = undefined;
|
|
729
|
-
|
|
929
|
+
let revisionCloseError;
|
|
930
|
+
try {
|
|
931
|
+
await closeRevision();
|
|
932
|
+
}
|
|
933
|
+
catch (error) {
|
|
934
|
+
revisionCloseError = error;
|
|
935
|
+
}
|
|
936
|
+
const results = await Promise.allSettled([nativeRunner?.runner.close(), capabilities?.close()]);
|
|
730
937
|
if (capabilities)
|
|
731
938
|
this.creationCapabilityRuntimes.delete(capabilities);
|
|
732
939
|
const failures = results.filter((result) => result.status === 'rejected');
|
|
733
|
-
if (closeError !== undefined || failures.length) {
|
|
734
|
-
throw new AggregateError([...(closeError === undefined ? [] : [closeError]),
|
|
940
|
+
if (closeError !== undefined || revisionCloseError !== undefined || failures.length) {
|
|
941
|
+
throw new AggregateError([...(closeError === undefined ? [] : [closeError]),
|
|
942
|
+
...(revisionCloseError === undefined ? [] : [revisionCloseError]), ...failures.map(result => result.reason)], 'source runtime cleanup failed');
|
|
735
943
|
}
|
|
736
944
|
};
|
|
737
945
|
try {
|
|
738
946
|
runtime.start();
|
|
739
947
|
this.abort.signal.throwIfAborted();
|
|
740
948
|
this.sourceRuntime = runtime;
|
|
949
|
+
// Producer/binding readiness can arrive while capabilities.start() is
|
|
950
|
+
// awaiting its source checks, before these runtime fields are public.
|
|
951
|
+
// Reconcile once after publication so that notification is not lost.
|
|
952
|
+
this.reconcileCreatedCapabilities();
|
|
741
953
|
return close;
|
|
742
954
|
}
|
|
743
955
|
catch (error) {
|
|
@@ -782,6 +994,10 @@ export class PluginControlPlaneService extends Service {
|
|
|
782
994
|
return discover(loaded.catalog, capability);
|
|
783
995
|
}
|
|
784
996
|
/** Host only: reread the exact source; no caller text or ratings are admitted. */
|
|
997
|
+
recordOwnerWorkItemFailureGap = (source) => {
|
|
998
|
+
this.abort.signal.throwIfAborted();
|
|
999
|
+
return this.taskGaps.recordWorkItem(source);
|
|
1000
|
+
};
|
|
785
1001
|
recordOwnerTaskFailureGap = (source) => {
|
|
786
1002
|
this.abort.signal.throwIfAborted();
|
|
787
1003
|
return this.taskGaps.record(source);
|
|
@@ -1021,7 +1237,8 @@ export class PluginControlPlaneService extends Service {
|
|
|
1021
1237
|
/** Host-only durable Usage reader; the caller's binding is always an expectation. */
|
|
1022
1238
|
registerSourceGrowthRunProducer = (producer) => {
|
|
1023
1239
|
this.abort.signal.throwIfAborted();
|
|
1024
|
-
if (producer
|
|
1240
|
+
if (!['assistant-growth-source-run-producer/v1', 'assistant-growth-source-run-producer/v2'].includes(producer?.protocol) || typeof producer.inspect !== 'function'
|
|
1241
|
+
|| producer.protocol === 'assistant-growth-source-run-producer/v2' && typeof producer.withWorkItemRunFence !== 'function'
|
|
1025
1242
|
|| this.growthRunProducer !== undefined || this.growthProviderDraining)
|
|
1026
1243
|
throw new Error('plugin-control-plane: source growth run producer is invalid or already registered');
|
|
1027
1244
|
this.growthRunProducer = producer;
|
|
@@ -1081,6 +1298,44 @@ export class PluginControlPlaneService extends Service {
|
|
|
1081
1298
|
this.abort.signal.throwIfAborted();
|
|
1082
1299
|
return this.creationCapabilityRuntime?.inspectCallEvidence(planId) ?? [];
|
|
1083
1300
|
};
|
|
1301
|
+
/** Host-only read-only revision adoption readiness; undefined when not configured or not admitting. */
|
|
1302
|
+
inspectRevisionAdoptionReadiness = () => {
|
|
1303
|
+
this.abort.signal.throwIfAborted();
|
|
1304
|
+
return this.revisionCapabilityRuntime?.inspectReadiness();
|
|
1305
|
+
};
|
|
1306
|
+
/** Host-only current stable entry version; no certificate, artifact bytes or key material escape. */
|
|
1307
|
+
inspectRevisionCapabilityEntry = (entryId) => {
|
|
1308
|
+
this.abort.signal.throwIfAborted();
|
|
1309
|
+
return this.revisionCapabilityRuntime?.inspectEntry(entryId);
|
|
1310
|
+
};
|
|
1311
|
+
/** Host-only bounded call attribution of one stable entry, including the exact version per call. */
|
|
1312
|
+
inspectRevisionCapabilityCalls = (entryId) => {
|
|
1313
|
+
this.abort.signal.throwIfAborted();
|
|
1314
|
+
return this.revisionCapabilityRuntime?.inspectCallEvidence(entryId) ?? [];
|
|
1315
|
+
};
|
|
1316
|
+
/**
|
|
1317
|
+
* Host-only authenticated-owner exact parent rollback under its own finite grant. The parent
|
|
1318
|
+
* adoption receipt must still be current and the entry must be on the exact expected revision.
|
|
1319
|
+
*/
|
|
1320
|
+
rollbackRevisionCapabilityAdoption = async (input) => {
|
|
1321
|
+
this.abort.signal.throwIfAborted();
|
|
1322
|
+
const runtime = this.revisionCapabilityRuntime;
|
|
1323
|
+
const config = this.config.revisionCapabilities;
|
|
1324
|
+
if (!runtime || !config)
|
|
1325
|
+
throw new Error('revision adoption is not configured');
|
|
1326
|
+
const delivery = this.ctx.get('assistantDelivery', false);
|
|
1327
|
+
if (!delivery || typeof delivery.validateOwnerRoute !== 'function')
|
|
1328
|
+
throw new Error('revision rollback requires Delivery owner validation');
|
|
1329
|
+
const receipt = delivery.validateOwnerRoute({ authorityId: config.owner.authorityId, principalId: config.owner.principalId,
|
|
1330
|
+
workspace: config.owner.workspace, agentPreset: config.owner.agentPreset });
|
|
1331
|
+
if (!receipt || receipt.authorityId !== config.owner.authorityId || receipt.authorityHash !== config.owner.authorityHash
|
|
1332
|
+
|| receipt.principalId !== config.owner.principalId || receipt.principalRecordId !== config.owner.principalRecordId
|
|
1333
|
+
|| receipt.principalVersion !== config.owner.principalVersion || receipt.workspace !== config.owner.workspace
|
|
1334
|
+
|| receipt.agentPreset !== config.owner.agentPreset) {
|
|
1335
|
+
throw new Error('revision rollback caller is not the current authorized owner');
|
|
1336
|
+
}
|
|
1337
|
+
return runtime.rollback({ entryId: input.entryId, expectedCurrentPlanId: input.expectedVersionPlanId });
|
|
1338
|
+
};
|
|
1084
1339
|
/** Host-only historical source data. It grants no continuation, activation or replacement. */
|
|
1085
1340
|
inspectCreatedCapabilitySource = (planId) => {
|
|
1086
1341
|
this.abort.signal.throwIfAborted();
|
|
@@ -1142,29 +1397,109 @@ export class PluginControlPlaneService extends Service {
|
|
|
1142
1397
|
};
|
|
1143
1398
|
/** Host-only current association of exact adopted call versions with authenticated task heads. */
|
|
1144
1399
|
inspectCreatedCapabilityTaskAssociations = (planId) => {
|
|
1400
|
+
const input = this.createdCapabilityFeedbackInput(planId);
|
|
1401
|
+
if (!input)
|
|
1402
|
+
return [];
|
|
1403
|
+
try {
|
|
1404
|
+
return inspectCreationCapabilityTaskAssociations(input());
|
|
1405
|
+
}
|
|
1406
|
+
catch {
|
|
1407
|
+
return [];
|
|
1408
|
+
}
|
|
1409
|
+
};
|
|
1410
|
+
/** Host-only descriptive ordinary-use counts over the same fenced associations; never causal or adoption evidence. */
|
|
1411
|
+
inspectCreatedCapabilityOrdinaryUseReport = (planId) => {
|
|
1412
|
+
const input = this.createdCapabilityFeedbackInput(planId);
|
|
1413
|
+
if (!input)
|
|
1414
|
+
return undefined;
|
|
1415
|
+
try {
|
|
1416
|
+
return inspectCreationCapabilityOrdinaryUseReport(input());
|
|
1417
|
+
}
|
|
1418
|
+
catch {
|
|
1419
|
+
return undefined;
|
|
1420
|
+
}
|
|
1421
|
+
};
|
|
1422
|
+
/** Host-only observational before/after attribution of one adoption over the owner's certified tasks; never causal. */
|
|
1423
|
+
inspectCreatedCapabilityAvailabilityAttribution = (planId, options = {}) => {
|
|
1424
|
+
const input = this.createdCapabilityFeedbackInput(planId);
|
|
1425
|
+
if (!input)
|
|
1426
|
+
return undefined;
|
|
1427
|
+
try {
|
|
1428
|
+
return inspectCreationCapabilityAvailabilityAttribution({ ...input(),
|
|
1429
|
+
otherAdoptedAt: this.creationCapabilityRuntime?.inspectOtherAdoptionTimes(planId) ?? [],
|
|
1430
|
+
...(options.windowMs === undefined ? {} : { windowMs: options.windowMs }) });
|
|
1431
|
+
}
|
|
1432
|
+
catch {
|
|
1433
|
+
return undefined;
|
|
1434
|
+
}
|
|
1435
|
+
};
|
|
1436
|
+
/**
|
|
1437
|
+
* Host-only longitudinal ordinary-use series of one adoption in fixed windows with a same-scope session-cohort
|
|
1438
|
+
* control and a model-stratified difference-in-differences estimate; observational, never causal. Pure read.
|
|
1439
|
+
*/
|
|
1440
|
+
inspectCreatedCapabilityOrdinaryUseTrend = (planId, options = {}) => {
|
|
1441
|
+
const input = this.createdCapabilityFeedbackInput(planId);
|
|
1442
|
+
if (!input)
|
|
1443
|
+
return undefined;
|
|
1444
|
+
try {
|
|
1445
|
+
return inspectCreationCapabilityOrdinaryUseTrend({ ...input(),
|
|
1446
|
+
otherAdoptedAt: this.creationCapabilityRuntime?.inspectOtherAdoptionTimes(planId) ?? [],
|
|
1447
|
+
...(options.windowMs === undefined ? {} : { windowMs: options.windowMs }),
|
|
1448
|
+
...(options.preWindows === undefined ? {} : { preWindows: options.preWindows }),
|
|
1449
|
+
...(options.maxPostWindows === undefined ? {} : { maxPostWindows: options.maxPostWindows }) });
|
|
1450
|
+
}
|
|
1451
|
+
catch {
|
|
1452
|
+
return undefined;
|
|
1453
|
+
}
|
|
1454
|
+
};
|
|
1455
|
+
/**
|
|
1456
|
+
* Host-only, owner-invoked: seal the current ordinary-use trend of one adoption into the append-only,
|
|
1457
|
+
* content-addressed snapshot ledger. Re-sealing an unchanged trend returns the existing row (`created: false`);
|
|
1458
|
+
* rows are never rewritten. Still observational (`causal: false`); never adoption or revocation evidence.
|
|
1459
|
+
*/
|
|
1460
|
+
sealCreatedCapabilityOrdinaryUseTrendSnapshot = (planId, options = {}) => {
|
|
1461
|
+
const trend = this.inspectCreatedCapabilityOrdinaryUseTrend(planId, options);
|
|
1462
|
+
if (!trend)
|
|
1463
|
+
return undefined;
|
|
1464
|
+
try {
|
|
1465
|
+
return this.store.sealCreatedCapabilityTrendSnapshot(trend);
|
|
1466
|
+
}
|
|
1467
|
+
catch {
|
|
1468
|
+
return undefined;
|
|
1469
|
+
}
|
|
1470
|
+
};
|
|
1471
|
+
/** Host-only bounded newest-first read of sealed trend snapshots; undefined on invalid query or any tampered row. */
|
|
1472
|
+
inspectCreatedCapabilityOrdinaryUseTrendSnapshots = (query = {}) => {
|
|
1473
|
+
try {
|
|
1474
|
+
this.abort.signal.throwIfAborted();
|
|
1475
|
+
return this.store.listCreatedCapabilityTrendSnapshots(query);
|
|
1476
|
+
}
|
|
1477
|
+
catch {
|
|
1478
|
+
return undefined;
|
|
1479
|
+
}
|
|
1480
|
+
};
|
|
1481
|
+
/** The historical read is deferred so callers keep it inside their own failure boundary. */
|
|
1482
|
+
createdCapabilityFeedbackInput(planId) {
|
|
1145
1483
|
this.abort.signal.throwIfAborted();
|
|
1146
1484
|
const snapshot = this.creationCapabilityRuntime?.inspectAssociationEvidence(planId);
|
|
1147
1485
|
if (!snapshot || !this.config.creationCapabilities)
|
|
1148
|
-
return
|
|
1486
|
+
return undefined;
|
|
1149
1487
|
const owner = this.config.creationCapabilities.owner;
|
|
1150
1488
|
const evaluation = this.ctx.get('assistantEvaluation', false);
|
|
1151
1489
|
const delivery = this.ctx.get('assistantDelivery', false);
|
|
1152
1490
|
if (!evaluation || !delivery)
|
|
1153
|
-
return
|
|
1154
|
-
|
|
1491
|
+
return undefined;
|
|
1492
|
+
return () => {
|
|
1155
1493
|
const historical = this.store.getRetainedPreparedCreation(planId);
|
|
1156
|
-
return
|
|
1494
|
+
return { ...snapshot, owner, evaluation, delivery,
|
|
1157
1495
|
triggerInboxId: historical.reference.projection.subjectRef,
|
|
1158
1496
|
readSourceCurrent: () => {
|
|
1159
1497
|
this.assertCreatedCapabilityAssociationSource(snapshot.record);
|
|
1160
1498
|
const current = this.taskGaps.inspectCurrent(historical.plan.gapId, historical.reference.owner);
|
|
1161
1499
|
return { projection: current.canonical.projection, scopeWatermark: current.canonical.scopeWatermark };
|
|
1162
|
-
} }
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
return [];
|
|
1166
|
-
}
|
|
1167
|
-
};
|
|
1500
|
+
} };
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1168
1503
|
/** Exact historical certificate and source check without nesting an Evaluation writer fence. */
|
|
1169
1504
|
assertCreatedCapabilityAssociationSource(record) {
|
|
1170
1505
|
this.abort.signal.throwIfAborted();
|
|
@@ -1252,11 +1587,113 @@ export class PluginControlPlaneService extends Service {
|
|
|
1252
1587
|
!== ownerGrant[key])) {
|
|
1253
1588
|
throw new Error('created capability retained source binding changed');
|
|
1254
1589
|
}
|
|
1590
|
+
if (growth.protocol === 'assistant-growth/source-run/v2') {
|
|
1591
|
+
if (reference.schemaVersion !== 2 || growth.ownerDigest !== controlPlaneDigest(reference.owner))
|
|
1592
|
+
throw new Error('retained work item reference changed');
|
|
1593
|
+
assertWorkItemRunReference(growth, reference);
|
|
1594
|
+
this.assertWorkItemPinsLocal(growth, 'retained');
|
|
1595
|
+
return callback(growth);
|
|
1596
|
+
}
|
|
1597
|
+
return callback();
|
|
1598
|
+
});
|
|
1599
|
+
}
|
|
1600
|
+
inspectWorkItemPreparedCreationProtocol = () => {
|
|
1601
|
+
return !this.abort.signal.aborted && this.config.sourceJobs?.creation?.workItemFailures === true && this.sourceRuntime?.available() ? 'dsh-prepared-creation/v2' : undefined;
|
|
1602
|
+
};
|
|
1603
|
+
inspectWorkItemCreationReadiness = (input) => {
|
|
1604
|
+
try {
|
|
1605
|
+
this.abort.signal.throwIfAborted();
|
|
1606
|
+
const owner = inertCreationData(input).owner, config = this.config, source = this.sourceRuntime?.inspectWorkItemReadiness(owner);
|
|
1607
|
+
if (!source || !config.sourceJobs || !config.sourceBuild || config.sourceJobs.creation?.workItemFailures !== true || config.creationCapabilities?.workItemFailures !== true || !config.creationVerifications)
|
|
1608
|
+
return undefined;
|
|
1609
|
+
const delivery = this.ctx.get('assistantDelivery', false);
|
|
1610
|
+
const verifier = this.ctx.get('assistantVerifier', false);
|
|
1611
|
+
if (typeof delivery?.withOwnerWorkItemEngineeringContextFence !== 'function' || typeof verifier?.inspectWorkItemCreationReviewReadiness !== 'function' || typeof this.creationCapabilityRuntime?.inspectWorkItemAdoptionReadiness !== 'function')
|
|
1612
|
+
return undefined;
|
|
1613
|
+
const actual = delivery.validateOwnerRoute({ authorityId: owner.authorityId, principalId: owner.principalId, workspace: owner.workspace, agentPreset: owner.agentPreset });
|
|
1614
|
+
if (!isSourceOwnerContinuation(actual, owner) || Object.entries(config.creationCapabilities.owner).some(([key, value]) => actual[key] !== value))
|
|
1615
|
+
return undefined;
|
|
1616
|
+
const review = verifier.inspectWorkItemCreationReviewReadiness(), adoption = this.creationCapabilityRuntime.inspectWorkItemAdoptionReadiness();
|
|
1617
|
+
if (!review || !adoption || review.protocol !== 'assistant-growth/work-item-review-readiness/v1' || adoption.protocol !== 'assistant-growth/work-item-adoption-readiness/v1'
|
|
1618
|
+
|| 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'
|
|
1619
|
+
|| review.workItemFailures !== true || adoption.workItemFailures !== true || controlPlaneDigest(review.authority) !== controlPlaneDigest(config.creationVerifications.authority)
|
|
1620
|
+
|| !createPublicKey(review.publicKey).export({ type: 'spki', format: 'der' }).equals(createPublicKey(config.creationVerifications.publicKey).export({ type: 'spki', format: 'der' }))
|
|
1621
|
+
|| adoption.authority.authorityDigest !== creationCapabilityAuthorityDigest({ path: join(config.statePath, 'creation-adoptions.sqlite'), config: config.creationCapabilities })
|
|
1622
|
+
|| adoption.authority.authorityId !== config.creationCapabilities.authorityId || adoption.authority.expiresAt !== config.creationCapabilities.expiresAt)
|
|
1623
|
+
return undefined;
|
|
1624
|
+
const pins = { protocol: 'assistant-growth/work-item-authorities/v1', sourceJobs: { authorityId: source.authority.id, authorityDigest: source.authority.digest, expiresAt: source.authority.expiresAt },
|
|
1625
|
+
creationGrant: { id: source.creation.id, digest: controlPlaneDigest(source.creation), expiresAt: source.creation.expiresAt }, creationAcceptance: structuredClone(review.authority), adoption: structuredClone(adoption.authority) };
|
|
1626
|
+
validateWorkItemGrowthAuthorityPins(pins);
|
|
1627
|
+
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))
|
|
1628
|
+
return undefined;
|
|
1629
|
+
const remaining = { ...source.remaining, verifications: review.remainingVerifications, adoptions: adoption.remainingAdoptions };
|
|
1630
|
+
if (Object.values(remaining).some(count => !Number.isSafeInteger(count) || count < 0))
|
|
1631
|
+
return undefined;
|
|
1632
|
+
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 };
|
|
1633
|
+
}
|
|
1634
|
+
catch {
|
|
1635
|
+
return undefined;
|
|
1636
|
+
}
|
|
1637
|
+
};
|
|
1638
|
+
assertWorkItemPinsLocal(run, phase = 'current') {
|
|
1639
|
+
const jobs = this.config.sourceJobs, verification = this.config.creationVerifications, adoption = this.config.creationCapabilities;
|
|
1640
|
+
if (!jobs || !this.config.sourceBuild || jobs.creation?.workItemFailures !== true || !verification || adoption?.workItemFailures !== true
|
|
1641
|
+
|| run.authorities.sourceJobs.authorityId !== jobs.authorityId || run.authorities.sourceJobs.authorityDigest !== controlPlaneDigest({ config: jobs, build: this.config.sourceBuild }) || run.authorities.sourceJobs.expiresAt !== jobs.expiresAt
|
|
1642
|
+
|| run.authorities.creationGrant.id !== jobs.creation.id || run.authorities.creationGrant.digest !== controlPlaneDigest(jobs.creation) || run.authorities.creationGrant.expiresAt !== jobs.creation.expiresAt
|
|
1643
|
+
|| controlPlaneDigest(run.creationAcceptance) !== controlPlaneDigest(verification.authority) || controlPlaneDigest(run.authorities.creationAcceptance) !== controlPlaneDigest(verification.authority)
|
|
1644
|
+
|| 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
|
|
1645
|
+
|| (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))
|
|
1646
|
+
throw new Error('work item creation authority pins changed or expired');
|
|
1647
|
+
}
|
|
1648
|
+
assertWorkItemRunLocal(run, gapId, owner, generation) {
|
|
1649
|
+
const reference = this.store.getOwnerTaskFailureReference(gapId);
|
|
1650
|
+
if (!reference || reference.schemaVersion !== 2 || run.ownerDigest !== controlPlaneDigest(reference.owner) || !isSourceOwnerContinuation(owner, reference.owner))
|
|
1651
|
+
throw new Error('work item creation owner reference changed');
|
|
1652
|
+
assertWorkItemRunReference(run, reference);
|
|
1653
|
+
this.assertWorkItemPinsLocal(run);
|
|
1654
|
+
if (Date.now() >= run.expiresAt || generation && Date.now() >= run.generationDeadlineAt)
|
|
1655
|
+
throw new Error('work item source run expired');
|
|
1656
|
+
if (run.modelOrigin === 'inherited-owner-task' && this.workItemCreationFrame && controlPlaneDigest(this.workItemCreationFrame.source.task.proofIdentity.supplier) !== controlPlaneDigest(run.model))
|
|
1657
|
+
throw new Error('work item source supplier changed');
|
|
1658
|
+
}
|
|
1659
|
+
withWorkItemGrowthFrame(run, gapId, owner, generation, callback) {
|
|
1660
|
+
this.abort.signal.throwIfAborted();
|
|
1661
|
+
if (this.workItemCreationFrame) {
|
|
1662
|
+
if (this.workItemCreationFrame.gapId !== gapId || sourceGrowthRunDigest(this.workItemCreationFrame.run) !== sourceGrowthRunDigest(run))
|
|
1663
|
+
throw new Error('work item growth frame reentry');
|
|
1664
|
+
this.assertWorkItemRunLocal(run, gapId, owner, generation);
|
|
1255
1665
|
return callback();
|
|
1666
|
+
}
|
|
1667
|
+
const producer = this.growthRunProducer;
|
|
1668
|
+
if (!producer || producer.protocol !== 'assistant-growth-source-run-producer/v2')
|
|
1669
|
+
throw new SourceGrowthRunUnavailableError('work item growth producer unavailable');
|
|
1670
|
+
const readiness = this.inspectWorkItemCreationReadiness({ owner });
|
|
1671
|
+
if (!readiness)
|
|
1672
|
+
throw new SourceGrowthRunUnavailableError('work item growth peers unavailable');
|
|
1673
|
+
if (controlPlaneDigest(readiness.pins) !== controlPlaneDigest(run.authorities))
|
|
1674
|
+
throw new Error('work item growth authority pins changed');
|
|
1675
|
+
const source = this.taskGaps.engineeringSnapshot(gapId, owner);
|
|
1676
|
+
return producer.withWorkItemRunFence({ runId: run.runId, intentDigest: run.intentDigest, expectedGrowthRunDigest: sourceGrowthRunDigest(run) }, actual => {
|
|
1677
|
+
if (this.growthRunProducer !== producer || sourceGrowthRunDigest(actual) !== sourceGrowthRunDigest(run) || this.workItemCreationFrame)
|
|
1678
|
+
throw new Error('work item producer changed its true binding');
|
|
1679
|
+
this.workItemCreationFrame = { gapId, run: actual, source };
|
|
1680
|
+
try {
|
|
1681
|
+
return this.store.withOwnerTaskFailureGapAdmission(gapId, () => { this.assertWorkItemRunLocal(actual, gapId, owner, generation); return callback(); });
|
|
1682
|
+
}
|
|
1683
|
+
finally {
|
|
1684
|
+
this.workItemCreationFrame = undefined;
|
|
1685
|
+
}
|
|
1256
1686
|
});
|
|
1257
1687
|
}
|
|
1258
1688
|
assertSourceGrowthRun(expected, gapId, owner, generation) {
|
|
1259
1689
|
validateSourceGrowthRunBinding(expected);
|
|
1690
|
+
if (expected.protocol === 'assistant-growth/source-run/v2') {
|
|
1691
|
+
if (this.workItemCreationFrame) {
|
|
1692
|
+
this.assertWorkItemRunLocal(expected, gapId, owner, generation);
|
|
1693
|
+
return;
|
|
1694
|
+
}
|
|
1695
|
+
return this.withWorkItemGrowthFrame(expected, gapId, owner, generation, () => { });
|
|
1696
|
+
}
|
|
1260
1697
|
const producer = this.growthRunProducer;
|
|
1261
1698
|
if (!producer)
|
|
1262
1699
|
throw new SourceGrowthRunUnavailableError('source growth run producer unavailable');
|
|
@@ -1264,6 +1701,8 @@ export class PluginControlPlaneService extends Service {
|
|
|
1264
1701
|
if (!actual)
|
|
1265
1702
|
throw new Error('source growth run is absent or no longer current');
|
|
1266
1703
|
validateSourceGrowthRunBinding(actual);
|
|
1704
|
+
if (actual.protocol !== 'assistant-growth/source-run/v1')
|
|
1705
|
+
throw new Error('foreground source cannot use a work item run');
|
|
1267
1706
|
if (sourceGrowthRunDigest(actual) !== sourceGrowthRunDigest(expected)
|
|
1268
1707
|
|| expected.ownerDigest !== controlPlaneDigest(owner))
|
|
1269
1708
|
throw new Error('source growth run immutable binding changed');
|
|
@@ -1282,7 +1721,7 @@ export class PluginControlPlaneService extends Service {
|
|
|
1282
1721
|
throw new Error('source growth run creation acceptance policy changed or was not pinned before authoring');
|
|
1283
1722
|
}
|
|
1284
1723
|
const reference = this.store.getOwnerTaskFailureReference(gapId);
|
|
1285
|
-
if (!reference || expected.source.outcomeId !== reference.outcomeId
|
|
1724
|
+
if (!reference || reference.schemaVersion !== 1 || expected.source.outcomeId !== reference.outcomeId
|
|
1286
1725
|
|| expected.source.sourceDigest !== reference.sourceDigest
|
|
1287
1726
|
|| controlPlaneDigest(expected.source.projection) !== controlPlaneDigest(reference.projection)
|
|
1288
1727
|
|| expected.ownerDigest !== controlPlaneDigest(reference.owner))
|
|
@@ -1306,6 +1745,12 @@ export class PluginControlPlaneService extends Service {
|
|
|
1306
1745
|
|| Date.now() >= plan.expiresAt || !plan.creation || Date.now() >= plan.creation.grant.expiresAt) {
|
|
1307
1746
|
throw new Error('prepared creation package is unavailable for independent verification');
|
|
1308
1747
|
}
|
|
1748
|
+
if (reference.schemaVersion === 2) {
|
|
1749
|
+
const run = plan.creation.growthRun;
|
|
1750
|
+
if (run?.protocol !== 'assistant-growth/source-run/v2')
|
|
1751
|
+
throw new Error('work item prepared creation run unavailable');
|
|
1752
|
+
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) }));
|
|
1753
|
+
}
|
|
1309
1754
|
return this.taskGaps.withCurrent(plan.gapId, reference.owner, () => {
|
|
1310
1755
|
this.abort.signal.throwIfAborted();
|
|
1311
1756
|
const job = this.store.getPreparedSourceJob(plan.id);
|
|
@@ -1339,6 +1784,17 @@ export class PluginControlPlaneService extends Service {
|
|
|
1339
1784
|
const reference = this.store.getOwnerTaskFailureReference(plan.gapId);
|
|
1340
1785
|
if (!reference)
|
|
1341
1786
|
throw new Error('creation verification source unavailable');
|
|
1787
|
+
if (reference.schemaVersion === 2) {
|
|
1788
|
+
const run = plan.creation?.growthRun;
|
|
1789
|
+
if (run?.protocol !== 'assistant-growth/source-run/v2')
|
|
1790
|
+
throw new Error('work item verification run unavailable');
|
|
1791
|
+
return this.withWorkItemGrowthFrame(run, plan.gapId, reference.owner, false, () => {
|
|
1792
|
+
const current = this.store.getSourcePlan(input.planId), job = this.store.getPreparedSourceJob(input.planId);
|
|
1793
|
+
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)
|
|
1794
|
+
throw new Error('work item prepared verification exact binding changed');
|
|
1795
|
+
return callback(run);
|
|
1796
|
+
});
|
|
1797
|
+
}
|
|
1342
1798
|
return this.taskGaps.withCurrent(plan.gapId, reference.owner, () => {
|
|
1343
1799
|
this.abort.signal.throwIfAborted();
|
|
1344
1800
|
const current = this.store.getSourcePlan(input.planId);
|
|
@@ -1382,6 +1838,105 @@ export class PluginControlPlaneService extends Service {
|
|
|
1382
1838
|
current();
|
|
1383
1839
|
return review;
|
|
1384
1840
|
};
|
|
1841
|
+
/** Host-only read of the original successful build; never admits or replays a job. */
|
|
1842
|
+
inspectPreparedCreationLinuxBuild = async (planId, signal) => {
|
|
1843
|
+
const combined = AbortSignal.any([this.abort.signal, signal, AbortSignal.timeout(120_000)]);
|
|
1844
|
+
combined.throwIfAborted();
|
|
1845
|
+
const prepared = this.inspectPreparedCreation(planId), build = this.config.sourceBuild;
|
|
1846
|
+
const native = this.nativeSourceRunner, evidence = prepared.plan.preparedEvidence;
|
|
1847
|
+
if (!build || !('backend' in build) || build.backend !== 'linux-native' || !build.userNamespace || !build.systemSnapshot
|
|
1848
|
+
|| native?.backend !== 'linux-native'
|
|
1849
|
+
|| evidence?.schemaVersion !== 3 || evidence.boundary !== 'linux-native-repository-v1'
|
|
1850
|
+
|| !('backend' in prepared.job.intent.build) || prepared.job.intent.build.backend !== 'linux-native'
|
|
1851
|
+
|| prepared.job.intent.nativeResource?.backend !== 'linux-native' || prepared.job.intent.nativeResource.key !== prepared.job.id
|
|
1852
|
+
|| evidence.runtimeDigest !== linuxRuntimeDigest(build.runtime)
|
|
1853
|
+
|| evidence.runtimeDigest !== native.runner.runtimeDigest || evidence.executionContractDigest !== native.runner.contractDigest
|
|
1854
|
+
|| evidence.buildScriptSha256 !== createHash('sha256').update(LINUX_PREPARED_SOURCE_BUILD_SCRIPT).digest('hex')) {
|
|
1855
|
+
throw new Error('creation verification Linux build binding unavailable');
|
|
1856
|
+
}
|
|
1857
|
+
const evidenceDigest = controlPlaneDigest(evidence);
|
|
1858
|
+
const current = () => this.withPreparedCreationFence({ planId, planDigest: prepared.plan.digest,
|
|
1859
|
+
artifactSha256: evidence.pack.sha256, growthRunDigest: sourceGrowthRunDigest(prepared.plan.creation.growthRun),
|
|
1860
|
+
referenceDigest: controlPlaneDigest(prepared.reference) }, () => {
|
|
1861
|
+
combined.throwIfAborted();
|
|
1862
|
+
if (this.nativeSourceRunner !== native || this.config.sourceBuild !== build || Date.now() >= build.expiresAt
|
|
1863
|
+
|| controlPlaneDigest(this.store.getSourcePlan(planId).preparedEvidence) !== evidenceDigest
|
|
1864
|
+
|| controlPlaneDigest(this.store.getPreparedSourceJob(planId)) !== controlPlaneDigest(prepared.job)) {
|
|
1865
|
+
throw new Error('creation verification Linux build authority changed');
|
|
1866
|
+
}
|
|
1867
|
+
});
|
|
1868
|
+
current();
|
|
1869
|
+
if (controlPlaneDigest(await this.boundTrust()) !== prepared.job.intent.trustDigest)
|
|
1870
|
+
throw new Error('creation verification trust changed');
|
|
1871
|
+
await verifyLinuxRuntime(build.runtime, combined);
|
|
1872
|
+
current();
|
|
1873
|
+
const execution = await native.runner.inspectSuccessfulExecution(prepared.job.id, combined);
|
|
1874
|
+
current();
|
|
1875
|
+
const artifact = execution?.artifacts[0];
|
|
1876
|
+
if (!execution || execution.runtimeDigest !== evidence.runtimeDigest || execution.contractDigest !== evidence.executionContractDigest
|
|
1877
|
+
|| execution.result.status !== 'succeeded' || !execution.result.quiescent || execution.result.exitCode !== 0
|
|
1878
|
+
|| !execution.receiptBinding || controlPlaneDigest(execution.receiptBinding) !== controlPlaneDigest(evidence.receipt)
|
|
1879
|
+
|| execution.artifacts.length !== 1 || artifact?.path !== LINUX_SOURCE_PACK_PATH || artifact.sha256 !== evidence.pack.sha256
|
|
1880
|
+
|| artifact.bytes.length !== evidence.pack.sizeBytes || !Buffer.from(artifact.bytes).equals(prepared.artifact)
|
|
1881
|
+
|| createHash('sha256').update((execution.result.stdout ?? '') + '\0' + (execution.result.stderr ?? '')).digest('hex') !== evidence.logDigest) {
|
|
1882
|
+
throw new Error('creation verification original Linux build receipt or artifact changed');
|
|
1883
|
+
}
|
|
1884
|
+
if (controlPlaneDigest(await this.boundTrust()) !== prepared.job.intent.trustDigest)
|
|
1885
|
+
throw new Error('creation verification trust changed');
|
|
1886
|
+
await verifyLinuxRuntime(build.runtime, combined);
|
|
1887
|
+
current();
|
|
1888
|
+
return { runtime: structuredClone(build.runtime), evidenceDigest };
|
|
1889
|
+
};
|
|
1890
|
+
/** Host-only read of the original successful revision build; never admits or replays a job. */
|
|
1891
|
+
inspectPreparedRevisionLinuxBuild = async (planId, signal) => {
|
|
1892
|
+
const combined = AbortSignal.any([this.abort.signal, signal, AbortSignal.timeout(120_000)]);
|
|
1893
|
+
combined.throwIfAborted();
|
|
1894
|
+
const prepared = this.inspectPreparedRevision(planId), build = this.config.sourceBuild;
|
|
1895
|
+
const native = this.nativeSourceRunner, evidence = prepared.plan.preparedEvidence;
|
|
1896
|
+
if (!build || !('backend' in build) || build.backend !== 'linux-native' || !build.userNamespace || !build.systemSnapshot
|
|
1897
|
+
|| native?.backend !== 'linux-native'
|
|
1898
|
+
|| evidence?.schemaVersion !== 3 || evidence.boundary !== 'linux-native-repository-v1'
|
|
1899
|
+
|| !('backend' in prepared.job.intent.build) || prepared.job.intent.build.backend !== 'linux-native'
|
|
1900
|
+
|| prepared.job.intent.nativeResource?.backend !== 'linux-native' || prepared.job.intent.nativeResource.key !== prepared.job.id
|
|
1901
|
+
|| evidence.runtimeDigest !== linuxRuntimeDigest(build.runtime)
|
|
1902
|
+
|| evidence.runtimeDigest !== native.runner.runtimeDigest || evidence.executionContractDigest !== native.runner.contractDigest
|
|
1903
|
+
|| evidence.buildScriptSha256 !== createHash('sha256').update(LINUX_PREPARED_SOURCE_BUILD_SCRIPT).digest('hex')) {
|
|
1904
|
+
throw new Error('revision verification Linux build binding unavailable');
|
|
1905
|
+
}
|
|
1906
|
+
const sourceRevision = prepared.plan.sourceRevision;
|
|
1907
|
+
const evidenceDigest = controlPlaneDigest(evidence);
|
|
1908
|
+
const current = () => this.withPreparedRevisionFence({ planId, planDigest: prepared.plan.digest,
|
|
1909
|
+
artifactSha256: evidence.pack.sha256, growthRunDigest: sourceGrowthRunDigest(sourceRevision.growthRun),
|
|
1910
|
+
referenceDigest: controlPlaneDigest(prepared.reference), parentDigest: controlPlaneDigest(sourceRevision.parent) }, () => {
|
|
1911
|
+
combined.throwIfAborted();
|
|
1912
|
+
if (this.nativeSourceRunner !== native || this.config.sourceBuild !== build || Date.now() >= build.expiresAt
|
|
1913
|
+
|| controlPlaneDigest(this.store.getSourcePlan(planId).preparedEvidence) !== evidenceDigest
|
|
1914
|
+
|| controlPlaneDigest(this.store.getPreparedRevisionJob(planId)) !== controlPlaneDigest(prepared.job)) {
|
|
1915
|
+
throw new Error('revision verification Linux build authority changed');
|
|
1916
|
+
}
|
|
1917
|
+
});
|
|
1918
|
+
current();
|
|
1919
|
+
if (controlPlaneDigest(await this.boundTrust()) !== prepared.job.intent.trustDigest)
|
|
1920
|
+
throw new Error('revision verification trust changed');
|
|
1921
|
+
await verifyLinuxRuntime(build.runtime, combined);
|
|
1922
|
+
current();
|
|
1923
|
+
const execution = await native.runner.inspectSuccessfulExecution(prepared.job.id, combined);
|
|
1924
|
+
current();
|
|
1925
|
+
const artifact = execution?.artifacts[0];
|
|
1926
|
+
if (!execution || execution.runtimeDigest !== evidence.runtimeDigest || execution.contractDigest !== evidence.executionContractDigest
|
|
1927
|
+
|| execution.result.status !== 'succeeded' || !execution.result.quiescent || execution.result.exitCode !== 0
|
|
1928
|
+
|| !execution.receiptBinding || controlPlaneDigest(execution.receiptBinding) !== controlPlaneDigest(evidence.receipt)
|
|
1929
|
+
|| execution.artifacts.length !== 1 || artifact?.path !== LINUX_SOURCE_PACK_PATH || artifact.sha256 !== evidence.pack.sha256
|
|
1930
|
+
|| artifact.bytes.length !== evidence.pack.sizeBytes || !Buffer.from(artifact.bytes).equals(prepared.artifact)
|
|
1931
|
+
|| createHash('sha256').update((execution.result.stdout ?? '') + '\0' + (execution.result.stderr ?? '')).digest('hex') !== evidence.logDigest) {
|
|
1932
|
+
throw new Error('revision verification original Linux build receipt or artifact changed');
|
|
1933
|
+
}
|
|
1934
|
+
if (controlPlaneDigest(await this.boundTrust()) !== prepared.job.intent.trustDigest)
|
|
1935
|
+
throw new Error('revision verification trust changed');
|
|
1936
|
+
await verifyLinuxRuntime(build.runtime, combined);
|
|
1937
|
+
current();
|
|
1938
|
+
return { runtime: structuredClone(build.runtime), evidenceDigest };
|
|
1939
|
+
};
|
|
1385
1940
|
/** Signed behavior evidence over stored pack/checks. Adoption must await async review-context recheck. */
|
|
1386
1941
|
inspectVerifiedCreation = (planId) => {
|
|
1387
1942
|
const certificate = this.store.getCreationVerification(planId);
|
|
@@ -1691,13 +2246,21 @@ export class PluginControlPlaneService extends Service {
|
|
|
1691
2246
|
throw new Error('plugin-control-plane: durable source jobs unavailable or still draining');
|
|
1692
2247
|
return this.sourceRuntime.reconcileUnknown(input);
|
|
1693
2248
|
};
|
|
1694
|
-
async prepareSourceJob(job, signal, assertCurrent) {
|
|
2249
|
+
async prepareSourceJob(job, signal, assertCurrent, preflight) {
|
|
1695
2250
|
this.abort.signal.throwIfAborted();
|
|
1696
|
-
if (this.sourceBuilds.size !== 0 || this.sourceInspections.size !== 0)
|
|
1697
|
-
|
|
2251
|
+
if (this.sourceBuilds.size !== 0 || this.sourceInspections.size !== 0) {
|
|
2252
|
+
const error = new Error('plugin-control-plane: another source operation is draining');
|
|
2253
|
+
// This call has not created an operation/worktree or called a runner.
|
|
2254
|
+
if (preflight && job.intent.nativeResource?.backend === 'linux-native' && job.intent.nativeResource.key === job.id
|
|
2255
|
+
&& controlPlaneDigest(job.intent) === job.intentDigest) {
|
|
2256
|
+
throw cleanedLinuxSourcePreflight(error, job, preflight.generation, () => !this.abort.signal.aborted
|
|
2257
|
+
&& this.nativeSourceRunner?.backend === 'linux-native' && this.nativeSourceRunner.runner === preflight.runner);
|
|
2258
|
+
}
|
|
2259
|
+
throw error;
|
|
2260
|
+
}
|
|
1698
2261
|
const intent = job.intent;
|
|
1699
2262
|
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);
|
|
2263
|
+
idempotencyKey: `source-job-plan:${job.id}`, expectedBaseCommit: intent.baseCommit, ttlMs: intent.ttlMs, timeoutMs: intent.build.timeoutMs, offline: true, signal, assertCurrent }, job, preflight);
|
|
1701
2264
|
this.sourceBuilds.add(operation);
|
|
1702
2265
|
try {
|
|
1703
2266
|
return await operation;
|
|
@@ -2016,7 +2579,7 @@ export class PluginControlPlaneService extends Service {
|
|
|
2016
2579
|
void operation.then(() => this.sourceBuilds.delete(operation), () => this.sourceBuilds.delete(operation));
|
|
2017
2580
|
return operation;
|
|
2018
2581
|
}
|
|
2019
|
-
async prepareSourcePlanOwned(input, sourceJob) {
|
|
2582
|
+
async prepareSourcePlanOwned(input, sourceJob, preflight) {
|
|
2020
2583
|
const signal = input.signal === undefined ? this.abort.signal : AbortSignal.any([this.abort.signal, input.signal]);
|
|
2021
2584
|
const gapOwner = sourceJob?.intent.owner ?? input.owner;
|
|
2022
2585
|
const creating = sourceJob?.intent.mode === 'create';
|
|
@@ -2065,8 +2628,10 @@ export class PluginControlPlaneService extends Service {
|
|
|
2065
2628
|
if (!Number.isSafeInteger(ttlMs) || ttlMs < 900_000 || ttlMs > 86_400_000) {
|
|
2066
2629
|
throw new ControlPlaneCliError('INVALID_ARGUMENT', 'ttlMs must be an integer within 900000..86400000');
|
|
2067
2630
|
}
|
|
2068
|
-
const
|
|
2069
|
-
|
|
2631
|
+
const buildConfig = this.config.sourceBuild;
|
|
2632
|
+
const repositoryBuild = buildConfig !== undefined && ('backend' in buildConfig
|
|
2633
|
+
? buildConfig.backend === 'linux-native' : ['repository', 'portable-repository'].includes(buildConfig.profile ?? ''));
|
|
2634
|
+
const maximumTimeoutMs = repositoryBuild ? 1_800_000 : 240_000;
|
|
2070
2635
|
const timeoutMs = input.timeoutMs ?? 180_000;
|
|
2071
2636
|
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 60_000 || timeoutMs > maximumTimeoutMs) {
|
|
2072
2637
|
throw new ControlPlaneCliError('INVALID_ARGUMENT', `timeoutMs must be an integer within 60000..${maximumTimeoutMs}`);
|
|
@@ -2087,6 +2652,7 @@ export class PluginControlPlaneService extends Service {
|
|
|
2087
2652
|
const stateRoot = join(this.config.statePath, 'source-worktrees');
|
|
2088
2653
|
const isolated = await createIsolatedWorktree({ stateRoot, repository, baseCommit, environment,
|
|
2089
2654
|
...(sourceJob === undefined ? {} : { worktreeName: basename(sourceJob.intent.worktree) }) });
|
|
2655
|
+
let linuxBuildInvoked = false;
|
|
2090
2656
|
try {
|
|
2091
2657
|
const generated = revision !== undefined ? await prepareRevisedPluginWorkspace({ worktree: isolated.worktree,
|
|
2092
2658
|
baseCommit, name, files: input.files, environment, signal, assertCurrent, revision, parentSource: parent.source, parentCertificate: parent.certificate })
|
|
@@ -2116,12 +2682,21 @@ export class PluginControlPlaneService extends Service {
|
|
|
2116
2682
|
parentCertificate: parent.certificate, files: input.files } }), ...(capturePack ? { capturePack: true } : {}) };
|
|
2117
2683
|
const checked = 'backend' in configured
|
|
2118
2684
|
? await (async () => {
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2685
|
+
const native = this.nativeSourceRunner;
|
|
2686
|
+
if (sourceJob === undefined || native === undefined || native.backend !== configured.backend
|
|
2687
|
+
|| sourceJob.intent.nativeResource?.backend !== configured.backend || sourceJob.intent.nativeResource.key !== sourceJob.id) {
|
|
2688
|
+
throw new ControlPlaneCliError('SOURCE_BOUNDARY', 'native source preparation requires its bound durable source job');
|
|
2689
|
+
}
|
|
2690
|
+
if (configured.backend === 'linux-native' && native.backend === 'linux-native') {
|
|
2691
|
+
linuxBuildInvoked = true;
|
|
2692
|
+
return runLinuxPreparedChecks({ ...common, config: { ...configured, timeoutMs: Math.min(timeoutMs, configured.timeoutMs) },
|
|
2693
|
+
runner: native.runner, sourceJob: { id: sourceJob.id } });
|
|
2694
|
+
}
|
|
2695
|
+
if (configured.backend !== 'macos-seatbelt' || native.backend !== 'macos-seatbelt' || !creating && !revising) {
|
|
2696
|
+
throw new ControlPlaneCliError('SOURCE_BOUNDARY', 'macOS source preparation requires a Host-scaffolded creation job');
|
|
2122
2697
|
}
|
|
2123
2698
|
return runNativePreparedChecks({ ...common, config: { ...configured, timeoutMs: Math.min(timeoutMs, configured.timeoutMs) },
|
|
2124
|
-
runner:
|
|
2699
|
+
runner: native.runner, sourceJob: { id: sourceJob.id } });
|
|
2125
2700
|
})()
|
|
2126
2701
|
: await runDockerPreparedChecks({ ...common, config: { ...configured, timeoutMs: Math.min(timeoutMs, configured.timeoutMs) },
|
|
2127
2702
|
...(sourceJob === undefined ? {} : { sourceJob: { id: sourceJob.id, containerName: sourceJob.intent.containerName } }) });
|
|
@@ -2145,7 +2720,15 @@ export class PluginControlPlaneService extends Service {
|
|
|
2145
2720
|
}
|
|
2146
2721
|
// The worktree survives success: the owner recomputes its digests on this
|
|
2147
2722
|
// exact directory during `source verify-prepared`.
|
|
2148
|
-
|
|
2723
|
+
const commitSource = (callback) => {
|
|
2724
|
+
if (creation?.growthRun?.protocol === 'assistant-growth/source-run/v2') {
|
|
2725
|
+
if (!gapOwner || !('receiptVersion' in gapOwner))
|
|
2726
|
+
throw new Error('work item creation owner unavailable');
|
|
2727
|
+
return this.withWorkItemGrowthFrame(creation.growthRun, input.gapId, gapOwner, false, callback);
|
|
2728
|
+
}
|
|
2729
|
+
return this.taskGaps.withCurrent(input.gapId, gapOwner, callback);
|
|
2730
|
+
};
|
|
2731
|
+
return commitSource(() => this.store.createSourcePlan({ gapId: input.gapId, repository, worktree: isolated.worktree, baseCommit,
|
|
2149
2732
|
name, generatorDigest: generated?.generatorDigest ?? MODIFY_GENERATOR_DIGEST,
|
|
2150
2733
|
scope: generated?.scope ?? [`plugins/${name}`], mode: creating ? 'prepared-create' : revising ? 'prepared-revise' : 'modify', ttlMs,
|
|
2151
2734
|
...(creation === undefined ? {} : { creation }), ...(revision === undefined ? {} : { revision }),
|
|
@@ -2156,6 +2739,19 @@ export class PluginControlPlaneService extends Service {
|
|
|
2156
2739
|
}
|
|
2157
2740
|
catch (error) {
|
|
2158
2741
|
await isolated.remove();
|
|
2742
|
+
if (preflight && sourceJob && sourceJob.intent.nativeResource?.backend === 'linux-native'
|
|
2743
|
+
&& sourceJob.intent.nativeResource.key === sourceJob.id && controlPlaneDigest(sourceJob.intent) === sourceJob.intentDigest) {
|
|
2744
|
+
// Preserve native authority checks across asynchronous worktree cleanup.
|
|
2745
|
+
// The runtime consumes the complete chain immediately before its original claim CAS.
|
|
2746
|
+
if (linuxBuildInvoked && isLinuxSourceBuildFailed(error)) {
|
|
2747
|
+
throw cleanedLinuxSourceBuildFailure(error, sourceJob, preflight.generation, () => !this.abort.signal.aborted && this.nativeSourceRunner?.backend === 'linux-native'
|
|
2748
|
+
&& this.nativeSourceRunner.runner === preflight.runner
|
|
2749
|
+
? consumeLinuxSourceBuildFailed(error, { runner: preflight.runner, key: sourceJob.id }) : undefined);
|
|
2750
|
+
}
|
|
2751
|
+
throw cleanedLinuxSourcePreflight(error, sourceJob, preflight.generation, () => !this.abort.signal.aborted && this.nativeSourceRunner?.backend === 'linux-native'
|
|
2752
|
+
&& this.nativeSourceRunner.runner === preflight.runner
|
|
2753
|
+
&& (!linuxBuildInvoked || consumeLinuxSourceNotStarted(error, { runner: preflight.runner, key: sourceJob.id })));
|
|
2754
|
+
}
|
|
2159
2755
|
throw error;
|
|
2160
2756
|
}
|
|
2161
2757
|
}
|