@vascend/talos 0.1.4 → 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 (251) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +37 -12
  3. package/dist/core/agent.d.ts +16 -3
  4. package/dist/core/agent.js +170 -70
  5. package/dist/core/background.d.ts +10 -3
  6. package/dist/core/background.js +55 -9
  7. package/dist/core/bootstrap.js +1 -0
  8. package/dist/core/compaction.js +1 -1
  9. package/dist/core/config-storage.d.ts +5 -0
  10. package/dist/core/config-storage.js +83 -0
  11. package/dist/core/config.d.ts +11 -2
  12. package/dist/core/config.js +24 -8
  13. package/dist/core/context-ledger.js +2 -1
  14. package/dist/core/context-policy.d.ts +1 -1
  15. package/dist/core/context-policy.js +10 -7
  16. package/dist/core/credentials.js +29 -30
  17. package/dist/core/effort.d.ts +1 -1
  18. package/dist/core/effort.js +5 -1
  19. package/dist/core/execution-guidance.d.ts +1 -1
  20. package/dist/core/execution-guidance.js +2 -0
  21. package/dist/core/file-coordination.d.ts +3 -0
  22. package/dist/core/file-coordination.js +28 -0
  23. package/dist/core/file-lease-store.d.ts +3 -0
  24. package/dist/core/file-lease-store.js +28 -12
  25. package/dist/core/fusion.d.ts +24 -0
  26. package/dist/core/fusion.js +80 -0
  27. package/dist/core/model-limits.d.ts +1 -0
  28. package/dist/core/model-limits.js +5 -1
  29. package/dist/core/permission-queue.js +15 -15
  30. package/dist/core/request-plan.js +4 -3
  31. package/dist/core/session-naming.d.ts +3 -0
  32. package/dist/core/session-naming.js +47 -0
  33. package/dist/core/session-usage.js +4 -1
  34. package/dist/core/session.d.ts +5 -2
  35. package/dist/core/session.js +22 -7
  36. package/dist/core/settings.d.ts +2 -0
  37. package/dist/core/settings.js +5 -16
  38. package/dist/core/step-policy.d.ts +18 -0
  39. package/dist/core/step-policy.js +6 -0
  40. package/dist/core/storage-health.d.ts +24 -0
  41. package/dist/core/storage-health.js +72 -0
  42. package/dist/core/tool-artifacts.d.ts +2 -0
  43. package/dist/core/tool-artifacts.js +21 -3
  44. package/dist/core/tool-result-index.js +29 -33
  45. package/dist/core/tool-result-store.js +6 -5
  46. package/dist/core/types.d.ts +26 -2
  47. package/dist/core/user-inbox.d.ts +9 -0
  48. package/dist/core/user-inbox.js +22 -3
  49. package/dist/core/user-input.js +1 -1
  50. package/dist/fusion/autopilot-fast.d.ts +59 -0
  51. package/dist/fusion/autopilot-fast.js +213 -0
  52. package/dist/fusion/autopilot.d.ts +75 -0
  53. package/dist/fusion/autopilot.js +308 -0
  54. package/dist/fusion/focus.d.ts +30 -0
  55. package/dist/fusion/focus.js +122 -0
  56. package/dist/fusion/gate.d.ts +30 -0
  57. package/dist/fusion/gate.js +95 -0
  58. package/dist/fusion/jev.d.ts +71 -0
  59. package/dist/fusion/jev.js +164 -0
  60. package/dist/fusion/receipts.d.ts +25 -0
  61. package/dist/fusion/receipts.js +34 -0
  62. package/dist/fusion/route.d.ts +25 -0
  63. package/dist/fusion/route.js +84 -0
  64. package/dist/fusion/run.d.ts +4 -0
  65. package/dist/fusion/run.js +186 -0
  66. package/dist/horizon/engine.d.ts +20 -1
  67. package/dist/horizon/engine.js +57 -0
  68. package/dist/horizon/index.d.ts +2 -2
  69. package/dist/horizon/index.js +1 -1
  70. package/dist/horizon/types.d.ts +29 -0
  71. package/dist/index.js +12 -0
  72. package/dist/providers/anthropic-oauth.d.ts +8 -3
  73. package/dist/providers/anthropic-oauth.js +13 -10
  74. package/dist/providers/anthropic.d.ts +9 -2
  75. package/dist/providers/anthropic.js +64 -21
  76. package/dist/providers/cli-models.d.ts +30 -0
  77. package/dist/providers/cli-models.js +194 -0
  78. package/dist/providers/model-effort.js +1 -1
  79. package/dist/providers/openai-oauth.d.ts +8 -3
  80. package/dist/providers/openai-oauth.js +9 -3
  81. package/dist/providers/openrouter-manual-fallback.d.ts +3 -1
  82. package/dist/providers/openrouter-manual-fallback.js +20 -20
  83. package/dist/providers/registry.js +3 -2
  84. package/dist/providers/retry.d.ts +3 -0
  85. package/dist/providers/retry.js +44 -1
  86. package/dist/remote/cifratura.d.ts +12 -0
  87. package/dist/remote/cifratura.js +66 -0
  88. package/dist/remote/index.d.ts +15 -0
  89. package/dist/remote/index.js +65 -0
  90. package/dist/remote/relay-client.d.ts +58 -0
  91. package/dist/remote/relay-client.js +315 -0
  92. package/dist/remote/sessione-remota.d.ts +60 -0
  93. package/dist/remote/sessione-remota.js +266 -0
  94. package/dist/sdk.d.ts +7 -2
  95. package/dist/sdk.js +3 -1
  96. package/dist/tools/builtin.js +41 -20
  97. package/dist/tools/capabilities.d.ts +3 -1
  98. package/dist/tools/capabilities.js +5 -2
  99. package/dist/tools/computer-use.js +2 -1
  100. package/dist/tools/pagination.js +1 -1
  101. package/dist/tools/program.js +34 -21
  102. package/dist/tools/team-tools.js +11 -6
  103. package/dist/tui/account-entry.js +14 -9
  104. package/dist/tui/agent-label.d.ts +2 -0
  105. package/dist/tui/agent-label.js +5 -0
  106. package/dist/tui/agent-sessions-screen.d.ts +35 -0
  107. package/dist/tui/agent-sessions-screen.js +178 -0
  108. package/dist/tui/agent-sessions.d.ts +53 -0
  109. package/dist/tui/agent-sessions.js +236 -0
  110. package/dist/tui/bottom-input.d.ts +57 -2
  111. package/dist/tui/bottom-input.js +449 -54
  112. package/dist/tui/branding.js +8 -4
  113. package/dist/tui/capability-lists.d.ts +10 -0
  114. package/dist/tui/capability-lists.js +41 -0
  115. package/dist/tui/command-output-registry.js +4 -0
  116. package/dist/tui/command-transcript.d.ts +1 -1
  117. package/dist/tui/command-transcript.js +17 -21
  118. package/dist/tui/commands.js +6 -2
  119. package/dist/tui/compaction-status.d.ts +7 -0
  120. package/dist/tui/compaction-status.js +39 -0
  121. package/dist/tui/config-recovery.d.ts +12 -0
  122. package/dist/tui/config-recovery.js +54 -0
  123. package/dist/tui/context-meter.d.ts +3 -0
  124. package/dist/tui/context-meter.js +35 -8
  125. package/dist/tui/diff.d.ts +9 -0
  126. package/dist/tui/diff.js +43 -19
  127. package/dist/tui/fusion-menu.d.ts +5 -0
  128. package/dist/tui/fusion-menu.js +89 -0
  129. package/dist/tui/i18n-catalog.js +351 -2
  130. package/dist/tui/live-command.d.ts +1 -0
  131. package/dist/tui/live-command.js +9 -0
  132. package/dist/tui/live-output.d.ts +1 -0
  133. package/dist/tui/live-output.js +11 -0
  134. package/dist/tui/logo.js +3 -0
  135. package/dist/tui/model-details.js +13 -12
  136. package/dist/tui/model-picker.d.ts +2 -0
  137. package/dist/tui/model-picker.js +59 -0
  138. package/dist/tui/model-selection.js +19 -10
  139. package/dist/tui/native-scrollback.d.ts +2 -1
  140. package/dist/tui/native-scrollback.js +31 -6
  141. package/dist/tui/prompt-queue-store.d.ts +1 -0
  142. package/dist/tui/prompt-queue-store.js +4 -0
  143. package/dist/tui/prompt-queue.d.ts +3 -0
  144. package/dist/tui/prompt-queue.js +19 -1
  145. package/dist/tui/provider-label.d.ts +2 -0
  146. package/dist/tui/provider-label.js +9 -0
  147. package/dist/tui/provider-menu.js +25 -24
  148. package/dist/tui/queue-screen.js +2 -1
  149. package/dist/tui/queued-questions.js +2 -4
  150. package/dist/tui/quick-help.d.ts +1 -1
  151. package/dist/tui/quick-help.js +3 -0
  152. package/dist/tui/remote-command.d.ts +7 -0
  153. package/dist/tui/remote-command.js +79 -0
  154. package/dist/tui/repl.d.ts +11 -2
  155. package/dist/tui/repl.js +457 -327
  156. package/dist/tui/review-store.d.ts +8 -1
  157. package/dist/tui/review-store.js +64 -11
  158. package/dist/tui/review-team.js +4 -3
  159. package/dist/tui/runtime-error.d.ts +2 -0
  160. package/dist/tui/runtime-error.js +26 -0
  161. package/dist/tui/scrollback-archive.d.ts +16 -0
  162. package/dist/tui/scrollback-archive.js +90 -0
  163. package/dist/tui/selector-mouse.d.ts +1 -0
  164. package/dist/tui/selector-mouse.js +1 -1
  165. package/dist/tui/selector.d.ts +11 -0
  166. package/dist/tui/selector.js +28 -4
  167. package/dist/tui/session-controls.d.ts +26 -0
  168. package/dist/tui/session-controls.js +65 -0
  169. package/dist/tui/session-naming-notice.d.ts +14 -0
  170. package/dist/tui/session-naming-notice.js +59 -0
  171. package/dist/tui/session-picker.d.ts +2 -0
  172. package/dist/tui/session-picker.js +32 -0
  173. package/dist/tui/session-replay.d.ts +1 -0
  174. package/dist/tui/session-replay.js +101 -32
  175. package/dist/tui/session-transcript-layout.d.ts +9 -0
  176. package/dist/tui/session-transcript-layout.js +41 -0
  177. package/dist/tui/startup.js +63 -33
  178. package/dist/tui/status-line.d.ts +1 -1
  179. package/dist/tui/status-line.js +20 -12
  180. package/dist/tui/storage-warning.d.ts +2 -0
  181. package/dist/tui/storage-warning.js +35 -0
  182. package/dist/tui/talos-art-data.d.ts +1 -0
  183. package/dist/tui/talos-art-data.js +2 -0
  184. package/dist/tui/talos-art.d.ts +1 -0
  185. package/dist/tui/talos-art.js +9 -0
  186. package/dist/tui/task-labels.d.ts +1 -0
  187. package/dist/tui/task-labels.js +8 -0
  188. package/dist/tui/terminal-text.d.ts +2 -1
  189. package/dist/tui/terminal-text.js +28 -2
  190. package/dist/tui/theme.js +2 -2
  191. package/dist/tui/tool-presentation.d.ts +3 -0
  192. package/dist/tui/tool-presentation.js +98 -0
  193. package/dist/tui/tool-style.js +4 -0
  194. package/dist/tui/transcript-store.d.ts +25 -0
  195. package/dist/tui/transcript-store.js +151 -0
  196. package/dist/tui/usage-footer.d.ts +1 -1
  197. package/dist/tui/usage-footer.js +3 -1
  198. package/dist/tui/vascend-id.d.ts +49 -0
  199. package/dist/tui/vascend-id.js +321 -0
  200. package/dist/tui/web-login-screen.js +4 -3
  201. package/dist/tui/web-login.d.ts +7 -15
  202. package/dist/tui/web-login.js +38 -78
  203. package/dist/tui/worker-transcripts.d.ts +31 -0
  204. package/dist/tui/worker-transcripts.js +91 -0
  205. package/dist/vascend/candidate-collector.d.ts +6 -5
  206. package/dist/vascend/candidate-collector.js +44 -43
  207. package/dist/vascend/context-feedback-loop.js +2 -0
  208. package/dist/vascend/context-lint.js +32 -2
  209. package/dist/vascend/context-market-selection.js +12 -5
  210. package/dist/vascend/context-pack-render.js +10 -4
  211. package/dist/vascend/context-packet-mirrors.d.ts +8 -0
  212. package/dist/vascend/context-packet-mirrors.js +24 -0
  213. package/dist/vascend/context-plan-events.d.ts +4 -0
  214. package/dist/vascend/context-plan-events.js +44 -0
  215. package/dist/vascend/context-snapshot.d.ts +1 -0
  216. package/dist/vascend/context-snapshot.js +8 -5
  217. package/dist/vascend/context-store-reader.d.ts +20 -0
  218. package/dist/vascend/context-store-reader.js +46 -0
  219. package/dist/vascend/context-window-compiler.d.ts +1 -0
  220. package/dist/vascend/context-window-compiler.js +1 -0
  221. package/dist/vascend/file-lock.d.ts +1 -0
  222. package/dist/vascend/file-lock.js +26 -0
  223. package/dist/vascend/granular-kingdom.d.ts +1 -0
  224. package/dist/vascend/granular-kingdom.js +52 -17
  225. package/dist/vascend/kingdom-bus.d.ts +1 -0
  226. package/dist/vascend/kingdom-bus.js +3 -0
  227. package/dist/vascend/kingdom-channel.d.ts +2 -0
  228. package/dist/vascend/kingdom-channel.js +3 -0
  229. package/dist/vascend/kingdom-live.d.ts +32 -4
  230. package/dist/vascend/kingdom-live.js +149 -13
  231. package/dist/vascend/kingdom-run.d.ts +5 -1
  232. package/dist/vascend/kingdom-run.js +6 -2
  233. package/dist/vascend/kingdom.d.ts +3 -0
  234. package/dist/vascend/kingdom.js +22 -11
  235. package/dist/vascend/memory-packet.d.ts +2 -1
  236. package/dist/vascend/memory-packet.js +31 -44
  237. package/dist/vascend/memory-store.d.ts +1 -0
  238. package/dist/vascend/memory-store.js +12 -4
  239. package/dist/vascend/plan-store.d.ts +3 -1
  240. package/dist/vascend/plan-store.js +6 -4
  241. package/dist/vascend/room-memory.d.ts +13 -2
  242. package/dist/vascend/room-memory.js +48 -6
  243. package/dist/vascend/runtime.d.ts +3 -11
  244. package/dist/vascend/runtime.js +11 -123
  245. package/dist/vascend/session-search.js +2 -0
  246. package/dist/vascend/team-session.js +3 -2
  247. package/dist/vascend/vision.d.ts +3 -1
  248. package/dist/vascend/vision.js +6 -4
  249. package/dist/vascend/worker-session.d.ts +20 -0
  250. package/dist/vascend/worker-session.js +83 -0
  251. package/package.json +2 -2
