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.
Files changed (93) hide show
  1. package/README.md +111 -0
  2. package/bundle/.agentera-build-source.json +8 -0
  3. package/bundle/CHANGELOG.md +17 -17
  4. package/bundle/extract-corpus-parity.json +1 -1
  5. package/bundle/references/adapters/package-publication.json +49 -53
  6. package/bundle/references/adapters/package-registry.yaml +5 -1
  7. package/bundle/references/adapters/package-surface-characterization.md +6 -4
  8. package/bundle/references/analysis/evidence-tier-authority.yaml +45 -4
  9. package/bundle/references/analysis/personal-glossary-evaluation-authority.yaml +175 -0
  10. package/bundle/references/analysis/personal-glossary-evaluation-corpus.yaml +291 -0
  11. package/bundle/references/analysis/personal-glossary-holdout.yaml +192 -0
  12. package/bundle/references/analysis/verification-policy.yaml +8 -13
  13. package/bundle/references/artifacts/glossary-entry-contract.yaml +1533 -35
  14. package/bundle/references/cli/update-channels.yaml +2 -2
  15. package/bundle/references/cli/vocabulary.md +1 -1
  16. package/bundle/references/meta/retained-reference-authority.yaml +16 -1
  17. package/bundle/skills/agentera/capabilities/profile/schemas/artifacts.yaml +11 -7
  18. package/bundle/skills/agentera/capabilities/profile/schemas/exit.yaml +6 -3
  19. package/bundle/skills/agentera/capabilities/profile/schemas/validation.yaml +13 -7
  20. package/bundle/skills/agentera/schemas/artifacts/glossary.yaml +5 -0
  21. package/bundle/skills/agentera/schemas/artifacts/plan.yaml +7 -0
  22. package/dist/.agentera-build-source.json +8 -0
  23. package/dist/analytics/extractCorpus/copilotSessions.js +13 -4
  24. package/dist/analytics/extractCorpus/core.js +223 -3
  25. package/dist/analytics/extractCorpus/corpus.js +18 -1
  26. package/dist/analytics/extractCorpus/cursorSessions.js +22 -4
  27. package/dist/analytics/extractCorpus/evidenceTiers.js +123 -17
  28. package/dist/analytics/extractCorpus/filesystemSources.js +4 -0
  29. package/dist/analytics/extractCorpus/index.js +2 -2
  30. package/dist/analytics/extractCorpus/jsonlSessions.js +52 -3
  31. package/dist/analytics/extractCorpus/sqliteSessions.js +12 -3
  32. package/dist/analytics/personalGlossaryAdmission.js +26 -46
  33. package/dist/analytics/personalGlossaryCandidateProjection.js +692 -0
  34. package/dist/analytics/personalGlossaryCandidateProjectionExcerpts.js +144 -0
  35. package/dist/analytics/personalGlossaryCandidateReadView.js +23 -0
  36. package/dist/analytics/personalGlossaryCurrentGeneration.js +25 -0
  37. package/dist/analytics/personalGlossaryDecision.js +203 -0
  38. package/dist/analytics/personalGlossaryExplicit.js +788 -0
  39. package/dist/analytics/personalGlossaryExplicitMining.js +285 -0
  40. package/dist/analytics/personalGlossaryExplicitScope.js +41 -0
  41. package/dist/analytics/personalGlossaryExplicitSegments.js +418 -0
  42. package/dist/analytics/personalGlossaryExplicitTypes.js +26 -0
  43. package/dist/analytics/personalGlossaryLexicalClassifier.js +175 -0
  44. package/dist/analytics/personalGlossaryProfile.js +13 -3
  45. package/dist/analytics/personalGlossaryRecurrence.js +618 -0
  46. package/dist/analytics/personalGlossaryReviewRecordStorage.js +428 -0
  47. package/dist/analytics/personalGlossaryReviewRecords.js +332 -0
  48. package/dist/capabilities/index.js +5 -1
  49. package/dist/capabilities/profile/instructions.js +50 -15
  50. package/dist/cli/commands/personalGlossaryCandidateReads.js +744 -0
  51. package/dist/cli/commands/personalGlossaryDecision.js +349 -0
  52. package/dist/cli/commands/personalGlossaryPublish.js +484 -0
  53. package/dist/cli/commands/personalGlossaryReviewRecordReads.js +209 -0
  54. package/dist/cli/commands/personalGlossaryReviewRecords.js +623 -0
  55. package/dist/cli/commands/prime/briefOrientation.js +118 -19
  56. package/dist/cli/commands/report.js +18 -2
  57. package/dist/cli/commands/schema.js +179 -0
  58. package/dist/cli/commands/verify.js +51 -9
  59. package/dist/cli/dispatch/lifecycle.js +51 -6
  60. package/dist/cli/help.js +48 -2
  61. package/dist/cli/migrationRequired.js +4 -1
  62. package/dist/core/atomicWriter.js +4 -1
  63. package/dist/core/developmentInvocation.js +4 -1
  64. package/dist/core/yaml.js +53 -1
  65. package/dist/eval/glossaryEvaluation.js +751 -0
  66. package/dist/eval/glossaryEvaluationProcess.js +29 -0
  67. package/dist/eval/glossaryEvaluationRunner.js +571 -0
  68. package/dist/eval/glossaryEvaluationSuccessReport.js +297 -0
  69. package/dist/registries/activationTuples.js +31 -3
  70. package/dist/registries/evidenceTierContract.js +4 -0
  71. package/dist/registries/explicitSegmentGrammarContract.js +619 -0
  72. package/dist/registries/glossaryCandidateContractPaths.js +6 -0
  73. package/dist/registries/glossaryCandidateContracts.js +820 -0
  74. package/dist/registries/glossaryCandidateDecisionAuthority.js +119 -0
  75. package/dist/registries/glossaryCandidateDecisionContract.js +62 -0
  76. package/dist/registries/glossaryCandidateProjectionAuthority.js +197 -0
  77. package/dist/registries/glossaryCandidateProjectionContract.js +104 -0
  78. package/dist/registries/glossaryEntryContract.js +105 -103
  79. package/dist/registries/glossaryEntryTemporal.js +12 -0
  80. package/dist/registries/glossaryMiningAuthority.js +764 -0
  81. package/dist/registries/glossaryProfileFullContract.js +24 -0
  82. package/dist/registries/glossaryReviewRecordsAuthority.js +426 -0
  83. package/dist/registries/glossaryReviewRecordsContract.js +129 -0
  84. package/dist/registries/glossaryTermIdentity.js +122 -0
  85. package/dist/registries/packagePublication.js +3 -3
  86. package/dist/registries/personalGlossaryContracts.js +1 -0
  87. package/dist/release/releaseMetadata.js +15 -8
  88. package/dist/upgrade/npxPlatformStatus.js +2 -1
  89. package/dist/upgrade/projectIntegration.js +7 -1
  90. package/dist/validate/activationArtifactEvidence.js +18 -4
  91. package/dist/validate/activationEvidenceManifest.js +101 -22
  92. package/package.json +2 -4
  93. package/dist/cli/commands/personalGlossary.js +0 -176
