agentera 0.0.0 → 3.0.0-dev.1

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 (256) hide show
  1. package/README.md +6 -45
  2. package/bundle/.agentera-npx-bundle.json +4 -0
  3. package/bundle/references/adapters/cursor.md +213 -0
  4. package/bundle/references/adapters/opencode.md +530 -0
  5. package/bundle/references/adapters/package-manifest-interface-model.yaml +337 -0
  6. package/bundle/references/adapters/package-registry.yaml +247 -0
  7. package/bundle/references/adapters/package-surface-characterization.md +48 -0
  8. package/bundle/references/adapters/runtime-adapter-characterization.md +79 -0
  9. package/bundle/references/adapters/runtime-adapter-interface-model.yaml +200 -0
  10. package/bundle/references/adapters/runtime-adapter-registry.yaml +548 -0
  11. package/bundle/references/adapters/runtime-feature-parity.md +189 -0
  12. package/bundle/references/analysis/benchmark.md +267 -0
  13. package/bundle/references/analysis/startup-measurement-contract.yaml +424 -0
  14. package/bundle/references/artifacts/artifact-registry-interface-model.yaml +288 -0
  15. package/bundle/references/cli/agent-ready-state-contract.yaml +950 -0
  16. package/bundle/references/cli/app-lifecycle-vocabulary.yaml +241 -0
  17. package/bundle/references/cli/audience-namespace-cli-migration.yaml +355 -0
  18. package/bundle/references/cli/bundle-skill-vocabulary.yaml +278 -0
  19. package/bundle/references/cli/capability-instruction-contract.yaml +123 -0
  20. package/bundle/references/cli/capability-tool-classification.yaml +53 -0
  21. package/bundle/references/cli/routing-execution-vocabulary.yaml +281 -0
  22. package/bundle/references/cli/update-channels.yaml +147 -0
  23. package/bundle/references/cli/vocabulary-index.yaml +160 -0
  24. package/bundle/references/cli/vocabulary.md +566 -0
  25. package/bundle/references/meta/documentation-inventory.md +43 -0
  26. package/bundle/references/v1-section-mapping.md +47 -0
  27. package/bundle/registry.json +39 -0
  28. package/bundle/skills/agentera/.claude-plugin/plugin.json +27 -0
  29. package/bundle/skills/agentera/SKILL.md +470 -0
  30. package/bundle/skills/agentera/agents/dokumentera.toml +6 -0
  31. package/bundle/skills/agentera/agents/hej.toml +6 -0
  32. package/bundle/skills/agentera/agents/inspektera.toml +6 -0
  33. package/bundle/skills/agentera/agents/inspirera.toml +6 -0
  34. package/bundle/skills/agentera/agents/optimera.toml +6 -0
  35. package/bundle/skills/agentera/agents/orkestrera.toml +6 -0
  36. package/bundle/skills/agentera/agents/planera.toml +6 -0
  37. package/bundle/skills/agentera/agents/profilera.toml +6 -0
  38. package/bundle/skills/agentera/agents/realisera.toml +6 -0
  39. package/bundle/skills/agentera/agents/resonera.toml +6 -0
  40. package/bundle/skills/agentera/agents/visionera.toml +6 -0
  41. package/bundle/skills/agentera/agents/visualisera.toml +6 -0
  42. package/bundle/skills/agentera/capabilities/dokumentera/instructions.md +428 -0
  43. package/bundle/skills/agentera/capabilities/dokumentera/schemas/artifacts.yaml +73 -0
  44. package/bundle/skills/agentera/capabilities/dokumentera/schemas/exit.yaml +35 -0
  45. package/bundle/skills/agentera/capabilities/dokumentera/schemas/triggers.yaml +35 -0
  46. package/bundle/skills/agentera/capabilities/dokumentera/schemas/validation.yaml +139 -0
  47. package/bundle/skills/agentera/capabilities/hej/instructions.md +331 -0
  48. package/bundle/skills/agentera/capabilities/hej/schemas/artifacts.yaml +69 -0
  49. package/bundle/skills/agentera/capabilities/hej/schemas/exit.yaml +32 -0
  50. package/bundle/skills/agentera/capabilities/hej/schemas/triggers.yaml +58 -0
  51. package/bundle/skills/agentera/capabilities/hej/schemas/validation.yaml +55 -0
  52. package/bundle/skills/agentera/capabilities/inspektera/instructions.md +514 -0
  53. package/bundle/skills/agentera/capabilities/inspektera/schemas/artifacts.yaml +76 -0
  54. package/bundle/skills/agentera/capabilities/inspektera/schemas/exit.yaml +36 -0
  55. package/bundle/skills/agentera/capabilities/inspektera/schemas/triggers.yaml +38 -0
  56. package/bundle/skills/agentera/capabilities/inspektera/schemas/validation.yaml +113 -0
  57. package/bundle/skills/agentera/capabilities/inspirera/instructions.md +280 -0
  58. package/bundle/skills/agentera/capabilities/inspirera/schemas/artifacts.yaml +24 -0
  59. package/bundle/skills/agentera/capabilities/inspirera/schemas/exit.yaml +33 -0
  60. package/bundle/skills/agentera/capabilities/inspirera/schemas/triggers.yaml +34 -0
  61. package/bundle/skills/agentera/capabilities/inspirera/schemas/validation.yaml +58 -0
  62. package/bundle/skills/agentera/capabilities/optimera/instructions.md +437 -0
  63. package/bundle/skills/agentera/capabilities/optimera/schemas/artifacts.yaml +69 -0
  64. package/bundle/skills/agentera/capabilities/optimera/schemas/exit.yaml +35 -0
  65. package/bundle/skills/agentera/capabilities/optimera/schemas/triggers.yaml +39 -0
  66. package/bundle/skills/agentera/capabilities/optimera/schemas/validation.yaml +91 -0
  67. package/bundle/skills/agentera/capabilities/orkestrera/instructions.md +433 -0
  68. package/bundle/skills/agentera/capabilities/orkestrera/schemas/artifacts.yaml +64 -0
  69. package/bundle/skills/agentera/capabilities/orkestrera/schemas/exit.yaml +34 -0
  70. package/bundle/skills/agentera/capabilities/orkestrera/schemas/triggers.yaml +42 -0
  71. package/bundle/skills/agentera/capabilities/orkestrera/schemas/validation.yaml +107 -0
  72. package/bundle/skills/agentera/capabilities/planera/instructions.md +368 -0
  73. package/bundle/skills/agentera/capabilities/planera/schemas/artifacts.yaml +62 -0
  74. package/bundle/skills/agentera/capabilities/planera/schemas/exit.yaml +33 -0
  75. package/bundle/skills/agentera/capabilities/planera/schemas/triggers.yaml +34 -0
  76. package/bundle/skills/agentera/capabilities/planera/schemas/validation.yaml +61 -0
  77. package/bundle/skills/agentera/capabilities/profilera/instructions.md +419 -0
  78. package/bundle/skills/agentera/capabilities/profilera/schemas/artifacts.yaml +18 -0
  79. package/bundle/skills/agentera/capabilities/profilera/schemas/exit.yaml +34 -0
  80. package/bundle/skills/agentera/capabilities/profilera/schemas/triggers.yaml +45 -0
  81. package/bundle/skills/agentera/capabilities/profilera/schemas/validation.yaml +57 -0
  82. package/bundle/skills/agentera/capabilities/realisera/instructions.md +403 -0
  83. package/bundle/skills/agentera/capabilities/realisera/schemas/artifacts.yaml +80 -0
  84. package/bundle/skills/agentera/capabilities/realisera/schemas/exit.yaml +35 -0
  85. package/bundle/skills/agentera/capabilities/realisera/schemas/triggers.yaml +39 -0
  86. package/bundle/skills/agentera/capabilities/realisera/schemas/validation.yaml +110 -0
  87. package/bundle/skills/agentera/capabilities/resonera/instructions.md +329 -0
  88. package/bundle/skills/agentera/capabilities/resonera/schemas/artifacts.yaml +47 -0
  89. package/bundle/skills/agentera/capabilities/resonera/schemas/exit.yaml +35 -0
  90. package/bundle/skills/agentera/capabilities/resonera/schemas/triggers.yaml +46 -0
  91. package/bundle/skills/agentera/capabilities/resonera/schemas/validation.yaml +77 -0
  92. package/bundle/skills/agentera/capabilities/visionera/instructions.md +309 -0
  93. package/bundle/skills/agentera/capabilities/visionera/schemas/artifacts.yaml +57 -0
  94. package/bundle/skills/agentera/capabilities/visionera/schemas/exit.yaml +35 -0
  95. package/bundle/skills/agentera/capabilities/visionera/schemas/triggers.yaml +41 -0
  96. package/bundle/skills/agentera/capabilities/visionera/schemas/validation.yaml +74 -0
  97. package/bundle/skills/agentera/capabilities/visualisera/instructions.md +400 -0
  98. package/bundle/skills/agentera/capabilities/visualisera/schemas/artifacts.yaml +44 -0
  99. package/bundle/skills/agentera/capabilities/visualisera/schemas/exit.yaml +34 -0
  100. package/bundle/skills/agentera/capabilities/visualisera/schemas/triggers.yaml +33 -0
  101. package/bundle/skills/agentera/capabilities/visualisera/schemas/validation.yaml +80 -0
  102. package/bundle/skills/agentera/capability_schema_contract.yaml +385 -0
  103. package/bundle/skills/agentera/protocol.yaml +463 -0
  104. package/bundle/skills/agentera/references/contract.md +1039 -0
  105. package/bundle/skills/agentera/schemas/artifacts/changelog.yaml +60 -0
  106. package/bundle/skills/agentera/schemas/artifacts/decisions.yaml +461 -0
  107. package/bundle/skills/agentera/schemas/artifacts/design.yaml +55 -0
  108. package/bundle/skills/agentera/schemas/artifacts/docs.yaml +402 -0
  109. package/bundle/skills/agentera/schemas/artifacts/experiments.yaml +373 -0
  110. package/bundle/skills/agentera/schemas/artifacts/health.yaml +484 -0
  111. package/bundle/skills/agentera/schemas/artifacts/objective.yaml +399 -0
  112. package/bundle/skills/agentera/schemas/artifacts/plan.yaml +342 -0
  113. package/bundle/skills/agentera/schemas/artifacts/progress.yaml +325 -0
  114. package/bundle/skills/agentera/schemas/artifacts/todo.yaml +110 -0
  115. package/bundle/skills/agentera/schemas/artifacts/vision.yaml +262 -0
  116. package/bundle/skills/hej/.claude-plugin/plugin.json +6 -0
  117. package/bundle/skills/hej/SKILL.md +69 -0
  118. package/bundle/skills/hej/agents/hej.toml +11 -0
  119. package/bundle/skills/hej/agents/openai.yaml +8 -0
  120. package/dist/analytics/extractCorpus.js +1791 -0
  121. package/dist/analytics/extractCorpus.js.map +1 -0
  122. package/dist/analytics/usageStats.js +487 -0
  123. package/dist/analytics/usageStats.js.map +1 -0
  124. package/dist/bin/agentera.js +4 -0
  125. package/dist/bin/agentera.js.map +1 -0
  126. package/dist/cli/appContext.js +226 -0
  127. package/dist/cli/appContext.js.map +1 -0
  128. package/dist/cli/argvalidate.js +41 -0
  129. package/dist/cli/argvalidate.js.map +1 -0
  130. package/dist/cli/capabilityContext.js +2421 -0
  131. package/dist/cli/capabilityContext.js.map +1 -0
  132. package/dist/cli/commands/backfill.js +84 -0
  133. package/dist/cli/commands/backfill.js.map +1 -0
  134. package/dist/cli/commands/capability.js +44 -0
  135. package/dist/cli/commands/capability.js.map +1 -0
  136. package/dist/cli/commands/compact.js +148 -0
  137. package/dist/cli/commands/compact.js.map +1 -0
  138. package/dist/cli/commands/doctor.js +180 -0
  139. package/dist/cli/commands/doctor.js.map +1 -0
  140. package/dist/cli/commands/lint.js +179 -0
  141. package/dist/cli/commands/lint.js.map +1 -0
  142. package/dist/cli/commands/prime.js +544 -0
  143. package/dist/cli/commands/prime.js.map +1 -0
  144. package/dist/cli/commands/query.js +346 -0
  145. package/dist/cli/commands/query.js.map +1 -0
  146. package/dist/cli/commands/report.js +210 -0
  147. package/dist/cli/commands/report.js.map +1 -0
  148. package/dist/cli/commands/schema.js +306 -0
  149. package/dist/cli/commands/schema.js.map +1 -0
  150. package/dist/cli/commands/state.js +1012 -0
  151. package/dist/cli/commands/state.js.map +1 -0
  152. package/dist/cli/commands/upgrade.js +48 -0
  153. package/dist/cli/commands/upgrade.js.map +1 -0
  154. package/dist/cli/commands/validate.js +519 -0
  155. package/dist/cli/commands/validate.js.map +1 -0
  156. package/dist/cli/commands/verify.js +204 -0
  157. package/dist/cli/commands/verify.js.map +1 -0
  158. package/dist/cli/dispatch.js +958 -0
  159. package/dist/cli/dispatch.js.map +1 -0
  160. package/dist/cli/orientation.js +595 -0
  161. package/dist/cli/orientation.js.map +1 -0
  162. package/dist/cli/prime-blob.js +3 -0
  163. package/dist/cli/prime-blob.js.map +1 -0
  164. package/dist/cli/stateQuery.js +292 -0
  165. package/dist/cli/stateQuery.js.map +1 -0
  166. package/dist/cli/structured.js +18 -0
  167. package/dist/cli/structured.js.map +1 -0
  168. package/dist/core/difflib.js +274 -0
  169. package/dist/core/difflib.js.map +1 -0
  170. package/dist/core/git.js +43 -0
  171. package/dist/core/git.js.map +1 -0
  172. package/dist/core/paths.js +50 -0
  173. package/dist/core/paths.js.map +1 -0
  174. package/dist/core/pyjson.js +101 -0
  175. package/dist/core/pyjson.js.map +1 -0
  176. package/dist/core/sourceRoot.js +72 -0
  177. package/dist/core/sourceRoot.js.map +1 -0
  178. package/dist/core/toml.js +11 -0
  179. package/dist/core/toml.js.map +1 -0
  180. package/dist/core/yaml.js +25 -0
  181. package/dist/core/yaml.js.map +1 -0
  182. package/dist/eval/evalSkills.js +258 -0
  183. package/dist/eval/evalSkills.js.map +1 -0
  184. package/dist/eval/semanticEval.js +148 -0
  185. package/dist/eval/semanticEval.js.map +1 -0
  186. package/dist/eval/semanticFixtures.js +227 -0
  187. package/dist/eval/semanticFixtures.js.map +1 -0
  188. package/dist/hooks/common.js +160 -0
  189. package/dist/hooks/common.js.map +1 -0
  190. package/dist/hooks/compaction.js +935 -0
  191. package/dist/hooks/compaction.js.map +1 -0
  192. package/dist/hooks/cursorPreToolUse.js +19 -0
  193. package/dist/hooks/cursorPreToolUse.js.map +1 -0
  194. package/dist/hooks/cursorSessionStart.js +71 -0
  195. package/dist/hooks/cursorSessionStart.js.map +1 -0
  196. package/dist/hooks/sessionStart.js +209 -0
  197. package/dist/hooks/sessionStart.js.map +1 -0
  198. package/dist/hooks/sessionStop.js +212 -0
  199. package/dist/hooks/sessionStop.js.map +1 -0
  200. package/dist/hooks/validateArtifact.js +933 -0
  201. package/dist/hooks/validateArtifact.js.map +1 -0
  202. package/dist/registries/artifactRegistry.js +206 -0
  203. package/dist/registries/artifactRegistry.js.map +1 -0
  204. package/dist/registries/capabilityContract.js +310 -0
  205. package/dist/registries/capabilityContract.js.map +1 -0
  206. package/dist/registries/packageRegistry.js +641 -0
  207. package/dist/registries/packageRegistry.js.map +1 -0
  208. package/dist/registries/runtimeAdapterRegistry.js +315 -0
  209. package/dist/registries/runtimeAdapterRegistry.js.map +1 -0
  210. package/dist/setup/codex.js +1056 -0
  211. package/dist/setup/codex.js.map +1 -0
  212. package/dist/setup/copilot.js +227 -0
  213. package/dist/setup/copilot.js.map +1 -0
  214. package/dist/setup/cursor.js +127 -0
  215. package/dist/setup/cursor.js.map +1 -0
  216. package/dist/setup/doctor.js +1276 -0
  217. package/dist/setup/doctor.js.map +1 -0
  218. package/dist/state/installRoot.js +279 -0
  219. package/dist/state/installRoot.js.map +1 -0
  220. package/dist/state/progressCommit.js +289 -0
  221. package/dist/state/progressCommit.js.map +1 -0
  222. package/dist/state/startupAnalysis.js +1953 -0
  223. package/dist/state/startupAnalysis.js.map +1 -0
  224. package/dist/upgrade/appModel.js +189 -0
  225. package/dist/upgrade/appModel.js.map +1 -0
  226. package/dist/upgrade/channels.js +208 -0
  227. package/dist/upgrade/channels.js.map +1 -0
  228. package/dist/upgrade/compatibility.js +201 -0
  229. package/dist/upgrade/compatibility.js.map +1 -0
  230. package/dist/upgrade/doctor.js +373 -0
  231. package/dist/upgrade/doctor.js.map +1 -0
  232. package/dist/upgrade/migrateArtifactsV2ToV3.js +332 -0
  233. package/dist/upgrade/migrateArtifactsV2ToV3.js.map +1 -0
  234. package/dist/upgrade/runtimeMigration.js +484 -0
  235. package/dist/upgrade/runtimeMigration.js.map +1 -0
  236. package/dist/upgrade/upgradeCommands.js +36 -0
  237. package/dist/upgrade/upgradeCommands.js.map +1 -0
  238. package/dist/upgrade/upgradeOrchestrator.js +299 -0
  239. package/dist/upgrade/upgradeOrchestrator.js.map +1 -0
  240. package/dist/upgrade/versionResolution.js +179 -0
  241. package/dist/upgrade/versionResolution.js.map +1 -0
  242. package/dist/validate/appHomeContract.js +150 -0
  243. package/dist/validate/appHomeContract.js.map +1 -0
  244. package/dist/validate/capability.js +412 -0
  245. package/dist/validate/capability.js.map +1 -0
  246. package/dist/validate/crossCapability.js +145 -0
  247. package/dist/validate/crossCapability.js.map +1 -0
  248. package/dist/validate/lifecycleAdapters.js +772 -0
  249. package/dist/validate/lifecycleAdapters.js.map +1 -0
  250. package/dist/validate/selfAudit.js +107 -0
  251. package/dist/validate/selfAudit.js.map +1 -0
  252. package/package.json +28 -8
  253. package/LICENSE +0 -201
  254. package/bin/agentera.mjs +0 -50
  255. package/lib/exec.mjs +0 -116
  256. package/lib/resolve.mjs +0 -129
