@hybridaione/hybridclaw 0.2.2 → 0.2.6

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 (277) hide show
  1. package/.github/workflows/ci.yml +70 -0
  2. package/.husky/pre-commit +1 -0
  3. package/CHANGELOG.md +85 -0
  4. package/CONTRIBUTING.md +33 -0
  5. package/README.md +41 -16
  6. package/SECURITY.md +17 -0
  7. package/biome.json +35 -0
  8. package/config.example.json +71 -8
  9. package/container/package-lock.json +2 -2
  10. package/container/package.json +1 -1
  11. package/container/src/approval-policy.ts +1303 -0
  12. package/container/src/browser-tools.ts +431 -136
  13. package/container/src/extensions.ts +36 -12
  14. package/container/src/hybridai-client.ts +34 -13
  15. package/container/src/index.ts +451 -109
  16. package/container/src/ipc.ts +5 -3
  17. package/container/src/token-usage.ts +20 -10
  18. package/container/src/tools.ts +599 -225
  19. package/container/src/types.ts +32 -2
  20. package/container/src/web-fetch.ts +89 -32
  21. package/dist/agent.d.ts.map +1 -1
  22. package/dist/agent.js +10 -2
  23. package/dist/agent.js.map +1 -1
  24. package/dist/audit-cli.d.ts.map +1 -1
  25. package/dist/audit-cli.js +4 -2
  26. package/dist/audit-cli.js.map +1 -1
  27. package/dist/audit-events.d.ts.map +1 -1
  28. package/dist/audit-events.js +53 -3
  29. package/dist/audit-events.js.map +1 -1
  30. package/dist/audit-trail.d.ts.map +1 -1
  31. package/dist/audit-trail.js +17 -8
  32. package/dist/audit-trail.js.map +1 -1
  33. package/dist/channels/discord/attachments.d.ts.map +1 -1
  34. package/dist/channels/discord/attachments.js +14 -7
  35. package/dist/channels/discord/attachments.js.map +1 -1
  36. package/dist/channels/discord/debounce.d.ts +9 -0
  37. package/dist/channels/discord/debounce.d.ts.map +1 -0
  38. package/dist/channels/discord/debounce.js +20 -0
  39. package/dist/channels/discord/debounce.js.map +1 -0
  40. package/dist/channels/discord/delivery.d.ts +4 -1
  41. package/dist/channels/discord/delivery.d.ts.map +1 -1
  42. package/dist/channels/discord/delivery.js +19 -3
  43. package/dist/channels/discord/delivery.js.map +1 -1
  44. package/dist/channels/discord/human-delay.d.ts +16 -0
  45. package/dist/channels/discord/human-delay.d.ts.map +1 -0
  46. package/dist/channels/discord/human-delay.js +29 -0
  47. package/dist/channels/discord/human-delay.js.map +1 -0
  48. package/dist/channels/discord/inbound.d.ts +4 -0
  49. package/dist/channels/discord/inbound.d.ts.map +1 -1
  50. package/dist/channels/discord/inbound.js +45 -4
  51. package/dist/channels/discord/inbound.js.map +1 -1
  52. package/dist/channels/discord/mentions.d.ts.map +1 -1
  53. package/dist/channels/discord/mentions.js +16 -4
  54. package/dist/channels/discord/mentions.js.map +1 -1
  55. package/dist/channels/discord/presence.d.ts +33 -0
  56. package/dist/channels/discord/presence.d.ts.map +1 -0
  57. package/dist/channels/discord/presence.js +111 -0
  58. package/dist/channels/discord/presence.js.map +1 -0
  59. package/dist/channels/discord/rate-limiter.d.ts +14 -0
  60. package/dist/channels/discord/rate-limiter.d.ts.map +1 -0
  61. package/dist/channels/discord/rate-limiter.js +49 -0
  62. package/dist/channels/discord/rate-limiter.js.map +1 -0
  63. package/dist/channels/discord/reactions.d.ts +38 -0
  64. package/dist/channels/discord/reactions.d.ts.map +1 -0
  65. package/dist/channels/discord/reactions.js +151 -0
  66. package/dist/channels/discord/reactions.js.map +1 -0
  67. package/dist/channels/discord/runtime.d.ts +6 -3
  68. package/dist/channels/discord/runtime.d.ts.map +1 -1
  69. package/dist/channels/discord/runtime.js +621 -125
  70. package/dist/channels/discord/runtime.js.map +1 -1
  71. package/dist/channels/discord/stream.d.ts +4 -1
  72. package/dist/channels/discord/stream.d.ts.map +1 -1
  73. package/dist/channels/discord/stream.js +16 -8
  74. package/dist/channels/discord/stream.js.map +1 -1
  75. package/dist/channels/discord/tool-actions.d.ts.map +1 -1
  76. package/dist/channels/discord/tool-actions.js +24 -12
  77. package/dist/channels/discord/tool-actions.js.map +1 -1
  78. package/dist/channels/discord/typing.d.ts +15 -0
  79. package/dist/channels/discord/typing.d.ts.map +1 -0
  80. package/dist/channels/discord/typing.js +106 -0
  81. package/dist/channels/discord/typing.js.map +1 -0
  82. package/dist/chunk.d.ts.map +1 -1
  83. package/dist/chunk.js +4 -2
  84. package/dist/chunk.js.map +1 -1
  85. package/dist/cli.js +47 -22
  86. package/dist/cli.js.map +1 -1
  87. package/dist/config.d.ts +19 -0
  88. package/dist/config.d.ts.map +1 -1
  89. package/dist/config.js +103 -18
  90. package/dist/config.js.map +1 -1
  91. package/dist/container-runner.d.ts.map +1 -1
  92. package/dist/container-runner.js +58 -26
  93. package/dist/container-runner.js.map +1 -1
  94. package/dist/container-setup.d.ts.map +1 -1
  95. package/dist/container-setup.js +10 -9
  96. package/dist/container-setup.js.map +1 -1
  97. package/dist/conversation.d.ts +2 -2
  98. package/dist/conversation.d.ts.map +1 -1
  99. package/dist/conversation.js +1 -1
  100. package/dist/conversation.js.map +1 -1
  101. package/dist/db.d.ts +118 -2
  102. package/dist/db.d.ts.map +1 -1
  103. package/dist/db.js +1568 -50
  104. package/dist/db.js.map +1 -1
  105. package/dist/delegation-manager.d.ts.map +1 -1
  106. package/dist/delegation-manager.js +3 -2
  107. package/dist/delegation-manager.js.map +1 -1
  108. package/dist/gateway-client.d.ts +2 -2
  109. package/dist/gateway-client.d.ts.map +1 -1
  110. package/dist/gateway-client.js +10 -4
  111. package/dist/gateway-client.js.map +1 -1
  112. package/dist/gateway-service.d.ts +3 -3
  113. package/dist/gateway-service.d.ts.map +1 -1
  114. package/dist/gateway-service.js +563 -73
  115. package/dist/gateway-service.js.map +1 -1
  116. package/dist/gateway-types.d.ts +24 -0
  117. package/dist/gateway-types.d.ts.map +1 -1
  118. package/dist/gateway-types.js.map +1 -1
  119. package/dist/gateway.js +179 -24
  120. package/dist/gateway.js.map +1 -1
  121. package/dist/health.d.ts.map +1 -1
  122. package/dist/health.js +20 -10
  123. package/dist/health.js.map +1 -1
  124. package/dist/heartbeat.d.ts +4 -0
  125. package/dist/heartbeat.d.ts.map +1 -1
  126. package/dist/heartbeat.js +48 -20
  127. package/dist/heartbeat.js.map +1 -1
  128. package/dist/hybridai-bots.d.ts.map +1 -1
  129. package/dist/hybridai-bots.js +4 -2
  130. package/dist/hybridai-bots.js.map +1 -1
  131. package/dist/instruction-approval-audit.d.ts.map +1 -1
  132. package/dist/instruction-approval-audit.js.map +1 -1
  133. package/dist/instruction-integrity.d.ts.map +1 -1
  134. package/dist/instruction-integrity.js +8 -2
  135. package/dist/instruction-integrity.js.map +1 -1
  136. package/dist/ipc.d.ts.map +1 -1
  137. package/dist/ipc.js +6 -1
  138. package/dist/ipc.js.map +1 -1
  139. package/dist/logger.js.map +1 -1
  140. package/dist/memory-consolidation.d.ts +17 -0
  141. package/dist/memory-consolidation.d.ts.map +1 -0
  142. package/dist/memory-consolidation.js +25 -0
  143. package/dist/memory-consolidation.js.map +1 -0
  144. package/dist/memory-service.d.ts +200 -0
  145. package/dist/memory-service.d.ts.map +1 -0
  146. package/dist/memory-service.js +294 -0
  147. package/dist/memory-service.js.map +1 -0
  148. package/dist/mount-security.d.ts.map +1 -1
  149. package/dist/mount-security.js +31 -7
  150. package/dist/mount-security.js.map +1 -1
  151. package/dist/observability-ingest.d.ts.map +1 -1
  152. package/dist/observability-ingest.js +32 -11
  153. package/dist/observability-ingest.js.map +1 -1
  154. package/dist/onboarding.d.ts.map +1 -1
  155. package/dist/onboarding.js +32 -9
  156. package/dist/onboarding.js.map +1 -1
  157. package/dist/proactive-policy.d.ts.map +1 -1
  158. package/dist/proactive-policy.js +2 -1
  159. package/dist/proactive-policy.js.map +1 -1
  160. package/dist/prompt-hooks.d.ts.map +1 -1
  161. package/dist/prompt-hooks.js +9 -7
  162. package/dist/prompt-hooks.js.map +1 -1
  163. package/dist/runtime-config.d.ts +98 -1
  164. package/dist/runtime-config.d.ts.map +1 -1
  165. package/dist/runtime-config.js +477 -23
  166. package/dist/runtime-config.js.map +1 -1
  167. package/dist/scheduled-task-runner.d.ts +1 -0
  168. package/dist/scheduled-task-runner.d.ts.map +1 -1
  169. package/dist/scheduled-task-runner.js +29 -10
  170. package/dist/scheduled-task-runner.js.map +1 -1
  171. package/dist/scheduler.d.ts +43 -4
  172. package/dist/scheduler.d.ts.map +1 -1
  173. package/dist/scheduler.js +530 -56
  174. package/dist/scheduler.js.map +1 -1
  175. package/dist/session-export.d.ts +26 -0
  176. package/dist/session-export.d.ts.map +1 -0
  177. package/dist/session-export.js +149 -0
  178. package/dist/session-export.js.map +1 -0
  179. package/dist/session-maintenance.d.ts.map +1 -1
  180. package/dist/session-maintenance.js +75 -13
  181. package/dist/session-maintenance.js.map +1 -1
  182. package/dist/session-transcripts.d.ts.map +1 -1
  183. package/dist/session-transcripts.js.map +1 -1
  184. package/dist/side-effects.d.ts.map +1 -1
  185. package/dist/side-effects.js +14 -2
  186. package/dist/side-effects.js.map +1 -1
  187. package/dist/skills-guard.d.ts.map +1 -1
  188. package/dist/skills-guard.js +893 -130
  189. package/dist/skills-guard.js.map +1 -1
  190. package/dist/skills.d.ts +5 -0
  191. package/dist/skills.d.ts.map +1 -1
  192. package/dist/skills.js +29 -15
  193. package/dist/skills.js.map +1 -1
  194. package/dist/token-efficiency.d.ts.map +1 -1
  195. package/dist/token-efficiency.js.map +1 -1
  196. package/dist/tui.js +92 -11
  197. package/dist/tui.js.map +1 -1
  198. package/dist/types.d.ts +146 -0
  199. package/dist/types.d.ts.map +1 -1
  200. package/dist/types.js +24 -1
  201. package/dist/types.js.map +1 -1
  202. package/dist/update.d.ts.map +1 -1
  203. package/dist/update.js +42 -14
  204. package/dist/update.js.map +1 -1
  205. package/dist/workspace.d.ts.map +1 -1
  206. package/dist/workspace.js +49 -9
  207. package/dist/workspace.js.map +1 -1
  208. package/docs/chat.html +9 -3
  209. package/docs/index.html +37 -13
  210. package/package.json +8 -2
  211. package/src/agent.ts +16 -3
  212. package/src/audit-cli.ts +44 -16
  213. package/src/audit-events.ts +69 -5
  214. package/src/audit-trail.ts +41 -15
  215. package/src/channels/discord/attachments.ts +81 -27
  216. package/src/channels/discord/debounce.ts +25 -0
  217. package/src/channels/discord/delivery.ts +57 -13
  218. package/src/channels/discord/human-delay.ts +48 -0
  219. package/src/channels/discord/inbound.ts +66 -7
  220. package/src/channels/discord/mentions.ts +42 -18
  221. package/src/channels/discord/presence.ts +148 -0
  222. package/src/channels/discord/rate-limiter.ts +58 -0
  223. package/src/channels/discord/reactions.ts +211 -0
  224. package/src/channels/discord/runtime.ts +1048 -182
  225. package/src/channels/discord/stream.ts +73 -27
  226. package/src/channels/discord/tool-actions.ts +78 -37
  227. package/src/channels/discord/typing.ts +140 -0
  228. package/src/chunk.ts +12 -4
  229. package/src/cli.ts +141 -56
  230. package/src/config.ts +192 -34
  231. package/src/container-runner.ts +132 -42
  232. package/src/container-setup.ts +57 -22
  233. package/src/conversation.ts +9 -7
  234. package/src/db.ts +2217 -84
  235. package/src/delegation-manager.ts +6 -2
  236. package/src/gateway-client.ts +41 -17
  237. package/src/gateway-service.ts +1019 -201
  238. package/src/gateway-types.ts +33 -0
  239. package/src/gateway.ts +321 -48
  240. package/src/health.ts +66 -26
  241. package/src/heartbeat.ts +84 -22
  242. package/src/hybridai-bots.ts +14 -5
  243. package/src/instruction-approval-audit.ts +4 -1
  244. package/src/instruction-integrity.ts +30 -9
  245. package/src/ipc.ts +23 -5
  246. package/src/logger.ts +4 -1
  247. package/src/memory-consolidation.ts +41 -0
  248. package/src/memory-service.ts +606 -0
  249. package/src/mount-security.ts +58 -13
  250. package/src/observability-ingest.ts +134 -35
  251. package/src/onboarding.ts +126 -35
  252. package/src/proactive-policy.ts +3 -1
  253. package/src/prompt-hooks.ts +40 -17
  254. package/src/runtime-config.ts +1114 -99
  255. package/src/scheduled-task-runner.ts +63 -11
  256. package/src/scheduler.ts +683 -60
  257. package/src/session-export.ts +196 -0
  258. package/src/session-maintenance.ts +125 -22
  259. package/src/session-transcripts.ts +12 -3
  260. package/src/side-effects.ts +28 -5
  261. package/src/skills-guard.ts +1067 -219
  262. package/src/skills.ts +163 -65
  263. package/src/token-efficiency.ts +31 -9
  264. package/src/tui.ts +166 -25
  265. package/src/types.ts +195 -2
  266. package/src/update.ts +79 -23
  267. package/src/workspace.ts +63 -11
  268. package/tests/approval-policy.test.ts +224 -0
  269. package/tests/discord.basic.test.ts +82 -2
  270. package/tests/discord.human-presence.test.ts +85 -0
  271. package/tests/gateway-service.media-routing.test.ts +8 -2
  272. package/tests/memory-service.test.ts +1114 -0
  273. package/tests/token-efficiency.basic.test.ts +8 -2
  274. package/vitest.e2e.config.ts +3 -1
  275. package/vitest.integration.config.ts +3 -1
  276. package/vitest.live.config.ts +3 -1
  277. package/vitest.unit.config.ts +9 -0
