blun-king-cli 9.1.567 → 9.1.570

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 (263) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/agent-spine-plugin/.claude-plugin/marketplace.json +1 -1
  3. package/agent-spine-plugin/.claude-plugin/plugin.json +1 -1
  4. package/agent-spine-plugin/.codex-plugin/plugin.json +2 -1
  5. package/agent-spine-plugin/CHANGELOG.md +1581 -0
  6. package/agent-spine-plugin/README.md +30 -4
  7. package/agent-spine-plugin/blun.plugin.json +3 -3
  8. package/agent-spine-plugin/docs/acceptance.md +61 -0
  9. package/agent-spine-plugin/docs/assignment-continuation.md +48 -0
  10. package/agent-spine-plugin/docs/host-integration.md +178 -0
  11. package/agent-spine-plugin/docs/preflight-recall.md +69 -0
  12. package/agent-spine-plugin/docs/preservation-contract.md +53 -0
  13. package/agent-spine-plugin/docs/quality-gates.md +50 -0
  14. package/agent-spine-plugin/docs/releasing.md +85 -0
  15. package/agent-spine-plugin/docs/session-timeline.md +251 -0
  16. package/agent-spine-plugin/docs/source-roots.md +113 -0
  17. package/agent-spine-plugin/docs/structured-completion.md +67 -0
  18. package/agent-spine-plugin/docs/world-model.md +94 -0
  19. package/agent-spine-plugin/hooks/codex.json +2 -2
  20. package/agent-spine-plugin/hooks/hooks.json +1 -1
  21. package/agent-spine-plugin/hooks/version.json +1 -1
  22. package/agent-spine-plugin/package.json +13 -3
  23. package/agent-spine-plugin/scripts/check-codex-install.js +226 -0
  24. package/agent-spine-plugin/scripts/check-hosts.js +14 -5
  25. package/agent-spine-plugin/scripts/check-install-hook.js +226 -0
  26. package/agent-spine-plugin/scripts/check-install-selfstarter.js +154 -0
  27. package/agent-spine-plugin/scripts/check-install.js +478 -0
  28. package/agent-spine-plugin/scripts/check-line-budget.js +58 -0
  29. package/agent-spine-plugin/scripts/check-syntax.js +29 -0
  30. package/agent-spine-plugin/scripts/github-actions.js +11 -0
  31. package/agent-spine-plugin/scripts/hermetic-process.js +183 -0
  32. package/agent-spine-plugin/scripts/release-check.js +145 -0
  33. package/agent-spine-plugin/scripts/run-acceptance.js +19 -0
  34. package/agent-spine-plugin/scripts/run-checks.js +47 -0
  35. package/agent-spine-plugin/scripts/run-tests-hermetic.js +89 -0
  36. package/agent-spine-plugin/skills/agent-spine/SKILL.md +16 -2
  37. package/agent-spine-plugin/spine-example/1-identity.md +12 -0
  38. package/agent-spine-plugin/spine-example/2-voice.md +6 -0
  39. package/agent-spine-plugin/spine-example/3-conduct.md +8 -0
  40. package/agent-spine-plugin/spine-example/4-history.md +4 -0
  41. package/agent-spine-plugin/src/cli-agent.js +296 -0
  42. package/agent-spine-plugin/src/cli-attention.js +95 -0
  43. package/agent-spine-plugin/src/cli-autonomy.js +36 -0
  44. package/agent-spine-plugin/src/cli-common.js +71 -0
  45. package/agent-spine-plugin/src/cli-continuity.js +116 -0
  46. package/agent-spine-plugin/src/cli-core.js +128 -0
  47. package/agent-spine-plugin/src/cli-diagnostics.js +291 -0
  48. package/agent-spine-plugin/src/cli-host.js +21 -0
  49. package/agent-spine-plugin/src/cli-learning.js +305 -0
  50. package/agent-spine-plugin/src/cli-premortem.js +16 -0
  51. package/agent-spine-plugin/src/cli-sharing.js +230 -0
  52. package/agent-spine-plugin/src/cli.js +40 -1350
  53. package/agent-spine-plugin/src/codex-reader-launcher.js +182 -0
  54. package/agent-spine-plugin/src/hook.js +185 -567
  55. package/agent-spine-plugin/src/index.js +16 -2
  56. package/agent-spine-plugin/src/lib/acceptance.js +113 -11
  57. package/agent-spine-plugin/src/lib/action-lesson-recall.js +53 -0
  58. package/agent-spine-plugin/src/lib/attention-context.js +167 -0
  59. package/agent-spine-plugin/src/lib/attention-events.js +113 -0
  60. package/agent-spine-plugin/src/lib/attention-privacy.js +72 -0
  61. package/agent-spine-plugin/src/lib/attention-schema.js +164 -0
  62. package/agent-spine-plugin/src/lib/attention-storage.js +93 -0
  63. package/agent-spine-plugin/src/lib/attention.js +9 -600
  64. package/agent-spine-plugin/src/lib/audit-premortem.js +223 -0
  65. package/agent-spine-plugin/src/lib/audit.js +56 -6
  66. package/agent-spine-plugin/src/lib/autonomy-policy.js +110 -0
  67. package/agent-spine-plugin/src/lib/autonomy-store.js +202 -0
  68. package/agent-spine-plugin/src/lib/autonomy.js +8 -0
  69. package/agent-spine-plugin/src/lib/briefing.js +67 -4
  70. package/agent-spine-plugin/src/lib/catalog-document-read.js +51 -0
  71. package/agent-spine-plugin/src/lib/catalog.js +1 -1
  72. package/agent-spine-plugin/src/lib/codex-installation.js +231 -0
  73. package/agent-spine-plugin/src/lib/codex-skill-installation.js +211 -0
  74. package/agent-spine-plugin/src/lib/context.js +3 -1
  75. package/agent-spine-plugin/src/lib/delivery-agent-usage.js +224 -0
  76. package/agent-spine-plugin/src/lib/delivery-assignment.js +220 -0
  77. package/agent-spine-plugin/src/lib/delivery-command-actions.js +453 -0
  78. package/agent-spine-plugin/src/lib/delivery-knowledge.js +78 -0
  79. package/agent-spine-plugin/src/lib/delivery-premortem-binding.js +107 -0
  80. package/agent-spine-plugin/src/lib/delivery-premortem-closure.js +171 -0
  81. package/agent-spine-plugin/src/lib/delivery-premortem-codec.js +45 -0
  82. package/agent-spine-plugin/src/lib/delivery-premortem-correction.js +101 -0
  83. package/agent-spine-plugin/src/lib/delivery-premortem-file.js +21 -0
  84. package/agent-spine-plugin/src/lib/delivery-premortem-index.js +493 -0
  85. package/agent-spine-plugin/src/lib/delivery-premortem-inspection.js +37 -0
  86. package/agent-spine-plugin/src/lib/delivery-premortem-recovery.js +120 -0
  87. package/agent-spine-plugin/src/lib/delivery-premortem-rejection.js +65 -0
  88. package/agent-spine-plugin/src/lib/delivery-premortem-results.js +28 -0
  89. package/agent-spine-plugin/src/lib/delivery-premortem-session-guard.js +46 -0
  90. package/agent-spine-plugin/src/lib/delivery-premortem-write-ledger.js +285 -0
  91. package/agent-spine-plugin/src/lib/delivery-premortem.js +499 -0
  92. package/agent-spine-plugin/src/lib/delivery-shell-heredoc.js +115 -0
  93. package/agent-spine-plugin/src/lib/delivery-shell-substitutions.js +111 -0
  94. package/agent-spine-plugin/src/lib/delivery-shell-wrapper.js +64 -0
  95. package/agent-spine-plugin/src/lib/delivery-target.js +73 -0
  96. package/agent-spine-plugin/src/lib/delivery-verification.js +445 -0
  97. package/agent-spine-plugin/src/lib/documents.js +27 -5
  98. package/agent-spine-plugin/src/lib/filesystem-retry.js +2 -0
  99. package/agent-spine-plugin/src/lib/gateway-common.js +68 -0
  100. package/agent-spine-plugin/src/lib/gateway-control.js +302 -0
  101. package/agent-spine-plugin/src/lib/gateway-delivery.js +103 -0
  102. package/agent-spine-plugin/src/lib/gateway-execution.js +350 -0
  103. package/agent-spine-plugin/src/lib/gateway-host-lifecycle.js +185 -0
  104. package/agent-spine-plugin/src/lib/gateway-inspection.js +85 -0
  105. package/agent-spine-plugin/src/lib/gateway-knowledge.js +129 -0
  106. package/agent-spine-plugin/src/lib/gateway-policy-provenance.js +197 -0
  107. package/agent-spine-plugin/src/lib/gateway-premortem-disposition.js +82 -0
  108. package/agent-spine-plugin/src/lib/gateway-premortem.js +363 -0
  109. package/agent-spine-plugin/src/lib/gateway-runs.js +300 -0
  110. package/agent-spine-plugin/src/lib/gateway-runtime-identity.js +31 -0
  111. package/agent-spine-plugin/src/lib/gateway-runtime-records.js +21 -0
  112. package/agent-spine-plugin/src/lib/gateway-runtime.js +18 -1623
  113. package/agent-spine-plugin/src/lib/gateway-state-transaction.js +356 -0
  114. package/agent-spine-plugin/src/lib/gateway-state.js +342 -0
  115. package/agent-spine-plugin/src/lib/hook-artifact-guards.js +356 -0
  116. package/agent-spine-plugin/src/lib/hook-audit.js +16 -2
  117. package/agent-spine-plugin/src/lib/hook-briefing-use.js +68 -0
  118. package/agent-spine-plugin/src/lib/hook-context.js +424 -0
  119. package/agent-spine-plugin/src/lib/hook-final-message.js +26 -0
  120. package/agent-spine-plugin/src/lib/hook-input.js +27 -0
  121. package/agent-spine-plugin/src/lib/hook-output.js +140 -0
  122. package/agent-spine-plugin/src/lib/hook-premortem.js +257 -0
  123. package/agent-spine-plugin/src/lib/hook-process-advisory.js +37 -0
  124. package/agent-spine-plugin/src/lib/hook-protection.js +95 -0
  125. package/agent-spine-plugin/src/lib/hook-stop-verification.js +84 -0
  126. package/agent-spine-plugin/src/lib/hook-timeline.js +91 -0
  127. package/agent-spine-plugin/src/lib/identifier-analysis.js +446 -0
  128. package/agent-spine-plugin/src/lib/indexed-memory.js +23 -6
  129. package/agent-spine-plugin/src/lib/knowledge-evidence.js +431 -0
  130. package/agent-spine-plugin/src/lib/learning-applications.js +441 -0
  131. package/agent-spine-plugin/src/lib/learning-candidates.js +274 -0
  132. package/agent-spine-plugin/src/lib/learning-context.js +130 -0
  133. package/agent-spine-plugin/src/lib/learning-delivery-contracts.js +310 -0
  134. package/agent-spine-plugin/src/lib/learning-evaluation-contracts.js +350 -0
  135. package/agent-spine-plugin/src/lib/learning-evaluation-registration.js +372 -0
  136. package/agent-spine-plugin/src/lib/learning-evaluation-revocation.js +292 -0
  137. package/agent-spine-plugin/src/lib/learning-evidence-contracts.js +321 -0
  138. package/agent-spine-plugin/src/lib/learning-findings.js +458 -0
  139. package/agent-spine-plugin/src/lib/learning-measurement-contracts.js +285 -0
  140. package/agent-spine-plugin/src/lib/learning-measurements.js +265 -0
  141. package/agent-spine-plugin/src/lib/learning-outcome-contracts.js +165 -0
  142. package/agent-spine-plugin/src/lib/learning-outcomes.js +343 -0
  143. package/agent-spine-plugin/src/lib/learning-reconciliation.js +290 -0
  144. package/agent-spine-plugin/src/lib/learning-retry-contracts.js +188 -0
  145. package/agent-spine-plugin/src/lib/learning-schema.js +229 -0
  146. package/agent-spine-plugin/src/lib/learning-scope-targets.js +424 -0
  147. package/agent-spine-plugin/src/lib/learning-state-upgrade.js +477 -0
  148. package/agent-spine-plugin/src/lib/learning-status-configuration.js +476 -0
  149. package/agent-spine-plugin/src/lib/learning-storage.js +203 -0
  150. package/agent-spine-plugin/src/lib/learning-trial-recovery.js +218 -0
  151. package/agent-spine-plugin/src/lib/learning-validation-contracts.js +375 -0
  152. package/agent-spine-plugin/src/lib/learning-validation-renewal.js +298 -0
  153. package/agent-spine-plugin/src/lib/learning-validation-runtime.js +234 -0
  154. package/agent-spine-plugin/src/lib/learning.js +36 -6923
  155. package/agent-spine-plugin/src/lib/lesson-recall-session.js +172 -0
  156. package/agent-spine-plugin/src/lib/mcp-autonomy-tools.js +37 -0
  157. package/agent-spine-plugin/src/lib/mcp-delivery-completion.js +124 -0
  158. package/agent-spine-plugin/src/lib/mcp-delivery-tools.js +45 -0
  159. package/agent-spine-plugin/src/lib/mcp-premortem.js +68 -0
  160. package/agent-spine-plugin/src/lib/mcp-runtime.js +269 -0
  161. package/agent-spine-plugin/src/lib/mcp-source-context.js +51 -0
  162. package/agent-spine-plugin/src/lib/mcp-timeline-tools.js +164 -0
  163. package/agent-spine-plugin/src/lib/mcp-world-tools.js +52 -0
  164. package/agent-spine-plugin/src/lib/owned-file-lock.js +30 -10
  165. package/agent-spine-plugin/src/lib/project-portfolio.js +176 -0
  166. package/agent-spine-plugin/src/lib/selfstarter-core.js +382 -0
  167. package/agent-spine-plugin/src/lib/selfstarter-jobs.js +149 -0
  168. package/agent-spine-plugin/src/lib/selfstarter-lease.js +204 -0
  169. package/agent-spine-plugin/src/lib/selfstarter-policy.js +90 -0
  170. package/agent-spine-plugin/src/lib/selfstarter-workspace.js +111 -0
  171. package/agent-spine-plugin/src/lib/selfstarter.js +11 -911
  172. package/agent-spine-plugin/src/lib/session-timeline-auth.js +316 -0
  173. package/agent-spine-plugin/src/lib/session-timeline-codex.js +58 -0
  174. package/agent-spine-plugin/src/lib/session-timeline-contract.js +48 -0
  175. package/agent-spine-plugin/src/lib/session-timeline-enrollment-source.js +41 -0
  176. package/agent-spine-plugin/src/lib/session-timeline-enrollment-storage.js +132 -0
  177. package/agent-spine-plugin/src/lib/session-timeline-enrollment-transport.js +17 -0
  178. package/agent-spine-plugin/src/lib/session-timeline-enrollment.js +500 -0
  179. package/agent-spine-plugin/src/lib/session-timeline-event-extract.js +157 -0
  180. package/agent-spine-plugin/src/lib/session-timeline-host-origin.js +74 -0
  181. package/agent-spine-plugin/src/lib/session-timeline-host-receipt.js +117 -0
  182. package/agent-spine-plugin/src/lib/session-timeline-invocation.js +201 -0
  183. package/agent-spine-plugin/src/lib/session-timeline-king.js +79 -0
  184. package/agent-spine-plugin/src/lib/session-timeline-prior.js +59 -0
  185. package/agent-spine-plugin/src/lib/session-timeline-provider.js +34 -0
  186. package/agent-spine-plugin/src/lib/session-timeline-query.js +55 -0
  187. package/agent-spine-plugin/src/lib/session-timeline-results.js +31 -0
  188. package/agent-spine-plugin/src/lib/session-timeline-root.js +11 -0
  189. package/agent-spine-plugin/src/lib/session-timeline-search.js +82 -0
  190. package/agent-spine-plugin/src/lib/session-timeline-sid-acl.js +217 -0
  191. package/agent-spine-plugin/src/lib/session-timeline-source.js +83 -0
  192. package/agent-spine-plugin/src/lib/session-timeline-state.js +45 -0
  193. package/agent-spine-plugin/src/lib/session-timeline-transport.js +50 -0
  194. package/agent-spine-plugin/src/lib/session-timeline-windows-acl.js +148 -0
  195. package/agent-spine-plugin/src/lib/session-timeline.js +453 -0
  196. package/agent-spine-plugin/src/lib/source-roots.js +69 -136
  197. package/agent-spine-plugin/src/lib/source-tree-scan.js +178 -0
  198. package/agent-spine-plugin/src/lib/task-knowledge-context.js +78 -0
  199. package/agent-spine-plugin/src/lib/timeline-tool-guard.js +202 -0
  200. package/agent-spine-plugin/src/lib/world-knowledge.js +249 -0
  201. package/agent-spine-plugin/src/lib/world-model.js +278 -0
  202. package/agent-spine-plugin/src/mcp.js +20 -161
  203. package/agent-spine-plugin/src/version.js +1 -1
  204. package/agent-spine-plugin/src/worker.js +22 -5
  205. package/bin/agent-resume-snapshot.cjs +2 -2
  206. package/bin/agentspine-king-goal-inbox.mjs +111 -0
  207. package/bin/agentspine-king-goal-intake.mjs +106 -0
  208. package/bin/baseline-skill-performance-policy.cjs +1 -16
  209. package/bin/core-bootstrap.js +3 -0
  210. package/bin/curiosity-scout-policy.cjs +5 -1
  211. package/bin/input-draft-persistence.cjs +2 -2
  212. package/bin/king-tui-function-contract.json +33 -0
  213. package/bin/launcher-restart-policy.cjs +150 -0
  214. package/bin/launcher-runtime.js +56 -44
  215. package/bin/managed-context-startup-policy.cjs +27 -0
  216. package/bin/managed-plugin-selection.cjs +116 -0
  217. package/bin/mistake-relevance-policy.cjs +1 -1
  218. package/bin/observer-hooks.cjs +14 -0
  219. package/bin/oversized-context-offload-policy.cjs +86 -0
  220. package/bin/plugin-bootstrap.js +7 -40
  221. package/bin/proactive-compaction-policy.cjs +1 -1
  222. package/bin/provider-model-refresh-deadline.cjs +53 -0
  223. package/bin/provider-model-refresh-policy.cjs +107 -0
  224. package/bin/release-artifact-freeze-policy.cjs +30 -0
  225. package/bin/repeated-user-message-projection.cjs +3 -126
  226. package/bin/research-page-result.cjs +74 -0
  227. package/bin/runtime-exit-ledger.cjs +1 -0
  228. package/bin/session-compaction-policy.cjs +84 -0
  229. package/bin/skill-listing-performance-policy.cjs +2 -2
  230. package/bin/standard-tools-bootstrap.js +0 -37
  231. package/bin/subagent-skill-policy.cjs +3 -1
  232. package/bin/telegram-approval-relay.cjs +12 -7
  233. package/bin/telegram-private-conversation-policy.cjs +3 -2
  234. package/bin/telegram-queue-handoff-policy.cjs +24 -0
  235. package/bin/thinking-activity-status-policy.cjs +1 -1
  236. package/bin/thinking-only-guard.cjs +16 -12
  237. package/bin/tool-call-loop-policy.cjs +0 -2
  238. package/bin/tool-result-offload-policy.cjs +11 -2
  239. package/bin/tui-functional-contract.cjs +55 -0
  240. package/bin/update-notice.js +18 -14
  241. package/bin/user-message-offload-policy.cjs +1 -1
  242. package/bin/user-prompt-hook-origin-policy.cjs +34 -0
  243. package/bin/windows-node-crash-dump.cjs +110 -0
  244. package/blun.mjs +2861 -1173
  245. package/codebase-index/codebase_index.py +4 -3
  246. package/package.json +5 -18
  247. package/standard-skills/research-evidence/SKILL.md +39 -0
  248. package/standard-skills/research-evidence/references/evidence-format.md +82 -0
  249. package/standard-skills/research-evidence/scripts/evidence-collection.cjs +254 -0
  250. package/standard-skills/research-evidence/scripts/score-report.cjs +112 -0
  251. package/standard-skills/web-lesen/SKILL.md +37 -22
  252. package/standard-skills/web-lesen/scripts/crawl_public.py +376 -0
  253. package/telegram-plugin/DELIVERY.md +36 -0
  254. package/telegram-plugin/bin/telegram-approval-relay.cjs +13 -7
  255. package/telegram-plugin/bin/telegram-launcher-status-queue.cjs +122 -0
  256. package/telegram-plugin/bin/telegram-private-conversation-policy.cjs +3 -2
  257. package/telegram-plugin/bin/telegram-reply-parts.cjs +149 -0
  258. package/telegram-plugin/dist/bridge.mjs +7 -56
  259. package/telegram-plugin/dist/mcp-server.mjs +33 -4
  260. package/agent-spine-plugin/skill/SKILL.md +0 -76
  261. package/bin/mnemo-connect-heartbeat.cjs +0 -204
  262. package/bin/mnemo-tool-agent-policy.cjs +0 -22
  263. package/telegram-plugin/bin/telegram-mnemo-capture.cjs +0 -297
