blun-king-cli 9.1.587 → 9.1.588

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 (436) hide show
  1. package/CHANGELOG.md +11 -185
  2. package/LIESMICH.txt +51 -13
  3. package/README.md +44 -47
  4. package/agent-spine-plugin/.codex-plugin/plugin.json +16 -4
  5. package/agent-spine-plugin/CHANGELOG.md +37 -5
  6. package/agent-spine-plugin/README.md +3 -3
  7. package/agent-spine-plugin/blun.plugin.json +45 -10
  8. package/agent-spine-plugin/docs/artifact-evaluation.md +93 -0
  9. package/agent-spine-plugin/docs/host-integration.md +42 -27
  10. package/agent-spine-plugin/docs/preflight-recall.md +4 -2
  11. package/agent-spine-plugin/docs/session-timeline.md +97 -236
  12. package/agent-spine-plugin/docs/world-model.md +25 -0
  13. package/agent-spine-plugin/hooks/codex.json +1 -1
  14. package/agent-spine-plugin/hooks/hooks.json +1 -1
  15. package/agent-spine-plugin/package.json +1 -3
  16. package/agent-spine-plugin/scripts/check-hosts.js +3 -3
  17. package/agent-spine-plugin/scripts/release-check.js +10 -5
  18. package/agent-spine-plugin/scripts/run-checks.js +4 -1
  19. package/agent-spine-plugin/scripts/run-tests-hermetic.js +32 -6
  20. package/agent-spine-plugin/src/cli-learning.js +15 -0
  21. package/agent-spine-plugin/src/cli.js +2 -0
  22. package/agent-spine-plugin/src/hook.js +32 -32
  23. package/agent-spine-plugin/src/lib/action-lesson-recall.js +73 -8
  24. package/agent-spine-plugin/src/lib/briefing.js +146 -36
  25. package/agent-spine-plugin/src/lib/channel-continuity.js +19 -0
  26. package/agent-spine-plugin/src/lib/delivery-agent-usage.js +14 -7
  27. package/agent-spine-plugin/src/lib/gateway-group-response.js +128 -0
  28. package/agent-spine-plugin/src/lib/gateway-runs.js +24 -15
  29. package/agent-spine-plugin/src/lib/hook-briefing-use.js +13 -3
  30. package/agent-spine-plugin/src/lib/hook-context.js +16 -3
  31. package/agent-spine-plugin/src/lib/hook-output.js +129 -5
  32. package/agent-spine-plugin/src/lib/hook-timeline.js +5 -3
  33. package/agent-spine-plugin/src/lib/indexed-memory.js +2 -2
  34. package/agent-spine-plugin/src/lib/learning-artifact-evaluator.js +114 -0
  35. package/agent-spine-plugin/src/lib/learning-context.js +11 -4
  36. package/agent-spine-plugin/src/lib/learning-measurements.js +2 -2
  37. package/agent-spine-plugin/src/lib/mcp-runtime.js +89 -3
  38. package/agent-spine-plugin/src/lib/mcp-source-context.js +12 -2
  39. package/agent-spine-plugin/src/lib/mcp-timeline-tools.js +91 -8
  40. package/agent-spine-plugin/src/lib/mcp-world-tools.js +2 -2
  41. package/agent-spine-plugin/src/lib/owned-file-lock.js +20 -1
  42. package/agent-spine-plugin/src/lib/persona-runtime.js +2 -2
  43. package/agent-spine-plugin/src/lib/preflight-delivery-id.js +27 -0
  44. package/agent-spine-plugin/src/lib/preflight.js +4 -4
  45. package/agent-spine-plugin/src/lib/session-timeline-codex.js +15 -0
  46. package/agent-spine-plugin/src/lib/session-timeline-contract.js +12 -4
  47. package/agent-spine-plugin/src/lib/session-timeline-event-extract.js +36 -7
  48. package/agent-spine-plugin/src/lib/session-timeline-host-origin.js +13 -10
  49. package/agent-spine-plugin/src/lib/session-timeline-invocation.js +1 -1
  50. package/agent-spine-plugin/src/lib/session-timeline-king.js +14 -0
  51. package/agent-spine-plugin/src/lib/session-timeline-prior.js +18 -12
  52. package/agent-spine-plugin/src/lib/session-timeline-provider.js +5 -0
  53. package/agent-spine-plugin/src/lib/session-timeline-query.js +2 -0
  54. package/agent-spine-plugin/src/lib/session-timeline-results.js +35 -10
  55. package/agent-spine-plugin/src/lib/session-timeline-source-open.js +30 -0
  56. package/agent-spine-plugin/src/lib/session-timeline.js +122 -75
  57. package/agent-spine-plugin/src/lib/source-roots.js +3 -2
  58. package/agent-spine-plugin/src/lib/task-knowledge-context.js +22 -1
  59. package/agent-spine-plugin/src/lib/timeline-continuation-update.js +100 -0
  60. package/agent-spine-plugin/src/lib/timeline-tool-guard.js +30 -7
  61. package/agent-spine-plugin/src/lib/timeline-user-feedback.js +217 -0
  62. package/agent-spine-plugin/src/lib/timeline-world-capture.js +233 -0
  63. package/agent-spine-plugin/src/lib/world-knowledge.js +59 -2
  64. package/agent-spine-plugin/src/lib/world-model.js +64 -9
  65. package/agent-spine-plugin/src/worker.js +13 -1
  66. package/bin/blun.js +43 -28
  67. package/bin/core-bootstrap.js +5 -4
  68. package/bin/king.js +43 -28
  69. package/bin/launcher-mode.js +1 -10
  70. package/bin/launcher-runtime.js +128 -295
  71. package/bin/managed-node.js +0 -0
  72. package/bin/managed-plugin-selection.cjs +0 -1
  73. package/bin/native-module-repair.js +0 -0
  74. package/bin/node-runtime.js +0 -0
  75. package/bin/node-version.js +0 -0
  76. package/bin/plugin-bootstrap.js +56 -120
  77. package/bin/private-paths.js +11 -34
  78. package/bin/standard-tools-bootstrap.js +34 -114
  79. package/bin/turn-thinking-policy.cjs +3 -11
  80. package/bin/update-copy.js +200 -0
  81. package/bin/update-lease.js +0 -0
  82. package/bin/update-notice.js +136 -289
  83. package/bin/verify-agent-behavior.cjs +122 -0
  84. package/bin/verify-agent-components.cjs +104 -0
  85. package/bin/verify-bundled-agent-sources.cjs +57 -0
  86. package/blun.mjs +143076 -135288
  87. package/bundled-agent-sources.json +701 -0
  88. package/package.json +12 -15
  89. package/standard-skills/translate-native/README.md +1293 -0
  90. package/standard-skills/translate-native/SKILL.md +172 -22
  91. package/standard-skills/translate-native/VERSION +1 -1
  92. package/standard-skills/translate-native/agents/openai.yaml +18 -0
  93. package/standard-skills/translate-native/assets/icon.svg +8 -0
  94. package/standard-skills/translate-native/docs/BLUN_CODE_INTEGRATION.md +76 -0
  95. package/standard-skills/translate-native/docs/PREMORTEM.md +489 -0
  96. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION.md +2035 -0
  97. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_API.md +1302 -0
  98. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_EVIDENCE_HTTP.md +136 -0
  99. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_HEALTH_HTTP.md +130 -0
  100. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_HTTP_PROVIDER.md +175 -0
  101. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_RECEIPT_VERIFIER_HTTP.md +86 -0
  102. package/standard-skills/translate-native/integrations/AGENT_RULES.md +32 -0
  103. package/standard-skills/translate-native/integrations/adapters/blun-code-language-guard.js +514 -0
  104. package/standard-skills/translate-native/integrations/adapters/node-language-guard.js +230 -0
  105. package/standard-skills/translate-native/integrations/audit_log.py +327 -0
  106. package/standard-skills/translate-native/integrations/claude_language_hook.js +1536 -0
  107. package/standard-skills/translate-native/integrations/commercial_localization_profile.py +42 -0
  108. package/standard-skills/translate-native/integrations/delivery-policy.example.json +28 -0
  109. package/standard-skills/translate-native/integrations/enforced_delivery.py +543 -0
  110. package/standard-skills/translate-native/integrations/guard_service.py +435 -0
  111. package/standard-skills/translate-native/integrations/language_gateway.py +67 -0
  112. package/standard-skills/translate-native/integrations/mcp_auth_headers.py +198 -0
  113. package/standard-skills/translate-native/integrations/mcp_http_gateway.py +429 -0
  114. package/standard-skills/translate-native/integrations/non_language_html_entities.js +1485 -0
  115. package/standard-skills/translate-native/integrations/pre_output_guard.py +65 -0
  116. package/standard-skills/translate-native/integrations/task_router.py +101 -0
  117. package/standard-skills/translate-native/integrations/website_localization.py +401 -0
  118. package/standard-skills/translate-native/integrations/website_localization_api.py +581 -0
  119. package/standard-skills/translate-native/integrations/website_localization_benchmark.py +1885 -0
  120. package/standard-skills/translate-native/integrations/website_localization_benchmark_campaign.py +1772 -0
  121. package/standard-skills/translate-native/integrations/website_localization_benchmark_candidate.py +506 -0
  122. package/standard-skills/translate-native/integrations/website_localization_benchmark_http.py +400 -0
  123. package/standard-skills/translate-native/integrations/website_localization_benchmark_review_store.py +781 -0
  124. package/standard-skills/translate-native/integrations/website_localization_benchmark_reviewer_http.py +500 -0
  125. package/standard-skills/translate-native/integrations/website_localization_benchmark_runtime.py +1107 -0
  126. package/standard-skills/translate-native/integrations/website_localization_benchmark_suite.py +463 -0
  127. package/standard-skills/translate-native/integrations/website_localization_cms.py +2835 -0
  128. package/standard-skills/translate-native/integrations/website_localization_cms_client.py +875 -0
  129. package/standard-skills/translate-native/integrations/website_localization_cms_dispatch.py +805 -0
  130. package/standard-skills/translate-native/integrations/website_localization_cms_http.py +588 -0
  131. package/standard-skills/translate-native/integrations/website_localization_cms_lifecycle_monitor.py +991 -0
  132. package/standard-skills/translate-native/integrations/website_localization_cms_receiver.py +1441 -0
  133. package/standard-skills/translate-native/integrations/website_localization_cms_receiver_runtime.py +414 -0
  134. package/standard-skills/translate-native/integrations/website_localization_cms_receiver_store.py +1073 -0
  135. package/standard-skills/translate-native/integrations/website_localization_cms_removal_dispatch.py +865 -0
  136. package/standard-skills/translate-native/integrations/website_localization_cms_source_client.py +583 -0
  137. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery.py +964 -0
  138. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_runtime.py +665 -0
  139. package/standard-skills/translate-native/integrations/website_localization_cms_source_http.py +1153 -0
  140. package/standard-skills/translate-native/integrations/website_localization_cms_source_runtime.py +675 -0
  141. package/standard-skills/translate-native/integrations/website_localization_cms_source_service.py +1125 -0
  142. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification.py +674 -0
  143. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_http.py +444 -0
  144. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver.py +1469 -0
  145. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver_runtime.py +1142 -0
  146. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_processing_monitor.py +634 -0
  147. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_receiver_client.py +804 -0
  148. package/standard-skills/translate-native/integrations/website_localization_deepl_baseline.py +922 -0
  149. package/standard-skills/translate-native/integrations/website_localization_evidence_http.py +482 -0
  150. package/standard-skills/translate-native/integrations/website_localization_health.py +1541 -0
  151. package/standard-skills/translate-native/integrations/website_localization_health_http.py +372 -0
  152. package/standard-skills/translate-native/integrations/website_localization_http_provider.py +297 -0
  153. package/standard-skills/translate-native/integrations/website_localization_native_reference_http.py +479 -0
  154. package/standard-skills/translate-native/integrations/website_localization_native_reference_intake.py +363 -0
  155. package/standard-skills/translate-native/integrations/website_localization_native_reference_queue.py +1449 -0
  156. package/standard-skills/translate-native/integrations/website_localization_native_reference_store.py +420 -0
  157. package/standard-skills/translate-native/integrations/website_localization_quality_profiles.py +235 -0
  158. package/standard-skills/translate-native/integrations/website_localization_queue.py +671 -0
  159. package/standard-skills/translate-native/integrations/website_localization_receipt_verifier_http.py +516 -0
  160. package/standard-skills/translate-native/integrations/website_localization_release.py +928 -0
  161. package/standard-skills/translate-native/integrations/website_localization_release_coordinator.py +1008 -0
  162. package/standard-skills/translate-native/integrations/website_localization_runner.py +276 -0
  163. package/standard-skills/translate-native/integrations/website_localization_runtime.py +862 -0
  164. package/standard-skills/translate-native/integrations/website_localization_service.py +350 -0
  165. package/standard-skills/translate-native/integrations/website_localization_supervisor.py +511 -0
  166. package/standard-skills/translate-native/integrations/website_localization_worker.py +663 -0
  167. package/standard-skills/translate-native/provenance.json +3 -4
  168. package/standard-skills/translate-native/references/commercial-localization.md +177 -0
  169. package/standard-skills/translate-native/scripts/blun_language_guard.py +7 -1
  170. package/standard-skills/translate-native/scripts/check_commercial_review.py +80 -0
  171. package/standard-skills/translate-native/scripts/commercial_localization_profile.py +333 -0
  172. package/standard-tools/language-guard/LICENSE +21 -0
  173. package/standard-tools/language-guard/VERSION +1 -0
  174. package/standard-tools/language-guard/blun_language_guard.py +7 -1
  175. package/standard-tools/language-guard/check_commercial_review.py +80 -0
  176. package/standard-tools/language-guard/commercial_localization_profile.py +333 -0
  177. package/standard-tools/language-guard/language_gateway.py +62 -0
  178. package/standard-tools/language-guard/pre_output_guard.py +64 -0
  179. package/standard-tools/language-guard/provenance.json +4 -11
  180. package/standard-tools/manifest.json +34 -11
  181. package/telegram-plugin/commands/access.md +2 -10
  182. package/telegram-plugin/dist/bridge.mjs +64041 -687
  183. package/telegram-plugin/dist/mcp-server.mjs +72810 -9027
  184. package/telegram-plugin/dist/noise.mjs +28 -63511
  185. package/agent-spine-plugin/CONTRIBUTING.md +0 -52
  186. package/agent-spine-plugin/SECURITY.md +0 -47
  187. package/agent-spine-plugin/docs/assignment-continuation.md +0 -48
  188. package/agent-spine-plugin/docs/releasing.md +0 -85
  189. package/agent-spine-plugin/docs/structured-completion.md +0 -67
  190. package/bin/abort-listener-policy.cjs +0 -43
  191. package/bin/active-steer-priority-policy.cjs +0 -24
  192. package/bin/agent-api-http-adapter.mjs +0 -446
  193. package/bin/agent-api-private-http-server.mjs +0 -288
  194. package/bin/agent-api-runtime.mjs +0 -252
  195. package/bin/agent-api-service-environment.mjs +0 -236
  196. package/bin/agent-api-service-host.mjs +0 -209
  197. package/bin/agent-api-service-process.mjs +0 -171
  198. package/bin/agent-api-session-registry.mjs +0 -428
  199. package/bin/agent-api-tool-broker.cjs +0 -248
  200. package/bin/agent-api-turn-controller.mjs +0 -461
  201. package/bin/agent-api-usage-journal.cjs +0 -259
  202. package/bin/agent-resume-snapshot.cjs +0 -241
  203. package/bin/agentspine-king-goal-inbox.mjs +0 -111
  204. package/bin/agentspine-king-goal-intake.mjs +0 -106
  205. package/bin/approval-rejection-stop.cjs +0 -15
  206. package/bin/assistant-message-offload-policy.cjs +0 -284
  207. package/bin/baseline-skill-performance-policy.cjs +0 -39
  208. package/bin/bash-search-scope-policy.cjs +0 -49
  209. package/bin/codebase-search-runtime.cjs +0 -23
  210. package/bin/cognitive-action-checkpoint.cjs +0 -1104
  211. package/bin/cognitive-attention-delivery.cjs +0 -76
  212. package/bin/cognitive-attention-policy.cjs +0 -143
  213. package/bin/cognitive-attention-runtime.cjs +0 -91
  214. package/bin/cognitive-context-projection.cjs +0 -73
  215. package/bin/cognitive-cross-portal-acceptance.cjs +0 -443
  216. package/bin/cognitive-effective-view.cjs +0 -77
  217. package/bin/cognitive-focus-projection.cjs +0 -206
  218. package/bin/cognitive-focus-scope.cjs +0 -37
  219. package/bin/cognitive-goal-autostart-policy.cjs +0 -72
  220. package/bin/cognitive-goal-time-trigger-controller.cjs +0 -146
  221. package/bin/cognitive-memory-adapter.cjs +0 -282
  222. package/bin/cognitive-memory-command.cjs +0 -293
  223. package/bin/cognitive-memory-provider.cjs +0 -92
  224. package/bin/cognitive-salience-policy.cjs +0 -159
  225. package/bin/cognitive-state-store.cjs +0 -508
  226. package/bin/cognitive-turn-lifecycle.cjs +0 -624
  227. package/bin/cognitive-work-focus.cjs +0 -180
  228. package/bin/compaction-history-archive.cjs +0 -166
  229. package/bin/compaction-history-startup.cjs +0 -50
  230. package/bin/compaction-model-policy.cjs +0 -31
  231. package/bin/compaction-stage-policy.cjs +0 -21
  232. package/bin/compaction-transaction-policy.cjs +0 -122
  233. package/bin/config-write-dedup-policy.cjs +0 -27
  234. package/bin/context-budget-ledger.cjs +0 -31
  235. package/bin/context-doctor-policy.cjs +0 -70
  236. package/bin/context-insight-policy.cjs +0 -36
  237. package/bin/context-performance-policy.cjs +0 -19
  238. package/bin/context-pressure-policy.cjs +0 -20
  239. package/bin/cron-run-output.cjs +0 -45
  240. package/bin/cron-run-store.cjs +0 -145
  241. package/bin/curiosity-scout-policy.cjs +0 -49
  242. package/bin/default-model-output-budget-policy.cjs +0 -28
  243. package/bin/durable-task-resume-policy.cjs +0 -130
  244. package/bin/durable-task-resume-runtime.cjs +0 -117
  245. package/bin/durable-task-resume-store.cjs +0 -88
  246. package/bin/editable-tool-approval-policy.cjs +0 -540
  247. package/bin/editable-tool-approval-runtime.cjs +0 -99
  248. package/bin/effective-system-prompt-cache-policy.cjs +0 -33
  249. package/bin/error-memory-performance-policy.cjs +0 -113
  250. package/bin/file-observation-policy.cjs +0 -133
  251. package/bin/foreground-output-capture-policy.cjs +0 -41
  252. package/bin/generated-source-health.cjs +0 -142
  253. package/bin/glob-pattern-policy.cjs +0 -13
  254. package/bin/goal-completion-evidence-policy.cjs +0 -120
  255. package/bin/grep-output-limit-policy.cjs +0 -39
  256. package/bin/historical-media-projection-policy.cjs +0 -48
  257. package/bin/history-offload-pressure-policy.cjs +0 -33
  258. package/bin/html-to-research-markdown.cjs +0 -147
  259. package/bin/identity-context-policy.cjs +0 -764
  260. package/bin/identity-journal-policy.cjs +0 -107
  261. package/bin/input-draft-persistence.cjs +0 -77
  262. package/bin/king-tui-function-contract.json +0 -33
  263. package/bin/launcher-restart-policy.cjs +0 -150
  264. package/bin/live-response-repetition-guard.cjs +0 -196
  265. package/bin/llm-config-log-dedup-policy.cjs +0 -76
  266. package/bin/loop-event-record-policy.cjs +0 -174
  267. package/bin/managed-context-startup-policy.cjs +0 -27
  268. package/bin/media-activity-layout-policy.cjs +0 -34
  269. package/bin/media-auto-retrieval-policy.cjs +0 -90
  270. package/bin/media-result-policy.cjs +0 -59
  271. package/bin/micro-compaction-policy.cjs +0 -145
  272. package/bin/mistake-relevance-policy.cjs +0 -319
  273. package/bin/model-retry-progress-policy.cjs +0 -46
  274. package/bin/native-large-file-io.cjs +0 -42
  275. package/bin/native-runtime-cache.cjs +0 -76
  276. package/bin/natural-presence-policy.cjs +0 -28
  277. package/bin/noninteractive-shell-env-policy.cjs +0 -19
  278. package/bin/observer-hooks.cjs +0 -14
  279. package/bin/outbound-claim-provenance.cjs +0 -150
  280. package/bin/oversized-context-offload-policy.cjs +0 -86
  281. package/bin/pending-media-policy.cjs +0 -182
  282. package/bin/pending-token-estimate-policy.cjs +0 -41
  283. package/bin/personal-memory-consent-policy.cjs +0 -72
  284. package/bin/personal-memory-performance-policy.cjs +0 -12
  285. package/bin/personality-choice-policy.cjs +0 -101
  286. package/bin/personality-memory-adapter.cjs +0 -379
  287. package/bin/personality-mode.cjs +0 -46
  288. package/bin/personality-setup-policy.cjs +0 -197
  289. package/bin/proactive-compaction-policy.cjs +0 -25
  290. package/bin/profile-identity-resolution.cjs +0 -136
  291. package/bin/profile-runtime.cjs +0 -318
  292. package/bin/profile-tool-exclusion-policy.cjs +0 -37
  293. package/bin/programmatic-context-isolation.cjs +0 -25
  294. package/bin/programmatic-tool-runtime.mjs +0 -627
  295. package/bin/provider-idle-timeout-policy.cjs +0 -14
  296. package/bin/provider-model-refresh-deadline.cjs +0 -53
  297. package/bin/provider-model-refresh-policy.cjs +0 -107
  298. package/bin/rate-limit-recovery-policy.cjs +0 -47
  299. package/bin/read-batch-policy.cjs +0 -32
  300. package/bin/read-continuation-policy.cjs +0 -59
  301. package/bin/recurring-cron-history-policy.cjs +0 -124
  302. package/bin/relationship-continuity-policy.cjs +0 -143
  303. package/bin/relationship-curiosity-policy.cjs +0 -107
  304. package/bin/relationship-learning-policy.cjs +0 -168
  305. package/bin/release-artifact-freeze-policy.cjs +0 -30
  306. package/bin/reload-plugin-bootstrap.cjs +0 -18
  307. package/bin/reload-queue-policy.cjs +0 -38
  308. package/bin/repeated-assistant-response-policy.cjs +0 -232
  309. package/bin/repeated-injection-projection.cjs +0 -107
  310. package/bin/repeated-user-message-projection.cjs +0 -8
  311. package/bin/research-page-result.cjs +0 -74
  312. package/bin/retry-checkpoint-policy.cjs +0 -13
  313. package/bin/runtime-exit-ledger.cjs +0 -144
  314. package/bin/scoped-cron-run-policy.cjs +0 -358
  315. package/bin/session-checkpoint-policy.cjs +0 -25
  316. package/bin/session-compaction-policy.cjs +0 -84
  317. package/bin/session-replay-policy.cjs +0 -20
  318. package/bin/session-replay-window-policy.cjs +0 -40
  319. package/bin/session-resume-checkpoint.cjs +0 -254
  320. package/bin/session-scrollback-archive.cjs +0 -229
  321. package/bin/skill-activation-performance-policy.cjs +0 -69
  322. package/bin/skill-listing-performance-policy.cjs +0 -92
  323. package/bin/soul-organization-policy.cjs +0 -78
  324. package/bin/soul-preservation-policy.cjs +0 -20
  325. package/bin/startup-preferences.cjs +0 -131
  326. package/bin/streaming-flush-performance-policy.cjs +0 -28
  327. package/bin/structured-agent-swarm-output.cjs +0 -325
  328. package/bin/structured-subagent-output.cjs +0 -252
  329. package/bin/subagent-context-fork-policy.cjs +0 -155
  330. package/bin/subagent-max-tokens-handoff-policy.cjs +0 -69
  331. package/bin/subagent-parent-responsiveness.cjs +0 -19
  332. package/bin/subagent-skill-policy.cjs +0 -206
  333. package/bin/subagent-timeout-policy.cjs +0 -182
  334. package/bin/subagent-tool-policy.cjs +0 -60
  335. package/bin/subagent-usage-rollup-policy.cjs +0 -29
  336. package/bin/system-prompt-context-policy.cjs +0 -124
  337. package/bin/system-prompt-token-cache-policy.cjs +0 -60
  338. package/bin/telegram-addressed-focus.cjs +0 -55
  339. package/bin/telegram-addressed-priority.cjs +0 -12
  340. package/bin/telegram-approval-relay.cjs +0 -290
  341. package/bin/telegram-bot-priority.cjs +0 -17
  342. package/bin/telegram-console-status-policy.cjs +0 -174
  343. package/bin/telegram-context-projection-policy.cjs +0 -141
  344. package/bin/telegram-delivery-lifecycle.cjs +0 -125
  345. package/bin/telegram-direct-focus-policy.cjs +0 -273
  346. package/bin/telegram-mcp-compatibility.cjs +0 -49
  347. package/bin/telegram-media-delivery-policy.cjs +0 -42
  348. package/bin/telegram-private-conversation-policy.cjs +0 -185
  349. package/bin/telegram-queue-handoff-policy.cjs +0 -73
  350. package/bin/telegram-remote-status-policy.cjs +0 -120
  351. package/bin/telegram-session-queue-runtime.mjs +0 -306
  352. package/bin/telegram-text-chunk-policy.cjs +0 -63
  353. package/bin/telegram-truncated-reply-policy.cjs +0 -37
  354. package/bin/telegram-urgent-policy.cjs +0 -45
  355. package/bin/telemetry-spool-policy.cjs +0 -57
  356. package/bin/thinking-activity-status-policy.cjs +0 -132
  357. package/bin/thinking-only-guard.cjs +0 -80
  358. package/bin/todo-list-turn-policy.cjs +0 -131
  359. package/bin/tool-call-loop-policy.cjs +0 -51
  360. package/bin/tool-file-persistence.cjs +0 -141
  361. package/bin/tool-result-offload-policy.cjs +0 -359
  362. package/bin/tool-result-offload-telemetry.cjs +0 -12
  363. package/bin/tool-schema-token-cache-policy.cjs +0 -41
  364. package/bin/tool-stream-preview-policy.cjs +0 -9
  365. package/bin/tui-functional-contract.cjs +0 -55
  366. package/bin/turn-tool-performance-policy.cjs +0 -486
  367. package/bin/usage-cache-efficiency-policy.cjs +0 -26
  368. package/bin/user-home-path-policy.cjs +0 -13
  369. package/bin/user-message-offload-policy.cjs +0 -103
  370. package/bin/user-prompt-hook-origin-policy.cjs +0 -34
  371. package/bin/user-tool-record-policy.cjs +0 -7
  372. package/bin/validated-learning-insight-policy.cjs +0 -58
  373. package/bin/validated-learning-outcome-trace.cjs +0 -107
  374. package/bin/validated-learning-performance-policy.cjs +0 -53
  375. package/bin/validated-learning-signal.cjs +0 -463
  376. package/bin/windows-bash-dialect-policy.cjs +0 -25
  377. package/bin/windows-node-crash-dump.cjs +0 -110
  378. package/bin/write-continuation-policy.cjs +0 -69
  379. package/codebase-index/README.md +0 -82
  380. package/codebase-index/codebase_index.py +0 -470
  381. package/standard-skills/agent-browser/SKILL.md +0 -19
  382. package/standard-skills/agent-browser/references/runtime.md +0 -8
  383. package/standard-skills/blun-session-inspector/SKILL.md +0 -41
  384. package/standard-skills/blun-session-inspector/scripts/inspect-session.cjs +0 -437
  385. package/standard-skills/design-taste-frontend/SKILL.md +0 -1206
  386. package/standard-skills/full-output-enforcement/SKILL.md +0 -49
  387. package/standard-skills/high-end-visual-design/SKILL.md +0 -98
  388. package/standard-skills/image-to-code/SKILL.md +0 -1228
  389. package/standard-skills/industrial-brutalist-ui/SKILL.md +0 -92
  390. package/standard-skills/minimalist-ui/SKILL.md +0 -85
  391. package/standard-skills/motion-design-taste/SKILL.md +0 -74
  392. package/standard-skills/playwright-testing/SKILL.md +0 -19
  393. package/standard-skills/playwright-testing/references/runtime.md +0 -7
  394. package/standard-skills/premortem/SKILL.md +0 -148
  395. package/standard-skills/redesign-existing-projects/SKILL.md +0 -178
  396. package/standard-skills/research-evidence/SKILL.md +0 -39
  397. package/standard-skills/research-evidence/references/evidence-format.md +0 -104
  398. package/standard-skills/research-evidence/scripts/evidence-collection.cjs +0 -260
  399. package/standard-skills/research-evidence/scripts/score-report.cjs +0 -130
  400. package/standard-skills/screenshot-lesen/SKILL.md +0 -52
  401. package/standard-skills/stitch-design-taste/DESIGN.md +0 -121
  402. package/standard-skills/stitch-design-taste/SKILL.md +0 -184
  403. package/standard-skills/telegram-channel/SKILL.md +0 -18
  404. package/standard-skills/telegram-channel/references/runtime.md +0 -7
  405. package/standard-skills/venture-flywheel/SKILL.md +0 -32
  406. package/standard-skills/venture-flywheel/identity/project-identity.cjs +0 -146
  407. package/standard-skills/venture-flywheel/policy/capability-engine.cjs +0 -114
  408. package/standard-skills/venture-flywheel/policy/repository-trust.cjs +0 -229
  409. package/standard-skills/venture-flywheel/references/BEISPIELE-phase0.md +0 -146
  410. package/standard-skills/venture-flywheel/references/CAPABILITY-MAP.md +0 -34
  411. package/standard-skills/venture-flywheel/references/SPEC-phase0-identity-trust.md +0 -77
  412. package/standard-skills/venture-flywheel/references/SPEC-phase0-state-events.md +0 -93
  413. package/standard-skills/venture-flywheel/schemas/capability-decision.schema.json +0 -13
  414. package/standard-skills/venture-flywheel/schemas/execution-event.schema.json +0 -44
  415. package/standard-skills/venture-flywheel/schemas/project-identity.schema.json +0 -32
  416. package/standard-skills/venture-flywheel/schemas/repository-trust.schema.json +0 -57
  417. package/standard-skills/venture-flywheel/schemas/run-transition.schema.json +0 -59
  418. package/standard-skills/venture-flywheel/state/execution-event.cjs +0 -191
  419. package/standard-skills/venture-flywheel/state/task-state-machine.cjs +0 -190
  420. package/standard-skills/web-lesen/SKILL.md +0 -73
  421. package/standard-skills/web-lesen/scripts/crawl_public.py +0 -379
  422. package/standard-skills/windows-mcp/SKILL.md +0 -19
  423. package/standard-skills/windows-mcp/references/runtime.md +0 -9
  424. package/telegram-plugin/DELIVERY.md +0 -36
  425. package/telegram-plugin/bin/telegram-approval-relay.cjs +0 -290
  426. package/telegram-plugin/bin/telegram-console-status-policy.cjs +0 -175
  427. package/telegram-plugin/bin/telegram-delivery-lifecycle.cjs +0 -125
  428. package/telegram-plugin/bin/telegram-direct-reply-policy.cjs +0 -48
  429. package/telegram-plugin/bin/telegram-launcher-status-queue.cjs +0 -122
  430. package/telegram-plugin/bin/telegram-private-conversation-policy.cjs +0 -186
  431. package/telegram-plugin/bin/telegram-remote-status-policy.cjs +0 -121
  432. package/telegram-plugin/bin/telegram-reply-parts.cjs +0 -149
  433. package/telegram-plugin/bin/telegram-text-chunk-policy.cjs +0 -63
  434. package/telegram-plugin/bin/telegram-typing-keepalive.cjs +0 -89
  435. package/telegram-plugin/compat/mcp-server-fa511cd1.mjs +0 -73825
  436. /package/{bin → scripts}/fix-node-pty-perms.js +0 -0
