blun-king-cli 9.1.566 → 9.1.569

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 (262) hide show
  1. package/agent-spine-plugin/.claude-plugin/marketplace.json +1 -1
  2. package/agent-spine-plugin/.claude-plugin/plugin.json +1 -1
  3. package/agent-spine-plugin/.codex-plugin/plugin.json +2 -1
  4. package/agent-spine-plugin/CHANGELOG.md +1581 -0
  5. package/agent-spine-plugin/README.md +30 -4
  6. package/agent-spine-plugin/blun.plugin.json +3 -3
  7. package/agent-spine-plugin/docs/acceptance.md +61 -0
  8. package/agent-spine-plugin/docs/assignment-continuation.md +48 -0
  9. package/agent-spine-plugin/docs/host-integration.md +178 -0
  10. package/agent-spine-plugin/docs/preflight-recall.md +69 -0
  11. package/agent-spine-plugin/docs/preservation-contract.md +53 -0
  12. package/agent-spine-plugin/docs/quality-gates.md +50 -0
  13. package/agent-spine-plugin/docs/releasing.md +85 -0
  14. package/agent-spine-plugin/docs/session-timeline.md +251 -0
  15. package/agent-spine-plugin/docs/source-roots.md +113 -0
  16. package/agent-spine-plugin/docs/structured-completion.md +67 -0
  17. package/agent-spine-plugin/docs/world-model.md +94 -0
  18. package/agent-spine-plugin/hooks/codex.json +2 -2
  19. package/agent-spine-plugin/hooks/hooks.json +1 -1
  20. package/agent-spine-plugin/hooks/version.json +1 -1
  21. package/agent-spine-plugin/package.json +13 -3
  22. package/agent-spine-plugin/scripts/check-codex-install.js +226 -0
  23. package/agent-spine-plugin/scripts/check-hosts.js +14 -5
  24. package/agent-spine-plugin/scripts/check-install-hook.js +226 -0
  25. package/agent-spine-plugin/scripts/check-install-selfstarter.js +154 -0
  26. package/agent-spine-plugin/scripts/check-install.js +478 -0
  27. package/agent-spine-plugin/scripts/check-line-budget.js +58 -0
  28. package/agent-spine-plugin/scripts/check-syntax.js +29 -0
  29. package/agent-spine-plugin/scripts/github-actions.js +11 -0
  30. package/agent-spine-plugin/scripts/hermetic-process.js +183 -0
  31. package/agent-spine-plugin/scripts/release-check.js +145 -0
  32. package/agent-spine-plugin/scripts/run-acceptance.js +19 -0
  33. package/agent-spine-plugin/scripts/run-checks.js +47 -0
  34. package/agent-spine-plugin/scripts/run-tests-hermetic.js +89 -0
  35. package/agent-spine-plugin/skills/agent-spine/SKILL.md +16 -2
  36. package/agent-spine-plugin/spine-example/1-identity.md +12 -0
  37. package/agent-spine-plugin/spine-example/2-voice.md +6 -0
  38. package/agent-spine-plugin/spine-example/3-conduct.md +8 -0
  39. package/agent-spine-plugin/spine-example/4-history.md +4 -0
  40. package/agent-spine-plugin/src/cli-agent.js +296 -0
  41. package/agent-spine-plugin/src/cli-attention.js +95 -0
  42. package/agent-spine-plugin/src/cli-autonomy.js +36 -0
  43. package/agent-spine-plugin/src/cli-common.js +71 -0
  44. package/agent-spine-plugin/src/cli-continuity.js +116 -0
  45. package/agent-spine-plugin/src/cli-core.js +128 -0
  46. package/agent-spine-plugin/src/cli-diagnostics.js +291 -0
  47. package/agent-spine-plugin/src/cli-host.js +21 -0
  48. package/agent-spine-plugin/src/cli-learning.js +305 -0
  49. package/agent-spine-plugin/src/cli-premortem.js +16 -0
  50. package/agent-spine-plugin/src/cli-sharing.js +230 -0
  51. package/agent-spine-plugin/src/cli.js +40 -1350
  52. package/agent-spine-plugin/src/codex-reader-launcher.js +182 -0
  53. package/agent-spine-plugin/src/hook.js +185 -567
  54. package/agent-spine-plugin/src/index.js +16 -2
  55. package/agent-spine-plugin/src/lib/acceptance.js +113 -11
  56. package/agent-spine-plugin/src/lib/action-lesson-recall.js +53 -0
  57. package/agent-spine-plugin/src/lib/attention-context.js +167 -0
  58. package/agent-spine-plugin/src/lib/attention-events.js +113 -0
  59. package/agent-spine-plugin/src/lib/attention-privacy.js +72 -0
  60. package/agent-spine-plugin/src/lib/attention-schema.js +164 -0
  61. package/agent-spine-plugin/src/lib/attention-storage.js +93 -0
  62. package/agent-spine-plugin/src/lib/attention.js +9 -600
  63. package/agent-spine-plugin/src/lib/audit-premortem.js +223 -0
  64. package/agent-spine-plugin/src/lib/audit.js +56 -6
  65. package/agent-spine-plugin/src/lib/autonomy-policy.js +110 -0
  66. package/agent-spine-plugin/src/lib/autonomy-store.js +202 -0
  67. package/agent-spine-plugin/src/lib/autonomy.js +8 -0
  68. package/agent-spine-plugin/src/lib/briefing.js +67 -4
  69. package/agent-spine-plugin/src/lib/catalog-document-read.js +51 -0
  70. package/agent-spine-plugin/src/lib/catalog.js +1 -1
  71. package/agent-spine-plugin/src/lib/codex-installation.js +231 -0
  72. package/agent-spine-plugin/src/lib/codex-skill-installation.js +211 -0
  73. package/agent-spine-plugin/src/lib/context.js +3 -1
  74. package/agent-spine-plugin/src/lib/delivery-agent-usage.js +224 -0
  75. package/agent-spine-plugin/src/lib/delivery-assignment.js +220 -0
  76. package/agent-spine-plugin/src/lib/delivery-command-actions.js +453 -0
  77. package/agent-spine-plugin/src/lib/delivery-knowledge.js +78 -0
  78. package/agent-spine-plugin/src/lib/delivery-premortem-binding.js +107 -0
  79. package/agent-spine-plugin/src/lib/delivery-premortem-closure.js +171 -0
  80. package/agent-spine-plugin/src/lib/delivery-premortem-codec.js +45 -0
  81. package/agent-spine-plugin/src/lib/delivery-premortem-correction.js +101 -0
  82. package/agent-spine-plugin/src/lib/delivery-premortem-file.js +21 -0
  83. package/agent-spine-plugin/src/lib/delivery-premortem-index.js +493 -0
  84. package/agent-spine-plugin/src/lib/delivery-premortem-inspection.js +37 -0
  85. package/agent-spine-plugin/src/lib/delivery-premortem-recovery.js +120 -0
  86. package/agent-spine-plugin/src/lib/delivery-premortem-rejection.js +65 -0
  87. package/agent-spine-plugin/src/lib/delivery-premortem-results.js +28 -0
  88. package/agent-spine-plugin/src/lib/delivery-premortem-session-guard.js +46 -0
  89. package/agent-spine-plugin/src/lib/delivery-premortem-write-ledger.js +285 -0
  90. package/agent-spine-plugin/src/lib/delivery-premortem.js +499 -0
  91. package/agent-spine-plugin/src/lib/delivery-shell-heredoc.js +115 -0
  92. package/agent-spine-plugin/src/lib/delivery-shell-substitutions.js +111 -0
  93. package/agent-spine-plugin/src/lib/delivery-shell-wrapper.js +64 -0
  94. package/agent-spine-plugin/src/lib/delivery-target.js +73 -0
  95. package/agent-spine-plugin/src/lib/delivery-verification.js +445 -0
  96. package/agent-spine-plugin/src/lib/documents.js +27 -5
  97. package/agent-spine-plugin/src/lib/filesystem-retry.js +2 -0
  98. package/agent-spine-plugin/src/lib/gateway-common.js +68 -0
  99. package/agent-spine-plugin/src/lib/gateway-control.js +302 -0
  100. package/agent-spine-plugin/src/lib/gateway-delivery.js +103 -0
  101. package/agent-spine-plugin/src/lib/gateway-execution.js +350 -0
  102. package/agent-spine-plugin/src/lib/gateway-host-lifecycle.js +185 -0
  103. package/agent-spine-plugin/src/lib/gateway-inspection.js +85 -0
  104. package/agent-spine-plugin/src/lib/gateway-knowledge.js +129 -0
  105. package/agent-spine-plugin/src/lib/gateway-policy-provenance.js +197 -0
  106. package/agent-spine-plugin/src/lib/gateway-premortem-disposition.js +82 -0
  107. package/agent-spine-plugin/src/lib/gateway-premortem.js +363 -0
  108. package/agent-spine-plugin/src/lib/gateway-runs.js +300 -0
  109. package/agent-spine-plugin/src/lib/gateway-runtime-identity.js +31 -0
  110. package/agent-spine-plugin/src/lib/gateway-runtime-records.js +21 -0
  111. package/agent-spine-plugin/src/lib/gateway-runtime.js +18 -1623
  112. package/agent-spine-plugin/src/lib/gateway-state-transaction.js +356 -0
  113. package/agent-spine-plugin/src/lib/gateway-state.js +342 -0
  114. package/agent-spine-plugin/src/lib/hook-artifact-guards.js +356 -0
  115. package/agent-spine-plugin/src/lib/hook-audit.js +16 -2
  116. package/agent-spine-plugin/src/lib/hook-briefing-use.js +68 -0
  117. package/agent-spine-plugin/src/lib/hook-context.js +424 -0
  118. package/agent-spine-plugin/src/lib/hook-final-message.js +26 -0
  119. package/agent-spine-plugin/src/lib/hook-input.js +27 -0
  120. package/agent-spine-plugin/src/lib/hook-output.js +140 -0
  121. package/agent-spine-plugin/src/lib/hook-premortem.js +257 -0
  122. package/agent-spine-plugin/src/lib/hook-process-advisory.js +37 -0
  123. package/agent-spine-plugin/src/lib/hook-protection.js +95 -0
  124. package/agent-spine-plugin/src/lib/hook-stop-verification.js +84 -0
  125. package/agent-spine-plugin/src/lib/hook-timeline.js +91 -0
  126. package/agent-spine-plugin/src/lib/identifier-analysis.js +446 -0
  127. package/agent-spine-plugin/src/lib/indexed-memory.js +23 -6
  128. package/agent-spine-plugin/src/lib/knowledge-evidence.js +431 -0
  129. package/agent-spine-plugin/src/lib/learning-applications.js +441 -0
  130. package/agent-spine-plugin/src/lib/learning-candidates.js +274 -0
  131. package/agent-spine-plugin/src/lib/learning-context.js +130 -0
  132. package/agent-spine-plugin/src/lib/learning-delivery-contracts.js +310 -0
  133. package/agent-spine-plugin/src/lib/learning-evaluation-contracts.js +350 -0
  134. package/agent-spine-plugin/src/lib/learning-evaluation-registration.js +372 -0
  135. package/agent-spine-plugin/src/lib/learning-evaluation-revocation.js +292 -0
  136. package/agent-spine-plugin/src/lib/learning-evidence-contracts.js +321 -0
  137. package/agent-spine-plugin/src/lib/learning-findings.js +458 -0
  138. package/agent-spine-plugin/src/lib/learning-measurement-contracts.js +285 -0
  139. package/agent-spine-plugin/src/lib/learning-measurements.js +265 -0
  140. package/agent-spine-plugin/src/lib/learning-outcome-contracts.js +165 -0
  141. package/agent-spine-plugin/src/lib/learning-outcomes.js +343 -0
  142. package/agent-spine-plugin/src/lib/learning-reconciliation.js +290 -0
  143. package/agent-spine-plugin/src/lib/learning-retry-contracts.js +188 -0
  144. package/agent-spine-plugin/src/lib/learning-schema.js +229 -0
  145. package/agent-spine-plugin/src/lib/learning-scope-targets.js +424 -0
  146. package/agent-spine-plugin/src/lib/learning-state-upgrade.js +477 -0
  147. package/agent-spine-plugin/src/lib/learning-status-configuration.js +476 -0
  148. package/agent-spine-plugin/src/lib/learning-storage.js +203 -0
  149. package/agent-spine-plugin/src/lib/learning-trial-recovery.js +218 -0
  150. package/agent-spine-plugin/src/lib/learning-validation-contracts.js +375 -0
  151. package/agent-spine-plugin/src/lib/learning-validation-renewal.js +298 -0
  152. package/agent-spine-plugin/src/lib/learning-validation-runtime.js +234 -0
  153. package/agent-spine-plugin/src/lib/learning.js +36 -6923
  154. package/agent-spine-plugin/src/lib/lesson-recall-session.js +172 -0
  155. package/agent-spine-plugin/src/lib/mcp-autonomy-tools.js +37 -0
  156. package/agent-spine-plugin/src/lib/mcp-delivery-completion.js +124 -0
  157. package/agent-spine-plugin/src/lib/mcp-delivery-tools.js +45 -0
  158. package/agent-spine-plugin/src/lib/mcp-premortem.js +68 -0
  159. package/agent-spine-plugin/src/lib/mcp-runtime.js +269 -0
  160. package/agent-spine-plugin/src/lib/mcp-source-context.js +51 -0
  161. package/agent-spine-plugin/src/lib/mcp-timeline-tools.js +164 -0
  162. package/agent-spine-plugin/src/lib/mcp-world-tools.js +52 -0
  163. package/agent-spine-plugin/src/lib/owned-file-lock.js +30 -10
  164. package/agent-spine-plugin/src/lib/project-portfolio.js +176 -0
  165. package/agent-spine-plugin/src/lib/selfstarter-core.js +382 -0
  166. package/agent-spine-plugin/src/lib/selfstarter-jobs.js +149 -0
  167. package/agent-spine-plugin/src/lib/selfstarter-lease.js +204 -0
  168. package/agent-spine-plugin/src/lib/selfstarter-policy.js +90 -0
  169. package/agent-spine-plugin/src/lib/selfstarter-workspace.js +111 -0
  170. package/agent-spine-plugin/src/lib/selfstarter.js +11 -911
  171. package/agent-spine-plugin/src/lib/session-timeline-auth.js +316 -0
  172. package/agent-spine-plugin/src/lib/session-timeline-codex.js +58 -0
  173. package/agent-spine-plugin/src/lib/session-timeline-contract.js +48 -0
  174. package/agent-spine-plugin/src/lib/session-timeline-enrollment-source.js +41 -0
  175. package/agent-spine-plugin/src/lib/session-timeline-enrollment-storage.js +132 -0
  176. package/agent-spine-plugin/src/lib/session-timeline-enrollment-transport.js +17 -0
  177. package/agent-spine-plugin/src/lib/session-timeline-enrollment.js +500 -0
  178. package/agent-spine-plugin/src/lib/session-timeline-event-extract.js +157 -0
  179. package/agent-spine-plugin/src/lib/session-timeline-host-origin.js +74 -0
  180. package/agent-spine-plugin/src/lib/session-timeline-host-receipt.js +117 -0
  181. package/agent-spine-plugin/src/lib/session-timeline-invocation.js +201 -0
  182. package/agent-spine-plugin/src/lib/session-timeline-king.js +79 -0
  183. package/agent-spine-plugin/src/lib/session-timeline-prior.js +59 -0
  184. package/agent-spine-plugin/src/lib/session-timeline-provider.js +34 -0
  185. package/agent-spine-plugin/src/lib/session-timeline-query.js +55 -0
  186. package/agent-spine-plugin/src/lib/session-timeline-results.js +31 -0
  187. package/agent-spine-plugin/src/lib/session-timeline-root.js +11 -0
  188. package/agent-spine-plugin/src/lib/session-timeline-search.js +82 -0
  189. package/agent-spine-plugin/src/lib/session-timeline-sid-acl.js +217 -0
  190. package/agent-spine-plugin/src/lib/session-timeline-source.js +83 -0
  191. package/agent-spine-plugin/src/lib/session-timeline-state.js +45 -0
  192. package/agent-spine-plugin/src/lib/session-timeline-transport.js +50 -0
  193. package/agent-spine-plugin/src/lib/session-timeline-windows-acl.js +148 -0
  194. package/agent-spine-plugin/src/lib/session-timeline.js +453 -0
  195. package/agent-spine-plugin/src/lib/source-roots.js +69 -136
  196. package/agent-spine-plugin/src/lib/source-tree-scan.js +178 -0
  197. package/agent-spine-plugin/src/lib/task-knowledge-context.js +78 -0
  198. package/agent-spine-plugin/src/lib/timeline-tool-guard.js +202 -0
  199. package/agent-spine-plugin/src/lib/world-knowledge.js +249 -0
  200. package/agent-spine-plugin/src/lib/world-model.js +278 -0
  201. package/agent-spine-plugin/src/mcp.js +20 -161
  202. package/agent-spine-plugin/src/version.js +1 -1
  203. package/agent-spine-plugin/src/worker.js +22 -5
  204. package/bin/agent-resume-snapshot.cjs +2 -2
  205. package/bin/agentspine-king-goal-inbox.mjs +111 -0
  206. package/bin/agentspine-king-goal-intake.mjs +106 -0
  207. package/bin/baseline-skill-performance-policy.cjs +1 -16
  208. package/bin/core-bootstrap.js +2 -0
  209. package/bin/curiosity-scout-policy.cjs +5 -1
  210. package/bin/input-draft-persistence.cjs +2 -2
  211. package/bin/king-tui-function-contract.json +33 -0
  212. package/bin/launcher-restart-policy.cjs +150 -0
  213. package/bin/launcher-runtime.js +56 -44
  214. package/bin/managed-context-startup-policy.cjs +27 -0
  215. package/bin/managed-plugin-selection.cjs +116 -0
  216. package/bin/mistake-relevance-policy.cjs +1 -1
  217. package/bin/observer-hooks.cjs +14 -0
  218. package/bin/oversized-context-offload-policy.cjs +86 -0
  219. package/bin/plugin-bootstrap.js +7 -40
  220. package/bin/proactive-compaction-policy.cjs +1 -1
  221. package/bin/provider-model-refresh-deadline.cjs +53 -0
  222. package/bin/provider-model-refresh-policy.cjs +107 -0
  223. package/bin/release-artifact-freeze-policy.cjs +30 -0
  224. package/bin/repeated-user-message-projection.cjs +3 -126
  225. package/bin/research-page-result.cjs +74 -0
  226. package/bin/runtime-exit-ledger.cjs +1 -0
  227. package/bin/session-compaction-policy.cjs +84 -0
  228. package/bin/skill-listing-performance-policy.cjs +2 -2
  229. package/bin/standard-tools-bootstrap.js +0 -37
  230. package/bin/subagent-skill-policy.cjs +3 -1
  231. package/bin/telegram-approval-relay.cjs +12 -7
  232. package/bin/telegram-private-conversation-policy.cjs +3 -2
  233. package/bin/telegram-queue-handoff-policy.cjs +24 -0
  234. package/bin/thinking-activity-status-policy.cjs +1 -1
  235. package/bin/thinking-only-guard.cjs +16 -12
  236. package/bin/tool-call-loop-policy.cjs +0 -2
  237. package/bin/tool-result-offload-policy.cjs +11 -2
  238. package/bin/tui-functional-contract.cjs +55 -0
  239. package/bin/update-notice.js +18 -14
  240. package/bin/user-message-offload-policy.cjs +1 -1
  241. package/bin/user-prompt-hook-origin-policy.cjs +34 -0
  242. package/bin/windows-node-crash-dump.cjs +110 -0
  243. package/blun.mjs +2517 -1048
  244. package/codebase-index/codebase_index.py +4 -3
  245. package/package.json +3 -17
  246. package/standard-skills/research-evidence/SKILL.md +39 -0
  247. package/standard-skills/research-evidence/references/evidence-format.md +82 -0
  248. package/standard-skills/research-evidence/scripts/evidence-collection.cjs +254 -0
  249. package/standard-skills/research-evidence/scripts/score-report.cjs +112 -0
  250. package/standard-skills/web-lesen/SKILL.md +37 -22
  251. package/standard-skills/web-lesen/scripts/crawl_public.py +376 -0
  252. package/telegram-plugin/DELIVERY.md +36 -0
  253. package/telegram-plugin/bin/telegram-approval-relay.cjs +13 -7
  254. package/telegram-plugin/bin/telegram-launcher-status-queue.cjs +122 -0
  255. package/telegram-plugin/bin/telegram-private-conversation-policy.cjs +3 -2
  256. package/telegram-plugin/bin/telegram-reply-parts.cjs +149 -0
  257. package/telegram-plugin/dist/bridge.mjs +11 -57
  258. package/telegram-plugin/dist/mcp-server.mjs +33 -4
  259. package/agent-spine-plugin/skill/SKILL.md +0 -76
  260. package/bin/mnemo-connect-heartbeat.cjs +0 -204
  261. package/bin/mnemo-tool-agent-policy.cjs +0 -22
  262. package/telegram-plugin/bin/telegram-mnemo-capture.cjs +0 -297
