@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
@@ -1,17 +1,18 @@
1
1
  import { sanitizeText } from "../core/text-sanitize.js";
2
- import { listMemoryScopes, MemoryStore } from "./memory-store.js";
3
- import { VascendPlanStore } from "./plan-store.js";
4
- import { RoomMemory } from "./room-memory.js";
5
- import { VascendVisionStore, visionCompleteness } from "./vision.js";
2
+ import { listMemoryScopes } from "./memory-store.js";
3
+ import { ContextStoreReader } from "./context-store-reader.js";
4
+ import { packetEventsWithoutMirrors } from "./context-packet-mirrors.js";
5
+ import { visionCompleteness } from "./vision.js";
6
6
  import { listContextMemoryScopes } from "./context-fabric.js";
7
- export function buildMemoryPacket(input) {
7
+ export function buildMemoryPacket(input, reader = new ContextStoreReader(input.sessionDir)) {
8
8
  const maxChars = input.maxChars && input.maxChars > 0 ? input.maxChars : 12_000;
9
9
  const recentLimit = input.recentLimit && input.recentLimit > 0 ? input.recentLimit : 10;
10
10
  const projectScopeLimit = input.projectScopeLimit && input.projectScopeLimit > 0 ? input.projectScopeLimit : 6;
11
- const rooms = safe(() => RoomMemory.open(input.sessionDir, input.scopeId).frontier(), []);
12
- const castles = safe(() => VascendPlanStore.open(input.sessionDir, input.planScopeId ?? input.scopeId).castles(), []);
13
- const visions = safe(() => VascendVisionStore.open(input.sessionDir, input.scopeId).all(), []);
14
- const events = safe(() => recentEvents(MemoryStore.open(input.sessionDir, input.scopeId).all(), recentLimit).map((event) => ({ scopeId: input.scopeId, value: event })), []);
11
+ const rooms = selectFrontierRooms(reader.rooms(input.scopeId));
12
+ const localPlan = reader.plan(input.scopeId);
13
+ const castles = (!input.planScopeId || input.planScopeId === input.scopeId
14
+ ? localPlan : reader.plan(input.planScopeId)) ?? [];
15
+ const visions = reader.visions(input.scopeId);
15
16
  const allowedScopes = !input.projectScopes && input.projectKey
16
17
  ? new Set(listContextMemoryScopes(input.sessionDir, input.projectKey))
17
18
  : null;
@@ -19,10 +20,18 @@ export function buildMemoryPacket(input) {
19
20
  excludeScope: input.scopeId,
20
21
  limit: allowedScopes ? 10_000 : projectScopeLimit,
21
22
  }).filter((scope) => !allowedScopes || allowedScopes.has(scope)).slice(0, projectScopeLimit);
22
- const projectRooms = loadProjectFrontiers(input.sessionDir, projectScopes, 8);
23
- const projectCastles = loadProjectCastles(input.sessionDir, projectScopes, 8);
24
- const projectVisions = loadProjectVisions(input.sessionDir, projectScopes, 5);
25
- const projectEvents = loadProjectEvents(input.sessionDir, projectScopes, recentLimit);
23
+ const projectRooms = projectScopes.flatMap(scopeId => reader.rooms(scopeId).slice(-2).map(value => ({ scopeId, value }))).slice(0, 8);
24
+ const projectCastles = projectScopes.flatMap(scopeId => (reader.plan(scopeId) ?? []).slice(0, 2).map(value => ({ scopeId, value }))).slice(0, 8);
25
+ const projectVisions = projectScopes.flatMap(scopeId => reader.visions(scopeId).slice(0, 2).map(value => ({ scopeId, value }))).slice(0, 5);
26
+ const localEvents = reader.events(input.scopeId);
27
+ const scopedEvents = projectScopes.flatMap(scopeId => reader.events(scopeId).map(value => ({ scopeId, value })));
28
+ const allEvents = [...localEvents.map(value => ({ scopeId: input.scopeId, value })), ...scopedEvents];
29
+ const displayedRooms = [...rooms.map(value => ({ scopeId: input.scopeId, value })), ...projectRooms];
30
+ const filtered = packetEventsWithoutMirrors(allEvents, displayedRooms);
31
+ const events = recentEvents(filtered.filter(e => e.scopeId === input.scopeId).map(e => e.value), recentLimit)
32
+ .map(value => ({ scopeId: input.scopeId, value }));
33
+ const projectEvents = filtered.filter(e => e.scopeId !== input.scopeId && !isSuperseded(e.value))
34
+ .sort((a, b) => b.value.createdAt - a.value.createdAt || b.value.id.localeCompare(a.value.id)).slice(0, recentLimit);
26
35
  const hasProject = Boolean(projectRooms.length || projectCastles.length || projectVisions.length || projectEvents.length);
27
36
  if (!rooms.length && !castles.length && !visions.length && !events.length && !hasProject)
28
37
  return "";
@@ -52,9 +61,7 @@ export function buildMemoryPacket(input) {
52
61
  ];
53
62
  return clampMemoryPacket(lines.filter(Boolean).join("\n"), maxChars);
54
63
  }
