@vascend/talos 0.1.5 → 0.1.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 (224) hide show
  1. package/dist/core/agent.d.ts +16 -3
  2. package/dist/core/agent.js +114 -34
  3. package/dist/core/background.d.ts +10 -3
  4. package/dist/core/background.js +55 -9
  5. package/dist/core/bootstrap.js +1 -0
  6. package/dist/core/compaction.js +1 -1
  7. package/dist/core/config-storage.d.ts +5 -0
  8. package/dist/core/config-storage.js +83 -0
  9. package/dist/core/config.d.ts +11 -2
  10. package/dist/core/config.js +24 -8
  11. package/dist/core/context-ledger.js +2 -1
  12. package/dist/core/context-policy.d.ts +1 -1
  13. package/dist/core/context-policy.js +10 -7
  14. package/dist/core/credentials.js +29 -30
  15. package/dist/core/effort.d.ts +1 -1
  16. package/dist/core/effort.js +5 -1
  17. package/dist/core/execution-guidance.d.ts +1 -1
  18. package/dist/core/execution-guidance.js +2 -0
  19. package/dist/core/file-coordination.d.ts +3 -0
  20. package/dist/core/file-coordination.js +28 -0
  21. package/dist/core/file-lease-store.d.ts +3 -0
  22. package/dist/core/file-lease-store.js +28 -12
  23. package/dist/core/fusion.d.ts +24 -0
  24. package/dist/core/fusion.js +80 -0
  25. package/dist/core/model-limits.d.ts +1 -0
  26. package/dist/core/model-limits.js +5 -1
  27. package/dist/core/permission-queue.js +15 -15
  28. package/dist/core/request-plan.js +4 -3
  29. package/dist/core/session-naming.js +4 -0
  30. package/dist/core/session.d.ts +4 -2
  31. package/dist/core/session.js +13 -4
  32. package/dist/core/settings.d.ts +2 -0
  33. package/dist/core/settings.js +5 -16
  34. package/dist/core/step-policy.d.ts +18 -0
  35. package/dist/core/step-policy.js +6 -0
  36. package/dist/core/tool-artifacts.d.ts +2 -0
  37. package/dist/core/tool-artifacts.js +21 -3
  38. package/dist/core/tool-result-index.js +29 -33
  39. package/dist/core/tool-result-store.js +6 -5
  40. package/dist/core/types.d.ts +10 -3
  41. package/dist/core/user-inbox.d.ts +9 -0
  42. package/dist/core/user-inbox.js +22 -3
  43. package/dist/core/user-input.js +1 -1
  44. package/dist/fusion/autopilot-fast.d.ts +59 -0
  45. package/dist/fusion/autopilot-fast.js +213 -0
  46. package/dist/fusion/autopilot.d.ts +75 -0
  47. package/dist/fusion/autopilot.js +308 -0
  48. package/dist/fusion/focus.d.ts +30 -0
  49. package/dist/fusion/focus.js +122 -0
  50. package/dist/fusion/gate.d.ts +30 -0
  51. package/dist/fusion/gate.js +95 -0
  52. package/dist/fusion/jev.d.ts +71 -0
  53. package/dist/fusion/jev.js +164 -0
  54. package/dist/fusion/receipts.d.ts +25 -0
  55. package/dist/fusion/receipts.js +34 -0
  56. package/dist/fusion/route.d.ts +25 -0
  57. package/dist/fusion/route.js +84 -0
  58. package/dist/fusion/run.d.ts +4 -0
  59. package/dist/fusion/run.js +186 -0
  60. package/dist/horizon/engine.d.ts +20 -1
  61. package/dist/horizon/engine.js +57 -0
  62. package/dist/horizon/index.d.ts +2 -2
  63. package/dist/horizon/index.js +1 -1
  64. package/dist/horizon/types.d.ts +29 -0
  65. package/dist/index.js +12 -0
  66. package/dist/providers/anthropic-oauth.d.ts +8 -3
  67. package/dist/providers/anthropic-oauth.js +13 -10
  68. package/dist/providers/anthropic.d.ts +9 -2
  69. package/dist/providers/anthropic.js +64 -21
  70. package/dist/providers/cli-models.d.ts +30 -0
  71. package/dist/providers/cli-models.js +194 -0
  72. package/dist/providers/model-effort.js +1 -1
  73. package/dist/providers/openai-oauth.d.ts +8 -3
  74. package/dist/providers/openai-oauth.js +9 -3
  75. package/dist/providers/openrouter-manual-fallback.d.ts +3 -1
  76. package/dist/providers/openrouter-manual-fallback.js +20 -20
  77. package/dist/providers/registry.js +3 -2
  78. package/dist/providers/retry.d.ts +3 -0
  79. package/dist/providers/retry.js +44 -1
  80. package/dist/remote/cifratura.d.ts +12 -0
  81. package/dist/remote/cifratura.js +66 -0
  82. package/dist/remote/index.d.ts +15 -0
  83. package/dist/remote/index.js +65 -0
  84. package/dist/remote/relay-client.d.ts +58 -0
  85. package/dist/remote/relay-client.js +315 -0
  86. package/dist/remote/sessione-remota.d.ts +60 -0
  87. package/dist/remote/sessione-remota.js +266 -0
  88. package/dist/sdk.d.ts +7 -2
  89. package/dist/sdk.js +3 -1
  90. package/dist/tools/builtin.js +41 -20
  91. package/dist/tools/capabilities.d.ts +3 -1
  92. package/dist/tools/capabilities.js +5 -2
  93. package/dist/tools/computer-use.js +2 -1
  94. package/dist/tools/pagination.js +1 -1
  95. package/dist/tools/program.js +34 -21
  96. package/dist/tools/team-tools.js +11 -6
  97. package/dist/tui/account-entry.js +14 -9
  98. package/dist/tui/agent-label.d.ts +2 -0
  99. package/dist/tui/agent-label.js +5 -0
  100. package/dist/tui/agent-sessions-screen.d.ts +12 -2
  101. package/dist/tui/agent-sessions-screen.js +62 -16
  102. package/dist/tui/agent-sessions.d.ts +14 -0
  103. package/dist/tui/agent-sessions.js +67 -6
  104. package/dist/tui/bottom-input.d.ts +46 -2
  105. package/dist/tui/bottom-input.js +358 -59
  106. package/dist/tui/branding.js +8 -4
  107. package/dist/tui/capability-lists.d.ts +10 -0
  108. package/dist/tui/capability-lists.js +41 -0
  109. package/dist/tui/command-output-registry.js +4 -0
  110. package/dist/tui/command-transcript.d.ts +1 -1
  111. package/dist/tui/command-transcript.js +17 -21
  112. package/dist/tui/commands.js +4 -1
  113. package/dist/tui/compaction-status.d.ts +7 -0
  114. package/dist/tui/compaction-status.js +39 -0
  115. package/dist/tui/config-recovery.d.ts +12 -0
  116. package/dist/tui/config-recovery.js +54 -0
  117. package/dist/tui/context-meter.d.ts +3 -0
  118. package/dist/tui/context-meter.js +35 -8
  119. package/dist/tui/diff.d.ts +9 -0
  120. package/dist/tui/diff.js +43 -19
  121. package/dist/tui/fusion-menu.d.ts +5 -0
  122. package/dist/tui/fusion-menu.js +89 -0
  123. package/dist/tui/i18n-catalog.js +312 -1
  124. package/dist/tui/live-command.d.ts +1 -0
  125. package/dist/tui/live-command.js +9 -0
  126. package/dist/tui/live-output.d.ts +1 -0
  127. package/dist/tui/live-output.js +11 -0
  128. package/dist/tui/model-details.js +13 -12
  129. package/dist/tui/model-picker.js +17 -7
  130. package/dist/tui/model-selection.js +19 -10
  131. package/dist/tui/native-scrollback.js +5 -0
  132. package/dist/tui/prompt-queue.d.ts +3 -0
  133. package/dist/tui/prompt-queue.js +19 -1
  134. package/dist/tui/provider-label.d.ts +2 -0
  135. package/dist/tui/provider-label.js +9 -0
  136. package/dist/tui/provider-menu.js +25 -24
  137. package/dist/tui/queue-screen.js +2 -1
  138. package/dist/tui/remote-command.d.ts +7 -0
  139. package/dist/tui/remote-command.js +79 -0
  140. package/dist/tui/repl.d.ts +8 -2
  141. package/dist/tui/repl.js +355 -311
  142. package/dist/tui/review-store.d.ts +1 -0
  143. package/dist/tui/review-store.js +28 -7
  144. package/dist/tui/review-team.js +4 -3
  145. package/dist/tui/runtime-error.d.ts +2 -0
  146. package/dist/tui/runtime-error.js +26 -0
  147. package/dist/tui/selector-mouse.d.ts +1 -0
  148. package/dist/tui/selector-mouse.js +1 -1
  149. package/dist/tui/selector.d.ts +7 -0
  150. package/dist/tui/selector.js +19 -2
  151. package/dist/tui/session-controls.d.ts +26 -0
  152. package/dist/tui/session-controls.js +65 -0
  153. package/dist/tui/session-naming-notice.js +5 -3
  154. package/dist/tui/session-replay.d.ts +1 -0
  155. package/dist/tui/session-replay.js +101 -32
  156. package/dist/tui/session-transcript-layout.d.ts +9 -0
  157. package/dist/tui/session-transcript-layout.js +41 -0
  158. package/dist/tui/startup.js +63 -33
  159. package/dist/tui/status-line.d.ts +1 -1
  160. package/dist/tui/status-line.js +20 -12
  161. package/dist/tui/talos-art-data.d.ts +1 -0
  162. package/dist/tui/talos-art-data.js +2 -0
  163. package/dist/tui/talos-art.d.ts +1 -0
  164. package/dist/tui/talos-art.js +9 -0
  165. package/dist/tui/task-labels.d.ts +1 -0
  166. package/dist/tui/task-labels.js +8 -0
  167. package/dist/tui/terminal-text.d.ts +2 -1
  168. package/dist/tui/terminal-text.js +28 -2
  169. package/dist/tui/theme.js +2 -2
  170. package/dist/tui/tool-presentation.d.ts +3 -0
  171. package/dist/tui/tool-presentation.js +98 -0
  172. package/dist/tui/tool-style.js +4 -0
  173. package/dist/tui/transcript-store.d.ts +25 -0
  174. package/dist/tui/transcript-store.js +151 -0
  175. package/dist/tui/usage-footer.d.ts +1 -1
  176. package/dist/tui/usage-footer.js +3 -1
  177. package/dist/tui/vascend-id.d.ts +49 -0
  178. package/dist/tui/vascend-id.js +321 -0
  179. package/dist/tui/web-login-screen.js +4 -3
  180. package/dist/tui/web-login.d.ts +7 -15
  181. package/dist/tui/web-login.js +38 -78
  182. package/dist/tui/worker-transcripts.d.ts +31 -0
  183. package/dist/tui/worker-transcripts.js +91 -0
  184. package/dist/vascend/candidate-collector.d.ts +6 -5
  185. package/dist/vascend/candidate-collector.js +44 -43
  186. package/dist/vascend/context-lint.js +32 -2
  187. package/dist/vascend/context-market-selection.js +12 -5
  188. package/dist/vascend/context-pack-render.js +10 -4
  189. package/dist/vascend/context-packet-mirrors.d.ts +8 -0
  190. package/dist/vascend/context-packet-mirrors.js +24 -0
  191. package/dist/vascend/context-plan-events.d.ts +4 -0
  192. package/dist/vascend/context-plan-events.js +44 -0
  193. package/dist/vascend/context-snapshot.js +4 -2
  194. package/dist/vascend/context-store-reader.d.ts +20 -0
  195. package/dist/vascend/context-store-reader.js +46 -0
  196. package/dist/vascend/file-lock.d.ts +1 -0
  197. package/dist/vascend/file-lock.js +26 -0
  198. package/dist/vascend/granular-kingdom.d.ts +1 -0
  199. package/dist/vascend/granular-kingdom.js +22 -15
  200. package/dist/vascend/kingdom-bus.d.ts +1 -0
  201. package/dist/vascend/kingdom-bus.js +3 -0
  202. package/dist/vascend/kingdom-channel.d.ts +2 -0
  203. package/dist/vascend/kingdom-channel.js +3 -0
  204. package/dist/vascend/kingdom-live.d.ts +32 -4
  205. package/dist/vascend/kingdom-live.js +149 -13
  206. package/dist/vascend/kingdom-run.d.ts +5 -1
  207. package/dist/vascend/kingdom-run.js +6 -2
  208. package/dist/vascend/kingdom.d.ts +3 -0
  209. package/dist/vascend/kingdom.js +20 -13
  210. package/dist/vascend/memory-packet.d.ts +2 -1
  211. package/dist/vascend/memory-packet.js +31 -44
  212. package/dist/vascend/plan-store.d.ts +3 -1
  213. package/dist/vascend/plan-store.js +6 -4
  214. package/dist/vascend/room-memory.d.ts +10 -1
  215. package/dist/vascend/room-memory.js +42 -2
  216. package/dist/vascend/runtime.d.ts +3 -11
  217. package/dist/vascend/runtime.js +11 -123
  218. package/dist/vascend/session-search.js +2 -0
  219. package/dist/vascend/team-session.js +3 -2
  220. package/dist/vascend/vision.d.ts +3 -1
  221. package/dist/vascend/vision.js +6 -4
  222. package/dist/vascend/worker-session.d.ts +20 -0
  223. package/dist/vascend/worker-session.js +83 -0
  224. package/package.json +1 -1