@@ -1,6 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
- import { constants as fsConstants } from "node:fs";
3
- import { mkdir, open } from "node:fs/promises";
2
+ import { mkdir } from "node:fs/promises";
4
3
  import { isAbsolute } from "node:path";
5
4
  import { withOwnedFileLock } from "./owned-file-lock.js";
6
5
  import {
@@ -23,10 +22,12 @@ import { timelineContinuationCapsule, timelineSearchResult } from "./session-tim
23
22
  import { seekTimelineEvidence, verifyTimelineEvent } from "./session-timeline-search.js";
24
23
  import { eventFromTimelineLine, extractTimelineTimestamp } from "./session-timeline-event-extract.js";
25
24
  import { verifiedTimelineEventFromLine } from "./session-timeline-event-extract.js";
26
- import { matchesSourceMetadata, pathMatchesSource } from "./session-timeline-source.js";
25
+ import { pathMatchesSource } from "./session-timeline-source.js";
26
+ import { readRange, unchangedHandle, validatedHandle } from "./session-timeline-source-open.js";
27
27
  import { readTimelineState, saveTimelineState } from "./session-timeline-state.js";
28
28
  import { sessionTimelineRootDigest } from "./session-timeline-root.js";
29
29
  import { priorTimelineHint, selectPriorTimelineSource, timelineSessionReference } from "./session-timeline-prior.js";
30
+ import { crossProviderTimelineEnabled } from "./session-timeline-provider.js";
30
31
 
31
32
  export const SESSION_TIMELINE_SCHEMA = "agentspine.session-timeline/v1";
32
33
  const STATE_SCHEMA = "agentspine.session-timeline-state/v1";
@@ -39,8 +40,7 @@ const MAX_INDEX_BYTES = 16 * 1024 * 1024;
39
40
  const ROOM_BYTES = 1024 * 1024;
40
41
  const EVENT_OUTCOMES = new Set(["pass", "fail", "blocked", "timeout", "error", "skipped"]);
41
42
  const EVENT_LABEL_RE = /^(?:suite-(?:0|[1-9]\d{0,3})|acceptance|audit|npm-check|ci|test)$/;
42
- const EVENT_KEYS = new Set(["nativeMessageId", "id", "at", "offset", "bytes", "sha256", "kind", "outcome", "count", "testLabel", "terms", "authority"]);
43
- const MUTATION_TAILS = new Map();
43
+ const EVENT_KEYS = new Set(["nativeMessageId", "id", "at", "offset", "bytes", "sha256", "kind", "outcome", "count", "testLabel", "nextStepSummary", "terms", "authority"]);
44
44
 
45
45
  function digest(value) { return createHash("sha256").update(value).digest("hex"); }
46
46
  function asDate(value) {
@@ -58,15 +58,26 @@ function validCount(item) {
58
58
  && item.total > 0 && item.value <= item.total);
59
59
  }
60
60
  function validEvent(item) {
61
- return item && typeof item === "object" && hasOnlyKeys(item, EVENT_KEYS)
61
+ const common = item && typeof item === "object" && hasOnlyKeys(item, EVENT_KEYS)
62
62
  && typeof item.id === "string" && TIMELINE_ID_RE.test(item.id) && date(item.at)
63
63
  && Number.isSafeInteger(item.offset) && item.offset >= 0 && Number.isSafeInteger(item.bytes) && item.bytes > 0
64
- && /^[a-f0-9]{64}$/.test(item.sha256 || "") && item.kind === "objective-result"
65
- && EVENT_OUTCOMES.has(item.outcome) && validCount(item.count)
66
- && (item.testLabel === null || typeof item.testLabel === "string" && EVENT_LABEL_RE.test(item.testLabel))
64
+ && /^[a-f0-9]{64}$/.test(item.sha256 || "")
67
65
  && Array.isArray(item.terms) && item.terms.length <= 24 && item.terms.every((term) => /^[\p{L}\p{N}]{3,}$/u.test(term))
68
66
  && (item.nativeMessageId === undefined || typeof item.nativeMessageId === "string" && TIMELINE_ID_RE.test(item.nativeMessageId))
69
67
  && item.authority === AUTHORITY;
68
+ if (!common) return false;
69
+ if (item.kind === "user-message-candidate") return item.outcome === undefined
70
+ && item.count === undefined && item.testLabel === undefined && item.nextStepSummary === undefined
71
+ && item.terms.join(" ") === "user message";
72
+ if (item.kind === "objective-result") {
73
+ return EVENT_OUTCOMES.has(item.outcome) && validCount(item.count)
74
+ && (item.testLabel === null || typeof item.testLabel === "string" && EVENT_LABEL_RE.test(item.testLabel))
75
+ && item.nextStepSummary === undefined;
76
+ }
77
+ return item.kind === "explicit-next-step-correction" && item.outcome === undefined
78
+ && item.count === undefined && item.testLabel === undefined
79
+ && (item.nextStepSummary === undefined || typeof item.nextStepSummary === "string"
80
+ && Boolean(item.nextStepSummary.trim()) && item.nextStepSummary.length <= 500);
70
81
  }
71
82
  function validSource(item) {
72
83
  return item && validTimelineBinding(item.binding) && typeof item.path === "string" && isAbsolute(item.path)
@@ -97,20 +108,6 @@ function saveState(state, path, assertOwned, root, assertStable) {
97
108
  return saveTimelineState({ state, path, root, maximumBytes: MAX_STATE_BYTES, assertOwned, assertStable });
98
109
  }
99
110
 
100
- async function serializeLocalMutation(key, task) {
101
- const previous = MUTATION_TAILS.get(key) || Promise.resolve();
102
- let release;
103
- const current = new Promise(resolve => { release = resolve; });
104
- const tail = previous.catch(() => {}).then(() => current);
105
- MUTATION_TAILS.set(key, tail);
106
- await previous.catch(() => {});
107
- try { return await task(); }
108
- finally {
109
- release();
110
- if (MUTATION_TAILS.get(key) === tail) MUTATION_TAILS.delete(key);
111
- }
112
- }
113
-
114
111
  function status(value, extra = {}) { return { schema: SESSION_TIMELINE_SCHEMA, ...value, ...extra, authority: AUTHORITY }; }
115
112
  function hasExactPrivateTimelineScope(scope) { return scope?.groupId === null; }
116
113
  function sourceFor(state, scope) { return state.sources.find((item) => sameTimelineBinding(item.binding, scope)) || null; }
@@ -204,7 +201,8 @@ export async function sessionTimelineLifecycleHint({
204
201
  const source = sourceFor(state, scoped);
205
202
  if (!source) return status({ status: "unavailable", reason: "timeline-not-registered" });
206
203
  return status({ status: source.indexedBytes >= source.size ? "indexed" : "partial", ...sourceMetadata(source),
207
- priorSessions: priorTimelineHint(state, scoped, (item) => sourceMetadata(item).sourceDigest),
204
+ priorSessions: priorTimelineHint(state, scoped, (item) => sourceMetadata(item).sourceDigest,
205
+ { includePriorProviders: crossProviderTimelineEnabled(environment) }),
208
206
  freshness: "source-not-read", instruction: "Use session_timeline_index before search when this snapshot is partial." });
209
207
  } catch {
210
208
  return status({ status: "unavailable", reason: "timeline-state-unavailable" });
@@ -222,32 +220,14 @@ export async function registerSessionTimelineSource() {
222
220
  function mergeEvents(existing, additions) {
223
221
  const byId = new Map(existing.map((item) => [item.id, item]));
224
222
  for (const item of additions) byId.set(item.id, item);
225
- return [...byId.values()].sort((left, right) => left.at.localeCompare(right.at) || left.offset - right.offset).slice(-MAX_EVENTS);
223
+ const ordered = [...byId.values()].sort((left, right) => left.at.localeCompare(right.at) || left.offset - right.offset);
224
+ const evidence = ordered.filter((item) => item.kind !== "user-message-candidate").slice(-MAX_EVENTS);
225
+ const available = Math.min(256, MAX_EVENTS - evidence.length);
226
+ const feedback = ordered.filter((item) => item.kind === "user-message-candidate")
227
+ .slice(available ? -available : ordered.length);
228
+ return [...evidence, ...feedback].sort((left, right) => left.at.localeCompare(right.at) || left.offset - right.offset);
226
229
  }
227
- async function unchangedHandle(handle, source, hostHome = null) {
228
- try {
229
- const metadata = await handle.stat({ bigint: true });
230
- return matchesSourceMetadata(metadata, source) && await pathMatchesSource(source, hostHome);
231
- } catch { return false; }
232
- }
233
-
234
- async function validatedHandle(source, hostHome = null) {
235
- if (!await pathMatchesSource(source, hostHome)) return { status: "unavailable", reason: "transcript-changed" };
236
- let handle;
237
- try { handle = await open(source.path, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW || 0)); }
238
- catch { return { status: "unavailable", reason: "transcript-changed" }; }
239
- if (!await unchangedHandle(handle, source, hostHome)) {
240
- await handle.close();
241
- return { status: "unavailable", reason: "transcript-changed" };
242
- }
243
- return { status: "open", handle, size: source.size };
244
- }
245
- async function readRange(handle, offset, length) {
246
- const buffer = Buffer.alloc(length);
247
- const { bytesRead } = await handle.read(buffer, 0, length, offset);
248
- return buffer.subarray(0, bytesRead);
249
- }
250
- function parsedEvents(buffer, start, dropFirst = false, host = "claude") {
230
+ function parsedEvents(buffer, start, dropFirst = false, host = "claude", includeUserMessages = false) {
251
231
  const result = [];
252
232
  let index = 0;
253
233
  while (index < buffer.byteLength) {
@@ -256,7 +236,11 @@ function parsedEvents(buffer, start, dropFirst = false, host = "claude") {
256
236
  const line = buffer.subarray(index, end);
257
237
  if (!(dropFirst && index === 0) && line.byteLength) {
258
238
  const event = eventFromTimelineLine(line.toString("utf8"), start + index, AUTHORITY, host);
259
- if (event) result.push(event);
239
+ if (event && (includeUserMessages || event.kind !== "user-message-candidate")) {
240
+ if (event.kind === "explicit-next-step-correction") delete event.nextStepSummary;
241
+ delete event.sourceText;
242
+ result.push(event);
243
+ }
260
244
  }
261
245
  index = end;
262
246
  }
@@ -276,7 +260,8 @@ async function indexRange(source, start, maximum, hostHome = null) {
276
260
  const before = start > 0 ? await readRange(opened.handle, start - 1, 1) : Buffer.alloc(0);
277
261
  if (!await unchangedHandle(opened.handle, source, hostHome)) return { status: "unavailable", reason: "transcript-changed" };
278
262
  return { status: "indexed", next: start + selected.byteLength,
279
- events: parsedEvents(selected, start, start > 0 && before[0] !== 0x0a, source.binding.host), size: opened.size };
263
+ events: parsedEvents(selected, start, start > 0 && before[0] !== 0x0a, source.binding.host,
264
+ Boolean(source.binding.portalRef && source.binding.threadRef)), size: opened.size };
280
265
  } finally { await opened.handle.close(); }
281
266
  }
282
267
 
@@ -286,7 +271,7 @@ async function mutateSource(root, scoped, task) {
286
271
  let names;
287
272
  try { names = await paths(root); }
288
273
  catch { return status({ status: "unavailable", reason: "timeline-state-unavailable" }); }
289
- return serializeLocalMutation(names.lock, () => withOwnedFileLock(names.lock, async ({ assertOwned }) => {
274
+ return withOwnedFileLock(names.lock, async ({ assertOwned }) => {
290
275
  let state;
291
276
  try { state = await readState(names.path, root, names.assertStable); }
292
277
  catch { return status({ status: "unavailable", reason: "timeline-state-unavailable" }); }
@@ -302,7 +287,7 @@ async function mutateSource(root, scoped, task) {
302
287
  return result.status === "indexed" ? status({ status: source.indexedBytes >= source.size ? "indexed"
303
288
  : result.preserveCursor ? "fresh-tail" : "partial", ...sourceMetadata(source), added: result.events.length })
304
289
  : status(result);
305
- }, { assertPath: names.assertStable })).catch(() =>
290
+ }, { assertPath: names.assertStable }).catch(() =>
306
291
  status({ status: "unavailable", reason: "timeline-state-unavailable" }));
307
292
  }
308
293
 
@@ -345,10 +330,11 @@ export async function sessionTimelineStatus({ root, host, sessionId, scope, envi
345
330
  }
346
331
 
347
332
  export async function authorizeSessionTimelineInvocation({
348
- root, host, sessionId, scope, hostHome, tool, request, toolUseId, transportDigest, enrollmentDigest
333
+ root, host, sessionId, scope, hostHome, tool, request, toolUseId, transportDigest, enrollmentDigest,
334
+ environment = process.env
349
335
  }) {
350
336
  const scoped = sessionTimelineBinding({ host, sessionId, scope });
351
- if (!hasExactPrivateTimelineScope(scope) || !completeTimelineBinding(scoped) || !/^(index|search)$/.test(tool || "")) return null;
337
+ if (!hasExactPrivateTimelineScope(scope) || !completeTimelineBinding(scoped) || !/^(index|search|capture)$/.test(tool || "")) return null;
352
338
  if (!hasVerifiedTimelinePrivateScope(scope)) return null;
353
339
  try {
354
340
  await ensureSessionTimelineTrust();
@@ -357,10 +343,15 @@ export async function authorizeSessionTimelineInvocation({
357
343
  const currentSource = sourceFor(state, scoped);
358
344
  if (!currentSource || !await pathMatchesSource(currentSource, hostHome)
359
345
  || !await confirmedSourceEnrollment({ root, scoped, source: currentSource, hostHome })) return null;
360
- const query = tool === "search" ? timelineQuery({ at: request?.at, query: request?.query }) : null;
346
+ const includePriorProviders = request?.includePriorProviders === true;
347
+ if (includePriorProviders && (request?.includePriorSessions !== true || !crossProviderTimelineEnabled(environment))) return null;
348
+ const query = tool !== "index" ? timelineQuery({ at: request?.at, query: request?.query }) : null;
361
349
  const source = request?.includePriorSessions === true
362
- ? selectPriorTimelineSource(state, scoped, { ...query, windowMs: request.windowSeconds * 1000 }) : currentSource;
363
- if (!source || !await pathMatchesSource(source, hostHome)) return null;
350
+ ? selectPriorTimelineSource(state, scoped, { ...query, windowMs: request.windowSeconds * 1000,
351
+ includePriorProviders }) : currentSource;
352
+ const sourceHome = source?.binding.host === scoped.host ? hostHome : source?.profileRoot;
353
+ if (!source || !await pathMatchesSource(source, sourceHome)
354
+ || !await confirmedSourceEnrollment({ root, scoped: source.binding, source, hostHome: sourceHome })) return null;
364
355
  const sourceDigest = sourceMetadata(source).sourceDigest;
365
356
  if (!await timelineTransportEnrollmentMatches({ root, binding: scoped, enrollmentDigest, transportDigest, hostHome })) return null;
366
357
  await issueSessionTimelineInvocation({
@@ -370,17 +361,60 @@ export async function authorizeSessionTimelineInvocation({
370
361
  } catch { return null; }
371
362
  }
372
363
 
373
- function searchResult(source, target, wanted, mode, events, extra = {}) {
364
+ // Reopen only exact, already-captured sources needed by one interpretation.
365
+ // The outer capture invocation remains the sole public gate.
366
+ export async function reverifyTimelineFeedbackSources({
367
+ root, host, sessionId, scope, references, includePriorProviders = false,
368
+ environment = process.env, hostHome = null
369
+ }) {
370
+ const scoped = sessionTimelineBinding({ host, sessionId, scope });
371
+ if (!completeTimelineBinding(scoped) || !hasVerifiedTimelinePrivateScope(scope)
372
+ || !Array.isArray(references) || !references.length || references.length > 3
373
+ || includePriorProviders && !crossProviderTimelineEnabled(environment)) return null;
374
+ try {
375
+ await ensureSessionTimelineTrust();
376
+ const names = await paths(root);
377
+ const state = await readState(names.path, root, names.assertStable);
378
+ const fields = ["entityId", "userId", "tenantId", "projectId", "taskId", "groupId", "portalRef", "threadRef"];
379
+ const eligible = state.sources.filter((source) => fields.every((key) =>
380
+ (source.binding[key] ?? null) === (scoped[key] ?? null))
381
+ && (includePriorProviders || source.binding.host === scoped.host));
382
+ const verified = [];
383
+ for (const reference of references) {
384
+ const source = eligible.find((item) => item.binding.host === reference.sourceProvider
385
+ && timelineSessionReference(item.binding) === reference.sessionRef
386
+ && sourceMetadata(item).sourceDigest === reference.sourceDigest);
387
+ const sourceHome = source?.binding.host === scoped.host ? hostHome : source?.profileRoot;
388
+ const indexed = source?.events.find((item) => item.id === reference.eventId
389
+ && item.sha256 === reference.messageDigest && item.at === reference.observedAt);
390
+ if (!source || !indexed || reference.messageRef !== reference.eventId
391
+ || !await confirmedSourceEnrollment({ root, scoped: source.binding, source, hostHome: sourceHome })) return null;
392
+ const opened = await validatedHandle(source, sourceHome);
393
+ if (opened.status !== "open") return null;
394
+ try {
395
+ const current = await verifyTimelineEvent({ handle: opened.handle, event: indexed, readRange, digest,
396
+ eventFromLine: (line, offset) => verifiedTimelineEventFromLine(line, offset, AUTHORITY, source.binding.host) });
397
+ if (!current || current.kind !== "user-message-candidate"
398
+ || !await unchangedHandle(opened.handle, source, sourceHome)) return null;
399
+ verified.push({ ...current, sourceDigest: reference.sourceDigest,
400
+ sourceProvider: reference.sourceProvider, sessionRef: reference.sessionRef });
401
+ } finally { await opened.handle.close(); }
402
+ }
403
+ return verified;
404
+ } catch { return null; }
405
+ }
406
+
407
+ function searchResult(source, target, wanted, mode, events, extra = {}, includeMessageDigest = false) {
374
408
  const index = sourceMetadata(source);
375
409
  return timelineSearchResult({ sourceDigest: index.sourceDigest, sessionRef: timelineSessionReference(source.binding),
376
- target, wanted, mode, events, index,
377
- roomBytes: ROOM_BYTES, authority: AUTHORITY, extra });
410
+ sourceProvider: source.binding.host, binding: source.binding, target, wanted, mode, events, index,
411
+ roomBytes: ROOM_BYTES, authority: AUTHORITY, extra, includeMessageDigest });
378
412
  }
379
413
 
380
414
  export async function searchSessionTimeline({
381
415
  root, host, sessionId, scope, at, query, windowSeconds = undefined,
382
- includePriorSessions = false,
383
- invocationRequest = null, transportDigest = null, enrollmentDigest = null, hostHome = null
416
+ includePriorSessions = false, includePriorProviders = false, environment = process.env,
417
+ invocationRequest = null, invocationTool = "search", transportDigest = null, enrollmentDigest = null, hostHome = null
384
418
  }) {
385
419
  const scoped = sessionTimelineBinding({ host, sessionId, scope });
386
420
  if (!hasExactPrivateTimelineScope(scope) || !hasVerifiedTimelinePrivateScope(scope) || !completeTimelineBinding(scoped)) {
@@ -392,6 +426,10 @@ export async function searchSessionTimeline({
392
426
  throw new Error("timeline window is invalid");
393
427
  }
394
428
  if (typeof includePriorSessions !== "boolean") throw new Error("prior session selection is invalid");
429
+ if (typeof includePriorProviders !== "boolean") throw new Error("prior provider selection is invalid");
430
+ if (includePriorProviders && (!includePriorSessions || !crossProviderTimelineEnabled(environment))) {
431
+ return { blocked: true, reason: "cross-provider timeline recall is unavailable", authority: AUTHORITY };
432
+ }
395
433
  try { await ensureSessionTimelineTrust(); }
396
434
  catch { return { blocked: true, reason: "session timeline is unavailable", authority: AUTHORITY }; }
397
435
  let names; let state;
@@ -406,23 +444,28 @@ export async function searchSessionTimeline({
406
444
  return { blocked: true, reason: "session timeline is unavailable", authority: AUTHORITY };
407
445
  }
408
446
  const source = includePriorSessions
409
- ? selectPriorTimelineSource(state, scoped, { target, wanted, windowMs: boundedWindowSeconds * 1000 })
447
+ ? selectPriorTimelineSource(state, scoped, { target, wanted, windowMs: boundedWindowSeconds * 1000,
448
+ includePriorProviders })
410
449
  : currentSource;
411
- if (!source || !await pathMatchesSource(source, hostHome)) {
450
+ const sourceHome = source?.binding.host === scoped.host ? hostHome : source?.profileRoot;
451
+ if (!source || !await pathMatchesSource(source, sourceHome)
452
+ || !await confirmedSourceEnrollment({ root, scoped: source.binding, source, hostHome: sourceHome })) {
412
453
  return { blocked: true, reason: "session timeline is unavailable", authority: AUTHORITY };
413
454
  }
414
455
  const sourceDigest = sourceMetadata(source).sourceDigest;
415
- if (!invocationRequest || !await timelineTransportEnrollmentMatches({ root, binding: scoped, enrollmentDigest, transportDigest, hostHome })
416
- || !await consumeSessionTimelineInvocation({ root, tool: "search", binding: scoped, sourceDigest,
456
+ if (!/^(search|capture)$/.test(invocationTool) || !invocationRequest
457
+ || !await timelineTransportEnrollmentMatches({ root, binding: scoped, enrollmentDigest, transportDigest, hostHome })
458
+ || !await consumeSessionTimelineInvocation({ root, tool: invocationTool, binding: scoped, sourceDigest,
417
459
  request: invocationRequest, transportDigest })) {
418
460
  return { blocked: true, reason: "session timeline invocation is unavailable", authority: AUTHORITY };
419
461
  }
420
462
  const indexed = rankTimelineEvents(source.events
421
463
  .filter((event) => matchesTimelineEvent(event, wanted, target, boundedWindowSeconds * 1000)), wanted, target).slice(0, 8);
422
464
  if (includePriorSessions && !indexed.length) {
423
- return searchResult(source, target, wanted, "prior-index", [], { priorSession: true });
465
+ return searchResult(source, target, wanted, "prior-index", [], { priorSession: true,
466
+ priorProvider: source.binding.host !== scoped.host }, invocationTool === "capture");
424
467
  }
425
- const opened = await validatedHandle(source, hostHome);
468
+ const opened = await validatedHandle(source, sourceHome);
426
469
  if (opened.status !== "open") return { blocked: true, reason: opened.reason, authority: AUTHORITY };
427
470
  try {
428
471
  const verified = [];
@@ -433,21 +476,25 @@ export async function searchSessionTimeline({
433
476
  verified.push(current);
434
477
  }
435
478
  if (verified.length) {
436
- if (!await unchangedHandle(opened.handle, source, hostHome)) return { blocked: true, reason: "transcript-changed", authority: AUTHORITY };
479
+ if (!await unchangedHandle(opened.handle, source, sourceHome)) return { blocked: true, reason: "transcript-changed", authority: AUTHORITY };
437
480
  return searchResult(source, target, wanted, includePriorSessions ? "prior-verified-index" : "verified-index", verified,
438
- includePriorSessions ? { priorSession: true } : {});
481
+ includePriorSessions ? { priorSession: true, priorProvider: source.binding.host !== scoped.host } : {},
482
+ invocationTool === "capture");
439
483
  }
440
- if (!target) return searchResult(source, target, wanted, "verified-index", []);
484
+ if (!target) return searchResult(source, target, wanted, "verified-index", [], {}, invocationTool === "capture");
441
485
  const sought = await seekTimelineEvidence({ handle: opened.handle, size: opened.size, target, wanted,
442
486
  windowMs: boundedWindowSeconds * 1000, readRange,
443
487
  eventFromLine: (line, offset) => verifiedTimelineEventFromLine(line, offset, AUTHORITY, source.binding.host),
444
488
  extractTimestamp: extractTimelineTimestamp,
445
489
  matches: matchesTimelineEvent, rank: rankTimelineEvents });
446
490
  if (sought.status === "searched") {
447
- if (!await unchangedHandle(opened.handle, source, hostHome)) return { blocked: true, reason: "transcript-changed", authority: AUTHORITY };
448
- return searchResult(source, target, wanted, "timestamp-seek", sought.events, { budgetExhausted: sought.budgetExhausted });
491
+ if (!await unchangedHandle(opened.handle, source, sourceHome)) return { blocked: true, reason: "transcript-changed", authority: AUTHORITY };
492
+ return searchResult(source, target, wanted, "timestamp-seek", sought.events,
493
+ { budgetExhausted: sought.budgetExhausted }, invocationTool === "capture");
494
+ }
495
+ if (sought.status === "out-of-range") {
496
+ return searchResult(source, target, wanted, "timestamp-seek", [], {}, invocationTool === "capture");
449
497
  }
450
- if (sought.status === "out-of-range") return searchResult(source, target, wanted, "timestamp-seek", []);
451
498
  return { blocked: true, reason: sought.reason, authority: AUTHORITY };
452
499
  } finally { await opened.handle.close(); }
453
500
  }
@@ -255,7 +255,7 @@ async function claudeSources({ cwd, projectRoot, configDir, input, env, registry
255
255
  const value = await readJsonObject(path);
256
256
  if (typeof value?.autoMemoryDirectory === "string") {
257
257
  const expanded = expandHome(value.autoMemoryDirectory, homedir());
258
- if (!isAbsolute(expanded)) throw new Error("Claude autoMemoryDirectory must be absolute or home-relative");
258
+ if (!isAbsolute(expanded)) throw new Error("autoMemoryDirectory must be absolute or home-relative");
259
259
  memoryRoot = await existingDirectory(expanded);
260
260
  memoryProvenance = "autoMemoryDirectory";
261
261
  }
@@ -358,7 +358,8 @@ export async function resolveHostSourceCatalog({ host, cwd = process.cwd(), inpu
358
358
  let hostDetails = {};
359
359
  let rootResolution = "explicit-root";
360
360
  if (host === "codex") {
361
- const codexHome = env.CODEX_HOME || env.BLUN_HOME || join(homedir(), ".codex");
361
+ const codexHome = env.BLUN_PLUGIN_ROOT && env.BLUN_HOME
362
+ ? env.BLUN_HOME : env.CODEX_HOME || env.BLUN_HOME || join(homedir(), ".codex");
362
363
  hostHome = await existingDirectory(resolve(codexHome)) || resolve(codexHome);
363
364
  const config = await codexConfig(hostHome);
364
365
  if (env.AGENTSPINE_ROOT) projectRoot = await canonicalPath(env.AGENTSPINE_ROOT);
@@ -1,3 +1,7 @@
1
+ import {
2
+ relevantUserFeedback, relevantUserFeedbackInterpretation
3
+ } from "./timeline-user-feedback.js";
4
+
1
5
  const MAX_ITEMS = 6;
2
6
  const STOP_WORDS = new Set([
3
7
  "and", "are", "das", "der", "die", "eine", "einer", "for", "from", "für", "ist",
@@ -39,7 +43,7 @@ function empty(taskId, status) {
39
43
 
40
44
  export function taskKnowledgeContext(entries, { taskId = null, continuation, maxItems = MAX_ITEMS } = {}) {
41
45
  if (!taskId) return empty(null, "not-requested");
42
- const task = continuation.tasks.find((item) => item.taskId === taskId);
46
+ const task = [...continuation.tasks, ...continuation.terminal].find((item) => item.taskId === taskId);
43
47
  if (!task) {
44
48
  const terminal = continuation.terminal.some((item) => item.taskId === taskId);
45
49
  return empty(taskId, terminal ? "terminal" : "unavailable");
@@ -50,7 +54,23 @@ export function taskKnowledgeContext(entries, { taskId = null, continuation, max
50
54
  const minimumMatches = Math.min(2, queryTerms.length);
51
55
  const seen = new Set();
52
56
  const ranked = [];
57
+ const feedbackIds = entries.filter((entry) => relevantUserFeedback(entry, task))
58
+ .map((entry) => entry.id).sort();
53
59
  for (const entry of entries) {
60
+ if (relevantUserFeedback(entry, task)) {
61
+ ranked.push({ entry, matchedTerms: [], score: 100_000 });
62
+ continue;
63
+ }
64
+ if (relevantUserFeedbackInterpretation(entry, task)) {
65
+ const sourceIds = [...(entry.value.sourceBindings?.map((item) => item.feedbackAssertionId)
66
+ || [entry.value.sourceFeedbackAssertionId])].sort();
67
+ if (sourceIds.length === feedbackIds.length
68
+ && sourceIds.every((id, index) => id === feedbackIds[index])) {
69
+ ranked.push({ entry, matchedTerms: [], score: 99_999 });
70
+ }
71
+ continue;
72
+ }
73
+ if (task.status === "completed") continue;
54
74
  if (entry.status !== "confirmed" || !(entry.kind in KIND_WEIGHT)) continue;
55
75
  const matchedTerms = terms(entryText(entry)).filter((item) => querySet.has(item));
56
76
  if (matchedTerms.length < minimumMatches) continue;
@@ -63,6 +83,7 @@ export function taskKnowledgeContext(entries, { taskId = null, continuation, max
63
83
  ranked.sort((left, right) => right.score - left.score
64
84
  || right.entry.observedAt.localeCompare(left.entry.observedAt)
65
85
  || left.entry.id.localeCompare(right.entry.id));
86
+ if (task.status === "completed" && !ranked.length) return empty(taskId, "terminal");
66
87
  const limit = Math.min(MAX_ITEMS, Math.max(1, maxItems));
67
88
  return {
68
89
  schema: "agentspine.task-knowledge-context/v1",
@@ -0,0 +1,100 @@
1
+ import { createHash } from "node:crypto";
2
+ import { recordWorldAssertion, worldContext } from "./world-model.js";
3
+ import { TIMELINE_OUTCOME_CONTRACT_SCHEMA } from "./world-knowledge.js";
4
+
5
+ const CONTINUATION_SCHEMA = "agentspine.task-continuation/v1";
6
+
7
+ function digest(value) {
8
+ return createHash("sha256").update(value).digest("hex");
9
+ }
10
+
11
+ function sameStep(left, right) {
12
+ return Boolean(left && right && left.id === right.id && left.summary === right.summary);
13
+ }
14
+
15
+ function measuredResult(event, contract) {
16
+ if (event.testLabel !== contract.measurement.testLabel) return null;
17
+ const expectedTotal = contract.measurement.total;
18
+ if ((expectedTotal === null) !== (event.count === null)
19
+ || (event.count && event.count.total !== expectedTotal)) return null;
20
+ const succeeded = event.outcome === "pass"
21
+ && (event.count === null || event.count.value === contract.measurement.successCount);
22
+ const result = succeeded ? "passed"
23
+ : event.outcome === "blocked" || event.outcome === "skipped" ? "blocked" : "failed";
24
+ return { succeeded, result };
25
+ }
26
+
27
+ function response(status, reason = null, assertion = null) {
28
+ return { schema: "agentspine.timeline-continuation-update/v1", status, reason, assertion,
29
+ completionVerified: assertion?.value?.status === "completed", automaticRetry: false,
30
+ authority: "context-only" };
31
+ }
32
+
33
+ export async function updateTimelineContinuationFromObjective({ root, scope, event, now }) {
34
+ try {
35
+ const context = await worldContext({ root, subjectId: scope.currentTaskId,
36
+ projectId: scope.projectId, groupId: null, includePrivate: true, includeKnowledgeHistory: true,
37
+ continuationTaskId: scope.currentTaskId, portalRef: scope.portalRef, threadRef: scope.threadRef,
38
+ maxItems: 100, now });
39
+ const continuations = context.knowledge.current.filter((item) => item.subjectId === scope.currentTaskId
40
+ && item.predicate === "task.continuation" && item.value?.schema === CONTINUATION_SCHEMA);
41
+ const replay = continuations.find((item) => item.status === "confirmed"
42
+ && item.source.id === event.id && item.value.lastVerifiedStep?.evidenceId === event.id);
43
+ if (replay) return response("duplicate", null, replay);
44
+ if (continuations.length !== 1 || continuations[0].status !== "confirmed") {
45
+ return response("unavailable", continuations.length ? "timeline-continuation-conflicted"
46
+ : "timeline-continuation-missing");
47
+ }
48
+ const current = continuations[0];
49
+ const contracts = context.knowledge.current.filter((item) => item.subjectId === scope.currentTaskId
50
+ && item.predicate === "task.timeline-outcome-contract"
51
+ && item.value?.schema === TIMELINE_OUTCOME_CONTRACT_SCHEMA);
52
+ if (contracts.length !== 1 || contracts[0].status !== "confirmed") {
53
+ return response("unavailable", contracts.length ? "timeline-outcome-contract-conflicted"
54
+ : "timeline-outcome-contract-missing");
55
+ }
56
+ const contract = contracts[0];
57
+ if (!sameStep(current.value.nextStep, contract.value.step)) {
58
+ return response("unavailable", "timeline-outcome-contract-superseded");
59
+ }
60
+ const eventTime = new Date(event.at).getTime();
61
+ if (eventTime < new Date(contract.recordedAt).getTime()) {
62
+ return response("unavailable", "timeline-outcome-contract-postdated");
63
+ }
64
+ if (eventTime < new Date(current.observedAt).getTime()
65
+ || eventTime < new Date(contract.observedAt).getTime()) {
66
+ return response("unavailable", "timeline-outcome-event-stale");
67
+ }
68
+ const measured = measuredResult(event, contract.value);
69
+ if (!measured) return response("unavailable", "timeline-outcome-contract-mismatch");
70
+ const value = structuredClone(current.value);
71
+ value.lastVerifiedStep = {
72
+ id: contract.value.step.id, summary: contract.value.step.summary, result: measured.result,
73
+ evidenceId: event.id, evidenceDigest: event.messageDigest,
74
+ observedAt: new Date(event.at).toISOString(), sessionRef: event.sessionRef,
75
+ messageRef: event.messageRef
76
+ };
77
+ if (measured.succeeded) {
78
+ value.status = contract.value.onSuccess.status;
79
+ value.nextStep = structuredClone(contract.value.onSuccess.nextStep);
80
+ if (value.status === "completed" && value.openQuestions.length) {
81
+ return response("unavailable", "timeline-outcome-completion-questions-open");
82
+ }
83
+ }
84
+ const material = [scope.currentTaskId, scope.projectId, scope.portalRef, scope.threadRef,
85
+ contract.id, current.id, event.id, event.messageDigest].join("\0");
86
+ const recorded = await recordWorldAssertion({ root,
87
+ id: `assertion:timeline-continuation-${digest(material).slice(0, 32)}`,
88
+ subjectId: scope.currentTaskId, predicate: "task.continuation", value,
89
+ evidenceKind: "objective-measurement", evidenceId: event.id,
90
+ evidenceDigest: event.messageDigest, observedAt: new Date(event.at).toISOString(),
91
+ projectId: scope.projectId, groupId: null, privacy: "private", knowledgeKind: "task-state",
92
+ sessionRef: event.sessionRef, messageRef: event.messageRef,
93
+ supersedes: [current.id], requireActiveSupersedes: true,
94
+ reason: `The source-verified timeline result matched pre-existing contract ${contract.id}.`,
95
+ portalRef: scope.portalRef, threadRef: scope.threadRef, now });
96
+ return response(recorded.status === "duplicate" ? "duplicate" : "updated", null, recorded.assertion);
97
+ } catch {
98
+ return response("unavailable", "timeline-continuation-update-unavailable");
99
+ }
100
+ }
@@ -1,13 +1,17 @@
1
1
  import { canonicalPath } from "./paths.js";
2
2
  import { resolveHostSourceCatalog } from "./source-roots.js";
3
3
  import { authorizeSessionTimelineInvocation } from "./session-timeline.js";
4
- import { timelineInvocationRequest, timelineToolKind } from "./mcp-timeline-tools.js";
4
+ import {
5
+ timelineInterpretationRequest, timelineInvocationRequest, timelineToolKind
6
+ } from "./mcp-timeline-tools.js";
5
7
  import { gatewayEnvironmentContext, hookDeliveryId, hostFromInput, sessionId } from "./hook-context.js";
6
8
  import { blockedHookOutput } from "./hook-output.js";
7
9
  import { sessionTimelineBinding } from "./session-timeline-contract.js";
8
10
  import { resolvePrivateSessionTimelineEnrollment } from "./session-timeline-enrollment.js";
9
11
  import { timelineTransportDigest } from "./session-timeline-transport.js";
10
- import { runtimeHostForTimeline, timelineHostForRuntime, timelineHostHome } from "./session-timeline-provider.js";
12
+ import {
13
+ crossProviderTimelineEnabled, runtimeHostForTimeline, timelineHostForRuntime, timelineHostHome
14
+ } from "./session-timeline-provider.js";
11
15
 
12
16
  function plainObject(value) {
13
17
  return value && typeof value === "object" && !Array.isArray(value)
@@ -32,6 +36,8 @@ function gatewayMatchesEnrollment(gateway, binding) {
32
36
  ];
33
37
  return gateway.host === runtimeHostForTimeline(binding.host)
34
38
  && fields.slice(1).every(([gatewayField, bindingField]) => gateway[gatewayField] === binding[bindingField])
39
+ && (gateway.portalRef ?? null) === (binding.portalRef ?? null)
40
+ && (gateway.threadRef ?? null) === (binding.threadRef ?? null)
35
41
  && process.env.AGENTSPINE_USER_ID === binding.userId && process.env.AGENTSPINE_TENANT_ID === binding.tenantId;
36
42
  }
37
43
 
@@ -39,6 +45,7 @@ function bindingScope(binding, enrollment) {
39
45
  return { entityId: binding.entityId, userId: binding.userId, tenantId: binding.tenantId,
40
46
  projectId: binding.projectId, groupId: null, currentTaskId: binding.taskId,
41
47
  goalId: binding.goalId, goalStepId: binding.goalStepId,
48
+ portalRef: binding.portalRef ?? null, threadRef: binding.threadRef ?? null,
42
49
  timelineVisibility: enrollment.timelineVisibility };
43
50
  }
44
51
 
@@ -73,6 +80,8 @@ function matchesScopeBinding(args, binding) {
73
80
  && scopeClaimsMatch(args, ["taskId", "task_id", "currentTaskId", "current_task_id"], binding.taskId)
74
81
  && scopeClaimsMatch(args, ["goalId", "goal_id"], binding.goalId)
75
82
  && scopeClaimsMatch(args, ["goalStepId", "goal_step_id"], binding.goalStepId)
83
+ && scopeClaimsMatch(args, ["portalRef", "portal_ref"], binding.portalRef ?? null)
84
+ && scopeClaimsMatch(args, ["threadRef", "thread_ref"], binding.threadRef ?? null)
76
85
  && noScopeClaims(args, ["groupId", "group_id"]);
77
86
  }
78
87
 
@@ -90,7 +99,7 @@ function matchesHostBinding(input, binding) {
90
99
  "transportDigest", "transport_digest", "enrollmentDigest", "enrollment_digest"]);
91
100
  }
92
101
 
93
- function claimedScope(input) {
102
+ function claimedScope(input, gateway) {
94
103
  const claim = (keys) => {
95
104
  const values = scopeValues(input, keys).filter((value) => !absent(value));
96
105
  return values.length && values.every((value) => value === values[0]) ? values[0] : null;
@@ -99,7 +108,8 @@ function claimedScope(input) {
99
108
  entityId: claim(["entityId", "entity_id"]), userId: claim(["userId", "user_id"]),
100
109
  tenantId: claim(["tenantId", "tenant_id"]), projectId: claim(["projectId", "project_id"]),
101
110
  groupId: claim(["groupId", "group_id"]), currentTaskId: claim(["taskId", "task_id", "currentTaskId", "current_task_id"]),
102
- goalId: claim(["goalId", "goal_id"]), goalStepId: claim(["goalStepId", "goal_step_id"])
111
+ goalId: claim(["goalId", "goal_id"]), goalStepId: claim(["goalStepId", "goal_step_id"]),
112
+ portalRef: gateway?.portalRef ?? null, threadRef: gateway?.threadRef ?? null
103
113
  };
104
114
  }
105
115
 
@@ -113,10 +123,19 @@ async function matchesRoot(value, root) {
113
123
  function requestInput(tool, args) {
114
124
  if (tool === "index") return args.maxBytes === undefined ? {} : { maxBytes: args.maxBytes };
115
125
  if (args.at === undefined && args.query === undefined) return null;
126
+ if (tool === "capture" && !/^timeline-event:[a-f0-9]{32}$/.test(args.eventId || "")) return null;
116
127
  if (args.includePriorSessions !== undefined && typeof args.includePriorSessions !== "boolean") return null;
117
- return { ...(args.at === undefined ? {} : { at: args.at }), ...(args.query === undefined ? {} : { query: args.query }),
128
+ if (args.includePriorProviders !== undefined && typeof args.includePriorProviders !== "boolean") return null;
129
+ if (args.includePriorProviders === true && args.includePriorSessions !== true) return null;
130
+ const interpretation = tool === "capture" && args.interpretation !== undefined
131
+ ? timelineInterpretationRequest(args.interpretation) : null;
132
+ if (tool === "capture" && args.interpretation !== undefined && !interpretation) return null;
133
+ return { ...(tool === "capture" ? { eventId: args.eventId,
134
+ ...(interpretation ? { interpretation } : {}) } : {}),
135
+ ...(args.at === undefined ? {} : { at: args.at }), ...(args.query === undefined ? {} : { query: args.query }),
118
136
  ...(args.windowSeconds === undefined ? {} : { windowSeconds: args.windowSeconds }),
119
- ...(args.includePriorSessions === undefined ? {} : { includePriorSessions: args.includePriorSessions }) };
137
+ ...(args.includePriorSessions === undefined ? {} : { includePriorSessions: args.includePriorSessions }),
138
+ ...(args.includePriorProviders === undefined ? {} : { includePriorProviders: args.includePriorProviders }) };
120
139
  }
121
140
 
122
141
  function denied(reason) {
@@ -155,7 +174,7 @@ export async function runTimelineToolGuard(input) {
155
174
  const hostSession = sessionId(input);
156
175
  const gateway = gatewayEnvironmentContext();
157
176
  if (rawGroupClaim(input, gateway)) return denied("AgentSpine session timeline is unavailable for group-scoped activity.");
158
- const provisionalBinding = sessionTimelineBinding({ host, sessionId: hostSession, scope: claimedScope(input) });
177
+ const provisionalBinding = sessionTimelineBinding({ host, sessionId: hostSession, scope: claimedScope(input, gateway) });
159
178
  const transportDigest = timelineTransportDigest({ root, binding: provisionalBinding });
160
179
  if (!transportDigest) {
161
180
  return denied("AgentSpine session timeline requires a locally configured per-session transport capability.");
@@ -179,11 +198,15 @@ export async function runTimelineToolGuard(input) {
179
198
  const scope = bindingScope(enrollment.binding, enrollment);
180
199
  const requestFields = requestInput(tool, input.tool_input);
181
200
  if (!requestFields) return denied("AgentSpine session timeline search needs an exact time or a concrete query.");
201
+ if (requestFields.includePriorProviders && !crossProviderTimelineEnabled(process.env)) {
202
+ return denied("AgentSpine cross-provider timeline recall is not enabled by this local host.");
203
+ }
182
204
  if (input.tool_input.host && input.tool_input.host !== host) return denied("Timeline provider binding mismatch.");
183
205
  const updatedInput = { ...requestFields, ...(["codex", "king"].includes(host) ? { host } : {}), root, sessionId: enrollment.binding.sessionId,
184
206
  entityId: enrollment.binding.entityId, userId: enrollment.binding.userId, tenantId: enrollment.binding.tenantId,
185
207
  projectId: enrollment.binding.projectId, taskId: enrollment.binding.taskId, groupId: null,
186
208
  goalId: enrollment.binding.goalId, goalStepId: enrollment.binding.goalStepId,
209
+ portalRef: enrollment.binding.portalRef ?? null, threadRef: enrollment.binding.threadRef ?? null,
187
210
  timelineVisibility: enrollment.timelineVisibility, enrollmentDigest: enrollment.enrollmentDigest };
188
211
  const request = timelineInvocationRequest(tool, updatedInput, root);
189
212
  const authorization = await authorizeSessionTimelineInvocation({