package/src/tui.ts CHANGED
@@ -2,17 +2,23 @@
2
2
  * HybridClaw TUI — thin client for the gateway API.
3
3
  * Usage: npm run tui
4
4
  */
5
- import readline from 'readline';
5
+ import readline from 'node:readline';
6
6
 
7
- import { APP_VERSION, GATEWAY_BASE_URL, HYBRIDAI_BASE_URL, HYBRIDAI_CHATBOT_ID, HYBRIDAI_MODEL } from './config.js';
8
7
  import {
8
+ APP_VERSION,
9
+ GATEWAY_BASE_URL,
10
+ HYBRIDAI_BASE_URL,
11
+ HYBRIDAI_CHATBOT_ID,
12
+ HYBRIDAI_MODEL,
13
+ } from './config.js';
14
+ import {
15
+ type GatewayChatResult,
16
+ type GatewayCommandResult,
17
+ gatewayChat,
9
18
  gatewayChatStream,
10
19
  gatewayCommand,
11
- gatewayChat,
12
20
  gatewayStatus,
13
21
  renderGatewayCommand,
14
- type GatewayChatResult,
15
- type GatewayCommandResult,
16
22
  } from './gateway-client.js';
17
23
  import { logger } from './logger.js';
18
24
 
@@ -53,7 +59,10 @@ function inferThemeFromColorFgBg(): TuiTheme | null {
53
59
  const raw = process.env.COLORFGBG;
54
60
  if (!raw) return null;
55
61
 
56
- const parts = raw.split(/[;:]/).map((part) => part.trim()).filter(Boolean);
62
+ const parts = raw
63
+ .split(/[;:]/)
64
+ .map((part) => part.trim())
65
+ .filter(Boolean);
57
66
  if (parts.length === 0) return null;
58
67
 
59
68
  const bg = Number.parseInt(parts[parts.length - 1], 10);
@@ -64,7 +73,14 @@ function inferThemeFromColorFgBg(): TuiTheme | null {
64
73
  }
65
74
 
66
75
  function resolveTuiTheme(): TuiTheme {
67
- const override = (process.env.HYBRIDCLAW_THEME || process.env.HYBRIDCLAW_TUI_THEME || process.env.TUI_THEME || '').trim().toLowerCase();
76
+ const override = (
77
+ process.env.HYBRIDCLAW_THEME ||
78
+ process.env.HYBRIDCLAW_TUI_THEME ||
79
+ process.env.TUI_THEME ||
80
+ ''
81
+ )
82
+ .trim()
83
+ .toLowerCase();
68
84
  if (override === 'light' || override === 'dark') return override;
69
85
  return inferThemeFromColorFgBg() || 'dark';
70
86
  }
@@ -87,6 +103,84 @@ const TOOL_PREVIEW_MAX_CHARS = 140;
87
103
 
88
104
  let activeRunAbortController: AbortController | null = null;
89
105
 
106
+ function findPendingApprovalRequestId(
107
+ result: GatewayChatResult,
108
+ ): string | null {
109
+ const executions = result.toolExecutions || [];
110
+ for (let i = executions.length - 1; i >= 0; i -= 1) {
111
+ const execution = executions[i];
112
+ if (execution.approvalDecision !== 'required') continue;
113
+ if (!execution.approvalRequestId) continue;
114
+ return execution.approvalRequestId;
115
+ }
116
+ return null;
117
+ }
118
+
119
+ function mapApprovalSelectionToCommand(
120
+ selection: string,
121
+ requestId: string,
122
+ ): string | null {
123
+ const normalized = selection.trim().toLowerCase().replace(/\s+/g, ' ');
124
+ if (!normalized) return null;
125
+
126
+ if (
127
+ normalized === '1' ||
128
+ normalized === 'yes' ||
129
+ normalized === 'y' ||
130
+ normalized === 'once'
131
+ ) {
132
+ return `yes ${requestId}`;
133
+ }
134
+ if (
135
+ normalized === '2' ||
136
+ normalized === 'session' ||
137
+ normalized === 'yes for session' ||
138
+ normalized === 'for session'
139
+ ) {
140
+ return `yes ${requestId} for session`;
141
+ }
142
+ if (
143
+ normalized === '3' ||
144
+ normalized === 'agent' ||
145
+ normalized === 'yes for agent' ||
146
+ normalized === 'for agent'
147
+ ) {
148
+ return `yes ${requestId} for agent`;
149
+ }
150
+ if (
151
+ normalized === '4' ||
152
+ normalized === 'no' ||
153
+ normalized === 'n' ||
154
+ normalized === 'skip'
155
+ ) {
156
+ return `skip ${requestId}`;
157
+ }
158
+ return null;
159
+ }
160
+
161
+ async function promptApprovalSelection(
162
+ rl: readline.Interface,
163
+ requestId: string,
164
+ ): Promise<string | null> {
165
+ console.log(
166
+ ` ${BOLD}${GOLD}Approval options${RESET} ${MUTED}(request ${requestId})${RESET}`,
167
+ );
168
+ console.log(` ${TEAL}1${RESET} yes (once)`);
169
+ console.log(` ${TEAL}2${RESET} yes for session`);
170
+ console.log(` ${TEAL}3${RESET} yes for agent`);
171
+ console.log(` ${TEAL}4${RESET} no / skip`);
172
+ const answer = await new Promise<string>((resolve) => {
173
+ rl.question(` ${MUTED}Select 1-4 (Enter to skip):${RESET} `, resolve);
174
+ });
175
+ const command = mapApprovalSelectionToCommand(answer, requestId);
176
+ if (answer.trim() && !command) {
177
+ printInfo(
178
+ `Unrecognized selection "${answer.trim()}". You can reply manually with yes/skip and the request id.`,
179
+ );
180
+ }
181
+ return command;
182
+ }
183
+
90
184
  function printBanner(): void {
91
185
  const T = TEAL;
92
186
  const N = NAVY;
@@ -124,12 +218,18 @@ function printBanner(): void {
124
218
  console.log();
125
219
  for (const line of logo) console.log(line);
126
220
  console.log();
127
- console.log(` \u{1F99E} ${BOLD}${TEAL}H y b r i d ${GOLD}C l a w${RESET} ${MUTED}v${APP_VERSION}${RESET}`);
221
+ console.log(
222
+ ` \u{1F99E} ${BOLD}${TEAL}H y b r i d ${GOLD}C l a w${RESET} ${MUTED}v${APP_VERSION}${RESET}`,
223
+ );
128
224
  console.log(`${MUTED} Powered by HybridAI${RESET}`);
129
225
  console.log();
130
- console.log(` ${MUTED}Model:${RESET} ${TEAL}${HYBRIDAI_MODEL}${RESET}${MUTED} | Bot:${RESET} ${GOLD}${HYBRIDAI_CHATBOT_ID || 'unset'}${RESET}`);
226
+ console.log(
227
+ ` ${MUTED}Model:${RESET} ${TEAL}${HYBRIDAI_MODEL}${RESET}${MUTED} | Bot:${RESET} ${GOLD}${HYBRIDAI_CHATBOT_ID || 'unset'}${RESET}`,
228
+ );
131
229
  console.log(` ${MUTED}Gateway:${RESET} ${TEAL}${GATEWAY_BASE_URL}${RESET}`);
132
- console.log(` ${MUTED}HybridAI:${RESET} ${TEAL}${HYBRIDAI_BASE_URL}${RESET}`);
230
+ console.log(
231
+ ` ${MUTED}HybridAI:${RESET} ${TEAL}${HYBRIDAI_BASE_URL}${RESET}`,
232
+ );
133
233
  console.log();
134
234
  }
135
235
 
@@ -162,12 +262,18 @@ function printError(text: string): void {
162
262
  }
163
263
 
164
264
  function printInfo(text: string): void {
165
- console.log(`\n ${GOLD}${text}${RESET}\n`);
265
+ console.log();
266
+ for (const line of text.split('\n')) {
267
+ console.log(` ${GOLD}${line}${RESET}`);
268
+ }
269
+ console.log();
166
270
  }
167
271
 
168
272
  function printToolUsage(tools: string[]): void {
169
273
  if (tools.length === 0) return;
170
- console.log(` ${MUTED}${JELLYFISH} tools:${RESET} ${GREEN}${tools.join(', ')}${RESET}`);
274
+ console.log(
275
+ ` ${MUTED}${JELLYFISH} tools:${RESET} ${GREEN}${tools.join(', ')}${RESET}`,
276
+ );
171
277
  }
172
278
 
173
279
  function printGatewayCommandResult(result: GatewayCommandResult): void {
@@ -190,7 +296,7 @@ function spinner(): {
190
296
  const clearLine = () => process.stdout.write('\r\x1b[2K');
191
297
  const render = () => {
192
298
  clearLine();
193
- process.stdout.write(`\r${TEAL}thinking${dots[i % dots.length]}${RESET}`);
299
+ process.stdout.write(`\r${TEAL}thinking${dots[i % dots.length]}${RESET}`);
194
300
  i++;
195
301
  };
196
302
  const interval = setInterval(render, 350);
@@ -203,7 +309,9 @@ function spinner(): {
203
309
  addTool: (toolName: string, preview?: string) => {
204
310
  clearLine();
205
311
  const previewText = preview ? ` ${MUTED}${preview}${RESET}` : '';
206
- process.stdout.write(` ${JELLYFISH} ${TEAL}${toolName}${RESET}${previewText}\n`);
312
+ process.stdout.write(
313
+ ` ${JELLYFISH} ${TEAL}${toolName}${RESET}${previewText}\n`,
314
+ );
207
315
  transientToolLines++;
208
316
  render();
209
317
  },
@@ -234,7 +342,10 @@ async function runGatewayCommand(args: string[]): Promise<void> {
234
342
  }
235
343
  }
236
344
 
237
- async function handleSlashCommand(input: string, rl: readline.Interface): Promise<boolean> {
345
+ async function handleSlashCommand(
346
+ input: string,
347
+ rl: readline.Interface,
348
+ ): Promise<boolean> {
238
349
  const parts = input.slice(1).trim().split(/\s+/).filter(Boolean);
239
350
  const cmd = (parts[0] || '').toLowerCase();
240
351
 
@@ -248,6 +359,7 @@ async function handleSlashCommand(input: string, rl: readline.Interface): Promis
248
359
  console.log(`\n ${GOLD}Goodbye!${RESET}\n`);
249
360
  rl.close();
250
361
  process.exit(0);
362
+ return true;
251
363
  case 'bots':
252
364
  await runGatewayCommand(['bot', 'list']);
253
365
  return true;
@@ -282,7 +394,10 @@ async function handleSlashCommand(input: string, rl: readline.Interface): Promis
282
394
  return true;
283
395
  case 'stop':
284
396
  case 'abort':
285
- if (activeRunAbortController && !activeRunAbortController.signal.aborted) {
397
+ if (
398
+ activeRunAbortController &&
399
+ !activeRunAbortController.signal.aborted
400
+ ) {
286
401
  activeRunAbortController.abort();
287
402
  printInfo('Stopping current request...');
288
403
  } else {
@@ -294,7 +409,10 @@ async function handleSlashCommand(input: string, rl: readline.Interface): Promis
294
409
  }
295
410
  }
296
411
 
297
- async function processMessage(content: string): Promise<void> {
412
+ async function processMessage(
413
+ content: string,
414
+ rl: readline.Interface,
415
+ ): Promise<void> {
298
416
  const s = spinner();
299
417
  const abortController = new AbortController();
300
418
  activeRunAbortController = abortController;
@@ -325,11 +443,17 @@ async function processMessage(content: string): Promise<void> {
325
443
  stream: true,
326
444
  },
327
445
  (event) => {
328
- if (event.type !== 'tool' || event.phase !== 'start' || !event.toolName) return;
446
+ if (
447
+ event.type !== 'tool' ||
448
+ event.phase !== 'start' ||
449
+ !event.toolName
450
+ )
451
+ return;
329
452
  const preview = (event.preview || '').replace(/\s+/g, ' ').trim();
330
- const previewText = preview.length > TOOL_PREVIEW_MAX_CHARS
331
- ? `${preview.slice(0, TOOL_PREVIEW_MAX_CHARS - 1)}…`
332
- : preview;
453
+ const previewText =
454
+ preview.length > TOOL_PREVIEW_MAX_CHARS
455
+ ? `${preview.slice(0, TOOL_PREVIEW_MAX_CHARS - 1)}…`
456
+ : preview;
333
457
  toolNames.add(event.toolName);
334
458
  s.addTool(event.toolName, previewText || undefined);
335
459
  },
@@ -361,7 +485,10 @@ async function processMessage(content: string): Promise<void> {
361
485
  printToolUsage(Array.from(toolNames));
362
486
  }
363
487
 
364
- if ((result.error || '').includes('aborted') || (result.error || '').includes('Interrupted')) {
488
+ if (
489
+ (result.error || '').includes('aborted') ||
490
+ (result.error || '').includes('Interrupted')
491
+ ) {
365
492
  return;
366
493
  }
367
494
 
@@ -371,6 +498,16 @@ async function processMessage(content: string): Promise<void> {
371
498
  }
372
499
 
373
500
  printResponse(result.result || 'No response.');
501
+ const pendingApprovalId = findPendingApprovalRequestId(result);
502
+ if (pendingApprovalId) {
503
+ const approvalCommand = await promptApprovalSelection(
504
+ rl,
505
+ pendingApprovalId,
506
+ );
507
+ if (approvalCommand) {
508
+ await processMessage(approvalCommand, rl);
509
+ }
510
+ }
374
511
  } catch (err) {
375
512
  s.stop();
376
513
  if (abortController.signal.aborted) return;
@@ -399,7 +536,8 @@ async function main(): Promise<void> {
399
536
  if (process.stdin.isTTY) process.stdin.setRawMode(true);
400
537
  process.stdin.on('keypress', (_str, key) => {
401
538
  if (key?.name !== 'escape') return;
402
- if (!activeRunAbortController || activeRunAbortController.signal.aborted) return;
539
+ if (!activeRunAbortController || activeRunAbortController.signal.aborted)
540
+ return;
403
541
  activeRunAbortController.abort();
404
542
  });
405
543
 
@@ -423,7 +561,7 @@ async function main(): Promise<void> {
423
561
  return;
424
562
  }
425
563
  }
426
- await processMessage(input);
564
+ await processMessage(input, rl);
427
565
  rl.prompt();
428
566
  })
429
567
  .catch((err) => {
@@ -446,7 +584,10 @@ async function main(): Promise<void> {
446
584
  rl.on('line', (line) => {
447
585
  pendingInputLines.push(line);
448
586
  if (pendingInputTimer) clearTimeout(pendingInputTimer);
449
- pendingInputTimer = setTimeout(flushPendingInput, TUI_MULTILINE_PASTE_DEBOUNCE_MS);
587
+ pendingInputTimer = setTimeout(
588
+ flushPendingInput,
589
+ TUI_MULTILINE_PASTE_DEBOUNCE_MS,
590
+ );
450
591
  });
451
592
 
452
593
  rl.on('close', () => {
package/src/types.ts CHANGED
@@ -59,7 +59,16 @@ export interface ContainerInput {
59
59
  gatewayApiToken?: string;
60
60
  model: string;
61
61
  channelId: string;
62
- scheduledTasks?: { id: number; cronExpr: string; runAt: string | null; everyMs: number | null; prompt: string; enabled: number; lastRun: string | null; createdAt: string }[];
62
+ scheduledTasks?: {
63
+ id: number;
64
+ cronExpr: string;
65
+ runAt: string | null;
66
+ everyMs: number | null;
67
+ prompt: string;
68
+ enabled: number;
69
+ lastRun: string | null;
70
+ createdAt: string;
71
+ }[];
63
72
  allowedTools?: string[];
64
73
  blockedTools?: string[];
65
74
  media?: MediaContextItem[];
@@ -73,6 +82,20 @@ export interface ToolExecution {
73
82
  isError?: boolean;
74
83
  blocked?: boolean;
75
84
  blockedReason?: string;
85
+ approvalTier?: 'green' | 'yellow' | 'red';
86
+ approvalBaseTier?: 'green' | 'yellow' | 'red';
87
+ approvalDecision?:
88
+ | 'auto'
89
+ | 'implicit'
90
+ | 'approved_once'
91
+ | 'approved_session'
92
+ | 'approved_agent'
93
+ | 'promoted'
94
+ | 'required'
95
+ | 'denied';
96
+ approvalActionKey?: string;
97
+ approvalReason?: string;
98
+ approvalRequestId?: string;
76
99
  }
77
100
 
78
101
  export interface ToolProgressEvent {
@@ -108,6 +131,7 @@ export interface ContainerOutput {
108
131
  toolExecutions?: ToolExecution[];
109
132
  tokenUsage?: TokenUsageStats;
110
133
  error?: string;
134
+ effectiveUserPrompt?: string;
111
135
  sideEffects?: {
112
136
  schedules?: ScheduleSideEffect[];
113
137
  delegations?: DelegationSideEffect[];
@@ -115,7 +139,13 @@ export interface ContainerOutput {
115
139
  }
116
140
 
117
141
  export type ScheduleSideEffect =
118
- | { action: 'add'; cronExpr?: string; runAt?: string; everyMs?: number; prompt: string }
142
+ | {
143
+ action: 'add';
144
+ cronExpr?: string;
145
+ runAt?: string;
146
+ everyMs?: number;
147
+ prompt: string;
148
+ }
119
149
  | { action: 'remove'; taskId: number };
120
150
 
121
151
  export interface DelegationTaskSpec {
@@ -162,6 +192,167 @@ export interface StoredMessage {
162
192
  created_at: string;
163
193
  }
164
194
 
195
+ export interface SemanticMemoryEntry {
196
+ id: number;
197
+ session_id: string;
198
+ role: string;
199
+ source: string;
200
+ scope: string;
201
+ metadata: Record<string, unknown>;
202
+ content: string;
203
+ confidence: number;
204
+ embedding: number[] | null;
205
+ source_message_id: number | null;
206
+ created_at: string;
207
+ accessed_at: string;
208
+ access_count: number;
209
+ }
210
+
211
+ export interface StructuredMemoryEntry {
212
+ agent_id: string;
213
+ key: string;
214
+ value: unknown;
215
+ version: number;
216
+ updated_at: string;
217
+ }
218
+
219
+ export enum KnowledgeEntityType {
220
+ Person = 'person',
221
+ Organization = 'organization',
222
+ Project = 'project',
223
+ Concept = 'concept',
224
+ Event = 'event',
225
+ Location = 'location',
226
+ Document = 'document',
227
+ Tool = 'tool',
228
+ }
229
+
230
+ export interface KnowledgeEntityCustomType {
231
+ custom: string;
232
+ }
233
+
234
+ export type KnowledgeEntityTypeValue =
235
+ | KnowledgeEntityType
236
+ | KnowledgeEntityCustomType;
237
+
238
+ export enum KnowledgeRelationType {
239
+ WorksAt = 'works_at',
240
+ KnowsAbout = 'knows_about',
241
+ RelatedTo = 'related_to',
242
+ DependsOn = 'depends_on',
243
+ OwnedBy = 'owned_by',
244
+ CreatedBy = 'created_by',
245
+ LocatedIn = 'located_in',
246
+ PartOf = 'part_of',
247
+ Uses = 'uses',
248
+ Produces = 'produces',
249
+ }
250
+
251
+ export interface KnowledgeRelationCustomType {
252
+ custom: string;
253
+ }
254
+
255
+ export type KnowledgeRelationTypeValue =
256
+ | KnowledgeRelationType
257
+ | KnowledgeRelationCustomType;
258
+
259
+ export interface KnowledgeEntity {
260
+ id: string;
261
+ entity_type: KnowledgeEntityTypeValue;
262
+ name: string;
263
+ properties: Record<string, unknown>;
264
+ created_at: string;
265
+ updated_at: string;
266
+ }
267
+
268
+ export interface KnowledgeRelation {
269
+ source: string;
270
+ relation: KnowledgeRelationTypeValue;
271
+ target: string;
272
+ properties: Record<string, unknown>;
273
+ confidence: number;
274
+ created_at: string;
275
+ }
276
+
277
+ export interface KnowledgeGraphPattern {
278
+ source?: string;
279
+ relation?: KnowledgeRelationTypeValue;
280
+ target?: string;
281
+ max_depth?: number;
282
+ }
283
+
284
+ export interface KnowledgeGraphMatch {
285
+ source: KnowledgeEntity;
286
+ relation: KnowledgeRelation;
287
+ target: KnowledgeEntity;
288
+ }
289
+
290
+ export interface CanonicalSessionMessage {
291
+ role: string;
292
+ content: string;
293
+ session_id: string;
294
+ channel_id: string | null;
295
+ created_at: string;
296
+ }
297
+
298
+ export interface CanonicalSession {
299
+ canonical_id: string;
300
+ agent_id: string;
301
+ user_id: string;
302
+ messages: CanonicalSessionMessage[];
303
+ compaction_cursor: number;
304
+ compacted_summary: string | null;
305
+ message_count: number;
306
+ created_at: string;
307
+ updated_at: string;
308
+ }
309
+
310
+ export interface CanonicalSessionContext {
311
+ summary: string | null;
312
+ recent_messages: CanonicalSessionMessage[];
313
+ }
314
+
315
+ export type UsageWindow = 'daily' | 'monthly' | 'all';
316
+
317
+ export interface UsageTotals {
318
+ total_input_tokens: number;
319
+ total_output_tokens: number;
320
+ total_tokens: number;
321
+ total_cost_usd: number;
322
+ call_count: number;
323
+ total_tool_calls: number;
324
+ }
325
+
326
+ export interface UsageModelAggregate {
327
+ model: string;
328
+ total_input_tokens: number;
329
+ total_output_tokens: number;
330
+ total_tokens: number;
331
+ total_cost_usd: number;
332
+ call_count: number;
333
+ total_tool_calls: number;
334
+ }
335
+
336
+ export interface UsageAgentAggregate {
337
+ agent_id: string;
338
+ total_input_tokens: number;
339
+ total_output_tokens: number;
340
+ total_tokens: number;
341
+ total_cost_usd: number;
342
+ call_count: number;
343
+ total_tool_calls: number;
344
+ }
345
+
346
+ export interface UsageDailyAggregate {
347
+ day: string;
348
+ total_input_tokens: number;
349
+ total_output_tokens: number;
350
+ total_tokens: number;
351
+ total_cost_usd: number;
352
+ call_count: number;
353
+ total_tool_calls: number;
354
+ }
355
+
165
356
  export interface ScheduledTask {
166
357
  id: number;
167
358
  session_id: string;
@@ -172,6 +363,8 @@ export interface ScheduledTask {
172
363
  prompt: string;
173
364
  enabled: number;
174
365
  last_run: string | null;
366
+ last_status: string | null;
367
+ consecutive_errors: number;
175
368
  created_at: string;
176
369
  }
177
370