@@ -0,0 +1,194 @@
1
+ import { spawn } from "node:child_process";
2
+ import { existsSync, mkdirSync, readFileSync, realpathSync, statSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { delimiter, dirname, isAbsolute, join } from "node:path";
5
+ import { talosConfigDir } from "../core/env-paths.js";
6
+ const QUERY_TIMEOUT_MS = 20_000;
7
+ function cachePath(env) {
8
+ if (env["TALOS_NO_GLOBAL_CONFIG"] === "1" || env["TALOS_DISABLE_CLI_MODELS"] === "1")
9
+ return null;
10
+ return join(talosConfigDir(env), "cli-models.json");
11
+ }
12
+ /** Primo CLI nel PATH con l'impronta del binario reale (symlink risolti), o null se non installato. */
13
+ export function findCli(name, env) {
14
+ const names = process.platform === "win32" ? [`${name}.exe`, `${name}.cmd`] : [name];
15
+ for (const directory of String(env["PATH"] ?? env["Path"] ?? "").split(delimiter)) {
16
+ if (!directory || !isAbsolute(directory))
17
+ continue;
18
+ for (const file of names) {
19
+ const candidate = join(directory, file);
20
+ if (!existsSync(candidate))
21
+ continue;
22
+ try {
23
+ const real = realpathSync(candidate);
24
+ return { executable: candidate, stamp: `${real}@${statSync(real).mtimeMs}` };
25
+ }
26
+ catch {
27
+ // Voce del PATH non leggibile: si prova la successiva.
28
+ }
29
+ }
30
+ }
31
+ return null;
32
+ }
33
+ function readCache(env) {
34
+ const path = cachePath(env);
35
+ if (!path || !existsSync(path))
36
+ return {};
37
+ try {
38
+ const raw = JSON.parse(readFileSync(path, "utf8"));
39
+ return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
40
+ }
41
+ catch {
42
+ return {};
43
+ }
44
+ }
45
+ /** Modelli in cache per il CLI installato ora, o undefined se assenti o superati da un aggiornamento. */
46
+ export function cachedCliModels(name, env) {
47
+ if (!cachePath(env))
48
+ return undefined;
49
+ const cli = findCli(name, env);
50
+ const entry = readCache(env)[name];
51
+ if (!cli || !entry || entry.stamp !== cli.stamp)
52
+ return undefined;
53
+ const models = Array.isArray(entry.models) ? entry.models.filter((model) => typeof model?.id === "string" && model.id && typeof model.label === "string") : [];
54
+ return models.length ? models : undefined;
55
+ }
56
+ /**
57
+ * Righe di `initialize` del CLI Claude Code. Gli alias (default, opus, sonnet)
58
+ * diventano il modello versionato che eseguono: una voce per modello reale,
59
+ * nell'ordine del CLI.
60
+ */
61
+ export function claudeModelsFromInitialize(models) {
62
+ const out = new Map();
63
+ for (const raw of Array.isArray(models) ? models : []) {
64
+ const row = raw;
65
+ if (row["value"] === "default")
66
+ continue;
67
+ const id = String(row["resolvedModel"] || row["value"] || "").trim();
68
+ if (!id || out.has(id))
69
+ continue;
70
+ const label = String(row["displayName"] || id).trim();
71
+ const description = String(row["description"] ?? "").trim();
72
+ out.set(id, { id, label, ...(description ? { description } : {}) });
73
+ }
74
+ return [...out.values()];
75
+ }
76
+ /** Righe di `model/list` dell'app-server Codex, senza i modelli nascosti. */
77
+ export function codexModelsFromList(data) {
78
+ const out = new Map();
79
+ for (const raw of Array.isArray(data) ? data : []) {
80
+ const row = raw;
81
+ if (row["hidden"] === true)
82
+ continue;
83
+ const id = String(row["model"] || row["id"] || "").trim();
84
+ if (!id || out.has(id))
85
+ continue;
86
+ const label = String(row["displayName"] || id).trim();
87
+ const description = String(row["description"] ?? "").trim();
88
+ out.set(id, { id, label, ...(description ? { description } : {}) });
89
+ }
90
+ return [...out.values()];
91
+ }
92
+ // Avvia il CLI, scrive le richieste JSONL e attende la prima riga che `pick`
93
+ // riconosce. Il processo viene sempre terminato, anche su timeout o errore.
94
+ function queryCli(executable, args, requests, pick, env, timeoutMs) {
95
+ return new Promise((resolve, reject) => {
96
+ const child = spawn(executable, [...args], {
97
+ env, cwd: tmpdir(), stdio: ["pipe", "pipe", "ignore"], windowsHide: true,
98
+ shell: process.platform === "win32" && executable.toLowerCase().endsWith(".cmd"),
99
+ });
100
+ let buffer = "";
101
+ let settled = false;
102
+ const finish = (error, value) => {
103
+ if (settled)
104
+ return;
105
+ settled = true;
106
+ clearTimeout(timer);
107
+ child.kill();
108
+ if (error)
109
+ reject(error);
110
+ else
111
+ resolve(value);
112
+ };
113
+ const timer = setTimeout(() => finish(new Error(`${executable} did not answer in time`)), timeoutMs);
114
+ child.on("error", (error) => finish(error));
115
+ child.on("close", () => finish(new Error(`${executable} exited before answering`)));
116
+ child.stdout.setEncoding("utf8");
117
+ child.stdout.on("data", (chunk) => {
118
+ buffer += chunk;
119
+ let newline;
120
+ while ((newline = buffer.indexOf("\n")) >= 0) {
121
+ const line = buffer.slice(0, newline).trim();
122
+ buffer = buffer.slice(newline + 1);
123
+ if (!line.startsWith("{"))
124
+ continue;
125
+ try {
126
+ const value = pick(JSON.parse(line));
127
+ if (value !== undefined)
128
+ finish(null, value);
129
+ }
130
+ catch {
131
+ // Righe non JSON (log) ignorate.
132
+ }
133
+ }
134
+ });
135
+ child.stdin.on("error", () => { });
136
+ child.stdin.write(requests.map((request) => JSON.stringify(request)).join("\n") + "\n");
137
+ });
138
+ }
139
+ async function queryClaudeModels(executable, env, timeoutMs) {
140
+ const response = await queryCli(executable, ["-p", "--input-format", "stream-json", "--output-format", "stream-json", "--verbose"], [{ type: "control_request", request_id: "talos-models", request: { subtype: "initialize" } }], (message) => {
141
+ const body = message["response"];
142
+ return message["type"] === "control_response" && body?.["request_id"] === "talos-models" ? body : undefined;
143
+ }, env, timeoutMs);
144
+ if (response["subtype"] !== "success")
145
+ throw new Error(String(response["error"] ?? "Claude Code initialize failed"));
146
+ return claudeModelsFromInitialize(response["response"]?.["models"]);
147
+ }
148
+ async function queryCodexModels(executable, env, timeoutMs) {
149
+ const response = await queryCli(executable, ["app-server"], [
150
+ { id: 1, method: "initialize", params: { clientInfo: { name: "talos", version: "1" } } },
151
+ { method: "initialized" },
152
+ { id: 2, method: "model/list", params: {} },
153
+ ], (message) => message["id"] === 2 ? message : undefined, env, timeoutMs);
154
+ if (response["error"])
155
+ throw new Error(JSON.stringify(response["error"]));
156
+ return codexModelsFromList(response["result"]?.["data"]);
157
+ }
158
+ /**
159
+ * Rilegge i modelli dai CLI installati la cui cache manca o e' superata, e la
160
+ * salva. Un CLI assente, lento o in errore lascia la cache com'e': il catalogo
161
+ * ripiega su quello statico.
162
+ */
163
+ export async function refreshCliModels(env, options = {}) {
164
+ const path = cachePath(env);
165
+ if (!path)
166
+ return;
167
+ const timeoutMs = options.timeoutMs ?? QUERY_TIMEOUT_MS;
168
+ const queries = { claude: queryClaudeModels, codex: queryCodexModels };
169
+ const updates = await Promise.all(Object.keys(queries).map(async (name) => {
170
+ if (!options.force && cachedCliModels(name, env))
171
+ return null;
172
+ const cli = findCli(name, env);
173
+ if (!cli)
174
+ return null;
175
+ try {
176
+ const models = await queries[name](cli.executable, env, timeoutMs);
177
+ return models.length ? [name, { stamp: cli.stamp, models }] : null;
178
+ }
179
+ catch {
180
+ return null;
181
+ }
182
+ }));
183
+ const fresh = updates.filter((update) => update !== null);
184
+ if (!fresh.length)
185
+ return;
186
+ const cache = { ...readCache(env), ...Object.fromEntries(fresh) };
187
+ try {
188
+ mkdirSync(dirname(path), { recursive: true });
189
+ writeFileSync(path, JSON.stringify(cache, null, 2) + "\n", { mode: 0o600 });
190
+ }
191
+ catch {
192
+ // Cache non scrivibile: il catalogo resta quello statico.
193
+ }
194
+ }
@@ -26,7 +26,7 @@ export function modelEfforts(fullModel, metadata = modelCapabilities(fullModel))
26
26
  return ["", "low", "medium", "high"];
27
27
  }
