blun-king-cli 9.1.563 → 9.1.565

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 (85) hide show
  1. package/agent-spine-plugin/.claude-plugin/marketplace.json +20 -20
  2. package/agent-spine-plugin/.claude-plugin/plugin.json +14 -14
  3. package/agent-spine-plugin/.codex-plugin/plugin.json +34 -34
  4. package/agent-spine-plugin/.mcp.json +8 -8
  5. package/agent-spine-plugin/CONTRIBUTING.md +52 -0
  6. package/agent-spine-plugin/LICENSE +186 -186
  7. package/agent-spine-plugin/README.md +394 -0
  8. package/agent-spine-plugin/SECURITY.md +47 -0
  9. package/agent-spine-plugin/assets/agentspine-banner.svg +32 -32
  10. package/agent-spine-plugin/bin/agentspine-mcp.js +4 -4
  11. package/agent-spine-plugin/bin/agentspine.js +7 -7
  12. package/agent-spine-plugin/blun.plugin.json +33 -33
  13. package/agent-spine-plugin/hooks/codex.json +47 -47
  14. package/agent-spine-plugin/hooks/hooks.json +106 -106
  15. package/agent-spine-plugin/hooks/version.json +5 -5
  16. package/agent-spine-plugin/package.json +69 -69
  17. package/agent-spine-plugin/scripts/check-hosts.js +199 -199
  18. package/agent-spine-plugin/skill/SKILL.md +76 -76
  19. package/agent-spine-plugin/skills/agent-spine/SKILL.md +85 -85
  20. package/agent-spine-plugin/src/cli.js +1488 -1442
  21. package/agent-spine-plugin/src/hook.js +886 -812
  22. package/agent-spine-plugin/src/index.js +93 -93
  23. package/agent-spine-plugin/src/lib/acceptance.js +333 -333
  24. package/agent-spine-plugin/src/lib/attention.js +755 -755
  25. package/agent-spine-plugin/src/lib/audit.js +351 -342
  26. package/agent-spine-plugin/src/lib/authentication.js +515 -515
  27. package/agent-spine-plugin/src/lib/briefing.js +317 -317
  28. package/agent-spine-plugin/src/lib/catalog.js +167 -167
  29. package/agent-spine-plugin/src/lib/channel-runtime.js +665 -665
  30. package/agent-spine-plugin/src/lib/context.js +154 -154
  31. package/agent-spine-plugin/src/lib/continuity.js +338 -338
  32. package/agent-spine-plugin/src/lib/coordination.js +577 -577
  33. package/agent-spine-plugin/src/lib/documents.js +217 -217
  34. package/agent-spine-plugin/src/lib/feed-transport.js +499 -499
  35. package/agent-spine-plugin/src/lib/filesystem-retry.js +32 -32
  36. package/agent-spine-plugin/src/lib/gateway-runtime.js +1119 -760
  37. package/agent-spine-plugin/src/lib/graph.js +337 -337
  38. package/agent-spine-plugin/src/lib/hook-audit.js +29 -0
  39. package/agent-spine-plugin/src/lib/https-transport.js +392 -392
  40. package/agent-spine-plugin/src/lib/indexed-memory-offline.js +40 -40
  41. package/agent-spine-plugin/src/lib/indexed-memory.js +281 -281
  42. package/agent-spine-plugin/src/lib/learning.js +6923 -6466
  43. package/agent-spine-plugin/src/lib/object-transport.js +206 -206
  44. package/agent-spine-plugin/src/lib/owned-file-lock.js +143 -143
  45. package/agent-spine-plugin/src/lib/paths.js +109 -109
  46. package/agent-spine-plugin/src/lib/peer-transport.js +283 -283
  47. package/agent-spine-plugin/src/lib/persona-runtime.js +581 -581
  48. package/agent-spine-plugin/src/lib/preflight.js +702 -702
  49. package/agent-spine-plugin/src/lib/runtime.js +13 -13
  50. package/agent-spine-plugin/src/lib/selfstarter.js +892 -819
  51. package/agent-spine-plugin/src/lib/sharing.js +969 -969
  52. package/agent-spine-plugin/src/lib/source-roots.js +529 -482
  53. package/agent-spine-plugin/src/lib/sqlite-transport.js +501 -501
  54. package/agent-spine-plugin/src/lib/telegram-adapter.js +119 -119
  55. package/agent-spine-plugin/src/lib/voice-runtime.js +39 -39
  56. package/agent-spine-plugin/src/mcp.js +597 -572
  57. package/agent-spine-plugin/src/version.js +1 -1
  58. package/agent-spine-plugin/src/worker.js +202 -195
  59. package/bin/active-steer-priority-policy.cjs +24 -0
  60. package/bin/launcher-runtime.js +8 -1
  61. package/bin/mnemo-tool-agent-policy.cjs +22 -0
  62. package/bin/thinking-activity-status-policy.cjs +132 -0
  63. package/bin/thinking-only-guard.cjs +75 -0
  64. package/bin/tool-call-loop-policy.cjs +53 -0
  65. package/bin/turn-thinking-policy.cjs +25 -1
  66. package/blun.mjs +554 -64
  67. package/package.json +4 -1
  68. package/standard-skills/translate-native/LICENSE +21 -21
  69. package/standard-skills/translate-native/references/evaluation-protocol.md +95 -95
  70. package/standard-skills/translate-native/references/native-orthography.md +79 -79
  71. package/standard-skills/translate-native/references/native-translation-standard.md +94 -94
  72. package/standard-skills/translate-native/references/structured-content.md +72 -72
  73. package/standard-skills/translate-native/references/translationese-review.md +77 -77
  74. package/standard-skills/translate-native/scripts/blun_language_guard.py +697 -697
  75. package/standard-skills/translate-native/scripts/check_diacritics.py +353 -353
  76. package/standard-skills/translate-native/scripts/guard_service_client.py +264 -264
  77. package/standard-skills/translate-native/scripts/language_gateway.py +62 -62
  78. package/standard-skills/translate-native/scripts/language_quality.py +377 -377
  79. package/standard-skills/translate-native/scripts/pre_output_guard.py +64 -64
  80. package/standard-skills/translate-native/scripts/translation_guard.py +916 -916
  81. package/standard-tools/language-guard/blun_language_guard.py +697 -697
  82. package/standard-tools/language-guard/check_diacritics.py +353 -353
  83. package/standard-tools/language-guard/guard_service_client.py +264 -264
  84. package/standard-tools/language-guard/language_quality.py +377 -377
  85. package/standard-tools/language-guard/translation_guard.py +916 -916