@@ -0,0 +1,266 @@
1
+ // Una sessione di controllo remoto: il ponte fra l'agente di Talos e il relay.
2
+ //
3
+ // Verso il telefono (eventi, cifrati, direzione "ev"):
4
+ // message {role: "user"|"assistant", text}
5
+ // delta {text} testo mentre il modello scrive
6
+ // tool_call {id, name, input, parent?} parent: chiamata che la contiene
7
+ // tool_result {id, error, output, parent?}
8
+ // permission {id, action, detail} Talos chiede un'approvazione
9
+ // status {state, label?, permission?: {id, outcome, by}}
10
+ // end {}
11
+ // Dal telefono (comandi, cifrati, direzione "cmd"): il contenuto porta anche
12
+ // il proprio cid e l'ora (ts, ms), cosi' il relay non puo' riusarlo ne'
13
+ // spostarlo su un altro comando.
14
+ // message {text} come se fosse scritto nella TUI (mai un "/")
15
+ // interrupt {text?} con testo: consegna subito; senza: stop
16
+ // stop {}
17
+ // approve {permission} deny {permission}
18
+ //
19
+ // Gli eventi si cifrano e si accodano in ordine (una catena di promesse): il
20
+ // seq del relay e' contiguo e non deve mai saltare.
21
+ import { hostname } from "node:os";
22
+ import { basename } from "node:path";
23
+ import { randomBytes } from "node:crypto";
24
+ import { CifraturaError, cifra, decifra, nuovaChiave } from "./cifratura.js";
25
+ import { RelayHost } from "./relay-client.js";
26
+ export const VIEWER_URL = "https://vascend.it/remoto";
27
+ const MAX_TESTO = 64_000;
28
+ const MAX_ANTEPRIMA = 4_000;
29
+ const RITMO_DELTA_MS = 120;
30
+ const FINESTRA_COMANDI_MS = 10 * 60_000;
31
+ function tronca(testo, max) {
32
+ return testo.length <= max ? testo : `${testo.slice(0, max)}\n[... ${testo.length - max} caratteri non inviati]`;
33
+ }
34
+ function anteprimaInput(input) {
35
+ try {
36
+ return tronca(JSON.stringify(input) ?? "", MAX_ANTEPRIMA);
37
+ }
38
+ catch {
39
+ return "";
40
+ }
41
+ }
42
+ export class SessioneRemota {
43
+ relay;
44
+ chiave;
45
+ link;
46
+ mani;
47
+ now;
48
+ catena = Promise.resolve();
49
+ delta = "";
50
+ timerDelta = null;
51
+ risposta = "";
52
+ viewer = 0;
53
+ permessi = new Map();
54
+ constructor(relay, chiave, link, mani, now) {
55
+ this.relay = relay;
56
+ this.chiave = chiave;
57
+ this.link = link;
58
+ this.mani = mani;
59
+ this.now = now;
60
+ }
61
+ static async avvia(opts) {
62
+ const { chiave, testo } = await nuovaChiave();
63
+ let sessione = null;
64
+ const relay = await RelayHost.apri(opts.base, {
65
+ title: basename(opts.cwd) || "Talos",
66
+ agent: "talos",
67
+ device: hostname().slice(0, 80),
68
+ }, {
69
+ ...opts.relayDeps,
70
+ token: opts.token,
71
+ onStato: opts.onStato,
72
+ onPresenza: (n) => { if (sessione)
73
+ sessione.viewer = n; opts.onPresenza?.(n); },
74
+ onComando: (c) => sessione ? sessione.comando(c) : Promise.resolve(),
75
+ });
76
+ const link = `${VIEWER_URL}#s=${relay.sessione}&k=${testo}`;
77
+ sessione = new SessioneRemota(relay, chiave, link, opts.mani, opts.now ?? Date.now);
78
+ sessione.stato({ state: "idle" });
79
+ return sessione;
80
+ }
81
+ get id() { return this.relay.sessione; }
82
+ statoRelay() { return this.relay.statoAttuale(); }
83
+ dispositivi() { return this.viewer; }
84
+ // --- dall'agente al telefono --------------------------------------------------
85
+ // Adattatore per gli eventi dell'agente di Talos.
86
+ osserva(event) {
87
+ switch (event.type) {
88
+ case "turn_start":
89
+ this.statoAgente("thinking");
90
+ return;
91
+ case "text":
92
+ this.testo(event.delta);
93
+ return;
94
+ case "tool_call":
95
+ this.chiamataTool({ id: event.call.id, name: event.call.name, input: event.call.input, parent: event.parentId });
96
+ return;
97
+ case "tool_result":
98
+ this.risultatoTool({ id: event.result.callId, error: event.result.isError, output: event.result.output ?? "", parent: event.parentId });
99
+ return;
100
+ case "status":
101
+ this.statoAgente("working", event.label);
102
+ return;
103
+ case "turn_end":
104
+ this.rispostaCompleta();
105
+ return;
106
+ case "done":
107
+ this.rispostaCompleta();
108
+ this.statoAgente("idle");
109
+ return;
110
+ case "error":
111
+ this.rispostaCompleta();
112
+ this.statoAgente("error", event.message);
113
+ return;
114
+ default: return;
115
+ }
116
+ }
117
+ // --- API generica: per chi non ha gli eventi di Talos (l'IDE con Claude,
118
+ // Codex e gli altri motori traduce i suoi eventi in queste chiamate) -------
119
+ // Un pezzo della risposta mentre il modello scrive.
120
+ testo(delta) {
121
+ if (!delta)
122
+ return;
123
+ this.risposta += delta;
124
+ this.delta += delta;
125
+ if (this.delta.length >= 2_048)
126
+ this.svuotaDelta();
127
+ else if (!this.timerDelta)
128
+ this.timerDelta = setTimeout(() => this.svuotaDelta(), RITMO_DELTA_MS);
129
+ }
130
+ // Fine della risposta in corso: diventa un messaggio completo.
131
+ rispostaCompleta() {
132
+ this.chiudiRisposta();
133
+ }
134
+ chiamataTool(c) {
135
+ this.chiudiRisposta();
136
+ this.evento("tool_call", {
137
+ id: c.id, name: tronca(c.name, 200), input: typeof c.input === "string" ? tronca(c.input, MAX_ANTEPRIMA) : anteprimaInput(c.input),
138
+ ...(c.parent ? { parent: c.parent } : {}),
139
+ });
140
+ }
141
+ risultatoTool(r) {
142
+ this.evento("tool_result", {
143
+ id: r.id, error: r.error, output: tronca(r.output, MAX_ANTEPRIMA), ...(r.parent ? { parent: r.parent } : {}),
144
+ });
145
+ }
146
+ statoAgente(state, label) {
147
+ this.stato(label ? { state, label: tronca(label, state === "error" ? 500 : 200) } : { state });
148
+ }
149
+ messaggioUtente(testo) {
150
+ this.chiudiRisposta();
151
+ this.evento("message", { role: "user", text: tronca(testo, MAX_TESTO) });
152
+ }
153
+ // Richiesta di permesso in gara con la TUI: vince la prima risposta. La TUI
154
+ // riceve un segnale che si annulla quando risponde il telefono (e viceversa).
155
+ async permessoConGara(azione, dettaglio, signal, tui) {
156
+ const id = randomBytes(12).toString("hex");
157
+ const locale = new AbortController();
158
+ const entrambi = AbortSignal.any([signal, locale.signal]);
159
+ const dalTelefono = new Promise((resolve) => {
160
+ this.permessi.set(id, (valore) => resolve({ valore, da: "remote" }));
161
+ entrambi.addEventListener("abort", () => this.permessi.delete(id), { once: true });
162
+ });
163
+ this.chiudiRisposta();
164
+ this.evento("permission", { id, action: tronca(azione, 200), detail: tronca(dettaglio, MAX_ANTEPRIMA) });
165
+ this.stato({ state: "waiting", label: tronca(azione, 200) });
166
+ let esito;
167
+ try {
168
+ esito = await Promise.race([tui(entrambi).then((valore) => ({ valore, da: "talos" })), dalTelefono]);
169
+ }
170
+ finally {
171
+ locale.abort();
172
+ this.permessi.delete(id);
173
+ }
174
+ const outcome = signal.aborted ? "cancelled" : esito.valore ? "allowed" : "denied";
175
+ this.stato({ state: "working", permission: { id, outcome, by: esito.da } });
176
+ return esito.valore;
177
+ }
178
+ // Fine: ultimo stato, "end", poi la sessione si chiude per tutti.
179
+ async chiudi() {
180
+ this.chiudiRisposta();
181
+ for (const risolvi of this.permessi.values())
182
+ risolvi(false);
183
+ this.permessi.clear();
184
+ this.evento("end", {});
185
+ await this.catena;
186
+ await this.relay.chiudi();
187
+ }
188
+ // --- dal telefono all'agente --------------------------------------------------
189
+ async comando(c) {
190
+ if (!c.b)
191
+ return;
192
+ let dati;
193
+ try {
194
+ const v = await decifra(this.chiave, this.relay.sessione, "cmd", c.b);
195
+ if (!v || typeof v !== "object" || Array.isArray(v))
196
+ return;
197
+ dati = v;
198
+ }
199
+ catch (e) {
200
+ if (e instanceof CifraturaError)
201
+ return; // non viene da un dispositivo abbinato
202
+ throw e;
203
+ }
204
+ // Il contenuto cifrato deve riguardare proprio questo comando e essere recente.
205
+ const ts = Number(dati["ts"]);
206
+ if (dati["cid"] !== c.cid || !Number.isFinite(ts) || Math.abs(this.now() - ts) > FINESTRA_COMANDI_MS)
207
+ return;
208
+ const testo = typeof dati["text"] === "string" ? dati["text"].slice(0, MAX_TESTO) : "";
209
+ switch (c.k) {
210
+ case "message":
211
+ if (testo.trim() && !this.mani.invia(testo, false))
212
+ this.stato({ state: "working", label: "comando non accettato" });
213
+ return;
214
+ case "interrupt":
215
+ if (testo.trim())
216
+ this.mani.invia(testo, true);
217
+ else
218
+ this.mani.ferma();
219
+ return;
220
+ case "stop":
221
+ this.mani.ferma();
222
+ return;
223
+ case "approve":
224
+ case "deny": {
225
+ const id = typeof dati["permission"] === "string" ? dati["permission"] : "";
226
+ this.permessi.get(id)?.(c.k === "approve");
227
+ return;
228
+ }
229
+ default:
230
+ return;
231
+ }
232
+ }
233
+ // --- invio ordinato -----------------------------------------------------------
234
+ svuotaDelta() {
235
+ if (this.timerDelta) {
236
+ clearTimeout(this.timerDelta);
237
+ this.timerDelta = null;
238
+ }
239
+ if (!this.delta)
240
+ return;
241
+ const testo = this.delta;
242
+ this.delta = "";
243
+ this.accoda("delta", { text: testo });
244
+ }
245
+ chiudiRisposta() {
246
+ this.svuotaDelta();
247
+ const testo = this.risposta.trim();
248
+ this.risposta = "";
249
+ if (testo)
250
+ this.accoda("message", { role: "assistant", text: tronca(testo, MAX_TESTO) });
251
+ }
252
+ stato(dati) {
253
+ this.accoda("status", dati);
254
+ }
255
+ evento(k, dati) {
256
+ this.svuotaDelta();
257
+ this.accoda(k, dati);
258
+ }
259
+ accoda(k, dati) {
260
+ const sessione = this.relay.sessione;
261
+ this.catena = this.catena
262
+ .then(() => cifra(this.chiave, sessione, "ev", dati))
263
+ .then((b) => { this.relay.invia(k, b); })
264
+ .catch(() => { });
265
+ }
266
+ }
package/dist/sdk.d.ts CHANGED
@@ -1,5 +1,10 @@
1
1
  export { bootstrap } from "./core/bootstrap.js";