55
- function frontierRows(rooms) {
56
- if (!rooms.length)
57
- return ["@frontiera[vuota]: nessuna attivita indicizzata"];
64
+ function selectFrontierRooms(rooms) {
58
65
  // La frontiera e' ordinata vecchio->recente. Conserva un indice per livello
59
66
  // ma dedica la maggior parte degli slot allo stato piu' recente: slice(0,10)
60
67
  // nascondeva proprio le ultime stanze nei run lunghi.
@@ -63,8 +70,12 @@ function frontierRows(rooms) {
63
70
  if (!byLevel.has(room.level))
64
71
  byLevel.set(room.level, room);
65
72
  const picked = [...byLevel.values(), ...rooms.slice(-8)];
66
- const unique = [...new Map(picked.map((room) => [room.room, room])).values()].slice(-10);
67
- return unique.map((r) => `@frontiera[${r.room}]: level=${r.level}, title="${q(r.title)}", termini=${r.terms.slice(0, 6).join(",")}`);
73
+ return [...new Map(picked.map((room) => [room.room, room])).values()].slice(-10);
74
+ }
75
+ function frontierRows(rooms) {
76
+ if (!rooms.length)
77
+ return ["@frontiera[vuota]: nessuna attivita indicizzata"];
78
+ return rooms.map((r) => `@frontiera[${r.room}]: level=${r.level}, title="${q(r.title)}", termini=${r.terms.slice(0, 6).join(",")}${r.attachments?.length ? `, allegati=${r.attachments.length}, recupero=recall rooms=["${r.room}"] attachments=true` : ''}`);
68
79
  }
69
80
  function planRows(castles) {
70
81
  if (!castles.length)
@@ -75,7 +86,7 @@ function planRows(castles) {
75
86
  .sort((a, b) => Number(a.status === "completed") - Number(b.status === "completed") || a.bit - b.bit)
76
87
  .slice(0, 8)
77
88
  .map((t) => `@task[${castle.slug}/${t.id}]: status=${t.status}, title="${q(t.title)}"`);
78
- return [`@plan[${castle.slug}]: title="${q(castle.title)}", target=${castle.maskTarget}, attivita=${castle.tasks.length}`, ...tasks];
89
+ return [`@plan[${castle.slug}]: title="${q(castle.title)}", target=${castle.maskTarget}, attivita=${castle.tasks.length}${castle.after?.trim() ? `, after="${q(castle.after)}"` : ""}`, ...tasks];
79
90
  }).slice(0, 32);
80
91
  }
81
92
  function visionRows(visions) {
@@ -93,7 +104,7 @@ function projectRows(rooms, castles, visions) {
93
104
  "[WORKING_SET_PROGETTO]",
94
105
  ...rooms.map((r) => `@project_frontiera[${r.scopeId}/${r.value.room}]: level=${r.value.level}, title="${q(r.value.title)}"`),
95
106
  ...castles.flatMap((c) => [
96
- `@project_plan[${c.scopeId}/${c.value.slug}]: title="${q(c.value.title)}", target=${c.value.maskTarget}, attivita=${c.value.tasks.length}`,
107
+ `@project_plan[${c.scopeId}/${c.value.slug}]: title="${q(c.value.title)}", target=${c.value.maskTarget}, attivita=${c.value.tasks.length}${c.value.after?.trim() ? `, after="${q(c.value.after)}"` : ""}`,
97
108
  ...c.value.tasks.slice(0, 4).map((t) => `@project_task[${c.scopeId}/${c.value.slug}/${t.id}]: status=${t.status}, title="${q(t.title)}"`),
98
109
  ]),
99
110
  ...visions.map((v) => `@project_vision[${v.scopeId}/${v.value.taskId}]: goal="${q(v.value.goal)}"`),
@@ -116,30 +127,6 @@ function isSuperseded(event) {
116
127
  const status = typeof event.payload?.["status"] === "string" ? event.payload["status"] : "active";
117
128
  return status === "superseded" || status === "archived" || typeof event.payload?.["supersededBy"] === "string";
118
129
  }
119
- function loadProjectFrontiers(sessionDir, scopes, limit) {
120
- return scopes.flatMap((scopeId) => safe(() => RoomMemory.open(sessionDir, scopeId).frontier().slice(-2).map((value) => ({ scopeId, value })), [])).slice(0, limit);
121
- }
122
- function loadProjectCastles(sessionDir, scopes, limit) {
123
- return scopes.flatMap((scopeId) => safe(() => VascendPlanStore.open(sessionDir, scopeId).castles().slice(0, 2).map((value) => ({ scopeId, value })), [])).slice(0, limit);
124
- }
125
- function loadProjectVisions(sessionDir, scopes, limit) {
126
- return scopes.flatMap((scopeId) => safe(() => VascendVisionStore.open(sessionDir, scopeId).all().slice(0, 2).map((value) => ({ scopeId, value })), [])).slice(0, limit);
127
- }
128
- function loadProjectEvents(sessionDir, scopes, limit) {
129
- const events = scopes.flatMap((scopeId) => safe(() => MemoryStore.open(sessionDir, scopeId).all().map((value) => ({ scopeId, value })), []));
130
- return events
131
- .filter((e) => !isSuperseded(e.value))
132
- .sort((a, b) => b.value.createdAt - a.value.createdAt || b.value.id.localeCompare(a.value.id))
133
- .slice(0, limit);
134
- }
135
- function safe(fn, fallback) {
136
- try {
137
- return fn();
138
- }
139
- catch {
140
- return fallback;
141
- }
142
- }
143
130
  function q(value, max = 120) {
144
131
  const oneLine = sanitizeText(value).replace(/\s+/g, " ").trim().replace(/"/g, "'");
145
132
  return oneLine.length > max ? oneLine.slice(0, max - 1).trimEnd() + "..." : oneLine;
@@ -37,8 +37,10 @@ export interface KingdomPlan {
37
37
  }
38
38
  export declare class VascendPlanStore {
39
39
  private readonly file;
40
- private readonly memory;
40
+ private readonly openMemory;
41
41
  private kingdom;
42
+ private memoryStore?;
43
+ private get memory();
42
44
  private constructor();
43
45
  static open(sessionDir: string, sessionId: string, options?: {
44
46
  strict?: boolean;
@@ -63,14 +63,16 @@ function buildCastle(slug, title, taskTitles, after) {
63
63
  }
64
64
  export class VascendPlanStore {
65
65
  file;
66
- memory;
66
+ openMemory;
67
67
  kingdom = { castles: [] };
68
- constructor(file, memory) {
68
+ memoryStore;
69
+ get memory() { return this.memoryStore ??= this.openMemory(); }
70
+ constructor(file, openMemory) {
69
71
  this.file = file;
70
- this.memory = memory;
72
+ this.openMemory = openMemory;
71
73
  }
72
74
  static open(sessionDir, sessionId, options = {}) {
73
- const store = new VascendPlanStore(join(sessionDir, `${sessionId}.castle.json`), MemoryStore.open(sessionDir, sessionId));
75
+ const store = new VascendPlanStore(join(sessionDir, `${sessionId}.castle.json`), () => MemoryStore.open(sessionDir, sessionId));
74
76
  if (existsSync(store.file)) {
75
77
  try {
76
78
  const raw = JSON.parse(readFileSync(store.file, "utf8"));
@@ -1,6 +1,13 @@
1
- import type { Message } from "../core/types.js";
1
+ import type { Message, ImagePart } from "../core/types.js";
2
2
  import { RoomGraph, type RoomEdge, type RoomNeighbor } from "./room-graph.js";
3
+ export interface RoomAttachment {
4
+ readonly id: string;
5
+ readonly mime: string;
6
+ readonly source?: string;
7
+ readonly bytes: number;
8
+ }
3
9
  export interface RoomRecord {
10
+ readonly attachments?: readonly RoomAttachment[];
4
11
  readonly room: string;
5
12
  readonly level: number;
6
13
  readonly title: string;
@@ -35,6 +42,8 @@ export declare class RoomMemory {
35
42
  private tokensFor;
36
43
  private indexedDocs;
37
44
  static open(sessionDir: string, sessionId: string, fanout?: number): RoomMemory;
45
+ private persistMedia;
46
+ attachmentMedia(rooms: readonly string[]): readonly ImagePart[];
38
47
  count(): number;
39
48
  all(): readonly RoomRecord[];
40
49
  frontier(): readonly RoomRecord[];
@@ -1,10 +1,15 @@
1
1
  import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
+ import { createHash } from "node:crypto";
3
4
  import { sanitizeText } from "../core/text-sanitize.js";
4
5
  import { MemoryStore } from "./memory-store.js";
5
6
  import { STOP, tokenize, buildSynonyms, expandQuery, bm25Score, } from "./lexical-search.js";
6
7
  import { withLockSync } from "./file-lock.js";
7
8
  import { RoomGraph, clusterByAffinity, clusterLabel, roomEdges, topTerms, } from "./room-graph.js";
9
+ const pendingMedia = new WeakMap();
10
+ const validImage = (image) => /^image\/(png|jpeg|webp|gif)$/.test(image.mime)
11
+ && image.data.length > 0 && image.data.length <= 28_000_000 && /^[A-Za-z0-9+/=\s]+$/.test(image.data);
12
+ const attachmentId = (image) => createHash('sha256').update(image.mime).update(image.data).digest('hex');
8
13
  // Peso di un vicino a 2 hop rispetto al seme che lo ha portato. Allineato al
9
14
  // peso del link tipizzato in context-retriever.ts: stessa semantica, stesso
10
15
  // numero, cosi' recall e compilazione del contesto non si contraddicono.
@@ -78,6 +83,31 @@ export class RoomMemory {
78
83
  }
79
84
  return mem;
80
85
  }
86
+ persistMedia(record) {
87
+ const media = pendingMedia.get(record) || [];
88
+ if (!media.length)
89
+ return;
90
+ const dir = this.file + '.attachments';
91
+ mkdirSync(dir, { recursive: true });
92
+ for (const image of media)
93
+ writeFileSync(join(dir, attachmentId(image) + '.json'), JSON.stringify(image), { encoding: 'utf8', mode: 0o600 });
94
+ pendingMedia.delete(record);
95
+ }
96
+ attachmentMedia(rooms) {
97
+ const allowed = this.expandRooms(rooms).flatMap(record => record.attachments || []);
98
+ const out = [];
99
+ for (const ref of allowed.slice(0, 8)) {
100
+ if (!/^[a-f0-9]{64}$/.test(ref.id))
101
+ continue;
102
+ try {
103
+ const image = JSON.parse(readFileSync(join(this.file + '.attachments', ref.id + '.json'), 'utf8'));
104
+ if (validImage(image) && attachmentId(image) === ref.id)
105
+ out.push(image);
106
+ }
107
+ catch { /* Allegato assente: non inventare dati visivi. */ }
108
+ }
109
+ return out;
110
+ }
81
111
  count() {
82
112
  return this.records.length;
83
113
  }
@@ -102,6 +132,7 @@ export class RoomMemory {
102
132
  // Aggiunge una stanza (livello 0): fast path append, protetto da lock
103
133
  // file-system cosi' due processi concorrenti non interlacciano le righe.
104
134
  append(record) {
135
+ this.persistMedia(record);
105
136
  this.records.push(record);
106
137
  mkdirSync(dirname(this.file), { recursive: true });
107
138
  withLockSync(this.file, () => {
@@ -151,6 +182,7 @@ export class RoomMemory {
151
182
  this.reloadFromDisk();
152
183
  const id = this.nextRoomId(ns);
153
184
  const rec = build(id);
185
+ this.persistMedia(rec);
154
186
  this.records.push(rec);
155
187
  appendFileSync(this.file, JSON.stringify(rec) + "\n", "utf8");
156
188
  return rec;
@@ -329,7 +361,7 @@ export class RoomMemory {
329
361
  const rows = front
330
362
  .map((r, i) => {
331
363
  const marker = i === last ? " <- TURNO PIU' RECENTE" : "";
332
- return `${i + 1}. @${levelLabel(r.level)}[${r.room}]: "${escapeValue(r.title)}" · termini=${r.terms.slice(0, 8).join(",")}${marker}`;
364
+ return `${i + 1}. @${levelLabel(r.level)}[${r.room}]: "${escapeValue(r.title)}" · termini=${r.terms.slice(0, 8).join(",")}${r.attachments?.length ? ` · allegati=${r.attachments.length}; recall rooms=["${r.room}"] attachments=true` : ''}${marker}`;
333
365
  })
334
366
  .join("\n");
335
367
  return `ATTIVITA_ARCHIVIATE (ordine cronologico: prima le piu' vecchie, ultima la piu' recente)
@@ -371,6 +403,8 @@ function roomEvent(type, record, children = []) {
371
403
  }
372
404
  // Record Danilov compatto di una STANZA (livello 0) dai messaggi del turno.
373
405
  export function formatRoomRecord(room, messages, createdAt) {
406
+ const media = messages.filter(message => message.role === 'user').flatMap(message => message.content.filter((part) => part.type === 'image')).filter(validImage).slice(0, 8);
407
+ const attachments = media.map(image => ({ id: attachmentId(image), mime: image.mime, bytes: Math.floor(image.data.length * 0.75), ...(image.source ? { source: image.source } : {}) }));
374
408
  const title = roomTitle(messages);
375
409
  const tools = collectToolCalls(messages);
376
410
  const files = collectFiles(messages);
@@ -382,6 +416,7 @@ export function formatRoomRecord(room, messages, createdAt) {
382
416
  ]).slice(0, 12);
383
417
  const def = [
384
418
  `@goal[${room}]: "${escapeValue(title)}"`,
419
+ ...attachments.map(att => `@attachment[${att.id}]: ${att.mime}, source=${escapeValue(att.source || 'immagine')}; recall rooms=["${room}"] attachments=true`),
385
420
  ...tools.slice(0, 12).map((t, i) => `@tool[${room}.${i + 1}]: ${t.name}(${escapeValue(t.arg)}) -> ${escapeValue(t.result)}`),
386
421
  ...files.slice(0, 12).map((f, i) => `@file[${room}.${i + 1}]: ${escapeValue(f)}`),
387
422
  ].join("\n");
@@ -401,7 +436,10 @@ RELAZIONI
401
436
  ${rel}
402
437
 
403
438
  OUTPUT: ${escapeValue(output) || "(nessun output testuale)"}`);
404
- return { room, level: 0, title, terms, body, children: [], parent: null, createdAt, edges };
439
+ const record = { room, level: 0, title, terms, body, children: [], parent: null, createdAt, edges, ...(attachments.length ? { attachments } : {}) };
440
+ if (media.length)
441
+ pendingMedia.set(record, media);
442
+ return record;
405
443
  }
406
444
  // Record di ROLLUP (castello/regno/...): indice sui figli che ha chiuso.
407
445
  export function formatRollupRecord(id, level, children, createdAt) {
@@ -439,6 +477,7 @@ OUTPUT: riepilogo delle attivita; apri i dettagli col tool recall.`);
439
477
  terms,
440
478
  body,
441
479
  children: children.map((c) => c.room),
480
+ attachments: children.flatMap(c => c.attachments || []),
442
481
  parent: null,
443
482
  // Il padre eredita il timestamp del figlio piu' recente: con i gruppi per
444
483
  // affinita' l'istante del rollup non direbbe nulla, mentre cosi' la
@@ -488,6 +527,7 @@ function normalize(raw) {
488
527
  parent: raw.parent ?? null,
489
528
  createdAt: typeof raw.createdAt === "number" ? raw.createdAt : 0,
490
529
  ...(Array.isArray(raw.edges) ? { edges: raw.edges.filter(isRoomEdge) } : {}),
530
+ ...(Array.isArray(raw.attachments) ? { attachments: raw.attachments.filter(att => att && /^[a-f0-9]{64}$/.test(att.id)) } : {}),
491
531
  };
492
532
  }
493
533
  // Aggiorna solo le etichette generate dei vecchi riepiloghi. Gli ID,
@@ -1,5 +1,4 @@
1
- import type { Message } from "../core/types.js";
2
- import { type VisionLike } from "./long-term-context.js";
1
+ import type { VisionLike } from "./long-term-context.js";
3
2
  export type VascendMode = "off" | "sticky";
4
3
  export type VascendPromptMode = "one-shot" | "sticky";
5
4
  export type VascendTaskStatus = "pending" | "running" | "completed" | "failed";
@@ -48,10 +47,6 @@ export type VascendCommandResult = {
48
47
  readonly message: string;
49
48
  readonly input: string;
50
49
  };
51
- export interface VascendCheckpoint {
52
- readonly path: string;
53
- readonly text: string;
54
- }
55
50
  export declare const VASCEND_SYSTEM_BLOCK = "# Lista delle attivita\nComunica in italiano naturale, con spiegazioni chiare e Markdown leggibile.\nDescrivi il lavoro con una to-do list: attivita, gruppi, dipendenze e stati\nda fare, in corso, completata o fallita. I riepiloghi della memoria sono solo\ncontesto interno: all'utente mostra le attivita e i risultati pertinenti.\nPer lavori con piu passi usa vascend_plan: un piano contiene gruppi di attivita,\nogni gruppo contiene attivita con stato, dipendenze e prove di verifica.\nAggiorna gli stati con vascend_mark; completa solo dopo le verifiche.\nUsa vascend_note per decisioni e blocchi e recall per recuperare il passato.\nPer lavori indipendenti valuta collaboratori tramite gli strumenti del team.\nPer domande o richieste semplici non creare un piano inutile.";
56
51
  export interface VascendVerdict {
57
52
  readonly ok: boolean;
@@ -66,19 +61,16 @@ export declare function createVascendState(): VascendState;
66
61
  export declare function createVascendGoal(state: VascendState, goal: string): VascendState;
67
62
  export declare function setVascendMode(state: VascendState, mode: VascendMode): VascendState;
68
63
  export declare function setVascendVision(state: VascendState, vision: VisionLike | null): VascendState;
69
- export declare function wrapVascendPrompt(state: VascendState, userText: string, mode: VascendPromptMode, checkpoint?: string): string;
70
- export declare function formatVascendCompact(state: VascendState, messages?: readonly Message[]): string;
64
+ export declare function wrapVascendPrompt(_state: VascendState, userText: string, _mode: VascendPromptMode): string;
71
65
  export declare class VascendRuntime {
72
- private readonly cwd;
73
66
  private stateValue;
74
67
  private customPlan;
75
- constructor(cwd: string, initialMode?: VascendMode);
68
+ constructor(_cwd: string, initialMode?: VascendMode);
76
69
  state(): VascendState;
77
70
  hasCustomPlan(): boolean;
78
71
  loadPlan(sessionDir: string, sessionId: string): boolean;
79
72
  resetPlan(sessionDir: string, sessionId: string): void;
80
73
  handleCommand(args: string): VascendCommandResult;
81
74
  prepareUserInput(userText: string): PreparedVascendInput;
82
- compact(messages: readonly Message[]): VascendCheckpoint;
83
75
  private wrap;
84
76
  }
@@ -1,8 +1,5 @@
1
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
- import { join } from "node:path";
3
1
  import { sanitizeText } from "../core/text-sanitize.js";
4
2
  import { VascendPlanStore } from "./plan-store.js";
5
- import { injectLongTermContext } from "./long-term-context.js";
6
3
  export const VASCEND_SYSTEM_BLOCK = `# Lista delle attivita
7
4
  Comunica in italiano naturale, con spiegazioni chiare e Markdown leggibile.
8
5
  Descrivi il lavoro con una to-do list: attivita, gruppi, dipendenze e stati
@@ -14,6 +11,7 @@ Aggiorna gli stati con vascend_mark; completa solo dopo le verifiche.
14
11
  Usa vascend_note per decisioni e blocchi e recall per recuperare il passato.
15
12
  Per lavori indipendenti valuta collaboratori tramite gli strumenti del team.
16
13
  Per domande o richieste semplici non creare un piano inutile.`;
14
+ const MAX_GOAL_CHARS = 240;
17
15
  export function validateKingdom(state) {
18
16
  const castles = state.kingdom.castles;
19
17
  const multi = castles.length > 1;
@@ -55,7 +53,9 @@ export function createVascendState() {
55
53
  return { mode: "off", goal: null, kingdom: { castles: [] } };
56
54
  }
57
55
  export function createVascendGoal(state, goal) {
58
- const title = oneLine(goal) || "obiettivo";
56
+ // Il goal e' una chiave breve per UI, routing e memoria. Resta nello stato
57
+ // strutturato e non viene copiato dentro al messaggio utente.
58
+ const title = oneLine(goal).slice(0, MAX_GOAL_CHARS) || "obiettivo";
59
59
  // NIENTE castello automatico: il castello (con le sue stanze) si crea solo su
60
60
  // richiesta (comando /castle) o quando l'agente pianifica con vascend_plan.
61
61
  // Cosi' il riquadro non compare quando non serve (es. un saluto).
@@ -69,90 +69,19 @@ export function setVascendMode(state, mode) {
69
69
  export function setVascendVision(state, vision) {
70
70
  return { ...state, vision };
71
71
  }
72
- export function wrapVascendPrompt(state, userText, mode, checkpoint) {
73
- if (!state.goal)
74
- return userText;
75
- const checkpointBlock = checkpoint
76
- ? `\nCHECKPOINT_PRECEDENTE\n${checkpoint.trim()}\n`
77
- : "";
78
- const immediatePrompt = `[VASCEND_GOAL]
79
- GOAL: ${state.goal}
80
- MODE: ${mode}
81
- ${checkpointBlock}
82
- Piano di lavoro:
83
- - Comunica in modo naturale e leggibile, senza imporre notazioni speciali.
84
- - Per lavori con piu passi crea o riprendi il piano con vascend_plan e vascend_status.
85
- - Organizza il piano in gruppi e attivita; aggiorna gli stati con vascend_mark.
86
- - Completa le attivita solo dopo verifiche concrete; registra decisioni e blocchi.
87
- - Per lavori indipendenti valuta collaboratori; per domande semplici non serve un piano.
88
-
89
- INPUT_UTENTE
90
- ${userText}`;
91
- // Antepone il CONTESTO A LUNGO TERMINE (Vision finale) come piano SEPARATO
92
- // dal goal immediato. Il goal corrente viene passato come "immediato" cosi'
93
- // che il blocco espliciti la distinzione momentaneo/visione. Se non c'e'
94
- // Vision con contenuto, injectLongTermContext ritorna il prompt invariato.
95
- const currentCastle = state.kingdom.castles[0];
96
- const currentRoom = currentCastle?.tasks.find((t) => t.status === "running");
97
- return injectLongTermContext(immediatePrompt, {
98
- vision: state.vision ?? null,
99
- immediate: {
100
- ...(state.goal ? { goal: state.goal } : {}),
101
- ...(currentCastle ? { castle: currentCastle.title } : {}),
102
- ...(currentRoom ? { room: currentRoom.title } : {}),
103
- },
104
- });
105
- }
106
- export function formatVascendCompact(state, messages = []) {
107
- const castle = state.kingdom.castles[0];
108
- const multi = state.kingdom.castles.length > 1;
109
- const tasks = state.kingdom.castles.flatMap(group => group.tasks.map(task => ({ ...task,
110
- id: multi ? `${group.slug}/${task.id}` : task.id,
111
- })));
112
- const groups = state.kingdom.castles.map(group => `@gruppo[${group.slug}]: titolo="${escapeValue(group.title)}", dipendenza=${group.after ?? "none"}`).join("\n");
113
- const taskRows = tasks.length
114
- ? tasks.map((task) => compactTaskRow(task)).join("\n")
115
- : "@aperto[goal]: nessun_gruppo_attivo, blocco=none";
116
- const recent = messagePreviews(messages)
117
- .map((text, index) => `@stato[recent_${index + 1}]: ${text}`)
118
- .join("\n");
119
- const relations = tasks.length
120
- ? tasks.map((task, index) => `@R${index + 1}: @stato[goal] -> @${task.status === "completed" ? "fatto" : "aperto"}[${task.id}], task`).join("\n")
121
- : "@R1: @stato[goal] -> @aperto[goal], crea_gruppo";
122
- // The synthetic summary is usually older than the last four messages.
123
- // Keep it in the cold-start checkpoint, not just 140-character previews.
124
- const summary = messages.flatMap(message => message.content
125
- .filter(part => part.type === "text" && part.text.startsWith("[Riepilogo del contesto precedente]"))
126
- .map(part => part.type === "text" ? part.text : "")).at(-1);
127
- return `INDICE
128
- 1 = goal
129
- 2 = gruppo
130
- 3 = task
131
- 4 = file
132
- 5 = contesto
133
-
134
- DEFINIZIONI
135
- @stato[goal]: valore="${escapeValue(state.goal ?? "nessun_goal")}", mode=${state.mode}
136
- @stato[gruppo]: slug=${castle?.slug ?? "none"}, mask=${castle?.maskTarget ?? "0x0000"}
137
- ${groups}
138
- ${taskRows}
139
- @file[checkpoint]: path=.vascend-compact.md, ruolo=checkpoint, stato=aggiornato${recent ? `\n${recent}` : ""}
140
-
141
- RELAZIONI
142
- ${relations}
143
- @R99: @file[checkpoint] -> @stato[goal], ripresa
144
-
145
- OUTPUT: stato compatto della sessione, pronto a riprendere a freddo.${summary ? `\n\nCONTESTO DI RIPRESA\n${summary}` : ""}`;
72
+ export function wrapVascendPrompt(_state, userText, _mode) {
73
+ // La richiesta dell'utente deve comparire una sola volta nel payload. Goal,
74
+ // piano, Vision e ricevute vengono gia' composti dal Context Fabric e dagli
75
+ // store JSON nel canale di contesto del turno.
76
+ return userText;
146
77
  }
147
78
  export class VascendRuntime {
148
- cwd;
149
79
  stateValue;
150
80
  customPlan = false;
151
81
  // initialMode permette di rendere il metodo Vascend NATIVO (sticky) di
152
82
  // default nella TUI, senza dover digitare /vascend on. Default "off" per
153
83
  // mantenere puro il runtime fuori dalla TUI (test, print, SDK).
154
- constructor(cwd, initialMode = "off") {
155
- this.cwd = cwd;
84
+ constructor(_cwd, initialMode = "off") {
156
85
  this.stateValue = setVascendMode(createVascendState(), initialMode);
157
86
  }
158
87
  state() {
@@ -233,54 +162,13 @@ export class VascendRuntime {
233
162
  this.stateValue = createVascendGoal(this.stateValue, userText);
234
163
  return { active: true, input: this.wrap(userText, "sticky") };
235
164
  }
236
- compact(messages) {
237
- const text = formatVascendCompact(this.stateValue, messages);
238
- const path = checkpointPath(this.cwd);
239
- writeFileSync(path, text + "\n", "utf8");
240
- return { path, text };
241
- }
242
165
  wrap(userText, mode) {
243
- return wrapVascendPrompt(this.stateValue, userText, mode, readCheckpoint(this.cwd));
166
+ return wrapVascendPrompt(this.stateValue, userText, mode);
244
167
  }
245
168
  }
246
- function compactTaskRow(task) {
247
- const kind = task.status === "completed" ? "fatto" : "aperto";
248
- const block = task.status === "failed" ? ", blocco=failed" : "";
249
- const evidence = task.evidence?.length
250
- ? `, prove="${escapeValue(task.evidence.slice(-3).join(" | "))}"`
251
- : "";
252
- return `@${kind}[${task.id}]: titolo="${escapeValue(task.title)}", bit=${task.bit}, mask=${task.mask}, esito=${task.status}${block}${evidence}`;
253
- }
254
- function messagePreviews(messages) {
255
- return messages.slice(-4).flatMap((message) => {
256
- const text = message.content
257
- .map((part) => {
258
- if (part.type === "text")
259
- return part.text;
260
- if (part.type === "tool_call")
261
- return `${part.name} ${JSON.stringify(part.input)}`;
262
- if (part.type === "image")
263
- return `[immagine ${part.mime}]`;
264
- return part.output;
265
- })
266
- .join(" ");
267
- const compact = oneLine(text).slice(0, 140);
268
- return compact ? [`role=${message.role}, text="${escapeValue(compact)}"`] : [];
269
- });
270
- }
271
- function checkpointPath(cwd) {
272
- return join(cwd, ".vascend-compact.md");
273
- }
274
- function readCheckpoint(cwd) {
275
- const path = checkpointPath(cwd);
276
- return existsSync(path) ? readFileSync(path, "utf8") : undefined;
277
- }
278
169
  function oneLine(value) {
279
170
  return sanitizeText(value).replace(/\s+/g, " ").trim();
280
171
  }
281
- function escapeValue(value) {
282
- return oneLine(value).replace(/"/g, "'");
283
- }
284
172
  function hexMask(value) {
285
173
  return `0x${value.toString(16).padStart(4, "0")}`;
286
174
  }
@@ -80,6 +80,8 @@ function lineText(line) {
80
80
  const BOILERPLATE = /^\s*(?:\[COLLABORATORE|\[VASSALLO|\[VASCEND_GOAL\]|INDICE\s+\d\s*=|<!--\s*vascend:|@regno|@stato\[)/i;
81
81
  const OBJECTIVE_MARKER = /(?:OBIETTIVO_ATTIVITA|OBIETTIVO_CASTELLO|OBIETTIVO|GOAL)\s*[:=]\s*(.{4,})/i;
82
82
  export function pickObjective(userTexts) {
83
+ // Compatibilita di sola lettura con sessioni storiche che contenevano il
84
+ // vecchio wrapper/checkpoint. Non carica file e non aggiunge dati al prompt.
83
85
  const cut = (text) => text.split(/\s(?:MODE|CHECKPOINT_PRECEDENTE|CHECKPOINT|INDICE|OUTPUT)\s*[:=]/)[0].trim().slice(0, 200);
84
86
  const plain = userTexts.find((text) => !BOILERPLATE.test(text));
85
87
  if (plain)
@@ -119,8 +119,9 @@ function checkpoint(team) {
119
119
  export async function waitForTeamChange(team, signal, interrupted) {
120
120
  const signature = (current) => JSON.stringify(current.members.map((member) => [member.slug, member.status]));
121
121
  const before = signature(team);
122
- const deadline = Date.now() + 30_000;
123
- while (!signal.aborted && !interrupted() && Date.now() < deadline) {
122
+ // Poll local state only. A wall-clock deadline used to wake the provider
123
+ // repeatedly even when no worker or message had changed.
124
+ while (!signal.aborted && !interrupted()) {
124
125
  const current = teamById(team.sessionId, team.id);
125
126
  if (!current || current.done || signature(current) !== before)
126
127
  return;
@@ -60,8 +60,10 @@ export declare function visionFromJSON(json: string, opts?: {
60
60
  }): Vision | null;
61
61
  export declare class VascendVisionStore {
62
62
  private readonly file;
63
- private readonly memory;
63
+ private readonly openMemory;
64
64
  private visions;
65
+ private memoryStore?;
66
+ private get memory();
65
67
  private constructor();
66
68
  static open(sessionDir: string, sessionId: string): VascendVisionStore;
67
69
  has(taskId: string): boolean;
@@ -202,14 +202,16 @@ export function visionFromJSON(json, opts) {
202
202
  // VascendPlanStore: API immutabile verso l'esterno, persistenza su mutazione.
203
203
  export class VascendVisionStore {
204
204
  file;
205
- memory;
205
+ openMemory;
206
206
  visions = {};
207
- constructor(file, memory) {
207
+ memoryStore;
208
+ get memory() { return this.memoryStore ??= this.openMemory(); }
209
+ constructor(file, openMemory) {
208
210
  this.file = file;
209
- this.memory = memory;
211
+ this.openMemory = openMemory;
210
212
  }
211
213
  static open(sessionDir, sessionId) {
212
- const store = new VascendVisionStore(join(sessionDir, `${sessionId}.vision.json`), MemoryStore.open(sessionDir, sessionId));
214
+ const store = new VascendVisionStore(join(sessionDir, `${sessionId}.vision.json`), () => MemoryStore.open(sessionDir, sessionId));
213
215
  if (existsSync(store.file)) {
214
216
  try {
215
217
  const raw = JSON.parse(readFileSync(store.file, "utf8"));
@@ -0,0 +1,20 @@
1
+ import { type AgentDeps } from "../core/agent.js";
2
+ import type { AgentEvent } from "../core/types.js";
3
+ import { createWorkerInbox } from "./kingdom-live.js";
4
+ /** Un incarico finisce, la conversazione puo' restare disponibile per l'host. */
5
+ export declare function createWorkerSession(opts: {
6
+ kingdomId: string;
7
+ slug: string;
8
+ sessionId: string;
9
+ goal: string;
10
+ parentSignal: AbortSignal;
11
+ controller: AbortController;
12
+ retain: boolean | "queued";
13
+ onController?: (controller: AbortController) => void;
14
+ binding: ReturnType<typeof createWorkerInbox>;
15
+ deps: AgentDeps;
16
+ sink: (event: AgentEvent) => void;
17
+ }): {
18
+ readonly signal: AbortSignal;
19
+ finish: () => void;
20
+ };