blun-king-cli 9.1.536 → 9.1.561

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 (129) hide show
  1. package/LIESMICH.txt +13 -869
  2. package/README.md +41 -833
  3. package/bin/assistant-message-offload-policy.cjs +3 -1
  4. package/bin/compaction-transaction-policy.cjs +122 -0
  5. package/bin/context-performance-policy.cjs +2 -5
  6. package/bin/context-pressure-policy.cjs +20 -0
  7. package/bin/cron-run-output.cjs +45 -0
  8. package/bin/cron-run-store.cjs +145 -0
  9. package/bin/default-model-output-budget-policy.cjs +28 -0
  10. package/bin/durable-task-resume-policy.cjs +130 -0
  11. package/bin/durable-task-resume-runtime.cjs +117 -0
  12. package/bin/durable-task-resume-store.cjs +88 -0
  13. package/bin/editable-tool-approval-policy.cjs +540 -0
  14. package/bin/editable-tool-approval-runtime.cjs +99 -0
  15. package/bin/file-observation-policy.cjs +133 -0
  16. package/bin/html-to-research-markdown.cjs +146 -0
  17. package/bin/launcher-runtime.js +0 -1
  18. package/bin/micro-compaction-policy.cjs +64 -0
  19. package/bin/mnemo-connect-heartbeat.cjs +1 -3
  20. package/bin/programmatic-tool-runtime.mjs +330 -4
  21. package/bin/read-continuation-policy.cjs +36 -5
  22. package/bin/retry-checkpoint-policy.cjs +13 -0
  23. package/bin/scoped-cron-run-policy.cjs +358 -0
  24. package/bin/session-checkpoint-policy.cjs +25 -0
  25. package/bin/startup-preferences.cjs +4 -3
  26. package/bin/structured-agent-swarm-output.cjs +325 -0
  27. package/bin/subagent-context-fork-policy.cjs +155 -0
  28. package/bin/subagent-skill-policy.cjs +204 -0
  29. package/bin/telegram-approval-relay.cjs +2 -1
  30. package/bin/tool-file-persistence.cjs +141 -0
  31. package/bin/tool-result-offload-policy.cjs +25 -33
  32. package/bin/turn-thinking-policy.cjs +2 -26
  33. package/bin/update-notice.js +30 -18
  34. package/bin/user-message-offload-policy.cjs +3 -1
  35. package/blun.mjs +1564 -640
  36. package/codebase-index/README.md +12 -0
  37. package/codebase-index/codebase_index.py +129 -18
  38. package/package.json +23 -58
  39. package/telegram-plugin/bin/telegram-mnemo-capture.cjs +1 -3
  40. package/telegram-plugin/bin/telegram-typing-keepalive.cjs +89 -0
  41. package/telegram-plugin/dist/bridge.mjs +8 -1
  42. package/CHANGELOG.md +0 -321
  43. package/agent-spine-plugin/CHANGELOG.md +0 -406
  44. package/agent-spine-plugin/CONTRIBUTING.md +0 -52
  45. package/agent-spine-plugin/README.md +0 -344
  46. package/agent-spine-plugin/SECURITY.md +0 -47
  47. package/agent-spine-plugin/docs/acceptance.md +0 -61
  48. package/agent-spine-plugin/docs/architecture.md +0 -183
  49. package/agent-spine-plugin/docs/attention.md +0 -121
  50. package/agent-spine-plugin/docs/automatic-continuity.md +0 -79
  51. package/agent-spine-plugin/docs/channel-runtime.md +0 -92
  52. package/agent-spine-plugin/docs/coordination.md +0 -138
  53. package/agent-spine-plugin/docs/feed-transport.md +0 -99
  54. package/agent-spine-plugin/docs/gateway-runtime.md +0 -116
  55. package/agent-spine-plugin/docs/harness-reference.md +0 -45
  56. package/agent-spine-plugin/docs/host-integration.md +0 -129
  57. package/agent-spine-plugin/docs/https-transport.md +0 -116
  58. package/agent-spine-plugin/docs/learning.md +0 -133
  59. package/agent-spine-plugin/docs/object-transport.md +0 -93
  60. package/agent-spine-plugin/docs/peer-transport.md +0 -88
  61. package/agent-spine-plugin/docs/preflight-recall.md +0 -69
  62. package/agent-spine-plugin/docs/preservation-contract.md +0 -53
  63. package/agent-spine-plugin/docs/quality-gates.md +0 -50
  64. package/agent-spine-plugin/docs/relationships.md +0 -73
  65. package/agent-spine-plugin/docs/releasing.md +0 -83
  66. package/agent-spine-plugin/docs/roadmap.md +0 -307
  67. package/agent-spine-plugin/docs/selfstarter.md +0 -88
  68. package/agent-spine-plugin/docs/session-briefing.md +0 -74
  69. package/agent-spine-plugin/docs/shared-memory.md +0 -259
  70. package/agent-spine-plugin/docs/source-roots.md +0 -86
  71. package/agent-spine-plugin/docs/sqlite-transport.md +0 -76
  72. package/agent-spine-plugin/scripts/check-hosts.js +0 -195
  73. package/agent-spine-plugin/scripts/check-install.js +0 -569
  74. package/agent-spine-plugin/scripts/check-syntax.js +0 -29
  75. package/agent-spine-plugin/scripts/github-actions.js +0 -11
  76. package/agent-spine-plugin/scripts/release-check.js +0 -128
  77. package/agent-spine-plugin/scripts/run-acceptance.js +0 -19
  78. package/agent-spine-plugin/scripts/run-checks.js +0 -46
  79. package/agent-spine-plugin/scripts/run-tests-hermetic.js +0 -73
  80. package/agent-spine-plugin/spine-example/1-identity.md +0 -12
  81. package/agent-spine-plugin/spine-example/2-voice.md +0 -6
  82. package/agent-spine-plugin/spine-example/3-conduct.md +0 -8
  83. package/agent-spine-plugin/spine-example/4-history.md +0 -4
  84. package/bin/empty-response-retry-policy.cjs +0 -29
  85. package/bin/fredrik-glm-provider.cjs +0 -256
  86. package/bin/package-regression-policy.cjs +0 -77
  87. package/fredrik-glm-profile.toml.example +0 -26
  88. package/release-planned-removals.json +0 -15
  89. package/scripts/check-active-profile-plugin-startup.js +0 -36
  90. package/scripts/check-active-work-steer-regression.js +0 -46
  91. package/scripts/check-approval-observability-regression.js +0 -111
  92. package/scripts/check-approval-queue-shortcuts-regression.js +0 -65
  93. package/scripts/check-bundled-agent-spine-regression.js +0 -48
  94. package/scripts/check-codebase-search-packaging-regression.js +0 -92
  95. package/scripts/check-copy-command-regression.js +0 -74
  96. package/scripts/check-current-turn-read-pin-mutation-regression.js +0 -72
  97. package/scripts/check-current-turn-read-pin-regression.js +0 -94
  98. package/scripts/check-deepseek-native-max-regression.js +0 -49
  99. package/scripts/check-empty-response-effort-downgrade-regression.js +0 -48
  100. package/scripts/check-fredrik-glm-mutation-regression.js +0 -18
  101. package/scripts/check-fredrik-glm-regression.js +0 -169
  102. package/scripts/check-historical-tool-result-preview-regression.js +0 -77
  103. package/scripts/check-history-pressure-offload-regression.js +0 -77
  104. package/scripts/check-mcp-startup-wait-budget.js +0 -48
  105. package/scripts/check-package-regression.js +0 -38
  106. package/scripts/check-plugin-startup-regression.js +0 -53
  107. package/scripts/check-programmatic-context-isolation-regression.js +0 -193
  108. package/scripts/check-programmatic-tool-regression.js +0 -294
  109. package/scripts/check-queue-controls-regression.js +0 -189
  110. package/scripts/check-release-metadata.js +0 -103
  111. package/scripts/check-reload-agent-spine-regression.js +0 -76
  112. package/scripts/check-resume-replay-regression.js +0 -102
  113. package/scripts/check-session-cancel-regression.js +0 -43
  114. package/scripts/check-session-picker-resume-metrics-regression.js +0 -97
  115. package/scripts/check-session-start-hook-context-regression.js +0 -228
  116. package/scripts/check-shell-terminal-isolation-regression.js +0 -81
  117. package/scripts/check-slash-escape-regression.js +0 -89
  118. package/scripts/check-startup-swarm-command-regression.js +0 -24
  119. package/scripts/check-structured-subagent-output-regression.js +0 -331
  120. package/scripts/check-telegram-bridge-watchdog.js +0 -60
  121. package/scripts/check-telegram-direct-work-resume-regression.js +0 -53
  122. package/scripts/check-telegram-loop-exactly-once-regression.js +0 -71
  123. package/scripts/check-todo-loop-regression.js +0 -78
  124. package/scripts/check-todo-progress-regression.js +0 -416
  125. package/scripts/check-todo-recovery-catalog-regression.js +0 -50
  126. package/scripts/check-tool-schema-capacity-regression.js +0 -40
  127. package/scripts/programmatic-tool-runtime.test.mjs +0 -365
  128. package/scripts/structured-subagent-output.test.cjs +0 -170
  129. /package/{scripts → bin}/fix-node-pty-perms.js +0 -0
