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,257 @@
1
+ import {
2
+ inspectPremortemState,
3
+ preparePremortemRequirement,
4
+ recordPremortemWriteIntent,
5
+ recordPremortemWrite,
6
+ verifyPremortemBeforeWrite,
7
+ verifyPremortemStop
8
+ } from "./delivery-premortem.js";
9
+ import { consumeDeliveryAgentUse, deliveryAgentUseGuidance,
10
+ removeDeliveryAgentUse, verifyDeliveryAgentUse } from "./delivery-agent-usage.js";
11
+ import { deliveryToolActions, verifyDeliveryStop } from "./delivery-verification.js";
12
+ import { boundedId } from "./hook-context.js";
13
+ import { auditGuard } from "./hook-protection.js";
14
+ import { comparablePath, projectId as localProjectId } from "./paths.js";
15
+ import { resolveFinalAssistantMessage } from "./hook-final-message.js";
16
+ import { assignmentPremortemBinding, beginDeliveryAssignment, continueDeliveryAssignment,
17
+ resolveDeliveryAssignment } from "./delivery-assignment.js";
18
+ import { deliveryWriteIdDigest } from "./delivery-premortem-binding.js";
19
+
20
+ export function premortemBinding(input, scope, root = null) {
21
+ return {
22
+ host: scope?.host || null,
23
+ sessionId: boundedId(input?.session_id ?? input?.sessionId, "sessionId"),
24
+ entityId: scope?.entityId || null,
25
+ groupId: scope?.groupId || null,
26
+ projectId: scope?.projectId || (root ? `project:${localProjectId(comparablePath(root))}` : null),
27
+ taskId: scope?.goalId || scope?.queueId ? null : scope?.currentTaskId || null,
28
+ assignmentId: input?.assignment_id || input?.assignmentId || null,
29
+ goalId: scope?.goalId || null,
30
+ goalStepId: scope?.goalStepId || null,
31
+ queueId: scope?.queueId || null,
32
+ planDefinitionsDigest: scope?.planDefinitionsDigest || null,
33
+ gatewayAttempt: scope?.gatewayAttempt || null
34
+ };
35
+ }
36
+
37
+ async function exactBinding(input, scope, root, begin = false) {
38
+ if (input?.assignment_id && input?.assignmentId && input.assignment_id !== input.assignmentId) {
39
+ return { status: "foreign-assignment", blocked: true,
40
+ reason: "Conflicting assignment_id and assignmentId cannot select a delivery." };
41
+ }
42
+ const binding = premortemBinding(input, scope, root);
43
+ if (begin && binding.assignmentId) {
44
+ return continueDeliveryAssignment({ root, binding, assignmentId: binding.assignmentId });
45
+ }
46
+ const suppliedEventId = input?.event_id ?? input?.hook_event_id ?? input?.turn_id;
47
+ const promptEventId = suppliedEventId
48
+ ? boundedId(suppliedEventId, "eventId")
49
+ : `prompt:${deliveryWriteIdDigest({ tool_input: input?.prompt ?? input?.user_prompt
50
+ ?? input?.message ?? input?.input ?? null })}`;
51
+ const assignment = begin
52
+ ? await beginDeliveryAssignment({ root, binding,
53
+ eventId: promptEventId,
54
+ now: input.timestamp || new Date() })
55
+ : await resolveDeliveryAssignment({ root, binding, assignmentId: binding.assignmentId });
56
+ return assignment.blocked ? assignment : {
57
+ ...assignment, binding: assignmentPremortemBinding(binding, assignment)
58
+ };
59
+ }
60
+
61
+ export function isPremortemWrite(input) {
62
+ return deliveryToolActions(input).some((item) => item.kind === "write");
63
+ }
64
+
65
+ async function diagnostic(input, phase, result) {
66
+ if (result?.status === "degraded") await auditGuard(input, phase, result);
67
+ return result;
68
+ }
69
+
70
+ function degraded(error, path) {
71
+ return {
72
+ status: "degraded", blocked: false, path,
73
+ reason: String(error?.message || error || "premortem state unavailable").slice(0, 2048)
74
+ };
75
+ }
76
+
77
+ function invalidWriteSession(error, path) {
78
+ return {
79
+ status: "invalid-session", blocked: true, path,
80
+ reason: "AgentSpine denied this write before mutation because a valid host session_id is required: "
81
+ + String(error?.message || error || "sessionId is missing").slice(0, 400)
82
+ };
83
+ }
84
+
85
+ function withRegistrationGuidance(result, root, assignmentId = null) {
86
+ const requirementId = result.requirementId || result.requirement?.requirementId || null;
87
+ const registration = { tool: "record_delivery_premortem", root, requirementId };
88
+ const agentSpineUse = deliveryAgentUseGuidance(root, requirementId);
89
+ return {
90
+ ...result, assignmentId, registration, agentSpineUse,
91
+ instruction: [
92
+ "Advisory preparation; never blocks authorized coding or replies. Bind calls to session, assignment and step:",
93
+ "Reuse the host-verified briefing; delivery_knowledge_query and record_delivery_premortem need no second briefing fetch.",
94
+ `Use registration.root; Requirement: ${requirementId || "<unavailable; continue the authorized task>"}.`,
95
+ "Premortem: exactly baseline-environment, contract-tests, delivery-path; each `this delivery fails because ` statement needs a concrete check.",
96
+ "Context only; no authority. Never reset receipts or repeat tests merely to dismiss a warning.",
97
+ "Host continuation: UserPromptSubmit assignmentId; omit for new work. Chat cannot select continuation.",
98
+ "After observed tests: ordinary assignments use complete_delivery; goals retain checkpoints/outcomes. Legacy closure:",
99
+ "`Premortem closure sha256 <64hex>`, `Premortem latest write sha256 <64hex>`, and:",
100
+ "- <category> <checkId>: PASS — <nonempty result>"
101
+ ].join("\n")
102
+ };
103
+ }
104
+
105
+ export async function prepareHookPremortem({ input, root, scope }) {
106
+ try {
107
+ const exact = await exactBinding(input, scope, root, true);
108
+ if (exact.blocked) return withRegistrationGuidance(exact, root, exact.assignmentId);
109
+ if (exact.status === "degraded") {
110
+ await diagnostic(input, "premortem-prepare", exact);
111
+ return withRegistrationGuidance(exact, root);
112
+ }
113
+ if (exact.status === "continued") return withRegistrationGuidance({
114
+ status: "required", blocked: false, requirementId: exact.requirementId,
115
+ requirement: exact.requirement, continuation: true
116
+ }, root, exact.assignmentId);
117
+ const result = await preparePremortemRequirement({
118
+ root, binding: exact.binding, now: input.timestamp || new Date()
119
+ });
120
+ await diagnostic(input, "premortem-prepare", result);
121
+ return withRegistrationGuidance(result, root, exact.assignmentId);
122
+ } catch (error) {
123
+ const result = degraded(error, root);
124
+ await diagnostic(input, "premortem-prepare", result);
125
+ return withRegistrationGuidance(result, root);
126
+ }
127
+ }
128
+
129
+ export async function verifyHookPremortemWrite({ input, root, scope }) {
130
+ if (!isPremortemWrite(input)) return { status: "not-applicable", blocked: false };
131
+ try {
132
+ if (!boundedId(input?.session_id ?? input?.sessionId, "sessionId")) {
133
+ throw new Error("sessionId is missing");
134
+ }
135
+ } catch (error) {
136
+ return invalidWriteSession(error, root);
137
+ }
138
+ try {
139
+ const exact = await exactBinding(input, scope, root);
140
+ if (exact.blocked) return exact;
141
+ const binding = exact.binding;
142
+ const result = await diagnostic(input, "premortem-before-write", await verifyPremortemBeforeWrite({
143
+ root, binding, now: input.timestamp || new Date()
144
+ }));
145
+ let requirement = null;
146
+ if (result.status === "missing") {
147
+ requirement = await diagnostic(input, "premortem-prepare-on-write", await preparePremortemRequirement({
148
+ root, binding, now: input.timestamp || new Date()
149
+ }));
150
+ if (requirement.status === "degraded") return withRegistrationGuidance(requirement, root);
151
+ if (requirement.blocked) return requirement;
152
+ }
153
+ if (result.blocked && new Set(["conflict", "late", "finalized", "mismatch"]).has(result.status)) {
154
+ const recovery = result.status === "conflict" ? {
155
+ tool: "recover_delivery_premortem", root,
156
+ predecessorRequirementId: result.requirementId,
157
+ instruction: "Recover into a fresh assignment-bound requirement, then run all three preflight calls again. History is preserved; recovery grants no authority."
158
+ } : null;
159
+ return { ...result, recovery,
160
+ reason: `${result.reason}\nPremortem status: ${result.status}.${recovery ? `\n${recovery.instruction}` : ""}` };
161
+ }
162
+ const requirementId = requirement?.requirementId || result.requirementId;
163
+ if (requirementId?.split(":").length === 3) {
164
+ const usage = await diagnostic(input, "delivery-agent-use-before-write",
165
+ await verifyDeliveryAgentUse({ root, requirementId }));
166
+ if (usage.blocked) {
167
+ const guidance = withRegistrationGuidance(requirement || result, root);
168
+ return { ...usage, reason: `${usage.reason}\n${guidance.instruction}` };
169
+ }
170
+ if (!result.blocked) return { ...result, agentSpineUse: usage };
171
+ } else if (!result.blocked) return result;
172
+ const named = `${result.reason}\nPremortem status: ${result.status}.`;
173
+ const guided = requirement ? withRegistrationGuidance(requirement, root) : null;
174
+ return result.status === "missing"
175
+ ? { ...result, requirementId: guided.requirementId, registration: guided.registration,
176
+ reason: `${named}\n${guided.instruction}` }
177
+ : { ...result, reason: named };
178
+ } catch (error) {
179
+ return diagnostic(input, "premortem-before-write", degraded(error, root));
180
+ }
181
+ }
182
+
183
+ export async function recordHookPremortemWrite({ input, root, scope, success }) {
184
+ if (!isPremortemWrite(input)) return { status: "not-applicable", blocked: false };
185
+ if (success !== true) return { status: "write-failed", blocked: false };
186
+ try {
187
+ const exact = await exactBinding(input, scope, root);
188
+ if (exact.blocked) return exact;
189
+ const result = await diagnostic(input, "premortem-write", await recordPremortemWrite({
190
+ root, binding: exact.binding, input,
191
+ now: input.timestamp || new Date()
192
+ }));
193
+ return result.status === "duplicate" ? { ...result, status: "write-recorded" } : result;
194
+ } catch (error) {
195
+ return diagnostic(input, "premortem-write", degraded(error, root));
196
+ }
197
+ }
198
+
199
+ export async function recordHookPremortemWriteIntent({ input, root, scope }) {
200
+ if (!isPremortemWrite(input)) return { status: "not-applicable", blocked: false };
201
+ try {
202
+ const exact = await exactBinding(input, scope, root);
203
+ if (exact.blocked) return exact;
204
+ return await diagnostic(input, "premortem-write-intent", await recordPremortemWriteIntent({
205
+ root, binding: exact.binding, input, now: input.timestamp || new Date()
206
+ }));
207
+ } catch (error) {
208
+ return diagnostic(input, "premortem-write-intent", degraded(error, root));
209
+ }
210
+ }
211
+
212
+ export async function verifyHookPremortemStop({ input, root, scope, paused = false,
213
+ consume = false }) {
214
+ if (paused) return { status: "paused-job", blocked: false };
215
+ try {
216
+ const message = resolveFinalAssistantMessage(input);
217
+ if (!message.known) {
218
+ return diagnostic(input, "premortem-stop", degraded(new Error(`premortem ${message.reason}`), root));
219
+ }
220
+ const exact = await exactBinding(input, scope, root);
221
+ if (exact.blocked) return exact;
222
+ const binding = exact.binding;
223
+ const inspection = await diagnostic(input, "premortem-stop-inspect",
224
+ await inspectPremortemState({ root, binding }));
225
+ let usage = null;
226
+ if (inspection.hasWrite && inspection.requirementId) {
227
+ usage = await diagnostic(input, "delivery-agent-use-stop",
228
+ await verifyDeliveryAgentUse({ root, requirementId: inspection.requirementId }));
229
+ if (usage.blocked) return usage;
230
+ }
231
+ let allowStoredCompletion = false;
232
+ if (inspection.completionSource === "mcp") {
233
+ const tested = await verifyDeliveryStop({ root, host: binding.host,
234
+ sessionId: binding.sessionId, scope });
235
+ if (tested.blocked) return diagnostic(input, "completion-test-check", tested);
236
+ allowStoredCompletion = tested.status === "verified";
237
+ }
238
+ const result = await diagnostic(input, "premortem-stop", await verifyPremortemStop({
239
+ root, binding, message: message.text,
240
+ allowStoredCompletion,
241
+ now: input.timestamp || new Date()
242
+ }));
243
+ if (!inspection.hasWrite && inspection.requirementId) {
244
+ await diagnostic(input, "delivery-agent-use-read-only-cleanup",
245
+ await removeDeliveryAgentUse({ root, requirementId: inspection.requirementId }));
246
+ }
247
+ if (consume && result.status === "closed" && usage && inspection.requirementId) {
248
+ const consumed = await diagnostic(input, "delivery-agent-use-consume",
249
+ await consumeDeliveryAgentUse({ root, requirementId: inspection.requirementId,
250
+ now: input.timestamp || new Date() }));
251
+ return { ...result, agentSpineUse: { ...usage, consumption: consumed } };
252
+ }
253
+ return result;
254
+ } catch (error) {
255
+ return diagnostic(input, "premortem-stop", degraded(error, root));
256
+ }
257
+ }
@@ -0,0 +1,37 @@
1
+ import { recordHookScanAudit } from "./hook-audit.js";
2
+
3
+ // This is deliberately called only for process evidence and code-quality
4
+ // findings, after effect authorization. It must never wrap access denials.
5
+ export async function processAdvisory(input, payload, root, scope, details) {
6
+ const event = input.hook_event_name || input.event_name;
7
+ const status = details.premortem?.status || details.deliveryVerification?.status || details.artifactGuard?.status;
8
+ const diagnostic = {
9
+ schema: "agentspine.process-advisory/v1", presentation: "internal-only",
10
+ status: /^[a-z-]{1,64}$/.test(status || "") ? status : "unavailable",
11
+ completionVerified: false, action: "continue-authorized-task", automaticRetry: false,
12
+ authority: "context-only",
13
+ instruction: "Do not quote this diagnostic. Continue working or answer normally. "
14
+ + "Mention only relevant evidence-backed limitations in the user's language; never invent a risk or a passed test. "
15
+ + "Ask only for a consequential user decision. Do not reset receipts or repeat work to satisfy process bookkeeping."
16
+ };
17
+ const fresh = await recordHookScanAudit({
18
+ event,
19
+ phase: "process-advisory",
20
+ error: { code: "UNVERIFIED_DELIVERY", message: JSON.stringify(diagnostic) },
21
+ onceKey: JSON.stringify(["process-advisory", root, scope,
22
+ input.session_id || input.sessionId, input.assignment_id || input.assignmentId || null]),
23
+ });
24
+ const result = {
25
+ ...details,
26
+ blocked: false,
27
+ advisory: true,
28
+ completionVerified: false,
29
+ diagnostic: fresh ? diagnostic : null
30
+ };
31
+ if (payload) return result;
32
+ const field = process.env.BLUN_PLUGIN_ROOT ? "message" : "additionalContext";
33
+ const output = fresh ? { hookSpecificOutput: {
34
+ hookEventName: event, [field]: JSON.stringify(diagnostic)
35
+ } } : {};
36
+ process.stdout.write(`${JSON.stringify(output)}\n`);
37
+ }
@@ -0,0 +1,95 @@
1
+ import { isAbsolute, relative } from "node:path";
2
+ import { deliveryToolActions } from "./delivery-command-actions.js";
3
+ import { recordHookScanAudit } from "./hook-audit.js";
4
+
5
+ export function candidatePaths(value, output = []) {
6
+ if (!value) return output;
7
+ if (typeof value === "string") {
8
+ for (const match of value.matchAll(/^\*\*\* (?:Add|Update|Delete) File: (.+)$/gm)) output.push(match[1].trim());
9
+ return output;
10
+ }
11
+ if (Array.isArray(value)) {
12
+ for (const item of value) candidatePaths(item, output);
13
+ return output;
14
+ }
15
+ for (const [key, item] of Object.entries(value)) {
16
+ if (["path", "file_path", "target_file", "filename"].includes(key) && typeof item === "string") output.push(item);
17
+ else candidatePaths(item, output);
18
+ }
19
+ return output;
20
+ }
21
+
22
+ export function isMutationTool(name = "") {
23
+ return /(^|__)(apply_patch|edit|write|delete|move|rename|bash|exec_command|shell|powershell|pwsh)(_|$)/i.test(name);
24
+ }
25
+
26
+ export function isScanFailOpenTool(name = "") {
27
+ return /(^|__)(apply_patch|edit|write|bash|exec_command|shell|powershell|pwsh)(_|$)/i.test(name);
28
+ }
29
+
30
+ function filesystemScanError(error) {
31
+ return Boolean(error && (error.code === "AGENTSPINE_SCAN_INCOMPLETE" || error.agentSpineScan === true));
32
+ }
33
+
34
+ export function hookScanFailureFailsOpen(error) {
35
+ return filesystemScanError(error) || (["EPERM", "EACCES"].includes(error?.code)
36
+ && ["opendir", "readdir", "scandir"].includes(error?.syscall));
37
+ }
38
+
39
+ export async function auditSkippedScans(input, phase, skipped = []) {
40
+ const item = skipped.find((entry) => entry.code === "AGENTSPINE_SCAN_INCOMPLETE") || skipped[0];
41
+ if (!item) return;
42
+ await recordHookScanAudit({
43
+ event: "PreToolUse", toolName: input.tool_name || null, phase,
44
+ error: { code: item.code, message: item.message || `${item.code}: ${item.operation} skipped ${item.path}` },
45
+ path: item.path, operation: item.operation, now: input.timestamp || new Date()
46
+ });
47
+ }
48
+
49
+ export async function auditGuard(input, phase, result, once = false) {
50
+ await recordHookScanAudit({
51
+ event: input.hook_event_name || input.event_name || "unknown", toolName: input.tool_name || null,
52
+ phase, error: { code: result.status || "GUARD_ERROR", message: result.reason || result.status },
53
+ path: result.path || input.cwd || process.cwd(), operation: "verify",
54
+ onceKey: once ? `${phase}\0${result.status}\0${result.path || input.cwd || ""}` : null,
55
+ now: input.timestamp || new Date()
56
+ });
57
+ }
58
+
59
+ async function allowScanFailure(input, phase, error) {
60
+ const event = input.hook_event_name || input.event_name || "unknown";
61
+ await recordHookScanAudit({
62
+ event, toolName: input.tool_name || null, phase, error,
63
+ path: error?.path || input.cwd || process.cwd(), now: input.timestamp || new Date()
64
+ });
65
+ return { blocked: false, scanFailedOpen: true, event, phase, error: error.message };
66
+ }
67
+
68
+ export async function finishScanFailure(input, payload, phase, error) {
69
+ const allowed = await allowScanFailure(input, phase, error);
70
+ if (payload) return allowed;
71
+ process.stdout.write("{}\n");
72
+ return undefined;
73
+ }
74
+
75
+ function stringValues(value, output = []) {
76
+ if (typeof value === "string") output.push(value);
77
+ else if (Array.isArray(value)) value.forEach((item) => stringValues(item, output));
78
+ else if (value && typeof value === "object") Object.values(value).forEach((item) => stringValues(item, output));
79
+ return output;
80
+ }
81
+
82
+ export function shellTargetsProtected(input, documents, cwd, root) {
83
+ if (!/(bash|exec_command|shell|powershell|pwsh)/i.test(input.tool_name || "")) return null;
84
+ const command = stringValues(input.tool_input || input.tool_args).join("\n").replaceAll("\\", "/");
85
+ if (!deliveryToolActions(input).some((item) => item.kind === "write")) return null;
86
+ for (const document of documents) {
87
+ const forms = new Set([document.path.replaceAll("\\", "/"), document.relativePath]);
88
+ for (const base of [cwd, root]) {
89
+ const candidate = relative(base, document.path);
90
+ if (candidate && !candidate.startsWith("..") && !isAbsolute(candidate)) forms.add(candidate.replaceAll("\\", "/"));
91
+ }
92
+ if ([...forms].some((form) => form && command.includes(form))) return document;
93
+ }
94
+ return null;
95
+ }
@@ -0,0 +1,84 @@
1
+ import { deliveryActorSession, recordDeliveryPause, verifyDeliveryStop } from "./delivery-verification.js";
2
+ import { blockStop } from "./hook-output.js";
3
+ import { verifyHookPremortemStop } from "./hook-premortem.js";
4
+
5
+ function explicitStopEventId(input) {
6
+ for (const candidate of [input?.event_id, input?.hook_event_id]) {
7
+ if (typeof candidate === "string" && candidate) return candidate;
8
+ }
9
+ return null;
10
+ }
11
+
12
+ function stopArguments(input, root, scope) {
13
+ return {
14
+ root,
15
+ host: scope.host,
16
+ sessionId: deliveryActorSession(input),
17
+ scope,
18
+ // Timestamps identify neither a host event nor a pause: two distinct Stop
19
+ // deliveries may share one. Only an explicit host-provided event id can
20
+ // make a pause idempotent or be honored by a later completion.
21
+ eventId: explicitStopEventId(input)
22
+ };
23
+ }
24
+
25
+ function blockedResult(deliveryVerification, premortem = null) {
26
+ return {
27
+ blocked: true,
28
+ reason: deliveryVerification.reason,
29
+ deliveryVerification,
30
+ premortem
31
+ };
32
+ }
33
+
34
+ export function denyHookStop(payload, event, reason, details) {
35
+ if (payload) return { blocked: true, reason, ...details };
36
+ blockStop(event, reason);
37
+ }
38
+
39
+ export async function verifyHookStopContracts({ input, root, scope, recordPause = false,
40
+ completionFence = false, afterDeliveryVerification = null,
41
+ afterPremortemVerification = null }) {
42
+ const argumentsForStop = stopArguments(input, root, scope);
43
+ const deliveryVerification = recordPause
44
+ ? await recordDeliveryPause(argumentsForStop)
45
+ : await verifyDeliveryStop(argumentsForStop);
46
+ if (deliveryVerification.blocked) return blockedResult(deliveryVerification);
47
+ if (typeof afterDeliveryVerification === "function") await afterDeliveryVerification();
48
+
49
+ const premortem = await verifyHookPremortemStop({
50
+ input,
51
+ root,
52
+ scope,
53
+ paused: deliveryVerification.status === "paused-job",
54
+ consume: completionFence
55
+ });
56
+ if (premortem.blocked) {
57
+ return { blocked: true, reason: premortem.reason, deliveryVerification, premortem };
58
+ }
59
+ if (typeof afterPremortemVerification === "function") await afterPremortemVerification();
60
+
61
+ const finalDeliveryVerification = completionFence
62
+ ? await verifyDeliveryStop(argumentsForStop)
63
+ : deliveryVerification;
64
+ if (finalDeliveryVerification.blocked) {
65
+ return blockedResult(finalDeliveryVerification, premortem);
66
+ }
67
+ const digestPattern = /^[a-f0-9]{64}$/;
68
+ if (completionFence && digestPattern.test(deliveryVerification.stateDigest || "")
69
+ && digestPattern.test(finalDeliveryVerification.stateDigest || "")
70
+ && deliveryVerification.stateDigest !== finalDeliveryVerification.stateDigest) {
71
+ return blockedResult({
72
+ ...finalDeliveryVerification,
73
+ status: "changed",
74
+ blocked: true,
75
+ reason: "AgentSpine delivery evidence changed during Stop; retry completion against the latest state."
76
+ }, premortem);
77
+ }
78
+ return {
79
+ blocked: false,
80
+ reason: null,
81
+ deliveryVerification: finalDeliveryVerification,
82
+ premortem
83
+ };
84
+ }
@@ -0,0 +1,91 @@
1
+ import { sessionTimelineLifecycleHint } from "./session-timeline.js";
2
+ import { issueHostTranscriptReceipt } from "./session-timeline-enrollment.js";
3
+ import { consumeTimelineHostOrigin } from "./session-timeline-host-origin.js";
4
+ import {
5
+ timelineHostForRuntime, timelineHostHome, timelineProtocolFromRuntime, timelineSourceFromRuntime
6
+ } from "./session-timeline-provider.js";
7
+
8
+ const AUTHORITY = "context-only";
9
+
10
+ function unavailable(reason) {
11
+ return { schema: "agentspine.session-timeline/v1", status: "unavailable", reason, authority: AUTHORITY };
12
+ }
13
+
14
+ function hasRawTimelineGroupSignal(input) {
15
+ try {
16
+ if (!input || typeof input !== "object" || Array.isArray(input)) return false;
17
+ const nested = input.agent_spine_scope;
18
+ const values = [input.groupId, input.group_id];
19
+ if (nested && typeof nested === "object" && !Array.isArray(nested)) {
20
+ values.push(nested.groupId, nested.group_id);
21
+ }
22
+ return values.some((value) => value !== undefined && value !== null && value !== "");
23
+ } catch {
24
+ // Parser-shaped uncertainty must not turn a lifecycle hint into a denial.
25
+ return false;
26
+ }
27
+ }
28
+
29
+ function groupSuppressed(reason) {
30
+ return { schema: "agentspine.session-timeline/v1", status: "group-suppressed", reason, authority: AUTHORITY };
31
+ }
32
+
33
+ function receiptObservation(receipt) {
34
+ return { schema: "agentspine.session-timeline-host-receipt/v1",
35
+ status: receipt.status === "pending" ? "pending" : "unavailable", reason: receipt.reason || null,
36
+ expiresAt: receipt.expiresAt || null, authority: AUTHORITY };
37
+ }
38
+
39
+ // This observes a provider hook payload but never returns the
40
+ // opaque enrollment token through model-visible context.
41
+ export async function observeHostTranscriptReceipt({ root, event, input, scope, hostHome, hostOrigin = null, clock = null }) {
42
+ if (event !== "UserPromptSubmit") return receiptObservation({ status: "not-applicable" });
43
+ if (hasRawTimelineGroupSignal(input)) return receiptObservation({ status: "unavailable", reason: "raw-group-scope" });
44
+ if (!scope || scope.groupId !== null) return receiptObservation({ status: "unavailable", reason: "computed-group-scope" });
45
+ const host = timelineHostForRuntime(scope.host, process.env);
46
+ const receipt = await issueHostTranscriptReceipt({ root, host, sessionId: input.session_id ?? input.sessionId,
47
+ scope, transcriptPath: timelineSourceFromRuntime(host, input), hostHome: timelineHostHome(host) ?? hostHome,
48
+ event, eventId: input.event_id ?? input.hook_event_id,
49
+ hostOrigin, clock, environment: process.env });
50
+ return receiptObservation(receipt);
51
+ }
52
+
53
+ export async function captureSessionTimelineLifecycle({ root, event, input, scope, hostHome, hostOrigin = null, clock = null }) {
54
+ if (hasRawTimelineGroupSignal(input)) return groupSuppressed("raw-group-scope");
55
+ if (!scope || scope.groupId !== null) return groupSuppressed("computed-group-scope");
56
+ let hostReceipt = null;
57
+ if (event === "UserPromptSubmit" && hostOrigin) {
58
+ try { hostReceipt = await observeHostTranscriptReceipt({ root, event, input, scope, hostHome, hostOrigin, clock }); }
59
+ catch (error) {
60
+ hostReceipt = { schema: "agentspine.session-timeline-host-receipt/v1", status: "degraded", reason: error.message,
61
+ expiresAt: null, authority: AUTHORITY };
62
+ }
63
+ }
64
+ const result = (value) => hostReceipt ? { ...value, hostReceipt } : value;
65
+ const host = timelineHostForRuntime(scope.host, process.env);
66
+ if (!host || (host === "king" && !timelineProtocolFromRuntime(host))) return unavailable("host-not-supported");
67
+ return result(await sessionTimelineLifecycleHint({ root, host,
68
+ sessionId: input.session_id ?? input.sessionId, scope, environment: process.env }));
69
+ }
70
+
71
+ // The calling hook performs this only at its final pre-answer boundary. The
72
+ // origin helper consumes the exact signed preflight receipt before this can
73
+ // touch timeline receipt state. Failure to form a timeline origin is optional
74
+ // and yields no opaque receipt; failure to consume preflight remains blocking.
75
+ export async function finalizeUserPromptSessionTimeline({
76
+ root, input, scope, resolvedSources, preflight, prompt, now = new Date()
77
+ }) {
78
+ const consumed = await consumeTimelineHostOrigin({ event: "UserPromptSubmit", input, scope, resolvedSources,
79
+ preflight, prompt, environment: process.env, now });
80
+ if (!consumed?.consumed) return { preflightConsumed: false, timeline: unavailable("preflight-receipt-unavailable") };
81
+ try {
82
+ return { preflightConsumed: true, timeline: await captureSessionTimelineLifecycle({
83
+ root, event: "UserPromptSubmit", input, scope, hostHome: resolvedSources.hostHome,
84
+ hostOrigin: consumed.origin, clock: () => new Date(now)
85
+ }) };
86
+ } catch (error) {
87
+ return { preflightConsumed: true, timeline: {
88
+ schema: "agentspine.session-timeline/v1", status: "degraded", reason: error.message, authority: AUTHORITY
89
+ } };
90
+ }
91
+ }