@@ -0,0 +1,278 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { readFile, stat, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { replaceFileWithRetry } from "./filesystem-retry.js";
5
+ import { withOwnedFileLock } from "./owned-file-lock.js";
6
+ import { canonicalPath, projectStateDir } from "./paths.js";
7
+
8
+ const SCHEMA = "agentspine.world-model/v1";
9
+ const ASSERTION_SCHEMA = "agentspine.world-assertion/v1";
10
+ const MAX_STATE_BYTES = 5 * 1024 * 1024;
11
+ const MAX_VALUE_BYTES = 8192;
12
+ const EVIDENCE_KINDS = new Set([
13
+ "objective-measurement", "explicit-user-feedback", "model-suggestion"
14
+ ]);
15
+ const PRIVACY = new Set(["private", "shared", "group"]);
16
+ const STABLE_ID = /^[a-z][a-z0-9-]{0,31}:[A-Za-z0-9][A-Za-z0-9._/-]{0,190}$/;
17
+ const PREDICATE = /^[a-z][a-z0-9.-]{0,127}$/;
18
+ const DIGEST = /^[a-f0-9]{64}$/;
19
+ const FORBIDDEN = /(^|[-_.])(permissions?|rights?|authori[sz]ation|credentials?|secrets?|tokens?|api[-_]?keys?|delegation|tool[-_]?access|production[-_]?access|payments?|spending)([-_.]|$)/i;
20
+
21
+ function sha256(value) {
22
+ return createHash("sha256").update(value).digest("hex");
23
+ }
24
+
25
+ function canonicalValue(value, path = "value") {
26
+ if (value === null || ["string", "boolean"].includes(typeof value)) return value;
27
+ if (typeof value === "number") {
28
+ if (!Number.isFinite(value)) throw new Error(`${path} numbers must be finite`);
29
+ return value;
30
+ }
31
+ if (Array.isArray(value)) return value.map((item, index) => canonicalValue(item, `${path}[${index}]`));
32
+ if (!value || typeof value !== "object" || Object.getPrototypeOf(value) !== Object.prototype) {
33
+ throw new Error(`${path} must be JSON data`);
34
+ }
35
+ const result = {};
36
+ for (const key of Object.keys(value).sort()) {
37
+ if (FORBIDDEN.test(key)) throw new Error(`${path}.${key} is authority data and cannot enter the world model`);
38
+ result[key] = canonicalValue(value[key], `${path}.${key}`);
39
+ }
40
+ return result;
41
+ }
42
+
43
+ function exactTime(value, field, fallback = null) {
44
+ if ((value === null || value === undefined) && fallback !== null) return fallback;
45
+ if (typeof value !== "string" || !value) throw new Error(`${field} is required`);
46
+ const date = new Date(value);
47
+ if (!Number.isFinite(date.getTime()) || date.toISOString() !== value) {
48
+ throw new Error(`${field} must be an exact ISO timestamp`);
49
+ }
50
+ return value;
51
+ }
52
+
53
+ function stableId(value, field) {
54
+ if (typeof value !== "string" || !STABLE_ID.test(value)) throw new Error(`${field} must be a stable namespaced id`);
55
+ return value;
56
+ }
57
+
58
+ function optionalStableId(value, field) {
59
+ return value === null || value === undefined ? null : stableId(value, field);
60
+ }
61
+
62
+ function emptyModel(root) {
63
+ return { schema: SCHEMA, root, revision: 0, assertions: [], events: [], authority: "context-only" };
64
+ }
65
+
66
+ function validateStoredAssertion(assertion, validKnowledgeFields) {
67
+ if (!assertion || assertion.schema !== ASSERTION_SCHEMA || assertion.authority !== "context-only") return false;
68
+ if (!STABLE_ID.test(assertion.id || "") || !STABLE_ID.test(assertion.subjectId || "")) return false;
69
+ if (!PREDICATE.test(assertion.predicate || "") || FORBIDDEN.test(assertion.predicate)
70
+ || !EVIDENCE_KINDS.has(assertion.evidenceKind)) return false;
71
+ if (!PRIVACY.has(assertion.privacy) || !DIGEST.test(assertion.evidenceDigest || "")) return false;
72
+ if ((assertion.privacy === "group") !== Boolean(assertion.groupId)
73
+ || (assertion.groupId && !STABLE_ID.test(assertion.groupId))
74
+ || (assertion.projectId && !STABLE_ID.test(assertion.projectId))
75
+ || !STABLE_ID.test(assertion.evidenceId || "") || !Array.isArray(assertion.supersedes)
76
+ || assertion.supersedes.some((item) => !STABLE_ID.test(item))) return false;
77
+ if (!validKnowledgeFields(assertion)) return false;
78
+ try {
79
+ if (exactTime(assertion.observedAt, "observedAt") !== assertion.observedAt) return false;
80
+ if (assertion.expiresAt) exactTime(assertion.expiresAt, "expiresAt");
81
+ const value = canonicalValue(assertion.value);
82
+ if (sha256(JSON.stringify(value)) !== assertion.valueDigest) return false;
83
+ } catch {
84
+ return false;
85
+ }
86
+ return assertion.status === (assertion.evidenceKind === "model-suggestion" ? "proposed" : "established");
87
+ }
88
+
89
+ function normalizeModel(value, root, validKnowledgeFields) {
90
+ if (!value || value.schema !== SCHEMA || value.root !== root || value.authority !== "context-only"
91
+ || !Number.isInteger(value.revision) || value.revision < 0 || !Array.isArray(value.assertions)
92
+ || !Array.isArray(value.events) || value.assertions.some((item) => !validateStoredAssertion(item, validKnowledgeFields))
93
+ || value.events.some((item) => !item || item.authority !== "context-only")) {
94
+ throw new Error("world model state is invalid");
95
+ }
96
+ return value;
97
+ }
98
+
99
+ async function stateNames(root) {
100
+ const canonicalRoot = await canonicalPath(root);
101
+ const directory = await projectStateDir(canonicalRoot);
102
+ const path = join(directory, "world-model.json");
103
+ return { root: canonicalRoot, path, lockPath: `${path}.lock` };
104
+ }
105
+
106
+ async function readModel(names) {
107
+ try {
108
+ const metadata = await stat(names.path);
109
+ if (metadata.size > MAX_STATE_BYTES) throw new Error("world model exceeds the 5 MiB read limit");
110
+ const { validKnowledgeFields } = await import("./world-knowledge.js");
111
+ return normalizeModel(JSON.parse(await readFile(names.path, "utf8")), names.root, validKnowledgeFields);
112
+ } catch (error) {
113
+ if (error.code === "ENOENT") return emptyModel(names.root);
114
+ if (error instanceof SyntaxError) throw new Error("world model state is not valid JSON");
115
+ throw error;
116
+ }
117
+ }
118
+
119
+ async function writeModel(model, names, assertOwned) {
120
+ const content = `${JSON.stringify(model, null, 2)}\n`;
121
+ if (Buffer.byteLength(content) > MAX_STATE_BYTES) throw new Error("world model exceeds 5 MiB");
122
+ const temporary = `${names.path}.${process.pid}.${randomUUID()}.tmp`;
123
+ await writeFile(temporary, content, { mode: 0o600 });
124
+ await replaceFileWithRetry(temporary, names.path, { beforeAttempt: assertOwned });
125
+ }
126
+
127
+ function assertionInput(input, now, normalizeKnowledgeFields) {
128
+ const id = stableId(input.id, "id");
129
+ const subjectId = stableId(input.subjectId, "subjectId");
130
+ if (typeof input.predicate !== "string" || !PREDICATE.test(input.predicate)) {
131
+ throw new Error("predicate must be a lowercase dotted key");
132
+ }
133
+ if (FORBIDDEN.test(input.predicate)) throw new Error("authority predicates cannot enter the world model");
134
+ if (!EVIDENCE_KINDS.has(input.evidenceKind)) throw new Error("unsupported evidenceKind");
135
+ const evidenceId = stableId(input.evidenceId, "evidenceId");
136
+ if (!DIGEST.test(input.evidenceDigest || "")) throw new Error("evidenceDigest must be a lowercase SHA-256 digest");
137
+ const observedAt = exactTime(input.observedAt, "observedAt");
138
+ const nowTime = new Date(now).getTime();
139
+ if (new Date(observedAt).getTime() > nowTime) throw new Error("observedAt cannot be in the future");
140
+ const expiresAt = input.expiresAt === null || input.expiresAt === undefined
141
+ ? null : exactTime(input.expiresAt, "expiresAt");
142
+ if (expiresAt && new Date(expiresAt).getTime() <= new Date(observedAt).getTime()) {
143
+ throw new Error("expiresAt must be after observedAt");
144
+ }
145
+ const privacy = input.privacy || "private";
146
+ if (!PRIVACY.has(privacy)) throw new Error("unsupported privacy");
147
+ const groupId = optionalStableId(input.groupId, "groupId");
148
+ if ((privacy === "group") !== Boolean(groupId)) throw new Error("group privacy requires exactly one groupId");
149
+ const value = canonicalValue(input.value);
150
+ if (Buffer.byteLength(JSON.stringify(value)) > MAX_VALUE_BYTES) throw new Error("value exceeds 8 KiB");
151
+ const supersedes = [...new Set((input.supersedes || []).map((item) => stableId(item, "supersedes")))].sort();
152
+ const knowledge = normalizeKnowledgeFields(input, input.evidenceKind, value);
153
+ return {
154
+ schema: ASSERTION_SCHEMA, id, subjectId, predicate: input.predicate, value,
155
+ valueDigest: sha256(JSON.stringify(value)), evidenceKind: input.evidenceKind,
156
+ evidenceId, evidenceDigest: input.evidenceDigest, observedAt, expiresAt,
157
+ projectId: optionalStableId(input.projectId, "projectId"), groupId, privacy,
158
+ supersedes, reason: typeof input.reason === "string" ? input.reason.trim().slice(0, 500) : "",
159
+ status: input.evidenceKind === "model-suggestion" ? "proposed" : "established",
160
+ recordedAt: new Date(now).toISOString(), authority: "context-only",
161
+ ...(knowledge.knowledgeKind ? knowledge : {})
162
+ };
163
+ }
164
+
165
+ function assertionDigest(assertion) {
166
+ const { recordedAt: _recordedAt, ...material } = assertion;
167
+ return sha256(JSON.stringify(material));
168
+ }
169
+
170
+ export async function recordWorldAssertion(input = {}) {
171
+ const now = input.now || new Date();
172
+ const { normalizeKnowledgeFields } = await import("./world-knowledge.js");
173
+ const candidate = assertionInput(input, now, normalizeKnowledgeFields);
174
+ const names = await stateNames(input.root || process.cwd());
175
+ return withOwnedFileLock(names.lockPath, async ({ assertOwned }) => {
176
+ const model = await readModel(names);
177
+ const existing = model.assertions.find((item) => item.id === candidate.id);
178
+ if (existing) {
179
+ if (assertionDigest(existing) !== assertionDigest(candidate)) throw new Error(`world assertion id is already used: ${candidate.id}`);
180
+ return { status: "duplicate", assertion: structuredClone(existing), revision: model.revision, authority: "context-only" };
181
+ }
182
+ for (const id of candidate.supersedes) {
183
+ const previous = model.assertions.find((item) => item.id === id);
184
+ if (!previous) throw new Error(`superseded assertion does not exist: ${id}`);
185
+ if (previous.subjectId !== candidate.subjectId || previous.predicate !== candidate.predicate) {
186
+ throw new Error("supersession must keep the same subject and predicate");
187
+ }
188
+ if (candidate.status !== "established") throw new Error("model suggestions cannot supersede established context");
189
+ }
190
+ model.revision += 1;
191
+ model.assertions.push(candidate);
192
+ model.events.push({ kind: candidate.supersedes.length ? "resolved" : "recorded", assertionId: candidate.id,
193
+ supersedes: candidate.supersedes, at: candidate.recordedAt, authority: "context-only" });
194
+ await writeModel(model, names, assertOwned);
195
+ return { status: "recorded", assertion: structuredClone(candidate), revision: model.revision, authority: "context-only" };
196
+ });
197
+ }
198
+
199
+ function visible(assertion, { includePrivate, groupId, projectId }) {
200
+ if (assertion.privacy === "private" && (!includePrivate || groupId)) return false;
201
+ if (assertion.privacy === "group" && assertion.groupId !== groupId) return false;
202
+ return assertion.projectId === null || assertion.projectId === projectId;
203
+ }
204
+
205
+ function publicAssertion(assertion) {
206
+ return structuredClone(assertion);
207
+ }
208
+
209
+ export async function worldContext({
210
+ root = process.cwd(), subjectId = null, projectId = null, groupId = null,
211
+ includePrivate = false, includeKnowledgeHistory = false, continuationTaskId = null,
212
+ maxItems = 100, now = new Date()
213
+ } = {}) {
214
+ if (groupId !== null && includePrivate) throw new Error("private world context cannot be assembled for a group audience");
215
+ if (!Number.isInteger(maxItems) || maxItems < 1 || maxItems > 500) throw new Error("maxItems must be between 1 and 500");
216
+ subjectId = optionalStableId(subjectId, "subjectId");
217
+ projectId = optionalStableId(projectId, "projectId");
218
+ groupId = optionalStableId(groupId, "groupId");
219
+ continuationTaskId = optionalStableId(continuationTaskId, "continuationTaskId");
220
+ const names = await stateNames(root);
221
+ const model = await readModel(names);
222
+ const { structuredKnowledgeView } = await import("./world-knowledge.js");
223
+ const cutoff = new Date(now).getTime();
224
+ if (!Number.isFinite(cutoff)) throw new Error("now is invalid");
225
+ const candidates = model.assertions.filter((item) => visible(item, { includePrivate, groupId, projectId })
226
+ && (!subjectId || item.subjectId === subjectId));
227
+ const stale = candidates.filter((item) => item.expiresAt && new Date(item.expiresAt).getTime() <= cutoff);
228
+ const current = candidates.filter((item) => !item.expiresAt || new Date(item.expiresAt).getTime() > cutoff);
229
+ const superseded = new Set(current.flatMap((item) => item.status === "established" ? item.supersedes : []));
230
+ const active = current.filter((item) => !superseded.has(item.id));
231
+ const proposals = active.filter((item) => item.status === "proposed");
232
+ const established = active.filter((item) => item.status === "established");
233
+ const buckets = new Map();
234
+ for (const item of established) {
235
+ const key = `${item.subjectId}\0${item.predicate}`;
236
+ if (!buckets.has(key)) buckets.set(key, []);
237
+ buckets.get(key).push(item);
238
+ }
239
+ const facts = [];
240
+ const conflicts = [];
241
+ const conflictAssertions = [];
242
+ for (const assertions of buckets.values()) {
243
+ const values = new Set(assertions.map((item) => item.valueDigest));
244
+ if (values.size > 1) {
245
+ conflictAssertions.push(assertions);
246
+ conflicts.push({ subjectId: assertions[0].subjectId, predicate: assertions[0].predicate,
247
+ assertions: assertions.map(publicAssertion), authority: "context-only" });
248
+ continue;
249
+ }
250
+ const newest = [...assertions].sort((a, b) => b.observedAt.localeCompare(a.observedAt))[0];
251
+ facts.push({ subjectId: newest.subjectId, predicate: newest.predicate, value: structuredClone(newest.value),
252
+ assertionIds: assertions.map((item) => item.id).sort(), evidenceKinds: [...new Set(assertions.map((item) => item.evidenceKind))].sort(),
253
+ latestObservedAt: newest.observedAt, expiresAt: newest.expiresAt, privacy: newest.privacy,
254
+ groupId: newest.groupId, projectId: newest.projectId, authority: "context-only" });
255
+ }
256
+ const order = (left, right) => left.subjectId.localeCompare(right.subjectId) || left.predicate.localeCompare(right.predicate);
257
+ facts.sort(order); conflicts.sort(order); proposals.sort(order); stale.sort(order);
258
+ const knowledge = structuredKnowledgeView({
259
+ candidates, stale, active, supersededIds: superseded, conflictAssertions,
260
+ includeHistory: Boolean(includeKnowledgeHistory), continuationTaskId, maxItems
261
+ });
262
+ return {
263
+ schema: "agentspine.world-context/v1", root: names.root, revision: model.revision,
264
+ facts: facts.slice(0, maxItems), conflicts: conflicts.slice(0, maxItems),
265
+ proposals: proposals.slice(0, maxItems).map(publicAssertion), stale: stale.slice(0, maxItems).map(publicAssertion),
266
+ knowledge,
267
+ omitted: { facts: Math.max(0, facts.length - maxItems), conflicts: Math.max(0, conflicts.length - maxItems),
268
+ proposals: Math.max(0, proposals.length - maxItems), stale: Math.max(0, stale.length - maxItems) },
269
+ uncertainty: { requiresResolution: conflicts.length > 0, conflicts: conflicts.length,
270
+ proposals: proposals.length, stale: stale.length },
271
+ authority: "context-only",
272
+ note: "Only unexpired, non-conflicting measured or explicitly user-confirmed assertions are facts. Model suggestions remain proposals and grant no authority."
273
+ };
274
+ }
275
+
276
+ export async function worldModelStatePath(root = process.cwd()) {
277
+ return (await stateNames(root)).path;
278
+ }
@@ -1,24 +1,12 @@
1
1
  import { VERSION } from "./version.js";
2
2
  import { isMainModule } from "./lib/runtime.js";
3
- import { scanAndSave, verifyCatalog } from "./lib/catalog.js";
4
- import { readDocument, resolveContext } from "./lib/context.js";
5
- import { sessionBriefing } from "./lib/briefing.js";
6
- import { runAudit } from "./lib/audit.js";
7
- import {
8
- attentionContext, configureAttention, deleteAttention,
9
- recordActivity, resolveAttention, upsertAttention
10
- } from "./lib/attention.js";
11
- import {
12
- addLearningEvidence, configureLearning, deleteLearning, evaluateLearning,
13
- learningContext, learningOutcomeStatus, proposeLearning, reviewLearning, rollbackLearning
14
- } from "./lib/learning.js";
15
- import { checkDelegation, createTask, taskContext, updateTask } from "./lib/coordination.js";
16
- import { sharedContext } from "./lib/sharing.js";
17
- import { resolveGoalKnowledgeGap } from "./lib/gateway-runtime.js";
18
- import {
19
- annotateDocument, linkDocuments, linkEntities,
20
- relationshipContext, upsertEntity
21
- } from "./lib/graph.js";
3
+ import { deliveryPremortemRecoveryTool, deliveryPremortemTool } from "./lib/mcp-premortem.js";
4
+ import { deliveryKnowledgeTool, sessionBriefingTool } from "./lib/mcp-delivery-tools.js";
5
+ import { worldModelTools } from "./lib/mcp-world-tools.js";
6
+ import { sessionTimelineTools } from "./lib/mcp-timeline-tools.js";
7
+ import { startMcpProtocol } from "./lib/mcp-runtime.js";
8
+ import { deliveryCompletionTool } from "./lib/mcp-delivery-completion.js";
9
+ import { autonomyTools } from "./lib/mcp-autonomy-tools.js";
22
10
 
23
11
  const tools = [
24
12
  {
@@ -38,34 +26,19 @@ const tools = [
38
26
  }
39
27
  }
40
28
  },
41
- {
42
- name: "session_briefing",
43
- description: "Assemble one byte-budgeted, privacy-filtered session packet across native sources, relationships, accepted learning, reviewed shared memory, tasks, and optional attention cues. Read-only and context-only.",
44
- inputSchema: {
45
- type: "object", additionalProperties: false,
46
- properties: {
47
- root: { type: "string" }, cwd: { type: "string" },
48
- host: { type: "string", enum: ["codex", "claude", "generic"] },
49
- entityId: { anyOf: [{ type: "string" }, { type: "null" }] },
50
- userId: { anyOf: [{ type: "string" }, { type: "null" }] },
51
- tenantId: { anyOf: [{ type: "string" }, { type: "null" }] },
52
- groupId: { anyOf: [{ type: "string" }, { type: "null" }] },
53
- projectId: { anyOf: [{ type: "string" }, { type: "null" }] },
54
- currentTaskId: { anyOf: [{ type: "string" }, { type: "null" }] },
55
- includePrivate: { type: "boolean" }, focusActive: { type: "boolean" },
56
- includeSourceContent: { type: "boolean" },
57
- maxBytes: { type: "integer", minimum: 4096, maximum: 262144 },
58
- now: { type: "string" }
59
- }
60
- }
61
- },
29
+ sessionBriefingTool,
30
+ deliveryKnowledgeTool,
31
+ ...worldModelTools,
32
+ ...sessionTimelineTools,
33
+ ...autonomyTools,
62
34
  {
63
35
  name: "read_document",
64
- description: "Read an indexed Markdown source byte range with its SHA-256 provenance.",
36
+ description: "Read a bounded host-indexed Markdown source byte range with verified SHA-256 provenance; no broad home scan.",
65
37
  inputSchema: {
66
38
  type: "object", required: ["path"],
67
39
  properties: {
68
40
  root: { type: "string" }, path: { type: "string" },
41
+ host: { type: "string", enum: ["codex", "claude", "generic"] },
69
42
  offset: { type: "integer", minimum: 0 }, length: { type: "integer", minimum: 1, maximum: 1048576 }
70
43
  }
71
44
  }
@@ -464,132 +437,18 @@ const tools = [
464
437
  }
465
438
  }
466
439
  },