2
2
  export type { Harness } from "./core/bootstrap.js";
3
+ export type { FusionConfig, FusionGateMode, FusionAvailability } from "./core/fusion.js";
4
+ export { DEFAULT_FUSION, fusionAvailability, isPaidTier, parseFusion } from "./core/fusion.js";
5
+ export type { StepDirective, StepPolicy } from "./core/step-policy.js";
6
+ export { createJevClient, isDevChannel, parseJevResponse, resolveJevTransport } from "./fusion/jev.js";
7
+ export type { JevAnswer, JevClient, JevOutcome, JevQuestion, JevResult, JevTransport } from "./fusion/jev.js";
3
8
  export { runTurn } from "./core/agent.js";
4
9
  export type { AgentDeps } from "./core/agent.js";
5
10
  export { FileCoordinationHub, fileActor, formatFileConflicts, withFileActor } from "./core/file-coordination.js";
@@ -52,8 +57,8 @@ export { buildArchitectureMap, renderArchitectureMap, scanArchitectureMap, } fro
52
57
  export type { ArchDependency, ArchEntryPoint, ArchExternal, ArchHotspot, ArchModuleStat, ArchSourceFile, ArchitectureMap, } from "./vascend/architecture-map.js";
53
58
  export { doctorExitCode, renderDoctorReport, runDoctor } from "./core/doctor.js";
