@jjh32456/dsh-longlive 1.6.7 → 1.8.0

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 (289) hide show
  1. package/CHANGELOG.md +70 -1508
  2. package/LICENSE +1 -1
  3. package/QUICKSTART.md +34 -13
  4. package/README.en.md +18 -7
  5. package/README.md +34 -9
  6. package/SPEC.md +5 -5
  7. package/cordis.patch.yml +23 -9
  8. package/docs/ANSWER-GUARD.md +92 -0
  9. package/docs/ARCHITECTURE.md +4 -3
  10. package/docs/AUTO-RECALL-WIRING.md +1 -1
  11. package/docs/AUTO-RECALL.md +15 -9
  12. package/docs/CONFIG.md +168 -35
  13. package/docs/ERRORS.md +17 -5
  14. package/docs/FAQ.md +7 -7
  15. package/docs/FEATURES.md +96 -28
  16. package/docs/MIGRATION-1.6.0.md +2 -2
  17. package/docs/OPERATIONS.md +6 -6
  18. package/docs/PANEL-INTEGRATION.md +6 -0
  19. package/docs/PRIVACY.md +88 -0
  20. package/docs/QUICKSTART.md +5 -5
  21. package/docs/README.en.md +4 -2
  22. package/docs/RESIDENT-WATCHER.md +1 -1
  23. package/docs/SESSION-WATCH.md +6 -6
  24. package/docs/TROUBLESHOOTING.md +4 -4
  25. package/package.json +47 -117
  26. package/runbook.md +2 -2
  27. package/scripts/backfill-auto-cards.mjs +98 -0
  28. package/scripts/install-surgery-task.ps1 +5 -2
  29. package/scripts/lib/session-watch-fixture.mjs +1 -1
  30. package/scripts/longlive-doctor.mjs +6 -0
  31. package/scripts/migrate-180.mjs +163 -0
  32. package/scripts/recall-index-build.mjs +128 -0
  33. package/scripts/session-watch-drill.mjs +1 -1
  34. package/scripts/surgery-run.cmd +1 -1
  35. package/scripts/unattended-surgery.ps1 +5 -2
  36. package/scripts/verify-card-anchors.mjs +138 -0
  37. package/src/actions/audit.js +125 -0
  38. package/src/actions/evidence.js +122 -0
  39. package/src/actions/fs.js +162 -0
  40. package/src/activity/index.js +760 -0
  41. package/src/activity/model.js +351 -0
  42. package/src/activity/store.js +644 -0
  43. package/src/activity/thinking.js +176 -0
  44. package/src/adapters/constants.js +11 -0
  45. package/src/adapters/engram/data/aliases.json +51 -0
  46. package/src/adapters/engram/data/policy.json +8 -0
  47. package/src/adapters/engram/data/routing-rules.json +546 -0
  48. package/src/adapters/engram/index.js +235 -0
  49. package/src/adapters/registry.js +315 -0
  50. package/src/answer/audit.js +193 -0
  51. package/src/answer/evidence.js +30 -0
  52. package/src/answer/guard.js +1370 -0
  53. package/src/answer/index.js +1067 -0
  54. package/src/answer/query.js +111 -0
  55. package/src/answer/second-hop.mjs +101 -0
  56. package/src/auto-truncate.js +1 -1
  57. package/src/config.js +171 -47
  58. package/src/deps.js +6 -4
  59. package/src/features.js +182 -6
  60. package/src/heartbeat.js +156 -23
  61. package/src/identity/handover.js +52 -1
  62. package/src/identity/index.js +13 -5
  63. package/src/identity/inject-position.js +2 -2
  64. package/src/index.js +97 -7
  65. package/src/lifecycle/gap-clocks.js +328 -0
  66. package/src/lifecycle/index.js +269 -0
  67. package/src/live.js +22 -2
  68. package/src/memory/activity-capture.js +229 -0
  69. package/src/memory/anchors.js +194 -0
  70. package/src/memory/audit-records.js +222 -0
  71. package/src/memory/auto-cards.js +401 -0
  72. package/src/memory/capture.js +442 -0
  73. package/src/memory/card-anchors.js +203 -0
  74. package/src/memory/conflicts.js +106 -0
  75. package/src/memory/decay.js +311 -0
  76. package/src/memory/distill.js +471 -0
  77. package/src/memory/event-index.js +917 -0
  78. package/src/memory/evidence-chain.js +295 -0
  79. package/src/memory/evidence.js +145 -0
  80. package/src/memory/filter-runner.js +1 -1
  81. package/src/memory/filter.js +5 -1
  82. package/src/memory/forget-v2.js +410 -0
  83. package/src/memory/forget.js +71 -0
  84. package/src/memory/frames-activation.js +247 -0
  85. package/src/memory/fts-query.js +174 -0
  86. package/src/memory/governance.js +604 -0
  87. package/src/memory/index.js +1 -0
  88. package/src/memory/l2-gate.js +169 -0
  89. package/src/memory/lock.js +121 -0
  90. package/src/memory/migrate.js +139 -0
  91. package/src/memory/recall-v2.js +223 -0
  92. package/src/memory/recall.js +65 -2
  93. package/src/memory/records.js +425 -0
  94. package/src/memory/retention.js +128 -0
  95. package/src/memory/review.js +124 -0
  96. package/src/memory/trust.js +88 -0
  97. package/src/memory/version-chain.js +264 -0
  98. package/src/memory/wal.js +137 -0
  99. package/src/memory/working-frame.js +516 -0
  100. package/src/metrics/classify.mjs +5 -3
  101. package/src/metrics/index.mjs +21 -1
  102. package/src/metrics/prestep-latency.mjs +300 -0
  103. package/src/metrics/store.mjs +25 -0
  104. package/src/policy-profile.js +18 -3
  105. package/src/recall/anaphora.mjs +87 -0
  106. package/src/recall/cards.mjs +7 -0
  107. package/src/recall/embedding.mjs +8 -8
  108. package/src/recall/entities.mjs +112 -0
  109. package/src/recall/index.js +235 -19
  110. package/src/recall/inject-order.mjs +10 -8
  111. package/src/recall/privacy.mjs +105 -16
  112. package/src/recall/recall-core.mjs +116 -10
  113. package/src/recall/shared-visibility.js +302 -0
  114. package/src/recall/time-normalizer.mjs +196 -0
  115. package/src/role/activation.js +191 -0
  116. package/src/role/attention.js +318 -0
  117. package/src/role/base.js +36 -0
  118. package/src/role/channels.js +423 -0
  119. package/src/role/index.js +425 -0
  120. package/src/role/interfaces.js +115 -0
  121. package/src/role/personality.js +370 -0
  122. package/src/role/readers.js +151 -0
  123. package/src/role/relationships.js +477 -0
  124. package/src/role/store.js +202 -0
  125. package/src/role/writeprotect.js +373 -0
  126. package/src/router/aliases.json +176 -25
  127. package/src/router/background/agent-runner.mjs +1 -1
  128. package/src/router/background/anchor.mjs +1 -1
  129. package/src/router/background/audit.mjs +2 -2
  130. package/src/router/background/budget-ledger.mjs +2 -2
  131. package/src/router/background/host-sim.mjs +1 -1
  132. package/src/router/background/memory.mjs +4 -4
  133. package/src/router/background/queue.mjs +1 -1
  134. package/src/router/background/retry.mjs +1 -1
  135. package/src/router/background/router.mjs +1 -1
  136. package/src/router/background/stream.mjs +1 -1
  137. package/src/router/background/tool-policy.mjs +1 -1
  138. package/src/router/design-headings.json +5 -5
  139. package/src/router/face/config.mjs +1 -1
  140. package/src/router/face/identity.mjs +3 -3
  141. package/src/router/judge.mjs +8 -7
  142. package/src/router/policies/generic.json +21 -4
  143. package/src/router/route.js +109 -22
  144. package/src/router/router.mjs +10 -6
  145. package/src/router/routing-rules.json +260 -445
  146. package/src/router/run-sim.mjs +8 -8
  147. package/src/router/runtime.js +23 -12
  148. package/src/router/service/board.mjs +95 -15
  149. package/src/router/service/config.mjs +5 -5
  150. package/src/router/service/context.mjs +1 -1
  151. package/src/router/service/hint.mjs +14 -0
  152. package/src/router/service/queue.mjs +1 -1
  153. package/src/router/service/service.mjs +27 -11
  154. package/src/router/shadow.mjs +4 -3
  155. package/src/surgery-recovery.js +2 -2
  156. package/src/tool-output.js +2 -2
  157. package/src/tools.js +39 -7
  158. package/src/truncate-visible.js +2 -2
  159. package/src/ui/actions.js +1312 -0
  160. package/src/ui/panel-api.js +367 -0
  161. package/src/ui/panel-auth.js +251 -0
  162. package/src/ui/panel-integrity.js +243 -0
  163. package/src/ui/routes-readonly.js +695 -0
  164. package/src/ui/routes.js +3 -0
  165. package/docs/DEV.md +0 -279
  166. package/docs/PRESENCE-BUS-WIRING.md +0 -491
  167. package/docs/PRESENCE-BUS.md +0 -694
  168. package/docs/SURGERY.md +0 -186
  169. package/scripts/mark-core-cards.mjs +0 -289
  170. package/scripts/pack.mjs +0 -392
  171. package/scripts/router-proposals.mjs +0 -386
  172. package/src/router/fixtures/M1.json +0 -16
  173. package/src/router/fixtures/M2.json +0 -16
  174. package/src/router/fixtures/M3.json +0 -20
  175. package/src/router/fixtures/M4.json +0 -20
  176. package/src/router/fixtures/M5.json +0 -45
  177. package/src/router/fixtures/M6.json +0 -53
  178. package/src/router/fixtures/M7.json +0 -46
  179. package/src/router/fixtures/S1.json +0 -20
  180. package/src/router/fixtures/S2.json +0 -23
  181. package/src/router/fixtures/S3.json +0 -20
  182. package/src/router/fixtures/S4.json +0 -34
  183. package/src/router/fixtures/S5.json +0 -43
  184. package/src/router/fixtures/S6.json +0 -47
  185. package/src/router/fixtures/T1.json +0 -23
  186. package/src/router/fixtures/T2.json +0 -16
  187. package/src/router/fixtures/T3.json +0 -19
  188. package/src/router/fixtures/T4.json +0 -16
  189. package/src/router/fixtures/X1.json +0 -17
  190. package/src/router/fixtures/X2.json +0 -20
  191. package/src/router/gen-design-headings.mjs +0 -107
  192. package/src/router/policies/owner-rulings-2026-11-03.json +0 -62
  193. package/test/auto-recall.test.mjs +0 -474
  194. package/test/auto-truncate.test.mjs +0 -931
  195. package/test/autobind.test.mjs +0 -385
  196. package/test/bus.test.mjs +0 -1572
  197. package/test/cli-guard.test.mjs +0 -198
  198. package/test/conclusion.test.mjs +0 -408
  199. package/test/config-schema.test.mjs +0 -204
  200. package/test/d6-output.test.mjs +0 -194
  201. package/test/d6.test.mjs +0 -395
  202. package/test/deps.test.mjs +0 -342
  203. package/test/doctor-setup.test.mjs +0 -297
  204. package/test/embedding-recall.test.mjs +0 -347
  205. package/test/feature-flags.test.mjs +0 -568
  206. package/test/filter-shadow.test.mjs +0 -170
  207. package/test/fixtures/baseline-0.8.0-registration.json +0 -556
  208. package/test/fixtures/d6-header-tools.json +0 -1
  209. package/test/fixtures/lock-child.mjs +0 -67
  210. package/test/fixtures/pre-step-container.mjs +0 -100
  211. package/test/fixtures/privacy-memory/topics/pii-unmarked.md +0 -9
  212. package/test/fixtures/privacy-memory/topics/secret-local-only.md +0 -11
  213. package/test/fixtures/profile-session-longlive-config.json +0 -20
  214. package/test/fixtures/proposal-cas-child.mjs +0 -66
  215. package/test/host-engine-status.test.mjs +0 -307
  216. package/test/hot-truncate.test.mjs +0 -1349
  217. package/test/identity-card-marker.test.mjs +0 -307
  218. package/test/identity-router/run.mjs +0 -721
  219. package/test/integration-pre-step.test.mjs +0 -553
  220. package/test/live.test.mjs +0 -1020
  221. package/test/maintenance-schedule.test.mjs +0 -538
  222. package/test/memory.test.mjs +0 -2828
  223. package/test/metrics.test.mjs +0 -478
  224. package/test/orchestrator.test.mjs +0 -266
  225. package/test/p1_probe.mjs +0 -97
  226. package/test/packaging.test.mjs +0 -182
  227. package/test/portability.test.mjs +0 -343
  228. package/test/rc4-regressions.test.mjs +0 -562
  229. package/test/rc5-regressions.test.mjs +0 -197
  230. package/test/rc6-chinese.test.mjs +0 -394
  231. package/test/router-1100/apply-off.test.mjs +0 -175
  232. package/test/router-1100/budget-reconcile.test.mjs +0 -215
  233. package/test/router-1100/config-context.test.mjs +0 -211
  234. package/test/router-1100/helpers.mjs +0 -129
  235. package/test/router-1100/hint-envelope.test.mjs +0 -150
  236. package/test/router-1100/lock-crossproc.test.mjs +0 -206
  237. package/test/router-1100/proposal-cas-crossproc.test.mjs +0 -184
  238. package/test/router-1100/router-apply.test.mjs +0 -372
  239. package/test/router-1100/router-fixb.test.mjs +0 -602
  240. package/test/router-1100/router-proposals.test.mjs +0 -405
  241. package/test/router-1100/router-queue-lease-wal.test.mjs +0 -355
  242. package/test/router-1100/router-security-cr.test.mjs +0 -333
  243. package/test/router-1100/service-lifecycle.test.mjs +0 -220
  244. package/test/router-1100/tools-contract.test.mjs +0 -164
  245. package/test/router-1100/turn-reflect-tick.test.mjs +0 -89
  246. package/test/router-1100/v6-contract.test.mjs +0 -158
  247. package/test/router-1100/v6b-contract.test.mjs +0 -293
  248. package/test/router-1100/v6c-contract.test.mjs +0 -249
  249. package/test/router-1100/wal-rotation.test.mjs +0 -375
  250. package/test/router-1100/wp1-config.test.mjs +0 -170
  251. package/test/router-1100/wp1-default-off.test.mjs +0 -77
  252. package/test/router-1100/wp1-model-status.test.mjs +0 -90
  253. package/test/router-1100/wp1-status.test.mjs +0 -220
  254. package/test/router-1100/wp1-tools.test.mjs +0 -146
  255. package/test/router-1100/wp5-cli.test.mjs +0 -240
  256. package/test/router-1100/wp5-model-auto.test.mjs +0 -418
  257. package/test/router-1100/wp6-merge.test.mjs +0 -386
  258. package/test/router-1100/wp6-wake.test.mjs +0 -502
  259. package/test/router-background/audit.test.mjs +0 -37
  260. package/test/router-background/budget-ledger.test.mjs +0 -330
  261. package/test/router-background/cross-process.test.mjs +0 -222
  262. package/test/router-background/fixtures/cross-child.mjs +0 -85
  263. package/test/router-background/fixtures/real-shapes.mjs +0 -239
  264. package/test/router-background/router.test.mjs +0 -1044
  265. package/test/router-background/stream-retry.test.mjs +0 -358
  266. package/test/router-background/tool-agent.test.mjs +0 -384
  267. package/test/router-face/bridge.test.mjs +0 -169
  268. package/test/router-face/face.test.mjs +0 -316
  269. package/test/router-failclosed.test.mjs +0 -466
  270. package/test/router-identity.test.mjs +0 -384
  271. package/test/router-l1-l2.contract.test.mjs +0 -1075
  272. package/test/router-memory-tools.test.mjs +0 -262
  273. package/test/router-policy.test.mjs +0 -225
  274. package/test/router-runtime.test.mjs +0 -438
  275. package/test/router-shadow.test.mjs +0 -287
  276. package/test/router.test.mjs +0 -903
  277. package/test/run-rc.mjs +0 -299
  278. package/test/run.mjs +0 -2469
  279. package/test/safety-defaults.test.mjs +0 -179
  280. package/test/seeded-load.test.mjs +0 -436
  281. package/test/session-original.test.mjs +0 -338
  282. package/test/surgery-recovery.test.mjs +0 -936
  283. package/test/t7_drive.mjs +0 -151
  284. package/test/tool-output.test.mjs +0 -393
  285. package/test/tool-schema.test.mjs +0 -177
  286. package/test/tools_contract.test.mjs +0 -716
  287. package/test/truncate-visible.test.mjs +0 -1150
  288. package/test/ui-flags.test.mjs +0 -477
  289. package/test/v4-hardening.test.mjs +0 -427
