@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.
- package/LICENSE +22 -0
- package/README.md +37 -12
- package/dist/core/agent.d.ts +16 -3
- package/dist/core/agent.js +170 -70
- package/dist/core/background.d.ts +10 -3
- package/dist/core/background.js +55 -9
- package/dist/core/bootstrap.js +1 -0
- package/dist/core/compaction.js +1 -1
- package/dist/core/config-storage.d.ts +5 -0
- package/dist/core/config-storage.js +83 -0
- package/dist/core/config.d.ts +11 -2
- package/dist/core/config.js +24 -8
- package/dist/core/context-ledger.js +2 -1
- package/dist/core/context-policy.d.ts +1 -1
- package/dist/core/context-policy.js +10 -7
- package/dist/core/credentials.js +29 -30
- package/dist/core/effort.d.ts +1 -1
- package/dist/core/effort.js +5 -1
- package/dist/core/execution-guidance.d.ts +1 -1
- package/dist/core/execution-guidance.js +2 -0
- package/dist/core/file-coordination.d.ts +3 -0
- package/dist/core/file-coordination.js +28 -0
- package/dist/core/file-lease-store.d.ts +3 -0
- package/dist/core/file-lease-store.js +28 -12
- package/dist/core/fusion.d.ts +24 -0
- package/dist/core/fusion.js +80 -0
- package/dist/core/model-limits.d.ts +1 -0
- package/dist/core/model-limits.js +5 -1
- package/dist/core/permission-queue.js +15 -15
- package/dist/core/request-plan.js +4 -3
- package/dist/core/session-naming.d.ts +3 -0
- package/dist/core/session-naming.js +47 -0
- package/dist/core/session-usage.js +4 -1
- package/dist/core/session.d.ts +5 -2
- package/dist/core/session.js +22 -7
- package/dist/core/settings.d.ts +2 -0
- package/dist/core/settings.js +5 -16
- package/dist/core/step-policy.d.ts +18 -0
- package/dist/core/step-policy.js +6 -0
- package/dist/core/storage-health.d.ts +24 -0
- package/dist/core/storage-health.js +72 -0
- package/dist/core/tool-artifacts.d.ts +2 -0
- package/dist/core/tool-artifacts.js +21 -3
- package/dist/core/tool-result-index.js +29 -33
- package/dist/core/tool-result-store.js +6 -5
- package/dist/core/types.d.ts +26 -2
- package/dist/core/user-inbox.d.ts +9 -0
- package/dist/core/user-inbox.js +22 -3
- package/dist/core/user-input.js +1 -1
- package/dist/fusion/autopilot-fast.d.ts +59 -0
- package/dist/fusion/autopilot-fast.js +213 -0
- package/dist/fusion/autopilot.d.ts +75 -0
- package/dist/fusion/autopilot.js +308 -0
- package/dist/fusion/focus.d.ts +30 -0
- package/dist/fusion/focus.js +122 -0
- package/dist/fusion/gate.d.ts +30 -0
- package/dist/fusion/gate.js +95 -0
- package/dist/fusion/jev.d.ts +71 -0
- package/dist/fusion/jev.js +164 -0
- package/dist/fusion/receipts.d.ts +25 -0
- package/dist/fusion/receipts.js +34 -0
- package/dist/fusion/route.d.ts +25 -0
- package/dist/fusion/route.js +84 -0
- package/dist/fusion/run.d.ts +4 -0
- package/dist/fusion/run.js +186 -0
- package/dist/horizon/engine.d.ts +20 -1
- package/dist/horizon/engine.js +57 -0
- package/dist/horizon/index.d.ts +2 -2
- package/dist/horizon/index.js +1 -1
- package/dist/horizon/types.d.ts +29 -0
- package/dist/index.js +12 -0
- package/dist/providers/anthropic-oauth.d.ts +8 -3
- package/dist/providers/anthropic-oauth.js +13 -10
- package/dist/providers/anthropic.d.ts +9 -2
- package/dist/providers/anthropic.js +64 -21
- package/dist/providers/cli-models.d.ts +30 -0
- package/dist/providers/cli-models.js +194 -0
- package/dist/providers/model-effort.js +1 -1
- package/dist/providers/openai-oauth.d.ts +8 -3
- package/dist/providers/openai-oauth.js +9 -3
- package/dist/providers/openrouter-manual-fallback.d.ts +3 -1
- package/dist/providers/openrouter-manual-fallback.js +20 -20
- package/dist/providers/registry.js +3 -2
- package/dist/providers/retry.d.ts +3 -0
- package/dist/providers/retry.js +44 -1
- package/dist/remote/cifratura.d.ts +12 -0
- package/dist/remote/cifratura.js +66 -0
- package/dist/remote/index.d.ts +15 -0
- package/dist/remote/index.js +65 -0
- package/dist/remote/relay-client.d.ts +58 -0
- package/dist/remote/relay-client.js +315 -0
- package/dist/remote/sessione-remota.d.ts +60 -0
- package/dist/remote/sessione-remota.js +266 -0
- package/dist/sdk.d.ts +7 -2
- package/dist/sdk.js +3 -1
- package/dist/tools/builtin.js +41 -20
- package/dist/tools/capabilities.d.ts +3 -1
- package/dist/tools/capabilities.js +5 -2
- package/dist/tools/computer-use.js +2 -1
- package/dist/tools/pagination.js +1 -1
- package/dist/tools/program.js +34 -21
- package/dist/tools/team-tools.js +11 -6
- package/dist/tui/account-entry.js +14 -9
- package/dist/tui/agent-label.d.ts +2 -0
- package/dist/tui/agent-label.js +5 -0
- package/dist/tui/agent-sessions-screen.d.ts +35 -0
- package/dist/tui/agent-sessions-screen.js +178 -0
- package/dist/tui/agent-sessions.d.ts +53 -0
- package/dist/tui/agent-sessions.js +236 -0
- package/dist/tui/bottom-input.d.ts +57 -2
- package/dist/tui/bottom-input.js +449 -54
- package/dist/tui/branding.js +8 -4
- package/dist/tui/capability-lists.d.ts +10 -0
- package/dist/tui/capability-lists.js +41 -0
- package/dist/tui/command-output-registry.js +4 -0
- package/dist/tui/command-transcript.d.ts +1 -1
- package/dist/tui/command-transcript.js +17 -21
- package/dist/tui/commands.js +6 -2
- package/dist/tui/compaction-status.d.ts +7 -0
- package/dist/tui/compaction-status.js +39 -0
- package/dist/tui/config-recovery.d.ts +12 -0
- package/dist/tui/config-recovery.js +54 -0
- package/dist/tui/context-meter.d.ts +3 -0
- package/dist/tui/context-meter.js +35 -8
- package/dist/tui/diff.d.ts +9 -0
- package/dist/tui/diff.js +43 -19
- package/dist/tui/fusion-menu.d.ts +5 -0
- package/dist/tui/fusion-menu.js +89 -0
- package/dist/tui/i18n-catalog.js +351 -2
- package/dist/tui/live-command.d.ts +1 -0
- package/dist/tui/live-command.js +9 -0
- package/dist/tui/live-output.d.ts +1 -0
- package/dist/tui/live-output.js +11 -0
- package/dist/tui/logo.js +3 -0
- package/dist/tui/model-details.js +13 -12
- package/dist/tui/model-picker.d.ts +2 -0
- package/dist/tui/model-picker.js +59 -0
- package/dist/tui/model-selection.js +19 -10
- package/dist/tui/native-scrollback.d.ts +2 -1
- package/dist/tui/native-scrollback.js +31 -6
- package/dist/tui/prompt-queue-store.d.ts +1 -0
- package/dist/tui/prompt-queue-store.js +4 -0
- package/dist/tui/prompt-queue.d.ts +3 -0
- package/dist/tui/prompt-queue.js +19 -1
- package/dist/tui/provider-label.d.ts +2 -0
- package/dist/tui/provider-label.js +9 -0
- package/dist/tui/provider-menu.js +25 -24
- package/dist/tui/queue-screen.js +2 -1
- package/dist/tui/queued-questions.js +2 -4
- package/dist/tui/quick-help.d.ts +1 -1
- package/dist/tui/quick-help.js +3 -0
- package/dist/tui/remote-command.d.ts +7 -0
- package/dist/tui/remote-command.js +79 -0
- package/dist/tui/repl.d.ts +11 -2
- package/dist/tui/repl.js +457 -327
- package/dist/tui/review-store.d.ts +8 -1
- package/dist/tui/review-store.js +64 -11
- package/dist/tui/review-team.js +4 -3
- package/dist/tui/runtime-error.d.ts +2 -0
- package/dist/tui/runtime-error.js +26 -0
- package/dist/tui/scrollback-archive.d.ts +16 -0
- package/dist/tui/scrollback-archive.js +90 -0
- package/dist/tui/selector-mouse.d.ts +1 -0
- package/dist/tui/selector-mouse.js +1 -1
- package/dist/tui/selector.d.ts +11 -0
- package/dist/tui/selector.js +28 -4
- package/dist/tui/session-controls.d.ts +26 -0
- package/dist/tui/session-controls.js +65 -0
- package/dist/tui/session-naming-notice.d.ts +14 -0
- package/dist/tui/session-naming-notice.js +59 -0
- package/dist/tui/session-picker.d.ts +2 -0
- package/dist/tui/session-picker.js +32 -0
- package/dist/tui/session-replay.d.ts +1 -0
- package/dist/tui/session-replay.js +101 -32
- package/dist/tui/session-transcript-layout.d.ts +9 -0
- package/dist/tui/session-transcript-layout.js +41 -0
- package/dist/tui/startup.js +63 -33
- package/dist/tui/status-line.d.ts +1 -1
- package/dist/tui/status-line.js +20 -12
- package/dist/tui/storage-warning.d.ts +2 -0
- package/dist/tui/storage-warning.js +35 -0
- package/dist/tui/talos-art-data.d.ts +1 -0
- package/dist/tui/talos-art-data.js +2 -0
- package/dist/tui/talos-art.d.ts +1 -0
- package/dist/tui/talos-art.js +9 -0
- package/dist/tui/task-labels.d.ts +1 -0
- package/dist/tui/task-labels.js +8 -0
- package/dist/tui/terminal-text.d.ts +2 -1
- package/dist/tui/terminal-text.js +28 -2
- package/dist/tui/theme.js +2 -2
- package/dist/tui/tool-presentation.d.ts +3 -0
- package/dist/tui/tool-presentation.js +98 -0
- package/dist/tui/tool-style.js +4 -0
- package/dist/tui/transcript-store.d.ts +25 -0
- package/dist/tui/transcript-store.js +151 -0
- package/dist/tui/usage-footer.d.ts +1 -1
- package/dist/tui/usage-footer.js +3 -1
- package/dist/tui/vascend-id.d.ts +49 -0
- package/dist/tui/vascend-id.js +321 -0
- package/dist/tui/web-login-screen.js +4 -3
- package/dist/tui/web-login.d.ts +7 -15
- package/dist/tui/web-login.js +38 -78
- package/dist/tui/worker-transcripts.d.ts +31 -0
- package/dist/tui/worker-transcripts.js +91 -0
- package/dist/vascend/candidate-collector.d.ts +6 -5
- package/dist/vascend/candidate-collector.js +44 -43
- package/dist/vascend/context-feedback-loop.js +2 -0
- package/dist/vascend/context-lint.js +32 -2
- package/dist/vascend/context-market-selection.js +12 -5
- package/dist/vascend/context-pack-render.js +10 -4
- package/dist/vascend/context-packet-mirrors.d.ts +8 -0
- package/dist/vascend/context-packet-mirrors.js +24 -0
- package/dist/vascend/context-plan-events.d.ts +4 -0
- package/dist/vascend/context-plan-events.js +44 -0
- package/dist/vascend/context-snapshot.d.ts +1 -0
- package/dist/vascend/context-snapshot.js +8 -5
- package/dist/vascend/context-store-reader.d.ts +20 -0
- package/dist/vascend/context-store-reader.js +46 -0
- package/dist/vascend/context-window-compiler.d.ts +1 -0
- package/dist/vascend/context-window-compiler.js +1 -0
- package/dist/vascend/file-lock.d.ts +1 -0
- package/dist/vascend/file-lock.js +26 -0
- package/dist/vascend/granular-kingdom.d.ts +1 -0
- package/dist/vascend/granular-kingdom.js +52 -17
- package/dist/vascend/kingdom-bus.d.ts +1 -0
- package/dist/vascend/kingdom-bus.js +3 -0
- package/dist/vascend/kingdom-channel.d.ts +2 -0
- package/dist/vascend/kingdom-channel.js +3 -0
- package/dist/vascend/kingdom-live.d.ts +32 -4
- package/dist/vascend/kingdom-live.js +149 -13
- package/dist/vascend/kingdom-run.d.ts +5 -1
- package/dist/vascend/kingdom-run.js +6 -2
- package/dist/vascend/kingdom.d.ts +3 -0
- package/dist/vascend/kingdom.js +22 -11
- package/dist/vascend/memory-packet.d.ts +2 -1
- package/dist/vascend/memory-packet.js +31 -44
- package/dist/vascend/memory-store.d.ts +1 -0
- package/dist/vascend/memory-store.js +12 -4
- package/dist/vascend/plan-store.d.ts +3 -1
- package/dist/vascend/plan-store.js +6 -4
- package/dist/vascend/room-memory.d.ts +13 -2
- package/dist/vascend/room-memory.js +48 -6
- package/dist/vascend/runtime.d.ts +3 -11
- package/dist/vascend/runtime.js +11 -123
- package/dist/vascend/session-search.js +2 -0
- package/dist/vascend/team-session.js +3 -2
- package/dist/vascend/vision.d.ts +3 -1
- package/dist/vascend/vision.js +6 -4
- package/dist/vascend/worker-session.d.ts +20 -0
- package/dist/vascend/worker-session.js +83 -0
- package/package.json +2 -2
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { sanitizeText } from "../core/text-sanitize.js";
|
|
2
|
-
import { listMemoryScopes
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
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 =
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
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 =
|
|
23
|
-
const projectCastles =
|
|
24
|
-
const projectVisions =
|
|
25
|
-
const
|
|
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
|
|
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
|
-
|
|
67
|
-
|
|
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;
|
|
@@ -106,6 +106,8 @@ export class MemoryStore {
|
|
|
106
106
|
}
|
|
107
107
|
const n = pool.length || 1;
|
|
108
108
|
const unfiltered = !opts.domain && !opts.type && pool.length === this.events.length;
|
|
109
|
+
if (unfiltered)
|
|
110
|
+
this.ensureSearchIndex();
|
|
109
111
|
const df = unfiltered ? this.globalDf : new Map();
|
|
110
112
|
const docTokens = unfiltered ? this.docTokens : pool.map((e) => {
|
|
111
113
|
const toks = tokensForEvent(e);
|
|
@@ -218,11 +220,17 @@ export class MemoryStore {
|
|
|
218
220
|
const numeric = /^E(\d+)$/i.exec(event.id)?.[1];
|
|
219
221
|
if (numeric)
|
|
220
222
|
this.maxNumericId = Math.max(this.maxNumericId, Number(numeric));
|
|
221
|
-
const tokens = tokensForEvent(event);
|
|
222
223
|
this.events.push(event);
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
224
|
+
}
|
|
225
|
+
// Reading/forwarding events does not need a second full-text index. Build
|
|
226
|
+
// this local index only for search(), and extend it after concurrent appends.
|
|
227
|
+
ensureSearchIndex() {
|
|
228
|
+
for (let i = this.docTokens.length; i < this.events.length; i++) {
|
|
229
|
+
const tokens = tokensForEvent(this.events[i]);
|
|
230
|
+
this.docTokens.push(tokens);
|
|
231
|
+
for (const token of new Set(tokens))
|
|
232
|
+
this.globalDf.set(token, (this.globalDf.get(token) ?? 0) + 1);
|
|
233
|
+
}
|
|
226
234
|
}
|
|
227
235
|
}
|
|
228
236
|
function tokensForEvent(event) {
|
|
@@ -37,8 +37,10 @@ export interface KingdomPlan {
|
|
|
37
37
|
}
|
|
38
38
|
export declare class VascendPlanStore {
|
|
39
39
|
private readonly file;
|
|
40
|
-
private readonly
|
|
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
|
-
|
|
66
|
+
openMemory;
|
|
67
67
|
kingdom = { castles: [] };
|
|
68
|
-
|
|
68
|
+
memoryStore;
|
|
69
|
+
get memory() { return this.memoryStore ??= this.openMemory(); }
|
|
70
|
+
constructor(file, openMemory) {
|
|
69
71
|
this.file = file;
|
|
70
|
-
this.
|
|
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;
|
|
@@ -22,17 +29,21 @@ export declare const DEFAULT_RECENT_KEEP = 2;
|
|
|
22
29
|
export declare class RoomMemory {
|
|
23
30
|
private readonly file;
|
|
24
31
|
private readonly fanout;
|
|
25
|
-
private readonly
|
|
32
|
+
private readonly openMemory;
|
|
26
33
|
private records;
|
|
27
34
|
private synonyms;
|
|
28
35
|
private tokenCache;
|
|
29
36
|
private graphCache;
|
|
30
37
|
private readonly recentKeep;
|
|
38
|
+
private memoryStore?;
|
|
39
|
+
private get memory();
|
|
31
40
|
private constructor();
|
|
32
41
|
withSynonyms(groups: readonly (readonly string[])[]): this;
|
|
33
42
|
private tokensFor;
|
|
34
43
|
private indexedDocs;
|
|
35
44
|
static open(sessionDir: string, sessionId: string, fanout?: number): RoomMemory;
|
|
45
|
+
private persistMedia;
|
|
46
|
+
attachmentMedia(rooms: readonly string[]): readonly ImagePart[];
|
|
36
47
|
count(): number;
|
|
37
48
|
all(): readonly RoomRecord[];
|
|
38
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.
|
|
@@ -18,7 +23,7 @@ export const DEFAULT_RECENT_KEEP = 2;
|
|
|
18
23
|
export class RoomMemory {
|
|
19
24
|
file;
|
|
20
25
|
fanout;
|
|
21
|
-
|
|
26
|
+
openMemory;
|
|
22
27
|
records = [];
|
|
23
28
|
// Indice sinonimi (default; configurabile via withSynonyms) per espandere le
|
|
24
29
|
// query verso forme equivalenti IT/EN.
|
|
@@ -33,10 +38,12 @@ export class RoomMemory {
|
|
|
33
38
|
// cambiano mai corpo ne' archi dopo la scrittura).
|
|
34
39
|
graphCache = null;
|
|
35
40
|
recentKeep;
|
|
36
|
-
|
|
41
|
+
memoryStore;
|
|
42
|
+
get memory() { return this.memoryStore ??= this.openMemory(); }
|
|
43
|
+
constructor(file, fanout, openMemory) {
|
|
37
44
|
this.file = file;
|
|
38
45
|
this.fanout = fanout;
|
|
39
|
-
this.
|
|
46
|
+
this.openMemory = openMemory;
|
|
40
47
|
this.recentKeep = Math.max(0, Math.min(this.fanout - 1, DEFAULT_RECENT_KEEP));
|
|
41
48
|
}
|
|
42
49
|
// Sostituisce la tabella sinonimi (es. per dominio specifico). Ritorna this
|
|
@@ -60,7 +67,7 @@ export class RoomMemory {
|
|
|
60
67
|
}
|
|
61
68
|
static open(sessionDir, sessionId, fanout = DEFAULT_FANOUT) {
|
|
62
69
|
const file = join(sessionDir, `${sessionId}.rooms.jsonl`);
|
|
63
|
-
const mem = new RoomMemory(file, Math.max(2, fanout), MemoryStore.open(sessionDir, sessionId));
|
|
70
|
+
const mem = new RoomMemory(file, Math.max(2, fanout), () => MemoryStore.open(sessionDir, sessionId));
|
|
64
71
|
if (existsSync(file)) {
|
|
65
72
|
const raw = readFileSync(file, "utf8");
|
|
66
73
|
for (const line of raw.split("\n")) {
|
|
@@ -76,6 +83,31 @@ export class RoomMemory {
|
|
|
76
83
|
}
|
|
77
84
|
return mem;
|
|
78
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
|
+
}
|
|
79
111
|
count() {
|
|
80
112
|
return this.records.length;
|
|
81
113
|
}
|
|
@@ -100,6 +132,7 @@ export class RoomMemory {
|
|
|
100
132
|
// Aggiunge una stanza (livello 0): fast path append, protetto da lock
|
|
101
133
|
// file-system cosi' due processi concorrenti non interlacciano le righe.
|
|
102
134
|
append(record) {
|
|
135
|
+
this.persistMedia(record);
|
|
103
136
|
this.records.push(record);
|
|
104
137
|
mkdirSync(dirname(this.file), { recursive: true });
|
|
105
138
|
withLockSync(this.file, () => {
|
|
@@ -149,6 +182,7 @@ export class RoomMemory {
|
|
|
149
182
|
this.reloadFromDisk();
|
|
150
183
|
const id = this.nextRoomId(ns);
|
|
151
184
|
const rec = build(id);
|
|
185
|
+
this.persistMedia(rec);
|
|
152
186
|
this.records.push(rec);
|
|
153
187
|
appendFileSync(this.file, JSON.stringify(rec) + "\n", "utf8");
|
|
154
188
|
return rec;
|
|
@@ -327,7 +361,7 @@ export class RoomMemory {
|
|
|
327
361
|
const rows = front
|
|
328
362
|
.map((r, i) => {
|
|
329
363
|
const marker = i === last ? " <- TURNO PIU' RECENTE" : "";
|
|
330
|
-
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}`;
|
|
331
365
|
})
|
|
332
366
|
.join("\n");
|
|
333
367
|
return `ATTIVITA_ARCHIVIATE (ordine cronologico: prima le piu' vecchie, ultima la piu' recente)
|
|
@@ -369,6 +403,8 @@ function roomEvent(type, record, children = []) {
|
|
|
369
403
|
}
|
|
370
404
|
// Record Danilov compatto di una STANZA (livello 0) dai messaggi del turno.
|
|
371
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 } : {}) }));
|
|
372
408
|
const title = roomTitle(messages);
|
|
373
409
|
const tools = collectToolCalls(messages);
|
|
374
410
|
const files = collectFiles(messages);
|
|
@@ -380,6 +416,7 @@ export function formatRoomRecord(room, messages, createdAt) {
|
|
|
380
416
|
]).slice(0, 12);
|
|
381
417
|
const def = [
|
|
382
418
|
`@goal[${room}]: "${escapeValue(title)}"`,
|
|
419
|
+
...attachments.map(att => `@attachment[${att.id}]: ${att.mime}, source=${escapeValue(att.source || 'immagine')}; recall rooms=["${room}"] attachments=true`),
|
|
383
420
|
...tools.slice(0, 12).map((t, i) => `@tool[${room}.${i + 1}]: ${t.name}(${escapeValue(t.arg)}) -> ${escapeValue(t.result)}`),
|
|
384
421
|
...files.slice(0, 12).map((f, i) => `@file[${room}.${i + 1}]: ${escapeValue(f)}`),
|
|
385
422
|
].join("\n");
|
|
@@ -399,7 +436,10 @@ RELAZIONI
|
|
|
399
436
|
${rel}
|
|
400
437
|
|
|
401
438
|
OUTPUT: ${escapeValue(output) || "(nessun output testuale)"}`);
|
|
402
|
-
|
|
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;
|
|
403
443
|
}
|
|
404
444
|
// Record di ROLLUP (castello/regno/...): indice sui figli che ha chiuso.
|
|
405
445
|
export function formatRollupRecord(id, level, children, createdAt) {
|
|
@@ -437,6 +477,7 @@ OUTPUT: riepilogo delle attivita; apri i dettagli col tool recall.`);
|
|
|
437
477
|
terms,
|
|
438
478
|
body,
|
|
439
479
|
children: children.map((c) => c.room),
|
|
480
|
+
attachments: children.flatMap(c => c.attachments || []),
|
|
440
481
|
parent: null,
|
|
441
482
|
// Il padre eredita il timestamp del figlio piu' recente: con i gruppi per
|
|
442
483
|
// affinita' l'istante del rollup non direbbe nulla, mentre cosi' la
|
|
@@ -486,6 +527,7 @@ function normalize(raw) {
|
|
|
486
527
|
parent: raw.parent ?? null,
|
|
487
528
|
createdAt: typeof raw.createdAt === "number" ? raw.createdAt : 0,
|
|
488
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)) } : {}),
|
|
489
531
|
};
|
|
490
532
|
}
|
|
491
533
|
// Aggiorna solo le etichette generate dei vecchi riepiloghi. Gli ID,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type {
|
|
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(
|
|
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(
|
|
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
|
}
|
package/dist/vascend/runtime.js
CHANGED
|
@@ -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
|
-
|
|
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(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
123
|
-
|
|
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;
|
package/dist/vascend/vision.d.ts
CHANGED
|
@@ -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
|
|
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;
|