28
28
  if (provider === "openai") {
29
- if (/gpt-6-astra/.test(id))
29
+ if (/gpt-6/.test(id))
30
30
  return ["", "low", "medium", "high", "xhigh", "max"];
31
31
  if (/gpt-5\.[456]/.test(id))
32
32
  return ["", "low", "medium", "high", "xhigh"];
@@ -1,3 +1,4 @@
1
+ import type { CliModel } from "./cli-models.js";
1
2
  import type { CompletionRequest, ModelTurn, Provider } from "../core/types.js";
2
3
  import { type OAuthCreds } from "./oauth-store.js";
3
4
  export declare class OpenAIOAuthProvider implements Provider {
@@ -5,10 +6,14 @@ export declare class OpenAIOAuthProvider implements Provider {
5
6
  private readonly env;
6
7
  private readonly now;
7
8
  readonly id = "chatgpt";
8
- readonly label = "ChatGPT (abbonamento)";
9
- readonly models: readonly ["gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna", "gpt-6-astra", "gpt-5-codex", "gpt-5", "gpt-5-mini", "o4-mini"];
9
+ readonly label = "ChatGPT";
10
+ readonly models: readonly string[];
11
+ readonly modelLabels?: ReadonlyMap<string, {
12
+ readonly label: string;
13
+ readonly description?: string;
14
+ }>;
10
15
  readonly supportsFastMode = true;
11
- constructor(creds: OAuthCreds, env: NodeJS.ProcessEnv, now?: () => number);
16
+ constructor(creds: OAuthCreds, env: NodeJS.ProcessEnv, now?: () => number, cliModels?: readonly CliModel[]);
12
17
  private ensureToken;
13
18
  complete(req: CompletionRequest): Promise<ModelTurn>;
14
19
  }
@@ -105,13 +105,19 @@ export class OpenAIOAuthProvider {
105
105
  env;
106
106
  now;
107
107
  id = "chatgpt";
108
- label = "ChatGPT (abbonamento)";
109
- models = CHATGPT_MODELS;
108
+ label = "ChatGPT";
109
+ models;
110
+ modelLabels;
110
111
  supportsFastMode = true;
111
- constructor(creds, env, now = () => Date.now()) {
112
+ constructor(creds, env, now = () => Date.now(),
113
+ // Modelli del CLI ufficiale installato; senza, il catalogo statico.
114
+ cliModels) {
112
115
  this.creds = creds;
113
116
  this.env = env;
114
117
  this.now = now;
118
+ this.models = cliModels?.length ? cliModels.map((model) => model.id) : CHATGPT_MODELS;
119
+ if (cliModels?.length)
120
+ this.modelLabels = new Map(cliModels.map((model) => [model.id, model]));
115
121
  }
116
122
  async ensureToken(signal) {
117
123
  if (!isExpired(this.creds, this.now()) || !this.creds.refreshToken) {
@@ -1,16 +1,18 @@
1
1
  import type { OAuthFlow } from "./openrouter-auth.js";
2
+ export type ManualTranslator = (italian: string, english: string, values?: Readonly<Record<string, string | number>>) => string;
2
3
  export declare function normalizeAuthCode(raw: string): string;
3
4
  export declare function isLikelyAuthCode(value: string): boolean;
4
5
  export interface ManualInstructions {
5
6
  readonly lines: readonly string[];
6
7
  readonly authUrl: string;
7
8
  }
8
- export declare function buildManualInstructions(authUrl: string, reason?: string): ManualInstructions;
9
+ export declare function buildManualInstructions(authUrl: string, reason?: string, tr?: ManualTranslator): ManualInstructions;
9
10
  export interface ManualFallbackIo {
10
11
  print(line: string): void;
11
12
  prompt(label: string): Promise<string | null>;
12
13
  }
13
14
  export interface ManualFallbackOptions {
15
+ readonly translate?: ManualTranslator;
14
16
  readonly reason?: string;
15
17
  readonly maxAttempts?: number;
16
18
  readonly signal?: AbortSignal;
@@ -13,6 +13,7 @@
13
13
  // (niente accesso diretto a stdin/stdout/console), e lo scambio del code arriva
14
14
  // dall'OAuthFlow.complete gia' fornito da openrouter-auth.ts. Nessun side effect
15
15
  // nascosto, niente dipendenze a runtime.
16
+ const defaultTranslation = (italian, _english, values) => italian.replace(/\{(\w+)\}/g, (match, key) => String(values?.[key] ?? match));
16
17
  // ---------------------------------------------------------------------------
17
18
  // Validazione / normalizzazione del code incollato
18
19
  // ---------------------------------------------------------------------------
@@ -91,26 +92,24 @@ export function isLikelyAuthCode(value) {
91
92
  // Costruisce le istruzioni per il percorso manuale. `reason` (opzionale) spiega
92
93
  // perche' siamo qui (es. "porta 8976 occupata", "nessun browser disponibile"):
93
94
  // se assente usiamo un messaggio neutro.
94
- export function buildManualInstructions(authUrl, reason) {
95
+ export function buildManualInstructions(authUrl, reason, tr = defaultTranslation) {
95
96
  const why = (reason ?? "").trim();
96
97
  const lines = [];
97
- lines.push("Login OpenRouter — modalita' manuale");
98
+ lines.push(tr("Login OpenRouter — modalita' manuale", "OpenRouter sign-in — manual mode"));
98
99
  if (why) {
99
- lines.push(`(callback automatico non disponibile: ${why})`);
100
+ lines.push(tr("(callback automatico non disponibile: {reason})", "(automatic callback unavailable: {reason})", { reason: why }));
100
101
  }
101
102
  else {
102
- lines.push("(callback automatico non disponibile: completiamo a mano)");
103
+ lines.push(tr("(callback automatico non disponibile: completiamo a mano)", "(automatic callback unavailable: complete sign-in manually)"));
103
104
  }
104
105
  lines.push("");
105
- lines.push("1. Apri questo link nel browser (copialo se non e' cliccabile):");
106
+ lines.push(tr("1. Apri questo link nel browser (copialo se non e' cliccabile):", "1. Open this link in the browser (copy it if it is not clickable):"));
106
107
  lines.push(` ${authUrl}`);
107
108
  lines.push("");
108
- lines.push("2. Autorizza l'accesso su OpenRouter.");
109
- lines.push("3. Verrai rediretto a un indirizzo localhost che potrebbe non");
110
- lines.push(" aprirsi (\"impossibile raggiungere il sito\"): e' normale.");
111
- lines.push("4. Copia il valore del parametro `code` dalla barra degli indirizzi");
112
- lines.push(" (oppure incolla l'intero URL: estraggo io il code).");
113
- lines.push("5. Incollalo qui sotto e premi Invio.");
109
+ lines.push(tr("2. Autorizza l'accesso su OpenRouter.", "2. Authorize access on OpenRouter."));
110
+ lines.push(tr("3. Verrai rediretto a un indirizzo localhost che potrebbe non aprirsi: e' normale.", "3. You will be redirected to a localhost address that may not open: this is normal."));
111
+ lines.push(tr("4. Copia il parametro `code` dalla barra degli indirizzi (oppure incolla l'intero URL).", "4. Copy the `code` parameter from the address bar (or paste the entire URL)."));
112
+ lines.push(tr("5. Incollalo qui sotto e premi Invio.", "5. Paste it below and press Enter."));
114
113
  return { lines, authUrl };
115
114
  }
116
115
  // Esegue il percorso di fallback manuale: mostra link+istruzioni, chiede il
@@ -118,15 +117,16 @@ export function buildManualInstructions(authUrl, reason) {
118
117
  // input invalido o scambio fallito, fino a maxAttempts o annullamento.
119
118
  export async function runManualCodeFallback(io, flow, opts = {}) {
120
119
  const maxAttempts = Math.max(1, Math.floor(opts.maxAttempts ?? 3));
121
- const instructions = buildManualInstructions(flow.authUrl, opts.reason);
120
+ const tr = opts.translate ?? defaultTranslation;
121
+ const instructions = buildManualInstructions(flow.authUrl, opts.reason, tr);
122
122
  for (const line of instructions.lines)
123
123
  io.print(line);
124
124
  let attempts = 0;
125
125
  let lastError = "";
126
126
  while (attempts < maxAttempts) {
127
127
  const remaining = maxAttempts - attempts;
128
- const hint = attempts === 0 ? "" : ` (tentativi rimasti: ${remaining})`;
129
- const raw = await io.prompt(`Incolla il code${hint}`);
128
+ const hint = attempts === 0 ? "" : tr(" (tentativi rimasti: {count})", " (attempts remaining: {count})", { count: remaining });
129
+ const raw = await io.prompt(tr("Incolla il code{hint}", "Paste the code{hint}", { hint }));
130
130
  // Annullamento esplicito (Esc/EOF): usciamo subito senza consumare logica.
131
131
  if (raw === null) {
132
132
  return { ok: false, error: "cancelled", attempts };
@@ -134,9 +134,9 @@ export async function runManualCodeFallback(io, flow, opts = {}) {
134
134
  attempts += 1;
135
135
  const code = normalizeAuthCode(raw);
136
136
  if (!isLikelyAuthCode(code)) {
137
- lastError = "code non riconosciuto";
137
+ lastError = tr("code non riconosciuto", "unrecognized code");
138
138
  if (attempts < maxAttempts) {
139
- io.print("Input non valido: incolla il code (o l'intero URL del callback). Riprova.");
139
+ io.print(tr("Input non valido: incolla il code (o l'intero URL del callback). Riprova.", "Invalid input: paste the code (or the entire callback URL). Try again."));
140
140
  }
141
141
  continue;
142
142
  }
@@ -148,14 +148,14 @@ export async function runManualCodeFallback(io, flow, opts = {}) {
148
148
  result = { ok: false, error: err instanceof Error ? err.message : String(err) };
149
149
  }
150
150
  if (result.ok && result.apiKey) {
151
- io.print("Autenticazione completata: API key ottenuta e salvata.");
151
+ io.print(tr("Autenticazione completata: API key ottenuta e salvata.", "Authentication complete: API key obtained and saved."));
152
152
  return { ok: true, apiKey: result.apiKey, attempts };
153
153
  }
154
- lastError = result.error ?? "scambio fallito";
154
+ lastError = result.error ?? tr("scambio fallito", "exchange failed");
155
155
  if (attempts < maxAttempts) {
156
- io.print(`Scambio fallito: ${lastError}. Verifica di aver copiato il code giusto e riprova.`);
156
+ io.print(tr("Scambio fallito: {error}. Verifica di aver copiato il code giusto e riprova.", "Exchange failed: {error}. Check that you copied the correct code and try again.", { error: lastError }));
157
157
  }
158
158
  }
159
- io.print("Tentativi esauriti: login manuale non riuscito.");
159
+ io.print(tr("Tentativi esauriti: login manuale non riuscito.", "No attempts left: manual sign-in failed."));
160
160
  return { ok: false, error: lastError || "exhausted", attempts };
161
161
  }
@@ -3,6 +3,7 @@ import { AnthropicOAuthProvider } from "./anthropic-oauth.js";
3
3
  import { OpenAIOAuthProvider } from "./openai-oauth.js";
4
4
  import { OpenAICompatProvider } from "./openai-compat.js";
5
5
  import { readClaudeCreds, readCodexCreds } from "./oauth-store.js";
6
+ import { cachedCliModels } from "./cli-models.js";
6
7
  import { readOpenRouterCreds } from "./openrouter-auth.js";
7
8
  import { PROVIDER_CATALOG } from "./catalog.js";
8
9
  import { retryConfigFromEnv, withRetry } from "./retry.js";
@@ -202,11 +203,11 @@ export function defaultRegistry(env, opts = {}) {
202
203
  if (opts.detectOAuth !== false) {
203
204
  const claude = readClaudeCreds(env);
204
205
  if (claude)
205
- add(new AnthropicOAuthProvider(claude, env));
206
+ add(new AnthropicOAuthProvider(claude, env, undefined, cachedCliModels("claude", env)));
206
207
  if (env["TALOS_DISABLE_CHATGPT_OAUTH"] !== "1") {
207
208
  const codex = readCodexCreds(env);
208
209
  if (codex)
209
- add(new OpenAIOAuthProvider(codex, env));
210
+ add(new OpenAIOAuthProvider(codex, env, undefined, cachedCliModels("codex", env)));
210
211
  }
211
212
  }
212
213
  return reg;
@@ -1,5 +1,6 @@
1
1
  import type { CompletionRequest, ModelTurn, PlanLimits, Provider } from "../core/types.js";
2
2
  export interface RetryConfig {
3
+ readonly autoResumeSession?: boolean;
3
4
  readonly maxRetries: number;
4
5
  readonly baseDelayMs: number;
5
6
  readonly timeoutMs: number;
@@ -25,12 +26,14 @@ export declare class RetryProvider implements Provider {
25
26
  get id(): string;
26
27
  get label(): string | undefined;
27
28
  get models(): readonly string[];
29
+ get modelLabels(): Provider["modelLabels"];
28
30
  get supportsFastMode(): boolean;
29
31
  private get netMax();
30
32
  private get netDelay();
31
33
  private get overloadMaxAttempts();
32
34
  private overloadDelay;
33
35
  private waitForOverload;
36
+ private waitForSessionReset;
34
37
  complete(req: CompletionRequest): Promise<ModelTurn>;
35
38
  private backoff;
36
39
  }
@@ -194,6 +194,9 @@ export class RetryProvider {
194
194
  get models() {
195
195
  return this.inner.models;
196
196
  }
197
+ get modelLabels() {
198
+ return this.inner.modelLabels;
199
+ }
197
200
  get supportsFastMode() { return this.inner.supportsFastMode === true; }
198
201
  get netMax() {
199
202
  return this.cfg.networkMaxRetries ?? 0;
@@ -218,6 +221,31 @@ export class RetryProvider {
218
221
  req.onActivity?.(`Modello sovraccarico · nuovo tentativo (${nextAttempt}/${this.overloadMaxAttempts}) · ${seconds}s`);
219
222
  });
220
223
  }
224
+ async waitForSessionReset(req, limits) {
225
+ if (this.cfg.autoResumeSession === false)
226
+ return false;
227
+ const rejected = rejectedQuotaWindows(limits);
228
+ const resetAt = quotaResetAt(limits);
229
+ // Never guess a reset or wait on credit/weekly exhaustion. Every rejected
230
+ // window must be the session window, with a future reset supplied by the provider.
231
+ const session = rejected.find(w => w.id === "5h");
232
+ // Claude also rejects the aggregate unified window when its 5h window
233
+ // is exhausted. It is not an independent weekly/credit limit.
234
+ if (!session || rejected.some(w => w.id !== "5h" && w.id !== "unified") || resetAt === undefined || resetAt <= Date.now())
235
+ return false;
236
+ if (req.signal.aborted)
237
+ return true;
238
+ const zone = Intl.DateTimeFormat().resolvedOptions().timeZone;
239
+ const time = new Date(resetAt).toLocaleTimeString("it-IT", { hour: "2-digit", minute: "2-digit", timeZone: zone });
240
+ req.onActivity?.(`Limite sessione di 5 ore raggiunto · reset alle ${time} (${zone}) · riprendo automaticamente al reset · Esc per annullare`);
241
+ // Short slices avoid timer overflow and remain responsive to clock changes.
242
+ while (!req.signal.aborted && Date.now() < resetAt) {
243
+ await delay(Math.min(60_000, resetAt - Date.now()), req.signal);
244
+ }
245
+ if (!req.signal.aborted)
246
+ req.onActivity?.("Limite sessione reimpostato · riprendo automaticamente il lavoro");
247
+ return true;
248
+ }
221
249
  async complete(req) {
222
250
  let netAttempt = 0;
223
251
  let genAttempt = 0;
@@ -248,13 +276,26 @@ export class RetryProvider {
248
276
  delivered = true;
249
277
  req.onToolDelta(event);
250
278
  } } : {}),
279
+ ...(req.onReasoning ? { onReasoning: (chunk) => {
280
+ if (!active || tm.signal.aborted)
281
+ return;
282
+ if (chunk)
283
+ delivered = true;
284
+ req.onReasoning(chunk);
285
+ } } : {}),
286
+ ...(req.onActivity ? { onActivity: (label) => {
287
+ if (active && !tm.signal.aborted)
288
+ req.onActivity(label);
289
+ } } : {}),
251
290
  });
252
291
  active = false;
253
292
  tm.cleanup();
254
293
  if (result.stopReason === "error") {
255
- // 1. Limite/quota abbonamento: stop, messaggio chiaro (no retry).
294
+ // 1. Sessione 5h: ripresa al reset solo se lo stream non e' iniziato.
256
295
  if (isQuotaError(result.text) || rejectedQuotaWindows(result.limits).length > 0) {
257
296
  const limits = quotaLimitsFromError(this.id, result.text, result.limits);
297
+ if (!delivered && await this.waitForSessionReset(req, limits))
298
+ continue;
258
299
  return { ...result, toolCalls: [], text: quotaMessage(this.id, result.text, limits), ...(limits ? { limits } : {}) };
259
300
  }
260
301
  if (delivered)
@@ -301,6 +342,8 @@ export class RetryProvider {
301
342
  const thrownText = err instanceof Error ? `${err.name}: ${err.message}` : String(err);
302
343
  if (!userAbort && isQuotaError(thrownText)) {
303
344
  const limits = quotaLimitsFromError(this.id, thrownText);
345
+ if (!delivered && await this.waitForSessionReset(req, limits))
346
+ continue;
304
347
  return { text: quotaMessage(this.id, thrownText, limits), toolCalls: [], stopReason: "error", ...(limits ? { limits } : {}) };
305
348
  }
306
349
  if (delivered)
@@ -0,0 +1,12 @@
1
+ export type Direzione = "ev" | "cmd";
2
+ export declare class CifraturaError extends Error {
3
+ readonly code: "chiave" | "corpo" | "autenticazione";
4
+ constructor(code: "chiave" | "corpo" | "autenticazione");
5
+ }
6
+ export declare function nuovaChiave(): Promise<{
7
+ readonly chiave: CryptoKey;
8
+ readonly testo: string;
9
+ }>;
10
+ export declare function importaChiave(testo: string): Promise<CryptoKey>;
11
+ export declare function cifra(chiave: CryptoKey, sessione: string, direzione: Direzione, dati: unknown): Promise<string>;
12
+ export declare function decifra(chiave: CryptoKey, sessione: string, direzione: Direzione, b: string): Promise<unknown>;
@@ -0,0 +1,66 @@
1
+ // Cifratura fra Talos e i dispositivi abbinati (telefono, web): AES-256-GCM.
2
+ //
3
+ // La chiave nasce qui, una per sessione, e arriva al telefono solo dentro il
4
+ // link di abbinamento, dopo il "#": il browser non manda mai quella parte a
5
+ // nessun server. Relay, Redis, Kafka e Flink vedono solo testo cifrato.
6
+ //
7
+ // Formato del campo `b`: base64url(iv 12 byte || testo cifrato con tag).
8
+ // I dati aggiuntivi autenticati legano ogni messaggio alla sessione e alla
9
+ // direzione: il relay non puo' spostare un evento in un'altra sessione ne'
10
+ // far passare un evento di Talos per un comando del telefono.
11
+ const TE = new TextEncoder();
12
+ const TD = new TextDecoder();
13
+ const VERSIONE = "vascend-relay/1";
14
+ export class CifraturaError extends Error {
15
+ code;
16
+ constructor(code) {
17
+ super(code);
18
+ this.code = code;
19
+ }
20
+ }
21
+ function subtle() {
22
+ const s = globalThis.crypto?.subtle;
23
+ if (!s)
24
+ throw new CifraturaError("chiave");
25
+ return s;
26
+ }
27
+ function aad(sessione, direzione) {
28
+ return TE.encode(`${VERSIONE}|${sessione}|${direzione}`);
29
+ }
30
+ // Chiave nuova e la sua forma da mettere nel link (base64url, 43 caratteri).
31
+ export async function nuovaChiave() {
32
+ const grezza = globalThis.crypto.getRandomValues(new Uint8Array(32));
33
+ const chiave = await subtle().importKey("raw", grezza, { name: "AES-GCM" }, false, ["encrypt", "decrypt"]);
34
+ return { chiave, testo: Buffer.from(grezza).toString("base64url") };
35
+ }
36
+ export async function importaChiave(testo) {
37
+ if (!/^[A-Za-z0-9_-]{43}$/.test(testo))
38
+ throw new CifraturaError("chiave");
39
+ return subtle().importKey("raw", Buffer.from(testo, "base64url"), { name: "AES-GCM" }, false, ["encrypt", "decrypt"]);
40
+ }
41
+ export async function cifra(chiave, sessione, direzione, dati) {
42
+ const iv = globalThis.crypto.getRandomValues(new Uint8Array(12));
43
+ const ct = new Uint8Array(await subtle().encrypt({ name: "AES-GCM", iv, additionalData: aad(sessione, direzione) }, chiave, TE.encode(JSON.stringify(dati))));
44
+ const out = new Uint8Array(12 + ct.length);
45
+ out.set(iv);
46
+ out.set(ct, 12);
47
+ return Buffer.from(out).toString("base64url");
48
+ }
49
+ export async function decifra(chiave, sessione, direzione, b) {
50
+ if (typeof b !== "string" || b.length < 40 || !/^[A-Za-z0-9_-]+$/.test(b))
51
+ throw new CifraturaError("corpo");
52
+ const raw = Buffer.from(b, "base64url");
53
+ let chiaro;
54
+ try {
55
+ chiaro = await subtle().decrypt({ name: "AES-GCM", iv: raw.subarray(0, 12), additionalData: aad(sessione, direzione) }, chiave, raw.subarray(12));
56
+ }
57
+ catch {
58
+ throw new CifraturaError("autenticazione");
59
+ }
60
+ try {
61
+ return JSON.parse(TD.decode(chiaro));
62
+ }
63
+ catch {
64
+ throw new CifraturaError("corpo");
65
+ }
66
+ }
@@ -0,0 +1,15 @@
1
+ import type { AgentEvent } from "../core/types.js";
2
+ import { type StatoRelay } from "./relay-client.js";
3
+ import { SessioneRemota, type ManiRemote } from "./sessione-remota.js";
4
+ export { RelayError } from "./relay-client.js";
5
+ export type { StatoRelay } from "./relay-client.js";
6
+ export declare function collegaMani(m: ManiRemote | null): void;
7
+ export declare function sessioneRemota(): SessioneRemota | null;
8
+ export declare function avviaRemoto(env: NodeJS.ProcessEnv, cwd: string, eventi?: {
9
+ readonly onStato?: (stato: StatoRelay, motivo?: string) => void;
10
+ readonly onPresenza?: (n: number) => void;
11
+ }): Promise<SessioneRemota>;
12
+ export declare function fermaRemoto(): Promise<boolean>;
13
+ export declare function osservaEvento(event: AgentEvent): void;
14
+ export declare function messaggioUtente(testo: string): void;
15
+ export declare function permessoConGara(azione: string, dettaglio: string, signal: AbortSignal, tui: (signal: AbortSignal) => Promise<boolean>): Promise<boolean>;
@@ -0,0 +1,65 @@
1
+ // Controllo remoto di Talos: una sessione per processo, accesa con /remote.
2
+ // La TUI chiama queste funzioni nei suoi punti di passaggio (eventi
3
+ // dell'agente, messaggi dell'utente, permessi): a controllo spento non fanno
4
+ // niente e non costano niente.
5
+ import { RELAY_RESOURCE, tokenRisorsa } from "../tui/vascend-id.js";
6
+ import { RelayError, relayOf } from "./relay-client.js";
7
+ import { SessioneRemota } from "./sessione-remota.js";
8
+ export { RelayError } from "./relay-client.js";
9
+ let mani = null;
10
+ let attiva = null;
11
+ let avvio = null;
12
+ // La TUI si presenta una volta: senza mani (input classico) il remoto non parte.
13
+ export function collegaMani(m) { mani = m; }
14
+ export function sessioneRemota() { return attiva; }
15
+ export async function avviaRemoto(env, cwd, eventi = {}) {
16
+ if (attiva)
17
+ return attiva;
18
+ if (avvio)
19
+ return avvio;
20
+ if (!mani)
21
+ throw new RelayError("risposta");
22
+ const token = async () => {
23
+ const esito = await tokenRisorsa(env, RELAY_RESOURCE);
24
+ if (esito.token)
25
+ return esito.token;
26
+ throw new RelayError(esito.errore === "rete" ? "rete" : "token");
27
+ };
28
+ avvio = SessioneRemota.avvia({
29
+ base: relayOf(env),
30
+ cwd,
31
+ mani,
32
+ token,
33
+ onStato: (stato, motivo) => {
34
+ // Chiusa dal relay (altro dispositivo, piano scaduto): il remoto si spegne.
35
+ if (stato === "chiuso" && attiva)
36
+ attiva = null;
37
+ eventi.onStato?.(stato, motivo);
38
+ },
39
+ onPresenza: eventi.onPresenza,
40
+ });
41
+ try {
42
+ attiva = await avvio;
43
+ return attiva;
44
+ }
45
+ finally {
46
+ avvio = null;
47
+ }
48
+ }
49
+ export async function fermaRemoto() {
50
+ const s = attiva;
51
+ attiva = null;
52
+ if (!s)
53
+ return false;
54
+ await s.chiudi();
55
+ return true;
56
+ }
57
+ export function osservaEvento(event) {
58
+ attiva?.osserva(event);
59
+ }
60
+ export function messaggioUtente(testo) {
61
+ attiva?.messaggioUtente(testo);
62
+ }
63
+ export function permessoConGara(azione, dettaglio, signal, tui) {
64
+ return attiva ? attiva.permessoConGara(azione, dettaglio, signal, tui) : tui(signal);
65
+ }