@@ -0,0 +1,1953 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { loadYamlMapping } from "../core/yaml.js";
5
+ import { resolveSourceRoot } from "../core/sourceRoot.js";
6
+ export const TRANSCRIPT_KEYS = new Set([
7
+ "content",
8
+ "text",
9
+ "prompt",
10
+ "message",
11
+ "preceding_context",
12
+ "input_text",
13
+ "output_text",
14
+ "transcript",
15
+ ]);
16
+ export const SESSION_KEYS = new Set(["session_id", "sessionID", "sessionId", "conversation_id"]);
17
+ export const PATH_KEYS = new Set(["path", "project_path", "store_path", "file_path", "cwd", "report_path"]);
18
+ export function contractPath(root = resolveSourceRoot()) {
19
+ return path.join(root, "references", "analysis", "startup-measurement-contract.yaml");
20
+ }
21
+ export function loadContract(p = contractPath()) {
22
+ return loadYamlMapping(fs.readFileSync(p, "utf8"));
23
+ }
24
+ export function hashLabel(kind, value, salt) {
25
+ if (!salt) {
26
+ throw new Error("salt is required for private labels");
27
+ }
28
+ const digest = crypto
29
+ .createHash("sha256")
30
+ .update(`${salt}\0${pyStr(value)}`, "utf8")
31
+ .digest("hex")
32
+ .slice(0, 16);
33
+ return `${kind}:${digest}`;
34
+ }
35
+ /** Approximate Python str() for the scalar/id values hashLabel receives. */
36
+ function pyStr(value) {
37
+ if (value === null)
38
+ return "None";
39
+ if (value === undefined)
40
+ return "None";
41
+ if (value === true)
42
+ return "True";
43
+ if (value === false)
44
+ return "False";
45
+ return String(value);
46
+ }
47
+ const FALLBACK_ARTIFACT_LABELS = [
48
+ [".agentera/plan.yaml", "PLAN.md"],
49
+ [".agentera/progress.yaml", "PROGRESS.md"],
50
+ [".agentera/docs.yaml", "DOCS.md"],
51
+ [".agentera/decisions.yaml", "DECISIONS.md"],
52
+ [".agentera/health.yaml", "HEALTH.md"],
53
+ [".agentera/vision.yaml", "VISION.md"],
54
+ [".agentera/objective.yaml", "OBJECTIVE.md"],
55
+ [".agentera/experiments.yaml", "EXPERIMENTS.md"],
56
+ ];
57
+ export function canonicalArtifactLabel(value, contract = null) {
58
+ const text = String(value).replace(/\\/g, "/");
59
+ const loaded = contract ?? loadContract();
60
+ const labels = (loaded.privacy_boundary ?? {}).canonical_artifact_labels;
61
+ if (labels && typeof labels === "object" && !Array.isArray(labels)) {
62
+ for (const [suffix, label] of Object.entries(labels)) {
63
+ const normalized = String(suffix).replace(/\\/g, "/");
64
+ if (text === normalized || text.endsWith("/" + normalized) || text.includes(normalized)) {
65
+ return String(label);
66
+ }
67
+ }
68
+ }
69
+ for (const [suffix, label] of FALLBACK_ARTIFACT_LABELS) {
70
+ if (text === suffix || text.endsWith("/" + suffix) || text.includes(suffix)) {
71
+ return label;
72
+ }
73
+ }
74
+ if (text.includes(".agentera/")) {
75
+ return "AGENTERA_ARTIFACTS";
76
+ }
77
+ return null;
78
+ }
79
+ export function redactForStartupOutput(value, salt, contract = null) {
80
+ const loaded = contract ?? loadContract();
81
+ if (Array.isArray(value)) {
82
+ return value.map((item) => redactForStartupOutput(item, salt, loaded));
83
+ }
84
+ if (value === null || typeof value !== "object") {
85
+ return value;
86
+ }
87
+ const redacted = {};
88
+ for (const [key, item] of Object.entries(value)) {
89
+ const keyText = String(key);
90
+ if (TRANSCRIPT_KEYS.has(keyText)) {
91
+ redacted[keyText] = "<redacted:transcript_text>";
92
+ }
93
+ else if (SESSION_KEYS.has(keyText)) {
94
+ redacted[keyText] = hashLabel("session", item, salt);
95
+ }
96
+ else if (PATH_KEYS.has(keyText)) {
97
+ const label = canonicalArtifactLabel(item, loaded);
98
+ redacted[keyText] = label || hashLabel("path", item, salt);
99
+ }
100
+ else {
101
+ redacted[keyText] = redactForStartupOutput(item, salt, loaded);
102
+ }
103
+ }
104
+ return redacted;
105
+ }
106
+ // --- Timestamp helpers -----------------------------------------------------
107
+ export function parseTimestamp(value) {
108
+ if (typeof value !== "string" || !value) {
109
+ return null;
110
+ }
111
+ const text = value.replace("Z", "+00:00");
112
+ const ms = Date.parse(text);
113
+ return Number.isNaN(ms) ? null : new Date(ms);
114
+ }
115
+ export function formatTimestamp(value) {
116
+ if (value === null) {
117
+ return null;
118
+ }
119
+ // ISO 8601 UTC, seconds precision (mirrors Python isoformat(timespec="seconds")).
120
+ return value.toISOString().replace(/\.\d{3}Z$/, "+00:00");
121
+ }
122
+ // ===========================================================================
123
+ // Slice 2: threshold evidence scanning + classification + event classification
124
+ // ===========================================================================
125
+ export const THRESHOLD_EVIDENCE_ENVELOPE = "threshold_evidence_scan_v1";
126
+ export const THRESHOLD_CLASSIFICATION_ENVELOPE = "threshold_evidence_classification_v1";
127
+ export const STATE_EVENT_CLASSES = new Set([
128
+ "cli_state_call",
129
+ "raw_artifact_access",
130
+ "capability_prose_read",
131
+ "implementation_boundary",
132
+ "non_state_context",
133
+ ]);
134
+ export const BOUNDARY_DEGRADATION_REASONS = new Set([
135
+ "pre_boundary_record",
136
+ "missing_timestamp",
137
+ "malformed_record",
138
+ "missing_conversation_key",
139
+ "no_agentera_state_sequence",
140
+ "privacy_redaction_required",
141
+ ]);
142
+ export const BOUNDED_RUNTIME_STATUSES = new Set([
143
+ "ok",
144
+ "available",
145
+ "missing",
146
+ "sparse",
147
+ "degraded",
148
+ "skipped",
149
+ ]);
150
+ export const BOUNDED_RUNTIME_REASONS = new Set([
151
+ "candidate_files_found",
152
+ "disabled",
153
+ "extractor_unimplemented",
154
+ "no_candidate_files",
155
+ "no_runtime_stores_approved",
156
+ "no_matching_records",
157
+ "records_extracted",
158
+ "schema_divergent",
159
+ "store_absent",
160
+ "store_locked",
161
+ "store_not_directory",
162
+ "store_unreadable",
163
+ ]);
164
+ export const STATE_CLI_COMMANDS = new Set([
165
+ "hej",
166
+ "prime",
167
+ "plan",
168
+ "progress",
169
+ "health",
170
+ "todo",
171
+ "decisions",
172
+ "docs",
173
+ "objective",
174
+ "experiments",
175
+ "query",
176
+ ]);
177
+ const CLI_COMMAND_ARTIFACTS = {
178
+ plan: new Set(["PLAN.md"]),
179
+ progress: new Set(["PROGRESS.md"]),
180
+ health: new Set(["HEALTH.md"]),
181
+ todo: new Set(["TODO.md"]),
182
+ decisions: new Set(["DECISIONS.md"]),
183
+ docs: new Set(["DOCS.md"]),
184
+ objective: new Set(["OBJECTIVE.md"]),
185
+ experiments: new Set(["EXPERIMENTS.md"]),
186
+ hej: new Set(["PLAN.md", "PROGRESS.md", "HEALTH.md", "TODO.md", "DOCS.md", "DECISIONS.md"]),
187
+ prime: new Set(["PLAN.md", "PROGRESS.md", "HEALTH.md", "TODO.md", "DOCS.md", "DECISIONS.md", "CHANGELOG.md"]),
188
+ };
189
+ const QUERY_ARTIFACTS = {
190
+ plan: "PLAN.md",
191
+ progress: "PROGRESS.md",
192
+ health: "HEALTH.md",
193
+ todo: "TODO.md",
194
+ decisions: "DECISIONS.md",
195
+ docs: "DOCS.md",
196
+ vision: "VISION.md",
197
+ objective: "OBJECTIVE.md",
198
+ experiments: "EXPERIMENTS.md",
199
+ };
200
+ const PRIMARY_ROUTE_TO_CAPABILITY = {
201
+ build: "realisera",
202
+ plan: "planera",
203
+ status: "hej",
204
+ discuss: "resonera",
205
+ research: "inspirera",
206
+ optimize: "optimera",
207
+ audit: "inspektera",
208
+ document: "dokumentera",
209
+ profile: "profilera",
210
+ design: "visualisera",
211
+ orchestrate: "orkestrera",
212
+ vision: "visionera",
213
+ };
214
+ const CAPABILITIES_WITH_HEJ = new Set([...Object.values(PRIMARY_ROUTE_TO_CAPABILITY), "hej"]);
215
+ const MARKER_RE = /─{2,}\s+(\S)\s+([a-z]+era|hej)\s+·\s+([a-z]+(?:\s+\d+)?)\s+─{2,}/g;
216
+ const BARE_AGENTERA_ROUTE_RE = /^\s*\/agentera(?:\s+([A-Za-z0-9._:-]+))?/m;
217
+ const BARE_CAPABILITY_ROUTE_RE = /^\s*\/([a-z]+era|hej)(?:\s|$)/m;
218
+ const XML_ROUTE_RE = /<command-name>\s*\/(?:agentera\s+)?([A-Za-z0-9._:-]+)\s*<\/command-name>/;
219
+ const THRESHOLD_WARNING_PATTERNS = [
220
+ [
221
+ "self_audit",
222
+ "verbosity",
223
+ "self_audit.verbosity",
224
+ /verbosity(?: mismatch)?|exceeds(?: the)?(?: advisory| compact)?(?: prose| word| entry)? budget|\b\d+\s+words?\s+exceeds\s+\d+\s+budget\b|full plans? exceed/i,
225
+ ],
226
+ ["self_audit", "abstraction", "self_audit.abstraction", /abstraction creep/i],
227
+ ["self_audit", "filler", "self_audit.filler", /\bfiller\s*:/i],
228
+ ["compaction", "over_limit", "compaction.uniform_10_40_50", /over(?:\s+|_)limit|uniform_10_40_50/i],
229
+ ["compaction", "protected_overflow", "compaction.protected_overflow", /protected[-_ ]overflow/i],
230
+ ];
231
+ const POST_AUDIT_FLAG_RE = /\[post-audit-flagged(?::[^\]]*)?\]/gi;
232
+ const BUDGET_PRESSURE_RE = THRESHOLD_WARNING_PATTERNS[0][3];
233
+ const FULL_PLAN_BUDGET_RE = /full[- ]plan|full plans?|PLAN\.md/i;
234
+ const DETAIL_ANCHOR_RE = /`[^`]+`|\b(?:[A-Za-z0-9_.-]+\/)+[A-Za-z0-9_.-]+\b|:\d{2,}\b|\b[0-9a-fA-F]{7,}\b/g;
235
+ function inc(counter, key) {
236
+ counter[key] = (counter[key] ?? 0) + 1;
237
+ }
238
+ function counterDict(counter) {
239
+ const out = {};
240
+ for (const key of Object.keys(counter).sort())
241
+ out[key] = counter[key];
242
+ return out;
243
+ }
244
+ function countMatches(re, text) {
245
+ const g = new RegExp(re.source, re.flags.includes("g") ? re.flags : re.flags + "g");
246
+ let n = 0;
247
+ while (g.exec(text) !== null)
248
+ n += 1;
249
+ return n;
250
+ }
251
+ function wordCount(text) {
252
+ return (text.match(/\S+/g) ?? []).length;
253
+ }
254
+ let _fallbackIdSeq = 0;
255
+ const _fallbackIds = new WeakMap();
256
+ function recordLabel(record, salt) {
257
+ let key = record.source_id;
258
+ if (key === null || key === undefined || key === "") {
259
+ let id = _fallbackIds.get(record);
260
+ if (id === undefined) {
261
+ id = ++_fallbackIdSeq;
262
+ _fallbackIds.set(record, id);
263
+ }
264
+ key = id;
265
+ }
266
+ return hashLabel("record", key, salt);
267
+ }
268
+ function extractText(record) {
269
+ const data = record.data;
270
+ if (data && typeof data === "object" && !Array.isArray(data)) {
271
+ const value = data.content || data.text || data.message || data.prompt;
272
+ return typeof value === "string" ? value : "";
273
+ }
274
+ return "";
275
+ }
276
+ function toolArguments(record) {
277
+ const data = record.data;
278
+ if (data && typeof data === "object" && !Array.isArray(data)) {
279
+ const args = data.arguments;
280
+ if (args && typeof args === "object" && !Array.isArray(args))
281
+ return args;
282
+ if (typeof args === "string") {
283
+ try {
284
+ const parsed = JSON.parse(args);
285
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : { value: parsed };
286
+ }
287
+ catch {
288
+ return { raw: args };
289
+ }
290
+ }
291
+ return data;
292
+ }
293
+ return record;
294
+ }
295
+ function toolName(record) {
296
+ const data = record.data;
297
+ const values = [record.tool, record.tool_name, record.name];
298
+ if (data && typeof data === "object" && !Array.isArray(data)) {
299
+ values.push(data.tool, data.tool_name, data.name);
300
+ }
301
+ for (const value of values) {
302
+ if (typeof value === "string" && value)
303
+ return value;
304
+ }
305
+ return "";
306
+ }
307
+ function toolArgument(record, ...keys) {
308
+ const args = toolArguments(record);
309
+ const candidates = keys.map((k) => args[k]);
310
+ candidates.push(...keys.map((k) => record[k]));
311
+ for (const candidate of candidates) {
312
+ if (typeof candidate === "string" && candidate)
313
+ return candidate;
314
+ }
315
+ return "";
316
+ }
317
+ function pyJsonString(str) {
318
+ let out = '"';
319
+ for (const ch of str) {
320
+ const cp = ch.codePointAt(0);
321
+ if (ch === '"')
322
+ out += '\\"';
323
+ else if (ch === "\\")
324
+ out += "\\\\";
325
+ else if (cp === 0x08)
326
+ out += "\\b";
327
+ else if (cp === 0x09)
328
+ out += "\\t";
329
+ else if (cp === 0x0a)
330
+ out += "\\n";
331
+ else if (cp === 0x0c)
332
+ out += "\\f";
333
+ else if (cp === 0x0d)
334
+ out += "\\r";
335
+ else if (cp < 0x20)
336
+ out += "\\u" + cp.toString(16).padStart(4, "0");
337
+ else if (cp < 0x80)
338
+ out += ch;
339
+ else if (cp > 0xffff) {
340
+ const v = cp - 0x10000;
341
+ const hi = 0xd800 + (v >> 10);
342
+ const lo = 0xdc00 + (v & 0x3ff);
343
+ out += "\\u" + hi.toString(16).padStart(4, "0") + "\\u" + lo.toString(16).padStart(4, "0");
344
+ }
345
+ else {
346
+ out += "\\u" + cp.toString(16).padStart(4, "0");
347
+ }
348
+ }
349
+ return out + '"';
350
+ }
351
+ /** Mirror Python json.dumps(value, sort_keys=True) (separators ", "/": ", ensure_ascii). */
352
+ function pyJsonDumps(value) {
353
+ if (value === null || value === undefined)
354
+ return "null";
355
+ if (value === true)
356
+ return "true";
357
+ if (value === false)
358
+ return "false";
359
+ if (value instanceof Flt)
360
+ return formatFloat(value.v);
361
+ if (typeof value === "number")
362
+ return String(value);
363
+ if (typeof value === "string")
364
+ return pyJsonString(value);
365
+ if (Array.isArray(value))
366
+ return "[" + value.map((v) => pyJsonDumps(v)).join(", ") + "]";
367
+ if (typeof value === "object") {
368
+ const keys = Object.keys(value).sort();
369
+ return "{" + keys.map((k) => `${pyJsonString(k)}: ${pyJsonDumps(value[k])}`).join(", ") + "}";
370
+ }
371
+ return "null";
372
+ }
373
+ function argumentsText(record) {
374
+ return pyJsonDumps(toolArguments(record));
375
+ }
376
+ function introCapability(text) {
377
+ const g = new RegExp(MARKER_RE.source, "g");
378
+ let m;
379
+ while ((m = g.exec(text)) !== null) {
380
+ const word = m[3];
381
+ if (!["complete", "flagged", "stuck", "waiting"].includes(word)) {
382
+ return m[2];
383
+ }
384
+ }
385
+ return null;
386
+ }
387
+ function routeCapability(text) {
388
+ let m = XML_ROUTE_RE.exec(text);
389
+ if (m) {
390
+ const route = m[1].toLowerCase();
391
+ return CAPABILITIES_WITH_HEJ.has(route) ? route : (PRIMARY_ROUTE_TO_CAPABILITY[route] ?? null);
392
+ }
393
+ m = BARE_AGENTERA_ROUTE_RE.exec(text);
394
+ if (m) {
395
+ const route = (m[1] || "status").toLowerCase();
396
+ return CAPABILITIES_WITH_HEJ.has(route) ? route : (PRIMARY_ROUTE_TO_CAPABILITY[route] ?? null);
397
+ }
398
+ m = BARE_CAPABILITY_ROUTE_RE.exec(text);
399
+ if (m) {
400
+ return m[1].toLowerCase();
401
+ }
402
+ return null;
403
+ }
404
+ function capabilityInvocation(text) {
405
+ const route = routeCapability(text);
406
+ if (route)
407
+ return route;
408
+ const marker = introCapability(text);
409
+ if (marker)
410
+ return marker;
411
+ const lowered = text.toLowerCase();
412
+ for (const capability of [...CAPABILITIES_WITH_HEJ].sort()) {
413
+ if (new RegExp(`\\b${capability.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`).test(lowered)) {
414
+ return capability;
415
+ }
416
+ }
417
+ if (lowered.includes("agentera"))
418
+ return "agentera";
419
+ return null;
420
+ }
421
+ function recordThresholdText(record) {
422
+ const parts = [extractText(record)];
423
+ if (record.source_kind === "tool_call")
424
+ parts.push(argumentsText(record));
425
+ return parts.filter((p) => p).join("\n");
426
+ }
427
+ function detailMetrics(text) {
428
+ return { word_count: wordCount(text), anchor_count: countMatches(DETAIL_ANCHOR_RE, text) };
429
+ }
430
+ function thresholdWarnings(text) {
431
+ const warnings = [];
432
+ const seen = new Set();
433
+ for (const [family, category, source, pattern] of THRESHOLD_WARNING_PATTERNS) {
434
+ if (pattern.test(text)) {
435
+ const key = `${family}\0${category}\0${source}`;
436
+ if (!seen.has(key)) {
437
+ warnings.push({ family, category, threshold_source: source });
438
+ seen.add(key);
439
+ }
440
+ }
441
+ }
442
+ return warnings;
443
+ }
444
+ function detailLossStatus(before, after) {
445
+ if (before.word_count === 0 || after.word_count === 0)
446
+ return "not_assessed";
447
+ const wordDrop = after.word_count < before.word_count * 0.75;
448
+ const anchorDrop = after.anchor_count < before.anchor_count;
449
+ if (wordDrop && anchorDrop)
450
+ return "possible_useful_detail_removed";
451
+ if (wordDrop)
452
+ return "possible_compression_without_anchor_loss";
453
+ return "not_detected";
454
+ }
455
+ function boundedRuntimeStatus(status) {
456
+ const runtime = String(status.runtime ?? "unknown");
457
+ const state = String(status.status ?? "degraded");
458
+ const reason = String(status.reason ?? "schema_divergent");
459
+ const item = {
460
+ runtime,
461
+ status: BOUNDED_RUNTIME_STATUSES.has(state) ? state : "degraded",
462
+ reason: BOUNDED_RUNTIME_REASONS.has(reason) ? reason : "schema_divergent",
463
+ };
464
+ for (const key of ["candidate_count", "record_count", "error_count"]) {
465
+ const value = status[key];
466
+ if (typeof value === "number" && Number.isInteger(value))
467
+ item[key] = value;
468
+ }
469
+ if (Array.isArray(status.remediation_labels)) {
470
+ item.remediation_labels = status.remediation_labels.map((l) => String(l));
471
+ }
472
+ return item;
473
+ }
474
+ function startupConversationKey(record) {
475
+ const key = record.conversation_key;
476
+ if (typeof key === "string" && key)
477
+ return key;
478
+ const sid = record.session_id;
479
+ if (typeof sid === "string" && sid)
480
+ return sid;
481
+ const data = record.data;
482
+ if (data && typeof data === "object" && !Array.isArray(data)) {
483
+ const dsid = data.session_id;
484
+ if (typeof dsid === "string" && dsid)
485
+ return dsid;
486
+ }
487
+ const ssid = record.source_id;
488
+ if (typeof ssid === "string" && ssid)
489
+ return ssid;
490
+ return null;
491
+ }
492
+ function stateCliCommand(command) {
493
+ if (!command)
494
+ return null;
495
+ const tokens = command.replace(/"/g, " ").replace(/'/g, " ").split(/\s+/).filter((t) => t);
496
+ for (let i = 0; i < tokens.length - 1; i++) {
497
+ if (tokens[i].endsWith("agentera") && STATE_CLI_COMMANDS.has(tokens[i + 1])) {
498
+ return tokens[i + 1];
499
+ }
500
+ }
501
+ return null;
502
+ }
503
+ function stateCliArtifacts(command, stateCommand) {
504
+ if (stateCommand === "query") {
505
+ const tokens = command.replace(/"/g, " ").replace(/'/g, " ").split(/\s+/).filter((t) => t);
506
+ for (let i = 0; i < tokens.length - 1; i++) {
507
+ if (tokens[i].endsWith("agentera") && tokens[i + 1] === "query") {
508
+ for (const arg of tokens.slice(i + 2)) {
509
+ const label = QUERY_ARTIFACTS[arg.toLowerCase()];
510
+ if (label)
511
+ return new Set([label]);
512
+ }
513
+ return new Set();
514
+ }
515
+ }
516
+ }
517
+ return new Set(CLI_COMMAND_ARTIFACTS[stateCommand] ?? []);
518
+ }
519
+ export function classifyStartupEvent(record) {
520
+ if (!record || typeof record !== "object" || Array.isArray(record) || record.source_kind !== "tool_call") {
521
+ return ["non_state_context", null, null, new Set()];
522
+ }
523
+ const tool = toolName(record).toLowerCase();
524
+ const command = toolArgument(record, "command");
525
+ if (tool === "bash") {
526
+ const stateCommand = stateCliCommand(command);
527
+ if (stateCommand) {
528
+ return ["cli_state_call", null, stateCommand, stateCliArtifacts(command, stateCommand)];
529
+ }
530
+ return ["implementation_boundary", null, null, new Set()];
531
+ }
532
+ const argsText = argumentsText(record).replace(/\\/g, "/");
533
+ if (["read", "grep", "glob"].includes(tool) &&
534
+ (argsText.includes("skills/agentera/capabilities/") ||
535
+ argsText.includes("skills/agentera/SKILL.md") ||
536
+ argsText.includes("skills/agentera/protocol.yaml"))) {
537
+ return ["capability_prose_read", argsText.includes("SKILL.md") ? "SKILL.md" : null, null, new Set()];
538
+ }
539
+ const artifactLabel = ["read", "grep", "glob"].includes(tool) ? canonicalArtifactLabel(argsText) : null;
540
+ if (artifactLabel) {
541
+ return ["raw_artifact_access", artifactLabel, null, new Set()];
542
+ }
543
+ if (["apply_patch", "edit", "write"].includes(tool)) {
544
+ return ["implementation_boundary", null, null, new Set()];
545
+ }
546
+ return ["non_state_context", null, null, new Set()];
547
+ }
548
+ function eventWarningKeys(event) {
549
+ const keys = [];
550
+ for (const warning of event.warnings ?? []) {
551
+ if (!warning || typeof warning !== "object")
552
+ continue;
553
+ const family = warning.family;
554
+ const category = warning.category;
555
+ if (typeof family === "string" && typeof category === "string")
556
+ keys.push(`${family}.${category}`);
557
+ }
558
+ return keys;
559
+ }
560
+ function eventClassification(event, coverageCaveated) {
561
+ const status = event.detail_loss_status;
562
+ if (status === "possible_useful_detail_removed")
563
+ return "likely_false_positive";
564
+ if (status === "retained_artifact_false_positive_signal")
565
+ return "likely_false_positive";
566
+ if (status === "possible_compression_without_anchor_loss" || status === "not_detected")
567
+ return "legitimate_pressure";
568
+ if (coverageCaveated)
569
+ return "unsupported_by_available_coverage";
570
+ return "inconclusive";
571
+ }
572
+ function categoryClassification(counts) {
573
+ if (counts.likely_false_positive)
574
+ return "likely_false_positive";
575
+ if (counts.unsupported_by_available_coverage)
576
+ return "unsupported_by_available_coverage";
577
+ if (counts.inconclusive)
578
+ return "inconclusive";
579
+ return "legitimate_pressure";
580
+ }
581
+ function nowIsoSeconds() {
582
+ return new Date().toISOString().replace(/\.\d{3}Z$/, "+00:00");
583
+ }
584
+ export function scanThresholdEvidence(corpus, opts) {
585
+ const salt = opts.salt;
586
+ const loaded = opts.contract ?? loadContract();
587
+ let records = corpus && typeof corpus === "object" && !Array.isArray(corpus) ? (corpus.records ?? []) : [];
588
+ if (!Array.isArray(records))
589
+ records = [];
590
+ let metadata = corpus && typeof corpus === "object" && !Array.isArray(corpus) ? (corpus.metadata ?? {}) : {};
591
+ if (!metadata || typeof metadata !== "object" || Array.isArray(metadata))
592
+ metadata = {};
593
+ let runtimeStatuses = metadata.runtime_statuses;
594
+ if (!Array.isArray(runtimeStatuses))
595
+ runtimeStatuses = [];
596
+ const runtimeCoverage = runtimeStatuses
597
+ .filter((s) => s && typeof s === "object" && !Array.isArray(s))
598
+ .map((s) => boundedRuntimeStatus(s));
599
+ const coverageCaveats = [];
600
+ if (runtimeCoverage.length === 0) {
601
+ coverageCaveats.push("No runtime coverage metadata was available for threshold evidence scanning.");
602
+ }
603
+ if (runtimeCoverage.some((s) => ["missing", "sparse", "degraded", "skipped"].includes(s.status))) {
604
+ coverageCaveats.push("Runtime coverage is incomplete or degraded; absence of warning evidence is not proof of absence.");
605
+ }
606
+ const groups = new Map();
607
+ const degradations = [];
608
+ for (const record of records) {
609
+ if (!record || typeof record !== "object" || Array.isArray(record)) {
610
+ degradations.push({ reason: "malformed_record" });
611
+ continue;
612
+ }
613
+ const key = startupConversationKey(record);
614
+ if (key === null) {
615
+ degradations.push({ record: recordLabel(record, salt), reason: "missing_conversation_key" });
616
+ continue;
617
+ }
618
+ if (!groups.has(key))
619
+ groups.set(key, []);
620
+ groups.get(key).push(record);
621
+ }
622
+ const warningCounts = {};
623
+ const artifactCounts = {};
624
+ const capabilityCounts = {};
625
+ const sourceCounts = {};
626
+ const detailStatusCounts = {};
627
+ const warningEvents = [];
628
+ for (const [conversationKey, items] of groups) {
629
+ items.sort((a, b) => {
630
+ const ta = String(a.timestamp ?? "");
631
+ const tb = String(b.timestamp ?? "");
632
+ return ta < tb ? -1 : ta > tb ? 1 : 0;
633
+ });
634
+ let capability = "unknown";
635
+ let pending = [];
636
+ for (const record of items) {
637
+ const text = recordThresholdText(record);
638
+ const data = record.data && typeof record.data === "object" && !Array.isArray(record.data) ? record.data : {};
639
+ const actor = data.actor;
640
+ if (actor === "user") {
641
+ capability = capabilityInvocation(text) || "unknown";
642
+ pending = [];
643
+ continue;
644
+ }
645
+ if (actor === "assistant") {
646
+ capability = introCapability(text) || capability;
647
+ }
648
+ const warnings = thresholdWarnings(text);
649
+ if (warnings.length > 0) {
650
+ const artifactLabel = canonicalArtifactLabel(text, loaded) || "unknown";
651
+ const beforeMetrics = detailMetrics(text);
652
+ const event = {
653
+ event_label: recordLabel(record, salt),
654
+ conversation: hashLabel("session", conversationKey, salt),
655
+ capability,
656
+ artifact_label: artifactLabel,
657
+ warnings,
658
+ detail_loss_status: "not_assessed",
659
+ rewrite_followup: null,
660
+ observed_counts: {
661
+ warning_word_count: beforeMetrics.word_count,
662
+ warning_anchor_count: beforeMetrics.anchor_count,
663
+ },
664
+ };
665
+ warningEvents.push(event);
666
+ pending.push({ event, metrics: beforeMetrics });
667
+ inc(artifactCounts, artifactLabel);
668
+ inc(capabilityCounts, capability);
669
+ for (const warning of warnings) {
670
+ inc(warningCounts, `${warning.family}.${warning.category}`);
671
+ inc(sourceCounts, warning.threshold_source);
672
+ }
673
+ }
674
+ if (pending.length > 0 && record.source_kind === "tool_call") {
675
+ const [eventClass] = classifyStartupEvent(record);
676
+ if (eventClass === "implementation_boundary") {
677
+ const afterMetrics = detailMetrics(text);
678
+ for (const pendingItem of pending) {
679
+ const event = pendingItem.event;
680
+ if (event.rewrite_followup !== null)
681
+ continue;
682
+ const status = detailLossStatus(pendingItem.metrics, afterMetrics);
683
+ event.detail_loss_status = status;
684
+ event.rewrite_followup = {
685
+ event_label: recordLabel(record, salt),
686
+ event_class: eventClass,
687
+ word_count_delta: afterMetrics.word_count - pendingItem.metrics.word_count,
688
+ anchor_count_delta: afterMetrics.anchor_count - pendingItem.metrics.anchor_count,
689
+ };
690
+ inc(detailStatusCounts, status);
691
+ }
692
+ pending = [];
693
+ }
694
+ }
695
+ }
696
+ for (const pendingItem of pending) {
697
+ inc(detailStatusCounts, pendingItem.event.detail_loss_status);
698
+ }
699
+ }
700
+ if (degradations.length > 0) {
701
+ coverageCaveats.push("One or more records were skipped because they were malformed or lacked conversation identity.");
702
+ }
703
+ return {
704
+ output_envelope: THRESHOLD_EVIDENCE_ENVELOPE,
705
+ contract_version: loaded.version,
706
+ generated_at: nowIsoSeconds(),
707
+ total_records: records.length,
708
+ runtime_coverage: runtimeCoverage,
709
+ coverage_caveats: [...new Set(coverageCaveats)],
710
+ counts: {
711
+ warning_events: warningEvents.length,
712
+ by_warning: counterDict(warningCounts),
713
+ by_artifact: counterDict(artifactCounts),
714
+ by_capability: counterDict(capabilityCounts),
715
+ by_threshold_source: counterDict(sourceCounts),
716
+ by_detail_loss_status: counterDict(detailStatusCounts),
717
+ },
718
+ warning_events: warningEvents,
719
+ degradations,
720
+ privacy_redaction_summary: {
721
+ raw_transcript_text: "not_emitted",
722
+ raw_tool_arguments: "not_emitted",
723
+ full_local_paths: "not_emitted",
724
+ raw_store_paths: "not_emitted",
725
+ session_ids: "salted_or_not_emitted",
726
+ artifact_labels: "canonical_only",
727
+ },
728
+ };
729
+ }
730
+ export function scanRetainedThresholdEvidence(artifacts, opts) {
731
+ const salt = opts.salt;
732
+ const loaded = opts.contract ?? loadContract();
733
+ const warningCounts = {};
734
+ const artifactCounts = {};
735
+ const sourceCounts = {};
736
+ const detailStatusCounts = {};
737
+ const warningEvents = [];
738
+ for (const sourceLabel of Object.keys(artifacts).sort()) {
739
+ const text = artifacts[sourceLabel];
740
+ if (typeof sourceLabel !== "string" || typeof text !== "string")
741
+ continue;
742
+ const artifactLabel = canonicalArtifactLabel(sourceLabel, loaded) || canonicalArtifactLabel(text, loaded) || "unknown";
743
+ const warnings = thresholdWarnings(text);
744
+ if (warnings.length === 0)
745
+ continue;
746
+ const postAuditMarkers = countMatches(POST_AUDIT_FLAG_RE, text);
747
+ const budgetMentions = countMatches(BUDGET_PRESSURE_RE, text);
748
+ const fullPlanBudgetPressure = artifactLabel === "PLAN.md" && budgetMentions > 0 && FULL_PLAN_BUDGET_RE.test(text);
749
+ const detailStatus = postAuditMarkers && fullPlanBudgetPressure ? "retained_artifact_false_positive_signal" : "not_assessed";
750
+ const event = {
751
+ event_label: hashLabel("record", sourceLabel, salt),
752
+ conversation: "retained_artifacts",
753
+ capability: "agentera",
754
+ artifact_label: artifactLabel,
755
+ evidence_source: "retained_artifact",
756
+ warnings,
757
+ detail_loss_status: detailStatus,
758
+ rewrite_followup: null,
759
+ observed_counts: {
760
+ post_audit_flag_markers: postAuditMarkers,
761
+ budget_pressure_mentions: budgetMentions,
762
+ },
763
+ };
764
+ warningEvents.push(event);
765
+ inc(artifactCounts, artifactLabel);
766
+ inc(detailStatusCounts, detailStatus);
767
+ for (const warning of warnings) {
768
+ inc(warningCounts, `${warning.family}.${warning.category}`);
769
+ inc(sourceCounts, warning.threshold_source);
770
+ }
771
+ }
772
+ return {
773
+ output_envelope: THRESHOLD_EVIDENCE_ENVELOPE,
774
+ contract_version: loaded.version,
775
+ generated_at: nowIsoSeconds(),
776
+ total_records: Object.keys(artifacts).length,
777
+ runtime_coverage: [],
778
+ coverage_caveats: [],
779
+ counts: {
780
+ warning_events: warningEvents.length,
781
+ by_warning: counterDict(warningCounts),
782
+ by_artifact: counterDict(artifactCounts),
783
+ by_capability: warningEvents.length > 0 ? { agentera: warningEvents.length } : {},
784
+ by_threshold_source: counterDict(sourceCounts),
785
+ by_detail_loss_status: counterDict(detailStatusCounts),
786
+ },
787
+ warning_events: warningEvents,
788
+ degradations: [],
789
+ privacy_redaction_summary: {
790
+ raw_artifact_text: "not_emitted",
791
+ raw_transcript_text: "not_emitted",
792
+ raw_tool_arguments: "not_emitted",
793
+ full_local_paths: "not_emitted",
794
+ artifact_labels: "canonical_only",
795
+ },
796
+ };
797
+ }
798
+ export function classifyThresholdEvidence(scan) {
799
+ let events = scan.warning_events;
800
+ if (!Array.isArray(events))
801
+ events = [];
802
+ let coverageCaveats = scan.coverage_caveats;
803
+ if (!Array.isArray(coverageCaveats))
804
+ coverageCaveats = [];
805
+ const byCategory = new Map();
806
+ const coverageCaveated = coverageCaveats.length > 0;
807
+ const unsupported = coverageCaveats.length > 0 && events.length === 0;
808
+ for (const event of events) {
809
+ if (!event || typeof event !== "object" || Array.isArray(event))
810
+ continue;
811
+ const classification = eventClassification(event, coverageCaveated);
812
+ const artifact = typeof event.artifact_label === "string" ? event.artifact_label : "unknown";
813
+ const capability = typeof event.capability === "string" ? event.capability : "unknown";
814
+ const detailStatus = typeof event.detail_loss_status === "string" ? event.detail_loss_status : "not_assessed";
815
+ for (const key of eventWarningKeys(event)) {
816
+ let entry = byCategory.get(key);
817
+ if (!entry) {
818
+ entry = {
819
+ warning: key,
820
+ event_count: 0,
821
+ artifacts: new Set(),
822
+ capabilities: new Set(),
823
+ classification_counts: {},
824
+ detail_loss_status_counts: {},
825
+ event_labels: [],
826
+ };
827
+ byCategory.set(key, entry);
828
+ }
829
+ entry.event_count += 1;
830
+ entry.artifacts.add(artifact);
831
+ entry.capabilities.add(capability);
832
+ inc(entry.classification_counts, classification);
833
+ inc(entry.detail_loss_status_counts, detailStatus);
834
+ if (typeof event.event_label === "string")
835
+ entry.event_labels.push(event.event_label);
836
+ }
837
+ }
838
+ const categories = [];
839
+ let repeatedFalsePositive = false;
840
+ const classificationCounts = {};
841
+ for (const key of [...byCategory.keys()].sort()) {
842
+ const entry = byCategory.get(key);
843
+ const classification = categoryClassification(entry.classification_counts);
844
+ inc(classificationCounts, classification);
845
+ if (classification === "likely_false_positive" && entry.event_count >= 2)
846
+ repeatedFalsePositive = true;
847
+ categories.push({
848
+ warning: key,
849
+ classification,
850
+ event_count: entry.event_count,
851
+ artifacts: [...entry.artifacts].sort(),
852
+ capabilities: [...entry.capabilities].sort(),
853
+ event_classification_counts: counterDict(entry.classification_counts),
854
+ detail_loss_status_counts: counterDict(entry.detail_loss_status_counts),
855
+ event_labels: entry.event_labels,
856
+ });
857
+ }
858
+ if (unsupported)
859
+ inc(classificationCounts, "unsupported_by_available_coverage");
860
+ let recommendation;
861
+ if (repeatedFalsePositive) {
862
+ recommendation = {
863
+ action: "consider_minimal_threshold_or_diagnostic_change",
864
+ reason: "At least one warning category has repeated redacted evidence of possible useful detail removal.",
865
+ };
866
+ }
867
+ else if (unsupported) {
868
+ recommendation = {
869
+ action: "defer_without_threshold_change",
870
+ reason: "Available runtime coverage cannot support a false-positive conclusion.",
871
+ };
872
+ }
873
+ else {
874
+ recommendation = {
875
+ action: "no_threshold_change_yet",
876
+ reason: "False-positive evidence is absent, single-instance, legitimate pressure, or inconclusive.",
877
+ };
878
+ }
879
+ return {
880
+ output_envelope: THRESHOLD_CLASSIFICATION_ENVELOPE,
881
+ input_envelope: scan.output_envelope,
882
+ generated_at: nowIsoSeconds(),
883
+ categories,
884
+ counts: {
885
+ by_classification: counterDict(classificationCounts),
886
+ category_count: categories.length,
887
+ repeated_false_positive_categories: categories.filter((c) => c.classification === "likely_false_positive" && c.event_count >= 2).length,
888
+ },
889
+ recommendation,
890
+ coverage_caveats: coverageCaveats.map((c) => String(c)),
891
+ privacy_redaction_summary: scan.privacy_redaction_summary || {
892
+ raw_transcript_text: "not_emitted",
893
+ raw_tool_arguments: "not_emitted",
894
+ },
895
+ };
896
+ }
897
+ // ===========================================================================
898
+ // Slice 3: startup record classification into state-gathering sequences
899
+ // ===========================================================================
900
+ function hasTranscriptBearingField(record) {
901
+ if ("transcript" in record)
902
+ return true;
903
+ const data = record.data;
904
+ return Boolean(data && typeof data === "object" && !Array.isArray(data) && "transcript" in data);
905
+ }
906
+ function boundedReason(reason, contract) {
907
+ const allowed = new Set([...(contract.degradation_reasons ?? []), ...BOUNDARY_DEGRADATION_REASONS]);
908
+ return allowed.has(reason) ? reason : "malformed_record";
909
+ }
910
+ function estimatedToolArgumentTokens(record) {
911
+ return Math.ceil(Buffer.byteLength(argumentsText(record), "utf8") / 4);
912
+ }
913
+ function timestampUtc(value) {
914
+ return parseTimestamp(value);
915
+ }
916
+ function newSequence(conversationKey, capability, salt) {
917
+ const counts = {};
918
+ for (const eventClass of [...STATE_EVENT_CLASSES].sort())
919
+ counts[eventClass] = 0;
920
+ return {
921
+ conversation: hashLabel("session", conversationKey, salt),
922
+ capability: capability || "unknown",
923
+ start_anchor: "first_cli_state_call_after_capability_invocation",
924
+ events: [],
925
+ counts,
926
+ cli_artifact_labels: [],
927
+ raw_artifact_labels_after_cli: [],
928
+ redundant_raw_artifact_labels: [],
929
+ estimated_raw_after_cli_tokens_by_artifact: {},
930
+ estimated_redundant_raw_tokens_by_artifact: {},
931
+ degradation_reasons: [],
932
+ };
933
+ }
934
+ function eventOutput(record, opts) {
935
+ const item = {
936
+ record: recordLabel(record, opts.salt),
937
+ event_class: STATE_EVENT_CLASSES.has(opts.eventClass) ? opts.eventClass : "non_state_context",
938
+ phase: opts.phase,
939
+ };
940
+ if (opts.artifactLabel)
941
+ item.artifact_label = opts.artifactLabel;
942
+ if (opts.stateCommand)
943
+ item.state_command = opts.stateCommand;
944
+ if (opts.redundantWithCli !== undefined && opts.redundantWithCli !== null) {
945
+ item.redundant_with_cli = opts.redundantWithCli;
946
+ }
947
+ return item;
948
+ }
949
+ export function classifyStartupRecords(corpus, opts) {
950
+ const salt = opts.salt;
951
+ const loaded = opts.contract ?? loadContract();
952
+ const boundary = timestampUtc((loaded.boundary ?? {}).committed_at);
953
+ const records = corpus && typeof corpus === "object" && !Array.isArray(corpus) ? (corpus.records ?? []) : [];
954
+ const degradations = [];
955
+ const groups = new Map();
956
+ for (const record of Array.isArray(records) ? records : []) {
957
+ if (!record || typeof record !== "object" || Array.isArray(record)) {
958
+ degradations.push({ reason: boundedReason("malformed_record", loaded) });
959
+ continue;
960
+ }
961
+ if (hasTranscriptBearingField(record)) {
962
+ degradations.push({ record: recordLabel(record, salt), reason: "privacy_redaction_required" });
963
+ continue;
964
+ }
965
+ const timestamp = timestampUtc(record.timestamp);
966
+ if (timestamp === null) {
967
+ degradations.push({ record: recordLabel(record, salt), reason: "missing_timestamp" });
968
+ continue;
969
+ }
970
+ if (boundary !== null && timestamp.getTime() <= boundary.getTime()) {
971
+ degradations.push({ record: recordLabel(record, salt), reason: "pre_boundary_record" });
972
+ continue;
973
+ }
974
+ const key = startupConversationKey(record);
975
+ if (key === null) {
976
+ degradations.push({ record: recordLabel(record, salt), reason: "missing_conversation_key" });
977
+ continue;
978
+ }
979
+ if (!groups.has(key))
980
+ groups.set(key, []);
981
+ groups.get(key).push(record);
982
+ }
983
+ const sequences = [];
984
+ for (const [conversationKey, items] of groups) {
985
+ items.sort((a, b) => {
986
+ const ta = String(a.timestamp ?? "");
987
+ const tb = String(b.timestamp ?? "");
988
+ return ta < tb ? -1 : ta > tb ? 1 : 0;
989
+ });
990
+ const state = {
991
+ active: null,
992
+ segmentCapability: null,
993
+ segmentOpen: false,
994
+ segmentHadStateSequence: false,
995
+ cliArtifactsSeen: new Set(),
996
+ };
997
+ const closeActive = () => {
998
+ if (state.active !== null) {
999
+ state.active.cli_artifact_labels = [...state.cliArtifactsSeen].sort();
1000
+ sequences.push(state.active);
1001
+ state.segmentHadStateSequence = true;
1002
+ state.active = null;
1003
+ state.cliArtifactsSeen = new Set();
1004
+ }
1005
+ };
1006
+ for (const record of items) {
1007
+ const text = extractText(record);
1008
+ const data = record.data && typeof record.data === "object" && !Array.isArray(record.data) ? record.data : {};
1009
+ const actor = data.actor;
1010
+ if (actor === "user") {
1011
+ closeActive();
1012
+ if (state.segmentOpen && !state.segmentHadStateSequence) {
1013
+ degradations.push({
1014
+ conversation: hashLabel("session", conversationKey, salt),
1015
+ reason: "no_agentera_state_sequence",
1016
+ });
1017
+ }
1018
+ state.segmentCapability = capabilityInvocation(text);
1019
+ state.segmentOpen = state.segmentCapability !== null;
1020
+ state.segmentHadStateSequence = false;
1021
+ continue;
1022
+ }
1023
+ if (!state.segmentOpen) {
1024
+ const introCap = actor === "assistant" ? introCapability(text) : null;
1025
+ if (introCap) {
1026
+ state.segmentCapability = introCap;
1027
+ state.segmentOpen = true;
1028
+ }
1029
+ else {
1030
+ continue;
1031
+ }
1032
+ }
1033
+ const [eventClass, artifactLabel, stateCommand, cliArtifactLabels] = classifyStartupEvent(record);
1034
+ if (eventClass === "non_state_context")
1035
+ continue;
1036
+ if (eventClass === "cli_state_call" && state.active === null) {
1037
+ state.active = newSequence(conversationKey, state.segmentCapability, salt);
1038
+ }
1039
+ if (state.active === null)
1040
+ continue;
1041
+ if (eventClass === "cli_state_call") {
1042
+ for (const label of cliArtifactLabels)
1043
+ state.cliArtifactsSeen.add(label);
1044
+ }
1045
+ const redundant = eventClass === "raw_artifact_access" && artifactLabel
1046
+ ? state.cliArtifactsSeen.has(artifactLabel)
1047
+ : null;
1048
+ const phase = eventClass === "implementation_boundary" ? "implementation_boundary" : "state_gathering";
1049
+ state.active.counts[eventClass] += 1;
1050
+ state.active.events.push(eventOutput(record, {
1051
+ eventClass,
1052
+ phase,
1053
+ salt,
1054
+ artifactLabel,
1055
+ stateCommand,
1056
+ redundantWithCli: redundant,
1057
+ }));
1058
+ if (eventClass === "raw_artifact_access" && artifactLabel) {
1059
+ state.active.raw_artifact_labels_after_cli.push(artifactLabel);
1060
+ const estimatedTokens = estimatedToolArgumentTokens(record);
1061
+ const rawEstimates = state.active.estimated_raw_after_cli_tokens_by_artifact;
1062
+ rawEstimates[artifactLabel] = (rawEstimates[artifactLabel] ?? 0) + estimatedTokens;
1063
+ if (redundant) {
1064
+ state.active.redundant_raw_artifact_labels.push(artifactLabel);
1065
+ const redundantEstimates = state.active.estimated_redundant_raw_tokens_by_artifact;
1066
+ redundantEstimates[artifactLabel] = (redundantEstimates[artifactLabel] ?? 0) + estimatedTokens;
1067
+ }
1068
+ }
1069
+ if (eventClass === "implementation_boundary") {
1070
+ closeActive();
1071
+ state.segmentOpen = false;
1072
+ state.segmentCapability = null;
1073
+ state.segmentHadStateSequence = false;
1074
+ }
1075
+ }
1076
+ closeActive();
1077
+ if (state.segmentOpen && !state.segmentHadStateSequence) {
1078
+ degradations.push({
1079
+ conversation: hashLabel("session", conversationKey, salt),
1080
+ reason: "no_agentera_state_sequence",
1081
+ });
1082
+ }
1083
+ }
1084
+ return {
1085
+ contract_version: loaded.version,
1086
+ boundary_source: (loaded.boundary ?? {}).source,
1087
+ state_gathering_sequences: sequences,
1088
+ degradations,
1089
+ };
1090
+ }
1091
+ // ===========================================================================
1092
+ // Slice 4: startup metrics aggregation + threshold derivation
1093
+ // ===========================================================================
1094
+ export const STARTUP_METRICS_ENVELOPE = "startup_state_metrics_v1";
1095
+ export const TOKEN_ESTIMATOR_VERSION = "approx_bytes_div_4_v1";
1096
+ /** Python round() — round half to even on IEEE-754 doubles. */
1097
+ function pyRound(x, ndigits) {
1098
+ const m = 10 ** ndigits;
1099
+ const v = x * m;
1100
+ const f = Math.floor(v);
1101
+ const diff = v - f;
1102
+ let r;
1103
+ if (diff > 0.5)
1104
+ r = f + 1;
1105
+ else if (diff < 0.5)
1106
+ r = f;
1107
+ else
1108
+ r = f % 2 === 0 ? f : f + 1;
1109
+ return r / m;
1110
+ }
1111
+ /** Python float marker: serializes with a trailing .0 for whole numbers. */
1112
+ class Flt {
1113
+ v;
1114
+ constructor(v) {
1115
+ this.v = v;
1116
+ }
1117
+ valueOf() {
1118
+ return this.v;
1119
+ }
1120
+ toJSON() {
1121
+ return this.v;
1122
+ }
1123
+ }
1124
+ function flt(n) {
1125
+ return new Flt(n);
1126
+ }
1127
+ function formatFloat(v) {
1128
+ return Number.isInteger(v) ? `${v}.0` : String(v);
1129
+ }
1130
+ /** Python str()/f-string rendering for report scalars. */
1131
+ function pyFmt(value) {
1132
+ if (value instanceof Flt)
1133
+ return formatFloat(value.v);
1134
+ if (value === null || value === undefined)
1135
+ return "None";
1136
+ if (value === true)
1137
+ return "True";
1138
+ if (value === false)
1139
+ return "False";
1140
+ return String(value);
1141
+ }
1142
+ function safeInt(value) {
1143
+ return typeof value === "number" && Number.isInteger(value) ? value : 0;
1144
+ }
1145
+ function mergeTokenEstimates(counter, value) {
1146
+ if (!value || typeof value !== "object" || Array.isArray(value))
1147
+ return;
1148
+ for (const [label, estimate] of Object.entries(value)) {
1149
+ if (typeof label === "string" && typeof estimate === "number" && Number.isInteger(estimate)) {
1150
+ counter[label] = (counter[label] ?? 0) + estimate;
1151
+ }
1152
+ }
1153
+ }
1154
+ function sequenceCount(sequence, eventClass) {
1155
+ const counts = sequence.counts;
1156
+ if (counts && typeof counts === "object" && typeof counts[eventClass] === "number") {
1157
+ return counts[eventClass];
1158
+ }
1159
+ return (sequence.events ?? []).filter((event) => event && typeof event === "object" && event.event_class === eventClass).length;
1160
+ }
1161
+ function distribution(values) {
1162
+ if (values.length === 0) {
1163
+ return { count: 0, min: 0, max: 0, mean: 0, p50: 0, p75: 0, histogram: {} };
1164
+ }
1165
+ const ordered = [...values].sort((a, b) => a - b);
1166
+ const percentile = (fraction) => {
1167
+ const index = Math.min(ordered.length - 1, Math.ceil(ordered.length * fraction - 1));
1168
+ return ordered[Math.max(index, 0)];
1169
+ };
1170
+ const histCounts = {};
1171
+ for (const v of ordered)
1172
+ histCounts[v] = (histCounts[v] ?? 0) + 1;
1173
+ const histogram = {};
1174
+ for (const key of Object.keys(histCounts).map(Number).sort((a, b) => a - b)) {
1175
+ histogram[String(key)] = histCounts[key];
1176
+ }
1177
+ return {
1178
+ count: ordered.length,
1179
+ min: ordered[0],
1180
+ max: ordered[ordered.length - 1],
1181
+ mean: flt(pyRound(ordered.reduce((a, b) => a + b, 0) / ordered.length, 2)),
1182
+ p50: percentile(0.5),
1183
+ p75: percentile(0.75),
1184
+ histogram,
1185
+ };
1186
+ }
1187
+ function unionAll(map) {
1188
+ const out = new Set();
1189
+ for (const s of Object.values(map))
1190
+ for (const v of s)
1191
+ out.add(v);
1192
+ return out;
1193
+ }
1194
+ function deriveStateThresholds(args) {
1195
+ const capabilityCount = Object.keys(args.perCapability).length;
1196
+ const credibleDistribution = args.totalSequences >= 3;
1197
+ const rawDistribution = distribution(args.rawAfterCliPerSequence);
1198
+ const redundantDistribution = distribution(args.redundantRawPerSequence);
1199
+ const redundantArtifacts = {};
1200
+ for (const label of Object.keys(args.redundantCounts).sort()) {
1201
+ const count = args.redundantCounts[label];
1202
+ if (count > 0) {
1203
+ redundantArtifacts[label] = {
1204
+ count,
1205
+ capability_count: (args.redundantCapabilities[label] ?? new Set()).size,
1206
+ };
1207
+ }
1208
+ }
1209
+ const redundantSequenceCount = args.redundantRawPerSequence.filter((v) => v > 0).length;
1210
+ const aggregateRedundantCapabilities = Object.keys(args.redundantCapabilities).length > 0 ? unionAll(args.redundantCapabilities) : new Set();
1211
+ let redundantSequenceThreshold;
1212
+ let thresholdReason;
1213
+ if (credibleDistribution) {
1214
+ redundantSequenceThreshold = Math.max(2, Math.ceil(args.totalSequences * 0.2));
1215
+ thresholdReason =
1216
+ "Selected from post-boundary state-gathering distribution: raw artifact " +
1217
+ "access after CLI state must recur in at least 20% of measured sequences, " +
1218
+ "with a floor of two sequences.";
1219
+ }
1220
+ else {
1221
+ redundantSequenceThreshold = null;
1222
+ thresholdReason = "No broad-envelope threshold: fewer than three state-gathering sequences were measured.";
1223
+ }
1224
+ let broadTrigger = null;
1225
+ if (credibleDistribution) {
1226
+ for (const [label, item] of Object.entries(redundantArtifacts)) {
1227
+ if (item.count >= redundantSequenceThreshold && item.capability_count >= 2) {
1228
+ broadTrigger = {
1229
+ event_class: "raw_artifact_access",
1230
+ artifact_label: label,
1231
+ count: item.count,
1232
+ capability_count: item.capability_count,
1233
+ threshold: redundantSequenceThreshold,
1234
+ };
1235
+ break;
1236
+ }
1237
+ }
1238
+ if (broadTrigger === null && redundantSequenceCount >= redundantSequenceThreshold) {
1239
+ broadTrigger = {
1240
+ event_class: "raw_artifact_access",
1241
+ artifact_label: "multiple",
1242
+ count: redundantSequenceCount,
1243
+ capability_count: aggregateRedundantCapabilities.size,
1244
+ threshold: redundantSequenceThreshold,
1245
+ aggregate: true,
1246
+ };
1247
+ }
1248
+ }
1249
+ let recommendation;
1250
+ if (broadTrigger !== null) {
1251
+ const trigger = broadTrigger.aggregate
1252
+ ? `redundant_raw_artifact_access in ${broadTrigger.count} of ${args.totalSequences} state sequences`
1253
+ : `raw_artifact_access_after_cli:${broadTrigger.artifact_label} repeated ` +
1254
+ `${broadTrigger.count} times across ${broadTrigger.capability_count} capabilities`;
1255
+ recommendation = {
1256
+ action: "plan_cli_startup_envelope",
1257
+ measured_trigger: trigger,
1258
+ rationale: "Raw artifact access after CLI state exceeded the broad startup-envelope threshold.",
1259
+ };
1260
+ }
1261
+ else if (Object.keys(redundantArtifacts).length > 0) {
1262
+ recommendation = {
1263
+ action: "targeted_capability_guidance_fixes",
1264
+ measured_trigger: "raw_artifact_access_after_cli_hotspot",
1265
+ rationale: "Raw artifact access follows CLI state, but evidence is narrow or below the broad-envelope gate.",
1266
+ };
1267
+ }
1268
+ else {
1269
+ recommendation = {
1270
+ action: "close_without_implementation",
1271
+ measured_trigger: "none",
1272
+ rationale: "No raw artifact access after overlapping CLI state was measured.",
1273
+ };
1274
+ }
1275
+ if (args.confidenceCaveats.includes("insufficient_post_2_3_state_sequences")) {
1276
+ recommendation = {
1277
+ action: "close_without_implementation",
1278
+ measured_trigger: "weak_evidence",
1279
+ rationale: "No post-boundary Agentera state-gathering sequences were available.",
1280
+ };
1281
+ }
1282
+ return {
1283
+ measured_distribution: {
1284
+ raw_after_cli_per_sequence: rawDistribution,
1285
+ redundant_raw_after_cli_per_sequence: redundantDistribution,
1286
+ redundant_sequence_count: redundantSequenceCount,
1287
+ redundant_artifacts: redundantArtifacts,
1288
+ capability_count: capabilityCount,
1289
+ },
1290
+ action_thresholds: {
1291
+ startup_envelope: {
1292
+ credible: credibleDistribution,
1293
+ redundant_sequence_threshold: redundantSequenceThreshold,
1294
+ selection_reason: thresholdReason,
1295
+ },
1296
+ targeted_guidance: {
1297
+ credible: Object.keys(redundantArtifacts).length > 0,
1298
+ selection_reason: "Selected when raw artifact access after CLI state is narrow or below the broad-envelope threshold.",
1299
+ },
1300
+ },
1301
+ recommendation,
1302
+ };
1303
+ }
1304
+ export function aggregateStartupMetrics(intermediateInput) {
1305
+ const intermediate = intermediateInput && typeof intermediateInput === "object" && !Array.isArray(intermediateInput) ? intermediateInput : {};
1306
+ const sequences = Array.isArray(intermediate.state_gathering_sequences) ? intermediate.state_gathering_sequences : [];
1307
+ const degradations = Array.isArray(intermediate.degradations) ? intermediate.degradations : [];
1308
+ const perCapability = {};
1309
+ const cliCommandCounts = {};
1310
+ const rawAfterCliCounts = {};
1311
+ const redundantRawCounts = {};
1312
+ const rawAfterCliTokenEstimates = {};
1313
+ const redundantRawTokenEstimates = {};
1314
+ const proseCounts = {};
1315
+ const implementationCounts = {};
1316
+ const degradationCounts = {};
1317
+ const redundantCapabilities = {};
1318
+ const rawAfterCliPerSequence = [];
1319
+ const redundantRawPerSequence = [];
1320
+ for (const item of degradations) {
1321
+ if (item && typeof item === "object" && typeof item.reason === "string") {
1322
+ inc(degradationCounts, item.reason);
1323
+ }
1324
+ }
1325
+ for (const sequence of sequences) {
1326
+ if (!sequence || typeof sequence !== "object" || Array.isArray(sequence))
1327
+ continue;
1328
+ let capability = sequence.capability;
1329
+ if (typeof capability !== "string" || !capability)
1330
+ capability = "unknown";
1331
+ if (!(capability in perCapability)) {
1332
+ perCapability[capability] = {
1333
+ state_sequences: 0,
1334
+ cli_state_call: 0,
1335
+ raw_artifact_access_after_cli: 0,
1336
+ redundant_raw_artifact_access: 0,
1337
+ capability_prose_read: 0,
1338
+ implementation_boundary: 0,
1339
+ };
1340
+ }
1341
+ const cc = perCapability[capability];
1342
+ cc.state_sequences += 1;
1343
+ const cliCount = sequenceCount(sequence, "cli_state_call");
1344
+ const rawCount = (sequence.raw_artifact_labels_after_cli ?? []).length;
1345
+ const redundantCount = (sequence.redundant_raw_artifact_labels ?? []).length;
1346
+ const proseCount = sequenceCount(sequence, "capability_prose_read");
1347
+ const implCount = sequenceCount(sequence, "implementation_boundary");
1348
+ cc.cli_state_call += cliCount;
1349
+ cc.raw_artifact_access_after_cli += rawCount;
1350
+ cc.redundant_raw_artifact_access += redundantCount;
1351
+ cc.capability_prose_read += proseCount;
1352
+ cc.implementation_boundary += implCount;
1353
+ proseCounts[capability] = (proseCounts[capability] ?? 0) + proseCount;
1354
+ implementationCounts[capability] = (implementationCounts[capability] ?? 0) + implCount;
1355
+ rawAfterCliPerSequence.push(rawCount);
1356
+ redundantRawPerSequence.push(redundantCount);
1357
+ mergeTokenEstimates(rawAfterCliTokenEstimates, sequence.estimated_raw_after_cli_tokens_by_artifact);
1358
+ mergeTokenEstimates(redundantRawTokenEstimates, sequence.estimated_redundant_raw_tokens_by_artifact);
1359
+ for (const event of sequence.events ?? []) {
1360
+ if (!event || typeof event !== "object")
1361
+ continue;
1362
+ const stateCommand = event.state_command;
1363
+ if (event.event_class === "cli_state_call" && typeof stateCommand === "string") {
1364
+ inc(cliCommandCounts, stateCommand);
1365
+ }
1366
+ const label = event.artifact_label;
1367
+ if (event.event_class === "raw_artifact_access" && typeof label === "string") {
1368
+ inc(rawAfterCliCounts, label);
1369
+ if (event.redundant_with_cli === true) {
1370
+ inc(redundantRawCounts, label);
1371
+ if (!(label in redundantCapabilities))
1372
+ redundantCapabilities[label] = new Set();
1373
+ redundantCapabilities[label].add(capability);
1374
+ }
1375
+ }
1376
+ }
1377
+ for (const reason of sequence.degradation_reasons ?? []) {
1378
+ if (typeof reason === "string")
1379
+ inc(degradationCounts, reason);
1380
+ }
1381
+ }
1382
+ const totalSequences = sequences.filter((s) => s && typeof s === "object" && !Array.isArray(s)).length;
1383
+ let runtimeCoverage = Array.isArray(intermediate.runtime_coverage) ? intermediate.runtime_coverage : [];
1384
+ runtimeCoverage = runtimeCoverage
1385
+ .filter((s) => s && typeof s === "object" && !Array.isArray(s))
1386
+ .map((s) => boundedRuntimeStatus(s));
1387
+ const runtimeStatusCounts = {};
1388
+ for (const status of runtimeCoverage) {
1389
+ if (status && typeof status === "object" && typeof status.status === "string")
1390
+ inc(runtimeStatusCounts, status.status);
1391
+ }
1392
+ const confidenceCaveats = [];
1393
+ if (totalSequences === 0)
1394
+ confidenceCaveats.push("insufficient_post_2_3_state_sequences");
1395
+ if (runtimeCoverage.some((s) => s && typeof s === "object" && ["missing", "sparse", "degraded", "skipped"].includes(s.status))) {
1396
+ confidenceCaveats.push("runtime_coverage_incomplete_or_degraded");
1397
+ }
1398
+ if (Object.keys(degradationCounts).length > 0)
1399
+ confidenceCaveats.push("some_records_or_sequences_degraded");
1400
+ const thresholdDerivation = deriveStateThresholds({
1401
+ totalSequences,
1402
+ perCapability,
1403
+ rawAfterCliPerSequence,
1404
+ redundantRawPerSequence,
1405
+ redundantCounts: redundantRawCounts,
1406
+ redundantCapabilities,
1407
+ confidenceCaveats,
1408
+ });
1409
+ const sequencesWithRaw = rawAfterCliPerSequence.filter((v) => v > 0).length;
1410
+ const sequencesWithRedundant = redundantRawPerSequence.filter((v) => v > 0).length;
1411
+ const recommendation = thresholdDerivation.recommendation;
1412
+ const sortedPerCapability = {};
1413
+ for (const key of Object.keys(perCapability).sort())
1414
+ sortedPerCapability[key] = perCapability[key];
1415
+ const sumValues = (o) => Object.values(o).reduce((a, b) => a + b, 0);
1416
+ const result = {
1417
+ output_envelope: STARTUP_METRICS_ENVELOPE,
1418
+ input_envelope: intermediate.output_envelope ?? null,
1419
+ contract_version: intermediate.contract_version ?? null,
1420
+ generated_at: nowIsoSeconds(),
1421
+ boundary_source: intermediate.boundary_source ?? null,
1422
+ boundary_commit: intermediate.boundary_commit ?? null,
1423
+ boundary_committed_at: intermediate.boundary_committed_at ?? null,
1424
+ benchmark_mode: intermediate.benchmark_mode || "full_boundary_snapshot",
1425
+ benchmark_previous_watermark_at: intermediate.benchmark_previous_watermark_at ?? null,
1426
+ benchmark_window_started_after: intermediate.benchmark_window_started_after ?? null,
1427
+ benchmark_watermark_at: intermediate.benchmark_watermark_at ?? null,
1428
+ corpus_adapter_version: intermediate.corpus_adapter_version ?? null,
1429
+ runtime_coverage: runtimeCoverage,
1430
+ runtime_status_counts: counterDict(runtimeStatusCounts),
1431
+ runtime_record_counts: intermediate.runtime_record_counts || {},
1432
+ total_records: safeInt(intermediate.total_records_read),
1433
+ total_state_sequences: totalSequences,
1434
+ state_sequences_with_raw_after_cli: sequencesWithRaw,
1435
+ state_sequences_with_redundant_raw_access: sequencesWithRedundant,
1436
+ total_cli_state_calls: sumValues(cliCommandCounts),
1437
+ total_raw_artifact_access_after_cli: sumValues(rawAfterCliCounts),
1438
+ total_redundant_raw_artifact_accesses: sumValues(redundantRawCounts),
1439
+ raw_after_cli_sequence_rate: totalSequences ? flt(pyRound(sequencesWithRaw / totalSequences, 4)) : 0,
1440
+ redundant_raw_sequence_rate: totalSequences ? flt(pyRound(sequencesWithRedundant / totalSequences, 4)) : 0,
1441
+ per_capability_state_counts: sortedPerCapability,
1442
+ cli_state_command_counts: counterDict(cliCommandCounts),
1443
+ raw_artifact_access_after_cli_counts: counterDict(rawAfterCliCounts),
1444
+ redundant_raw_artifact_access_counts: counterDict(redundantRawCounts),
1445
+ token_estimator_version: TOKEN_ESTIMATOR_VERSION,
1446
+ estimated_raw_after_cli_tokens: sumValues(rawAfterCliTokenEstimates),
1447
+ estimated_redundant_raw_tokens: sumValues(redundantRawTokenEstimates),
1448
+ estimated_raw_after_cli_tokens_by_artifact: counterDict(rawAfterCliTokenEstimates),
1449
+ estimated_redundant_raw_tokens_by_artifact: counterDict(redundantRawTokenEstimates),
1450
+ estimated_tokens_saved_vs_previous: null,
1451
+ estimated_tokens_saved_vs_previous_null_reason: "previous_row_missing",
1452
+ capability_prose_read_counts: counterDict(proseCounts),
1453
+ implementation_boundary_counts: counterDict(implementationCounts),
1454
+ degradation_reason_counts: counterDict(degradationCounts),
1455
+ privacy_redaction_summary: {
1456
+ raw_transcript_text: "not_emitted",
1457
+ full_local_paths: "not_emitted",
1458
+ raw_store_paths: "not_emitted",
1459
+ session_ids: "salted_or_not_emitted",
1460
+ artifact_labels: "canonical_only",
1461
+ },
1462
+ confidence_caveats: confidenceCaveats,
1463
+ insufficient_evidence_reason: totalSequences === 0 ? "no_post_2_3_state_sequences" : null,
1464
+ threshold_derivation: thresholdDerivation,
1465
+ startup_recommendation: recommendation,
1466
+ implementation_recommended: recommendation.action === "plan_cli_startup_envelope",
1467
+ compatibility_note: "Section 22 corpus records are read-only; aggregation consumes only startup_state_analysis_v1.",
1468
+ };
1469
+ if (totalSequences) {
1470
+ result.recommendation_gate_input = thresholdDerivation.measured_distribution;
1471
+ }
1472
+ return result;
1473
+ }
1474
+ // ===========================================================================
1475
+ // Slice 5: startup report rendering + writing
1476
+ // ===========================================================================
1477
+ export const STARTUP_REPORT_MARKDOWN = "startup-overhead-report.md";
1478
+ export const STARTUP_REPORT_JSON = "startup-overhead-report.json";
1479
+ /** Python json.dumps(value, indent=2, sort_keys=True) — multiline, ensure_ascii, Flt-aware. */
1480
+ function pyJsonIndent(value, level = 0, indent = " ") {
1481
+ const pad = indent.repeat(level);
1482
+ const childPad = indent.repeat(level + 1);
1483
+ if (value === null || value === undefined)
1484
+ return "null";
1485
+ if (value === true)
1486
+ return "true";
1487
+ if (value === false)
1488
+ return "false";
1489
+ if (value instanceof Flt)
1490
+ return formatFloat(value.v);
1491
+ if (typeof value === "number")
1492
+ return String(value);
1493
+ if (typeof value === "string")
1494
+ return pyJsonString(value);
1495
+ if (Array.isArray(value)) {
1496
+ if (value.length === 0)
1497
+ return "[]";
1498
+ const items = value.map((v) => childPad + pyJsonIndent(v, level + 1, indent));
1499
+ return "[\n" + items.join(",\n") + "\n" + pad + "]";
1500
+ }
1501
+ if (typeof value === "object") {
1502
+ const keys = Object.keys(value).sort();
1503
+ if (keys.length === 0)
1504
+ return "{}";
1505
+ const items = keys.map((k) => childPad + pyJsonString(k) + ": " + pyJsonIndent(value[k], level + 1, indent));
1506
+ return "{\n" + items.join(",\n") + "\n" + pad + "}";
1507
+ }
1508
+ return "null";
1509
+ }
1510
+ function markdownTable(headers, rows) {
1511
+ const lines = [
1512
+ "| " + headers.join(" | ") + " |",
1513
+ "| " + headers.map(() => "---").join(" | ") + " |",
1514
+ ];
1515
+ if (rows.length === 0) {
1516
+ return [...lines, "| " + headers.map(() => "none").join(" | ") + " |"];
1517
+ }
1518
+ return [...lines, ...rows.map((row) => "| " + row.map((v) => pyFmt(v)).join(" | ") + " |")];
1519
+ }
1520
+ export function renderStartupReport(metrics) {
1521
+ const threshold = (metrics.threshold_derivation ?? {}).action_thresholds ?? {};
1522
+ const envelopeThreshold = threshold.startup_envelope ?? {};
1523
+ const guidanceThreshold = threshold.targeted_guidance ?? {};
1524
+ const recommendation = metrics.startup_recommendation ?? {};
1525
+ const measuredDistribution = (metrics.threshold_derivation ?? {}).measured_distribution ?? {};
1526
+ const runtimeCoverage = Array.isArray(metrics.runtime_coverage) ? metrics.runtime_coverage : [];
1527
+ const capabilityCounts = metrics.per_capability_state_counts ?? {};
1528
+ const lines = [
1529
+ "# Agentera Startup State-Access Analysis",
1530
+ "",
1531
+ "This report is local-only and privacy-preserving. It measures raw Agentera artifact access after CLI state calls during capability startup/state gathering.",
1532
+ "",
1533
+ "## Boundary Source",
1534
+ "",
1535
+ `- Contract version: \`${pyFmt(metrics.contract_version)}\``,
1536
+ `- Boundary source: \`${pyFmt(metrics.boundary_source)}\``,
1537
+ `- Boundary commit: \`${pyFmt(metrics.boundary_commit)}\``,
1538
+ `- Boundary timestamp: \`${pyFmt(metrics.boundary_committed_at)}\``,
1539
+ `- Corpus adapter version: \`${pyFmt(metrics.corpus_adapter_version)}\``,
1540
+ "",
1541
+ "## Benchmark Window",
1542
+ "",
1543
+ `- Mode: \`${pyFmt(metrics.benchmark_mode)}\``,
1544
+ `- Previous watermark: \`${pyFmt(metrics.benchmark_previous_watermark_at)}\``,
1545
+ `- Window started after: \`${pyFmt(metrics.benchmark_window_started_after)}\``,
1546
+ `- Watermark: \`${pyFmt(metrics.benchmark_watermark_at)}\``,
1547
+ "",
1548
+ "## Runtime Coverage",
1549
+ "",
1550
+ ];
1551
+ const runtimeRows = [];
1552
+ for (const status of runtimeCoverage) {
1553
+ if (status && typeof status === "object" && !Array.isArray(status)) {
1554
+ runtimeRows.push([
1555
+ status.runtime ?? "unknown",
1556
+ status.status ?? "unknown",
1557
+ status.reason ?? "unknown",
1558
+ status.record_count ?? 0,
1559
+ status.candidate_count ?? 0,
1560
+ status.error_count ?? 0,
1561
+ ]);
1562
+ }
1563
+ }
1564
+ lines.push(...markdownTable(["Runtime", "Status", "Reason", "Records", "Candidates", "Errors"], runtimeRows));
1565
+ lines.push("", "## Metrics", "", `- Total state-gathering sequences: \`${pyFmt(metrics.total_state_sequences)}\``, `- Sequences with raw artifact access after CLI: \`${pyFmt(metrics.state_sequences_with_raw_after_cli)}\``, `- Sequences with redundant raw artifact access: \`${pyFmt(metrics.state_sequences_with_redundant_raw_access)}\``, `- Raw-after-CLI sequence rate: \`${pyFmt(metrics.raw_after_cli_sequence_rate)}\``, `- Redundant raw sequence rate: \`${pyFmt(metrics.redundant_raw_sequence_rate)}\``, `- CLI state command counts: \`${pyJsonDumps(metrics.cli_state_command_counts ?? {})}\``, `- Raw artifact access after CLI counts: \`${pyJsonDumps(metrics.raw_artifact_access_after_cli_counts ?? {})}\``, `- Redundant raw artifact access counts: \`${pyJsonDumps(metrics.redundant_raw_artifact_access_counts ?? {})}\``, "", "## Estimated Token Impact", "", `- Token estimator version: \`${pyFmt(metrics.token_estimator_version)}\``, `- Estimated raw-after-CLI tokens: \`${pyFmt(metrics.estimated_raw_after_cli_tokens)}\``, `- Estimated redundant raw tokens: \`${pyFmt(metrics.estimated_redundant_raw_tokens)}\``, `- Estimated raw-after-CLI tokens by artifact: \`${pyJsonDumps(metrics.estimated_raw_after_cli_tokens_by_artifact ?? {})}\``, `- Estimated redundant raw tokens by artifact: \`${pyJsonDumps(metrics.estimated_redundant_raw_tokens_by_artifact ?? {})}\``, `- Estimated tokens saved vs previous: \`${pyFmt(metrics.estimated_tokens_saved_vs_previous)}\``, `- Estimated tokens saved null reason: \`${pyFmt(metrics.estimated_tokens_saved_vs_previous_null_reason)}\``, "");
1566
+ const capabilityRows = [];
1567
+ for (const capability of Object.keys(capabilityCounts).sort()) {
1568
+ const counts = capabilityCounts[capability];
1569
+ if (counts && typeof counts === "object" && !Array.isArray(counts)) {
1570
+ capabilityRows.push([
1571
+ capability,
1572
+ counts.state_sequences ?? 0,
1573
+ counts.cli_state_call ?? 0,
1574
+ counts.raw_artifact_access_after_cli ?? 0,
1575
+ counts.redundant_raw_artifact_access ?? 0,
1576
+ counts.capability_prose_read ?? 0,
1577
+ ]);
1578
+ }
1579
+ }
1580
+ lines.push(...markdownTable(["Capability", "Sequences", "CLI Calls", "Raw After CLI", "Redundant Raw", "Prose Reads"], capabilityRows));
1581
+ lines.push("", "## Threshold Rationale", "", `- Startup envelope threshold credible: \`${pyFmt(envelopeThreshold.credible)}\``, `- Redundant-sequence threshold: \`${pyFmt(envelopeThreshold.redundant_sequence_threshold)}\``, `- Startup envelope selection reason: ${pyFmt(envelopeThreshold.selection_reason)}`, `- Targeted-guidance selection reason: ${pyFmt(guidanceThreshold.selection_reason)}`, `- Measured distribution: \`${pyJsonDumps(measuredDistribution)}\``, "", "## Recommendation", "", `- Action: \`${pyFmt(recommendation.action)}\``, `- Measured trigger: \`${pyFmt(recommendation.measured_trigger)}\``, `- Rationale: ${pyFmt(recommendation.rationale)}`, `- Implementation recommended: \`${pyFmt(metrics.implementation_recommended)}\``, "", "## Privacy Caveats", "", "- Raw transcript text is not emitted.", "- Full local paths and raw store paths are not emitted.", "- Session identifiers are salted or omitted.", "- Raw artifact accesses use canonical artifact labels such as `PLAN.md`, not filesystem paths.", "- Runtime coverage may be incomplete or degraded; inspect `confidence_caveats` before selecting follow-up work.", "");
1582
+ return lines.join("\n");
1583
+ }
1584
+ export function writeStartupReports(metrics, outputDir) {
1585
+ fs.mkdirSync(outputDir, { recursive: true });
1586
+ const jsonPath = path.join(outputDir, STARTUP_REPORT_JSON);
1587
+ const markdownPath = path.join(outputDir, STARTUP_REPORT_MARKDOWN);
1588
+ fs.writeFileSync(jsonPath, pyJsonIndent(metrics) + "\n");
1589
+ fs.writeFileSync(markdownPath, renderStartupReport(metrics));
1590
+ return { structured: jsonPath, human_readable: markdownPath };
1591
+ }
1592
+ // ===========================================================================
1593
+ // Slice 6: benchmark persistence + intermediate building + corpus-file extraction
1594
+ // (extract_*_from_runtime_stores depends on extract_corpus and lands in Phase 10)
1595
+ // ===========================================================================
1596
+ import { spawnSync as _spawnSync } from "node:child_process";
1597
+ import { loadTomlFile } from "../core/toml.js";
1598
+ export const STARTUP_INTERMEDIATE_ENVELOPE = "startup_state_analysis_v1";
1599
+ export const BENCHMARK_HISTORY_JSONL = "runs.jsonl";
1600
+ export const BENCHMARK_LATEST_REPORT_JSON = "latest-report.json";
1601
+ export const BENCHMARK_LATEST_REPORT_MARKDOWN = "latest-report.md";
1602
+ const TOKEN_AGGREGATE_FIELDS = new Set([
1603
+ "token_estimator_version",
1604
+ "estimated_raw_after_cli_tokens",
1605
+ "estimated_redundant_raw_tokens",
1606
+ "estimated_raw_after_cli_tokens_by_artifact",
1607
+ "estimated_redundant_raw_tokens_by_artifact",
1608
+ ]);
1609
+ function maxRecordTimestamp(records, after = null) {
1610
+ let latest = null;
1611
+ for (const record of records) {
1612
+ if (!record || typeof record !== "object" || Array.isArray(record))
1613
+ continue;
1614
+ const ts = parseTimestamp(record.timestamp);
1615
+ if (ts === null)
1616
+ continue;
1617
+ if (after !== null && ts.getTime() <= after.getTime())
1618
+ continue;
1619
+ if (latest === null || ts.getTime() > latest.getTime())
1620
+ latest = ts;
1621
+ }
1622
+ return latest;
1623
+ }
1624
+ function artifactLabelCounts(sequences) {
1625
+ const counts = {};
1626
+ for (const sequence of sequences) {
1627
+ for (const event of sequence.events ?? []) {
1628
+ if (event && typeof event === "object" && typeof event.artifact_label === "string") {
1629
+ inc(counts, event.artifact_label);
1630
+ }
1631
+ }
1632
+ }
1633
+ return counterDict(counts);
1634
+ }
1635
+ function runtimeRecordCounts(records) {
1636
+ const counts = {};
1637
+ for (const record of records) {
1638
+ if (record && typeof record === "object" && typeof record.runtime === "string") {
1639
+ inc(counts, record.runtime);
1640
+ }
1641
+ }
1642
+ return counterDict(counts);
1643
+ }
1644
+ function agenteraVersion(root = resolveSourceRoot()) {
1645
+ try {
1646
+ const data = loadTomlFile(path.join(root, "pyproject.toml"));
1647
+ const project = data.project;
1648
+ if (project && typeof project === "object" && typeof project.version === "string") {
1649
+ return project.version;
1650
+ }
1651
+ }
1652
+ catch {
1653
+ return "unknown";
1654
+ }
1655
+ return "unknown";
1656
+ }
1657
+ function gitOutput(args, root = resolveSourceRoot()) {
1658
+ const result = _spawnSync("git", args, { cwd: root, encoding: "utf8", timeout: 5000 });
1659
+ if (result.error || result.status !== 0)
1660
+ return null;
1661
+ return (result.stdout ?? "").trim();
1662
+ }
1663
+ function gitMetadata(root = resolveSourceRoot()) {
1664
+ const commit = gitOutput(["rev-parse", "HEAD"], root) || "unknown";
1665
+ const status = gitOutput(["status", "--porcelain"], root);
1666
+ return { git_commit: commit, git_dirty: status !== null ? Boolean(status) : false };
1667
+ }
1668
+ function runtimeScope(metrics, approvedScope = null) {
1669
+ if (approvedScope && approvedScope.length > 0) {
1670
+ return [...new Set(approvedScope.filter((l) => l).map((l) => String(l)))].sort();
1671
+ }
1672
+ const labels = new Set();
1673
+ for (const item of metrics.runtime_coverage ?? []) {
1674
+ if (item && typeof item === "object" && typeof item.runtime === "string")
1675
+ labels.add(item.runtime);
1676
+ }
1677
+ const runtimeCounts = metrics.runtime_record_counts;
1678
+ if (runtimeCounts && typeof runtimeCounts === "object") {
1679
+ for (const label of Object.keys(runtimeCounts))
1680
+ if (label)
1681
+ labels.add(String(label));
1682
+ }
1683
+ const sorted = [...labels].sort();
1684
+ return sorted.length > 0 ? sorted : ["unknown"];
1685
+ }
1686
+ function runtimeScopeMatches(row, scope) {
1687
+ const value = row.runtime_scope;
1688
+ if (!Array.isArray(value))
1689
+ return false;
1690
+ const a = value.map((l) => String(l)).sort();
1691
+ const b = scope.map((l) => String(l)).sort();
1692
+ return a.length === b.length && a.every((v, i) => v === b[i]);
1693
+ }
1694
+ export function previousBenchmarkWatermark(benchmarkDir, scope) {
1695
+ const historyPath = path.join(benchmarkDir, BENCHMARK_HISTORY_JSONL);
1696
+ let lines;
1697
+ try {
1698
+ lines = fs.readFileSync(historyPath, "utf8").split(/\r\n|\r|\n/);
1699
+ }
1700
+ catch {
1701
+ return null;
1702
+ }
1703
+ let watermark = null;
1704
+ for (const line of lines) {
1705
+ if (!line.trim())
1706
+ continue;
1707
+ let row;
1708
+ try {
1709
+ row = JSON.parse(line);
1710
+ }
1711
+ catch {
1712
+ continue;
1713
+ }
1714
+ if (!row || typeof row !== "object" || !runtimeScopeMatches(row, scope))
1715
+ continue;
1716
+ const candidate = parseTimestamp(row.benchmark_watermark_at);
1717
+ if (candidate !== null)
1718
+ watermark = candidate;
1719
+ }
1720
+ return watermark;
1721
+ }
1722
+ function previousBenchmarkRow(benchmarkDir, scope) {
1723
+ const historyPath = path.join(benchmarkDir, BENCHMARK_HISTORY_JSONL);
1724
+ let lines;
1725
+ try {
1726
+ lines = fs.readFileSync(historyPath, "utf8").split(/\r\n|\r|\n/);
1727
+ }
1728
+ catch {
1729
+ return null;
1730
+ }
1731
+ let previous = null;
1732
+ for (const line of lines) {
1733
+ if (!line.trim())
1734
+ continue;
1735
+ let row;
1736
+ try {
1737
+ row = JSON.parse(line);
1738
+ }
1739
+ catch {
1740
+ continue;
1741
+ }
1742
+ if (row && typeof row === "object" && runtimeScopeMatches(row, scope))
1743
+ previous = row;
1744
+ }
1745
+ return previous;
1746
+ }
1747
+ function withEstimatedTokensSaved(metrics, benchmarkDir, scope) {
1748
+ const enriched = { ...metrics };
1749
+ const previous = previousBenchmarkRow(benchmarkDir, scope);
1750
+ const currentVersion = enriched.token_estimator_version;
1751
+ const currentRedundant = enriched.estimated_redundant_raw_tokens;
1752
+ let reason = null;
1753
+ let saved = null;
1754
+ if (previous === null) {
1755
+ reason = "previous_row_missing";
1756
+ }
1757
+ else if (![...TOKEN_AGGREGATE_FIELDS].every((f) => f in previous)) {
1758
+ reason = "previous_missing_token_estimates";
1759
+ }
1760
+ else if (previous.contract_version !== enriched.contract_version) {
1761
+ reason = "contract_version_mismatch";
1762
+ }
1763
+ else if (previous.benchmark_mode !== (enriched.benchmark_mode || "full_boundary_snapshot")) {
1764
+ reason = "benchmark_mode_mismatch";
1765
+ }
1766
+ else if (!runtimeScopeMatches(previous, scope)) {
1767
+ reason = "runtime_scope_mismatch";
1768
+ }
1769
+ else if (previous.token_estimator_version !== currentVersion) {
1770
+ reason = "estimator_version_mismatch";
1771
+ }
1772
+ else if (!(typeof previous.estimated_redundant_raw_tokens === "number" && Number.isInteger(previous.estimated_redundant_raw_tokens)) ||
1773
+ !(typeof currentRedundant === "number" && Number.isInteger(currentRedundant))) {
1774
+ reason = "previous_missing_token_estimates";
1775
+ }
1776
+ else {
1777
+ saved = previous.estimated_redundant_raw_tokens - currentRedundant;
1778
+ }
1779
+ enriched.estimated_tokens_saved_vs_previous = saved;
1780
+ enriched.estimated_tokens_saved_vs_previous_null_reason = reason;
1781
+ return enriched;
1782
+ }
1783
+ export function buildBenchmarkHistoryRow(metrics, scope = null) {
1784
+ const recommendation = metrics && typeof metrics === "object" && metrics.startup_recommendation && typeof metrics.startup_recommendation === "object"
1785
+ ? metrics.startup_recommendation
1786
+ : {};
1787
+ return {
1788
+ contract_version: metrics.contract_version ?? null,
1789
+ generated_at: metrics.generated_at ?? null,
1790
+ agentera_version: agenteraVersion(),
1791
+ ...gitMetadata(),
1792
+ runtime_scope: runtimeScope(metrics, scope),
1793
+ benchmark_mode: metrics.benchmark_mode || "full_boundary_snapshot",
1794
+ benchmark_previous_watermark_at: metrics.benchmark_previous_watermark_at ?? null,
1795
+ benchmark_window_started_after: metrics.benchmark_window_started_after ?? null,
1796
+ benchmark_watermark_at: metrics.benchmark_watermark_at ?? null,
1797
+ total_records: safeInt(metrics.total_records),
1798
+ total_state_sequences: safeInt(metrics.total_state_sequences),
1799
+ state_sequences_with_raw_after_cli: safeInt(metrics.state_sequences_with_raw_after_cli),
1800
+ state_sequences_with_redundant_raw_access: safeInt(metrics.state_sequences_with_redundant_raw_access),
1801
+ raw_after_cli_rate: metrics.raw_after_cli_sequence_rate ?? 0,
1802
+ redundant_raw_access_rate: metrics.redundant_raw_sequence_rate ?? 0,
1803
+ cli_state_command_counts: metrics.cli_state_command_counts ?? {},
1804
+ raw_artifact_access_after_cli_counts: metrics.raw_artifact_access_after_cli_counts ?? {},
1805
+ redundant_raw_artifact_access_counts: metrics.redundant_raw_artifact_access_counts ?? {},
1806
+ token_estimator_version: metrics.token_estimator_version ?? null,
1807
+ estimated_raw_after_cli_tokens: safeInt(metrics.estimated_raw_after_cli_tokens),
1808
+ estimated_redundant_raw_tokens: safeInt(metrics.estimated_redundant_raw_tokens),
1809
+ estimated_raw_after_cli_tokens_by_artifact: metrics.estimated_raw_after_cli_tokens_by_artifact ?? {},
1810
+ estimated_redundant_raw_tokens_by_artifact: metrics.estimated_redundant_raw_tokens_by_artifact ?? {},
1811
+ estimated_tokens_saved_vs_previous: metrics.estimated_tokens_saved_vs_previous ?? null,
1812
+ estimated_tokens_saved_vs_previous_null_reason: metrics.estimated_tokens_saved_vs_previous_null_reason ?? null,
1813
+ per_capability_state_counts: metrics.per_capability_state_counts ?? {},
1814
+ degradation_reason_counts: metrics.degradation_reason_counts ?? {},
1815
+ bounded_degradation_counts: {
1816
+ record_or_sequence: metrics.degradation_reason_counts ?? {},
1817
+ runtime_status: metrics.runtime_status_counts ?? {},
1818
+ },
1819
+ startup_recommendation_action: recommendation.action ?? null,
1820
+ };
1821
+ }
1822
+ function temporaryPeerPath(p) {
1823
+ const ts = new Date().toISOString().replace(/[-:.TZ]/g, "");
1824
+ const dir = path.dirname(p);
1825
+ const base = path.basename(p);
1826
+ return path.join(dir, `.${base}.${process.pid}.${ts}.tmp`);
1827
+ }
1828
+ export function persistStartupBenchmark(metrics, benchmarkDir, scope = null) {
1829
+ const resolvedDir = path.resolve(benchmarkDir);
1830
+ if (!path.isAbsolute(resolvedDir)) {
1831
+ throw new Error("benchmark directory must be an absolute path");
1832
+ }
1833
+ const resolvedScope = runtimeScope(metrics, scope);
1834
+ const enriched = withEstimatedTokensSaved(metrics, resolvedDir, resolvedScope);
1835
+ const structuredText = pyJsonIndent(enriched) + "\n";
1836
+ const humanText = renderStartupReport(enriched);
1837
+ const rowText = pyJsonDumps(buildBenchmarkHistoryRow(enriched, resolvedScope)) + "\n";
1838
+ fs.mkdirSync(resolvedDir, { recursive: true });
1839
+ const historyPath = path.join(resolvedDir, BENCHMARK_HISTORY_JSONL);
1840
+ const jsonPath = path.join(resolvedDir, BENCHMARK_LATEST_REPORT_JSON);
1841
+ const markdownPath = path.join(resolvedDir, BENCHMARK_LATEST_REPORT_MARKDOWN);
1842
+ const jsonTmp = temporaryPeerPath(jsonPath);
1843
+ const markdownTmp = temporaryPeerPath(markdownPath);
1844
+ try {
1845
+ fs.writeFileSync(jsonTmp, structuredText);
1846
+ fs.writeFileSync(markdownTmp, humanText);
1847
+ fs.appendFileSync(historyPath, rowText);
1848
+ fs.renameSync(jsonTmp, jsonPath);
1849
+ fs.renameSync(markdownTmp, markdownPath);
1850
+ }
1851
+ catch (err) {
1852
+ for (const tmp of [jsonTmp, markdownTmp]) {
1853
+ try {
1854
+ fs.unlinkSync(tmp);
1855
+ }
1856
+ catch {
1857
+ /* ignore */
1858
+ }
1859
+ }
1860
+ throw err;
1861
+ }
1862
+ return { history: historyPath, structured: jsonPath, human_readable: markdownPath };
1863
+ }
1864
+ export function buildStartupIntermediate(corpus, opts) {
1865
+ const salt = opts.salt;
1866
+ const loaded = opts.contract ?? loadContract();
1867
+ let records = corpus && typeof corpus === "object" && !Array.isArray(corpus) ? (corpus.records ?? []) : [];
1868
+ if (!Array.isArray(records))
1869
+ records = [];
1870
+ let metadata = corpus && typeof corpus === "object" && !Array.isArray(corpus) ? (corpus.metadata ?? {}) : {};
1871
+ if (!metadata || typeof metadata !== "object" || Array.isArray(metadata))
1872
+ metadata = {};
1873
+ let runtimeStatuses = metadata.runtime_statuses;
1874
+ if (!Array.isArray(runtimeStatuses))
1875
+ runtimeStatuses = [];
1876
+ const boundary = parseTimestamp((loaded.boundary ?? {}).committed_at);
1877
+ const windowStartedAfter = opts.benchmarkWindowStartedAfter ?? boundary;
1878
+ const watermarkAt = opts.benchmarkWatermarkAt ?? maxRecordTimestamp(records, windowStartedAfter);
1879
+ const classified = classifyStartupRecords(corpus, { salt, contract: loaded });
1880
+ const sequences = classified.state_gathering_sequences;
1881
+ const degradations = classified.degradations;
1882
+ const runtimeCoverage = runtimeStatuses
1883
+ .filter((s) => s && typeof s === "object" && !Array.isArray(s))
1884
+ .map((s) => boundedRuntimeStatus(s));
1885
+ return {
1886
+ output_envelope: STARTUP_INTERMEDIATE_ENVELOPE,
1887
+ contract_version: loaded.version ?? null,
1888
+ boundary_source: (loaded.boundary ?? {}).source ?? null,
1889
+ boundary_commit: (loaded.boundary ?? {}).commit ?? null,
1890
+ boundary_committed_at: (loaded.boundary ?? {}).committed_at ?? null,
1891
+ benchmark_mode: opts.benchmarkMode || "full_boundary_snapshot",
1892
+ benchmark_previous_watermark_at: formatTimestamp(opts.benchmarkPreviousWatermarkAt ?? null),
1893
+ benchmark_window_started_after: formatTimestamp(windowStartedAfter),
1894
+ benchmark_watermark_at: formatTimestamp(watermarkAt),
1895
+ corpus_adapter_version: metadata.adapter_version ?? null,
1896
+ runtime_coverage: runtimeCoverage,
1897
+ runtime_record_counts: runtimeRecordCounts(records),
1898
+ total_records_read: records.length,
1899
+ total_state_sequences: sequences.length,
1900
+ artifact_label_counts: artifactLabelCounts(sequences),
1901
+ state_gathering_sequences: sequences,
1902
+ degradations,
1903
+ compatibility_note: "Section 22 corpus records are read-only; startup state data is emitted only in startup_state_analysis_v1.",
1904
+ };
1905
+ }
1906
+ export function buildNoRuntimeStartupIntermediate(opts = {}) {
1907
+ const loaded = opts.contract ?? loadContract();
1908
+ const boundary = parseTimestamp((loaded.boundary ?? {}).committed_at);
1909
+ const windowStartedAfter = opts.benchmarkPreviousWatermarkAt ?? boundary;
1910
+ return {
1911
+ output_envelope: STARTUP_INTERMEDIATE_ENVELOPE,
1912
+ contract_version: loaded.version ?? null,
1913
+ boundary_source: (loaded.boundary ?? {}).source ?? null,
1914
+ boundary_commit: (loaded.boundary ?? {}).commit ?? null,
1915
+ boundary_committed_at: (loaded.boundary ?? {}).committed_at ?? null,
1916
+ benchmark_mode: opts.benchmarkMode ?? "since_previous_benchmark",
1917
+ benchmark_previous_watermark_at: formatTimestamp(opts.benchmarkPreviousWatermarkAt ?? null),
1918
+ benchmark_window_started_after: formatTimestamp(windowStartedAfter),
1919
+ benchmark_watermark_at: formatTimestamp(opts.benchmarkPreviousWatermarkAt ?? null),
1920
+ corpus_adapter_version: null,
1921
+ runtime_coverage: [{ runtime: "none", status: "skipped", reason: "no_runtime_stores_approved", record_count: 0 }],
1922
+ runtime_record_counts: {},
1923
+ total_records_read: 0,
1924
+ total_state_sequences: 0,
1925
+ artifact_label_counts: {},
1926
+ state_gathering_sequences: [],
1927
+ degradations: [],
1928
+ compatibility_note: "No runtime stores were approved, so no local runtime history was read.",
1929
+ };
1930
+ }
1931
+ export function extractStartupIntermediateFromCorpusFile(corpusPath, opts) {
1932
+ let corpus;
1933
+ try {
1934
+ corpus = JSON.parse(fs.readFileSync(corpusPath, "utf8"));
1935
+ }
1936
+ catch {
1937
+ corpus = {
1938
+ metadata: {
1939
+ runtime_statuses: [
1940
+ { runtime: "local-corpus", status: "degraded", reason: "schema_divergent", error_count: 1 },
1941
+ ],
1942
+ },
1943
+ records: [],
1944
+ };
1945
+ }
1946
+ const intermediate = buildStartupIntermediate(corpus, { salt: opts.salt, contract: opts.contract ?? null });
1947
+ if (opts.outputPath) {
1948
+ fs.mkdirSync(path.dirname(opts.outputPath), { recursive: true });
1949
+ fs.writeFileSync(opts.outputPath, pyJsonIndent(intermediate) + "\n");
1950
+ }
1951
+ return intermediate;
1952
+ }
1953
+ //# sourceMappingURL=startupAnalysis.js.map