54
59
  export type { DoctorCheck, DoctorReport, DoctorStatus } from "./core/doctor.js";
55
- export { HORIZON_CAPABILITIES, HorizonContextUnavailableError, HorizonEngine, HorizonMainTeam, needsGuideArbitration, HorizonSessionNotFoundError, HorizonStateError, routeWorkerModels, } from "./horizon/index.js";
56
- export type { HorizonCapabilityDescriptor, HorizonContextStatus, HorizonEngineOptions, HorizonEngineState, HorizonModelCatalog, HorizonModelDescriptor, HorizonModelInspectionOptions, HorizonProviderDescriptor, HorizonRuntime, HorizonSessionListItem, HorizonSessionSnapshot, HorizonTurn, HorizonTurnInput, HorizonTurnOptions, HorizonTurnResult, HorizonTurnStatus, HorizonVibeStatus, } from "./horizon/index.js";
60
+ export { HORIZON_CAPABILITIES, HorizonContextUnavailableError, HorizonFusionUnavailableError, HorizonEngine, HorizonMainTeam, needsGuideArbitration, HorizonSessionNotFoundError, HorizonStateError, routeWorkerModels, } from "./horizon/index.js";
61
+ export type { HorizonCapabilityDescriptor, HorizonContextStatus, HorizonEngineOptions, HorizonEngineState, HorizonModelCatalog, HorizonModelDescriptor, HorizonModelInspectionOptions, HorizonProviderDescriptor, HorizonRuntime, HorizonSessionListItem, HorizonSessionSnapshot, HorizonTurn, HorizonTurnInput, HorizonTurnOptions, HorizonTurnResult, HorizonTurnStatus, HorizonVibeStatus, HorizonFusionStatus, } from "./horizon/index.js";
57
62
  export type { HorizonGuideCooperation, HorizonGuideDecision, HorizonGuideDecisionRecord, HorizonGuideDecisionRequest, HorizonGuidePlan, HorizonGuideTask, HorizonGuideVerdict, HorizonMainExchange, HorizonMainRef, HorizonMainTeamConfig, HorizonMainTeamEvent, HorizonMainTeamResult, HorizonMainTeamTurn, HorizonTeamRef, HorizonAgentPurpose, } from "./horizon/index.js";
