agentera 3.0.0-dev.72 → 3.0.0-dev.75
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 +111 -0
- package/bundle/.agentera-build-source.json +8 -0
- package/bundle/CHANGELOG.md +17 -17
- package/bundle/extract-corpus-parity.json +1 -1
- package/bundle/references/adapters/package-publication.json +49 -53
- package/bundle/references/adapters/package-registry.yaml +5 -1
- package/bundle/references/adapters/package-surface-characterization.md +6 -4
- package/bundle/references/analysis/evidence-tier-authority.yaml +45 -4
- package/bundle/references/analysis/personal-glossary-evaluation-authority.yaml +175 -0
- package/bundle/references/analysis/personal-glossary-evaluation-corpus.yaml +291 -0
- package/bundle/references/analysis/personal-glossary-holdout.yaml +192 -0
- package/bundle/references/analysis/verification-policy.yaml +8 -13
- package/bundle/references/artifacts/glossary-entry-contract.yaml +1533 -35
- package/bundle/references/cli/update-channels.yaml +2 -2
- package/bundle/references/cli/vocabulary.md +1 -1
- package/bundle/references/meta/retained-reference-authority.yaml +16 -1
- package/bundle/skills/agentera/capabilities/profile/schemas/artifacts.yaml +11 -7
- package/bundle/skills/agentera/capabilities/profile/schemas/exit.yaml +6 -3
- package/bundle/skills/agentera/capabilities/profile/schemas/validation.yaml +13 -7
- package/bundle/skills/agentera/schemas/artifacts/glossary.yaml +5 -0
- package/bundle/skills/agentera/schemas/artifacts/plan.yaml +7 -0
- package/dist/.agentera-build-source.json +8 -0
- package/dist/analytics/extractCorpus/copilotSessions.js +13 -4
- package/dist/analytics/extractCorpus/core.js +223 -3
- package/dist/analytics/extractCorpus/corpus.js +18 -1
- package/dist/analytics/extractCorpus/cursorSessions.js +22 -4
- package/dist/analytics/extractCorpus/evidenceTiers.js +123 -17
- package/dist/analytics/extractCorpus/filesystemSources.js +4 -0
- package/dist/analytics/extractCorpus/index.js +2 -2
- package/dist/analytics/extractCorpus/jsonlSessions.js +52 -3
- package/dist/analytics/extractCorpus/sqliteSessions.js +12 -3
- package/dist/analytics/personalGlossaryAdmission.js +26 -46
- package/dist/analytics/personalGlossaryCandidateProjection.js +692 -0
- package/dist/analytics/personalGlossaryCandidateProjectionExcerpts.js +144 -0
- package/dist/analytics/personalGlossaryCandidateReadView.js +23 -0
- package/dist/analytics/personalGlossaryCurrentGeneration.js +25 -0
- package/dist/analytics/personalGlossaryDecision.js +203 -0
- package/dist/analytics/personalGlossaryExplicit.js +788 -0
- package/dist/analytics/personalGlossaryExplicitMining.js +285 -0
- package/dist/analytics/personalGlossaryExplicitScope.js +41 -0
- package/dist/analytics/personalGlossaryExplicitSegments.js +418 -0
- package/dist/analytics/personalGlossaryExplicitTypes.js +26 -0
- package/dist/analytics/personalGlossaryLexicalClassifier.js +175 -0
- package/dist/analytics/personalGlossaryProfile.js +13 -3
- package/dist/analytics/personalGlossaryRecurrence.js +618 -0
- package/dist/analytics/personalGlossaryReviewRecordStorage.js +428 -0
- package/dist/analytics/personalGlossaryReviewRecords.js +332 -0
- package/dist/capabilities/index.js +5 -1
- package/dist/capabilities/profile/instructions.js +50 -15
- package/dist/cli/commands/personalGlossaryCandidateReads.js +744 -0
- package/dist/cli/commands/personalGlossaryDecision.js +349 -0
- package/dist/cli/commands/personalGlossaryPublish.js +484 -0
- package/dist/cli/commands/personalGlossaryReviewRecordReads.js +209 -0
- package/dist/cli/commands/personalGlossaryReviewRecords.js +623 -0
- package/dist/cli/commands/prime/briefOrientation.js +118 -19
- package/dist/cli/commands/report.js +18 -2
- package/dist/cli/commands/schema.js +179 -0
- package/dist/cli/commands/verify.js +51 -9
- package/dist/cli/dispatch/lifecycle.js +51 -6
- package/dist/cli/help.js +48 -2
- package/dist/cli/migrationRequired.js +4 -1
- package/dist/core/atomicWriter.js +4 -1
- package/dist/core/developmentInvocation.js +4 -1
- package/dist/core/yaml.js +53 -1
- package/dist/eval/glossaryEvaluation.js +751 -0
- package/dist/eval/glossaryEvaluationProcess.js +29 -0
- package/dist/eval/glossaryEvaluationRunner.js +571 -0
- package/dist/eval/glossaryEvaluationSuccessReport.js +297 -0
- package/dist/registries/activationTuples.js +31 -3
- package/dist/registries/evidenceTierContract.js +4 -0
- package/dist/registries/explicitSegmentGrammarContract.js +619 -0
- package/dist/registries/glossaryCandidateContractPaths.js +6 -0
- package/dist/registries/glossaryCandidateContracts.js +820 -0
- package/dist/registries/glossaryCandidateDecisionAuthority.js +119 -0
- package/dist/registries/glossaryCandidateDecisionContract.js +62 -0
- package/dist/registries/glossaryCandidateProjectionAuthority.js +197 -0
- package/dist/registries/glossaryCandidateProjectionContract.js +104 -0
- package/dist/registries/glossaryEntryContract.js +105 -103
- package/dist/registries/glossaryEntryTemporal.js +12 -0
- package/dist/registries/glossaryMiningAuthority.js +764 -0
- package/dist/registries/glossaryProfileFullContract.js +24 -0
- package/dist/registries/glossaryReviewRecordsAuthority.js +426 -0
- package/dist/registries/glossaryReviewRecordsContract.js +129 -0
- package/dist/registries/glossaryTermIdentity.js +122 -0
- package/dist/registries/packagePublication.js +3 -3
- package/dist/registries/personalGlossaryContracts.js +1 -0
- package/dist/release/releaseMetadata.js +15 -8
- package/dist/upgrade/npxPlatformStatus.js +2 -1
- package/dist/upgrade/projectIntegration.js +7 -1
- package/dist/validate/activationArtifactEvidence.js +18 -4
- package/dist/validate/activationEvidenceManifest.js +101 -22
- package/package.json +2 -4
- package/dist/cli/commands/personalGlossary.js +0 -176
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
function escapeRegExp(value) {
|
|
2
3
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3
4
|
}
|
|
@@ -15,3 +16,124 @@ export function unicodeCaselessExact(left, right) {
|
|
|
15
16
|
const literal = escapeRegExp(left);
|
|
16
17
|
return new RegExp(`^(?:${literal})$(?![\\s\\S])`, "iu").test(right);
|
|
17
18
|
}
|
|
19
|
+
function compareCodePoints(left, right) {
|
|
20
|
+
const leftCodePoint = left.codePointAt(0) ?? 0;
|
|
21
|
+
const rightCodePoint = right.codePointAt(0) ?? 0;
|
|
22
|
+
return leftCodePoint - rightCodePoint;
|
|
23
|
+
}
|
|
24
|
+
export function compareGlossaryUnicodeStrings(left, right) {
|
|
25
|
+
const leftScalars = [...left];
|
|
26
|
+
const rightScalars = [...right];
|
|
27
|
+
for (let index = 0; index < Math.min(leftScalars.length, rightScalars.length); index += 1) {
|
|
28
|
+
const comparison = compareCodePoints(leftScalars[index], rightScalars[index]);
|
|
29
|
+
if (comparison !== 0)
|
|
30
|
+
return comparison;
|
|
31
|
+
}
|
|
32
|
+
return leftScalars.length - rightScalars.length;
|
|
33
|
+
}
|
|
34
|
+
function singleScalar(value) {
|
|
35
|
+
return [...value].length === 1 ? value : null;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Build the same simple-case equivalence key used by the stable identity.
|
|
39
|
+
* Lowercase mapping is the primary Unicode simple-fold projection. A
|
|
40
|
+
* lowercased uppercase mapping is admitted only when the existing equality
|
|
41
|
+
* matcher confirms it, which avoids transitive casing mistakes such as
|
|
42
|
+
* treating ASCII `I` and dotless `ı` as equal.
|
|
43
|
+
*/
|
|
44
|
+
function unicodeSimpleCaseFoldKey(value) {
|
|
45
|
+
return [...value]
|
|
46
|
+
.map((character) => {
|
|
47
|
+
const lower = singleScalar(character.toLowerCase()) ?? character;
|
|
48
|
+
const upper = singleScalar(character.toUpperCase());
|
|
49
|
+
const upperLower = upper === null ? null : singleScalar(upper.toLowerCase());
|
|
50
|
+
if (upperLower !== null && unicodeCaselessExact(character, upperLower)) {
|
|
51
|
+
return upperLower;
|
|
52
|
+
}
|
|
53
|
+
return lower;
|
|
54
|
+
})
|
|
55
|
+
.join("");
|
|
56
|
+
}
|
|
57
|
+
function sha256Utf8(value) {
|
|
58
|
+
return createHash("sha256").update(value, "utf8").digest("hex");
|
|
59
|
+
}
|
|
60
|
+
function requireNonEmpty(value, field) {
|
|
61
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
62
|
+
throw new TypeError(`${field} must be a non-empty string`);
|
|
63
|
+
}
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Return a content-addressed identity for one Unicode caseless-exact term.
|
|
68
|
+
* The original spelling is not changed or stored in the identity key.
|
|
69
|
+
*/
|
|
70
|
+
export function stableGlossaryTermIdentity(term) {
|
|
71
|
+
const value = requireNonEmpty(term, "term");
|
|
72
|
+
const key = unicodeSimpleCaseFoldKey(value);
|
|
73
|
+
return sha256Utf8(JSON.stringify({
|
|
74
|
+
schema_version: "agentera.personalGlossaryTermIdentity.v1",
|
|
75
|
+
key,
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
export const GLOSSARY_CANDIDATE_SCOPES = ["personal", "project", "ambiguous"];
|
|
79
|
+
export function isGlossaryCandidateScope(value) {
|
|
80
|
+
return GLOSSARY_CANDIDATE_SCOPES.includes(value);
|
|
81
|
+
}
|
|
82
|
+
export function canonicalGlossaryJson(value) {
|
|
83
|
+
if (value === undefined)
|
|
84
|
+
throw new TypeError("canonical JSON cannot contain undefined");
|
|
85
|
+
if (value === null || typeof value !== "object")
|
|
86
|
+
return JSON.stringify(value);
|
|
87
|
+
if (Array.isArray(value))
|
|
88
|
+
return `[${value.map(canonicalGlossaryJson).join(",")}]`;
|
|
89
|
+
const record = value;
|
|
90
|
+
return `{${Object.keys(record)
|
|
91
|
+
.sort(compareGlossaryUnicodeStrings)
|
|
92
|
+
.map((key) => `${JSON.stringify(key)}:${canonicalGlossaryJson(record[key])}`)
|
|
93
|
+
.join(",")}}`;
|
|
94
|
+
}
|
|
95
|
+
export function glossaryCanonicalSha256(value) {
|
|
96
|
+
return sha256Utf8(canonicalGlossaryJson(value));
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Return a content-addressed candidate revision without mining or publishing.
|
|
100
|
+
* Evidence order is not semantic, but every evidence identity remains bound.
|
|
101
|
+
*/
|
|
102
|
+
export function glossaryCandidateRevision(input) {
|
|
103
|
+
const stableTermIdentity = requireNonEmpty(input.stable_term_identity, "stable_term_identity");
|
|
104
|
+
const meaning = requireNonEmpty(input.meaning, "meaning");
|
|
105
|
+
const policyVersion = requireNonEmpty(input.policy_version, "policy_version");
|
|
106
|
+
const generation = requireNonEmpty(input.generation, "generation");
|
|
107
|
+
if (!Array.isArray(input.evidence))
|
|
108
|
+
throw new TypeError("evidence must be a list");
|
|
109
|
+
if (!isGlossaryCandidateScope(input.scope)) {
|
|
110
|
+
throw new TypeError("scope must be personal, project, or ambiguous");
|
|
111
|
+
}
|
|
112
|
+
const evidence = input.evidence.map((item) => {
|
|
113
|
+
requireNonEmpty(item.source_id, "evidence.source_id");
|
|
114
|
+
requireNonEmpty(item.evidence_anchor, "evidence.evidence_anchor");
|
|
115
|
+
return item;
|
|
116
|
+
});
|
|
117
|
+
const canonicalEvidence = new Set();
|
|
118
|
+
for (const item of evidence) {
|
|
119
|
+
const canonical = canonicalGlossaryJson(item);
|
|
120
|
+
if (canonicalEvidence.has(canonical)) {
|
|
121
|
+
throw new TypeError("evidence must not contain duplicate canonical evidence records");
|
|
122
|
+
}
|
|
123
|
+
canonicalEvidence.add(canonical);
|
|
124
|
+
}
|
|
125
|
+
evidence.sort((left, right) => {
|
|
126
|
+
const leftCanonical = canonicalGlossaryJson(left);
|
|
127
|
+
const rightCanonical = canonicalGlossaryJson(right);
|
|
128
|
+
return compareGlossaryUnicodeStrings(leftCanonical, rightCanonical);
|
|
129
|
+
});
|
|
130
|
+
return sha256Utf8(canonicalGlossaryJson({
|
|
131
|
+
schema_version: "agentera.personalGlossaryCandidateRevision.v1",
|
|
132
|
+
stable_term_identity: stableTermIdentity,
|
|
133
|
+
meaning,
|
|
134
|
+
scope: input.scope,
|
|
135
|
+
evidence,
|
|
136
|
+
policy_version: policyVersion,
|
|
137
|
+
generation,
|
|
138
|
+
}));
|
|
139
|
+
}
|
|
@@ -52,12 +52,12 @@ export const ACTIVATION_CENSUS_AUTHORITY = deepFreeze({
|
|
|
52
52
|
cli: { count: 27, sha256: "986a637f5c11c57ab7a897a01bd291d0811846e5339c3831c715932cf9659ad4" },
|
|
53
53
|
capability: { count: 12, sha256: "007e1157a892fec54182b1803aeaa442cf8e2e332e6a055c3bd020e2b0731867" },
|
|
54
54
|
runtime: { count: 81, sha256: "03fe600a3a27f05daf0d1b59c16b00bedc354a13c70ea60be0b457175fdf743c" },
|
|
55
|
-
reference: { count:
|
|
55
|
+
reference: { count: 25, sha256: "2094946cd6e189c6ab3db16dd0725596cec759abcea88d8335b05215b65ccfc2" },
|
|
56
56
|
state: { count: 38, sha256: "7ab0dd6ef1b1b1ce66bd9ea94d1de3d542528233e928a44102e289bf12416681" },
|
|
57
|
-
package: { count:
|
|
57
|
+
package: { count: 67, sha256: "d73cd86fed39057cf9ae73e462a59e842372392b1e88846f2b220378a2ff75cc" },
|
|
58
58
|
bootstrap: { count: 34, sha256: "71c2038744e2518a6adb722acbb5f9352bddfb5d1c92eeb0e347297ef2ca2f1e" },
|
|
59
59
|
},
|
|
60
|
-
total: { count:
|
|
60
|
+
total: { count: 284, sha256: "3f30b26839aec47639f2197c9efda5caf2a73d5afca9fb38ff9a341e18a72b98" },
|
|
61
61
|
});
|
|
62
62
|
/** Each dimension names the production contract it observes independently. */
|
|
63
63
|
export const ACTIVATION_EVIDENCE_SOURCES = deepFreeze({
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -184,7 +184,7 @@ function readPackageJson(root) {
|
|
|
184
184
|
}
|
|
185
185
|
return readJson(candidate);
|
|
186
186
|
}
|
|
187
|
-
function readUpdateChannels(root) {
|
|
187
|
+
export function readUpdateChannels(root) {
|
|
188
188
|
const candidate = path.join(root, "references/cli/update-channels.yaml");
|
|
189
189
|
if (!fs.existsSync(candidate)) {
|
|
190
190
|
return null;
|
|
@@ -231,22 +231,22 @@ function pickChannelDefaults(authority) {
|
|
|
231
231
|
stable: typeof defaults.stable === "string" ? defaults.stable : null,
|
|
232
232
|
};
|
|
233
233
|
}
|
|
234
|
-
export function readReleaseMetadata(root = rootDefault()) {
|
|
234
|
+
export function readReleaseMetadata(root = rootDefault(), packageOverride) {
|
|
235
235
|
const resolved = resolvePath(root);
|
|
236
236
|
const registry = readRegistryVersion(resolved);
|
|
237
237
|
const pkg = readPackageJson(resolved);
|
|
238
238
|
const channels = readUpdateChannels(resolved);
|
|
239
239
|
const bundle = readBundleSentinel(resolved);
|
|
240
|
-
const topVersion = pkg && typeof pkg.version === "string" ? pkg.version : null;
|
|
240
|
+
const topVersion = packageOverride?.version ?? (pkg && typeof pkg.version === "string" ? pkg.version : null);
|
|
241
241
|
const agenteraBlock = pkg && typeof pkg.agentera === "object" && pkg.agentera !== null
|
|
242
242
|
? pkg.agentera
|
|
243
243
|
: null;
|
|
244
244
|
const suiteVersion = agenteraBlock && typeof agenteraBlock.suiteVersion === "string"
|
|
245
245
|
? agenteraBlock.suiteVersion
|
|
246
246
|
: null;
|
|
247
|
-
const gitRef = agenteraBlock && typeof agenteraBlock.gitRef === "string"
|
|
247
|
+
const gitRef = packageOverride?.gitRef ?? (agenteraBlock && typeof agenteraBlock.gitRef === "string"
|
|
248
248
|
? agenteraBlock.gitRef
|
|
249
|
-
: null;
|
|
249
|
+
: null);
|
|
250
250
|
const { development, stable } = pickChannelDefaults(channels);
|
|
251
251
|
return {
|
|
252
252
|
authoritativeVersion: registry.version,
|
|
@@ -263,7 +263,7 @@ export function readReleaseMetadata(root = rootDefault()) {
|
|
|
263
263
|
* Validate release-metadata coherence. Returns an empty array when the
|
|
264
264
|
* source-tree version surfaces agree on a single release train.
|
|
265
265
|
*/
|
|
266
|
-
export function validateReleaseMetadata(root = rootDefault(), adapter = "development") {
|
|
266
|
+
export function validateReleaseMetadata(root = rootDefault(), adapter = "development", packageOverride) {
|
|
267
267
|
if (adapter === "stable") {
|
|
268
268
|
const manifestPath = "packages/cli/shim/package.json";
|
|
269
269
|
const shim = readJson(path.join(resolvePath(root), manifestPath));
|
|
@@ -279,7 +279,7 @@ export function validateReleaseMetadata(root = rootDefault(), adapter = "develop
|
|
|
279
279
|
return validateStableShimGitRefProvenance(resolvePath(root), gitRef);
|
|
280
280
|
}
|
|
281
281
|
const errors = [];
|
|
282
|
-
const snap = readReleaseMetadata(root);
|
|
282
|
+
const snap = readReleaseMetadata(root, packageOverride);
|
|
283
283
|
const rootLabel = path.basename(resolvePath(root)) || "repo root";
|
|
284
284
|
// 1. registry.json must publish a valid semver `skills[0].version`.
|
|
285
285
|
if (snap.authoritativeVersion === null) {
|
|
@@ -373,7 +373,14 @@ export function releaseMetadataMain(opts = {}) {
|
|
|
373
373
|
const root = resolvePath(opts.root ?? rootDefault());
|
|
374
374
|
const out = opts.out ?? ((line) => process.stdout.write(line + "\n"));
|
|
375
375
|
const err = opts.err ?? ((line) => process.stderr.write(line + "\n"));
|
|
376
|
-
const
|
|
376
|
+
const overrideVersion = process.env.AGENTERA_RELEASE_PACKAGE_VERSION;
|
|
377
|
+
const overrideGitRef = process.env.AGENTERA_RELEASE_GIT_REF;
|
|
378
|
+
if ((overrideVersion === undefined) !== (overrideGitRef === undefined)) {
|
|
379
|
+
out("release-metadata validation failed:");
|
|
380
|
+
out("- AGENTERA_RELEASE_PACKAGE_VERSION and AGENTERA_RELEASE_GIT_REF must be supplied together");
|
|
381
|
+
return 1;
|
|
382
|
+
}
|
|
383
|
+
const errors = validateReleaseMetadata(root, opts.adapter, overrideVersion === undefined ? undefined : { version: overrideVersion, gitRef: overrideGitRef });
|
|
377
384
|
if (errors.length > 0) {
|
|
378
385
|
out("release-metadata validation failed:");
|
|
379
386
|
for (const message of errors)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { expanduser } from "../core/paths.js";
|
|
2
|
+
import { SOURCE_LABELS } from "../state/installRoot.js";
|
|
2
3
|
import { resolvePlatformAppHome } from "./appModel.js";
|
|
3
4
|
import { buildDoctorStatus } from "./doctor.js";
|
|
4
5
|
/** Shared npx platform app-home resolution for prime and projectIntegration. */
|
|
@@ -6,7 +7,7 @@ export function resolveNpxPlatformStatus(opts) {
|
|
|
6
7
|
const env = { ...process.env, ...(opts.env ?? {}), HOME: expanduser(opts.home) };
|
|
7
8
|
const platformRoot = resolvePlatformAppHome(opts.home, env);
|
|
8
9
|
const doctorOpts = {
|
|
9
|
-
rootSource:
|
|
10
|
+
rootSource: SOURCE_LABELS.default,
|
|
10
11
|
sourceRoot: opts.sourceRoot,
|
|
11
12
|
home: opts.home,
|
|
12
13
|
project: opts.project,
|
|
@@ -65,10 +65,16 @@ function resolveIntegrationTargets(args) {
|
|
|
65
65
|
project: args.project,
|
|
66
66
|
env: args.env,
|
|
67
67
|
});
|
|
68
|
+
// The executing npm bundle is already a complete app. A missing optional
|
|
69
|
+
// platform install therefore needs no integration work, while an existing
|
|
70
|
+
// stale or damaged platform install still retains its observed status.
|
|
71
|
+
const platformBundleStatus = platformStatus.rootStatus === "missing"
|
|
72
|
+
? APP_UP_TO_DATE
|
|
73
|
+
: platformStatus.status;
|
|
68
74
|
return {
|
|
69
75
|
installRoot: platformRoot,
|
|
70
76
|
bundleStatus: args.bundleStatus,
|
|
71
|
-
platformBundleStatus
|
|
77
|
+
platformBundleStatus,
|
|
72
78
|
crossMajorBoundary: Boolean(platformStatus.crossMajorBoundary),
|
|
73
79
|
crossMajorBoundaryDetected: Boolean(platformStatus.crossMajorBoundaryDetected),
|
|
74
80
|
};
|
|
@@ -452,6 +452,12 @@ function createRecord(producerKind, artifactClass, artifactIdentity, artifactCon
|
|
|
452
452
|
function finishEvidence(value) {
|
|
453
453
|
return { ...value, evidenceDigest: observationDigest(value) };
|
|
454
454
|
}
|
|
455
|
+
function packageManifestSummary(files) {
|
|
456
|
+
const normalized = [...files]
|
|
457
|
+
.map(({ path: file, size, mode }) => ({ path: file, size, mode }))
|
|
458
|
+
.sort((left, right) => left.path.localeCompare(right.path));
|
|
459
|
+
return { count: normalized.length, digest: observationDigest(normalized) };
|
|
460
|
+
}
|
|
455
461
|
function packageRecord(root) {
|
|
456
462
|
return loadRegistry(path.join(root, "references/adapters/package-registry.yaml"), root).get("agentera");
|
|
457
463
|
}
|
|
@@ -639,6 +645,7 @@ export async function finalizePackageOwnerEvidence(options) {
|
|
|
639
645
|
const record = packageRecord(path.join(runtimeRoot, "bundle"));
|
|
640
646
|
const packageArtifact = packageArtifactObservation(runtimeRoot, fixture, options.requiredFiles, options.snapshotDirectory);
|
|
641
647
|
const integrity = packageArtifact.integrity;
|
|
648
|
+
const secondManifestFiles = fixture.pathIndependence.secondManifest.files;
|
|
642
649
|
const portability = {
|
|
643
650
|
deterministicPackRuns: fixture.deterministicBytes.packRuns,
|
|
644
651
|
deterministicTarballSha256: fixture.deterministicBytes.sha256,
|
|
@@ -655,7 +662,7 @@ export async function finalizePackageOwnerEvidence(options) {
|
|
|
655
662
|
filename: fixture.pathIndependence.secondManifest.filename,
|
|
656
663
|
integrity: fixture.pathIndependence.secondManifest.integrity,
|
|
657
664
|
shasum: fixture.pathIndependence.secondManifest.shasum,
|
|
658
|
-
files:
|
|
665
|
+
files: packageManifestSummary(secondManifestFiles),
|
|
659
666
|
},
|
|
660
667
|
};
|
|
661
668
|
const records = {
|
|
@@ -716,7 +723,7 @@ export function createGeneratedOwnerEvidence(options) {
|
|
|
716
723
|
const bundleRoot = path.join(options.generationRoot, "bundle");
|
|
717
724
|
const record = packageRecord(bundleRoot);
|
|
718
725
|
const bootstrap = runtimeObservation.bootstrap;
|
|
719
|
-
const generationFiles = [".agentera-generation.json", "dist/.agentera-generation.json", "bundle/.agentera-generation.json", "bundle/.agentera-npx-bundle.json", "bundle/extract-corpus-parity.json"]
|
|
726
|
+
const generationFiles = [".agentera-generation.json", "dist/.agentera-generation.json", "bundle/.agentera-generation.json", "dist/.agentera-build-source.json", "bundle/.agentera-build-source.json", "bundle/.agentera-npx-bundle.json", "bundle/extract-corpus-parity.json"]
|
|
720
727
|
.filter((file) => fs.existsSync(path.join(options.generationRoot, file)));
|
|
721
728
|
const records = {
|
|
722
729
|
"capability.generated-modules": createRecord("generated-owner", "capability-generated-modules", "generation/dist/capabilities/*/instructions.js", capabilities.artifacts.modules, capabilities.modules, options.generation, null),
|
|
@@ -746,8 +753,15 @@ export function createGeneratedOwnerEvidence(options) {
|
|
|
746
753
|
export function writeContentAddressedOwnerEvidence(directory, evidence) {
|
|
747
754
|
const bytes = `${canonicalObservationJson(evidence)}\n`;
|
|
748
755
|
const byteLength = Buffer.byteLength(bytes, "utf8");
|
|
749
|
-
if (byteLength > OWNER_EVIDENCE_MAX_BYTES)
|
|
750
|
-
|
|
756
|
+
if (byteLength > OWNER_EVIDENCE_MAX_BYTES) {
|
|
757
|
+
const contributors = Object.entries(evidence.records)
|
|
758
|
+
.map(([ref, record]) => [ref, Buffer.byteLength(canonicalObservationJson(record), "utf8")])
|
|
759
|
+
.sort((left, right) => right[1] - left[1])
|
|
760
|
+
.slice(0, 3)
|
|
761
|
+
.map(([ref, length]) => `${ref}=${length}`)
|
|
762
|
+
.join(", ");
|
|
763
|
+
throw new Error(`activation owner evidence is ${byteLength} bytes, over the ${OWNER_EVIDENCE_MAX_BYTES}-byte bound; largest records: ${contributors}`);
|
|
764
|
+
}
|
|
751
765
|
fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
752
766
|
const name = `${evidence.producerKind}-${evidence.evidenceDigest}.json`;
|
|
753
767
|
const target = path.join(directory, name);
|
|
@@ -185,6 +185,57 @@ export function createActivationEvidenceManifest(options) {
|
|
|
185
185
|
};
|
|
186
186
|
return { ...unsigned, manifestDigest: observationDigest(unsigned) };
|
|
187
187
|
}
|
|
188
|
+
function deepFreeze(value, seen = new WeakSet()) {
|
|
189
|
+
if (!value || typeof value !== "object" || seen.has(value))
|
|
190
|
+
return value;
|
|
191
|
+
seen.add(value);
|
|
192
|
+
for (const child of Object.values(value))
|
|
193
|
+
deepFreeze(child, seen);
|
|
194
|
+
return Object.freeze(value);
|
|
195
|
+
}
|
|
196
|
+
/** Observe, snapshot, assemble, and validate immediate release evidence inside one trust boundary. */
|
|
197
|
+
export function assembleAndValidateActivationEvidence(context) {
|
|
198
|
+
let sourceObserverCalls = 0;
|
|
199
|
+
let generatedObserverCalls = 0;
|
|
200
|
+
sourceObserverCalls += 1;
|
|
201
|
+
const sourceEvidence = deepFreeze(structuredClone(context.sourceEvidence));
|
|
202
|
+
generatedObserverCalls += 1;
|
|
203
|
+
const observedGeneratedEvidence = createGeneratedOwnerEvidence({
|
|
204
|
+
root: context.root,
|
|
205
|
+
generationRoot: context.generationRoot,
|
|
206
|
+
generation: context.generation,
|
|
207
|
+
productionInputs: context.productionInputs,
|
|
208
|
+
});
|
|
209
|
+
const generatedEvidence = deepFreeze(structuredClone(observedGeneratedEvidence));
|
|
210
|
+
const productionEvidence = deepFreeze(structuredClone(context.productionEvidence));
|
|
211
|
+
const packageEvidence = deepFreeze(structuredClone(context.packageEvidence));
|
|
212
|
+
const expectedPackageIdentity = deepFreeze(structuredClone(context.expectedPackageIdentity));
|
|
213
|
+
const manifest = deepFreeze(createActivationEvidenceManifest({
|
|
214
|
+
root: context.root,
|
|
215
|
+
generation: context.generation,
|
|
216
|
+
productionEvidence,
|
|
217
|
+
sourceEvidence,
|
|
218
|
+
generatedEvidence,
|
|
219
|
+
packageEvidence,
|
|
220
|
+
}));
|
|
221
|
+
const violations = activationEvidenceViolationsInternal(manifest, {
|
|
222
|
+
root: context.root,
|
|
223
|
+
generationRoot: context.generationRoot,
|
|
224
|
+
generation: context.generation,
|
|
225
|
+
sourceEvidence,
|
|
226
|
+
generatedEvidence,
|
|
227
|
+
expectedPackageIdentity,
|
|
228
|
+
});
|
|
229
|
+
if (violations.length > 0)
|
|
230
|
+
throw new Error(violations[0]);
|
|
231
|
+
return {
|
|
232
|
+
manifest: structuredClone(manifest),
|
|
233
|
+
observerCalls: {
|
|
234
|
+
source: sourceObserverCalls,
|
|
235
|
+
generated: generatedObserverCalls,
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
}
|
|
188
239
|
function validateOwnerEvidence(owner, producerKind, schemaVersion, sourceDigest, generation, packageIntegrity, violations) {
|
|
189
240
|
if (!owner || owner.schemaVersion !== schemaVersion || owner.producerKind !== producerKind) {
|
|
190
241
|
violations.push(`${producerKind} evidence schema or producer is wrong`);
|
|
@@ -326,12 +377,25 @@ function validateExecutedArtifactSemantics(manifest, violations) {
|
|
|
326
377
|
violations.push("complete extracted package tree is not bound to the current tarball payload");
|
|
327
378
|
}
|
|
328
379
|
const portability = records.get("package.portability")?.content;
|
|
380
|
+
const extractedManifestFiles = Array.isArray(extractedEntries)
|
|
381
|
+
? extractedEntries
|
|
382
|
+
.filter((entry) => entry.type === "file")
|
|
383
|
+
.map((entry) => ({ path: entry.path, size: entry.size, mode: entry.mode }))
|
|
384
|
+
.sort((left, right) => left.path.localeCompare(right.path))
|
|
385
|
+
: null;
|
|
386
|
+
const secondManifest = portability?.secondManifest;
|
|
329
387
|
if (portability?.deterministicPackRuns !== 2
|
|
330
388
|
|| !/^[a-f0-9]{64}$/.test(portability?.deterministicTarballSha256 ?? "")
|
|
331
389
|
|| portability?.secondTarballSha256 !== portability?.deterministicTarballSha256
|
|
332
390
|
|| portability?.constructionRootCount !== 2 || portability?.constructionRootsDistinct !== true
|
|
333
391
|
|| portability?.extractedRootCount !== 1 || portability?.extractedRootsDistinct !== true
|
|
334
|
-
|| portability?.forbiddenPathMatches?.length !== 0 || !/^[a-f0-9]{64}$/.test(portability?.contentSha256 ?? "")
|
|
392
|
+
|| portability?.forbiddenPathMatches?.length !== 0 || !/^[a-f0-9]{64}$/.test(portability?.contentSha256 ?? "")
|
|
393
|
+
|| secondManifest?.filename !== manifest.packageArtifact.filename
|
|
394
|
+
|| secondManifest?.integrity !== manifest.packageArtifact.integrity
|
|
395
|
+
|| secondManifest?.shasum !== manifest.packageArtifact.shasum
|
|
396
|
+
|| !Array.isArray(extractedManifestFiles)
|
|
397
|
+
|| secondManifest?.files?.count !== extractedManifestFiles.length
|
|
398
|
+
|| secondManifest?.files?.digest !== observationDigest(extractedManifestFiles)) {
|
|
335
399
|
violations.push("extracted package portability evidence is incomplete or failed");
|
|
336
400
|
}
|
|
337
401
|
const sourceAuthority = records.get("bootstrap.source-authority")?.content;
|
|
@@ -453,15 +517,13 @@ function validateChecks(manifest, violations) {
|
|
|
453
517
|
if (used.size !== Object.keys(PROVENANCE).length)
|
|
454
518
|
violations.push("activation evidence contains unconsumed or multiply consumed producer records");
|
|
455
519
|
}
|
|
456
|
-
|
|
520
|
+
function activationEvidenceViolationsInternal(actual, context) {
|
|
457
521
|
const manifest = actual;
|
|
458
522
|
if (!manifest || typeof manifest !== "object")
|
|
459
523
|
return ["activation evidence manifest is missing"];
|
|
460
524
|
const violations = [];
|
|
461
|
-
const
|
|
462
|
-
const
|
|
463
|
-
const generation = context?.generation ?? expected.generation;
|
|
464
|
-
const sourceDigest = context ? activationSourceDigest(context.root) : expected.currentSourceDigest;
|
|
525
|
+
const generation = context?.generation ?? manifest.generation;
|
|
526
|
+
const sourceDigest = context ? activationSourceDigest(context.root) : manifest.currentSourceDigest;
|
|
465
527
|
const packageIntegrity = context
|
|
466
528
|
? context.expectedPackageIdentity?.packageArtifact?.integrity ?? null
|
|
467
529
|
: manifest.packageArtifact?.integrity ?? null;
|
|
@@ -491,21 +553,8 @@ export function activationEvidenceViolations(actual, expectedOrContext) {
|
|
|
491
553
|
if (Buffer.byteLength(`${canonicalObservationJson(manifest)}\n`, "utf8") > ACTIVATION_EVIDENCE_MAX_BYTES)
|
|
492
554
|
violations.push("activation evidence manifest exceeds its byte bound");
|
|
493
555
|
if (context) {
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
root: context.root,
|
|
497
|
-
generationRoot: context.generationRoot,
|
|
498
|
-
generation: context.generation,
|
|
499
|
-
productionInputs: context.productionInputs,
|
|
500
|
-
});
|
|
501
|
-
validateAuthoritativeOwner(manifest.producers?.source, authoritativeSource, violations);
|
|
502
|
-
validateAuthoritativeOwner(manifest.producers?.generated, authoritativeGenerated, violations);
|
|
503
|
-
if (!/^[a-f0-9]{64}$/.test(context.expectedManifestDigest)) {
|
|
504
|
-
violations.push("trusted release activation evidence digest is missing or malformed");
|
|
505
|
-
}
|
|
506
|
-
else if (manifest.manifestDigest !== context.expectedManifestDigest) {
|
|
507
|
-
violations.push("activation evidence manifest differs from the trusted release observation");
|
|
508
|
-
}
|
|
556
|
+
validateAuthoritativeOwner(manifest.producers?.source, context.sourceEvidence, violations);
|
|
557
|
+
validateAuthoritativeOwner(manifest.producers?.generated, context.generatedEvidence, violations);
|
|
509
558
|
violations.push(...activationPackageIdentityViolations(context.expectedPackageIdentity, manifest.producers?.package));
|
|
510
559
|
for (const relative of [".agentera-generation.json", "dist/.agentera-generation.json", "bundle/.agentera-generation.json"]) {
|
|
511
560
|
try {
|
|
@@ -517,8 +566,38 @@ export function activationEvidenceViolations(actual, expectedOrContext) {
|
|
|
517
566
|
}
|
|
518
567
|
}
|
|
519
568
|
}
|
|
520
|
-
|
|
569
|
+
return [...new Set(violations)];
|
|
570
|
+
}
|
|
571
|
+
export function activationEvidenceManifestViolations(actual, expected) {
|
|
572
|
+
const violations = activationEvidenceViolationsInternal(actual, null);
|
|
573
|
+
if (canonicalObservationJson(actual) !== canonicalObservationJson(expected)) {
|
|
521
574
|
violations.push("activation evidence differs from the expected independently observed manifest");
|
|
575
|
+
}
|
|
576
|
+
return [...new Set(violations)];
|
|
577
|
+
}
|
|
578
|
+
export function activationEvidenceViolations(actual, context) {
|
|
579
|
+
const sourceEvidence = createSourceOwnerEvidence(context.root, context.productionInputs);
|
|
580
|
+
const generatedEvidence = createGeneratedOwnerEvidence({
|
|
581
|
+
root: context.root,
|
|
582
|
+
generationRoot: context.generationRoot,
|
|
583
|
+
generation: context.generation,
|
|
584
|
+
productionInputs: context.productionInputs,
|
|
585
|
+
});
|
|
586
|
+
const violations = activationEvidenceViolationsInternal(actual, {
|
|
587
|
+
root: context.root,
|
|
588
|
+
generationRoot: context.generationRoot,
|
|
589
|
+
generation: context.generation,
|
|
590
|
+
sourceEvidence,
|
|
591
|
+
generatedEvidence,
|
|
592
|
+
expectedPackageIdentity: context.expectedPackageIdentity,
|
|
593
|
+
});
|
|
594
|
+
const manifest = actual;
|
|
595
|
+
if (!/^[a-f0-9]{64}$/.test(context.expectedManifestDigest)) {
|
|
596
|
+
violations.push("trusted release activation evidence digest is missing or malformed");
|
|
597
|
+
}
|
|
598
|
+
else if (manifest?.manifestDigest !== context.expectedManifestDigest) {
|
|
599
|
+
violations.push("activation evidence manifest differs from the trusted release observation");
|
|
600
|
+
}
|
|
522
601
|
return [...new Set(violations)];
|
|
523
602
|
}
|
|
524
603
|
export function readActivationEvidenceManifest(generationRoot) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentera",
|
|
3
|
-
"version": "3.0.0-dev.
|
|
3
|
+
"version": "3.0.0-dev.75",
|
|
4
4
|
"description": "One CLI, many capabilities: project state, artifact validation, and capability routing for AI coding agents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agentera",
|
|
@@ -43,8 +43,6 @@
|
|
|
43
43
|
"generated:cleanup": "node scripts/build-package.mjs --cleanup",
|
|
44
44
|
"pack:dry-run": "node scripts/pack-package.mjs --dry-run",
|
|
45
45
|
"release:prepare": "node scripts/publication-transaction.mjs prepare development",
|
|
46
|
-
"release:prepare:push": "node scripts/publication-transaction.mjs prepare-push development",
|
|
47
|
-
"release:validate:push": "node scripts/publication-transaction.mjs validate-push development",
|
|
48
46
|
"release:qualify:source": "node scripts/release-qualification.mjs source",
|
|
49
47
|
"release:ready": "node scripts/release-readiness.mjs development",
|
|
50
48
|
"release:qualify:candidate": "node scripts/release-qualification.mjs candidate --adapter development",
|
|
@@ -82,6 +80,6 @@
|
|
|
82
80
|
},
|
|
83
81
|
"agentera": {
|
|
84
82
|
"suiteVersion": "3.0.0",
|
|
85
|
-
"gitRef": "
|
|
83
|
+
"gitRef": "01a0c460f2310bae5805a637973021af30960ee5"
|
|
86
84
|
}
|
|
87
85
|
}
|