@mnemom/mnemom 0.17.33 → 0.17.34-next.2

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 (236) hide show
  1. package/dist/base-url-proxy.mjs +18 -4
  2. package/dist/commands/agent-config.js +5 -1
  3. package/dist/commands/agent-doctor.d.ts +9 -0
  4. package/dist/commands/agent-doctor.js +4 -0
  5. package/dist/commands/agent-session.d.ts +1 -2
  6. package/dist/commands/agent-session.js +1 -2
  7. package/dist/commands/agent-setup.d.ts +6 -0
  8. package/dist/commands/agent-setup.js +12 -1
  9. package/dist/commands/agent-share-render.d.ts +1 -1
  10. package/dist/commands/agent-share-render.js +1 -1
  11. package/dist/commands/agent.d.ts +26 -0
  12. package/dist/commands/agent.js +228 -53
  13. package/dist/commands/durable-doctor.d.ts +50 -0
  14. package/dist/commands/durable-doctor.js +186 -0
  15. package/dist/commands/durable-lock.d.ts +254 -0
  16. package/dist/commands/durable-lock.js +595 -0
  17. package/dist/commands/durable-sessions.d.ts +87 -0
  18. package/dist/commands/durable-sessions.js +303 -0
  19. package/dist/commands/durable-verbs.d.ts +116 -0
  20. package/dist/commands/durable-verbs.js +393 -0
  21. package/dist/commands/protection.js +1 -1
  22. package/dist/commands/scan.d.ts +97 -0
  23. package/dist/commands/scan.js +238 -0
  24. package/dist/durable/flock.d.ts +26 -0
  25. package/dist/durable/flock.js +80 -0
  26. package/dist/durable/launch-spec.d.ts +7 -1
  27. package/dist/durable/launch-spec.js +10 -3
  28. package/dist/durable/seat-proxy-entry.d.ts +11 -0
  29. package/dist/durable/seat-proxy-entry.js +25 -0
  30. package/dist/durable/seat-proxy.d.ts +107 -0
  31. package/dist/durable/seat-proxy.js +599 -0
  32. package/dist/durable/seat-socket.d.ts +46 -0
  33. package/dist/durable/seat-socket.js +179 -0
  34. package/dist/durable/seat-spec-validator.d.ts +28 -0
  35. package/dist/durable/seat-spec-validator.js +59 -0
  36. package/dist/durable/test-mode.mjs +81 -0
  37. package/dist/durable/turn-chain.d.ts +55 -0
  38. package/dist/durable/turn-chain.js +178 -0
  39. package/dist/host/auto-continue.d.ts +67 -0
  40. package/dist/host/auto-continue.js +118 -0
  41. package/dist/host/bootid.d.ts +13 -0
  42. package/dist/host/bootid.js +41 -0
  43. package/dist/host/client.d.ts +123 -0
  44. package/dist/host/client.js +171 -0
  45. package/dist/host/commands.d.ts +2 -0
  46. package/dist/host/commands.js +122 -0
  47. package/dist/host/control.d.ts +33 -0
  48. package/dist/host/control.js +144 -0
  49. package/dist/host/daemon.d.ts +217 -0
  50. package/dist/host/daemon.js +606 -0
  51. package/dist/host/gate.d.ts +46 -0
  52. package/dist/host/gate.js +82 -0
  53. package/dist/host/grant.d.ts +24 -0
  54. package/dist/host/grant.js +24 -0
  55. package/dist/host/instance.d.ts +35 -0
  56. package/dist/host/instance.js +62 -0
  57. package/dist/host/integrity.d.ts +35 -0
  58. package/dist/host/integrity.js +115 -0
  59. package/dist/host/launchd.d.ts +16 -0
  60. package/dist/host/launchd.js +45 -0
  61. package/dist/host/lock.d.ts +20 -0
  62. package/dist/host/lock.js +63 -0
  63. package/dist/host/owner.d.ts +55 -0
  64. package/dist/host/owner.js +121 -0
  65. package/dist/host/plist.d.ts +16 -0
  66. package/dist/host/plist.js +85 -0
  67. package/dist/host/procs.d.ts +27 -0
  68. package/dist/host/procs.js +105 -0
  69. package/dist/host/run.d.ts +66 -0
  70. package/dist/host/run.js +314 -0
  71. package/dist/host/runtime.d.ts +8 -0
  72. package/dist/host/runtime.js +22 -0
  73. package/dist/host/s1-names.d.ts +22 -0
  74. package/dist/host/s1-names.js +21 -0
  75. package/dist/host/seat-credential.d.ts +27 -0
  76. package/dist/host/seat-credential.js +41 -0
  77. package/dist/host/seat-launch-main.d.ts +1 -0
  78. package/dist/host/seat-launch-main.js +59 -0
  79. package/dist/host/seat-launch.d.ts +116 -0
  80. package/dist/host/seat-launch.js +302 -0
  81. package/dist/host/seat.d.ts +48 -0
  82. package/dist/host/seat.js +149 -0
  83. package/dist/host/state.d.ts +33 -0
  84. package/dist/host/state.js +117 -0
  85. package/dist/host/status.d.ts +101 -0
  86. package/dist/host/status.js +307 -0
  87. package/dist/host/tmux.d.ts +56 -0
  88. package/dist/host/tmux.js +182 -0
  89. package/dist/host/transcript.d.ts +30 -0
  90. package/dist/host/transcript.js +130 -0
  91. package/dist/host/verbs.d.ts +50 -0
  92. package/dist/host/verbs.js +271 -0
  93. package/dist/index.d.ts +8 -6
  94. package/dist/index.js +232 -35
  95. package/dist/lib/agent.d.ts +8 -26
  96. package/dist/lib/agent.js +11 -26
  97. package/dist/lib/api.d.ts +13 -0
  98. package/dist/lib/api.js +8 -0
  99. package/dist/lib/claude-plugin.d.ts +11 -2
  100. package/dist/lib/claude-plugin.js +10 -1
  101. package/dist/lib/durable/api.d.ts +136 -0
  102. package/dist/lib/durable/api.js +220 -0
  103. package/dist/lib/durable/codes.d.ts +8 -0
  104. package/dist/lib/durable/codes.js +2 -0
  105. package/dist/lib/durable/first-turn.d.ts +50 -0
  106. package/dist/lib/durable/first-turn.js +102 -0
  107. package/dist/lib/durable/gate.d.ts +48 -0
  108. package/dist/lib/durable/gate.js +122 -0
  109. package/dist/lib/durable/handover.d.ts +101 -0
  110. package/dist/lib/durable/handover.js +175 -0
  111. package/dist/lib/durable/launch.d.ts +182 -0
  112. package/dist/lib/durable/launch.js +443 -0
  113. package/dist/lib/durable/launching.d.ts +15 -0
  114. package/dist/lib/durable/launching.js +70 -0
  115. package/dist/lib/durable/live.d.ts +194 -0
  116. package/dist/lib/durable/live.js +635 -0
  117. package/dist/lib/durable/refusal.d.ts +57 -0
  118. package/dist/lib/durable/refusal.js +78 -0
  119. package/dist/lib/durable/view.d.ts +83 -0
  120. package/dist/lib/durable/view.js +244 -0
  121. package/dist/lib/experimental.d.ts +0 -9
  122. package/dist/lib/experimental.js +18 -17
  123. package/dist/lib/launcher-config.d.ts +7 -0
  124. package/dist/lib/launcher-config.js +21 -0
  125. package/dist/lib/scan/caps.d.ts +35 -0
  126. package/dist/lib/scan/caps.js +67 -0
  127. package/dist/lib/scan/credential.d.ts +37 -0
  128. package/dist/lib/scan/credential.js +104 -0
  129. package/dist/lib/scan/failure.d.ts +10 -0
  130. package/dist/lib/scan/failure.js +43 -0
  131. package/dist/lib/scan/pr-source.d.ts +57 -0
  132. package/dist/lib/scan/pr-source.js +130 -0
  133. package/dist/lib/scan/record.d.ts +49 -0
  134. package/dist/lib/scan/record.js +85 -0
  135. package/dist/lib/scan/report-email.d.ts +92 -0
  136. package/dist/lib/scan/report-email.js +144 -0
  137. package/dist/lib/scan/report.d.ts +13 -0
  138. package/dist/lib/scan/report.js +30 -0
  139. package/dist/lib/scan/scan.d.ts +64 -0
  140. package/dist/lib/scan/scan.js +182 -0
  141. package/dist/lib/scan/share.d.ts +38 -0
  142. package/dist/lib/scan/share.js +47 -0
  143. package/dist/lib/scan/spend/ai-tool-detect.d.ts +6 -0
  144. package/dist/lib/scan/spend/ai-tool-detect.js +18 -0
  145. package/dist/lib/scan/spend/anthropic-usage.d.ts +57 -0
  146. package/dist/lib/scan/spend/anthropic-usage.js +244 -0
  147. package/dist/lib/scan/spend/cost-corpus.v1.d.ts +56 -0
  148. package/dist/lib/scan/spend/cost-corpus.v1.js +64 -0
  149. package/dist/lib/scan/spend/prices.d.ts +33 -0
  150. package/dist/lib/scan/spend/prices.js +57 -0
  151. package/dist/lib/scan/spend/spend-estimate.d.ts +30 -0
  152. package/dist/lib/scan/spend/spend-estimate.js +70 -0
  153. package/dist/lib/scan/spend/spend-tiers.d.ts +25 -0
  154. package/dist/lib/scan/spend/spend-tiers.js +42 -0
  155. package/dist/lib/scan/spend/token-rate.d.ts +30 -0
  156. package/dist/lib/scan/spend/token-rate.js +54 -0
  157. package/dist/lib/session-key.d.ts +41 -0
  158. package/dist/lib/session-key.js +124 -0
  159. package/dist/lib/subscription-login.d.ts +15 -0
  160. package/dist/lib/subscription-login.js +28 -1
  161. package/dist/rc-key-401.mjs +75 -0
  162. package/dist/rc-proxy.mjs +221 -25
  163. package/dist/vendor/durable-contract/index.js +240 -18
  164. package/dist/vendor/shared/durable-contract/admission.d.ts +240 -0
  165. package/dist/vendor/shared/durable-contract/admission.js +533 -0
  166. package/dist/vendor/shared/durable-contract/arm.d.ts +51 -0
  167. package/dist/vendor/shared/durable-contract/arm.js +133 -0
  168. package/dist/vendor/shared/durable-contract/canonical.d.ts +29 -0
  169. package/dist/vendor/shared/durable-contract/canonical.js +98 -0
  170. package/dist/vendor/shared/durable-contract/cell-id.d.ts +22 -0
  171. package/dist/vendor/shared/durable-contract/cell-id.js +38 -0
  172. package/dist/vendor/shared/durable-contract/cli-output.d.ts +342 -0
  173. package/dist/vendor/shared/durable-contract/cli-output.js +201 -0
  174. package/dist/vendor/shared/durable-contract/cli-surface.d.ts +119 -0
  175. package/dist/vendor/shared/durable-contract/cli-surface.js +270 -0
  176. package/dist/vendor/shared/durable-contract/codes.d.ts +537 -0
  177. package/dist/vendor/shared/durable-contract/codes.js +475 -0
  178. package/dist/vendor/shared/durable-contract/constants.d.ts +153 -0
  179. package/dist/vendor/shared/durable-contract/constants.js +179 -0
  180. package/dist/vendor/shared/durable-contract/doors.d.ts +119 -0
  181. package/dist/vendor/shared/durable-contract/doors.js +249 -0
  182. package/dist/vendor/shared/durable-contract/envelope.d.ts +103 -0
  183. package/dist/vendor/shared/durable-contract/envelope.js +317 -0
  184. package/dist/vendor/shared/durable-contract/events.d.ts +147 -0
  185. package/dist/vendor/shared/durable-contract/events.js +630 -0
  186. package/dist/vendor/shared/durable-contract/executor-state.d.ts +41 -0
  187. package/dist/vendor/shared/durable-contract/executor-state.js +32 -0
  188. package/dist/vendor/shared/durable-contract/fixlines.d.ts +318 -0
  189. package/dist/vendor/shared/durable-contract/fixlines.js +483 -0
  190. package/dist/vendor/shared/durable-contract/freeze-record.d.ts +787 -0
  191. package/dist/vendor/shared/durable-contract/freeze-record.js +361 -0
  192. package/dist/vendor/shared/durable-contract/index.d.ts +36 -0
  193. package/dist/vendor/shared/durable-contract/index.js +37 -0
  194. package/dist/vendor/shared/durable-contract/journal-approvals.d.ts +29 -0
  195. package/dist/vendor/shared/durable-contract/journal-approvals.js +60 -0
  196. package/dist/vendor/shared/durable-contract/keys.d.ts +24 -0
  197. package/dist/vendor/shared/durable-contract/keys.js +66 -0
  198. package/dist/vendor/shared/durable-contract/lock.d.ts +128 -0
  199. package/dist/vendor/shared/durable-contract/lock.js +151 -0
  200. package/dist/vendor/shared/durable-contract/names.d.ts +25 -0
  201. package/dist/vendor/shared/durable-contract/names.js +78 -0
  202. package/dist/vendor/shared/durable-contract/owner-verbs.d.ts +175 -0
  203. package/dist/vendor/shared/durable-contract/owner-verbs.js +403 -0
  204. package/dist/vendor/shared/durable-contract/paths.d.ts +23 -0
  205. package/dist/vendor/shared/durable-contract/paths.js +35 -0
  206. package/dist/vendor/shared/durable-contract/pure-fn.d.ts +48 -0
  207. package/dist/vendor/shared/durable-contract/pure-fn.js +89 -0
  208. package/dist/vendor/shared/durable-contract/routes.d.ts +205 -0
  209. package/dist/vendor/shared/durable-contract/routes.js +315 -0
  210. package/dist/vendor/shared/durable-contract/schema.d.ts +126 -0
  211. package/dist/vendor/shared/durable-contract/schema.js +441 -0
  212. package/dist/vendor/shared/durable-contract/seal.d.ts +54 -0
  213. package/dist/vendor/shared/durable-contract/seal.js +99 -0
  214. package/dist/vendor/shared/durable-contract/secret-scope.d.ts +28 -0
  215. package/dist/vendor/shared/durable-contract/secret-scope.js +76 -0
  216. package/dist/vendor/shared/durable-contract/session-id.d.ts +21 -0
  217. package/dist/vendor/shared/durable-contract/session-id.js +58 -0
  218. package/dist/vendor/shared/durable-contract/spec.d.ts +162 -0
  219. package/dist/vendor/shared/durable-contract/spec.js +286 -0
  220. package/dist/vendor/shared/durable-contract/states.d.ts +96 -0
  221. package/dist/vendor/shared/durable-contract/states.js +218 -0
  222. package/dist/vendor/shared/durable-contract/telemetry.d.ts +94 -0
  223. package/dist/vendor/shared/durable-contract/telemetry.js +180 -0
  224. package/dist/vendor/shared/durable-contract/test-mode.d.ts +62 -0
  225. package/dist/vendor/shared/durable-contract/test-mode.js +140 -0
  226. package/dist/vendor/shared/durable-contract/turn.d.ts +57 -0
  227. package/dist/vendor/shared/durable-contract/turn.js +81 -0
  228. package/dist/vendor/shared/durable-contract/vectors.d.ts +191 -0
  229. package/dist/vendor/shared/durable-contract/vectors.js +208 -0
  230. package/package.json +4 -3
  231. package/plugins/integrity-manifest.json +35 -0
  232. package/plugins/mnemom/hooks/hooks.json +15 -1
  233. package/plugins/mnemom/hooks/register.tsx +60 -2
  234. package/plugins/mnemom/hooks/seat-hello.mjs +116 -0
  235. package/dist/commands/agent-implicit-notice.d.ts +0 -31
  236. package/dist/commands/agent-implicit-notice.js +0 -73