58
63
  export type { AgentEvent, EventSink, Message, ContentPart, Tool, ToolDef, ToolRegistrationDef, RegisteredTool, ToolHandler, ToolHandlerOutput, ToolContext, ImagePart, Provider, ModelTurn, CompletionRequest, } from "./core/types.js";
59
64
  export type { Extension, ExtensionApi, SlashCommand, } from "./extensions/types.js";
package/dist/sdk.js CHANGED
@@ -2,6 +2,8 @@
2
2
  // (es. il processo main di vascend-terminal). Quattro modalita' condividono
3
3
  // lo stesso nucleo: TUI, print, RPC e questo SDK.
4
4
  export { bootstrap } from "./core/bootstrap.js";
5
+ export { DEFAULT_FUSION, fusionAvailability, isPaidTier, parseFusion } from "./core/fusion.js";
6
+ export { createJevClient, isDevChannel, parseJevResponse, resolveJevTransport } from "./fusion/jev.js";
5
7
  export { runTurn } from "./core/agent.js";
6
8
  export { FileCoordinationHub, fileActor, formatFileConflicts, withFileActor } from "./core/file-coordination.js";
7
9
  // Casella dei messaggi scritti mentre l'agente lavora: un host che usa runTurn
@@ -53,7 +55,7 @@ export { buildArchitectureMap, renderArchitectureMap, scanArchitectureMap, } fro
53
55
  // Diagnostica ambiente (stessa del comando `talos --doctor`).
54
56
  export { doctorExitCode, renderDoctorReport, runDoctor } from "./core/doctor.js";
55
57
  // Horizon: facade headless stabile per l'embedding in Vascend Terminal.
56
- export { HORIZON_CAPABILITIES, HorizonContextUnavailableError, HorizonEngine, HorizonMainTeam, needsGuideArbitration, HorizonSessionNotFoundError, HorizonStateError, routeWorkerModels, } from "./horizon/index.js";
58
+ export { HORIZON_CAPABILITIES, HorizonContextUnavailableError, HorizonFusionUnavailableError, HorizonEngine, HorizonMainTeam, needsGuideArbitration, HorizonSessionNotFoundError, HorizonStateError, routeWorkerModels, } from "./horizon/index.js";
57
59
  // Modulo Cowork: workspace condivisi collaborativi su engine Talos (desktop).
58
60
  export { CoworkManager, localIdentity, CoworkStore, CoworkSyncChannel, CoworkEngine, createCoworkIpc, createCoworkClient, } from "./cowork/index.js";
59
61
  // Kingdoms Mode osservabile e INTERROGABILE dall'host: scrivere a un singolo
@@ -286,8 +286,10 @@ function runBashSpawn(command, ctx, options) {
286
286
  mgr.clearForeground(task.id);
287
287
  resolvePromise(isError ? toolError(msg) : msg);
288
288
  };