@@ -1,259 +0,0 @@
1
- # Shared memory adapters
2
-
3
- AgentSpine can exchange reviewed context between installations without requiring a vendor account, hosted database, or network service. The first reference adapter uses an ordinary directory. That directory may remain local or be placed on a user-controlled network drive or synchronization service.
4
-
5
- A signed directory can also be exported as an immutable JSON file for the hardened, provider-neutral [HTTPS snapshot transport](https-transport.md). HTTPS is only another delivery path: all signer trust, quarantine, local review, privacy, history, and authority rules remain identical.
6
-
7
- Shared memory is optional. Local discovery, identity, learning, relationships, attention, coordination, and auditing remain complete when no adapter exists.
8
-
9
- ## Trust flow
10
-
11
- ```mermaid
12
- flowchart LR
13
- L["Locally accepted learning"]
14
- P["Owner-confirmed publish"]
15
- E["Immutable shared event"]
16
- Q["Receiving quarantine"]
17
- R["Second local review"]
18
- C["Privacy-filtered shared context"]
19
- L --> P --> E --> Q --> R --> C
20
- Q -. "not visible" .-> C
21
- C -. "never grants" .-> A["Host or delegation authority"]
22
- ```
23
-
24
- Only an accepted local learning record can be published. The receiving installation imports the event as `pending`; it is absent from `shared_context` until a local user explicitly accepts it. This double-review model treats transport integrity and human trust as separate concerns.
25
-
26
- AgentSpine never shares:
27
-
28
- - private learning;
29
- - source Markdown or document contents;
30
- - evidence summaries or conversation transcripts;
31
- - delegation policy or assignment grants;
32
- - tasks, attention cues, credentials, or host settings;
33
- - graph state or private relationship profiles.
34
-
35
- ## Directory adapter
36
-
37
- Initialize a scope in a directory outside the scanned project:
38
-
39
- ```bash
40
- agentspine share-init /srv/agent-memory/team-alpha \
41
- --scope team:alpha \
42
- --adapter adapter:team-alpha \
43
- --confirm-local-share
44
- ```
45
-
46
- The resulting layout is deliberately portable:
47
-
48
- ```text
49
- team-alpha/
50
- .agentspine-exchange.json
51
- events/
52
- <sha256-of-event-id>.json
53
- ```
54
-
55
- The manifest binds the directory to one stable scope. Events are immutable JSON records. Their filenames derive from stable event IDs, which gives different writers deterministic collision behavior without relying on a central server.
56
-
57
- The directory must be outside the scanned project, including after symlink resolution. Adapter manifests, event files, and the event directory must be regular filesystem objects rather than symlinks. A pull is capped at 2,000 events and 20 MiB; each manifest or event is capped at 64 KiB.
58
-
59
- ## Authenticated signed mode
60
-
61
- The baseline adapter uses canonical SHA-256 integrity for compatibility. Anyone with write access can replace a baseline event and recompute its digest. Signed mode adds Ed25519 origin authentication while preserving the same event payload, quarantine, privacy, and review workflow.
62
-
63
- ```mermaid
64
- flowchart LR
65
- K["Publisher private key\ninstallation-local"] --> S["Signed manifest + events"]
66
- P["Exported public identity"] --> T["Receiver project trust store"]
67
- S --> V["Signature verification"]
68
- T --> V
69
- V --> Q["Pending quarantine"]
70
- Q --> R["Independent local content review"]
71
- R --> C["Context-only memory"]
72
- V -. "authenticates key, not permission" .-> A["No host or delegation authority"]
73
- ```
74
-
75
- Create a local signer and export only its public identity:
76
-
77
- ```bash
78
- agentspine share-keygen signer:team-alpha \
79
- --root /path/to/publisher-project \
80
- --public-out /safe/exchange/team-alpha-signer.json \
81
- --confirm-local-share
82
- ```
83
-
84
- The private key is generated under the AgentSpine installation state directory. It is never returned by the command, copied into the project, placed in the adapter, exposed through MCP, or injected by hooks. The exported JSON contains the Ed25519 public key, stable signer ID, cryptographic key fingerprint, creation time, and integrity digest.
85
-
86
- The receiving project must trust that exact exported key through a genuine local owner action:
87
-
88
- ```bash
89
- agentspine share-trust /safe/exchange/team-alpha-signer.json \
90
- --root /path/to/receiving-project \
91
- --confirm-local-share
92
-
93
- agentspine share-trust-list /path/to/receiving-project --json
94
- ```
95
-
96
- Create and publish through a signed adapter:
97
-
98
- ```bash
99
- agentspine share-init /srv/agent-memory/team-alpha \
100
- --root /path/to/publisher-project \
101
- --scope team:alpha \
102
- --signer signer:team-alpha \
103
- --confirm-local-share
104
-
105
- agentspine share-publish /srv/agent-memory/team-alpha \
106
- --root /path/to/publisher-project \
107
- --learning learning:release-process \
108
- --id shared:release-process-v1 \
109
- --signer signer:team-alpha \
110
- --confirm-local-share
111
- ```
112
-
113
- Require authentication at the receiver:
114
-
115
- ```bash
116
- agentspine share-pull /srv/agent-memory/team-alpha \
117
- --root /path/to/receiving-project \
118
- --require-authenticated
119
- ```
120
-
121
- Pull verifies the signed manifest and every signed event before any quarantine write. A signed adapter cannot mix unsigned events, and an unsigned adapter cannot smuggle signed files. Each event signer is checked independently, so a trusted adapter owner does not implicitly trust another writer.
122
-
123
- ### What a signature means
124
-
125
- A valid signature proves that the envelope matches the private key corresponding to a public key explicitly trusted for this receiving project. It does not prove the operator's legal identity, truthfulness, current role, relationship, or permission. It does not approve the claim, grant delegation, authorize a tool, or bypass the second local content review.
126
-
127
- Public-key exchange must therefore use a channel appropriate to the deployment. Compare the full key fingerprint out of band when identity matters. Encrypt the transport when metadata confidentiality matters; signatures provide authenticity and integrity, not encryption.
128
-
129
- ### Rotation and revocation
130
-
131
- Rotation is explicit and never overwrites the old public identity silently:
132
-
133
- ```bash
134
- agentspine share-keygen signer:team-alpha \
135
- --root /path/to/publisher-project \
136
- --rotate \
137
- --public-out /safe/exchange/team-alpha-signer-v2.json \
138
- --confirm-local-share
139
- ```
140
-
141
- The installation retains the retired public identity in history and removes the retired local private key. Receivers import the new public identity as a separate trusted fingerprint. Old signatures remain cryptographically verifiable as long as the old public trust record remains available.
142
-
143
- Revoke a compromised or retired key locally:
144
-
145
- ```bash
146
- agentspine share-trust-revoke ed25519:<full-fingerprint> \
147
- --root /path/to/receiving-project \
148
- --reason "Key retired after verified rotation" \
149
- --confirm-local-share
150
- ```
151
-
152
- Revocation blocks new acceptance immediately. If already accepted context depends on that key, shared context and audit fail closed until the user rolls back, rejects, or permanently deletes the affected import. Revocation never rewrites source Markdown or remote adapter files.
153
-
154
- ## Publish and receive
155
-
156
- Publish an already accepted learning:
157
-
158
- ```bash
159
- agentspine share-publish /srv/agent-memory/team-alpha \
160
- --root /path/to/source-project \
161
- --learning learning:release-process \
162
- --id shared:release-process-v1 \
163
- --confirm-local-share
164
- ```
165
-
166
- Pull on another installation:
167
-
168
- ```bash
169
- agentspine share-pull /srv/agent-memory/team-alpha \
170
- --root /path/to/receiving-project
171
-
172
- agentspine share-inbox /path/to/receiving-project --status pending --json
173
- ```
174
-
175
- Pulling reads the adapter and writes only to the receiving project's external `sharing.json`. It never changes the adapter, the project, or accepted context. Repeated and concurrent pulls are idempotent.
176
-
177
- Review locally, then read:
178
-
179
- ```bash
180
- agentspine share-review shared:release-process-v1 \
181
- --root /path/to/receiving-project \
182
- --decision accept \
183
- --reason "Confirmed for this installation" \
184
- --confirmed-by-user
185
-
186
- agentspine share-context /path/to/receiving-project \
187
- --scope team:alpha \
188
- --json
189
- ```
190
-
191
- `--confirmed-by-user` is an integration attestation, not authentication. A wrapper must bind it to a real local user action. It must never be inferred from an imported event, another agent, memory, Markdown, a task, or a prior confirmation at the publishing installation.
192
-
193
- ## Event contract
194
-
195
- Every `agentspine.shared-event/v1` contains only:
196
-
197
- - stable event, scope, and origin-instance IDs;
198
- - learning kind and descriptive claim;
199
- - optional subject and exact group scope;
200
- - confidence and publication timestamp;
201
- - minimal provenance: source learning ID, acceptance timestamp, automatic/manual marker, evidence count, and a digest of the original review proof;
202
- - optional predecessor event ID;
203
- - `authority: context-only`;
204
- - a SHA-256 digest over canonical JSON.
205
-
206
- Signed mode wraps the unchanged event in an `agentspine.signed-envelope/v1` containing a strict public identity, envelope kind, signing timestamp, `context-only` marker, and Ed25519 signature. The receiver retains the minimal signature proof with the quarantined record and replays it during reads and audits. Public keys and signatures are intentionally omitted from hook text and MCP context results; callers see only a signer ID, key fingerprint, and verification time.
207
-
208
- Unknown fields are rejected. IDs are identifiers, not identities or authentication claims. An `originInstanceId` distinguishes local installations but does not prove who operated one.
209
-
210
- The baseline digest detects accidental damage and unsophisticated mutation. It is not a signature: anyone who can write the adapter directory can replace an event and recompute its digest. Signed mode detects that replacement unless the attacker also controls a locally trusted private key. Both modes retain quarantine because authentication and content trust are different decisions.
211
-
212
- ## Supersession and rollback
213
-
214
- New shared information does not overwrite old context. Publish the replacement with an explicit predecessor:
215
-
216
- ```bash
217
- agentspine share-publish /srv/agent-memory/team-alpha \
218
- --root /path/to/source-project \
219
- --learning learning:release-process-v2 \
220
- --id shared:release-process-v2 \
221
- --supersedes shared:release-process-v1 \
222
- --confirm-local-share
223
- ```
224
-
225
- The adapter verifies that predecessor and replacement retain the same kind, subject, and privacy scope. When the receiver accepts the replacement, the old active record becomes `superseded` and remains in history. A rollback restores it atomically:
226
-
227
- ```bash
228
- agentspine share-rollback shared:release-process-v2 \
229
- --root /path/to/receiving-project \
230
- --reason "The replacement was incorrect"
231
- ```
232
-
233
- Permanent local deletion is CLI-only, requires `--confirm-local-share`, and removes the selected import plus retained local versions. It does not delete the immutable event from the shared adapter.
234
-
235
- ## Privacy and groups
236
-
237
- Only `shared` and `group` learning can be published. Group events retain the exact group ID and optional subject. The receiving installation must know that group and its visible membership before acceptance. `includePrivate` cannot bypass a missing or different group audience.
238
-
239
- Lifecycle hooks have no group audience. They expose only counts and kinds of already accepted, non-private shared context. They never inject claims, subjects, adapter paths, pending inbox records, or event provenance.
240
-
241
- ## MCP boundary
242
-
243
- MCP exposes only `shared_context`, which reads locally accepted records. Adapter initialization, publication, pulling, inbox review, rollback, configuration, deletion, key generation, rotation, trust, and revocation remain local CLI operations. An agent therefore cannot use AgentSpine's MCP surface to connect an arbitrary path, export data, accept its own import, read a private key, trust itself, or widen trust.
244
-
245
- ## Adapter compatibility
246
-
247
- The implemented HTTPS, mutable-feed, peer, and local SQLite adapters map their transport back into the reference directory semantics before local import. Future hosted adapters must produce the same strict manifest and event semantics or map their transport into them before local import:
248
-
249
- 1. immutable stable event IDs;
250
- 2. canonical integrity digest and, for authenticated transports, a verifiable signed envelope;
251
- 3. one explicit scope per adapter connection;
252
- 4. no private, authority, source-document, evidence-text, task, or policy payloads;
253
- 5. quarantine before local review;
254
- 6. idempotent import and collision detection;
255
- 7. retained supersession history and rollback;
256
- 8. no dependency for local AgentSpine operation.
257
- 9. explicit key trust, rotation, and revocation without converting signatures into authority.
258
-
259
- Transport plugins may strengthen authenticity, encryption, retention, and remote access control. They may never weaken privacy filtering, local confirmation, the context-only authority marker, or protected-source preservation.
@@ -1,86 +0,0 @@
1
- # Host-native source roots
2
-
3
- AgentSpine `0.8.0` resolves active user, project, and host-memory sources before every production lifecycle hook. Resolution does not depend on the directory from which the plugin was installed, and it never treats the entire home directory as one project.
4
-
5
- ## Resolution contract
6
-
7
- ```mermaid
8
- flowchart LR
9
- H["Native hook payload"] --> R["Provider-neutral source-root resolver"]
10
- C["Claude profile"] --> R
11
- X["Codex profile"] --> R
12
- P["Active project chain"] --> R
13
- B["Explicit local state binding"] --> R
14
- R --> U["User-wide sources"]
15
- R --> J["Exact project sources"]
16
- R --> M["Exact Claude project memory"]
17
- U --> S["Byte-budgeted session_briefing"]
18
- J --> S
19
- M --> S
20
- ```
21
-
22
- Claude resolution follows the documented user and project hierarchy: `CLAUDE_CONFIG_DIR` or `~/.claude`, user `CLAUDE.md` and rules, the active project chain, and the exact project-memory directory evidenced by `autoMemoryDirectory`, `CLAUDE_CODE_PROJECT_DIR_NAME`, or the native hook `transcript_path`. Inside that directory, `MEMORY.md` is the only index. The live path never enumerates that directory and never follows links found inside a fact file. AgentSpine does not guess Claude's private project-directory encoding. See Anthropic's [memory hierarchy and storage-location documentation](https://code.claude.com/docs/en/memory) and [Claude configuration directory documentation](https://code.claude.com/docs/en/claude-directory).
23
-
24
- ## Indexed and lazy memory
25
-
26
- Every direct index link is counted as indexed, but its target is opened only when a marker proves relevance:
27
-
28
- ```markdown
29
- - [Communication style](style.md) <!-- agentspine:always -->
30
- - [BLUN project](projects/blun.md) <!-- agentspine:project=project:blun -->
31
- - [Alpha team](groups/alpha.md) <!-- agentspine:group=group:alpha -->
32
- - [Owner preference](people/owner.md) <!-- agentspine:entity=person:owner -->
33
- - [Current handoff](tasks/handoff.md) <!-- agentspine:task=task:handoff -->
34
- - [Carbonara preference](food/pasta.md) <!-- agentspine:keywords=carbonara,pasta -->
35
- ```
36
-
37
- An exact person, project, group, or task ID must match the current hook scope. Prompt relevance requires a normalized keyword match against the link label, filename, or explicit `keywords` marker. If relevance is uncertain, the target remains unopened. The index itself is always loaded so the host retains its native memory overview.
38
-
39
- The persistent cache lives under AgentSpine's platform state directory, outside ordinary agent projects. If the active project root is exactly a recognized user home, the configured AgentSpine state subtree may be below that root but is explicitly pruned before source enumeration; it is never context. The exception does not apply to nested project roots. The cache stores integrity-checked snapshots keyed by an opaque root digest and relative path. A cache hit still opens and validates the original path and file identity, but does not reread or rehash unchanged source bytes. Corrections, deletion, link removal, source-binding rollback, purge, restart, and compaction invalidate or prune the affected cache record immediately. Cache contents and relevance markers are context-only.
40
-
41
- Indexed targets use no-follow open semantics. Parent components, canonical scope, regular-file status, size, identity, modification metadata, and the pathname-to-handle identity are checked around the same read. A changing target is retried a bounded number of times and then rejected as a race; mixed snapshots are never injected.
42
-
43
- The live resolver processes at most 128 direct index links, one target at a time, under the host resolver's two-second work budget. `MEMORY.md` and each target are limited to 4 MiB, while the complete external cache is capped at 16 MiB. Exceeding a bound fails closed instead of widening discovery.
44
-
45
- Codex resolution uses `CODEX_HOME` or `~/.codex`, selects `AGENTS.override.md` before `AGENTS.md` at user scope, and walks from the configured project root to `cwd`. Per directory it selects override, regular, then the configured `project_doc_fallback_filenames`; `project_root_markers` and `project_doc_max_bytes` are read from the active profile's `config.toml`. Without a root marker, only `cwd` is the project root. See OpenAI's [AGENTS.md discovery order](https://developers.openai.com/codex/agent-configuration/agents-md) and [configuration reference](https://developers.openai.com/codex/config-reference).
46
-
47
- Only regular files under these evidenced roots are read. Symlinks are skipped. The resolver caps source count, per-file bytes, aggregate bytes, and recursive host-rule files. A project-root scan is never run when the resolved root is the user's home directory. Foreign repositories and arbitrary hidden directories are not traversed.
48
-
49
- ## Portable user continuity
50
-
51
- Accepted preferences, no-gos, corrections, and references can be attached once to the local user through an explicit state binding. The binding references the existing external AgentSpine state; it does not copy records between project hashes.
52
-
53
- ```bash
54
- agentspine source-bind /path/where/continuity-was-configured \
55
- --host all \
56
- --scope state-user \
57
- --project /current/project \
58
- --host-home /current/profile \
59
- --confirm-local-binding
60
- ```
61
-
62
- Only portable low-risk learning and the known person relationship context are read from this binding. Project facts, tasks, attention events, group/private project content, delegation policy, execution grants, jobs, secrets, and trust material remain in the exact project state. The registry is append-audited and supports explicit rollback and purge:
63
-
64
- ```bash
65
- agentspine source-status --host claude --cwd /current/project --json
66
- agentspine source-rollback binding:ID --confirm-local-binding
67
- agentspine source-purge binding:ID --confirm-local-binding
68
- ```
69
-
70
- Bindings and their provenance are context-only. They cannot create identity equivalence, roles, permissions, delegation, host trust, or self-starter rights.
71
-
72
- ## Empty and damaged state
73
-
74
- Hook context includes a bounded `sourceResolution` report with checked scopes, counts, profile digest, project root, and the concrete empty or fail-closed reason. Indexed-memory diagnostics add counts for indexed, relevant, loaded, cache hits, cache misses, missing targets, scope omissions, path escapes, symlinks, size rejection, races, and live directory enumeration. They never include fact contents or fact paths. `agentspine doctor --host claude|codex --cwd … --json`, `agentspine source-status`, and `agentspine audit … --host … --json` expose the same report.
75
-
76
- Live hooks never enumerate orphaned files. An operator can request the separate bounded offline diagnostic explicitly:
77
-
78
- ```bash
79
- agentspine doctor --host claude --cwd /current/project --offline-memory-orphans --json
80
- ```
81
-
82
- It reports counts only, reads no orphan content, follows no symlinks, and grants no cleanup or deletion authority.
83
-
84
- The installed-bundle check reproduces the original zero-source failure from an AgentSpine checkout and a foreign `cwd`, repeats restart and compaction, exercises custom Claude and Codex homes, Codex fallback and nested override precedence, and proves no broad home scan, no foreign-project visibility, zero model-side MCP calls, exactly one hook set, and unchanged source bytes.
85
-
86
- The scale acceptance creates 50,000 real unindexed files beside six indexed entries. For the fully matching scope it records five loaded facts, seven safe opens (`MEMORY.md` twice plus five targets), zero directory enumerations, and no opens, reads, hashes, or counts for the 50,000 files. The same instrumentation result is independent of the unindexed file count; wall-clock thresholds are deliberately not used as a correctness oracle.
@@ -1,76 +0,0 @@
1
- # Local SQLite snapshot transport
2
-
3
- AgentSpine can retain authenticated shared-memory snapshots in one operator-selected SQLite file. The adapter is local, provider-neutral, optional, and implemented with Node.js `node:sqlite`; it adds no package dependency or cloud account. SQLite commands require Node.js 22.13 or newer for the hardened read-only and extension-control options. The rest of AgentSpine continues to support the runtime range declared by the package.
4
-
5
- The database is a transport history, not a new source of identity, policy, or canonical memory. It stores complete signed snapshots outside the scanned project and imports only the latest verified snapshot into the existing quarantine.
6
-
7
- ```mermaid
8
- sequenceDiagram
9
- participant O as Local owner CLI
10
- participant D as Signed directory adapter
11
- participant S as SQLite file outside project
12
- participant R as Receiving project
13
- O->>D: Build independently validated signed snapshot
14
- O->>S: BEGIN IMMEDIATE
15
- S->>S: Validate schema, integrity, metadata,<br/>all prior snapshots, and hash chain
16
- O->>S: Append immutable revision + compare current head
17
- O->>S: COMMIT revision and head atomically
18
- R->>S: Read-only integrity and full-chain validation
19
- R->>R: Verify snapshot + every nested signature
20
- R->>R: Import claims only into quarantine
21
- ```
22
-
23
- ## Initialize and publish
24
-
25
- Start with an authenticated directory adapter. Keep the SQLite file outside every scanned agent project:
26
-
27
- ```bash
28
- agentspine share-sqlite-init /srv/agent-memory/team-alpha \
29
- --root /path/to/publisher-project \
30
- --database /srv/agentspine-db/team-alpha.sqlite \
31
- --confirm-local-share
32
-
33
- agentspine share-sqlite-publish /srv/agent-memory/team-alpha \
34
- --root /path/to/publisher-project \
35
- --database /srv/agentspine-db/team-alpha.sqlite \
36
- --id snapshot:team-alpha-2026-08-28 \
37
- --confirm-local-share
38
- ```
39
-
40
- Initialization permanently binds the database to the directory adapter's signed manifest, scope, adapter ID, signer ID, and key ID. A later command cannot silently repurpose the file for another adapter or signing identity. Initialization and every publication require an explicit local owner confirmation.
41
-
42
- Publication uses `BEGIN IMMEDIATE`, validates the entire retained state before mutation, then appends one revision and advances the single head in the same transaction. Repeating the exact same snapshot is idempotent. Different snapshots receive monotonically increasing sequence numbers and link to the previous revision digest. There is no update or delete command for revision history.
43
-
44
- ## Inspect and pull
45
-
46
- ```bash
47
- agentspine share-sqlite-inspect \
48
- --root /path/to/project \
49
- --database /srv/agentspine-db/team-alpha.sqlite
50
-
51
- agentspine share-sqlite-pull \
52
- --root /path/to/receiver-project \
53
- --database /srv/agentspine-db/team-alpha.sqlite
54
- ```
55
-
56
- Inspection reports only transport metadata, hashes, counts, and context-only authority. Pull opens the database read-only, validates its SQLite integrity, exact application schema, authenticated manifest, every retained snapshot, the complete revision chain, and the atomic head. It then sends the latest snapshot through the normal signed importer. Received claims remain pending and invisible until a second local user review.
57
-
58
- ## Database contract
59
-
60
- The v1 file has exactly three AgentSpine tables:
61
-
62
- - `agentspine_meta`: one immutable binding to a signed directory manifest;
63
- - `agentspine_revisions`: append-only signed-snapshot JSON plus a SHA-256 revision chain;
64
- - `agentspine_head`: one transactionally advanced pointer to the newest revision.
65
-
66
- Unexpected tables, views, triggers, indexes, schema versions, rows, broken digests, invalid JSON, malformed snapshots, signature failures, metadata changes, missing links, head mismatches, files over 128 MiB, symbolic-link or hard-link database and sidecar paths, and database paths inside the scanned project fail closed. SQLite extensions and double-quoted string literals are disabled, foreign keys and `trusted_schema=OFF` are set on every connection, defensive mode is enabled when the runtime exposes it, and SQL values use bound parameters.
67
-
68
- The file contains repeated full snapshots so every retained revision is independently verifiable. It is intentionally capped at 1,000 revisions, 2,000 events per snapshot through the shared snapshot contract, 21 MiB per snapshot, and 128 MiB for the database and retained JSON validation budget. Operators should rotate to a new explicitly initialized file before reaching a limit; AgentSpine does not compact or delete history automatically.
69
-
70
- ## Security and authority boundary
71
-
72
- Filesystem access to the SQLite file is the transport access-control boundary. AgentSpine requests owner-only file mode on platforms that support POSIX permissions, but operators remain responsible for directory permissions, backups, disk encryption, copying, OS-level locks, and physical device security. The adapter is not a multi-tenant database server, a replication protocol, or a remote credential store.
73
-
74
- SQLite atomicity, integrity checks, hash chains, snapshot digests, and Ed25519 signatures detect defined corruption and origin-key mismatch. They do not establish real-world identity, freshness beyond the retained local head, truth, permission, delegation, production access, spending rights, or policy exceptions. Restoring an older but internally valid database copy cannot be distinguished without a separately retained external checkpoint.
75
-
76
- Database initialization, publication, inspection, paths, and pull are absent from MCP and lifecycle hooks. Agents cannot select a database, write a revision, approve an import, or gain database access through AgentSpine's agent-controlled surfaces. Existing `AGENTS.md`, `CLAUDE.md`, `SOUL.md`, `MEMORY.md`, and every other discovered Markdown source remain in place and byte-for-byte unchanged.
@@ -1,195 +0,0 @@
1
- #!/usr/bin/env node
2
- import { spawn } from "node:child_process";
3
- import { readFile, stat } from "node:fs/promises";
4
- import { isAbsolute, relative, resolve } from "node:path";
5
- import { fileURLToPath } from "node:url";
6
-
7
- function assert(condition, message) {
8
- if (!condition) throw new Error(message);
9
- }
10
-
11
- async function json(root, path) {
12
- return JSON.parse(await readFile(resolve(root, path), "utf8"));
13
- }
14
-
15
- function expand(value, variable, root) {
16
- assert(typeof value === "string" && value.length > 0, "host command values must be non-empty strings");
17
- return value.split(`\${${variable}}`).join(root);
18
- }
19
-
20
- async function validateEntrypoint(root, value) {
21
- const target = resolve(value);
22
- const within = relative(root, target);
23
- assert(within && !within.startsWith("..") && !isAbsolute(within), "MCP entrypoint must remain inside the plugin");
24
- const metadata = await stat(target);
25
- assert(metadata.isFile(), "MCP entrypoint must be a regular file");
26
- }
27
-
28
- function validateHooks(root, hooks, { required, commandRoot }) {
29
- assert(hooks && typeof hooks === "object" && !Array.isArray(hooks), "hook bundle is missing");
30
- assert(Object.keys(hooks).every((key) => ["description", "hooks"].includes(key)), "hook bundle contains unsupported top-level metadata");
31
- assert(hooks.description && typeof hooks.description === "string", "hook bundle description is missing");
32
- for (const event of required) {
33
- const registrations = hooks.hooks?.[event];
34
- assert(Array.isArray(registrations) && registrations.length === 1, `${event} must have exactly one registration`);
35
- assert(Array.isArray(registrations[0].hooks) && registrations[0].hooks.length === 1, `${event} must have exactly one hook command`);
36
- const command = registrations[0].hooks[0];
37
- assert(command.type === "command", `${event} must use a command hook`);
38
- assert(command.command === `node "\${${commandRoot}}/src/hook.js"`, `${event} must use the bundled lifecycle adapter`);
39
- assert(Number.isInteger(command.timeout) && command.timeout > 0 && command.timeout <= 15, `${event} timeout is unsafe`);
40
- }
41
- const extras = Object.keys(hooks.hooks || {}).filter((event) => !required.includes(event));
42
- assert(extras.length === 0, `unknown hook events: ${extras.join(", ")}`);
43
- return { events: required, commands: required.length, entrypoint: relative(root, resolve(root, "src/hook.js")) };
44
- }
45
-
46
- function validateBlunHooks(root, hooks) {
47
- const required = [
48
- "SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse",
49
- "PreCompact", "PostCompact", "Stop", "SubagentStop"
50
- ];
51
- assert(Array.isArray(hooks), "BLUN hook bundle is missing");
52
- assert(hooks.length === required.length, "BLUN must register exactly one command per lifecycle event");
53
- assert(JSON.stringify(hooks.map(({ event }) => event)) === JSON.stringify(required),
54
- "BLUN lifecycle events must remain complete and ordered");
55
- for (const event of required) {
56
- const registrations = hooks.filter((hook) => hook.event === event);
57
- assert(registrations.length === 1, `${event} must have exactly one BLUN registration`);
58
- const command = registrations[0];
59
- assert(command.command === 'node "./src/hook.js"', `${event} must use the bundled BLUN lifecycle adapter`);
60
- assert(Number.isInteger(command.timeout) && command.timeout > 0 && command.timeout <= 15, `${event} BLUN timeout is unsafe`);
61
- }
62
- return { events: required, commands: required.length, entrypoint: relative(root, resolve(root, "src/hook.js")) };
63
- }
64
-
65
- async function initializeServer({ label, root, variable, server, version }) {
66
- assert(server && typeof server === "object" && !Array.isArray(server), `${label} MCP registration is missing`);
67
- assert(server.command === "node", `${label} MCP registration must use the Node.js runtime`);
68
- assert(Array.isArray(server.args) && server.args.length === 1, `${label} MCP registration must name exactly one entrypoint`);
69
- const command = process.execPath;
70
- const args = server.args.map((value) => {
71
- const expanded = expand(value, variable, root);
72
- return isAbsolute(expanded) ? expanded : resolve(root, expanded);
73
- });
74
- assert(!args.some((value) => value.includes("${")), `${label} MCP registration contains an unresolved variable`);
75
- await validateEntrypoint(root, args[0]);
76
-
77
- return await new Promise((resolveResult, reject) => {
78
- const child = spawn(command, args, {
79
- cwd: root,
80
- env: { ...process.env, CLAUDE_PLUGIN_ROOT: root, PLUGIN_ROOT: root },
81
- stdio: ["pipe", "pipe", "pipe"]
82
- });
83
- let stdout = "";
84
- let stderr = "";
85
- let settled = false;
86
- const finish = (error, value) => {
87
- if (settled) return;
88
- settled = true;
89
- clearTimeout(timer);
90
- const settle = () => {
91
- if (error) reject(error);
92
- else resolveResult(value);
93
- };
94
- if (child.exitCode !== null || child.signalCode !== null) return settle();
95
- child.once("close", settle);
96
- child.kill();
97
- };
98
- const timer = setTimeout(() => {
99
- finish(new Error(`${label} MCP initialize timed out${stderr ? `: ${stderr.trim()}` : ""}`));
100
- }, 3000);
101
- child.stderr.setEncoding("utf8");
102
- child.stderr.on("data", (chunk) => { stderr += chunk; });
103
- child.stdout.setEncoding("utf8");
104
- child.stdout.on("data", (chunk) => {
105
- stdout += chunk;
106
- const newline = stdout.indexOf("\n");
107
- if (newline < 0) return;
108
- try {
109
- const message = JSON.parse(stdout.slice(0, newline));
110
- assert(message.id === 1, `${label} MCP initialize returned the wrong request id`);
111
- assert(message.result?.serverInfo?.name === "agent-spine", `${label} MCP initialize returned the wrong server identity`);
112
- assert(message.result?.serverInfo?.version === version, `${label} MCP initialize returned a stale server version`);
113
- finish(null, { label, server: message.result.serverInfo.name, entrypoint: relative(root, args[0]) });
114
- } catch (error) {
115
- finish(error);
116
- }
117
- });
118
- child.once("error", (error) => finish(error));
119
- child.once("close", (code) => {
120
- if (!settled) finish(new Error(`${label} MCP server exited with ${code}${stderr ? `: ${stderr.trim()}` : ""}`));
121
- });
122
- // A real MCP host keeps stdio open after initialization. Closing stdin
123
- // here let macOS and Windows terminate the server before its queued reply.
124
- child.stdin.write(`${JSON.stringify({
125
- jsonrpc: "2.0", id: 1, method: "initialize", params: { protocolVersion: "2025-06-18" }
126
- })}\n`);
127
- });
128
- }
129
-
130
- export async function checkHosts(root = process.cwd()) {
131
- root = resolve(root);
132
- const [pkg, blunManifest, claudeManifest, claudeMcp, codexManifest, claudeHooks, codexHooks, hookVersion] = await Promise.all([
133
- json(root, "package.json"),
134
- json(root, "blun.plugin.json"),
135
- json(root, ".claude-plugin/plugin.json"),
136
- json(root, ".mcp.json"),
137
- json(root, ".codex-plugin/plugin.json"),
138
- json(root, "hooks/hooks.json"),
139
- json(root, "hooks/codex.json"),
140
- json(root, "hooks/version.json")
141
- ]);
142
- assert(blunManifest.version === pkg.version && claudeManifest.version === pkg.version && codexManifest.version === pkg.version,
143
- "host manifests must use the package cache version");
144
- assert(hookVersion.schema === "agentspine.hook-bundle/v1" && hookVersion.version === pkg.version,
145
- "hook bundle version must match the package cache version");
146
- assert(hookVersion.contract === "agentspine.preflight/v2", "hook bundle preflight contract is missing");
147
- assert(pkg.bin?.["agentspine-worker"] === "./src/worker.js", "package must register exactly one gateway worker entrypoint");
148
- await validateEntrypoint(root, resolve(root, pkg.bin["agentspine-worker"]));
149
- assert(claudeManifest.mcpServers === "./.mcp.json", "Claude manifest must explicitly reference ./.mcp.json");
150
- assert(claudeManifest.hooks === undefined, "default hooks/hooks.json must not also be registered through a supplemental manifest path");
151
- assert(codexManifest.hooks === undefined, "Codex manifest must omit unsupported hook registration fields");
152
- assert(claudeMcp.mcpServers && Object.keys(claudeMcp.mcpServers).length === 1, "Claude MCP file must contain one mcpServers registration");
153
- assert(codexManifest.mcpServers && Object.keys(codexManifest.mcpServers).length === 1, "Codex manifest must contain one MCP registration");
154
- const commonEvents = ["SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "PreCompact", "PostCompact", "Stop", "SubagentStop"];
155
- const claudeHookInventory = validateHooks(root, claudeHooks, {
156
- required: [...commonEvents, "InstructionsLoaded"], commandRoot: "CLAUDE_PLUGIN_ROOT"
157
- });
158
- const codexHookInventory = validateHooks(root, codexHooks, { required: commonEvents, commandRoot: "PLUGIN_ROOT" });
159
- const blunHookInventory = validateBlunHooks(root, blunManifest.hooks);
160
- const registrations = await Promise.all([
161
- initializeServer({ label: "blun", root, variable: "BLUN_PLUGIN_ROOT", server: blunManifest.mcpServers["agent-spine"], version: pkg.version }),
162
- initializeServer({ label: "claude", root, variable: "CLAUDE_PLUGIN_ROOT", server: claudeMcp.mcpServers["agent-spine"], version: pkg.version }),
163
- initializeServer({ label: "codex", root, variable: "PLUGIN_ROOT", server: codexManifest.mcpServers["agent-spine"], version: pkg.version })
164
- ]);
165
- return {
166
- ok: true, root, version: pkg.version, registrations,
167
- hooks: { blun: blunHookInventory, claude: claudeHookInventory, codex: codexHookInventory },
168
- hookDiscovery: {
169
- blun: "plugin-manifest", claude: "default-hooks-directory", codex: "bundled-host-adapter",
170
- trust: "host-user-required", liveTrustVerified: false
171
- },
172
- worker: { entrypoint: pkg.bin["agentspine-worker"], setsPerInstall: 1 },
173
- exactlyOnce: { mcpServersPerHost: 1, hookSetsPerHost: 1, workerSetsPerInstall: 1 },
174
- authority: "registration-check-only"
175
- };
176
- }
177
-
178
- async function main() {
179
- const args = process.argv.slice(2);
180
- let root = process.cwd();
181
- let pretty = false;
182
- for (let index = 0; index < args.length; index += 1) {
183
- if (args[index] === "--root") root = args[++index];
184
- else if (args[index] === "--json") pretty = true;
185
- else throw new Error(`unknown host-check argument: ${args[index]}`);
186
- }
187
- process.stdout.write(`${JSON.stringify(await checkHosts(root), null, pretty ? 2 : 0)}\n`);
188
- }
189
-
190
- if (process.argv[1] && resolve(process.argv[1]) === resolve(fileURLToPath(import.meta.url))) {
191
- main().catch((error) => {
192
- process.stderr.write(`AgentSpine host check failed: ${error.message}\n`);
193
- process.exitCode = 1;
194
- });
195
- }