@@ -0,0 +1,303 @@
1
+ /**
2
+ * `mnemom agent sessions` and `mnemom agent show <name>` with the `durable` gate on (BACKBONE C1;
3
+ * C01-R12, R24, R25, R27, R28; C11-R27): read from the server registry, never from the local
4
+ * ledger. With the gate off these are not reached and the ledger commands run exactly as today.
5
+ *
6
+ * sessions one row per session: name, user-facing state (of the EFFECTIVE state), agent,
7
+ * machine, spend (guard_state.spend_usd to cents for locked rows, `-` for tracked) and
8
+ * last activity; under a row, its fix line and the grant warning when they apply.
9
+ * show the S1 SHOW_KEYS sections in order (the --json keys are the same strings, same
10
+ * order); keys that do not apply to a tracked session print NA_TEXT (null in --json).
11
+ *
12
+ * A registry that is absent for this caller (route 404, not entitled 403) falls back to the ledger
13
+ * command (`fallback`); an unreachable one refuses with session_registry_unavailable.
14
+ */
15
+ import { APPROVALS_TEXT_M1, JOURNAL_TAIL_N, NA_TEXT, OUTAGE_TEXT_M1, SEAT_TEXT_M1, SHOW_KEYS, SHOW_KEYS_LOCKED_ONLY, STORAGE_TEXT_M1, } from "../vendor/shared/durable-contract/fixlines.js";
16
+ import { RegistryAnswerError, RegistryUnavailable, } from "../lib/durable/api.js";
17
+ import { DurableRefusal, printDurableRefusal } from "../lib/durable/refusal.js";
18
+ import { effectiveOf, grantWarning, isEnded, isLocked, lastActivity, liveRow, machineText, reasonOf, rowFixLine, spendJson, spendText, stateLabel, userState, } from "../lib/durable/view.js";
19
+ const registryDown = (detail) => new DurableRefusal({
20
+ code: "session_registry_unavailable",
21
+ message: `mnemom agent: the session registry is unreachable (${detail})`,
22
+ fix: null,
23
+ });
24
+ /** Rows from the registry, or "absent" (route 404 / not entitled 403). Down -> a refusal is thrown. */
25
+ export async function readRows(api, filter) {
26
+ try {
27
+ return await api.listSessions(filter);
28
+ }
29
+ catch (e) {
30
+ if (e instanceof RegistryUnavailable)
31
+ throw registryDown(e.message);
32
+ if (e instanceof RegistryAnswerError) {
33
+ if (e.answer.status === 403 || e.answer.status === 404)
34
+ return "absent";
35
+ throw registryDown(`HTTP ${e.answer.status}`);
36
+ }
37
+ throw e;
38
+ }
39
+ }
40
+ /** Newest first by last activity (rows without one last, input order kept among equals). */
41
+ export function newestFirst(rows) {
42
+ const t = (r) => {
43
+ const v = lastActivity(r);
44
+ const n = v ? Date.parse(v) : NaN;
45
+ return Number.isFinite(n) ? n : -Infinity;
46
+ };
47
+ return rows
48
+ .map((r, i) => ({ r, i }))
49
+ .sort((a, b) => t(b.r) - t(a.r) || a.i - b.i)
50
+ .map((x) => x.r);
51
+ }
52
+ const SESSIONS_DEFAULT_LIMIT = 20;
53
+ /** One row's --json document and its fix / grant lines (the human table prints the same). */
54
+ export function sessionJson(row, launcherKey, home) {
55
+ const nameHeld = !isEnded(row);
56
+ const machine = isLocked(row) ? machineText(row) : NA_TEXT;
57
+ return {
58
+ name: row.name ?? null,
59
+ conversation_id: row.conversation_id,
60
+ state: stateLabel(row),
61
+ internal_state: row.state,
62
+ effective_state: effectiveOf(row),
63
+ state_reason: reasonOf(row),
64
+ durable_mode: isLocked(row) ? "locked" : "tracked",
65
+ agent: row.agent_name ?? row.agent_id,
66
+ machine: machine === NA_TEXT ? null : machine,
67
+ spend: spendJson(row),
68
+ last_activity: lastActivity(row),
69
+ fix: rowFixLine(row, { launcherKey, nameHeld }, home),
70
+ grant_warning: isLocked(row) && !isEnded(row) ? grantWarning(row, home.nowMs) : null,
71
+ };
72
+ }
73
+ export async function durableSessionsCommand(opts, deps, fallback) {
74
+ let rows;
75
+ try {
76
+ rows = await readRows(deps.api, {});
77
+ }
78
+ catch (e) {
79
+ if (e instanceof DurableRefusal)
80
+ return printDurableRefusal(e, opts.json === true, deps);
81
+ throw e;
82
+ }
83
+ if (rows === "absent")
84
+ return fallback();
85
+ const shown = newestFirst(rows).slice(0, opts.all ? undefined : SESSIONS_DEFAULT_LIMIT);
86
+ const key = deps.launcherKey();
87
+ const home = await deps.home();
88
+ const docs = shown.map((r) => sessionJson(r, key, home));
89
+ if (opts.json) {
90
+ deps.out(JSON.stringify(docs, null, 2));
91
+ return 0;
92
+ }
93
+ if (docs.length === 0) {
94
+ deps.out("No sessions yet. Start one with `mnemom agent <name>`.");
95
+ return 0;
96
+ }
97
+ const header = ["NAME", "STATE", "AGENT", "MACHINE", "SPEND", "LAST ACTIVITY"];
98
+ const cells = shown.map((r, i) => {
99
+ const d = docs[i];
100
+ return [
101
+ d.name ?? d.conversation_id.slice(0, 8),
102
+ d.state,
103
+ d.agent,
104
+ d.machine ?? NA_TEXT,
105
+ spendText(r),
106
+ d.last_activity ?? NA_TEXT,
107
+ ];
108
+ });
109
+ const widths = header.map((h, c) => Math.max(h.length, ...cells.map((row) => (row[c] ?? "").length)));
110
+ const line = (vals) => vals
111
+ .map((v, c) => (c === vals.length - 1 ? v : v.padEnd(widths[c] ?? 0)))
112
+ .join(" ")
113
+ .trimEnd();
114
+ deps.out(line(header));
115
+ cells.forEach((row, i) => {
116
+ deps.out(line(row));
117
+ const d = docs[i];
118
+ if (d.fix)
119
+ deps.out(` fix: ${d.fix}`);
120
+ if (d.grant_warning)
121
+ deps.out(` ${d.grant_warning}`);
122
+ });
123
+ return 0;
124
+ }
125
+ const rec = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? v : {};
126
+ const strOrNull = (v) => (typeof v === "string" && v !== "" ? v : null);
127
+ const numOrNull = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
128
+ /** The show document: SHOW_KEYS in order; locked-only keys are null on a tracked session. */
129
+ export function showDocument(i) {
130
+ const { row } = i;
131
+ const d = i.detail ?? {};
132
+ const locked = isLocked(row);
133
+ const s = sessionJson(row, i.launcherKey, i.home);
134
+ const charter = rec(d.charter);
135
+ const guard = rec(row.guard_state ?? d.guard_state);
136
+ const goalSrc = rec(d.goal);
137
+ const journalSrc = Array.isArray(d.journal) ? d.journal : Array.isArray(d.events) ? d.events : [];
138
+ const doc = {
139
+ state: {
140
+ state: userState(row),
141
+ label: s.state,
142
+ internal_state: s.internal_state,
143
+ effective_state: s.effective_state,
144
+ state_reason: s.state_reason,
145
+ fix: s.fix,
146
+ },
147
+ machine: {
148
+ machine: s.machine,
149
+ executor_id: row.home_executor_id ?? row.home_executor?.id ?? null,
150
+ hostname: row.home_executor?.hostname ?? null,
151
+ grant_warning: s.grant_warning,
152
+ },
153
+ sharing: {
154
+ other_live_sessions: i.others === null ? null : i.others.map((o) => o.name ?? o.conversation_id),
155
+ durable_only: i.durableOnly === "protocol_error" ? null : i.durableOnly,
156
+ durable_only_error: i.durableOnly === "protocol_error" ? "protocol_error" : null,
157
+ },
158
+ goal: Object.keys(goalSrc).length === 0
159
+ ? null
160
+ : {
161
+ statement: strOrNull(goalSrc.statement),
162
+ requirements: Array.isArray(goalSrc.requirements) ? goalSrc.requirements : [],
163
+ },
164
+ rules: strOrNull(charter.profile ?? d.rules ?? d.profile),
165
+ pins: {
166
+ card_hash_prefix: strOrNull(charter.card_hash_prefix ?? d.card_hash_prefix),
167
+ policy_engine_major: numOrNull(charter.policy_engine_major ?? d.policy_engine_major),
168
+ },
169
+ limits: {
170
+ budget_usd: numOrNull(guard.budget_usd ?? row.budget_usd),
171
+ spend: s.spend,
172
+ max_turns: numOrNull(guard.max_turns ?? charter.max_turns),
173
+ max_duration_s: numOrNull(guard.max_duration_s ?? charter.max_duration_s),
174
+ },
175
+ approvals: APPROVALS_TEXT_M1,
176
+ outage: OUTAGE_TEXT_M1,
177
+ storage: STORAGE_TEXT_M1,
178
+ seat: SEAT_TEXT_M1,
179
+ journal: journalSrc.slice(-JOURNAL_TAIL_N).map((e) => {
180
+ const r = rec(e);
181
+ return { kind: strOrNull(r.kind ?? r.event), at: strOrNull(r.at ?? r.created_at) };
182
+ }),
183
+ };
184
+ if (!locked)
185
+ for (const k of SHOW_KEYS_LOCKED_ONLY)
186
+ doc[k] = null;
187
+ return doc;
188
+ }
189
+ /** The human lines of a show document, one section per SHOW_KEYS entry, in order. */
190
+ export function showLines(name, doc) {
191
+ const lines = [name];
192
+ const put = (k, v) => {
193
+ lines.push(` ${`${k}:`.padEnd(11)}${v}`);
194
+ };
195
+ for (const k of SHOW_KEYS) {
196
+ const v = doc[k];
197
+ if (v === null) {
198
+ put(k, NA_TEXT);
199
+ continue;
200
+ }
201
+ const r = rec(v);
202
+ switch (k) {
203
+ case "state": {
204
+ put(k, `${String(r.label)} (${String(r.internal_state)})`);
205
+ if (r.fix)
206
+ lines.push(` fix: ${String(r.fix)}`);
207
+ break;
208
+ }
209
+ case "machine":
210
+ put(k, String(r.machine ?? NA_TEXT));
211
+ if (r.grant_warning)
212
+ lines.push(` ${String(r.grant_warning)}`);
213
+ break;
214
+ case "sharing": {
215
+ // Only a list actually read is "no other live sessions"; an unread one is unknown.
216
+ const others = Array.isArray(r.other_live_sessions) ? r.other_live_sessions : null;
217
+ const only = r.durable_only === true
218
+ ? "; agent is durable-only"
219
+ : r.durable_only_error === "protocol_error"
220
+ ? "; durable-only unknown (protocol error: the agent record has no agent_durable_only)"
221
+ : "";
222
+ const live = others === null
223
+ ? "other live sessions unknown (session registry unavailable)"
224
+ : others.length === 0
225
+ ? "no other live sessions"
226
+ : others.join(", ");
227
+ put(k, `${live}${only}`);
228
+ break;
229
+ }
230
+ case "goal":
231
+ put(k, String(r.statement ?? NA_TEXT));
232
+ break;
233
+ case "pins":
234
+ put(k, `card ${String(r.card_hash_prefix ?? NA_TEXT)} · policy engine ${String(r.policy_engine_major ?? NA_TEXT)}`);
235
+ break;
236
+ case "limits":
237
+ put(k, `budget ${r.budget_usd ?? NA_TEXT} (spent ${String(r.spend ?? NA_TEXT)}) · max turns ${r.max_turns ?? NA_TEXT} · time ${r.max_duration_s ?? NA_TEXT}`);
238
+ break;
239
+ case "journal": {
240
+ const j = Array.isArray(v) ? v : [];
241
+ put(k, j.length === 0 ? NA_TEXT : `${j.length} recent`);
242
+ for (const e of j)
243
+ lines.push(` ${String(rec(e).at ?? "")} ${String(rec(e).kind ?? "")}`.trimEnd());
244
+ break;
245
+ }
246
+ default:
247
+ put(k, String(v));
248
+ }
249
+ }
250
+ return lines;
251
+ }
252
+ /** A 200 agent record's agent_durable_only: a boolean, or a protocol error when it is missing. */
253
+ export function durableOnlyOf(body) {
254
+ const v = rec(rec(body).agent ?? body).agent_durable_only;
255
+ return typeof v === "boolean" ? v : "protocol_error";
256
+ }
257
+ export async function durableShowCommand(ref, opts, deps, fallback) {
258
+ // No ref, or a ref that names no registry session: the ledger show (a cid prefix, or the newest).
259
+ if (ref === undefined)
260
+ return fallback();
261
+ let rows;
262
+ try {
263
+ rows = await readRows(deps.api, { name: ref });
264
+ }
265
+ catch (e) {
266
+ if (e instanceof DurableRefusal)
267
+ return printDurableRefusal(e, opts.json === true, deps);
268
+ throw e;
269
+ }
270
+ if (rows === "absent")
271
+ return fallback();
272
+ const row = liveRow(rows) ?? newestFirst(rows)[0] ?? null;
273
+ if (row === null)
274
+ return fallback();
275
+ const [detail, siblings, durableOnly] = await Promise.all([
276
+ deps.api
277
+ .getSession(row.conversation_id, row.agent_id, "goal")
278
+ .then((a) => (a.status === 200 ? rec(rec(a.body).session ?? a.body) : null))
279
+ .catch(() => null),
280
+ readRows(deps.api, { agentId: row.agent_id }).catch(() => "absent"),
281
+ deps.api
282
+ .getAgent(row.agent_id)
283
+ .then((a) => (a.status === 200 ? durableOnlyOf(a.body) : null))
284
+ .catch(() => null),
285
+ ]);
286
+ const others = siblings === "absent"
287
+ ? null
288
+ : siblings.filter((o) => o.conversation_id !== row.conversation_id && !isEnded(o));
289
+ const doc = showDocument({
290
+ row,
291
+ detail,
292
+ others,
293
+ durableOnly,
294
+ launcherKey: deps.launcherKey(),
295
+ home: await deps.home(),
296
+ });
297
+ if (opts.json)
298
+ deps.out(JSON.stringify(doc, null, 2));
299
+ else
300
+ for (const l of showLines(row.name ?? ref, doc))
301
+ deps.out(l);
302
+ return 0;
303
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * The flat durable owner verbs (BACKBONE C1; S1 owner-verbs.ts, ROUTES.own_*; C11, C16):
3
+ *
4
+ * mnemom agent pause|resume|done|kill <name> [--json]
5
+ * mnemom agent amend <name> [--budget <n>] [--turns <n>] [--json]
6
+ * mnemom agent sessions unlock <name>
7
+ * mnemom agent containment pause|resume <agent>
8
+ *
9
+ * The API decides (S1 owner-verb table, first match wins); the CLI resolves the name through the
10
+ * registry, sends the verb, and renders the answer: a refusal with its S1 fix line and machine
11
+ * code, a 200 no-op with its state, a 202 device step (user_code + verification_uri_complete,
12
+ * polled at the server interval with the 202's device_code and client_id; never a token printed
13
+ * or kept), or the applied change. `done`/`kill` on a tracked session also stop the local tracked
14
+ * claude after the 200 (final F04; SIGTERM, SIGKILL after TRACKED_STOP_GRACE_S).
15
+ *
16
+ * Every dependency is injected (`DurableVerbDeps`), so each branch is unit-tested.
17
+ */
18
+ import { type LauncherKey } from "../vendor/shared/durable-contract/fixlines.js";
19
+ import type { RouteName } from "../vendor/shared/durable-contract/routes.js";
20
+ import { type ApiAnswer, type RegistryRow } from "../lib/durable/api.js";
21
+ import { DurableRefusal } from "../lib/durable/refusal.js";
22
+ /** The slice of DurableApi the verbs use (a fake in tests). */
23
+ export interface VerbApi {
24
+ listSessions(filter: {
25
+ name?: string;
26
+ agentId?: string;
27
+ }): Promise<RegistryRow[]>;
28
+ ownerVerb(route: RouteName, cid: string, agentId: string, body?: unknown): Promise<ApiAnswer>;
29
+ containment(orgId: string, agentId: string, verb: "pause" | "resume"): Promise<ApiAnswer>;
30
+ getAgent(agentId: string): Promise<ApiAnswer>;
31
+ /** Agents by name across the caller's orgs (the org fleet; JWT). */
32
+ agentsByName(name: string): Promise<Array<{
33
+ id: string;
34
+ org_id: string;
35
+ }>>;
36
+ devicePoll(deviceCode: string, clientId: string): Promise<ApiAnswer>;
37
+ credentialType(): Promise<"jwt" | "api-key" | "none">;
38
+ }
39
+ export interface DurableVerbDeps {
40
+ api: VerbApi;
41
+ launcherKey: () => LauncherKey;
42
+ out: (line: string) => void;
43
+ err: (line: string) => void;
44
+ sleep: (ms: number) => Promise<void>;
45
+ now: () => number;
46
+ /** Stop the local tracked claude of a session (done/kill on tracked, final F04). Absent: nothing to stop. */
47
+ stopTrackedClaude?: (row: RegistryRow, graceS: number) => Promise<void>;
48
+ }
49
+ export type OwnerVerbWord = "pause" | "resume" | "done" | "kill";
50
+ /** The session a verb acts on: the non-terminal row holding the name, else the newest row. */
51
+ export declare function resolveTarget(api: VerbApi, name: string): Promise<RegistryRow>;
52
+ /**
53
+ * A refusal from an API answer: the API's message and code, its rendered fix (details.fix, as the
54
+ * API already applied the fix-line rule), else the CLI's catalogue fallback for that code.
55
+ */
56
+ export declare function refusalFromAnswer(a: ApiAnswer, name: string, launcherKey: LauncherKey, row?: RegistryRow): DurableRefusal;
57
+ /** The catalogue fix the CLI prints when the API body carries none. */
58
+ export declare function fallbackFix(code: string, details: Record<string, unknown>, name: string, launcherKey: LauncherKey, row?: RegistryRow): string | null;
59
+ /** The device-step fields of a 202 body (C16-R01). */
60
+ export interface DeviceStep {
61
+ user_code: string;
62
+ verification_uri_complete: string;
63
+ device_code: string;
64
+ interval: number;
65
+ client_id: string;
66
+ approval_id?: string;
67
+ }
68
+ export declare function deviceStepOf(body: unknown): DeviceStep | null;
69
+ /** How long the CLI keeps polling a device step before giving up (the approval TTL). */
70
+ export declare const DEVICE_STEP_POLL_MAX_MS: number;
71
+ /**
72
+ * Print the device step and poll until it resolves. 0 when applied; throws a DurableRefusal when
73
+ * denied, expired or refused. Never prints the device_code or any token.
74
+ */
75
+ export declare function runDeviceStep(step: DeviceStep, note: string | null, deps: DurableVerbDeps): Promise<number>;
76
+ /** Run one verb end to end and return its exit status (refusals rendered here). */
77
+ export declare function runVerb(verb: OwnerVerbWord | "amend" | "unlock", name: string, body: Record<string, unknown>, opts: {
78
+ json?: boolean;
79
+ }, deps: DurableVerbDeps): Promise<number>;
80
+ /**
81
+ * Confirm an applied done / kill: the answer's own `{changed: true, state}` when it names the ended
82
+ * state, else one re-read of the session by conversation id. Throws a refusal (nothing stopped
83
+ * locally) when neither shows the session ended.
84
+ */
85
+ export declare function confirmEnded(verb: "done" | "kill", row: RegistryRow, a: ApiAnswer, api: VerbApi, name: string): Promise<void>;
86
+ export declare function handleVerbError(e: unknown, json: boolean, deps: DurableVerbDeps): number;
87
+ /** Parse a positive amount (amend --budget); `integer` for a count (amend --turns). */
88
+ export declare function parsePositive(flag: string, raw: string | undefined, integer?: boolean): number | undefined;
89
+ export declare function amendCommand(name: string, opts: {
90
+ budget?: string;
91
+ turns?: string;
92
+ json?: boolean;
93
+ }, deps: DurableVerbDeps): Promise<number>;
94
+ export declare function unlockCommand(name: string, deps: DurableVerbDeps): Promise<number>;
95
+ /**
96
+ * `mnemom agent containment pause|resume <agent>` (BACKBONE C1, amendment 35; C16-R30): a thin
97
+ * verb over POST /v1/orgs/{org}/agents/{id}/pause|resume. The org comes from the agent record,
98
+ * never the active org. It needs a session login: on an API-key-only credential, or the api's
99
+ * 403 org_scope_requires_session, it exits non-zero with the containment_login line and never
100
+ * sends the API key or prints the raw 403.
101
+ */
102
+ export declare function containmentCommand(verb: "pause" | "resume", agentRef: string, deps: DurableVerbDeps): Promise<number>;
103
+ /** An agent id as the CLI recognises one (lib/api.ts resolveAgentId). */
104
+ export declare const AGENT_ID_RE: RegExp;
105
+ /**
106
+ * The containment target and its org, from the agent record (never the active org): an id is read
107
+ * with GET /v1/agents/{id}; a name is looked up in the caller's org fleets and must match exactly one.
108
+ */
109
+ export declare function resolveContainmentAgent(agentRef: string, deps: DurableVerbDeps, launcherKey: LauncherKey): Promise<{
110
+ orgId: string;
111
+ agentId: string;
112
+ }>;
113
+ /** The thin verbs (pause/resume/done/kill) with no body. */
114
+ export declare function ownerVerbCommand(verb: OwnerVerbWord, name: string, opts: {
115
+ json?: boolean;
116
+ }, deps: DurableVerbDeps): Promise<number>;