467
- {
468
- name: "goal_clarify",
469
- description: "Resolve one exact open knowledge gap for a dependency-bound goal-plan step. This binds context only and grants no authority.",
470
- inputSchema: {
471
- type: "object", additionalProperties: false,
472
- required: ["goalId", "gapId", "answer", "confirmLocalGoal"],
473
- properties: {
474
- root: { type: "string" }, goalId: { type: "string" }, gapId: { type: "string" },
475
- answer: { type: "string", maxLength: 2000 },
476
- answerSource: { type: "string", enum: ["owner-input", "objective-observation"] },
477
- sourceDigest: { anyOf: [{ type: "string", pattern: "^[a-f0-9]{64}$" }, { type: "null" }] },
478
- confirmLocalGoal: { type: "boolean", const: true }
479
- }
480
- }
481
- },
482
440
  {
483
441
  name: "audit",
484
442
  description: "Run AgentSpine's ten deterministic quality gates for discovery, hierarchy, links, authority, privacy, budget, and source preservation.",
485
443
  inputSchema: { type: "object", properties: { root: { type: "string" } } }
486
- }
444
+ },
445
+ deliveryPremortemTool,
446
+ deliveryCompletionTool,
447
+ deliveryPremortemRecoveryTool
487
448
  ];
488
449
 
