agentera 3.0.0-dev.16 → 3.0.0-dev.17
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 +9 -5
- package/bundle/CHANGELOG.md +12 -0
- package/bundle/README.md +6 -2
- package/bundle/UPGRADE.md +18 -4
- package/bundle/references/adapters/package-manifest-interface-model.yaml +0 -4
- package/bundle/references/adapters/package-surface-characterization.md +4 -4
- package/bundle/references/adapters/runtime-feature-parity.md +10 -2
- package/bundle/references/adapters/runtime-lifecycle-authority.yaml +17 -0
- package/bundle/references/adapters/runtime-lifecycle-operation-contract.yaml +1 -0
- package/bundle/skills/agentera/schemas/artifacts/plan.yaml +1 -1
- package/dist/cli/commands/prime/collectOrientationState.js +42 -33
- package/dist/cli/commands/prime/collectOrientationState.js.map +1 -1
- package/dist/cli/commands/schema.js +12 -3
- package/dist/cli/commands/schema.js.map +1 -1
- package/dist/cli/commands/state/plan.js +82 -46
- package/dist/cli/commands/state/plan.js.map +1 -1
- package/dist/cli/help.js +1 -1
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/orientation/attention.js +99 -1
- package/dist/cli/orientation/attention.js.map +1 -1
- package/dist/cli/orientation.js +44 -17
- package/dist/cli/orientation.js.map +1 -1
- package/dist/cli/planArtifacts.js +95 -0
- package/dist/cli/planArtifacts.js.map +1 -0
- package/dist/cli/planEvidence.js +158 -0
- package/dist/cli/planEvidence.js.map +1 -0
- package/dist/cli/prime-blob.js +1 -1
- package/dist/cli/prime-blob.js.map +1 -1
- package/dist/cli/stateQuery.js +1 -0
- package/dist/cli/stateQuery.js.map +1 -1
- package/dist/registries/runtimeAdapterRegistry.js +4 -1
- package/dist/registries/runtimeAdapterRegistry.js.map +1 -1
- package/dist/release/releaseMetadata.js +1 -1
- package/dist/release/releaseMetadata.js.map +1 -1
- package/dist/runtime/lifecycleAdapterContract.js +63 -41
- package/dist/runtime/lifecycleAdapterContract.js.map +1 -1
- package/dist/runtime/lifecycleAdapters.js +162 -103
- package/dist/runtime/lifecycleAdapters.js.map +1 -1
- package/dist/runtime/lifecycleAuthority.js +81 -31
- package/dist/runtime/lifecycleAuthority.js.map +1 -1
- package/dist/runtime/lifecycleOperationContract.js +4 -0
- package/dist/runtime/lifecycleOperationContract.js.map +1 -1
- package/dist/runtime/lifecycleOperations.js +10 -2
- package/dist/runtime/lifecycleOperations.js.map +1 -1
- package/dist/runtime/lifecycleSnapshot.js +290 -25
- package/dist/runtime/lifecycleSnapshot.js.map +1 -1
- package/dist/runtime/retiredRuntimeCleanup.js +15 -9
- package/dist/runtime/retiredRuntimeCleanup.js.map +1 -1
- package/dist/upgrade/channels.js +6 -8
- package/dist/upgrade/channels.js.map +1 -1
- package/dist/upgrade/compatibility.js +5 -0
- package/dist/upgrade/compatibility.js.map +1 -1
- package/dist/upgrade/doctor.js +14 -13
- package/dist/upgrade/doctor.js.map +1 -1
- package/dist/upgrade/lifecycleUpgrade.js +68 -27
- package/dist/upgrade/lifecycleUpgrade.js.map +1 -1
- package/dist/upgrade/projectIntegration.js +148 -69
- package/dist/upgrade/projectIntegration.js.map +1 -1
- package/dist/upgrade/projectIntegrationDecision.js +99 -0
- package/dist/upgrade/projectIntegrationDecision.js.map +1 -1
- package/dist/upgrade/runtimeMigration.js +2 -2
- package/dist/upgrade/runtimeMigration.js.map +1 -1
- package/dist/upgrade/upgradeOrchestrator.js +21 -7
- package/dist/upgrade/upgradeOrchestrator.js.map +1 -1
- package/dist/validate/lifecycleAdapters.js +26 -2
- package/dist/validate/lifecycleAdapters.js.map +1 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { buildRuntimeLifecycleState, loadLifecycleAuthority, } from "./lifecycleAuthority.js";
|
|
4
4
|
import { RUNTIME_ADAPTER_CATEGORIES, loadRuntimeLifecycleAdapterContract, } from "./lifecycleAdapterContract.js";
|
|
5
|
-
import { applyLifecycleOperations, createLifecycleOwnershipManifest, emptyLifecycleOwnershipLedger, planLifecycleOperations, } from "./lifecycleOperations.js";
|
|
5
|
+
import { applyLifecycleOperations, createLifecycleOwnershipManifest, emptyLifecycleOwnershipLedger, LIFECYCLE_MANUAL_REVIEW_GUIDANCE, planLifecycleOperations, } from "./lifecycleOperations.js";
|
|
6
6
|
const STATE_PRIORITY = {
|
|
7
7
|
blocked_unowned: 100,
|
|
8
8
|
action_required: 95,
|
|
@@ -50,26 +50,41 @@ function expandResource(declaration, values) {
|
|
|
50
50
|
if (declaration.kind === "directory_files") {
|
|
51
51
|
let entries;
|
|
52
52
|
try {
|
|
53
|
-
entries = fs
|
|
53
|
+
entries = fs
|
|
54
|
+
.readdirSync(sourcePath)
|
|
54
55
|
.filter((name) => name.endsWith(declaration.extension))
|
|
55
56
|
.sort();
|
|
56
57
|
}
|
|
57
58
|
catch (error) {
|
|
58
|
-
return [
|
|
59
|
+
return [
|
|
60
|
+
{
|
|
59
61
|
declaration,
|
|
60
62
|
sourcePath,
|
|
61
63
|
destinationPath,
|
|
62
64
|
sourceError: `could not read declared source directory: ${error.message}`,
|
|
63
|
-
}
|
|
65
|
+
},
|
|
66
|
+
];
|
|
64
67
|
}
|
|
65
68
|
if (entries.length === 0) {
|
|
66
|
-
return [
|
|
69
|
+
return [
|
|
70
|
+
{
|
|
71
|
+
declaration,
|
|
72
|
+
sourcePath,
|
|
73
|
+
destinationPath,
|
|
74
|
+
sourceError: "declared source directory is empty",
|
|
75
|
+
},
|
|
76
|
+
];
|
|
67
77
|
}
|
|
68
78
|
return entries.map((name) => {
|
|
69
79
|
const source = path.join(sourcePath, name);
|
|
70
80
|
const destination = path.join(destinationPath, name);
|
|
71
81
|
if (source === destination) {
|
|
72
|
-
return {
|
|
82
|
+
return {
|
|
83
|
+
declaration,
|
|
84
|
+
sourcePath: source,
|
|
85
|
+
destinationPath: destination,
|
|
86
|
+
alreadyInPlace: true,
|
|
87
|
+
};
|
|
73
88
|
}
|
|
74
89
|
try {
|
|
75
90
|
return {
|
|
@@ -101,9 +116,17 @@ function expandResource(declaration, values) {
|
|
|
101
116
|
}
|
|
102
117
|
if (declaration.kind === "symlink") {
|
|
103
118
|
if (!fs.existsSync(sourcePath)) {
|
|
104
|
-
return [
|
|
119
|
+
return [
|
|
120
|
+
{
|
|
121
|
+
declaration,
|
|
122
|
+
sourcePath,
|
|
123
|
+
destinationPath,
|
|
124
|
+
sourceError: "declared symlink source is missing",
|
|
125
|
+
},
|
|
126
|
+
];
|
|
105
127
|
}
|
|
106
|
-
return [
|
|
128
|
+
return [
|
|
129
|
+
{
|
|
107
130
|
declaration,
|
|
108
131
|
sourcePath,
|
|
109
132
|
destinationPath,
|
|
@@ -115,10 +138,12 @@ function expandResource(declaration, values) {
|
|
|
115
138
|
linkTarget: sourcePath,
|
|
116
139
|
required: declaration.required,
|
|
117
140
|
},
|
|
118
|
-
}
|
|
141
|
+
},
|
|
142
|
+
];
|
|
119
143
|
}
|
|
120
144
|
try {
|
|
121
|
-
return [
|
|
145
|
+
return [
|
|
146
|
+
{
|
|
122
147
|
declaration,
|
|
123
148
|
sourcePath,
|
|
124
149
|
destinationPath,
|
|
@@ -130,26 +155,35 @@ function expandResource(declaration, values) {
|
|
|
130
155
|
content: fs.readFileSync(sourcePath),
|
|
131
156
|
required: declaration.required,
|
|
132
157
|
},
|
|
133
|
-
}
|
|
158
|
+
},
|
|
159
|
+
];
|
|
134
160
|
}
|
|
135
161
|
catch (error) {
|
|
136
|
-
return [
|
|
162
|
+
return [
|
|
163
|
+
{
|
|
137
164
|
declaration,
|
|
138
165
|
sourcePath,
|
|
139
166
|
destinationPath,
|
|
140
167
|
sourceError: `could not read declared source: ${error.message}`,
|
|
141
|
-
}
|
|
168
|
+
},
|
|
169
|
+
];
|
|
142
170
|
}
|
|
143
171
|
}
|
|
144
172
|
function nearestPlanState(operation) {
|
|
145
173
|
switch (operation.action) {
|
|
146
|
-
case "noop":
|
|
147
|
-
|
|
174
|
+
case "noop":
|
|
175
|
+
return "confirmed";
|
|
176
|
+
case "create":
|
|
177
|
+
return "absent";
|
|
148
178
|
case "update":
|
|
149
|
-
case "finalize_ownership":
|
|
150
|
-
|
|
151
|
-
case "
|
|
152
|
-
|
|
179
|
+
case "finalize_ownership":
|
|
180
|
+
return "drifted";
|
|
181
|
+
case "blocked_unowned":
|
|
182
|
+
return "blocked_unowned";
|
|
183
|
+
case "action_required":
|
|
184
|
+
return operation.ownership === "unowned" ? "blocked_unowned" : "action_required";
|
|
185
|
+
case "remove":
|
|
186
|
+
return "action_required";
|
|
153
187
|
}
|
|
154
188
|
}
|
|
155
189
|
function probeSkill(location, canonical, surfaces) {
|
|
@@ -157,7 +191,13 @@ function probeSkill(location, canonical, surfaces) {
|
|
|
157
191
|
try {
|
|
158
192
|
const stat = fs.lstatSync(skillFile);
|
|
159
193
|
if (!stat.isFile()) {
|
|
160
|
-
return {
|
|
194
|
+
return {
|
|
195
|
+
location,
|
|
196
|
+
canonical,
|
|
197
|
+
surfaces,
|
|
198
|
+
state: "drifted",
|
|
199
|
+
detail: "SKILL.md is not a regular file",
|
|
200
|
+
};
|
|
161
201
|
}
|
|
162
202
|
const handle = fs.openSync(skillFile, "r");
|
|
163
203
|
try {
|
|
@@ -165,9 +205,21 @@ function probeSkill(location, canonical, surfaces) {
|
|
|
165
205
|
fs.readSync(handle, bytes, 0, bytes.length, 0);
|
|
166
206
|
const text = bytes.toString("utf8");
|
|
167
207
|
if (!/^name:\s*agentera\s*$/m.test(text)) {
|
|
168
|
-
return {
|
|
208
|
+
return {
|
|
209
|
+
location,
|
|
210
|
+
canonical,
|
|
211
|
+
surfaces,
|
|
212
|
+
state: "drifted",
|
|
213
|
+
detail: "SKILL.md does not declare name: agentera",
|
|
214
|
+
};
|
|
169
215
|
}
|
|
170
|
-
return {
|
|
216
|
+
return {
|
|
217
|
+
location,
|
|
218
|
+
canonical,
|
|
219
|
+
surfaces,
|
|
220
|
+
state: "confirmed",
|
|
221
|
+
detail: "Agentera SKILL.md detected",
|
|
222
|
+
};
|
|
171
223
|
}
|
|
172
224
|
finally {
|
|
173
225
|
fs.closeSync(handle);
|
|
@@ -175,7 +227,13 @@ function probeSkill(location, canonical, surfaces) {
|
|
|
175
227
|
}
|
|
176
228
|
catch (error) {
|
|
177
229
|
if (error.code === "ENOENT") {
|
|
178
|
-
return {
|
|
230
|
+
return {
|
|
231
|
+
location,
|
|
232
|
+
canonical,
|
|
233
|
+
surfaces,
|
|
234
|
+
state: "absent",
|
|
235
|
+
detail: "Agentera SKILL.md is absent",
|
|
236
|
+
};
|
|
179
237
|
}
|
|
180
238
|
return {
|
|
181
239
|
location,
|
|
@@ -220,8 +278,7 @@ function evidenceState(value, category) {
|
|
|
220
278
|
return "unknown";
|
|
221
279
|
}
|
|
222
280
|
function resourceReports(expanded, plan, category, surfaceId) {
|
|
223
|
-
const matching = expanded.filter((resource) => resource.declaration.category === category
|
|
224
|
-
&& (resource.declaration.surfaceId === undefined || resource.declaration.surfaceId === surfaceId));
|
|
281
|
+
const matching = expanded.filter((resource) => resource.declaration.category === category && (resource.declaration.surfaceId === undefined || resource.declaration.surfaceId === surfaceId));
|
|
225
282
|
const evidence = [];
|
|
226
283
|
const states = [];
|
|
227
284
|
const operations = [];
|
|
@@ -248,9 +305,7 @@ function resourceReports(expanded, plan, category, surfaceId) {
|
|
|
248
305
|
});
|
|
249
306
|
continue;
|
|
250
307
|
}
|
|
251
|
-
const operation = resource.operation
|
|
252
|
-
? plan.operations.find((candidate) => candidate.id === resource.operation?.id)
|
|
253
|
-
: undefined;
|
|
308
|
+
const operation = resource.operation ? plan.operations.find((candidate) => candidate.id === resource.operation?.id) : undefined;
|
|
254
309
|
const state = operation ? nearestPlanState(operation) : "unknown";
|
|
255
310
|
states.push(state);
|
|
256
311
|
if (operation)
|
|
@@ -269,22 +324,35 @@ function resourceReports(expanded, plan, category, surfaceId) {
|
|
|
269
324
|
operations,
|
|
270
325
|
};
|
|
271
326
|
}
|
|
327
|
+
function hasObservedAgenteraIntegration(surfaceId, expanded, plan, skills) {
|
|
328
|
+
const resourceIntegration = expanded.some((resource) => {
|
|
329
|
+
if (resource.declaration.surfaceId !== surfaceId)
|
|
330
|
+
return false;
|
|
331
|
+
if (resource.alreadyInPlace)
|
|
332
|
+
return true;
|
|
333
|
+
const operation = resource.operation ? plan.operations.find((candidate) => candidate.id === resource.operation?.id) : undefined;
|
|
334
|
+
return operation !== undefined && operation.state !== "missing";
|
|
335
|
+
});
|
|
336
|
+
const skillIntegration = skills.some((skill) => !skill.canonical && skill.surfaces.includes(surfaceId) && !["absent", "unknown", "not_applicable"].includes(skill.state));
|
|
337
|
+
return resourceIntegration || skillIntegration;
|
|
338
|
+
}
|
|
339
|
+
function lifecycleSurfaceExpected(surface, hostPresence, expanded, plan, skills) {
|
|
340
|
+
return surface.presence === "required" || hostPresence === true || hasObservedAgenteraIntegration(surface.id, expanded, plan, skills);
|
|
341
|
+
}
|
|
272
342
|
function actionRequiredSummary(operations) {
|
|
273
343
|
const missingParentOperations = operations.filter((operation) => operation.reason === "allowed root is not an existing safe directory");
|
|
274
344
|
if (missingParentOperations.length === operations.length) {
|
|
275
345
|
const parents = [...new Set(missingParentOperations.map((operation) => path.dirname(operation.destination)))];
|
|
276
346
|
return `Create the destination parent ${parents.join(", ")} as a real, non-symlink directory you control, then rerun preview; Agentera will not create or replace it.`;
|
|
277
347
|
}
|
|
278
|
-
return `Resolve these lifecycle blockers manually, then rerun preview: ${operations
|
|
279
|
-
.map((operation) => `${operation.id}: ${operation.reason}`)
|
|
280
|
-
.join("; ")}.`;
|
|
348
|
+
return `Resolve these lifecycle blockers manually, then rerun preview: ${operations.map((operation) => `${operation.id}: ${operation.reason}`).join("; ")}.`;
|
|
281
349
|
}
|
|
282
350
|
function remediationFor(state, claim, operations, nativeActions, runtimeId, surfaceId, evidencePath, sourceRoot) {
|
|
283
351
|
const blockedOperations = operations.filter((operation) => operation.action === "blocked_unowned");
|
|
284
352
|
if (blockedOperations.length > 0) {
|
|
285
353
|
return {
|
|
286
354
|
kind: "action_required",
|
|
287
|
-
summary:
|
|
355
|
+
summary: LIFECYCLE_MANUAL_REVIEW_GUIDANCE,
|
|
288
356
|
operationIds: blockedOperations.map((operation) => operation.id),
|
|
289
357
|
nativeActions: [],
|
|
290
358
|
};
|
|
@@ -342,13 +410,16 @@ function remediationFor(state, claim, operations, nativeActions, runtimeId, surf
|
|
|
342
410
|
return { kind: "action_required", summary, operationIds: [], nativeActions: [] };
|
|
343
411
|
}
|
|
344
412
|
if (state === "confirmed" || state === "not_applicable") {
|
|
345
|
-
return {
|
|
413
|
+
return {
|
|
414
|
+
kind: "none",
|
|
415
|
+
summary: "No remediation is required.",
|
|
416
|
+
operationIds: [],
|
|
417
|
+
nativeActions: [],
|
|
418
|
+
};
|
|
346
419
|
}
|
|
347
420
|
return {
|
|
348
421
|
kind: "unavailable",
|
|
349
|
-
summary: claim.capability === "unsupported"
|
|
350
|
-
? "The host surface is explicitly unsupported."
|
|
351
|
-
: "No verified safe remediation is declared for this host surface.",
|
|
422
|
+
summary: claim.capability === "unsupported" ? "The host surface is explicitly unsupported." : "No verified safe remediation is declared for this host surface.",
|
|
352
423
|
operationIds: [],
|
|
353
424
|
nativeActions: [],
|
|
354
425
|
};
|
|
@@ -366,11 +437,8 @@ function assertSurfaceRemediationConsistency(category, state, operations, remedi
|
|
|
366
437
|
throw new Error(`${category}: action-required operation must produce action_required reporting`);
|
|
367
438
|
}
|
|
368
439
|
if (remediation.kind === "repair") {
|
|
369
|
-
const repairableIds = new Set(operations
|
|
370
|
-
|
|
371
|
-
.map((operation) => operation.id));
|
|
372
|
-
if (remediation.operationIds.length === 0
|
|
373
|
-
|| remediation.operationIds.some((operationId) => !repairableIds.has(operationId))) {
|
|
440
|
+
const repairableIds = new Set(operations.filter((operation) => ["create", "update", "finalize_ownership"].includes(operation.action)).map((operation) => operation.id));
|
|
441
|
+
if (remediation.operationIds.length === 0 || remediation.operationIds.some((operationId) => !repairableIds.has(operationId))) {
|
|
374
442
|
throw new Error(`${category}: repair remediation must reference only repairable plan operations`);
|
|
375
443
|
}
|
|
376
444
|
}
|
|
@@ -385,13 +453,20 @@ function categorySurface(runtimeId, surface, category, claim, expected, context,
|
|
|
385
453
|
source: claim.evidence,
|
|
386
454
|
required: false,
|
|
387
455
|
diagnosisComplete: true,
|
|
388
|
-
evidence: [
|
|
456
|
+
evidence: [
|
|
457
|
+
{
|
|
389
458
|
kind: "host_probe",
|
|
390
459
|
state: "not_applicable",
|
|
391
460
|
detail: "conditional host surface is absent",
|
|
392
461
|
surfaceId: surface.id,
|
|
393
|
-
}
|
|
394
|
-
|
|
462
|
+
},
|
|
463
|
+
],
|
|
464
|
+
remediation: {
|
|
465
|
+
kind: "none",
|
|
466
|
+
summary: "No remediation is required.",
|
|
467
|
+
operationIds: [],
|
|
468
|
+
nativeActions: [],
|
|
469
|
+
},
|
|
395
470
|
};
|
|
396
471
|
}
|
|
397
472
|
let state;
|
|
@@ -408,9 +483,7 @@ function categorySurface(runtimeId, surface, category, claim, expected, context,
|
|
|
408
483
|
evidence = relevant.map((skill) => ({
|
|
409
484
|
kind: "filesystem",
|
|
410
485
|
state: skill.state,
|
|
411
|
-
detail: skill.canonical
|
|
412
|
-
? `${skill.detail}; canonical shared location`
|
|
413
|
-
: `${skill.detail}; additional discovery location can shadow or collide with canonical Agentera`,
|
|
486
|
+
detail: skill.canonical ? `${skill.detail}; canonical shared location` : `${skill.detail}; additional discovery location can shadow or collide with canonical Agentera`,
|
|
414
487
|
path: skill.location,
|
|
415
488
|
surfaceId: surface.id,
|
|
416
489
|
}));
|
|
@@ -420,9 +493,7 @@ function categorySurface(runtimeId, surface, category, claim, expected, context,
|
|
|
420
493
|
state = aggregateState([state, resources.state]);
|
|
421
494
|
}
|
|
422
495
|
else if (claim.capability === "repairable" || claim.evidence.startsWith("managed_resource:")) {
|
|
423
|
-
const resourceCategory = claim.evidence.startsWith("managed_resource:")
|
|
424
|
-
? expanded.find((resource) => resource.declaration.id === claim.evidence.slice("managed_resource:".length))?.declaration.category ?? category
|
|
425
|
-
: category;
|
|
496
|
+
const resourceCategory = claim.evidence.startsWith("managed_resource:") ? (expanded.find((resource) => resource.declaration.id === claim.evidence.slice("managed_resource:".length))?.declaration.category ?? category) : category;
|
|
426
497
|
const resources = resourceReports(expanded, plan, resourceCategory, surface.id);
|
|
427
498
|
state = resources.state;
|
|
428
499
|
evidence = resources.evidence;
|
|
@@ -454,49 +525,59 @@ function categorySurface(runtimeId, surface, category, claim, expected, context,
|
|
|
454
525
|
catch (error) {
|
|
455
526
|
state = error.code === "ENOENT" ? "absent" : "unknown";
|
|
456
527
|
}
|
|
457
|
-
evidence = [
|
|
528
|
+
evidence = [
|
|
529
|
+
{
|
|
530
|
+
kind: "filesystem",
|
|
531
|
+
state,
|
|
532
|
+
detail: claim.evidence,
|
|
533
|
+
path: evidencePath,
|
|
534
|
+
surfaceId: surface.id,
|
|
535
|
+
},
|
|
536
|
+
];
|
|
458
537
|
}
|
|
459
538
|
else if (claim.evidence.startsWith("env:")) {
|
|
460
539
|
const key = claim.evidence.slice("env:".length);
|
|
461
540
|
const value = (context.env ?? process.env)[key];
|
|
462
541
|
state = value ? "confirmed" : "absent";
|
|
463
|
-
evidence = [
|
|
542
|
+
evidence = [
|
|
543
|
+
{
|
|
464
544
|
kind: "environment",
|
|
465
545
|
state,
|
|
466
546
|
detail: value ? `${key} is present` : `${key} is absent`,
|
|
467
547
|
surfaceId: surface.id,
|
|
468
|
-
}
|
|
548
|
+
},
|
|
549
|
+
];
|
|
469
550
|
}
|
|
470
551
|
else if (claim.evidence === "canonical_skill") {
|
|
471
552
|
const canonical = skills.find((skill) => skill.canonical && skill.surfaces.includes(surface.id));
|
|
472
553
|
state = canonical?.state ?? "unknown";
|
|
473
|
-
evidence = [
|
|
554
|
+
evidence = [
|
|
555
|
+
{
|
|
474
556
|
kind: "filesystem",
|
|
475
557
|
state,
|
|
476
558
|
detail: canonical?.detail ?? "canonical skill was not diagnosed",
|
|
477
559
|
path: canonical?.location,
|
|
478
560
|
surfaceId: surface.id,
|
|
479
|
-
}
|
|
561
|
+
},
|
|
562
|
+
];
|
|
480
563
|
const resources = resourceReports(expanded, plan, "skills", surface.id);
|
|
481
564
|
operations = resources.operations;
|
|
482
565
|
evidence.push(...resources.evidence);
|
|
483
566
|
state = aggregateState([state, resources.state]);
|
|
484
567
|
}
|
|
485
568
|
else {
|
|
486
|
-
const override = context.categoryEvidence?.[category]?.[surface.id]
|
|
487
|
-
?? (category === "trust" ? context.surfaceEvidence?.[surface.id]?.trusted : undefined)
|
|
488
|
-
?? (category === "enablement" ? context.surfaceEvidence?.[surface.id]?.enabled : undefined);
|
|
569
|
+
const override = context.categoryEvidence?.[category]?.[surface.id] ?? (category === "trust" ? context.surfaceEvidence?.[surface.id]?.trusted : undefined) ?? (category === "enablement" ? context.surfaceEvidence?.[surface.id]?.enabled : undefined);
|
|
489
570
|
state = evidenceState(override ?? "unknown", category);
|
|
490
|
-
evidence = [
|
|
571
|
+
evidence = [
|
|
572
|
+
{
|
|
491
573
|
kind: "contract",
|
|
492
574
|
state,
|
|
493
575
|
detail: override === undefined ? `${claim.evidence}; no verified host observation was supplied` : claim.evidence,
|
|
494
576
|
surfaceId: surface.id,
|
|
495
|
-
}
|
|
577
|
+
},
|
|
578
|
+
];
|
|
496
579
|
}
|
|
497
|
-
const diagnosisComplete = claim.required
|
|
498
|
-
? state !== "unknown" && claim.capability !== "unverified"
|
|
499
|
-
: true;
|
|
580
|
+
const diagnosisComplete = claim.required ? state !== "unknown" && claim.capability !== "unverified" : true;
|
|
500
581
|
const remediation = remediationFor(state, claim, operations, nativeActions, runtimeId, surface.id, evidencePath, context.sourceRoot);
|
|
501
582
|
assertSurfaceRemediationConsistency(category, state, operations, remediation);
|
|
502
583
|
return {
|
|
@@ -517,7 +598,7 @@ function canonicalDetection(skills) {
|
|
|
517
598
|
return "unknown";
|
|
518
599
|
return canonical.state === "confirmed" ? true : false;
|
|
519
600
|
}
|
|
520
|
-
function lifecycleObservation(runtimeId, surfaces, hostPresence, categories, canonicalSkillDetected, diagnosisComplete, context) {
|
|
601
|
+
function lifecycleObservation(runtimeId, surfaces, hostPresence, expectedSurfaces, categories, canonicalSkillDetected, diagnosisComplete, context) {
|
|
521
602
|
return {
|
|
522
603
|
runtimeId,
|
|
523
604
|
canonicalSkillDetected,
|
|
@@ -525,11 +606,9 @@ function lifecycleObservation(runtimeId, surfaces, hostPresence, categories, can
|
|
|
525
606
|
unmetMandatoryFields: [],
|
|
526
607
|
surfaces: surfaces.map((surface) => {
|
|
527
608
|
const host = hostPresence[surface.id] ?? "unknown";
|
|
528
|
-
const expected = surface.
|
|
609
|
+
const expected = expectedSurfaces[surface.id] === true;
|
|
529
610
|
const skill = categories.skills.surfaces.find((item) => item.surfaceId === surface.id);
|
|
530
|
-
const trust = context.categoryEvidence?.trust?.[surface.id]
|
|
531
|
-
?? context.surfaceEvidence?.[surface.id]?.trusted
|
|
532
|
-
?? "unknown";
|
|
611
|
+
const trust = context.categoryEvidence?.trust?.[surface.id] ?? context.surfaceEvidence?.[surface.id]?.trusted ?? "unknown";
|
|
533
612
|
let enabled;
|
|
534
613
|
if (!expected)
|
|
535
614
|
enabled = "not_applicable";
|
|
@@ -544,6 +623,7 @@ function lifecycleObservation(runtimeId, surfaces, hostPresence, categories, can
|
|
|
544
623
|
enabled = "unknown";
|
|
545
624
|
return {
|
|
546
625
|
id: surface.id,
|
|
626
|
+
applicability: surface.presence === "required" ? "required" : expected ? "conditional" : "not_applicable",
|
|
547
627
|
evidence: {
|
|
548
628
|
host_present: host,
|
|
549
629
|
installed: !expected ? "not_applicable" : canonicalSkillDetected,
|
|
@@ -581,11 +661,10 @@ export class RuntimeLifecycleAdapter {
|
|
|
581
661
|
throw new Error(`${this.runtimeId}: missing declared resource ${id}`);
|
|
582
662
|
return declaration;
|
|
583
663
|
});
|
|
584
|
-
const expanded = declarations
|
|
664
|
+
const expanded = declarations
|
|
665
|
+
.flatMap((declaration) => expandResource(declaration, values))
|
|
585
666
|
.map((resource) => {
|
|
586
|
-
if (resource.declaration.id !== "canonical_skill"
|
|
587
|
-
|| resource.operation?.kind !== "symlink"
|
|
588
|
-
|| !context.canonicalSkillTarget)
|
|
667
|
+
if (resource.declaration.id !== "canonical_skill" || resource.operation?.kind !== "symlink" || !context.canonicalSkillTarget)
|
|
589
668
|
return resource;
|
|
590
669
|
const target = path.resolve(context.canonicalSkillTarget);
|
|
591
670
|
return {
|
|
@@ -594,12 +673,8 @@ export class RuntimeLifecycleAdapter {
|
|
|
594
673
|
operation: { ...resource.operation, linkTarget: target },
|
|
595
674
|
};
|
|
596
675
|
});
|
|
597
|
-
const operations = expanded.flatMap((resource) => resource.operation ? [resource.operation] : []);
|
|
598
|
-
const allowedRoots = [...new Set([
|
|
599
|
-
path.resolve(context.sourceRoot),
|
|
600
|
-
...(context.canonicalSkillTarget ? [path.resolve(context.canonicalSkillTarget, "..")] : []),
|
|
601
|
-
...expanded.map((resource) => path.dirname(resource.destinationPath)),
|
|
602
|
-
])];
|
|
676
|
+
const operations = expanded.flatMap((resource) => (resource.operation ? [resource.operation] : []));
|
|
677
|
+
const allowedRoots = [...new Set([path.resolve(context.sourceRoot), ...(context.canonicalSkillTarget ? [path.resolve(context.canonicalSkillTarget, "..")] : []), ...expanded.map((resource) => path.dirname(resource.destinationPath))])];
|
|
603
678
|
const repairPlan = planLifecycleOperations({
|
|
604
679
|
allowedRoots,
|
|
605
680
|
operations,
|
|
@@ -609,17 +684,14 @@ export class RuntimeLifecycleAdapter {
|
|
|
609
684
|
const skills = this.runtime.skillLocations.map((location) => probeSkill(expandTemplate(location.path, values), location.canonical, location.surfaces));
|
|
610
685
|
const hostPresence = {};
|
|
611
686
|
for (const surface of this.runtimeAuthority.surfaces) {
|
|
612
|
-
hostPresence[surface.id] = context.surfaceEvidence?.[surface.id]?.host_present
|
|
613
|
-
?? probeBinary(this.runtime.binaries[surface.id], env);
|
|
687
|
+
hostPresence[surface.id] = context.surfaceEvidence?.[surface.id]?.host_present ?? probeBinary(this.runtime.binaries[surface.id], env);
|
|
614
688
|
}
|
|
689
|
+
const expectedSurfaces = Object.fromEntries(this.runtimeAuthority.surfaces.map((surface) => [surface.id, lifecycleSurfaceExpected(surface, hostPresence[surface.id], expanded, repairPlan, skills)]));
|
|
615
690
|
const categories = {};
|
|
616
691
|
for (const category of RUNTIME_ADAPTER_CATEGORIES) {
|
|
617
692
|
const surfaceReports = this.runtimeAuthority.surfaces.map((surface) => {
|
|
618
|
-
const
|
|
619
|
-
|
|
620
|
-
return categorySurface(this.runtimeId, surface, category, this.runtime.categories[category][surface.id], expected, context, values, expanded, repairPlan, skills, category === "native_actions"
|
|
621
|
-
? this.runtime.nativeActions.filter((action) => action.surfaceId === surface.id)
|
|
622
|
-
: []);
|
|
693
|
+
const expected = expectedSurfaces[surface.id] === true;
|
|
694
|
+
return categorySurface(this.runtimeId, surface, category, this.runtime.categories[category][surface.id], expected, context, values, expanded, repairPlan, skills, category === "native_actions" ? this.runtime.nativeActions.filter((action) => action.surfaceId === surface.id) : []);
|
|
623
695
|
});
|
|
624
696
|
categories[category] = {
|
|
625
697
|
category,
|
|
@@ -631,12 +703,9 @@ export class RuntimeLifecycleAdapter {
|
|
|
631
703
|
}
|
|
632
704
|
const detected = canonicalDetection(skills);
|
|
633
705
|
const requiredSkillReports = categories.skills.surfaces.filter((surface) => surface.required);
|
|
634
|
-
const diagnosisComplete = detected !== "unknown"
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
const observation = lifecycleObservation(this.runtimeId, this.runtimeAuthority.surfaces, hostPresence, categories, detected, diagnosisComplete, context);
|
|
638
|
-
const authoritativeState = buildRuntimeLifecycleState(this.authority, [observation])
|
|
639
|
-
.runtimes.find((runtime) => runtime.runtimeId === this.runtimeId);
|
|
706
|
+
const diagnosisComplete = detected !== "unknown" && requiredSkillReports.length > 0 && requiredSkillReports.every((surface) => surface.diagnosisComplete);
|
|
707
|
+
const observation = lifecycleObservation(this.runtimeId, this.runtimeAuthority.surfaces, hostPresence, expectedSurfaces, categories, detected, diagnosisComplete, context);
|
|
708
|
+
const authoritativeState = buildRuntimeLifecycleState(this.authority, [observation]).runtimes.find((runtime) => runtime.runtimeId === this.runtimeId);
|
|
640
709
|
if (!authoritativeState)
|
|
641
710
|
throw new Error(`${this.runtimeId}: lifecycle authority state is missing`);
|
|
642
711
|
const supportFloor = {
|
|
@@ -645,12 +714,7 @@ export class RuntimeLifecycleAdapter {
|
|
|
645
714
|
};
|
|
646
715
|
const expectedCategorySurfaces = RUNTIME_ADAPTER_CATEGORIES.flatMap((category) => categories[category].surfaces.filter((surface) => surface.expected));
|
|
647
716
|
const degraded = expectedCategorySurfaces.some((surface) => !["confirmed", "not_applicable"].includes(surface.state));
|
|
648
|
-
const caveats = [
|
|
649
|
-
...this.contract.caveats,
|
|
650
|
-
...expanded
|
|
651
|
-
.filter((resource) => resource.sourceError)
|
|
652
|
-
.map((resource) => `${resource.declaration.id}: ${resource.sourceError}`),
|
|
653
|
-
];
|
|
717
|
+
const caveats = [...this.contract.caveats, ...expanded.filter((resource) => resource.sourceError).map((resource) => `${resource.declaration.id}: ${resource.sourceError}`)];
|
|
654
718
|
return {
|
|
655
719
|
schemaVersion: "agentera.runtimeAdapterReport.v1",
|
|
656
720
|
runtimeId: this.runtimeId,
|
|
@@ -686,12 +750,7 @@ export class CopilotLifecycleAdapter extends RuntimeLifecycleAdapter {
|
|
|
686
750
|
}
|
|
687
751
|
}
|
|
688
752
|
export function runtimeLifecycleAdapters(contract = loadRuntimeLifecycleAdapterContract(), authority = loadLifecycleAuthority()) {
|
|
689
|
-
return [
|
|
690
|
-
new OpenCodeLifecycleAdapter(contract, authority),
|
|
691
|
-
new CodexLifecycleAdapter(contract, authority),
|
|
692
|
-
new CursorLifecycleAdapter(contract, authority),
|
|
693
|
-
new CopilotLifecycleAdapter(contract, authority),
|
|
694
|
-
];
|
|
753
|
+
return [new OpenCodeLifecycleAdapter(contract, authority), new CodexLifecycleAdapter(contract, authority), new CursorLifecycleAdapter(contract, authority), new CopilotLifecycleAdapter(contract, authority)];
|
|
695
754
|
}
|
|
696
755
|
export function inspectRuntimeLifecycleAdapters(context, contract = loadRuntimeLifecycleAdapterContract(), authority = loadLifecycleAuthority()) {
|
|
697
756
|
const reports = runtimeLifecycleAdapters(contract, authority).map((adapter) => adapter.inspect(context));
|