@@ -0,0 +1,431 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ export const KNOWLEDGE_GAP_SCHEMA = "agentspine.knowledge-gap/v1";
4
+ export const SELF_HELP_REPORT_SCHEMA = "agentspine.self-help-report/v1";
5
+ export const SELF_HELP_REQUIREMENT_SCHEMA = "agentspine.self-help-requirement/v1";
6
+ export const SELF_HELP_ESCALATION_SCHEMA = "agentspine.self-help-escalation/v1";
7
+
8
+ const ID_RE = /^[A-Za-z0-9][A-Za-z0-9:_.@/+~-]{0,255}$/;
9
+ const DIGEST_RE = /^[a-f0-9]{64}$/;
10
+ const COMMIT_RE = /^[a-f0-9]{40}$/;
11
+ const KNOWLEDGE_EVIDENCE = new Set(["owner-input", "objective-observation"]);
12
+ const REPOSITORY_KINDS = new Set(["current-tree", "documentation", "test", "history", "issue", "pull-request"]);
13
+ const EXTERNAL_KINDS = new Set(["official-documentation", "standard", "research-paper", "public-repository"]);
14
+ const SECRET_RE = /-----BEGIN [A-Z ]*PRIVATE KEY-----|\b(?:sk|gh[opusu])_[A-Za-z0-9_-]{20,}\b|\bBearer\s+[A-Za-z0-9._~+/-]{20,}|\b(?:api[-_ ]?key|token|password|secret)\s*[:=]\s*\S{8,}/i;
15
+ const AUTHORITY_RE = /\b(?:permission|rights?|roles?|owner|trusted|delegat|authorized|approval|production|payment|spending|tool capability|send capability)\b/i;
16
+
17
+ function sha256(value) { return createHash("sha256").update(value).digest("hex"); }
18
+
19
+ function exactId(value, field) {
20
+ if (typeof value !== "string" || !ID_RE.test(value) || value.includes("*")) {
21
+ throw new Error(field + " must be an exact stable ID without wildcards");
22
+ }
23
+ return value;
24
+ }
25
+
26
+ function safeText(value, field, maximum) {
27
+ if (typeof value !== "string" || !value.trim()) throw new Error(field + " is required");
28
+ const text = value.trim().replace(/\s+/g, " ").slice(0, maximum);
29
+ if (SECRET_RE.test(text)) throw new Error(field + " appears to contain a secret");
30
+ return text;
31
+ }
32
+
33
+ function safeKnowledgeText(value, field, maximum) {
34
+ const text = safeText(value, field, maximum);
35
+ if (AUTHORITY_RE.test(text)) throw new Error(field + " cannot grant or describe authority");
36
+ return text;
37
+ }
38
+
39
+ function timestamp(value, field = "timestamp") {
40
+ const date = value instanceof Date ? value : new Date(value);
41
+ if (!Number.isFinite(date.getTime())) throw new Error(field + " is invalid");
42
+ return date.toISOString();
43
+ }
44
+
45
+ function knowledgeGapSubjectMaterial(gap) {
46
+ return {
47
+ goalId: gap.goalId, goalStepId: gap.goalStepId, planDefinitionsDigest: gap.planDefinitionsDigest,
48
+ question: gap.question, reason: gap.reason, requiredEvidence: gap.requiredEvidence,
49
+ authority: "context-only"
50
+ };
51
+ }
52
+
53
+ function knowledgeGapRequestMaterial(gap) {
54
+ return {
55
+ subjectDigest: gap.subjectDigest, requestedAt: gap.requestedAt,
56
+ requestedByQueueId: gap.requestedByQueueId, authority: "context-only"
57
+ };
58
+ }
59
+
60
+ function knowledgeGapResolutionMaterial(gap) {
61
+ return {
62
+ requestDigest: gap.requestDigest, answer: gap.answer, answerSource: gap.answerSource,
63
+ sourceDigest: gap.sourceDigest, resolvedAt: gap.resolvedAt,
64
+ resolvedBySubjectId: gap.resolvedBySubjectId, authority: "context-only"
65
+ };
66
+ }
67
+
68
+ function selfHelpRequirementMaterial(requirement) {
69
+ return { goalId: requirement.goalId, goalStepId: requirement.goalStepId,
70
+ planDefinitionsDigest: requirement.planDefinitionsDigest, question: requirement.question,
71
+ reason: requirement.reason, requestedAt: requirement.requestedAt,
72
+ requestedByQueueId: requirement.requestedByQueueId, authority: "context-only-research" };
73
+ }
74
+
75
+ export function validKnowledgeGap(gap) {
76
+ if (!(gap && gap.schema === KNOWLEDGE_GAP_SCHEMA && ID_RE.test(gap.gapId || "")
77
+ && ID_RE.test(gap.goalId || "") && ID_RE.test(gap.goalStepId || "")
78
+ && DIGEST_RE.test(gap.planDefinitionsDigest || "")
79
+ && typeof gap.question === "string" && gap.question.length <= 500
80
+ && typeof gap.reason === "string" && gap.reason.length <= 500
81
+ && KNOWLEDGE_EVIDENCE.has(gap.requiredEvidence)
82
+ && ID_RE.test(gap.requestedByQueueId || "")
83
+ && Number.isFinite(new Date(gap.requestedAt).getTime())
84
+ && DIGEST_RE.test(gap.subjectDigest || "") && DIGEST_RE.test(gap.requestDigest || "")
85
+ && ["open", "resolved"].includes(gap.status) && gap.authority === "context-only")) return false;
86
+ try {
87
+ if (safeKnowledgeText(gap.question, "knowledgeGap.question", 500) !== gap.question
88
+ || safeKnowledgeText(gap.reason, "knowledgeGap.reason", 500) !== gap.reason) return false;
89
+ } catch { return false; }
90
+ if (gap.subjectDigest !== sha256(JSON.stringify(knowledgeGapSubjectMaterial(gap)))
91
+ || gap.gapId !== "knowledge-gap:" + gap.subjectDigest.slice(0, 32)
92
+ || gap.requestDigest !== sha256(JSON.stringify(knowledgeGapRequestMaterial(gap)))) return false;
93
+ if (gap.status === "open") return gap.answer === null && gap.answerSource === null && gap.sourceDigest === null
94
+ && gap.resolvedAt === null && gap.resolvedBySubjectId === null && gap.resolutionDigest === null;
95
+ if (!(typeof gap.answer === "string" && gap.answer.length <= 2000
96
+ && gap.answerSource === gap.requiredEvidence
97
+ && (gap.sourceDigest === null || DIGEST_RE.test(gap.sourceDigest))
98
+ && Number.isFinite(new Date(gap.resolvedAt).getTime()) && ID_RE.test(gap.resolvedBySubjectId || "")
99
+ && DIGEST_RE.test(gap.resolutionDigest || ""))) return false;
100
+ if (new Date(gap.resolvedAt) < new Date(gap.requestedAt)) return false;
101
+ if ((gap.answerSource === "objective-observation") !== (gap.sourceDigest !== null)) return false;
102
+ try { if (safeKnowledgeText(gap.answer, "knowledgeGap.answer", 2000) !== gap.answer) return false; } catch { return false; }
103
+ return gap.resolutionDigest === sha256(JSON.stringify(knowledgeGapResolutionMaterial(gap)));
104
+ }
105
+
106
+ export function createKnowledgeGap(goal, step, queueId, request, now) {
107
+ const gap = {
108
+ schema: KNOWLEDGE_GAP_SCHEMA, gapId: null, goalId: goal.goalId, goalStepId: step.stepId,
109
+ planDefinitionsDigest: goal.plan.definitionsDigest,
110
+ question: safeKnowledgeText(request?.question, "knowledgeGap.question", 500),
111
+ reason: safeKnowledgeText(request?.reason, "knowledgeGap.reason", 500),
112
+ requiredEvidence: request?.requiredEvidence,
113
+ requestedAt: timestamp(now), requestedByQueueId: exactId(queueId, "requestedByQueueId"),
114
+ subjectDigest: null, requestDigest: null, status: "open", answer: null, answerSource: null,
115
+ sourceDigest: null, resolvedAt: null, resolvedBySubjectId: null, resolutionDigest: null,
116
+ authority: "context-only"
117
+ };
118
+ if (!KNOWLEDGE_EVIDENCE.has(gap.requiredEvidence)) {
119
+ throw new Error("knowledgeGap.requiredEvidence must be owner-input or objective-observation");
120
+ }
121
+ gap.subjectDigest = sha256(JSON.stringify(knowledgeGapSubjectMaterial(gap)));
122
+ gap.gapId = "knowledge-gap:" + gap.subjectDigest.slice(0, 32);
123
+ gap.requestDigest = sha256(JSON.stringify(knowledgeGapRequestMaterial(gap)));
124
+ if (!validKnowledgeGap(gap)) throw new Error("knowledge gap request is invalid");
125
+ return gap;
126
+ }
127
+
128
+ export function resolveKnowledgeGapCandidate(gap, { answer, answerSource, sourceDigest = null,
129
+ resolvedAt, resolvedBySubjectId }) {
130
+ answer = safeKnowledgeText(answer, "answer", 2000);
131
+ if (!KNOWLEDGE_EVIDENCE.has(answerSource) || answerSource !== gap.requiredEvidence) {
132
+ throw new Error("answer source does not satisfy the requested evidence class");
133
+ }
134
+ sourceDigest = sourceDigest === null || sourceDigest === undefined || sourceDigest === "" ? null : String(sourceDigest);
135
+ if ((answerSource === "objective-observation") !== DIGEST_RE.test(sourceDigest || "")) {
136
+ throw new Error("objective observation answers require one exact SHA-256 source digest");
137
+ }
138
+ const candidate = { ...gap, status: "resolved", answer, answerSource, sourceDigest,
139
+ resolvedAt: timestamp(resolvedAt), resolvedBySubjectId: exactId(resolvedBySubjectId, "resolvedBySubjectId"),
140
+ resolutionDigest: null };
141
+ candidate.resolutionDigest = sha256(JSON.stringify(knowledgeGapResolutionMaterial(candidate)));
142
+ if (!validKnowledgeGap(candidate)) throw new Error("knowledge gap resolution is invalid");
143
+ return candidate;
144
+ }
145
+
146
+ export function sameKnowledgeGapResolution(gap, resolution) {
147
+ const repeated = resolveKnowledgeGapCandidate(gap, { ...resolution,
148
+ resolvedAt: gap.resolvedAt, resolvedBySubjectId: gap.resolvedBySubjectId });
149
+ return gap.resolutionDigest === repeated.resolutionDigest;
150
+ }
151
+
152
+ export function createSelfHelpRequirement(goal, step, queueId, request, now) {
153
+ if (request?.requiredEvidence !== "objective-observation") {
154
+ throw new Error("self-help requirements only replace objective-observation questions");
155
+ }
156
+ const result = { schema: SELF_HELP_REQUIREMENT_SCHEMA, requirementId: null,
157
+ goalId: exactId(goal.goalId, "goalId"), goalStepId: exactId(step.stepId, "goalStepId"),
158
+ planDefinitionsDigest: goal.plan.definitionsDigest,
159
+ question: safeKnowledgeText(request?.question, "knowledgeGap.question", 500),
160
+ reason: safeKnowledgeText(request?.reason, "knowledgeGap.reason", 500),
161
+ requestedAt: timestamp(now), requestedByQueueId: exactId(queueId, "requestedByQueueId"),
162
+ requirementDigest: null, authority: "context-only-research" };
163
+ result.requirementDigest = sha256(JSON.stringify(selfHelpRequirementMaterial(result)));
164
+ result.requirementId = "self-help-requirement:" + result.requirementDigest.slice(0, 32);
165
+ if (!validSelfHelpRequirement(result)) throw new Error("self-help requirement is invalid");
166
+ return result;
167
+ }
168
+
169
+ export function validSelfHelpRequirement(requirement) {
170
+ if (!(requirement && requirement.schema === SELF_HELP_REQUIREMENT_SCHEMA
171
+ && ID_RE.test(requirement.requirementId || "") && ID_RE.test(requirement.goalId || "")
172
+ && ID_RE.test(requirement.goalStepId || "") && DIGEST_RE.test(requirement.planDefinitionsDigest || "")
173
+ && ID_RE.test(requirement.requestedByQueueId || "")
174
+ && Number.isFinite(new Date(requirement.requestedAt).getTime())
175
+ && DIGEST_RE.test(requirement.requirementDigest || "")
176
+ && requirement.authority === "context-only-research")) return false;
177
+ try {
178
+ if (safeKnowledgeText(requirement.question, "knowledgeGap.question", 500) !== requirement.question
179
+ || safeKnowledgeText(requirement.reason, "knowledgeGap.reason", 500) !== requirement.reason) return false;
180
+ } catch { return false; }
181
+ return requirement.requirementDigest === sha256(JSON.stringify(selfHelpRequirementMaterial(requirement)))
182
+ && requirement.requirementId === "self-help-requirement:" + requirement.requirementDigest.slice(0, 32);
183
+ }
184
+
185
+ export function pendingSelfHelpRequirement(step) {
186
+ const reports = step.selfHelpReports || [];
187
+ return [...(step.selfHelpRequirements || [])].reverse().find((requirement) =>
188
+ !reports.some((report) => report.question === requirement.question && report.reason === requirement.reason)) || null;
189
+ }
190
+
191
+ export function validStepKnowledgeState(step, planDefinitionsDigest) {
192
+ const gaps = step.knowledgeGaps;
193
+ const reports = step.selfHelpReports;
194
+ const requirements = step.selfHelpRequirements;
195
+ return (gaps === undefined || (Array.isArray(gaps) && gaps.length <= 16
196
+ && new Set(gaps.map((gap) => gap.gapId)).size === gaps.length
197
+ && gaps.every((gap) => validKnowledgeGap(gap) && gap.goalStepId === step.stepId
198
+ && gap.planDefinitionsDigest === planDefinitionsDigest)))
199
+ && (reports === undefined || (Array.isArray(reports) && reports.length <= 16
200
+ && new Set(reports.map((report) => report.reportId)).size === reports.length
201
+ && reports.every((report) => validSelfHelpReport(report) && report.goalStepId === step.stepId
202
+ && report.planDefinitionsDigest === planDefinitionsDigest)))
203
+ && (requirements === undefined || (Array.isArray(requirements) && requirements.length <= 16
204
+ && new Set(requirements.map((item) => item.requirementId)).size === requirements.length
205
+ && requirements.every((item) => validSelfHelpRequirement(item) && item.goalStepId === step.stepId
206
+ && item.planDefinitionsDigest === planDefinitionsDigest)
207
+ && requirements.filter((item) => !reports?.some((report) => report.question === item.question
208
+ && report.reason === item.reason)).length <= 1));
209
+ }
210
+
211
+ function safeRelativePath(value, field) {
212
+ const path = safeText(value, field, 500).replaceAll("\\", "/");
213
+ if (path.startsWith("/") || /^[A-Za-z]:\//.test(path) || path.split("/").includes("..")) {
214
+ throw new Error(field + " must stay inside the researched repository");
215
+ }
216
+ return path;
217
+ }
218
+
219
+ function publicHttpsUrl(value, field) {
220
+ let url;
221
+ try { url = new URL(value); } catch { throw new Error(field + " must be an HTTPS URL"); }
222
+ const host = url.hostname.toLowerCase();
223
+ if (url.protocol !== "https:" || url.username || url.password || host === "localhost"
224
+ || host.endsWith(".local") || /^\d+(?:\.\d+){3}$/.test(host) || host.includes(":")) {
225
+ throw new Error(field + " must be a public HTTPS URL without credentials");
226
+ }
227
+ return url.toString();
228
+ }
229
+
230
+ function evidenceTime(value, field, completedAt) {
231
+ const observedAt = timestamp(value, field);
232
+ const completed = new Date(completedAt);
233
+ const observed = new Date(observedAt);
234
+ if (observed > completed || completed.getTime() - observed.getTime() > 86400000) {
235
+ throw new Error(field + " must be fresh and no later than the report");
236
+ }
237
+ return observedAt;
238
+ }
239
+
240
+ function normalizeRepositorySource(source, index, completedAt) {
241
+ const field = `selfHelp.repositorySources[${index}]`;
242
+ if (!REPOSITORY_KINDS.has(source?.kind)) throw new Error(field + ".kind is unsupported");
243
+ if (!DIGEST_RE.test(source?.sourceDigest || "")) throw new Error(field + ".sourceDigest must be SHA-256");
244
+ if (!COMMIT_RE.test(source?.commit || "")) throw new Error(field + ".commit must be an exact Git commit");
245
+ return { kind: source.kind, path: safeRelativePath(source.path, field + ".path"), commit: source.commit,
246
+ sourceDigest: source.sourceDigest, observedAt: evidenceTime(source.observedAt, field + ".observedAt", completedAt),
247
+ relevance: safeKnowledgeText(source.relevance, field + ".relevance", 500) };
248
+ }
249
+
250
+ function normalizeExternalSource(source, index, completedAt) {
251
+ const field = `selfHelp.externalSources[${index}]`;
252
+ if (!EXTERNAL_KINDS.has(source?.kind)) throw new Error(field + ".kind is unsupported");
253
+ if (!DIGEST_RE.test(source?.sourceDigest || "")) throw new Error(field + ".sourceDigest must be SHA-256");
254
+ return { kind: source.kind, url: publicHttpsUrl(source.url, field + ".url"),
255
+ version: safeText(source.version, field + ".version", 200),
256
+ license: safeText(source.license, field + ".license", 200), sourceDigest: source.sourceDigest,
257
+ observedAt: evidenceTime(source.observedAt, field + ".observedAt", completedAt),
258
+ relevance: safeKnowledgeText(source.relevance, field + ".relevance", 500), untrusted: true };
259
+ }
260
+
261
+ function selfHelpMaterial(report) {
262
+ return { goalId: report.goalId, goalStepId: report.goalStepId,
263
+ planDefinitionsDigest: report.planDefinitionsDigest, question: report.question, reason: report.reason,
264
+ repositorySufficient: report.repositorySufficient, repositorySources: report.repositorySources,
265
+ externalSources: report.externalSources, conclusion: report.conclusion, completedAt: report.completedAt,
266
+ authority: "context-only-research" };
267
+ }
268
+
269
+ function selfHelpEscalationMaterial(report) {
270
+ return { goalId: report.goalId, goalStepId: report.goalStepId,
271
+ planDefinitionsDigest: report.planDefinitionsDigest, question: report.question, reason: report.reason,
272
+ repositorySufficient: false, repositorySources: report.repositorySources,
273
+ externalSources: report.externalSources, outcome: "needs-owner-input",
274
+ conflictSourceDigests: report.conflictSourceDigests, ownerQuestion: report.ownerQuestion,
275
+ ownerReason: report.ownerReason, options: report.options, completedAt: report.completedAt,
276
+ authority: "context-only-research" };
277
+ }
278
+
279
+ export function validSelfHelpReport(report) {
280
+ if (report?.schema === SELF_HELP_ESCALATION_SCHEMA) {
281
+ try {
282
+ const normalized = createSelfHelpEscalation({ goal: { goalId: report.goalId,
283
+ plan: { definitionsDigest: report.planDefinitionsDigest } }, step: { stepId: report.goalStepId },
284
+ report, now: report.completedAt });
285
+ return JSON.stringify(normalized) === JSON.stringify(report);
286
+ } catch { return false; }
287
+ }
288
+ if (!(report && report.schema === SELF_HELP_REPORT_SCHEMA && ID_RE.test(report.reportId || "")
289
+ && ID_RE.test(report.goalId || "") && ID_RE.test(report.goalStepId || "")
290
+ && DIGEST_RE.test(report.planDefinitionsDigest || "") && typeof report.repositorySufficient === "boolean"
291
+ && Array.isArray(report.repositorySources) && report.repositorySources.length >= 1 && report.repositorySources.length <= 16
292
+ && Array.isArray(report.externalSources) && report.externalSources.length <= 8
293
+ && typeof report.conclusion === "string" && report.conclusion.length <= 2000
294
+ && Number.isFinite(new Date(report.completedAt).getTime()) && DIGEST_RE.test(report.reportDigest || "")
295
+ && report.authority === "context-only-research")) return false;
296
+ try {
297
+ const normalized = createSelfHelpReport({ goal: { goalId: report.goalId,
298
+ plan: { definitionsDigest: report.planDefinitionsDigest } }, step: { stepId: report.goalStepId },
299
+ report, now: report.completedAt });
300
+ return JSON.stringify(normalized) === JSON.stringify(report);
301
+ } catch { return false; }
302
+ }
303
+
304
+ export function createSelfHelpReport({ goal, step, report, now }) {
305
+ if (report?.outcome === "needs-owner-input") {
306
+ return createSelfHelpEscalation({ goal, step, report, now });
307
+ }
308
+ const completedAt = timestamp(now);
309
+ const repositorySources = (report?.repositorySources || []).map((source, index) =>
310
+ normalizeRepositorySource(source, index, completedAt));
311
+ const externalSources = (report?.externalSources || []).map((source, index) =>
312
+ normalizeExternalSource(source, index, completedAt));
313
+ if (repositorySources.length < 1 || repositorySources.length > 16) {
314
+ throw new Error("self-help requires 1-16 repository evidence sources before escalation");
315
+ }
316
+ if (externalSources.length > 8) throw new Error("self-help external research exceeds eight sources");
317
+ const repositorySufficient = report?.repositorySufficient === true;
318
+ if (repositorySufficient && externalSources.length) {
319
+ throw new Error("self-help cannot use external sources when repository evidence is sufficient");
320
+ }
321
+ if (!repositorySufficient) {
322
+ const origins = new Set(externalSources.map((source) => new URL(source.url).origin));
323
+ if (externalSources.length < 2 || origins.size < 2) {
324
+ throw new Error("insufficient repository evidence requires two independent public primary sources");
325
+ }
326
+ }
327
+ const lastRepositoryRead = repositorySources.reduce((latest, source) =>
328
+ source.observedAt > latest ? source.observedAt : latest, repositorySources[0].observedAt);
329
+ if (externalSources.some((source) => source.observedAt < lastRepositoryRead)) {
330
+ throw new Error("self-help must inspect the repository before external research");
331
+ }
332
+ const result = { schema: SELF_HELP_REPORT_SCHEMA, reportId: null,
333
+ goalId: exactId(goal.goalId, "goalId"), goalStepId: exactId(step.stepId, "goalStepId"),
334
+ planDefinitionsDigest: goal.plan.definitionsDigest,
335
+ question: safeKnowledgeText(report?.question, "selfHelp.question", 500),
336
+ reason: safeKnowledgeText(report?.reason, "selfHelp.reason", 500), repositorySufficient,
337
+ repositorySources, externalSources, conclusion: safeKnowledgeText(report?.conclusion, "selfHelp.conclusion", 2000),
338
+ completedAt, reportDigest: null, authority: "context-only-research" };
339
+ result.reportDigest = sha256(JSON.stringify(selfHelpMaterial(result)));
340
+ result.reportId = "self-help-report:" + result.reportDigest.slice(0, 32);
341
+ return result;
342
+ }
343
+
344
+ export function createSelfHelpEscalation({ goal, step, report, now }) {
345
+ const completedAt = timestamp(now);
346
+ if (report?.repositorySufficient !== false) {
347
+ throw new Error("owner escalation requires exhausted repository evidence");
348
+ }
349
+ const repositorySources = (report?.repositorySources || []).map((source, index) =>
350
+ normalizeRepositorySource(source, index, completedAt));
351
+ const externalSources = (report?.externalSources || []).map((source, index) =>
352
+ normalizeExternalSource(source, index, completedAt));
353
+ if (repositorySources.length < 1 || repositorySources.length > 16) {
354
+ throw new Error("owner escalation requires 1-16 repository evidence sources");
355
+ }
356
+ const origins = new Set(externalSources.map((source) => new URL(source.url).origin));
357
+ if (externalSources.length < 2 || externalSources.length > 8 || origins.size < 2) {
358
+ throw new Error("owner escalation requires two independent public primary sources");
359
+ }
360
+ const lastRepositoryRead = repositorySources.reduce((latest, source) =>
361
+ source.observedAt > latest ? source.observedAt : latest, repositorySources[0].observedAt);
362
+ if (externalSources.some((source) => source.observedAt < lastRepositoryRead)) {
363
+ throw new Error("self-help must inspect the repository before external research");
364
+ }
365
+ const conflictSourceDigests = [...new Set(report?.conflictSourceDigests || [])].sort();
366
+ if (conflictSourceDigests.length !== 2 || conflictSourceDigests.some((digest) => !DIGEST_RE.test(digest))) {
367
+ throw new Error("owner escalation requires exactly two conflicting source digests");
368
+ }
369
+ const conflictSources = conflictSourceDigests.map((digest) =>
370
+ externalSources.find((source) => source.sourceDigest === digest));
371
+ if (conflictSources.some((source) => !source)
372
+ || new Set(conflictSources.map((source) => new URL(source.url).origin)).size !== 2) {
373
+ throw new Error("conflicting digests must bind two independent external primary sources");
374
+ }
375
+ const options = (report?.options || []).map((option, index) =>
376
+ safeKnowledgeText(option, `selfHelp.options[${index}]`, 500));
377
+ if (options.length < 2 || options.length > 8 || new Set(options).size !== options.length) {
378
+ throw new Error("owner escalation requires 2-8 distinct bounded options");
379
+ }
380
+ const result = { schema: SELF_HELP_ESCALATION_SCHEMA, reportId: null,
381
+ goalId: exactId(goal.goalId, "goalId"), goalStepId: exactId(step.stepId, "goalStepId"),
382
+ planDefinitionsDigest: goal.plan.definitionsDigest,
383
+ question: safeKnowledgeText(report?.question, "selfHelp.question", 500),
384
+ reason: safeKnowledgeText(report?.reason, "selfHelp.reason", 500), repositorySufficient: false,
385
+ repositorySources, externalSources, outcome: "needs-owner-input", conflictSourceDigests,
386
+ ownerQuestion: safeKnowledgeText(report?.ownerQuestion, "selfHelp.ownerQuestion", 500),
387
+ ownerReason: safeKnowledgeText(report?.ownerReason, "selfHelp.ownerReason", 500), options,
388
+ completedAt, reportDigest: null, authority: "context-only-research" };
389
+ result.reportDigest = sha256(JSON.stringify(selfHelpEscalationMaterial(result)));
390
+ result.reportId = "self-help-escalation:" + result.reportDigest.slice(0, 32);
391
+ return result;
392
+ }
393
+
394
+ export function createSelfHelpResolution({ goal, step, queueId, report, agentId, now }) {
395
+ const evidence = createSelfHelpReport({ goal, step, report, now });
396
+ const requirement = pendingSelfHelpRequirement(step);
397
+ const repeated = (step.selfHelpReports || []).some((existing) =>
398
+ existing.question === evidence.question && existing.reason === evidence.reason);
399
+ if (!requirement && !repeated) {
400
+ throw new Error("self-help report requires an exact pending repository-first requirement");
401
+ }
402
+ if (requirement && (requirement.question !== evidence.question || requirement.reason !== evidence.reason)) {
403
+ throw new Error("self-help report does not answer the pending repository-first requirement");
404
+ }
405
+ const needsOwner = evidence.schema === SELF_HELP_ESCALATION_SCHEMA;
406
+ const gap = createKnowledgeGap(goal, step, queueId, { question: needsOwner ? evidence.ownerQuestion : evidence.question,
407
+ reason: needsOwner ? evidence.ownerReason : evidence.reason,
408
+ requiredEvidence: needsOwner ? "owner-input" : "objective-observation" }, now);
409
+ if (needsOwner) return { status: "needs-owner-input", report: evidence, gap };
410
+ return { status: "resolved", report: evidence, gap: resolveKnowledgeGapCandidate(gap, {
411
+ answer: evidence.conclusion, answerSource: "objective-observation", sourceDigest: evidence.reportDigest,
412
+ resolvedAt: now, resolvedBySubjectId: agentId
413
+ }) };
414
+ }
415
+
416
+ export function selfHelpPolicyForWorkItem(step = null) {
417
+ const pending = step && pendingSelfHelpRequirement(step);
418
+ return { schema: "agentspine.self-help-policy/v1", order: ["repository", "public-primary-sources"],
419
+ repositoryFirst: true, externalOnlyWhenRepositoryInsufficient: true, maxRepositorySources: 16,
420
+ maxExternalSources: 8, externalContentTrust: "untrusted", reportSchema: SELF_HELP_REPORT_SCHEMA,
421
+ reportFields: ["question", "reason", "repositorySufficient", "repositorySources", "externalSources", "conclusion"],
422
+ escalationSchema: SELF_HELP_ESCALATION_SCHEMA, escalationOutcome: "needs-owner-input",
423
+ escalationFields: ["question", "reason", "repositorySufficient", "repositorySources", "externalSources",
424
+ "outcome", "conflictSourceDigests", "ownerQuestion", "ownerReason", "options"],
425
+ escalationRequiresIndependentConflict: true,
426
+ repositorySourceFields: ["kind", "path", "commit", "sourceDigest", "observedAt", "relevance"],
427
+ externalSourceFields: ["kind", "url", "version", "license", "sourceDigest", "observedAt", "relevance"],
428
+ repositoryKinds: [...REPOSITORY_KINDS], externalKinds: [...EXTERNAL_KINDS],
429
+ objectiveQuestionRequiresSelfHelp: true, pendingRequirement: pending ? structuredClone(pending) : null,
430
+ mayGrantAuthority: false, authority: "context-only-research" };
431
+ }