@@ -1,556 +0,0 @@
1
- {
2
- "provenance": {
3
- "source": "merged 1.4.0-rc.3 tree (0.8.0 registration contract + D3 session_original)",
4
- "version": "1.4.0-rc.3",
5
- "node": "v24.21.0",
6
- "note": "Regenerated by _work2/gen-baseline.mjs after D3 added the 22nd tool; all flags off, metrics off, routerRuntime off. Do not hand-edit."
7
- },
8
- "returned": {},
9
- "toolCount": 22,
10
- "tools": [
11
- {
12
- "name": "session_health",
13
- "description": "Inspect a DSH session log (session.v4.jsonl.zstd): size, event count, last event type, visible-message count and sha256, age, backups, last truncation. Read-only. Pass sessionPath, sessionId, or session:\"latest\"/\"current\".",
14
- "parameters": {
15
- "sessionPath": {
16
- "type": "string",
17
- "description": "Absolute path to a session.v4.jsonl.zstd file (highest priority)."
18
- },
19
- "sessionId": {
20
- "type": "string",
21
- "description": "Session id (the folder name under <DSH_HOME>/sessions/<scope>/)."
22
- },
23
- "session": {
24
- "type": "string",
25
- "description": "Shortcut instead of an id: \"latest\" (most recently modified log) or \"current\"."
26
- }
27
- },
28
- "hasExecute": true
29
- },
30
- {
31
- "name": "session_original",
32
- "description": "D3 \"fetch the original\": return the text of ONE turn from a session log, including tool output that was compressed out of the context. Read-only and privacy-gated: the FULL selection is scanned before slicing, so maxBytes/offset cannot page around a match. turn is required and 1-based (a user message belongs to its preceding turn/start). kind defaults to \"tool\" (tool results); use \"message\" for user/assistant text, \"call\" for tool arguments, or \"all\". Narrow a turn with toolCallId, or with messageIndex (0-based among the turn's user/assistant messages). The response is capped at maxBytes UTF-8 bytes (default 65536, config originalFetchMaxBytes): when truncated, content stops on a code-point boundary and nextOffset is the byte offset for the next call. Find a turn first with session_search or memory_recall. The result also reports the D2 archive path <memoryRoot>/.longlive/tool-output/<turn>/<id>.txt but NEVER writes it. Pass sessionPath, sessionId, or session:\"latest\"/\"current\".",
33
- "parameters": {
34
- "sessionPath": {
35
- "type": "string",
36
- "description": "Absolute path to a session.v4.jsonl.zstd file (highest priority)."
37
- },
38
- "sessionId": {
39
- "type": "string",
40
- "description": "Session id (the folder name under <DSH_HOME>/sessions/<scope>/)."
41
- },
42
- "session": {
43
- "type": "string",
44
- "description": "Shortcut instead of an id: \"latest\" (most recently modified log) or \"current\"."
45
- },
46
- "turn": {
47
- "type": "integer",
48
- "description": "Required. 1-based turn number (the value in turn/start events)."
49
- },
50
- "kind": {
51
- "type": "string",
52
- "enum": [
53
- "tool",
54
- "message",
55
- "call",
56
- "all"
57
- ],
58
- "description": "Default \"tool\" (tool results). \"message\" = user/assistant text, \"call\" = tool arguments, \"all\" = every item in the turn."
59
- },
60
- "toolCallId": {
61
- "type": "string",
62
- "description": "Select one tool call/result by call id (the toolCallId on the result event)."
63
- },
64
- "messageIndex": {
65
- "type": "integer",
66
- "description": "Select one message by its 0-based index among the turn's user/assistant messages."
67
- },
68
- "offset": {
69
- "type": "integer",
70
- "description": "Byte offset into the full selection for paging (default 0). Use nextOffset from a truncated response."
71
- },
72
- "maxBytes": {
73
- "type": "integer",
74
- "description": "Byte cap for this call (default config originalFetchMaxBytes = 65536; clamped to 1024..4194304)."
75
- }
76
- },
77
- "hasExecute": true
78
- },
79
- {
80
- "name": "session_backup",
81
- "description": "Plan or create a byte-identical backup of a session log next to it (session.v4.jsonl.zstd.bak-YYYYMMDD, sha256 verified). dryRun=true writes nothing. Pass sessionPath, sessionId, or session:\"latest\"/\"current\".",
82
- "parameters": {
83
- "sessionPath": {
84
- "type": "string",
85
- "description": "Absolute path to a session.v4.jsonl.zstd file (highest priority)."
86
- },
87
- "sessionId": {
88
- "type": "string",
89
- "description": "Session id (the folder name under <DSH_HOME>/sessions/<scope>/)."
90
- },
91
- "session": {
92
- "type": "string",
93
- "description": "Shortcut instead of an id: \"latest\" (most recently modified log) or \"current\"."
94
- },
95
- "dryRun": {
96
- "type": "boolean",
97
- "description": "Default true: only report the planned backup."
98
- },
99
- "confirm": {
100
- "type": "string",
101
- "description": "Pass \"BACKUP\" to actually create the backup."
102
- }
103
- },
104
- "hasExecute": true
105
- },
106
- {
107
- "name": "session_truncate",
108
- "description": "Safely drop the oldest events of a session log while keeping the visible conversation byte-identical (verified by a deriveMessages digest), with backup, tmp verify and rollback. dryRun=true writes nothing; writing requires confirm:\"TRUNCATE\". Refuses the session this app has loaded (VISIBLE_TARGET_IS_LIVE_SESSION; unknown caller id: VISIBLE_CURRENT_ID_UNKNOWN): stop the app first. Pass sessionPath, sessionId, or session:\"latest\"/\"current\".",
109
- "parameters": {
110
- "sessionPath": {
111
- "type": "string",
112
- "description": "Absolute path to a session.v4.jsonl.zstd file (highest priority)."
113
- },
114
- "sessionId": {
115
- "type": "string",
116
- "description": "Session id (the folder name under <DSH_HOME>/sessions/<scope>/)."
117
- },
118
- "session": {
119
- "type": "string",
120
- "description": "Shortcut instead of an id: \"latest\" (most recently modified log) or \"current\"."
121
- },
122
- "keepEvents": {
123
- "type": "integer",
124
- "description": "How many trailing events to keep (integer >= 1, default 20000)."
125
- },
126
- "dryRun": {
127
- "type": "boolean",
128
- "description": "Default true: plan and verify without writing."
129
- },
130
- "confirm": {
131
- "type": "string",
132
- "description": "Pass \"TRUNCATE\" to actually rewrite the log."
133
- },
134
- "allowLive": {
135
- "type": "boolean",
136
- "description": "Diagnostics only. Bypass the live-session refusal; also requires confirm:\"I-KNOW-THIS-CORRUPTS\". Never use it on the session this app has loaded."
137
- }
138
- },
139
- "hasExecute": true
140
- },
141
- {
142
- "name": "session_truncate_visible",
143
- "description": "Drop whole turns from the FRONT of the visible conversation so the model actually receives less context. The cut is always on a turn boundary; the first user-authored message survives (copied into the retained head when its turn is dropped); a protected system/message is kept; a synthetic user/message archive card records what was removed. The removed prefix is archived under <session>.archive/<ts>.jsonl.zstd; seq is renumbered 0..n-1; the write is tmp + fsync + atomic rename with read-back verification and an inflight rollback record. dryRun=true writes nothing; writing requires confirm:\"TRUNCATE-VISIBLE\". Refuses the session this app has loaded (VISIBLE_TARGET_IS_LIVE_SESSION; unknown caller id: VISIBLE_CURRENT_ID_UNKNOWN): truncate it only while the app is stopped. Defaults: keepTurns=12, keepTokens=80000 (the later boundary wins). When the first user message is dropped it is carried as a bounded prefix (firstUserCapChars, default 4000) that names the archive instead of copying megabytes; the post-cut token budget counts that card, the archive card and the protected head. An unstable boundary is retried by dropping one more turn (boundaryRetries, default 3); if no cut fits, the result is feasible=false with the attempted cuts, never a silent success. Pass sessionPath, sessionId, or session:\"latest\"/\"current\".",
144
- "parameters": {
145
- "sessionPath": {
146
- "type": "string",
147
- "description": "Absolute path to a session.v4.jsonl.zstd file (highest priority)."
148
- },
149
- "sessionId": {
150
- "type": "string",
151
- "description": "Session id (the folder name under <DSH_HOME>/sessions/<scope>/)."
152
- },
153
- "session": {
154
- "type": "string",
155
- "description": "Shortcut instead of an id: \"latest\" (most recently modified log) or \"current\"."
156
- },
157
- "keepTurns": {
158
- "type": "integer",
159
- "description": "Keep at least this many trailing turns (integer >= 1, default 12)."
160
- },
161
- "keepTokens": {
162
- "type": "integer",
163
- "description": "Keep at least this many estimated trailing message tokens (integer >= 1, default 80000)."
164
- },
165
- "firstUserCapChars": {
166
- "type": "integer",
167
- "description": "Cap the carried first user message at this many characters (integer >= 1, default 4000); the full text stays in the archive."
168
- },
169
- "boundaryRetries": {
170
- "type": "integer",
171
- "description": "When the cut boundary cannot be stabilized, retry with one more dropped turn this many times (integer >= 0, default 3)."
172
- },
173
- "dryRun": {
174
- "type": "boolean",
175
- "description": "Default true: plan and report without writing."
176
- },
177
- "confirm": {
178
- "type": "string",
179
- "description": "Pass \"TRUNCATE-VISIBLE\" to actually rewrite the log."
180
- },
181
- "carryFirstUserMessage": {
182
- "type": "boolean",
183
- "description": "Copy the first user-authored message into the retained head when its turn is dropped (default true)."
184
- },
185
- "insertArchiveCard": {
186
- "type": "boolean",
187
- "description": "Insert the synthetic [已归档 ...] user/message card (default true). Set false if the host rejects it."
188
- },
189
- "allowLive": {
190
- "type": "boolean",
191
- "description": "Diagnostics only. Bypass the live-session refusal; also requires confirm:\"I-KNOW-THIS-CORRUPTS\". Never use it on the session this app has loaded."
192
- }
193
- },
194
- "hasExecute": true
195
- },
196
- {
197
- "name": "session_compact",
198
- "description": "Trigger one native harness compaction so the summary is written into the session log (compaction/summary + compact-checkpoint) instead of living in process memory. This is the only way to make a long session shorter without losing what it knows; run it BEFORE session_truncate, because truncation can only delete history a compaction already shadowed. dryRun=true only reports availability. When called inside a running turn the request is queued until the agent goes idle.",
199
- "parameters": {
200
- "dryRun": {
201
- "type": "boolean",
202
- "description": "Default true: only report whether compaction is available."
203
- },
204
- "sourceCommandId": {
205
- "type": "string",
206
- "description": "Optional command identity recorded on the compaction."
207
- }
208
- },
209
- "hasExecute": true
210
- },
211
- {
212
- "name": "session_wake",
213
- "description": "Read a bounded wake-up slice of the memory store (default <DSH home>/longlive-memory; override with plugin config memoryRoot / memRoot or $DSH_LONGLIVE_MEMORY_ROOT, legacy $DSH_MEM): the STATE.md sections 我在哪 / 在等什么 / 最近三件 (400 chars each), flow/current.md (800 chars max), the last three \"##\" headings of the newest journal/*.md (newest by year/month, not by string sort), and up to 20 INDEX.md lines shorter than 60 chars. Read-only. Missing files are skipped with a note instead of failing; an absent memory root reports unavailable. The text is capped at 2500 characters; truncated is true whenever a section or the whole text was cut.",
214
- "parameters": {
215
- "session": {
216
- "type": "string",
217
- "description": "Optional session label for the caller. It never selects or replaces memory content; newlines and \"#\" are stripped before it can appear in the text."
218
- }
219
- },
220
- "hasExecute": true
221
- },
222
- {
223
- "name": "lease_acquire",
224
- "description": "Atomically take the memory-store write lease (<memoryRoot>/.lease, default <DSH home>/longlive-memory/.lease; override with memoryRoot / memRoot, $DSH_LONGLIVE_MEMORY_ROOT, or legacy $DSH_MEM). Concurrency-safe across processes (O_EXCL create): exactly one caller gets ok:true. An existing, unexpired lease returns ok:false with its holder and acquiredAt (reason \"held by another writer\"); an expired or unparseable lease is replaced, retried exactly once.",
225
- "parameters": {
226
- "holder": {
227
- "type": "string",
228
- "description": "Who wants the lease (a process/session name). Required."
229
- },
230
- "ttlSec": {
231
- "type": "integer",
232
- "description": "Lease lifetime in whole seconds (integer 1..86400, default 300). A day is the ceiling: a longer lease is a typo, not a plan."
233
- }
234
- },
235
- "hasExecute": true
236
- },
237
- {
238
- "name": "lease_release",
239
- "description": "Release the memory-store write lease, but only for the holder recorded inside it. No file reports ok:false reason \"no lease\"; a different holder reports ok:false reason \"not the holder\" and the lease is left in place.",
240
- "parameters": {
241
- "holder": {
242
- "type": "string",
243
- "description": "Who is releasing; must match the recorded holder. Required."
244
- }
245
- },
246
- "hasExecute": true
247
- },
248
- {
249
- "name": "worker_spawn",
250
- "description": "Dispatch one background child session (worker) through the host subagents service and return immediately with its workerId. The child runs in its own context and reports back through the host; read its state with worker_status and stop it with worker_kill. Pass prompt (required: the task text), an optional label, and timeoutSec (default 600).",
251
- "parameters": {
252
- "prompt": {
253
- "type": "string",
254
- "description": "The task to hand to the child session. Required."
255
- },
256
- "label": {
257
- "type": "string",
258
- "description": "Optional short label for the child (defaults to the prompt head)."
259
- },
260
- "timeoutSec": {
261
- "type": "integer",
262
- "description": "How long the dispatch itself may take, in whole seconds (integer >= 1, default 600)."
263
- }
264
- },
265
- "hasExecute": true
266
- },
267
- {
268
- "name": "worker_status",
269
- "description": "Report where a worker stands: live status from the Agent registry, else the parent child catalog, else its persisted session log (events count, last message time, status). Pass workerId (required), the id worker_spawn returned.",
270
- "parameters": {
271
- "workerId": {
272
- "type": "string",
273
- "description": "The worker id returned by worker_spawn. Required."
274
- }
275
- },
276
- "hasExecute": true
277
- },
278
- {
279
- "name": "worker_kill",
280
- "description": "Stop one running worker through the host subagents service (interrupt, fire-and-return). A worker that is not running reports unavailable instead of a fake success. Pass workerId (required) and an optional reason.",
281
- "parameters": {
282
- "workerId": {
283
- "type": "string",
284
- "description": "The worker id returned by worker_spawn. Required."
285
- },
286
- "reason": {
287
- "type": "string",
288
- "description": "Optional short reason recorded in the report."
289
- }
290
- },
291
- "hasExecute": true
292
- },
293
- {
294
- "name": "memory_index",
295
- "description": "Rebuild the generated section of <memoryRoot>/INDEX.md from every card under topics/, journal/ and procedures/. Only the region between <!-- BEGIN GENERATED --> and <!-- END GENERATED --> is replaced; hand-written content outside is preserved byte-for-byte. Cards without frontmatter are legacy cards and still get a row. Procedures get their own 「程序性」 table (name / trigger / path / strength / version) so they are counted without appearing twice; a broken procedure card is listed as an invalid row instead of failing the run. Default is dry-run (returns the section and the resulting file, writes nothing); pass apply:true to write. Memory root: plugin config memoryRoot (alias memRoot), else $DSH_LONGLIVE_MEMORY_ROOT, else legacy $DSH_MEM, else <DSH home>/longlive-memory (created on demand).",
296
- "parameters": {
297
- "apply": {
298
- "type": "boolean",
299
- "description": "Default false (dry-run). true rewrites only the generated section, after backing INDEX.md up."
300
- }
301
- },
302
- "hasExecute": true
303
- },
304
- {
305
- "name": "memory_consolidate",
306
- "description": "Read journal/*.md entries dated after the last consolidation (or an explicit since) and produce a proposal report: which paragraph belongs in which card, why, a 120-char excerpt, and a suggested salience. Journal paragraphs may carry `<!-- affect: N | reason -->` (N 1..5); the marker wins over the regex fallback, and malformed markers are listed under badAffect without failing the run. Every proposal gets a stable id (sha1(source paragraph text)[:12]); ids already in state.seenProposals are never proposed again. Report only — it never edits a card. It writes exactly one file under <memoryRoot>/.longlive/reports/ and records new proposal ids in memory-state.json; lastConsolidated advances only after every proposal for the newest scanned day is resolved (accepted/rejected/superseded). There is no apply flag by design.",
307
- "parameters": {
308
- "since": {
309
- "type": "string",
310
- "description": "Optional YYYY-MM-DD override for the incremental start; defaults to memory-state.json lastConsolidated."
311
- }
312
- },
313
- "hasExecute": true
314
- },
315
- {
316
- "name": "memory_reinforce",
317
- "description": "Record one real use of a card: strength = min(9, strength+1), last_used = today, revisions += 1, plus one <!-- rev: ... --> line with the reason. The prose body must stay byte-identical; the original is backed up to <memoryRoot>/.longlive/backups/ before writing, and any post-write mismatch rolls the card back. Pass path (required, relative to memoryRoot or absolute inside it).",
318
- "parameters": {
319
- "path": {
320
- "type": "string",
321
- "description": "The card to reinforce, relative to memoryRoot or an absolute path inside it. Required."
322
- },
323
- "reason": {
324
- "type": "string",
325
- "description": "Optional short reason stored in the rev comment (default \"被真实使用\")."
326
- }
327
- },
328
- "hasExecute": true
329
- },
330
- {
331
- "name": "memory_forget",
332
- "description": "Propose forgetting by decay rules: last_used > 90 days and strength <= 2 demotes to epochs/, last_used > 365 days tombstones. Cards with salience >= 5 are never proposed. Default is dry-run and writes nothing: it only reports proposals and whether epochs/, archive/tombstones/ and .longlive/backups exist. Execution requires confirm:\"FORGET\": it creates those paths, backs up each card into .longlive/backups/ first, rewrites tombstones.md atomically with a three-question epitaph per card (reason / affects / stillUseful) BEFORE moving the card to archive/tombstones/ (or epochs/), appends exactly one line to .longlive/audit.log, and never deletes a file.",
333
- "parameters": {
334
- "confirm": {
335
- "type": "string",
336
- "description": "Default absent = dry-run. Pass \"FORGET\" to execute the proposals."
337
- }
338
- },
339
- "hasExecute": true
340
- },
341
- {
342
- "name": "memory_procedure",
343
- "description": "Procedural memory: how a task is done, not what happened. One card per procedure at <memoryRoot>/procedures/<slug>.md. Frontmatter is required: name / trigger / salience / strength / last_used (plus revisions). Body sections: 何时用 / 怎么做 (a numbered list) / 失败模式 / 例子. action:\"save\" creates or merges by slug (same slug -> revisions+1, the old version is backed up under .longlive/backups/, and fields you omit keep their old value); a NEW card needs name plus when and at least one step. action:\"list\" filters by trigger keyword (space or comma separated, case-insensitive, OR over keywords); action:\"get\" returns one card including its raw text; action:\"report\" records one real use: success -> strength+1, failure -> strength-1 (clamped to 0..9), last_used = today, plus one <!-- rev: ... --> line. report leaves revisions alone: revisions counts content versions, strength counts how often it worked. A card whose frontmatter is broken or misses a required field is reported with E_PROCEDURE_INVALID and is never silently overwritten.",
344
- "parameters": {
345
- "action": {
346
- "type": "string",
347
- "enum": [
348
- "save",
349
- "list",
350
- "get",
351
- "report"
352
- ],
353
- "description": "Required. save | list | get | report."
354
- },
355
- "name": {
356
- "type": "string",
357
- "description": "Card title (frontmatter name). save derives the slug from it when slug is absent; get/report accept it instead of slug."
358
- },
359
- "slug": {
360
- "type": "string",
361
- "description": "File stem under procedures/. save/get/report may pass it explicitly (no path separators, no ..)."
362
- },
363
- "trigger": {
364
- "type": "array",
365
- "items": {
366
- "type": "string"
367
- },
368
- "description": "When this procedure applies (frontmatter trigger). save replaces the whole list; list uses the keywords as an OR filter."
369
- },
370
- "salience": {
371
- "type": "integer",
372
- "description": "Importance 1..5 (default 3 on a new card, kept on merge unless given)."
373
- },
374
- "strength": {
375
- "type": "integer",
376
- "description": "How often it worked 0..9 (default 3 on a new card; report changes it, save sets it explicitly)."
377
- },
378
- "when": {
379
- "type": "string",
380
- "description": "Body section 「何时用」: the situation that calls for this procedure. Required for a new card."
381
- },
382
- "steps": {
383
- "type": "array",
384
- "items": {
385
- "type": "string"
386
- },
387
- "description": "Body section 「怎么做」: ordered steps, written as a numbered list. Required (at least one) for a new card. A single string with newlines is accepted too."
388
- },
389
- "failureModes": {
390
- "type": "string",
391
- "description": "Optional body section 「失败模式」: how this procedure goes wrong."
392
- },
393
- "example": {
394
- "type": "string",
395
- "description": "Optional body section 「例子」: one concrete run."
396
- },
397
- "outcome": {
398
- "type": "string",
399
- "enum": [
400
- "success",
401
- "failure"
402
- ],
403
- "description": "report only, required: success raises strength by 1, failure lowers it by 1."
404
- },
405
- "reason": {
406
- "type": "string",
407
- "description": "report only: short reason stored in the rev comment."
408
- },
409
- "limit": {
410
- "type": "integer",
411
- "description": "list only: page size (default 50, max 500). total/count/truncated are always reported."
412
- }
413
- },
414
- "hasExecute": true
415
- },
416
- {
417
- "name": "memory_recall",
418
- "description": "Search memory cards by meaning-ish keywords and get the best few back. Mixed, reproducible score: title hits *5 + tags *3 + body frequency (capped 3) + whole-query phrase bonus + character-bigram Dice similarity. Returns {path, title, score, excerpt} sorted by score desc then path asc; the same tree and query always produce byte-identical results. Defaults: topK 3 (1..20), room any, status active+locked — already-locked cards are searchable. Pass status:\"archived\" or \"any\" to widen, room:\"topics\" to narrow. READ-ONLY: it never writes a card, never builds INDEX.md and never creates .longlive/. An empty query throws E_RECALL_EMPTY_QUERY; no hit is not an error — it returns an empty results list with code E_RECALL_NO_MATCH and a hint.",
419
- "parameters": {
420
- "query": {
421
- "type": "string",
422
- "description": "Required. Natural language or keywords, e.g. \"听觉 训练\"."
423
- },
424
- "topK": {
425
- "type": "integer",
426
- "description": "How many cards to return (default 3, clamped to 1..20)."
427
- },
428
- "room": {
429
- "type": "string",
430
- "description": "Optional relative directory filter: topics, journal, or a subdirectory of either."
431
- },
432
- "status": {
433
- "type": "string",
434
- "enum": [
435
- "active",
436
- "locked",
437
- "archived",
438
- "any"
439
- ],
440
- "description": "Card status filter. Default active+locked; \"any\" includes archived."
441
- }
442
- },
443
- "hasExecute": true
444
- },
445
- {
446
- "name": "session_live_update",
447
- "description": "Write or patch this session's row in the live registry (<memoryRoot>/.longlive/live/<sessionId>.json). state is required: idle | running | waiting | blocked | stale. Omitted optional fields keep their previous value, except blocked_on, which is cleared whenever state is not \"blocked\". pid and host are always the calling process and machine; startedAt is preserved. goal is cut to 80 characters, queued to 200 items of 300 characters each, and last_action.detail to 500 characters; the result reports every cut (goalTruncated / queuedTruncated / lastActionDetailTruncated). The file is written tmp -> rename with a read-back, so a crash never leaves half a JSON. If a directory or symlink occupies the row path, the write is refused up front with E_LIVE_ROW_BLOCKED instead of failing late in rename. Pass sessionId only to write another session's row; by default the host's current session is used.",
448
- "parameters": {
449
- "sessionId": {
450
- "type": "string",
451
- "description": "Optional session id; defaults to the host's current session id."
452
- },
453
- "state": {
454
- "type": "string",
455
- "enum": [
456
- "idle",
457
- "running",
458
- "waiting",
459
- "blocked",
460
- "stale"
461
- ],
462
- "description": "Required writer-declared state. dead/corrupt are computed by readers and cannot be written."
463
- },
464
- "turn": {
465
- "type": "integer",
466
- "description": "Current turn number (integer >= 0, default keeps the previous value or 0)."
467
- },
468
- "goal": {
469
- "type": "string",
470
- "description": "What this session is doing; stored at most 80 characters (longer input is truncated and flagged)."
471
- },
472
- "blocked_on": {
473
- "type": "string",
474
- "description": "What the session is waiting on. Only kept while state is \"blocked\"."
475
- },
476
- "queued": {
477
- "type": "array",
478
- "items": {
479
- "type": "string"
480
- },
481
- "description": "Queued work items (strings); replaces the previous queue."
482
- },
483
- "ttlMs": {
484
- "type": "integer",
485
- "description": "Heartbeat lifetime in ms (integer 1000..86400000, default 60000). Older than 2x this is stale."
486
- },
487
- "last_action": {
488
- "type": "object",
489
- "additionalProperties": false,
490
- "properties": {
491
- "at": {
492
- "type": "string",
493
- "description": "ISO timestamp; defaults to now."
494
- },
495
- "kind": {
496
- "type": "string",
497
- "description": "Short action kind, e.g. \"edit\" or \"tool\"."
498
- },
499
- "detail": {
500
- "type": "string",
501
- "description": "Short human-readable detail."
502
- }
503
- },
504
- "description": "Last action {at, kind, detail}; defaults to now/create or now/update."
505
- }
506
- },
507
- "hasExecute": true
508
- },
509
- {
510
- "name": "session_live_list",
511
- "description": "List the live registry (<memoryRoot>/.longlive/live) with a computed effectiveState for every row. Liveness is heartbeat-first: a heartbeat within one ttlMs is running regardless of pid (pid is the writer's, not the session's); older than 2x ttlMs is stale; older than 4x ttlMs, or same-host with a pid that definitively no longer exists, is dead; a file that cannot be parsed or has the wrong shape is corrupt (listed, never thrown). Other hosts' pids are never probed, so remote rows age purely by heartbeat and can be stale or dead but are never treated as dead by pid. dead rows are hidden unless includeDead:true; the result always reports hiddenDead and per-state counts. Orphan tmp files are ignored by the list.",
512
- "parameters": {
513
- "includeDead": {
514
- "type": "boolean",
515
- "description": "Default false: hide rows whose same-host pid no longer exists."
516
- }
517
- },
518
- "hasExecute": true
519
- },
520
- {
521
- "name": "session_live_get",
522
- "description": "Read one row of the live registry by sessionId, with the same computed effectiveState as session_live_list (dead / stale / corrupt included). sessionId must already be canonical (lowercase): a case or whitespace alias throws E_SESSION_ID_NOT_CANONICAL with the canonical id in error.canonical, because NTFS maps aliases to one file and a silent lookup cannot say which session was read. A symlinked row is refused rather than followed. A missing row returns ok:false status:\"absent\" instead of failing; a row that cannot be parsed throws E_LIVE_CORRUPT.",
523
- "parameters": {
524
- "sessionId": {
525
- "type": "string",
526
- "description": "Session id whose row to read. Required."
527
- }
528
- },
529
- "hasExecute": true
530
- },
531
- {
532
- "name": "session_live_prune",
533
- "description": "Clean the live registry. Row candidates are dead rows from this host (heartbeat older than 4x ttlMs, or same-host pid definitively gone), or corrupt rows without another host's stamp. Before unlinking, each candidate is re-read and re-judged: it is deleted only if it is STILL dead/corrupt AND its bytes did not change; otherwise it is returned under skipped. A row carrying blocked_on is never auto-deleted (skipped), and every deleted/skipped row keeps blocked_on, queued and startedAt in the result. Remote rows are never deleted: a merely dead remote row is left alone, and a corrupt row whose bytes still name a remote host is skipped too (host unknown is the only corrupt case prune may remove). Also reports (and with confirm removes) orphan *.tmp-* files older than one hour and empty directory placeholders; non-empty directories and symlinks are reported only. Default is dry-run (candidates, wouldDelete, skipped; deletes nothing); a real delete requires confirm:\"PRUNE\". dryRun:false without the confirm word throws E_CONFIRM_REQUIRED.",
534
- "parameters": {
535
- "dryRun": {
536
- "type": "boolean",
537
- "description": "Default true (preview only). false with confirm:\"PRUNE\" really deletes."
538
- },
539
- "confirm": {
540
- "type": "string",
541
- "description": "Pass \"PRUNE\" to actually delete the previewed candidates."
542
- }
543
- },
544
- "hasExecute": true
545
- }
546
- ],
547
- "hooks": [
548
- {
549
- "name": "session/event",
550
- "prepend": false,
551
- "priority": null
552
- }
553
- ],
554
- "warnings": [],
555
- "infos": []
556
- }