289
- child.stdout?.on("data", (d) => mgr.appendOutput(task.id, d.toString("utf8")));
290
- child.stderr?.on("data", (d) => mgr.appendOutput(task.id, d.toString("utf8")));
289
+ child.stdout?.setEncoding("utf8");
290
+ child.stderr?.setEncoding("utf8");
291
+ child.stdout?.on("data", (text) => mgr.appendOutput(task.id, text));
292
+ child.stderr?.on("data", (text) => mgr.appendOutput(task.id, text));
291
293
  child.on("error", (err) => {
292
294
  mgr.finishTask(task.id, "failed");
293
295
  finish(`errore esecuzione: ${err.message}`, true);
@@ -308,7 +310,7 @@ function runBashSpawn(command, ctx, options) {
308
310
  finish(`comando in background [${task.id}]: ${command.length > 60 ? command.slice(0, 57) + "..." : command}\n` +
309
311
  `Continua a girare. Leggi l'output con bash_output id="${task.id}".`);
310
312
  };
311
- mgr.setForeground({ taskId: task.id, background: detach });
313
+ mgr.setForeground({ taskId: task.id, sessionId: ctx.sessionId, background: detach });
312
314
  // Un agente non puo' decidere di interrompere un tool mentre ne attende il
313
315
  // risultato. Una breve attesa evita un secondo giro per i comandi rapidi;
314
316
  // oltre il limite bash_output permette di osservare e terminare il task.
@@ -330,12 +332,18 @@ function runBashSpawn(command, ctx, options) {
330
332
  export const bashOutputTool = {
331
333
  name: "bash_output",
332
334
  description: "Monitora e gestisce un comando shell in background. Passa `id` (es. \"bg1\") per " +
333
- "leggerne stato e output; senza id elenca i task. `kill: true` interrompe il processo.",
335
+ "attendere il risultato finale senza polling del modello; l'attesa e' interrompibile. " +
336
+ "`wait: false` legge subito stato/output (utile per server persistenti); senza id elenca i task. " +
337
+ "`timeout_seconds` limita esplicitamente l'attesa; omettilo per attendere il completamento. " +
338
+ "`kill: true` interrompe subito il processo.",
334
339
  schema: {
335
340
  type: "object",
336
341
  properties: {
337
342
  id: { type: "string", description: "id del task (es. bg1)" },
338
343
  kill: { type: "boolean", description: "se true termina il task" },
344
+ wait: { type: "boolean", description: "default true: attende il completamento; false: legge subito, per server persistenti o diagnosi" },
345
+ timeout_seconds: { type: "number", minimum: 0, maximum: 3600,
346
+ description: "Limite opzionale dell'attesa in secondi; omesso attende il risultato finale, 0 legge subito" },
339
347
  },
340
348
  },
341
349
  handler: async (input, ctx) => {
@@ -353,6 +361,13 @@ export const bashOutputTool = {
353
361
  return toolError(`task non trovato: ${id}`);
354
362
  if (input["kill"] === true)
355
363
  return mgr.kill(id) ? `terminato ${id}` : `${id} non e' piu' in esecuzione`;
364
+ if (input["wait"] !== false && t.status === "running") {
365
+ const timeout = input["timeout_seconds"];
366
+ const timeoutMs = typeof timeout === "number" && Number.isFinite(timeout)
367
+ ? Math.min(3600, Math.max(0, timeout)) * 1000 : undefined;
368
+ await mgr.waitForTask(id, ctx.signal, { timeoutMs, interrupted: ctx.waitInterrupted });
369
+ ctx.signal.throwIfAborted();
370
+ }
356
371
  const head = `${t.id} [${t.status}${t.exitCode != null ? ` · uscita ${t.exitCode}` : ""}] ${t.command}`;
357
372
  const output = `${head}\n${clamp(t.output) || "(nessun output)"}`;
358
373
  return t.status === "failed" || t.status === "killed" ? toolError(output) : output;
@@ -367,7 +382,7 @@ const READ_FINESTRA_BYTES = 2_000_000;
367
382
  const READ_SCAN_MAX_BYTES = 16_000_000;
368
383
  const READ_CHUNK_BYTES = 262_144;
369
384
  // Un read senza limit su un file grande ritorna comunque una finestra finita.
370
- const READ_RIGHE_DEFAULT_GRANDI = 1_000;
385
+ const READ_RIGHE_DEFAULT_GRANDI = 200;
371
386
  // Lettura incrementale per righe di un file grande: chunk da 256KB, decoder
372
387
  // streaming (i confini multi-byte non si spezzano), respiro periodico per
373
388
  // l'event loop, stop appena la finestra richiesta e' completa.
@@ -437,14 +452,14 @@ async function leggiFinestra(abs, rel, taglia, offset, limit, ctx) {
437
452
  export const readTool = {
438
453
  name: "read_file",
439
454
  execution: "parallel",
440
- description: "Legge un file di testo (path libero). Per sbirciarne solo una porzione usa " +
455
+ description: "Legge un file di testo (path libero), default 200 righe. Per scegliere una porzione usa " +
441
456
  "`offset` (riga di partenza, 1-based) e `limit` (numero di righe): NON serve bash/head/cat.",
442
457
  schema: {
443
458
  type: "object",
444
459
  properties: {
445
460
  path: { type: "string" },
446
461
  offset: { type: "number", description: "riga di partenza (1-based), default 1" },
447
- limit: { type: "number", description: "numero massimo di righe da leggere" },
462
+ limit: { type: "number", description: "numero massimo di righe da leggere (default 200)" },
448
463
  },
449
464
  required: ["path"],
450
465
  },
@@ -461,8 +476,10 @@ export const readTool = {
461
476
  mode: "viewing", others: conflicts, at: Date.now(),
462
477
  });
463
478
  }
464
- const offset = typeof input["offset"] === "number" && input["offset"] > 0 ? Math.floor(input["offset"]) : 1;
465
- const limit = typeof input["limit"] === "number" && input["limit"] > 0 ? Math.floor(input["limit"]) : 0;
479
+ const offset = typeof input["offset"] === "number" && Number.isFinite(input["offset"]) && input["offset"] > 0
480
+ ? Math.max(1, Math.floor(input["offset"])) : 1;
481
+ const limit = typeof input["limit"] === "number" && Number.isFinite(input["limit"]) && input["limit"] > 0
482
+ ? Math.max(1, Math.floor(input["limit"])) : READ_RIGHE_DEFAULT_GRANDI;
466
483
  // I file grandi non si leggono mai integrali: finestra incrementale.
467
484
  const taglia = (await stat(abs)).size;
468
485
  const notaPresenza = conflicts.length
@@ -471,14 +488,13 @@ export const readTool = {
471
488
  if (taglia > READ_FINESTRA_BYTES)
472
489
  return await leggiFinestra(abs, rel, taglia, offset, limit, ctx) + notaPresenza;
473
490
  const full = await readFile(abs, "utf8");
474
- if (offset <= 1 && limit <= 0)
475
- return paginate(full, `file ${rel}`, ctx) + notaPresenza;
476
491
  // Lettura parziale per righe (stile Claude Code): porzione [offset, offset+limit).
477
492
  const lines = full.split("\n");
478
493
  const start = Math.min(offset - 1, lines.length);
479
494
  const slice = limit > 0 ? lines.slice(start, start + limit) : lines.slice(start);
480
495
  const note = start + slice.length < lines.length || start > 0
481
- ? `\n[righe ${start + 1}-${start + slice.length} di ${lines.length}]`
496
+ ? `\n[righe ${start + 1}-${start + slice.length} di ${lines.length}`
497
+ + (start + slice.length < lines.length ? `; continua con read_file offset=${start + slice.length + 1} limit=${limit}` : "") + "]"
482
498
  : "";
483
499
  return paginate(slice.join("\n"), `file ${rel}`, ctx) + note + notaPresenza;
484
500
  }
@@ -487,11 +503,11 @@ export const readTool = {
487
503
  }
488
504
  },
489
505
  };
490
- function acquireFileWrite(ctx, abs) {
506
+ async function acquireFileWrite(ctx, abs) {
491
507
  const coordination = ctx.fileCoordination;
492
508
  if (!coordination)
493
509
  return "";
494
- const outcome = coordination.hub.acquire(abs, coordination.actor);
510
+ const outcome = await coordination.hub.acquireAsync(abs, coordination.actor, ctx.signal);
495
511
  if (!outcome.ok) {
496
512
  coordination.emit?.({
497
513
  phase: "blocked", path: abs, actor: coordination.actor,
@@ -519,10 +535,10 @@ function validateFileWrite(ctx, lease) {
519
535
  return `conflitto file sopraggiunto: ${formatFileConflicts(conflicts)}. `
520
536
  + "La scrittura e' stata annullata senza sovrascrivere il lavoro concorrente.";
521
537
  }
522
- function releaseFileWrite(ctx, lease) {
538
+ async function releaseFileWrite(ctx, lease) {
523
539
  if (typeof lease === "string" || !ctx.fileCoordination)
524
540
  return;
525
- ctx.fileCoordination.hub.release(lease);
541
+ await ctx.fileCoordination.hub.releaseAsync(lease);
526
542
  ctx.fileCoordination.emit?.({
527
543
  phase: "released", path: lease.path, actor: lease.actor,
528
544
  mode: "writing", others: [], at: Date.now(),
@@ -587,7 +603,7 @@ export const writeTool = {
587
603
  }
588
604
  const abs = await safePath(ctx.cwd, rel, ctx.fsRoots);
589
605
  ctx.signal.throwIfAborted();
590
- const lease = acquireFileWrite(ctx, abs);
606
+ const lease = await acquireFileWrite(ctx, abs);
591
607
  if (typeof lease === "string" && lease)
592
608
  return { output: lease, isError: true };
593
609
  const append = input["append"] === true;
@@ -615,7 +631,7 @@ export const writeTool = {
615
631
  return `${append ? "accodato" : "scritto"}: ${rel}${notaNul}`;
616
632
  }
617
633
  finally {
618
- releaseFileWrite(ctx, lease);
634
+ await releaseFileWrite(ctx, lease);
619
635
  }
620
636
  }
621
637
  catch (err) {
@@ -755,7 +771,7 @@ export const editTool = {
755
771
  }
756
772
  const abs = await safePath(ctx.cwd, rel, ctx.fsRoots);
757
773
  ctx.signal.throwIfAborted();
758
- const lease = acquireFileWrite(ctx, abs);
774
+ const lease = await acquireFileWrite(ctx, abs);
759
775
  if (typeof lease === "string" && lease)
760
776
  return { output: lease, isError: true };
761
777
  try {
@@ -793,7 +809,7 @@ export const editTool = {
793
809
  return `modificato: ${rel}${hint}${notaNul}`;
794
810
  }
795
811
  finally {
796
- releaseFileWrite(ctx, lease);
812
+ await releaseFileWrite(ctx, lease);
797
813
  }
798
814
  }
799
815
  catch (err) {
@@ -862,6 +878,7 @@ export const recallTool = {
862
878
  properties: {
863
879
  query: { type: "string", description: "Testo da cercare nelle attivita archiviate" },
864
880
  rooms: { type: "array", items: { type: "string" }, description: "Id attivita da recuperare integralmente" },
881
+ attachments: { type: "boolean", description: "Con rooms: recupera le immagini originali del checkpoint come input visivo, on demand. Senza questa opzione restituisce solo l'indice." },
865
882
  files: { type: "array", items: { type: "string" }, description: "Path: restituisce le attivita che hanno toccato quei file (traversata del grafo)" },
866
883
  expand: { type: "boolean", description: "Se true, apre gruppi e liste fino alle singole attivita" },
867
884
  domains: { type: "array", items: { type: "string" }, description: "Domini memoria: rooms, plan, notes, skills, vision, assets, decisions, contracts, sessions (conversazioni passate)" },
@@ -893,6 +910,10 @@ export const recallTool = {
893
910
  const found = wantsRooms && mem.count() ? input["expand"] === true ? mem.expandRooms(rooms) : mem.byRooms(rooms) : [];
894
911
  if (!found.length)
895
912
  return `nessuna attivita trovata per: ${rooms.join(", ")}`;
913
+ if (input["attachments"] === true) {
914
+ const media = mem.attachmentMedia(rooms);
915
+ return { output: found.map(r => r.body).join('\n\n---\n\n') + `\nAllegati visivi recuperati: ${media.length}.` + (media.length ? '' : ' Originali non disponibili; non dedurre dettagli visivi dal riepilogo.'), media };
916
+ }
896
917
  return paginate(found.map((r) => r.body).join("\n\n---\n\n"), "attivita", ctx);
897
918
  }
898
919
  // TRAVERSATA DEL GRAFO: "chi ha gia' messo le mani su questo file?".
@@ -1,5 +1,7 @@
1
1
  export declare const PROTOCOL_TOOLS: ReadonlySet<string>;
2
2
  export declare const NATIVE_CALL_TOOLS: ReadonlySet<string>;
3
+ /** Desktop consent option; hosts and Fusion recognize the consent question by it. */
4
+ export declare const COMPUTER_USE_CONSENT = "Consenti accesso al desktop";
3
5
  export declare const BUILTIN_CAPABILITIES: Readonly<Record<string, readonly string[]>>;
4
6
  export declare const BUILTIN_CONTINUATIONS: Readonly<Record<string, readonly string[]>>;
5
- export declare const COMPACT_TOOL_GUIDANCE = "Tool protocol: resolve and execute\nAll operation names mentioned elsewhere (bash, read_file, vascend_plan, ask_user, etc.)\nare internal tools. Use execute to call them; only tools in the native list can be\ncalled at top level. Interpret the user's language yourself and emit stable capability\nIDs; never translate file paths, search patterns or user data to discover a tool.\nresolve({capabilities:[\"process.run\",\"files.edit\"]}) returns exact tools, full input\ncontracts, continuations and a catalog revision. Reuse that revision and the contracts\nuntil stale_contract; do not resolve again for tools you already know.\nFamilies: files, process, output, plan, memory, user, workspace, code, web, image,\nskills, team, branches, market. resolve({namespace:\"files\"}) lists that family's\noperations; resolve({namespace:\"tools\"}) lists all installed tools in pages, including\nextensions. resolve({names:[\"read_file\",\"output_page\"]}) gets known names in one call.\nCommon capabilities: files.read/write/edit/search/find/list, process.run/observe,\nplan.create/update/inspect/note, user.ask, memory.recall, output.read.\nFor one operation: execute({mode:\"call\",revision,tool:\"read_file\",args:{path:\"src/a.ts\"}}).\nPrefer call mode for shell scripts and file edits. Use write_file/edit_file for UTF-8\nsource changes. In Python always specify encoding=\"utf-8\" when reading/writing text.\nFor sequences, pass payloads in data: execute({mode:\"program\",revision,data:{path:\"src/a.ts\"},code:\"return await tools.read_file({path:tools.$data.path});\"}).\nKeep scripts, file content and multiline text in data, never inside JavaScript\nstring literals; tools.$data contains this invocation's data, default {}.\nPrograms have tools, state, console and await; state persists per session, return emits\nthe result. Tools return text; tools.$result(name,args) also returns structuredContent,\ncontent and isError. Keep intermediate data in state and return only useful evidence.\nAwait dependent writes and approvals sequentially; use bounded Promise.allSettled for\nindependent work and inspect every outcome. Do not repeat a completed action on error.\nPrefer process.run with bash for rg, rg --files and git. Follow background processes\nwith bash_output through execute until the final exit status. Unknown or unavailable\ncapabilities require catalog inspection or independent work, never invented tool names.\nThe revision checks catalog freshness, not permission. All existing permission gates\nstill apply. Tools marked native must be called directly, including for images or\nincremental computer control when their contract requires it.";
7
+ export declare const COMPACT_TOOL_GUIDANCE = "Tool protocol: resolve and execute\nAll operation names mentioned elsewhere (bash, read_file, vascend_plan, ask_user, etc.)\nare internal tools. Use execute to call them; only tools in the native list can be\ncalled at top level. Interpret the user's language yourself and emit stable capability\nIDs; never translate file paths, search patterns or user data to discover a tool.\nresolve({capabilities:[\"process.run\",\"files.edit\"]}) returns exact tools, full input\ncontracts, continuations and a catalog revision. Reuse that revision and the contracts\nuntil stale_contract; do not resolve again for tools you already know.\nFamilies: files, process, output, plan, memory, user, workspace, code, web, image,\nskills, team, branches, market. resolve({namespace:\"files\"}) lists that family's\noperations; resolve({namespace:\"tools\"}) lists all installed tools in pages, including\nextensions. resolve({names:[\"read_file\",\"output_page\"]}) gets known names in one call.\nCommon capabilities: files.read/write/edit/search/find/list, process.run/observe,\nplan.create/update/inspect/note, user.ask, memory.recall, output.read.\nFor one operation: execute({mode:\"call\",revision,tool:\"read_file\",args:{path:\"src/a.ts\"}}).\nPrefer call mode for shell scripts and file edits. Use write_file/edit_file for UTF-8\nsource changes. In Python always specify encoding=\"utf-8\" when reading/writing text.\nFor sequences, pass payloads in data: execute({mode:\"program\",revision,data:{path:\"src/a.ts\"},code:\"return await tools.read_file({path:tools.$data.path});\"}).\nKeep scripts, file content and multiline text in data, never inside JavaScript\nstring literals; tools.$data contains this invocation's data, default {}.\nPrograms have tools, state, console and await; state persists per session, return emits\nthe result. Tools return text; tools.$result(name,args) also returns structuredContent,\ncontent and isError. Keep intermediate data in state and return only useful evidence.\nAwait dependent writes and approvals sequentially; use bounded Promise.allSettled for\nindependent work and inspect every outcome. Do not repeat a completed action on error.\nPrefer process.run with bash for rg, rg --files and git. Collect a background result\nwith one bash_output call through execute: it waits locally for the final exit status.\nUse wait=false for persistent server snapshots, not repeated polling. Unknown or unavailable\ncapabilities require catalog inspection or independent work, never invented tool names.\nThe revision checks catalog freshness, not permission. All existing permission gates\nstill apply. Tools marked native must be called directly, including for images or\nincremental computer control when their contract requires it.";
@@ -1,6 +1,8 @@
1
1
  // Local operational metadata. These declarations never confer permissions.
2
2
  export const PROTOCOL_TOOLS = new Set(["resolve", "execute"]);
3
3
  export const NATIVE_CALL_TOOLS = new Set(["computer_control", "computer_batch"]);
4
+ /** Desktop consent option; hosts and Fusion recognize the consent question by it. */
5
+ export const COMPUTER_USE_CONSENT = "Consenti accesso al desktop";
4
6
  export const BUILTIN_CAPABILITIES = {
5
7
  read_file: ["files.read"], write_file: ["files.write"], edit_file: ["files.edit"],
6
8
  grep: ["files.search"], glob: ["files.find"], list_dir: ["files.list"], file_activity: ["files.activity"],
@@ -47,8 +49,9 @@ the result. Tools return text; tools.$result(name,args) also returns structuredC
47
49
  content and isError. Keep intermediate data in state and return only useful evidence.
48
50
  Await dependent writes and approvals sequentially; use bounded Promise.allSettled for
49
51
  independent work and inspect every outcome. Do not repeat a completed action on error.
50
- Prefer process.run with bash for rg, rg --files and git. Follow background processes
51
- with bash_output through execute until the final exit status. Unknown or unavailable
52
+ Prefer process.run with bash for rg, rg --files and git. Collect a background result
53
+ with one bash_output call through execute: it waits locally for the final exit status.
54
+ Use wait=false for persistent server snapshots, not repeated polling. Unknown or unavailable
52
55
  capabilities require catalog inspection or independent work, never invented tool names.
53
56
  The revision checks catalog freshness, not permission. All existing permission gates
54
57
  still apply. Tools marked native must be called directly, including for images or
@@ -1,11 +1,12 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { fileURLToPath } from "node:url";
3
3
  import { isAbsolute } from "node:path";
4
+ import { COMPUTER_USE_CONSENT } from "./capabilities.js";
4
5
  import { captureComputerDesktop } from "./computer-capture.js";
5
6
  import { normalizeMcpToolResult } from "../mcp/protocol.js";
6
7
  import { posixComputerTools } from "./computer-posix.js";
7
8
  const require = createRequire(import.meta.url);
8
- const CONSENT = "Consenti accesso al desktop";
9
+ const CONSENT = COMPUTER_USE_CONSENT;
9
10
  const unavailable = (output) => ({ output, isError: true });
10
11
  /** Standalone desktop tools. Loading definitions does not access the desktop. */
11
12
  export function registerComputerUse(registry, env, options = {}) {
@@ -4,7 +4,7 @@ import { ToolOutputStore } from "../core/tool-result-store.js";
4
4
  // mostra la prima pagina e si conserva il resto, recuperabile dal modello con il
5
5
  // tool output_page quando lo ritiene utile. Niente perdita di informazione.
6
6
  // Store di processo (in-memory), con eviction per non accumulare.
7
- const PAGE_CHARS = 30_000; // dimensione di una pagina
7
+ const PAGE_CHARS = 8_000; // circa 2k token; dettagli ulteriori solo su richiesta
8
8
  const MAX_STORED = 5_000_000; // tetto per entry (5MB): oltre, si tronca a monte
9
9
  const MAX_ENTRIES = 24; // quante uscite lunghe tenere
10
10
  class PageStore {