@@ -281,7 +281,10 @@ function briefHistory(history, projection = "normal") {
281
281
  projected.caveats = caveats;
282
282
  if (isObject(entryObj.degraded_history)) {
283
283
  const degraded = pick(entryObj.degraded_history, ["summary_count", "returned_count", "omitted_count"]);
284
- degraded.retrieval = briefHistoryRetrieval(entryObj.degraded_history.retrieval);
284
+ // Compact routing already retains the same exact list/get commands on the
285
+ // parent history entry. Keep the nested copy only in the normal brief.
286
+ if (projection === "normal")
287
+ degraded.retrieval = briefHistoryRetrieval(entryObj.degraded_history.retrieval);
285
288
  const degradedCaveats = briefHistoryCaveats(entryObj.degraded_history.caveats, maxChars);
286
289
  if (degradedCaveats !== undefined)
287
290
  degraded.caveats = degradedCaveats;
@@ -389,20 +392,94 @@ function briefDecisionAttention(attention) {
389
392
  : [];
390
393
  return out;
391
394
  }
392
- function briefDocs(docs) {
393
- // No default-bare consumer reads the docs projection detail. Keep the
394
- // availability signal and mapping count; the verbose source_contract with
395
- // state_families recovers via the canonical docs list fallback.
396
- return pick(docs, ["exists", "status", "mapping_entries", "indexed_documents"]);
395
+ function briefDocsRetrieval(value, maxChars) {
396
+ const out = {};
397
+ if (isObject(value)) {
398
+ for (const key of ["list", "get", "exact", "detail_command"]) {
399
+ const command = boundedString(value[key], maxChars);
400
+ if (command !== undefined)
401
+ out[key] = command;
402
+ }
403
+ }
404
+ return out;
405
+ }
406
+ function briefDocEntry(value, maxChars) {
407
+ if (!isObject(value))
408
+ return {};
409
+ const out = pick(value, ["id", "artifact"]);
410
+ const source = isObject(value.record) ? value.record : value;
411
+ const record = {};
412
+ for (const key of ["document", "path", "last_updated", "status"]) {
413
+ const bounded = boundedString(source[key], maxChars);
414
+ if (bounded !== undefined)
415
+ record[key] = bounded;
416
+ }
417
+ const recordCaveats = briefHistoryCaveats(source.caveats, maxChars);
418
+ if (recordCaveats !== undefined)
419
+ record.caveats = recordCaveats;
420
+ if (Object.keys(record).length > 0)
421
+ out.record = record;
422
+ const caveats = briefHistoryCaveats(value.caveats, maxChars);
423
+ if (caveats !== undefined)
424
+ out.caveats = caveats;
425
+ const retrieval = briefDocsRetrieval(value.retrieval, maxChars);
426
+ if (Object.keys(retrieval).length > 0)
427
+ out.retrieval = retrieval;
428
+ return out;
397
429
  }
398
- function briefProgress(progress) {
430
+ function briefDocs(docs, projection = "normal") {
431
+ if (!isObject(docs))
432
+ return {};
433
+ const maxChars = projectionMaxChars(projection);
434
+ const entryLimit = projection === "normal" ? 3 : 1;
435
+ const out = pick(docs, ["exists", "status", "last_audit", "mapping_entries", "indexed_documents"]);
436
+ const absenceReason = boundedString(docs.absence_reason, maxChars);
437
+ if (absenceReason !== undefined)
438
+ out.absence_reason = absenceReason;
439
+ const caveats = briefHistoryCaveats(docs.caveats, maxChars);
440
+ if (caveats !== undefined)
441
+ out.caveats = caveats;
442
+ const sourceEntries = Array.isArray(docs.entries) ? docs.entries : null;
443
+ let detailAvailability = Number(docs.mapping_entries) > 0 || Number(docs.indexed_documents) > 0
444
+ ? "summary"
445
+ : "full";
446
+ if (sourceEntries) {
447
+ const entries = sourceEntries.slice(0, entryLimit).map((entry) => briefDocEntry(entry, maxChars));
448
+ const declaredTotal = Number(docs.indexed_documents);
449
+ const total = Number.isSafeInteger(declaredTotal) && declaredTotal >= entries.length
450
+ ? declaredTotal
451
+ : sourceEntries.length;
452
+ out.counts = { total, returned: entries.length, remaining: Math.max(0, total - entries.length) };
453
+ out.entries = entries;
454
+ detailAvailability = total > entries.length ? "summary" : "full";
455
+ }
456
+ const sourceContract = isObject(docs.source_contract)
457
+ ? pick(docs.source_contract, ["detail_availability", "raw_artifact_reads_required", "capability_startup_complete", "inventory_authority"])
458
+ : {};
459
+ sourceContract.detail_availability = detailAvailability;
460
+ sourceContract.retrieval = STATE_FAMILY_FALLBACK_COMMANDS.docs;
461
+ out.source_contract = sourceContract;
462
+ return out;
463
+ }
464
+ function briefDegradedHistory(value, omitRetrieval) {
465
+ if (!isObject(value))
466
+ return undefined;
467
+ if (!omitRetrieval)
468
+ return { ...value };
469
+ const out = pick(value, ["status", "summary_count", "returned_count", "omitted_count", "caveats"]);
470
+ return out;
471
+ }
472
+ function briefProgress(progress, omitDegradedRetrieval = false) {
399
473
  // opencode reads progress.latest.number/what/next; the brief keeps the
400
474
  // latest cycle but caps free-text scalars at BRIEF_SCALAR_MAX_CHARS so a
401
475
  // pathological value cannot blow the budget. Full detail recovers via
402
476
  // `agentera state progress get --id ID --format json`.
403
477
  if (!isObject(progress))
404
478
  return {};
405
- const out = pick(progress, ["exists", "status", "latest_verification", "cycle_count", "degraded_history"]);
479
+ const out = pick(progress, ["exists", "status", "latest_verification", "cycle_count"]);
480
+ const degradedHistory = briefDegradedHistory(progress.degraded_history, omitDegradedRetrieval);
481
+ if (degradedHistory !== undefined)
482
+ out.degraded_history = degradedHistory;
406
483
  const latest = progress.latest;
407
484
  if (isObject(latest)) {
408
485
  const boundedLatest = {};
@@ -416,8 +493,22 @@ function briefProgress(progress) {
416
493
  }
417
494
  return out;
418
495
  }
419
- function briefHealth(health) {
420
- return pick(health, ["exists", "id", "artifact", "date", "trajectory", "grade", "degraded_history"]);
496
+ function briefHealth(health, omitDegradedRetrieval = false) {
497
+ const out = pick(health, ["exists", "id", "artifact", "date", "trajectory", "grade"]);
498
+ if (isObject(health)) {
499
+ const degradedHistory = briefDegradedHistory(health.degraded_history, omitDegradedRetrieval);
500
+ if (degradedHistory !== undefined)
501
+ out.degraded_history = degradedHistory;
502
+ }
503
+ return out;
504
+ }
505
+ function hasCanonicalHistoryRetrieval(history, artifact) {
506
+ if (!isObject(history) || !isObject(history[artifact]))
507
+ return false;
508
+ const retrieval = history[artifact].retrieval;
509
+ return isObject(retrieval)
510
+ && typeof retrieval.list === "string" && retrieval.list.length > 0
511
+ && typeof retrieval.get === "string" && retrieval.get.length > 0;
421
512
  }
422
513
  function briefAction(action, maxChars) {
423
514
  if (!isObject(action))
@@ -562,10 +653,10 @@ function projectBriefBody(payload) {
562
653
  out[key] = briefSharedSkill(value);
563
654
  break;
564
655
  case "progress":
565
- out[key] = briefProgress(value);
656
+ out[key] = briefProgress(value, hasCanonicalHistoryRetrieval(payload.history, "progress"));
566
657
  break;
567
658
  case "health":
568
- out[key] = briefHealth(value);
659
+ out[key] = briefHealth(value, hasCanonicalHistoryRetrieval(payload.history, "health"));
569
660
  break;
570
661
  case "next_action":
571
662
  out[key] = briefNextAction(value);
@@ -621,19 +712,27 @@ function degradedBody(payload, projection) {
621
712
  out.project_integration = briefProjectIntegration(payload.project_integration);
622
713
  if ("todo_reconciliation" in payload)
623
714
  out.todo_reconciliation = payload.todo_reconciliation;
624
- if ("startup" in payload)
625
- out.startup = payload.startup;
715
+ if ("startup" in payload) {
716
+ // The top-level reconciliation object is the canonical bare-prime route.
717
+ // Do not repeat it inside startup when compacting an over-budget envelope.
718
+ const startup = isObject(payload.startup) ? { ...payload.startup } : payload.startup;
719
+ if (isObject(startup) && "todo_reconciliation" in out)
720
+ delete startup.todo_reconciliation;
721
+ out.startup = startup;
722
+ }
626
723
  if ("health" in payload)
627
- out.health = briefHealth(payload.health);
724
+ out.health = briefHealth(payload.health, hasCanonicalHistoryRetrieval(payload.history, "health"));
628
725
  if ("todo" in payload)
629
726
  out.todo = payload.todo;
630
727
  if ("progress" in payload)
631
- out.progress = briefProgress(payload.progress);
728
+ out.progress = briefProgress(payload.progress, hasCanonicalHistoryRetrieval(payload.history, "progress"));
632
729
  if ("attention" in payload)
633
730
  out.attention = briefAttention(payload.attention);
634
731
  if ("source" in payload)
635
732
  out.source = briefSource(payload.source);
636
- for (const conditional of ["v1_migration", "docs", "objective"]) {
733
+ if ("docs" in payload)
734
+ out.docs = briefDocs(payload.docs, projection);
735
+ for (const conditional of ["v1_migration", "objective"]) {
637
736
  if (conditional in payload)
638
737
  out[conditional] = payload[conditional];
639
738
  }
@@ -711,8 +810,8 @@ function degradedBriefEnvelope(payload, budget, attemptedBytes, mode) {
711
810
  path_diagnostics_recovery: PATH_DIAGNOSTICS_RECOVERY,
712
811
  error: {
713
812
  class: "brief_output_budget",
714
- message: "the projected decision brief exceeded the authority byte budget; compact recovery metadata was emitted",
715
- recovery: `Run \`${preCutoverCommand("prime --context status --format json")}\` for status startup.`,
813
+ message: "brief exceeds byte budget",
814
+ recovery: `Run \`${preCutoverCommand("prime --context status --format json")}\`.`,
716
815
  },
717
816
  };
718
817
  const compact = settledBriefEnvelope(body("compact"), compactMeta);
@@ -167,8 +167,24 @@ export function cmdReport(args, io = {}) {
167
167
  return 2;
168
168
  }
169
169
  if (!dryRun && consent !== "local-history") {
170
- err("Error: agentera stats refresh requires explicit --consent local-history to read local runtime history. " +
171
- "Preview first with agentera stats refresh --dry-run\n");
170
+ const recovery = "agentera report refresh --consent local-history";
171
+ const message = "Error: agentera stats refresh requires explicit --consent local-history to read local runtime history. " +
172
+ "Preview first with agentera stats refresh --dry-run";
173
+ if (outputFormat === "json") {
174
+ out(JSON.stringify({
175
+ command: "stats refresh",
176
+ status: "degraded_consent_required",
177
+ recovery,
178
+ privacy: {
179
+ local_history_read: false,
180
+ local_history_write: false,
181
+ tier_write: false,
182
+ required_consent: "local-history",
183
+ provided_consent: consent,
184
+ },
185
+ }, null, 2) + "\n");
186
+ }
187
+ err(`${message}\nRecovery: ${recovery}\n`);
172
188
  return 2;
173
189
  }
174
190
  const corpusPath = args.output || statsCorpusPath();
@@ -9,6 +9,9 @@ import { CANONICAL_SHARED_SKILL_PATH } from "../../setup/sharedSkill.js";
9
9
  import { loadStateRetrievalAuthority } from "../../state/retrievalAuthority.js";
10
10
  import { entityListFamilies } from "../../state/entityRetrievalHelp.js";
11
11
  import { personalGlossaryOutputContract } from "../../registries/glossaryEntryContract.js";
12
+ import { personalGlossaryCandidateProjectionContract } from "../../registries/glossaryCandidateProjectionContract.js";
13
+ import { personalGlossaryCandidateDecisionContract } from "../../registries/glossaryCandidateDecisionContract.js";
14
+ import { personalGlossaryReviewRecordsContract } from "../../registries/glossaryReviewRecordsContract.js";
12
15
  import { describeArtifactSchemaFields } from "../../registries/artifactSchemaProjection.js";
13
16
  import { advertisedValidateFamilyNames } from "./validate.js";
14
17
  import { SCHEMA_TOP_LEVEL_COMMANDS } from "../dispatch/projections.js";
@@ -331,6 +334,9 @@ export function buildSchemaPayload(command = "schema") {
331
334
  gaps.push(...schemaGaps);
332
335
  const retrievalAuthority = loadStateRetrievalAuthority();
333
336
  const profileGlossary = personalGlossaryOutputContract();
337
+ const candidateReads = personalGlossaryCandidateProjectionContract();
338
+ const candidateDecision = personalGlossaryCandidateDecisionContract();
339
+ const reviewRecords = personalGlossaryReviewRecordsContract();
334
340
  const authorityPath = integrationAuthorityPath();
335
341
  return {
336
342
  schemaVersion: "agentera.schema.v1",
@@ -375,8 +381,181 @@ export function buildSchemaPayload(command = "schema") {
375
381
  personal_glossary: {
376
382
  command: profileGlossary.command,
377
383
  request_schema_version: profileGlossary.requestSchemaVersion,
384
+ request_fields: profileGlossary.requestFields,
385
+ request_optional_fields: profileGlossary.requestOptionalFields,
386
+ max_request_utf8_bytes: profileGlossary.maxRequestUtf8Bytes,
387
+ result_schema_version: profileGlossary.resultSchemaVersion,
388
+ result_fields: profileGlossary.resultFields,
389
+ max_result_utf8_bytes: profileGlossary.maxResultUtf8Bytes,
378
390
  output_statuses: profileGlossary.outputStatuses,
391
+ review_authorization: {
392
+ fields: profileGlossary.reviewAuthorizationFields,
393
+ dispositions: profileGlossary.reviewAuthorizationDispositions,
394
+ },
379
395
  project_checkout: "not_required",
396
+ candidate_retrieval: {
397
+ command: candidateReads.candidateReadCommand,
398
+ schema_version: candidateReads.candidateReadSchemaVersion,
399
+ list: {
400
+ default_limit: candidateReads.candidateReadDefaultLimit,
401
+ maximum_limit: candidateReads.candidateReadMaximumLimit,
402
+ max_serialized_utf8_bytes: candidateReads.candidateReadMaxSerializedUtf8Bytes,
403
+ order: candidateReads.candidateReadOrder,
404
+ projection_binding_field: candidateReads.candidateReadListProjectionBindingField,
405
+ source_families: candidateReads.candidateReadSourceFamilies,
406
+ provenance_kinds: candidateReads.candidateReadProvenanceKinds,
407
+ scopes: candidateReads.candidateReadScopes,
408
+ cursor: {
409
+ authority: candidateReads.candidateReadCursorAuthority,
410
+ vocabulary: candidateReads.candidateReadCursorVocabulary,
411
+ binding: candidateReads.candidateReadCursorBinding,
412
+ invalid_behavior: candidateReads.candidateReadCursorInvalidBehavior,
413
+ unavailable_behavior: candidateReads.candidateReadCursorUnavailableBehavior,
414
+ },
415
+ },
416
+ exact: {
417
+ required_bindings: candidateReads.candidateReadExactRequiredBindings,
418
+ projection_binding_field: candidateReads.candidateReadExactProjectionBindingField,
419
+ occurrences_max: candidateReads.candidateReadExactOccurrencesMax,
420
+ safe_context_max_utf8_bytes: candidateReads.candidateReadSafeContextMaxUtf8Bytes,
421
+ max_serialized_utf8_bytes: candidateReads.candidateReadExactMaxSerializedUtf8Bytes,
422
+ },
423
+ safe_context_view: {
424
+ authority: candidateReads.candidateReadSafeContextViewAuthority,
425
+ retention_days: candidateReads.candidateReadSafeContextRetentionDays,
426
+ expiry: candidateReads.candidateReadSafeContextViewExpiry,
427
+ mutation: candidateReads.candidateReadSafeContextViewMutation,
428
+ snapshot: candidateReads.candidateReadSafeContextViewSnapshot,
429
+ },
430
+ current_generation: {
431
+ source: candidateReads.candidateReadCurrentGenerationSource,
432
+ projection_generation: candidateReads.candidateReadCurrentGenerationProjectionBinding,
433
+ unavailable_behavior: candidateReads.candidateReadCurrentGenerationUnavailableBehavior,
434
+ stale_projection_behavior: candidateReads.candidateReadCurrentGenerationStaleProjectionBehavior,
435
+ },
436
+ project_checkout: "not_required",
437
+ },
438
+ candidate_decision: {
439
+ command: candidateDecision.command,
440
+ request_schema_version: candidateDecision.requestSchemaVersion,
441
+ request_fields: candidateDecision.requestFields,
442
+ max_request_utf8_bytes: candidateDecision.maxRequestUtf8Bytes,
443
+ result_schema_version: candidateDecision.resultSchemaVersion,
444
+ result_fields: candidateDecision.resultFields,
445
+ statuses: candidateDecision.resultStatuses,
446
+ reason_codes_by_outcome: candidateDecision.reasonCodesByOutcome,
447
+ max_result_utf8_bytes: candidateDecision.maxResultUtf8Bytes,
448
+ receipt_construction: {
449
+ request_schema_version: candidateDecision.receiptConstructionRequestSchemaVersion,
450
+ request_fields: candidateDecision.receiptConstructionRequestFields,
451
+ max_request_utf8_bytes: candidateDecision.receiptConstructionMaxRequestUtf8Bytes,
452
+ result_schema_version: candidateDecision.receiptConstructionResultSchemaVersion,
453
+ result_fields: candidateDecision.receiptConstructionResultFields,
454
+ statuses: candidateDecision.receiptConstructionResultStatuses,
455
+ max_result_utf8_bytes: candidateDecision.receiptConstructionMaxResultUtf8Bytes,
456
+ },
457
+ automatic_admission: {
458
+ allowed_provenance: candidateDecision.automaticProvenance,
459
+ inferred_automatic_admission: candidateDecision.inferredAutomaticAdmission,
460
+ quality_gate: candidateDecision.qualityGate,
461
+ },
462
+ project_checkout: "not_required",
463
+ effects: [],
464
+ },
465
+ review_records: {
466
+ command: reviewRecords.command,
467
+ queue: {
468
+ request_schema_version: reviewRecords.queueRequestSchemaVersion,
469
+ request_fields: reviewRecords.queueRequestFields,
470
+ max_request_utf8_bytes: reviewRecords.queueMaxRequestUtf8Bytes,
471
+ decision_outcome: reviewRecords.queueDecisionOutcome,
472
+ current_bindings: reviewRecords.queueCurrentBindings,
473
+ result_schema_version: reviewRecords.queueResultSchemaVersion,
474
+ statuses: reviewRecords.queueResultStatuses,
475
+ max_result_utf8_bytes: reviewRecords.queueMaxResultUtf8Bytes,
476
+ no_question_channel: reviewRecords.queueNoQuestionChannel,
477
+ },
478
+ disposition: {
479
+ request_schema_version: reviewRecords.dispositionRequestSchemaVersion,
480
+ request_fields: reviewRecords.dispositionRequestFields,
481
+ max_request_utf8_bytes: reviewRecords.dispositionMaxRequestUtf8Bytes,
482
+ result_schema_version: reviewRecords.dispositionResultSchemaVersion,
483
+ statuses: reviewRecords.dispositionResultStatuses,
484
+ max_result_utf8_bytes: reviewRecords.dispositionMaxResultUtf8Bytes,
485
+ publication_authorization: {
486
+ dispositions: reviewRecords.dispositionPublicationAuthorizationDispositions,
487
+ fields: reviewRecords.dispositionPublicationAuthorizationFields,
488
+ },
489
+ },
490
+ trusted_host_key: {
491
+ file: reviewRecords.trustedHostKeyFile,
492
+ schema_version: reviewRecords.trustedHostKeySchemaVersion,
493
+ fields: reviewRecords.trustedHostKeyFields,
494
+ owner: reviewRecords.trustedHostKeyOwner,
495
+ public_key_algorithm: reviewRecords.trustedHostKeyAlgorithm,
496
+ max_serialized_utf8_bytes: reviewRecords.trustedHostKeyMaxSerializedUtf8Bytes,
497
+ },
498
+ persistence: {
499
+ schema_version: reviewRecords.storeSchemaVersion,
500
+ record_schema_version: reviewRecords.recordSchemaVersion,
501
+ owner: reviewRecords.storeOwner,
502
+ file: reviewRecords.storeFile,
503
+ fields: reviewRecords.storeFields,
504
+ record_fields: reviewRecords.recordFields,
505
+ records_max: reviewRecords.recordsMax,
506
+ replay_index_fields: reviewRecords.replayIndexFields,
507
+ replay_entries_max: reviewRecords.replayEntriesMax,
508
+ record_max_serialized_utf8_bytes: reviewRecords.recordMaxSerializedUtf8Bytes,
509
+ max_serialized_utf8_bytes: reviewRecords.storeMaxSerializedUtf8Bytes,
510
+ replay: reviewRecords.replay,
511
+ conflict: reviewRecords.conflict,
512
+ compatibility: {
513
+ accepted_store_schema_versions: reviewRecords.compatibilityStoreSchemaVersions,
514
+ accepted_record_schema_versions: reviewRecords.compatibilityRecordSchemaVersions,
515
+ read_mutation: reviewRecords.compatibilityReadMutation,
516
+ migration_operation: reviewRecords.compatibilityMigrationOperation,
517
+ scope_derivation: reviewRecords.compatibilityScopeDerivation,
518
+ invalid_behavior: reviewRecords.compatibilityInvalidBehavior,
519
+ preserved_bindings: reviewRecords.compatibilityPreservedBindings,
520
+ legacy_digest: reviewRecords.compatibilityLegacyDigest,
521
+ migrated_digest: reviewRecords.compatibilityMigratedDigest,
522
+ },
523
+ suppression_binding: reviewRecords.suppressionBinding,
524
+ suppression_dispositions: reviewRecords.suppressionDispositions,
525
+ reopen_reasons: reviewRecords.reopenReasons,
526
+ forbidden_fields: reviewRecords.forbiddenFields,
527
+ },
528
+ retrieval: {
529
+ schema_version: reviewRecords.retrievalSchemaVersion,
530
+ owner: reviewRecords.retrievalOwner,
531
+ list: {
532
+ default_limit: reviewRecords.listDefaultLimit,
533
+ maximum_limit: reviewRecords.listMaximumLimit,
534
+ max_serialized_utf8_bytes: reviewRecords.listMaxSerializedUtf8Bytes,
535
+ order: reviewRecords.listOrder,
536
+ statuses: reviewRecords.listStatuses,
537
+ cursor: {
538
+ authority: reviewRecords.cursorAuthority,
539
+ vocabulary: reviewRecords.cursorVocabulary,
540
+ binding: reviewRecords.cursorBinding,
541
+ invalid_behavior: reviewRecords.cursorInvalidBehavior,
542
+ unavailable_behavior: reviewRecords.cursorUnavailableBehavior,
543
+ },
544
+ },
545
+ exact: {
546
+ required_bindings: reviewRecords.exactRequiredBindings,
547
+ current_binding_field: reviewRecords.exactCurrentBindingField,
548
+ max_serialized_utf8_bytes: reviewRecords.exactMaxSerializedUtf8Bytes,
549
+ },
550
+ },
551
+ maintenance: {
552
+ terminal_metadata_days: reviewRecords.terminalMetadataDays,
553
+ exposure: reviewRecords.maintenanceExposure,
554
+ purge: reviewRecords.maintenancePurge,
555
+ forbidden_effects: reviewRecords.maintenanceForbiddenEffects,
556
+ },
557
+ project_checkout: "not_required",
558
+ },
380
559
  },
381
560
  historical_import: {
382
561
  source: "claude",
@@ -1,11 +1,13 @@
1
1
  import { main as evalSkillsMain } from "../../eval/evalSkills.js";
2
2
  import { main as semanticEvalMain } from "../../eval/semanticEval.js";
3
+ import { runGlossaryEvaluationProcess } from "../../eval/glossaryEvaluationProcess.js";
4
+ import { validateGlossaryEvaluationSuccessReport, } from "../../eval/glossaryEvaluationSuccessReport.js";
3
5
  import { evaluateHybridRoute } from "../../eval/hybridRouteEvaluation.js";
4
6
  import { routeEvaluationExitCode } from "./route.js";
5
7
  export const VERIFY_FAMILIES = ["eval"];
6
8
  export const RETIRED_VERIFY_FAMILIES = ["smoke"];
7
9
  export const VERIFY_TARGETS = {
8
- eval: ["skills", "semantic", "routing"],
10
+ eval: ["skills", "semantic", "routing", "glossary"],
9
11
  };
10
12
  export const VERIFY_FORMATS = ["text", "json"];
11
13
  export const VERIFY_DIAGNOSTIC_LINE_LIMIT = 20;
@@ -63,15 +65,20 @@ export function validateVerifyRequest(args) {
63
65
  }
64
66
  if (family === "eval" && target === "semantic" && (args.fixtures ?? []).length === 0) {
65
67
  throw new Error("semantic verify requires explicit fixture path(s); broad fixture discovery is not a safe default. " +
66
- "Valid targets for eval: skills, semantic. " +
68
+ "Valid targets for eval: skills, semantic, routing, glossary. " +
67
69
  "Syntax: agentera verify eval semantic <fixture> [<fixture>...] [--format text|json]. " +
68
70
  "Example: agentera verify eval semantic fixtures/semantic/bare-agentera-message.md --format json");
69
71
  }
72
+ if (family === "eval" && target === "glossary" && (args.fixtures ?? []).length > 0) {
73
+ throw new Error("glossary verify uses the contract-owned frozen holdout and accepts no fixture path. " +
74
+ "It always runs current product behavior. Syntax: agentera check verify eval glossary --format text|json.");
75
+ }
70
76
  return [family, target, outputFormat];
71
77
  }
72
78
  /**
73
- * Resolve the engine command + safety metadata for a verify target. eval engines
74
- * run in-process (ported TS). Only the eval family is supported; the smoke
79
+ * Resolve the engine command + safety metadata for a verify target. The frozen
80
+ * glossary evaluator runs in its isolated process so unrelated CLI starts do
81
+ * not load its mining graph. Only the eval family is supported; the smoke
75
82
  * family (maintainer/CI harnesses) was retired in the self-contained package.
76
83
  */
77
84
  function runVerifyEngine(family, target, args) {
@@ -117,6 +124,22 @@ function runVerifyEngine(family, target, args) {
117
124
  const result = runInProcess(["eval", "semantic", ...fixtures], (out) => semanticEvalMain(fixtures, (l) => out(l)));
118
125
  return { result, safety };
119
126
  }
127
+ if (family === "eval" && target === "glossary") {
128
+ const safety = {
129
+ mode: "offline-frozen-holdout",
130
+ summary: "runs frozen synthetic inputs through current glossary discovery, classification, and V2 decision seams without a semantic host or user-local effects",
131
+ live: false,
132
+ long_running_default: false,
133
+ };
134
+ const evaluation = runGlossaryEvaluationProcess();
135
+ const result = {
136
+ command: ["node", "agentera", "verify", "eval", "glossary"],
137
+ returncode: evaluation.returncode,
138
+ stdout: evaluation.stdout,
139
+ stderr: evaluation.stderr,
140
+ };
141
+ return { result, safety };
142
+ }
120
143
  if (family === "eval" && target === "routing") {
121
144
  const safety = {
122
145
  mode: "offline-frozen-corpus",
@@ -162,23 +185,42 @@ function boundedLines(text, limit = VERIFY_DIAGNOSTIC_LINE_LIMIT) {
162
185
  return lines;
163
186
  return [...lines.slice(0, limit), `... truncated ${lines.length - limit} line(s)`];
164
187
  }
188
+ function publicVerifyResult(family, target, result) {
189
+ if (family !== "eval" || target !== "glossary" || result.returncode !== 0)
190
+ return { result };
191
+ const glossaryEvaluation = validateGlossaryEvaluationSuccessReport(result);
192
+ if (glossaryEvaluation !== null)
193
+ return { result, glossaryEvaluation };
194
+ const separator = result.stderr === "" || result.stderr.endsWith("\n") ? "" : "\n";
195
+ return {
196
+ result: {
197
+ ...result,
198
+ returncode: 1,
199
+ stderr: `${result.stderr}${separator}invalid glossary evaluation success report\n`,
200
+ },
201
+ };
202
+ }
165
203
  function verifyStatus(result) {
166
204
  return result.returncode === 0 ? "pass" : "fail";
167
205
  }
168
206
  export function buildVerifyPayload(family, target, outputFormat, result, safety) {
207
+ const publicResult = publicVerifyResult(family, target, result);
169
208
  return {
170
209
  command: "verify",
171
- status: verifyStatus(result),
210
+ status: verifyStatus(publicResult.result),
172
211
  family,
173
212
  target,
174
213
  format: outputFormat,
175
- engine: { command: result.command, exit_code: result.returncode },
214
+ engine: { command: publicResult.result.command, exit_code: publicResult.result.returncode },
176
215
  diagnostics: {
177
- stdout: boundedLines(result.stdout),
178
- stderr: boundedLines(result.stderr),
216
+ stdout: boundedLines(publicResult.result.stdout),
217
+ stderr: boundedLines(publicResult.result.stderr),
179
218
  line_limit: VERIFY_DIAGNOSTIC_LINE_LIMIT,
180
219
  },
181
220
  safety,
221
+ ...(publicResult.glossaryEvaluation === undefined
222
+ ? {}
223
+ : { glossary_evaluation: publicResult.glossaryEvaluation }),
182
224
  };
183
225
  }
184
226
  function emitVerifyText(payload, out) {
@@ -221,5 +263,5 @@ export function cmdVerify(args, io = {}) {
221
263
  else {
222
264
  emitVerifyText(payload, out);
223
265
  }
224
- return result.returncode;
266
+ return payload.engine.exit_code;
225
267
  }
@@ -8,7 +8,10 @@ import { cmdVerify } from "../commands/verify.js";
8
8
  import { cmdGate } from "../commands/compact.js";
9
9
  import { cmdReport } from "../commands/report.js";
10
10
  import { runGlossaryAdviceCommand } from "../commands/glossaryAdvice.js";
11
- import { runPersonalGlossaryCommand } from "../commands/personalGlossary.js";
11
+ import { runPersonalGlossaryPublishCommand } from "../commands/personalGlossaryPublish.js";
12
+ import { runPersonalGlossaryCandidateReadsCommand } from "../commands/personalGlossaryCandidateReads.js";
13
+ import { runPersonalGlossaryDecisionCommand } from "../commands/personalGlossaryDecision.js";
14
+ import { runPersonalGlossaryReviewRecordsCommand } from "../commands/personalGlossaryReviewRecords.js";
12
15
  import { runProfileGroundingCommand } from "../commands/profileGrounding.js";
13
16
  import { preCutoverCommand } from "../preCutoverCommand.js";
14
17
  import { usageMain } from "../../analytics/usageStats.js";
@@ -442,6 +445,48 @@ export function runVerify(argv, io, prog) {
442
445
  }
443
446
  }
444
447
  export function runReport(argv, io, prog) {
448
+ if (argv[0] === "personal-glossary-decision") {
449
+ if (prog !== "agentera report") {
450
+ return emitInvalidInput(io, {
451
+ format: "json",
452
+ body: {
453
+ class: "unsupported_target",
454
+ message: "personal-glossary-decision has no stats alias",
455
+ valid_values: ["report personal-glossary-decision"],
456
+ recovery: `Run ${preCutoverCommand("report personal-glossary-decision --input - --format json")}; no bytes were changed.`,
457
+ },
458
+ });
459
+ }
460
+ return runPersonalGlossaryDecisionCommand(argv.slice(1), io);
461
+ }
462
+ if (argv[0] === "personal-glossary-candidates") {
463
+ if (prog !== "agentera report") {
464
+ return emitInvalidInput(io, {
465
+ format: "json",
466
+ body: {
467
+ class: "unsupported_target",
468
+ message: "personal-glossary-candidates has no stats alias",
469
+ valid_values: ["report personal-glossary-candidates"],
470
+ recovery: `Run ${preCutoverCommand("report personal-glossary-candidates list --limit 20 --format json")}; no projection bytes were changed.`,
471
+ },
472
+ });
473
+ }
474
+ return runPersonalGlossaryCandidateReadsCommand(argv.slice(1), io);
475
+ }
476
+ if (argv[0] === "personal-glossary-reviews") {
477
+ if (prog !== "agentera report") {
478
+ return emitInvalidInput(io, {
479
+ format: "json",
480
+ body: {
481
+ class: "unsupported_target",
482
+ message: "personal-glossary-reviews has no stats alias",
483
+ valid_values: ["report personal-glossary-reviews"],
484
+ recovery: `Run ${preCutoverCommand("report personal-glossary-reviews list --limit 20 --format json")}; no review metadata was changed.`,
485
+ },
486
+ });
487
+ }
488
+ return runPersonalGlossaryReviewRecordsCommand(argv.slice(1), io);
489
+ }
445
490
  if (argv[0] === "glossary-advice") {
446
491
  if (prog !== "agentera report") {
447
492
  return emitInvalidInput(io, {
@@ -470,19 +515,19 @@ export function runReport(argv, io, prog) {
470
515
  }
471
516
  return runProfileGroundingCommand(argv.slice(1), io);
472
517
  }
473
- if (argv[0] === "profile-glossary") {
518
+ if (argv[0] === "personal-glossary-publish") {
474
519
  if (prog !== "agentera report") {
475
520
  return emitInvalidInput(io, {
476
521
  format: "json",
477
522
  body: {
478
523
  class: "unsupported_target",
479
- message: "profile-glossary has no stats alias",
480
- valid_values: ["report profile-glossary"],
481
- recovery: `Run ${preCutoverCommand("report profile-glossary --input PATH --format json")} with the same input; no profile bytes were changed.`,
524
+ message: "personal-glossary-publish has no stats alias",
525
+ valid_values: ["report personal-glossary-publish"],
526
+ recovery: `Run ${preCutoverCommand("report personal-glossary-publish --input - --format json")}; no profile bytes were changed.`,
482
527
  },
483
528
  });
484
529
  }
485
- return runPersonalGlossaryCommand(argv.slice(1), io);
530
+ return runPersonalGlossaryPublishCommand(argv.slice(1), io);
486
531
  }
487
532
  const args = {
488
533
  action: null,