@@ -1,572 +1,597 @@
1
- import { VERSION } from "./version.js";
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 {
18
- annotateDocument, linkDocuments, linkEntities,
19
- relationshipContext, upsertEntity
20
- } from "./lib/graph.js";
21
-
22
- const tools = [
23
- {
24
- name: "scan",
25
- description: "Discover and fingerprint Markdown identity, rules, memory, soul, and reference files without modifying them.",
26
- inputSchema: { type: "object", properties: { root: { type: "string" } } }
27
- },
28
- {
29
- name: "resolve_context",
30
- description: "Resolve the relevant constitution, soul, memory index, and linked facts for a host and working directory.",
31
- inputSchema: {
32
- type: "object",
33
- properties: {
34
- root: { type: "string" }, cwd: { type: "string" },
35
- host: { type: "string", enum: ["codex", "claude", "generic"] },
36
- maxBytes: { type: "integer", minimum: 0 }, includeContent: { type: "boolean" }
37
- }
38
- }
39
- },
40
- {
41
- name: "session_briefing",
42
- 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.",
43
- inputSchema: {
44
- type: "object", additionalProperties: false,
45
- properties: {
46
- root: { type: "string" }, cwd: { type: "string" },
47
- host: { type: "string", enum: ["codex", "claude", "generic"] },
48
- entityId: { anyOf: [{ type: "string" }, { type: "null" }] },
49
- userId: { anyOf: [{ type: "string" }, { type: "null" }] },
50
- tenantId: { anyOf: [{ type: "string" }, { type: "null" }] },
51
- groupId: { anyOf: [{ type: "string" }, { type: "null" }] },
52
- projectId: { anyOf: [{ type: "string" }, { type: "null" }] },
53
- currentTaskId: { anyOf: [{ type: "string" }, { type: "null" }] },
54
- includePrivate: { type: "boolean" }, focusActive: { type: "boolean" },
55
- includeSourceContent: { type: "boolean" },
56
- maxBytes: { type: "integer", minimum: 4096, maximum: 262144 },
57
- now: { type: "string" }
58
- }
59
- }
60
- },
61
- {
62
- name: "read_document",
63
- description: "Read an indexed Markdown source byte range with its SHA-256 provenance.",
64
- inputSchema: {
65
- type: "object", required: ["path"],
66
- properties: {
67
- root: { type: "string" }, path: { type: "string" },
68
- offset: { type: "integer", minimum: 0 }, length: { type: "integer", minimum: 1, maximum: 1048576 }
69
- }
70
- }
71
- },
72
- {
73
- name: "verify",
74
- description: "Compare current Markdown files with the last saved catalog and report additions, removals, or byte changes.",
75
- inputSchema: { type: "object", properties: { root: { type: "string" } } }
76
- },
77
- {
78
- name: "link_documents",
79
- description: "Record an agent-inferred relationship between two indexed documents in the external context graph. This never edits either document and never grants authority.",
80
- inputSchema: {
81
- type: "object", required: ["from", "to", "relation"],
82
- properties: {
83
- root: { type: "string" }, from: { type: "string" }, to: { type: "string" },
84
- relation: { type: "string", enum: ["loads", "belongs-to", "explains", "supports", "related", "contradicts", "supersedes-in-relevance"] },
85
- reason: { type: "string" }, confidence: { type: "number", minimum: 0, maximum: 1 }
86
- }
87
- }
88
- },
89
- {
90
- name: "annotate_document",
91
- description: "Store the agent's semantic classification of an indexed document as a reversible overlay annotation, never as a source rewrite.",
92
- inputSchema: {
93
- type: "object", required: ["path", "layer"],
94
- properties: {
95
- root: { type: "string" }, path: { type: "string" },
96
- layer: { type: "string", enum: ["soul", "memory-index", "memory-fact", "reference", "project-reference", "identity", "voice", "conduct", "history"] },
97
- reason: { type: "string" }, confidence: { type: "number", minimum: 0, maximum: 1 }
98
- }
99
- }
100
- },
101
- {
102
- name: "upsert_entity",
103
- description: "Create or update a person, agent, group, channel, or project in the local relationship graph. Authority fields are rejected.",
104
- inputSchema: {
105
- type: "object", required: ["id", "kind"],
106
- properties: {
107
- root: { type: "string" }, id: { type: "string" },
108
- kind: { type: "string", enum: ["person", "agent", "group", "channel", "project"] },
109
- displayName: { type: "string" }, aliases: { type: "array", items: { type: "string" } },
110
- attributes: { type: "object" }, sourceDocument: { anyOf: [{ type: "string" }, { type: "null" }] },
111
- confidence: { type: "number", minimum: 0, maximum: 1 },
112
- privacy: { type: "string", enum: ["private", "shared", "group"] }
113
- }
114
- }
115
- },
116
- {
117
- name: "link_entities",
118
- description: "Record a contextual relationship between known entities. Relationships never grant permissions or delegation authority.",
119
- inputSchema: {
120
- type: "object", required: ["from", "to", "relation"],
121
- properties: {
122
- root: { type: "string" }, from: { type: "string" }, to: { type: "string" },
123
- relation: { type: "string", enum: ["knows", "works-with", "member-of", "communicates-via", "responsible-for", "reports-to", "related"] },
124
- reason: { type: "string" }, confidence: { type: "number", minimum: 0, maximum: 1 },
125
- privacy: { type: "string", enum: ["private", "shared", "group"] }
126
- }
127
- }
128
- },
129
- {
130
- name: "relationship_context",
131
- description: "Return the privacy-filtered local relationship neighborhood for one entity. Private data is excluded unless explicitly requested.",
132
- inputSchema: {
133
- type: "object", required: ["entityId"],
134
- properties: {
135
- root: { type: "string" }, entityId: { type: "string" }, includePrivate: { type: "boolean" },
136
- groupId: { anyOf: [{ type: "string" }, { type: "null" }] }
137
- }
138
- }
139
- },
140
- {
141
- name: "upsert_attention",
142
- description: "Create or update a local attention cue for an unanswered question, promise, check-in, or meaningful change. Cues are context-only and never send messages.",
143
- inputSchema: {
144
- type: "object", required: ["kind", "summary"],
145
- properties: {
146
- root: { type: "string" }, id: { type: "string" },
147
- kind: { type: "string", enum: ["unanswered-question", "promise", "check-in", "meaningful-change"] },
148
- summary: { type: "string", maxLength: 500 }, entityId: { anyOf: [{ type: "string" }, { type: "null" }] },
149
- dueAt: { anyOf: [{ type: "string" }, { type: "null" }] }, priority: { type: "number", minimum: 0, maximum: 100 },
150
- privacy: { type: "string", enum: ["private", "shared", "group"] },
151
- groupId: { anyOf: [{ type: "string" }, { type: "null" }] },
152
- sourceDocument: { anyOf: [{ type: "string" }, { type: "null" }] },
153
- confidence: { type: "number", minimum: 0, maximum: 1 }
154
- }
155
- }
156
- },
157
- {
158
- name: "record_activity",
159
- description: "Record a minimal interaction timestamp for a known entity so check-in suggestions reflect recent contact without storing conversation content.",
160
- inputSchema: {
161
- type: "object", required: ["entityId"],
162
- properties: {
163
- root: { type: "string" }, entityId: { type: "string" },
164
- kind: { type: "string", enum: ["message", "interaction", "task", "check-in"] },
165
- at: { type: "string" }, privacy: { type: "string", enum: ["private", "shared", "group"] },
166
- groupId: { anyOf: [{ type: "string" }, { type: "null" }] }
167
- }
168
- }
169
- },
170
- {
171
- name: "attention_context",
172
- description: "Return a sparse, privacy-filtered list of due attention suggestions. Focus mode, quiet hours, and presentation throttling can suppress all output.",
173
- inputSchema: {
174
- type: "object",
175
- properties: {
176
- root: { type: "string" }, includePrivate: { type: "boolean" }, focusActive: { type: "boolean" },
177
- markPresented: { type: "boolean" }, maxItems: { type: "integer", minimum: 0, maximum: 20 },
178
- entityId: { anyOf: [{ type: "string" }, { type: "null" }] },
179
- groupId: { anyOf: [{ type: "string" }, { type: "null" }] },
180
- projectId: { anyOf: [{ type: "string" }, { type: "null" }] },
181
- currentTaskId: { anyOf: [{ type: "string" }, { type: "null" }] }, now: { type: "string" }
182
- }
183
- }
184
- },
185
- {
186
- name: "resolve_attention",
187
- description: "Mark an attention cue open, completed, or dismissed while retaining its previous state in private history.",
188
- inputSchema: {
189
- type: "object", required: ["id"],
190
- properties: {
191
- root: { type: "string" }, id: { type: "string" },
192
- status: { type: "string", enum: ["open", "completed", "dismissed"] }
193
- }
194
- }
195
- },
196
- {
197
- name: "configure_attention",
198
- description: "Configure local attention limits, silence threshold, quiet hours, or the global on/off switch.",
199
- inputSchema: {
200
- type: "object", required: ["config"],
201
- properties: {
202
- root: { type: "string" },
203
- config: {
204
- type: "object", additionalProperties: false,
205
- properties: {
206
- enabled: { type: "boolean" }, minIntervalHours: { type: "number", minimum: 1, maximum: 720 },
207
- entitySilenceDays: { type: "number", minimum: 1, maximum: 3650 },
208
- heartbeatStaleMinutes: { type: "integer", minimum: 1, maximum: 10080 },
209
- maxItems: { type: "integer", minimum: 1, maximum: 20 },
210
- quietHours: {
211
- anyOf: [
212
- { type: "null" },
213
- {
214
- type: "object", required: ["start", "end"], additionalProperties: false,
215
- properties: {
216
- start: { type: "integer", minimum: 0, maximum: 23 }, end: { type: "integer", minimum: 0, maximum: 23 },
217
- utcOffsetMinutes: { type: "integer", minimum: -720, maximum: 840 }
218
- }
219
- }
220
- ]
221
- }
222
- }
223
- }
224
- }
225
- }
226
- },
227
- {
228
- name: "delete_attention",
229
- description: "Permanently delete one cue, one lifecycle event, or all attention data for one entity, including retained history, receipts, and presentation timestamps.",
230
- inputSchema: {
231
- type: "object",
232
- properties: {
233
- root: { type: "string" }, signalId: { type: "string" }, eventId: { type: "string" }, entityId: { type: "string" }
234
- },
235
- oneOf: [{ required: ["signalId"] }, { required: ["eventId"] }, { required: ["entityId"] }]
236
- }
237
- },
238
- {
239
- name: "propose_learning",
240
- description: "Create an evidence-backed learning candidate in external state. Candidates are invisible to learned context until explicitly accepted or safely auto-promoted.",
241
- inputSchema: {
242
- type: "object", required: ["kind", "claim", "evidence"],
243
- properties: {
244
- root: { type: "string" }, id: { type: "string" },
245
- kind: { type: "string", enum: ["preference", "no-go", "goal", "correction", "personal-fact", "project-fact", "reference", "behavior"] },
246
- claim: { type: "string", maxLength: 1000 }, subjectId: { anyOf: [{ type: "string" }, { type: "null" }] },
247
- privacy: { type: "string", enum: ["private", "shared", "group"] },
248
- groupId: { anyOf: [{ type: "string" }, { type: "null" }] },
249
- scope: {
250
- type: "object", additionalProperties: false,
251
- properties: {
252
- personaId: { anyOf: [{ type: "string" }, { type: "null" }] }, userId: { anyOf: [{ type: "string" }, { type: "null" }] },
253
- tenantId: { anyOf: [{ type: "string" }, { type: "null" }] }, projectId: { anyOf: [{ type: "string" }, { type: "null" }] },
254
- groupId: { anyOf: [{ type: "string" }, { type: "null" }] }, taskId: { anyOf: [{ type: "string" }, { type: "null" }] }
255
- }
256
- },
257
- supersedesId: { anyOf: [{ type: "string" }, { type: "null" }] },
258
- evidence: { "$ref": "#/$defs/evidence" }
259
- },
260
- "$defs": {
261
- evidence: {
262
- type: "object", required: ["summary"], additionalProperties: false,
263
- properties: {
264
- id: { type: "string" }, type: { type: "string", enum: ["user-statement", "document", "interaction", "test"] },
265
- summary: { type: "string", maxLength: 500 }, sourceDocument: { anyOf: [{ type: "string" }, { type: "null" }] },
266
- confidence: { type: "number", minimum: 0, maximum: 1 }, observedAt: { type: "string" }
267
- }
268
- }
269
- }
270
- }
271
- },
272
- {
273
- name: "add_learning_evidence",
274
- description: "Append distinct evidence to an unreviewed learning candidate while retaining the previous candidate version in history.",
275
- inputSchema: {
276
- type: "object", required: ["id", "evidence"],
277
- properties: {
278
- root: { type: "string" }, id: { type: "string" },
279
- evidence: {
280
- type: "object", required: ["summary"], additionalProperties: false,
281
- properties: {
282
- id: { type: "string" }, type: { type: "string", enum: ["user-statement", "document", "interaction", "test"] },
283
- summary: { type: "string", maxLength: 500 }, sourceDocument: { anyOf: [{ type: "string" }, { type: "null" }] },
284
- confidence: { type: "number", minimum: 0, maximum: 1 }, observedAt: { type: "string" }
285
- }
286
- }
287
- }
288
- }
289
- },
290
- {
291
- name: "review_learning",
292
- description: "Accept or reject one candidate. Acceptance requires an explicit user-confirmation marker and never grants authority.",
293
- inputSchema: {
294
- type: "object", required: ["id", "decision", "reason"],
295
- properties: {
296
- root: { type: "string" }, id: { type: "string" },
297
- decision: { type: "string", enum: ["accept", "reject"] }, reason: { type: "string", maxLength: 500 },
298
- confirmedByUser: { type: "boolean" }
299
- }
300
- }
301
- },
302
- {
303
- name: "learning_context",
304
- description: "Return accepted learning only, filtered by privacy, exact group audience, kind, subject, and context limit.",
305
- inputSchema: {
306
- type: "object",
307
- properties: {
308
- root: { type: "string" }, includePrivate: { type: "boolean" },
309
- groupId: { anyOf: [{ type: "string" }, { type: "null" }] },
310
- scope: {
311
- type: "object", additionalProperties: false,
312
- properties: {
313
- personaId: { anyOf: [{ type: "string" }, { type: "null" }] }, userId: { anyOf: [{ type: "string" }, { type: "null" }] },
314
- tenantId: { anyOf: [{ type: "string" }, { type: "null" }] }, projectId: { anyOf: [{ type: "string" }, { type: "null" }] },
315
- groupId: { anyOf: [{ type: "string" }, { type: "null" }] }, taskId: { anyOf: [{ type: "string" }, { type: "null" }] }
316
- }
317
- },
318
- kinds: { type: "array", items: { type: "string", enum: ["preference", "no-go", "goal", "correction", "personal-fact", "project-fact", "reference", "behavior"] } },
319
- subjectIds: { type: "array", items: { type: "string" } }, maxItems: { type: "integer", minimum: 0, maximum: 50 }
320
- }
321
- }
322
- },
323
- {
324
- name: "learning_outcome_status",
325
- description: "Read outcome receipt counts, contradiction state, and canary health for learned context. This read-only view is context-only and cannot record evidence, promote learning, or grant authority.",
326
- inputSchema: {
327
- type: "object",
328
- properties: {
329
- root: { type: "string" },
330
- scope: {
331
- type: "object", additionalProperties: false,
332
- properties: {
333
- personaId: { anyOf: [{ type: "string" }, { type: "null" }] },
334
- userId: { anyOf: [{ type: "string" }, { type: "null" }] },
335
- tenantId: { anyOf: [{ type: "string" }, { type: "null" }] },
336
- projectId: { anyOf: [{ type: "string" }, { type: "null" }] },
337
- groupId: { anyOf: [{ type: "string" }, { type: "null" }] },
338
- taskId: { anyOf: [{ type: "string" }, { type: "null" }] }
339
- }
340
- }
341
- }
342
- }
343
- },
344
- {
345
- name: "evaluate_learning",
346
- description: "Evaluate pending candidates against the opt-in low-risk auto-promotion policy. Auto-promotion is disabled by default and limited to project facts and references.",
347
- inputSchema: { type: "object", properties: { root: { type: "string" } } }
348
- },
349
- {
350
- name: "rollback_learning",
351
- description: "Roll back an accepted learning and restore the accepted fact it superseded, without deleting history.",
352
- inputSchema: {
353
- type: "object", required: ["id", "reason"],
354
- properties: { root: { type: "string" }, id: { type: "string" }, reason: { type: "string", maxLength: 500 } }
355
- }
356
- },
357
- {
358
- name: "configure_learning",
359
- description: "Configure the local learning context limit and the default-off, low-risk auto-promotion evidence thresholds.",
360
- inputSchema: {
361
- type: "object", required: ["config"],
362
- properties: {
363
- root: { type: "string" },
364
- config: {
365
- type: "object", additionalProperties: false,
366
- properties: {
367
- autoPromote: { type: "boolean" }, minConfidence: { type: "number", minimum: 0.5, maximum: 1 },
368
- minEvidence: { type: "integer", minimum: 1, maximum: 10 }, maxContextItems: { type: "integer", minimum: 1, maximum: 50 },
369
- minOutcomeReceipts: { type: "integer", minimum: 2, maximum: 10 },
370
- minImprovement: { type: "number", minimum: 0, maximum: 1 },
371
- regressionTolerance: { type: "number", minimum: 0, maximum: 1 },
372
- outcomeMaxAgeDays: { type: "integer", minimum: 1, maximum: 365 },
373
- canaryReceipts: { type: "integer", minimum: 1, maximum: 10 },
374
- canaryTtlDays: { type: "integer", minimum: 1, maximum: 90 }
375
- }
376
- }
377
- }
378
- }
379
- },
380
- {
381
- name: "delete_learning",
382
- description: "Permanently delete one candidate and its retained learning history. Superseding active facts must be rolled back first.",
383
- inputSchema: {
384
- type: "object", required: ["id"],
385
- properties: { root: { type: "string" }, id: { type: "string" } }
386
- }
387
- },
388
- {
389
- name: "check_delegation",
390
- description: "Check the dedicated default-deny local policy before coordinating work for another person or agent. Relationships, memory, learning, and Markdown never affect this decision or grant host permissions.",
391
- inputSchema: {
392
- type: "object", required: ["actorId", "action"], additionalProperties: false,
393
- properties: {
394
- root: { type: "string" }, actorId: { type: "string" },
395
- action: { type: "string", enum: ["assign", "reassign", "manage", "complete", "cancel"] },
396
- targetId: { anyOf: [{ type: "string" }, { type: "null" }] }
397
- }
398
- }
399
- },
400
- {
401
- name: "create_task",
402
- description: "Create a context-only task, open thread, or handoff. Cross-entity assignment fails closed unless a separate explicit local delegation policy allows it.",
403
- inputSchema: {
404
- type: "object", required: ["actorId", "title"], additionalProperties: false,
405
- properties: {
406
- root: { type: "string" }, id: { type: "string" }, actorId: { type: "string" },
407
- assigneeId: { anyOf: [{ type: "string" }, { type: "null" }] },
408
- kind: { type: "string", enum: ["task", "open-thread", "handoff"] },
409
- title: { type: "string", maxLength: 200 }, summary: { anyOf: [{ type: "string", maxLength: 1000 }, { type: "null" }] },
410
- projectId: { anyOf: [{ type: "string" }, { type: "null" }] },
411
- privacy: { type: "string", enum: ["private", "shared", "group"] },
412
- groupId: { anyOf: [{ type: "string" }, { type: "null" }] },
413
- priority: { type: "number", minimum: 0, maximum: 100 },
414
- dueAt: { anyOf: [{ type: "string" }, { type: "null" }] }
415
- }
416
- }
417
- },
418
- {
419
- name: "update_task",
420
- description: "Update a coordination task with append-only prior history. Reassignment and third-party management are checked against the separate default-deny policy.",
421
- inputSchema: {
422
- type: "object", required: ["id", "actorId", "patch"], additionalProperties: false,
423
- properties: {
424
- root: { type: "string" }, id: { type: "string" }, actorId: { type: "string" },
425
- patch: {
426
- type: "object", minProperties: 1, additionalProperties: false,
427
- properties: {
428
- status: { type: "string", enum: ["open", "in-progress", "blocked", "completed", "cancelled"] },
429
- assigneeId: { anyOf: [{ type: "string" }, { type: "null" }] },
430
- title: { type: "string", maxLength: 200 }, summary: { anyOf: [{ type: "string", maxLength: 1000 }, { type: "null" }] },
431
- priority: { type: "number", minimum: 0, maximum: 100 }, dueAt: { anyOf: [{ type: "string" }, { type: "null" }] },
432
- note: { anyOf: [{ type: "string", maxLength: 1000 }, { type: "null" }] }
433
- }
434
- }
435
- }
436
- }
437
- },
438
- {
439
- name: "task_context",
440
- description: "Return privacy-filtered coordination context without delegation snapshots. Tasks describe work only and never grant host, tool, file, network, production, or spending rights.",
441
- inputSchema: {
442
- type: "object", additionalProperties: false,
443
- properties: {
444
- root: { type: "string" }, includePrivate: { type: "boolean" },
445
- groupId: { anyOf: [{ type: "string" }, { type: "null" }] },
446
- actorId: { anyOf: [{ type: "string" }, { type: "null" }] },
447
- assigneeId: { anyOf: [{ type: "string" }, { type: "null" }] },
448
- projectId: { anyOf: [{ type: "string" }, { type: "null" }] }, includeClosed: { type: "boolean" },
449
- maxItems: { type: "integer", minimum: 0, maximum: 100 }
450
- }
451
- }
452
- },
453
- {
454
- name: "shared_context",
455
- description: "Return locally reviewed, privacy-filtered shared memory. This tool cannot connect adapters, import, publish, review, or grant authority.",
456
- inputSchema: {
457
- type: "object", additionalProperties: false,
458
- properties: {
459
- root: { type: "string" }, scopeId: { anyOf: [{ type: "string" }, { type: "null" }] },
460
- groupId: { anyOf: [{ type: "string" }, { type: "null" }] }, includePrivate: { type: "boolean" },
461
- kinds: { type: "array", items: { type: "string", enum: ["preference", "no-go", "goal", "correction", "personal-fact", "project-fact", "reference"] } },
462
- subjectIds: { type: "array", items: { type: "string" } }, maxItems: { type: "integer", minimum: 0, maximum: 50 }
463
- }
464
- }
465
- },
466
- {
467
- name: "audit",
468
- description: "Run AgentSpine's ten deterministic quality gates for discovery, hierarchy, links, authority, privacy, budget, and source preservation.",
469
- inputSchema: { type: "object", properties: { root: { type: "string" } } }
470
- }
471
- ];
472
-
473
- function textResult(value, isError = false) {
474
- return { content: [{ type: "text", text: JSON.stringify(value, null, 2) }], isError };
475
- }
476
-
477
- async function callTool(name, args = {}) {
478
- const root = args.root || process.cwd();
479
- if (name === "scan") return textResult(await scanAndSave(root));
480
- if (name === "resolve_context") return textResult(await resolveContext({
481
- root,
482
- cwd: args.cwd || root,
483
- host: args.host || "generic",
484
- maxBytes: args.maxBytes ?? 65536,
485
- includeContent: args.includeContent ?? true
486
- }));
487
- if (name === "session_briefing") return textResult(await sessionBriefing({ ...args, root }));
488
- if (name === "read_document") return textResult(await readDocument({
489
- root, path: args.path, offset: args.offset ?? 0, length: args.length ?? 65536
490
- }));
491
- if (name === "verify") return textResult(await verifyCatalog(root));
492
- if (name === "link_documents") return textResult(await linkDocuments({ ...args, root }));
493
- if (name === "annotate_document") return textResult(await annotateDocument({ ...args, root }));
494
- if (name === "upsert_entity") return textResult(await upsertEntity({ ...args, root }));
495
- if (name === "link_entities") return textResult(await linkEntities({ ...args, root }));
496
- if (name === "relationship_context") return textResult(await relationshipContext({ ...args, root }));
497
- if (name === "upsert_attention") return textResult(await upsertAttention({ ...args, root }));
498
- if (name === "record_activity") return textResult(await recordActivity({ ...args, root }));
499
- if (name === "attention_context") return textResult(await attentionContext({ ...args, root }));
500
- if (name === "resolve_attention") return textResult(await resolveAttention({ ...args, root }));
501
- if (name === "configure_attention") return textResult(await configureAttention({ ...args, root }));
502
- if (name === "delete_attention") return textResult(await deleteAttention({ ...args, root }));
503
- if (name === "propose_learning") return textResult(await proposeLearning({ ...args, root }));
504
- if (name === "add_learning_evidence") return textResult(await addLearningEvidence({ ...args, root }));
505
- if (name === "review_learning") return textResult(await reviewLearning({ ...args, root }));
506
- if (name === "learning_context") return textResult(await learningContext({ ...args, root }));
507
- if (name === "learning_outcome_status") return textResult(await learningOutcomeStatus({ ...args, root }));
508
- if (name === "evaluate_learning") return textResult(await evaluateLearning({ ...args, root }));
509
- if (name === "rollback_learning") return textResult(await rollbackLearning({ ...args, root }));
510
- if (name === "configure_learning") return textResult(await configureLearning({ ...args, root }));
511
- if (name === "delete_learning") return textResult(await deleteLearning({ ...args, root }));
512
- if (name === "check_delegation") return textResult(await checkDelegation({ ...args, root }));
513
- if (name === "create_task") return textResult(await createTask({ ...args, root }));
514
- if (name === "update_task") return textResult(await updateTask({ ...args, root }));
515
- if (name === "task_context") return textResult(await taskContext({ ...args, root }));
516
- if (name === "shared_context") return textResult(await sharedContext({ ...args, root }));
517
- if (name === "audit") return textResult(await runAudit(root));
518
- throw new Error(`Unknown tool: ${name}`);
519
- }
520
-
521
- async function dispatch(message) {
522
- const { id, method, params = {} } = message;
523
- if (method === "initialize") {
524
- return {
525
- jsonrpc: "2.0", id,
526
- result: {
527
- protocolVersion: params.protocolVersion || "2025-06-18",
528
- capabilities: { tools: { listChanged: false } },
529
- serverInfo: { name: "agent-spine", version: VERSION }
530
- }
531
- };
532
- }
533
- if (method === "ping") return { jsonrpc: "2.0", id, result: {} };
534
- if (method === "tools/list") return { jsonrpc: "2.0", id, result: { tools } };
535
- if (method === "tools/call") {
536
- try {
537
- return { jsonrpc: "2.0", id, result: await callTool(params.name, params.arguments) };
538
- } catch (error) {
539
- return { jsonrpc: "2.0", id, result: textResult({ error: error.message }, true) };
540
- }
541
- }
542
- if (method?.startsWith("notifications/")) return null;
543
- return { jsonrpc: "2.0", id, error: { code: -32601, message: `Method not found: ${method}` } };
544
- }
545
-
546
- export function startMcpServer(input = process.stdin, output = process.stdout) {
547
- input.setEncoding("utf8");
548
- let buffer = "";
549
- let queue = Promise.resolve();
550
- input.on("data", (chunk) => {
551
- buffer += chunk;
552
- let newline;
553
- while ((newline = buffer.indexOf("\n")) >= 0) {
554
- const line = buffer.slice(0, newline).trim();
555
- buffer = buffer.slice(newline + 1);
556
- if (!line) continue;
557
- queue = queue.then(async () => {
558
- let response;
559
- try {
560
- response = await dispatch(JSON.parse(line));
561
- } catch (error) {
562
- response = { jsonrpc: "2.0", id: null, error: { code: -32700, message: error.message } };
563
- }
564
- if (response) output.write(`${JSON.stringify(response)}\n`);
565
- });
566
- }
567
- });
568
- }
569
-
570
- if (isMainModule(import.meta.url)) {
571
- startMcpServer();
572
- }
1
+ import { VERSION } from "./version.js";
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";
22
+
23
+ const tools = [
24
+ {
25
+ name: "scan",
26
+ description: "Discover and fingerprint Markdown identity, rules, memory, soul, and reference files without modifying them.",
27
+ inputSchema: { type: "object", properties: { root: { type: "string" } } }
28
+ },
29
+ {
30
+ name: "resolve_context",
31
+ description: "Resolve the relevant constitution, soul, memory index, and linked facts for a host and working directory.",
32
+ inputSchema: {
33
+ type: "object",
34
+ properties: {
35
+ root: { type: "string" }, cwd: { type: "string" },
36
+ host: { type: "string", enum: ["codex", "claude", "generic"] },
37
+ maxBytes: { type: "integer", minimum: 0 }, includeContent: { type: "boolean" }
38
+ }
39
+ }
40
+ },
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
+ },
62
+ {
63
+ name: "read_document",
64
+ description: "Read an indexed Markdown source byte range with its SHA-256 provenance.",
65
+ inputSchema: {
66
+ type: "object", required: ["path"],
67
+ properties: {
68
+ root: { type: "string" }, path: { type: "string" },
69
+ offset: { type: "integer", minimum: 0 }, length: { type: "integer", minimum: 1, maximum: 1048576 }
70
+ }
71
+ }
72
+ },
73
+ {
74
+ name: "verify",
75
+ description: "Compare current Markdown files with the last saved catalog and report additions, removals, or byte changes.",
76
+ inputSchema: { type: "object", properties: { root: { type: "string" } } }
77
+ },
78
+ {
79
+ name: "link_documents",
80
+ description: "Record an agent-inferred relationship between two indexed documents in the external context graph. This never edits either document and never grants authority.",
81
+ inputSchema: {
82
+ type: "object", required: ["from", "to", "relation"],
83
+ properties: {
84
+ root: { type: "string" }, from: { type: "string" }, to: { type: "string" },
85
+ relation: { type: "string", enum: ["loads", "belongs-to", "explains", "supports", "related", "contradicts", "supersedes-in-relevance"] },
86
+ reason: { type: "string" }, confidence: { type: "number", minimum: 0, maximum: 1 }
87
+ }
88
+ }
89
+ },
90
+ {
91
+ name: "annotate_document",
92
+ description: "Store the agent's semantic classification of an indexed document as a reversible overlay annotation, never as a source rewrite.",
93
+ inputSchema: {
94
+ type: "object", required: ["path", "layer"],
95
+ properties: {
96
+ root: { type: "string" }, path: { type: "string" },
97
+ layer: { type: "string", enum: ["soul", "memory-index", "memory-fact", "reference", "project-reference", "identity", "voice", "conduct", "history"] },
98
+ reason: { type: "string" }, confidence: { type: "number", minimum: 0, maximum: 1 }
99
+ }
100
+ }
101
+ },
102
+ {
103
+ name: "upsert_entity",
104
+ description: "Create or update a person, agent, group, channel, or project in the local relationship graph. Authority fields are rejected.",
105
+ inputSchema: {
106
+ type: "object", required: ["id", "kind"],
107
+ properties: {
108
+ root: { type: "string" }, id: { type: "string" },
109
+ kind: { type: "string", enum: ["person", "agent", "group", "channel", "project"] },
110
+ displayName: { type: "string" }, aliases: { type: "array", items: { type: "string" } },
111
+ attributes: { type: "object" }, sourceDocument: { anyOf: [{ type: "string" }, { type: "null" }] },
112
+ confidence: { type: "number", minimum: 0, maximum: 1 },
113
+ privacy: { type: "string", enum: ["private", "shared", "group"] }
114
+ }
115
+ }
116
+ },
117
+ {
118
+ name: "link_entities",
119
+ description: "Record a contextual relationship between known entities. Relationships never grant permissions or delegation authority.",
120
+ inputSchema: {
121
+ type: "object", required: ["from", "to", "relation"],
122
+ properties: {
123
+ root: { type: "string" }, from: { type: "string" }, to: { type: "string" },
124
+ relation: { type: "string", enum: ["knows", "works-with", "member-of", "communicates-via", "responsible-for", "reports-to", "related"] },
125
+ reason: { type: "string" }, confidence: { type: "number", minimum: 0, maximum: 1 },
126
+ privacy: { type: "string", enum: ["private", "shared", "group"] }
127
+ }
128
+ }
129
+ },
130
+ {
131
+ name: "relationship_context",
132
+ description: "Return the privacy-filtered local relationship neighborhood for one entity. Private data is excluded unless explicitly requested.",
133
+ inputSchema: {
134
+ type: "object", required: ["entityId"],
135
+ properties: {
136
+ root: { type: "string" }, entityId: { type: "string" }, includePrivate: { type: "boolean" },
137
+ groupId: { anyOf: [{ type: "string" }, { type: "null" }] }
138
+ }
139
+ }
140
+ },
141
+ {
142
+ name: "upsert_attention",
143
+ description: "Create or update a local attention cue for an unanswered question, promise, check-in, or meaningful change. Cues are context-only and never send messages.",
144
+ inputSchema: {
145
+ type: "object", required: ["kind", "summary"],
146
+ properties: {
147
+ root: { type: "string" }, id: { type: "string" },
148
+ kind: { type: "string", enum: ["unanswered-question", "promise", "check-in", "meaningful-change"] },
149
+ summary: { type: "string", maxLength: 500 }, entityId: { anyOf: [{ type: "string" }, { type: "null" }] },
150
+ dueAt: { anyOf: [{ type: "string" }, { type: "null" }] }, priority: { type: "number", minimum: 0, maximum: 100 },
151
+ privacy: { type: "string", enum: ["private", "shared", "group"] },
152
+ groupId: { anyOf: [{ type: "string" }, { type: "null" }] },
153
+ sourceDocument: { anyOf: [{ type: "string" }, { type: "null" }] },
154
+ confidence: { type: "number", minimum: 0, maximum: 1 }
155
+ }
156
+ }
157
+ },
158
+ {
159
+ name: "record_activity",
160
+ description: "Record a minimal interaction timestamp for a known entity so check-in suggestions reflect recent contact without storing conversation content.",
161
+ inputSchema: {
162
+ type: "object", required: ["entityId"],
163
+ properties: {
164
+ root: { type: "string" }, entityId: { type: "string" },
165
+ kind: { type: "string", enum: ["message", "interaction", "task", "check-in"] },
166
+ at: { type: "string" }, privacy: { type: "string", enum: ["private", "shared", "group"] },
167
+ groupId: { anyOf: [{ type: "string" }, { type: "null" }] }
168
+ }
169
+ }
170
+ },
171
+ {
172
+ name: "attention_context",
173
+ description: "Return a sparse, privacy-filtered list of due attention suggestions. Focus mode, quiet hours, and presentation throttling can suppress all output.",
174
+ inputSchema: {
175
+ type: "object",
176
+ properties: {
177
+ root: { type: "string" }, includePrivate: { type: "boolean" }, focusActive: { type: "boolean" },
178
+ markPresented: { type: "boolean" }, maxItems: { type: "integer", minimum: 0, maximum: 20 },
179
+ entityId: { anyOf: [{ type: "string" }, { type: "null" }] },
180
+ groupId: { anyOf: [{ type: "string" }, { type: "null" }] },
181
+ projectId: { anyOf: [{ type: "string" }, { type: "null" }] },
182
+ currentTaskId: { anyOf: [{ type: "string" }, { type: "null" }] }, now: { type: "string" }
183
+ }
184
+ }
185
+ },
186
+ {
187
+ name: "resolve_attention",
188
+ description: "Mark an attention cue open, completed, or dismissed while retaining its previous state in private history.",
189
+ inputSchema: {
190
+ type: "object", required: ["id"],
191
+ properties: {
192
+ root: { type: "string" }, id: { type: "string" },
193
+ status: { type: "string", enum: ["open", "completed", "dismissed"] }
194
+ }
195
+ }
196
+ },
197
+ {
198
+ name: "configure_attention",
199
+ description: "Configure local attention limits, silence threshold, quiet hours, or the global on/off switch.",
200
+ inputSchema: {
201
+ type: "object", required: ["config"],
202
+ properties: {
203
+ root: { type: "string" },
204
+ config: {
205
+ type: "object", additionalProperties: false,
206
+ properties: {
207
+ enabled: { type: "boolean" }, minIntervalHours: { type: "number", minimum: 1, maximum: 720 },
208
+ entitySilenceDays: { type: "number", minimum: 1, maximum: 3650 },
209
+ heartbeatStaleMinutes: { type: "integer", minimum: 1, maximum: 10080 },
210
+ maxItems: { type: "integer", minimum: 1, maximum: 20 },
211
+ quietHours: {
212
+ anyOf: [
213
+ { type: "null" },
214
+ {
215
+ type: "object", required: ["start", "end"], additionalProperties: false,
216
+ properties: {
217
+ start: { type: "integer", minimum: 0, maximum: 23 }, end: { type: "integer", minimum: 0, maximum: 23 },
218
+ utcOffsetMinutes: { type: "integer", minimum: -720, maximum: 840 }
219
+ }
220
+ }
221
+ ]
222
+ }
223
+ }
224
+ }
225
+ }
226
+ }
227
+ },
228
+ {
229
+ name: "delete_attention",
230
+ description: "Permanently delete one cue, one lifecycle event, or all attention data for one entity, including retained history, receipts, and presentation timestamps.",
231
+ inputSchema: {
232
+ type: "object",
233
+ properties: {
234
+ root: { type: "string" }, signalId: { type: "string" }, eventId: { type: "string" }, entityId: { type: "string" }
235
+ },
236
+ oneOf: [{ required: ["signalId"] }, { required: ["eventId"] }, { required: ["entityId"] }]
237
+ }
238
+ },
239
+ {
240
+ name: "propose_learning",
241
+ description: "Create an evidence-backed learning candidate in external state. Candidates are invisible to learned context until explicitly accepted or safely auto-promoted.",
242
+ inputSchema: {
243
+ type: "object", required: ["kind", "claim", "evidence"],
244
+ properties: {
245
+ root: { type: "string" }, id: { type: "string" },
246
+ kind: { type: "string", enum: ["preference", "no-go", "goal", "correction", "personal-fact", "project-fact", "reference", "behavior"] },
247
+ claim: { type: "string", maxLength: 1000 }, subjectId: { anyOf: [{ type: "string" }, { type: "null" }] },
248
+ privacy: { type: "string", enum: ["private", "shared", "group"] },
249
+ groupId: { anyOf: [{ type: "string" }, { type: "null" }] },
250
+ scope: {
251
+ type: "object", additionalProperties: false,
252
+ properties: {
253
+ personaId: { anyOf: [{ type: "string" }, { type: "null" }] }, userId: { anyOf: [{ type: "string" }, { type: "null" }] },
254
+ tenantId: { anyOf: [{ type: "string" }, { type: "null" }] }, projectId: { anyOf: [{ type: "string" }, { type: "null" }] },
255
+ groupId: { anyOf: [{ type: "string" }, { type: "null" }] }, taskId: { anyOf: [{ type: "string" }, { type: "null" }] }
256
+ }
257
+ },
258
+ supersedesId: { anyOf: [{ type: "string" }, { type: "null" }] },
259
+ evidence: { "$ref": "#/$defs/evidence" }
260
+ },
261
+ "$defs": {
262
+ evidence: {
263
+ type: "object", required: ["summary"], additionalProperties: false,
264
+ properties: {
265
+ id: { type: "string" }, type: { type: "string", enum: ["user-statement", "document", "interaction", "test"] },
266
+ summary: { type: "string", maxLength: 500 }, sourceDocument: { anyOf: [{ type: "string" }, { type: "null" }] },
267
+ confidence: { type: "number", minimum: 0, maximum: 1 }, observedAt: { type: "string" }
268
+ }
269
+ }
270
+ }
271
+ }
272
+ },
273
+ {
274
+ name: "add_learning_evidence",
275
+ description: "Append distinct evidence to an unreviewed learning candidate while retaining the previous candidate version in history.",
276
+ inputSchema: {
277
+ type: "object", required: ["id", "evidence"],
278
+ properties: {
279
+ root: { type: "string" }, id: { type: "string" },
280
+ evidence: {
281
+ type: "object", required: ["summary"], additionalProperties: false,
282
+ properties: {
283
+ id: { type: "string" }, type: { type: "string", enum: ["user-statement", "document", "interaction", "test"] },
284
+ summary: { type: "string", maxLength: 500 }, sourceDocument: { anyOf: [{ type: "string" }, { type: "null" }] },
285
+ confidence: { type: "number", minimum: 0, maximum: 1 }, observedAt: { type: "string" }
286
+ }
287
+ }
288
+ }
289
+ }
290
+ },
291
+ {
292
+ name: "review_learning",
293
+ description: "Accept or reject one candidate. Acceptance requires an explicit user-confirmation marker and never grants authority.",
294
+ inputSchema: {
295
+ type: "object", required: ["id", "decision", "reason"],
296
+ properties: {
297
+ root: { type: "string" }, id: { type: "string" },
298
+ decision: { type: "string", enum: ["accept", "reject"] }, reason: { type: "string", maxLength: 500 },
299
+ confirmedByUser: { type: "boolean" }
300
+ }
301
+ }
302
+ },
303
+ {
304
+ name: "learning_context",
305
+ description: "Return accepted learning only, filtered by privacy, exact group audience, kind, subject, and context limit.",
306
+ inputSchema: {
307
+ type: "object",
308
+ properties: {
309
+ root: { type: "string" }, includePrivate: { type: "boolean" },
310
+ groupId: { anyOf: [{ type: "string" }, { type: "null" }] },
311
+ scope: {
312
+ type: "object", additionalProperties: false,
313
+ properties: {
314
+ personaId: { anyOf: [{ type: "string" }, { type: "null" }] }, userId: { anyOf: [{ type: "string" }, { type: "null" }] },
315
+ tenantId: { anyOf: [{ type: "string" }, { type: "null" }] }, projectId: { anyOf: [{ type: "string" }, { type: "null" }] },
316
+ groupId: { anyOf: [{ type: "string" }, { type: "null" }] }, taskId: { anyOf: [{ type: "string" }, { type: "null" }] }
317
+ }
318
+ },
319
+ kinds: { type: "array", items: { type: "string", enum: ["preference", "no-go", "goal", "correction", "personal-fact", "project-fact", "reference", "behavior"] } },
320
+ subjectIds: { type: "array", items: { type: "string" } }, maxItems: { type: "integer", minimum: 0, maximum: 50 }
321
+ }
322
+ }
323
+ },
324
+ {
325
+ name: "learning_outcome_status",
326
+ description: "Read outcome receipt counts, contradiction state, and canary health for learned context. This read-only view is context-only and cannot record evidence, promote learning, or grant authority.",
327
+ inputSchema: {
328
+ type: "object",
329
+ properties: {
330
+ root: { type: "string" },
331
+ scope: {
332
+ type: "object", additionalProperties: false,
333
+ properties: {
334
+ personaId: { anyOf: [{ type: "string" }, { type: "null" }] },
335
+ userId: { anyOf: [{ type: "string" }, { type: "null" }] },
336
+ tenantId: { anyOf: [{ type: "string" }, { type: "null" }] },
337
+ projectId: { anyOf: [{ type: "string" }, { type: "null" }] },
338
+ groupId: { anyOf: [{ type: "string" }, { type: "null" }] },
339
+ taskId: { anyOf: [{ type: "string" }, { type: "null" }] }
340
+ }
341
+ }
342
+ }
343
+ }
344
+ },
345
+ {
346
+ name: "evaluate_learning",
347
+ description: "Evaluate pending candidates against the opt-in low-risk auto-promotion policy. Auto-promotion is disabled by default and limited to project facts and references.",
348
+ inputSchema: { type: "object", properties: { root: { type: "string" } } }
349
+ },
350
+ {
351
+ name: "rollback_learning",
352
+ description: "Roll back an accepted learning and restore the accepted fact it superseded, without deleting history.",
353
+ inputSchema: {
354
+ type: "object", required: ["id", "reason"],
355
+ properties: { root: { type: "string" }, id: { type: "string" }, reason: { type: "string", maxLength: 500 } }
356
+ }
357
+ },
358
+ {
359
+ name: "configure_learning",
360
+ description: "Configure the local learning context limit and the default-off, low-risk auto-promotion evidence thresholds.",
361
+ inputSchema: {
362
+ type: "object", required: ["config"],
363
+ properties: {
364
+ root: { type: "string" },
365
+ config: {
366
+ type: "object", additionalProperties: false,
367
+ properties: {
368
+ autoPromote: { type: "boolean" }, minConfidence: { type: "number", minimum: 0.5, maximum: 1 },
369
+ minEvidence: { type: "integer", minimum: 1, maximum: 10 }, maxContextItems: { type: "integer", minimum: 1, maximum: 50 },
370
+ minOutcomeReceipts: { type: "integer", minimum: 2, maximum: 10 },
371
+ minImprovement: { type: "number", minimum: 0, maximum: 1 },
372
+ regressionTolerance: { type: "number", minimum: 0, maximum: 1 },
373
+ outcomeMaxAgeDays: { type: "integer", minimum: 1, maximum: 365 },
374
+ canaryReceipts: { type: "integer", minimum: 1, maximum: 10 },
375
+ canaryTtlDays: { type: "integer", minimum: 1, maximum: 90 }
376
+ }
377
+ }
378
+ }
379
+ }
380
+ },
381
+ {
382
+ name: "delete_learning",
383
+ description: "Permanently delete one candidate and its retained learning history. Superseding active facts must be rolled back first.",
384
+ inputSchema: {
385
+ type: "object", required: ["id"],
386
+ properties: { root: { type: "string" }, id: { type: "string" } }
387
+ }
388
+ },
389
+ {
390
+ name: "check_delegation",
391
+ description: "Check the dedicated default-deny local policy before coordinating work for another person or agent. Relationships, memory, learning, and Markdown never affect this decision or grant host permissions.",
392
+ inputSchema: {
393
+ type: "object", required: ["actorId", "action"], additionalProperties: false,
394
+ properties: {
395
+ root: { type: "string" }, actorId: { type: "string" },
396
+ action: { type: "string", enum: ["assign", "reassign", "manage", "complete", "cancel"] },
397
+ targetId: { anyOf: [{ type: "string" }, { type: "null" }] }
398
+ }
399
+ }
400
+ },
401
+ {
402
+ name: "create_task",
403
+ description: "Create a context-only task, open thread, or handoff. Cross-entity assignment fails closed unless a separate explicit local delegation policy allows it.",
404
+ inputSchema: {
405
+ type: "object", required: ["actorId", "title"], additionalProperties: false,
406
+ properties: {
407
+ root: { type: "string" }, id: { type: "string" }, actorId: { type: "string" },
408
+ assigneeId: { anyOf: [{ type: "string" }, { type: "null" }] },
409
+ kind: { type: "string", enum: ["task", "open-thread", "handoff"] },
410
+ title: { type: "string", maxLength: 200 }, summary: { anyOf: [{ type: "string", maxLength: 1000 }, { type: "null" }] },
411
+ projectId: { anyOf: [{ type: "string" }, { type: "null" }] },
412
+ privacy: { type: "string", enum: ["private", "shared", "group"] },
413
+ groupId: { anyOf: [{ type: "string" }, { type: "null" }] },
414
+ priority: { type: "number", minimum: 0, maximum: 100 },
415
+ dueAt: { anyOf: [{ type: "string" }, { type: "null" }] }
416
+ }
417
+ }
418
+ },
419
+ {
420
+ name: "update_task",
421
+ description: "Update a coordination task with append-only prior history. Reassignment and third-party management are checked against the separate default-deny policy.",
422
+ inputSchema: {
423
+ type: "object", required: ["id", "actorId", "patch"], additionalProperties: false,
424
+ properties: {
425
+ root: { type: "string" }, id: { type: "string" }, actorId: { type: "string" },
426
+ patch: {
427
+ type: "object", minProperties: 1, additionalProperties: false,
428
+ properties: {
429
+ status: { type: "string", enum: ["open", "in-progress", "blocked", "completed", "cancelled"] },
430
+ assigneeId: { anyOf: [{ type: "string" }, { type: "null" }] },
431
+ title: { type: "string", maxLength: 200 }, summary: { anyOf: [{ type: "string", maxLength: 1000 }, { type: "null" }] },
432
+ priority: { type: "number", minimum: 0, maximum: 100 }, dueAt: { anyOf: [{ type: "string" }, { type: "null" }] },
433
+ note: { anyOf: [{ type: "string", maxLength: 1000 }, { type: "null" }] }
434
+ }
435
+ }
436
+ }
437
+ }
438
+ },
439
+ {
440
+ name: "task_context",
441
+ description: "Return privacy-filtered coordination context without delegation snapshots. Tasks describe work only and never grant host, tool, file, network, production, or spending rights.",
442
+ inputSchema: {
443
+ type: "object", additionalProperties: false,
444
+ properties: {
445
+ root: { type: "string" }, includePrivate: { type: "boolean" },
446
+ groupId: { anyOf: [{ type: "string" }, { type: "null" }] },
447
+ actorId: { anyOf: [{ type: "string" }, { type: "null" }] },
448
+ assigneeId: { anyOf: [{ type: "string" }, { type: "null" }] },
449
+ projectId: { anyOf: [{ type: "string" }, { type: "null" }] }, includeClosed: { type: "boolean" },
450
+ maxItems: { type: "integer", minimum: 0, maximum: 100 }
451
+ }
452
+ }
453
+ },
454
+ {
455
+ name: "shared_context",
456
+ description: "Return locally reviewed, privacy-filtered shared memory. This tool cannot connect adapters, import, publish, review, or grant authority.",
457
+ inputSchema: {
458
+ type: "object", additionalProperties: false,
459
+ properties: {
460
+ root: { type: "string" }, scopeId: { anyOf: [{ type: "string" }, { type: "null" }] },
461
+ groupId: { anyOf: [{ type: "string" }, { type: "null" }] }, includePrivate: { type: "boolean" },
462
+ kinds: { type: "array", items: { type: "string", enum: ["preference", "no-go", "goal", "correction", "personal-fact", "project-fact", "reference"] } },
463
+ subjectIds: { type: "array", items: { type: "string" } }, maxItems: { type: "integer", minimum: 0, maximum: 50 }
464
+ }
465
+ }
466
+ },
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
+ {
483
+ name: "audit",
484
+ description: "Run AgentSpine's ten deterministic quality gates for discovery, hierarchy, links, authority, privacy, budget, and source preservation.",
485
+ inputSchema: { type: "object", properties: { root: { type: "string" } } }
486
+ }
487
+ ];
488
+
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
+ });
593
+ }
594
+
595
+ if (isMainModule(import.meta.url)) {
596
+ startMcpServer();
597
+ }