489
- function textResult(value, isError = false) {
490
- return { content: [{ type: "text", text: JSON.stringify(value, null, 2) }], isError };
491
- }
492
-
493
- async function callTool(name, args = {}) {
494
- const root = args.root || process.cwd();
495
- if (name === "scan") return textResult(await scanAndSave(root));
496
- if (name === "resolve_context") return textResult(await resolveContext({
497
- root,
498
- cwd: args.cwd || root,
499
- host: args.host || "generic",
500
- maxBytes: args.maxBytes ?? 65536,
501
- includeContent: args.includeContent ?? true
502
- }));
503
- if (name === "session_briefing") return textResult(await sessionBriefing({ ...args, root }));
504
- if (name === "read_document") return textResult(await readDocument({
505
- root, path: args.path, offset: args.offset ?? 0, length: args.length ?? 65536
506
- }));
507
- if (name === "verify") return textResult(await verifyCatalog(root));
508
- if (name === "link_documents") return textResult(await linkDocuments({ ...args, root }));
509
- if (name === "annotate_document") return textResult(await annotateDocument({ ...args, root }));
510
- if (name === "upsert_entity") return textResult(await upsertEntity({ ...args, root }));
511
- if (name === "link_entities") return textResult(await linkEntities({ ...args, root }));
512
- if (name === "relationship_context") return textResult(await relationshipContext({ ...args, root }));
513
- if (name === "upsert_attention") return textResult(await upsertAttention({ ...args, root }));
514
- if (name === "record_activity") return textResult(await recordActivity({ ...args, root }));
515
- if (name === "attention_context") return textResult(await attentionContext({ ...args, root }));
516
- if (name === "resolve_attention") return textResult(await resolveAttention({ ...args, root }));
517
- if (name === "configure_attention") return textResult(await configureAttention({ ...args, root }));
518
- if (name === "delete_attention") return textResult(await deleteAttention({ ...args, root }));
519
- if (name === "propose_learning") return textResult(await proposeLearning({ ...args, root }));
520
- if (name === "add_learning_evidence") return textResult(await addLearningEvidence({ ...args, root }));
521
- if (name === "review_learning") return textResult(await reviewLearning({ ...args, root }));
522
- if (name === "learning_context") return textResult(await learningContext({ ...args, root }));
523
- if (name === "learning_outcome_status") return textResult(await learningOutcomeStatus({ ...args, root }));
524
- if (name === "evaluate_learning") return textResult(await evaluateLearning({ ...args, root }));
525
- if (name === "rollback_learning") return textResult(await rollbackLearning({ ...args, root }));
526
- if (name === "configure_learning") return textResult(await configureLearning({ ...args, root }));
527
- if (name === "delete_learning") return textResult(await deleteLearning({ ...args, root }));
528
- if (name === "check_delegation") return textResult(await checkDelegation({ ...args, root }));
529
- if (name === "create_task") return textResult(await createTask({ ...args, root }));
530
- if (name === "update_task") return textResult(await updateTask({ ...args, root }));
531
- if (name === "task_context") return textResult(await taskContext({ ...args, root }));
532
- if (name === "shared_context") return textResult(await sharedContext({ ...args, root }));
533
- if (name === "goal_clarify") return textResult(await resolveGoalKnowledgeGap({
534
- root,
535
- goalId: args.goalId,
536
- gapId: args.gapId,
537
- answer: args.answer,
538
- answerSource: args.answerSource || "owner-input",
539
- sourceDigest: args.sourceDigest || null,
540
- confirmation: args.confirmLocalGoal === true ? "local-owner-confirmed" : null
541
- }));
542
- if (name === "audit") return textResult(await runAudit(root));
543
- throw new Error(`Unknown tool: ${name}`);
544
- }
545
-
546
- async function dispatch(message) {
547
- const { id, method, params = {} } = message;
548
- if (method === "initialize") {
549
- return {
550
- jsonrpc: "2.0", id,
551
- result: {
552
- protocolVersion: params.protocolVersion || "2025-06-18",
553
- capabilities: { tools: { listChanged: false } },
554
- serverInfo: { name: "agent-spine", version: VERSION }
555
- }
556
- };
557
- }
558
- if (method === "ping") return { jsonrpc: "2.0", id, result: {} };
559
- if (method === "tools/list") return { jsonrpc: "2.0", id, result: { tools } };
560
- if (method === "tools/call") {
561
- try {
562
- return { jsonrpc: "2.0", id, result: await callTool(params.name, params.arguments) };
563
- } catch (error) {
564
- return { jsonrpc: "2.0", id, result: textResult({ error: error.message }, true) };
565
- }
566
- }
567
- if (method?.startsWith("notifications/")) return null;
568
- return { jsonrpc: "2.0", id, error: { code: -32601, message: `Method not found: ${method}` } };
569
- }
570
-
571
- export function startMcpServer(input = process.stdin, output = process.stdout) {
572
- input.setEncoding("utf8");
573
- let buffer = "";
574
- let queue = Promise.resolve();
575
- input.on("data", (chunk) => {
576
- buffer += chunk;
577
- let newline;
578
- while ((newline = buffer.indexOf("\n")) >= 0) {
579
- const line = buffer.slice(0, newline).trim();
580
- buffer = buffer.slice(newline + 1);
581
- if (!line) continue;
582
- queue = queue.then(async () => {
583
- let response;
584
- try {
585
- response = await dispatch(JSON.parse(line));
586
- } catch (error) {
587
- response = { jsonrpc: "2.0", id: null, error: { code: -32700, message: error.message } };
588
- }
589
- if (response) output.write(`${JSON.stringify(response)}\n`);
590
- });
591
- }
592
- });
450
+ export function startMcpServer(input = process.stdin, output = process.stdout, { environment = process.env } = {}) {
451
+ return startMcpProtocol({ input, output, tools, version: VERSION, environment });
593
452
  }
594
453
 
595
454
  if (isMainModule(import.meta.url)) {
@@ -1 +1 @@
1
- export const VERSION = "0.59.1";
1
+ export const VERSION = "0.73.0";
@@ -5,11 +5,12 @@ import { lstat, realpath } from "node:fs/promises";
5
5
  import { isAbsolute, join, resolve } from "node:path";
6
6
  import {
7
7
  claimGatewayWork, completeGatewayRun, deliverPrepared, executionAttemptForStep,
8
- failGatewayRun, loadGatewayRuntime, reconcileGateway, updateGatewayHealth
8
+ failGatewayRun, loadGatewayRuntime, markGatewayHostStarted, reconcileGateway, updateGatewayHealth
9
9
  } from "./lib/gateway-runtime.js";
10
10
  import { createTelegramAdapter } from "./lib/telegram-adapter.js";
11
11
  import { acknowledgeChannelDelivery, loadChannelRuntime } from "./lib/channel-runtime.js";
12
12
  import { loadPersonaRuntime, syncPersonaRosterFromEnvironment } from "./lib/persona-runtime.js";
13
+ import { selfHelpPolicyForWorkItem } from "./lib/knowledge-evidence.js";
13
14
  import { isMainModule } from "./lib/runtime.js";
14
15
 
15
16
  const MAX_FRAME = 64 * 1024;
@@ -105,11 +106,20 @@ async function hostWorkItem(root, item) {
105
106
  goal: goal ? structuredClone(goal) : null,
106
107
  goalStep: goalStep ? { ...structuredClone(goalStep),
107
108
  ...(executionAttempt === null ? {} : { executionAttempt }) } : null,
109
+ selfHelpPolicy: goalStep ? selfHelpPolicyForWorkItem(goalStep) : null,
108
110
  hostEnvironment: {
109
111
  AGENTSPINE_GATEWAY_CONTEXT: "agentspine.gateway-start/v1",
110
112
  AGENTSPINE_ENTITY_ID: item.agentId,
111
113
  AGENTSPINE_PROJECT_ID: item.projectId,
114
+ AGENTSPINE_HOST: identity.host,
115
+ AGENTSPINE_GATEWAY_QUEUE_ID: item.queueId,
116
+ AGENTSPINE_GATEWAY_ATTEMPT: String(item.attempts),
112
117
  ...(item.groupId === null ? {} : { AGENTSPINE_GROUP_ID: item.groupId }),
118
+ ...(goalStep ? {
119
+ AGENTSPINE_GOAL_ID: goal.goalId,
120
+ AGENTSPINE_GOAL_STEP_ID: goalStep.stepId,
121
+ AGENTSPINE_PLAN_DEFINITIONS_DIGEST: goal.plan.definitionsDigest
122
+ } : {}),
113
123
  ...(event ? {
114
124
  AGENTSPINE_CHANNEL_EVENT_ID: event.eventId,
115
125
  AGENTSPINE_CHANNEL_PROVIDER: event.provider
@@ -151,22 +161,29 @@ export async function runWorkerTick({ root = process.cwd(), workerId = "gateway-
151
161
  return { status: delivery.outbox.status, processed: true, outboxId: delivery.outbox.outboxId,
152
162
  recoveredDelivery: true };
153
163
  }
154
- const claim = await claimGatewayWork({ root, workerId, now });
164
+ const claim = await claimGatewayWork({ root, workerId, executionMode: "host-effect", now });
155
165
  if (!claim.item) return { status: claim.reason, processed: false };
166
+ await markGatewayHostStarted({ root, queueId: claim.item.queueId, workerId,
167
+ claimedAt: claim.item.lease.claimedAt, attempt: claim.item.attempts, now });
156
168
  let result;
157
169
  try { result = await hostRunner(await hostWorkItem(root, claim.item), { env }); }
158
170
  catch (error) {
159
171
  const failed = await failGatewayRun({ root, queueId: claim.item.queueId, workerId,
172
+ claimedAt: claim.item.lease.claimedAt, attempt: claim.item.attempts,
160
173
  error: "Host runtime unavailable: " + String(error.message).slice(0, 400), now });
161
174
  return { status: failed.item.status, processed: true, queueId: failed.item.queueId, retryAt: failed.item.availableAt };
162
175
  }
163
- const completed = await completeGatewayRun({ root, queueId: claim.item.queueId, workerId, result, now });
176
+ const completed = await completeGatewayRun({ root, queueId: claim.item.queueId, workerId,
177
+ claimedAt: claim.item.lease.claimedAt, attempt: claim.item.attempts, result, now });
164
178
  if (!completed.outbox) return {
165
179
  status: completed.clarification ? "needs-clarification"
166
- : completed.exploration ? "exploring" : completed.item.status,
180
+ : completed.exploration ? "exploring" : completed.selfHelp ? "self-help-resolved"
181
+ : completed.selfHelpRequired ? "self-help-required" : completed.item.status,
167
182
  processed: true, queueId: completed.item.queueId,
168
183
  ...(completed.clarification ? { clarification: completed.clarification } : {}),
169
- ...(completed.exploration ? { exploration: completed.exploration } : {})
184
+ ...(completed.exploration ? { exploration: completed.exploration } : {}),
185
+ ...(completed.selfHelp ? { selfHelp: completed.selfHelp } : {}),
186
+ ...(completed.selfHelpRequired ? { selfHelpRequired: completed.selfHelpRequired } : {})
170
187
  };
171
188
  const delivery = await deliverPrepared({ root, outboxId: completed.outbox.outboxId,
172
189
  adapter: deliveryAdapter, now });
@@ -94,7 +94,7 @@ function createAgentResumeSnapshot(agent) {
94
94
  goal: agent.goal.state ?? null,
95
95
  turn: {
96
96
  turnId: agent.turn.turnId,
97
- telegramDeliveryIdentities: agent.turn.telegramDeliveryLedger.snapshot(),
97
+ telegramDeliveryIdentities: agent.turn.telegramDeliveryLedger?.snapshot?.() ?? [],
98
98
  },
99
99
  microCompaction: { cutoff: agent.microCompaction.cutoff },
100
100
  };
@@ -211,7 +211,7 @@ function restoreAgentResumeSnapshot(agent, snapshot) {
211
211
  agent.turn.turnId = Number.isInteger(snapshot.turn.turnId) ? snapshot.turn.turnId : -1;
212
212
  agent.turn.activeTurn = 'resuming';
213
213
  agent.turn.steerBuffer.length = 0;
214
- agent.turn.telegramDeliveryLedger.restore(snapshot.turn.telegramDeliveryIdentities);
214
+ agent.turn.telegramDeliveryLedger?.restore?.(snapshot.turn.telegramDeliveryIdentities);
215
215
  agent.microCompaction.cutoff = Number.isInteger(snapshot.microCompaction?.cutoff)
216
216
  ? Math.max(0, Math.min(snapshot.context.history.length, snapshot.microCompaction.cutoff))
217
217
  : 0;