@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
package/dist/core/agent.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { contextLearningIdentity } from "../vascend/context-learning-identity.js";
|
|
2
2
|
import { trackSessionUsage } from "./session-usage.js";
|
|
3
|
+
import { nameSession } from "./session-naming.js";
|
|
3
4
|
import { quotaResetAt } from "../providers/quota-limits.js";
|
|
4
5
|
import { RequestBudget } from "./request-plan.js";
|
|
5
|
-
import {
|
|
6
|
+
import { maxInputFor, resolveMaxTokens } from "./model-limits.js";
|
|
7
|
+
import { projectToolResult, visibleOutputPages, boundHistoricalToolResults } from "./tool-artifacts.js";
|
|
6
8
|
import { contextWindowFor, estimateMessagesTokens, estimateTokens, estimateToolDefinitionTokens, IMAGE_TOKEN_ESTIMATE, planCompaction, planCompactionMove, protectedExcerpts, repairInstructions, summarizeMessages, verifySummary, withProtectedExcerpts, } from "./compaction.js";
|
|
7
9
|
import { RoomMemory, formatRoomRecord } from "../vascend/room-memory.js";
|
|
8
|
-
import {} from "./context-ledger.js";
|
|
10
|
+
import { buildContextLedger } from "./context-ledger.js";
|
|
9
11
|
import { applyMediaEviction, applyToolResultDemotion, mediaBudgetPolicy, planMediaEviction, planToolResultDemotion, slimMessageForSession, toolIndexPolicy, EMPTY_PLAN, } from "./tool-result-index.js";
|
|
10
12
|
import { createToolExecutor } from "./tool-executor.js";
|
|
11
13
|
import { ToolOutputStore } from "./tool-result-store.js";
|
|
@@ -25,6 +27,7 @@ import { DecisionStore, promoteDecisions, decisionToRoomRecord } from "../vascen
|
|
|
25
27
|
import { ContractStore, promoteContractChanges, contractToRoomRecord } from "../vascend/contract-memory.js";
|
|
26
28
|
import { VascendPlanStore } from "../vascend/plan-store.js";
|
|
27
29
|
import { pendingMembers, teamForSession, waitForTeamChange } from "../vascend/team-session.js";
|
|
30
|
+
import { resolveKingdomBus } from "../vascend/kingdom-channel.js";
|
|
28
31
|
import { getKingdomScribe } from "../vascend/kingdom-scribe.js";
|
|
29
32
|
import { toolCompressOptions } from "./tool-compress.js";
|
|
30
33
|
import { routeEffort, isRoutineBatch, effectiveToolName, effortRoutingEnabled } from "./effort.js";
|
|
@@ -38,14 +41,6 @@ import { RunTiming } from "./run-timing.js";
|
|
|
38
41
|
import { queuedConfirmation } from "./permission-queue.js";
|
|
39
42
|
import { workspaceMessageText, workspacePeersContext } from "./workspace-sessions.js";
|
|
40
43
|
const CONTEXT_READ_TOOLS = new Set(["read_file", "glob", "grep", "list_dir", "recall", "output_page", "vascend_status", "ram_status", "file_activity", "workspace_sessions", "tool_search", "tool_list", "tool_describe", "bash_output"]);
|
|
41
|
-
// Tetto di sicurezza ai turni di un singolo giro (anti-loop). Alto di default
|
|
42
|
-
// (i goal Danilov multi-castello richiedono molti turni) e configurabile via
|
|
43
|
-
// TALOS_MAX_TURNS. Al raggiungimento NON e' un errore fatale: il giro termina e
|
|
44
|
-
// la validazione del regno sveglia l'agente sulle stanze ancora al buio.
|
|
45
|
-
function maxTurns(env) {
|
|
46
|
-
const n = Number(env["TALOS_MAX_TURNS"]);
|
|
47
|
-
return Number.isFinite(n) && n > 0 ? n : 200;
|
|
48
|
-
}
|
|
49
44
|
// Quante volte auto-continuare un output troncato (stop=max_tokens) prima di
|
|
50
45
|
// arrendersi e chiedere di spezzare il compito. Configurabile via
|
|
51
46
|
// TALOS_MAX_AUTOCONTINUE. 0 = disabilita l'auto-continuazione.
|
|
@@ -152,9 +147,13 @@ function coerceParam(raw) {
|
|
|
152
147
|
return raw.replace(/^\n/, "").replace(/\n$/, "");
|
|
153
148
|
}
|
|
154
149
|
// Esegue un giro completo: dato l'input utente, alterna chiamate al modello
|
|
155
|
-
// ed esecuzione di tool finche' il modello non si ferma
|
|
156
|
-
//
|
|
150
|
+
// ed esecuzione di tool finche' il modello non si ferma, si verifica un errore
|
|
151
|
+
// o l'utente interrompe via signal. Nessun tetto al numero di turni.
|
|
157
152
|
export async function runTurn(deps, userInput, sink, signal) {
|
|
153
|
+
if (deps.config.fusion && !deps.kingdom) {
|
|
154
|
+
const { runFusionTurn } = await import("../fusion/run.js");
|
|
155
|
+
return runFusionTurn(deps, userInput, sink, signal, runTurn);
|
|
156
|
+
}
|
|
158
157
|
// Un collaboratore costruito copiando AgentDeps non assume l'identita'
|
|
159
158
|
// della sessione principale: il team ha gia' il proprio canale dedicato.
|
|
160
159
|
const channel = deps.workspaceSession?.owns(deps.config.cwd, deps.session.id, deps.executionTarget)
|
|
@@ -165,17 +164,31 @@ export async function runTurn(deps, userInput, sink, signal) {
|
|
|
165
164
|
const timing = new RunTiming(deps.config.sessionDir, deps.session.id, deps.env ?? process.env);
|
|
166
165
|
const releaseStorage = protectSessionStorage(deps.config.sessionDir, [deps.session.id, deps.config.roomStoreId, deps.config.planStoreId].filter((value) => Boolean(value)), deps.env ?? process.env, deps.config.cwd);
|
|
167
166
|
let failed = false;
|
|
167
|
+
const namingController = new AbortController();
|
|
168
|
+
const abortNaming = () => namingController.abort();
|
|
169
|
+
signal.addEventListener("abort", abortNaming, { once: true });
|
|
170
|
+
if (signal.aborted)
|
|
171
|
+
abortNaming();
|
|
172
|
+
const naming = (deps.env ?? process.env)["TALOS_AUTO_TITLE"] === "1" && !deps.kingdom
|
|
173
|
+
? nameSession(deps, text, sink, namingController.signal) : Promise.resolve();
|
|
168
174
|
try {
|
|
169
175
|
await runAgentLoop({ ...deps, workspaceSession: channel,
|
|
170
176
|
provider: trackSessionUsage(timing.provider(deps.provider), deps.config.sessionDir, deps.session.id, sink),
|
|
171
177
|
confirm: (action, detail) => timing.wait("permission", () => queuedConfirmation(deps.confirm, action, detail, signal)),
|
|
172
|
-
}, userInput, event => {
|
|
178
|
+
}, userInput, event => {
|
|
179
|
+
if (event.type === "status" && event.label.startsWith("Limite sessione di 5 ore raggiunto"))
|
|
180
|
+
abortNaming();
|
|
181
|
+
timing.event(event);
|
|
182
|
+
sink(event);
|
|
183
|
+
}, signal);
|
|
173
184
|
}
|
|
174
185
|
catch (error) {
|
|
175
186
|
failed = true;
|
|
176
187
|
throw error;
|
|
177
188
|
}
|
|
178
189
|
finally {
|
|
190
|
+
await naming;
|
|
191
|
+
signal.removeEventListener("abort", abortNaming);
|
|
179
192
|
timing.finish(signal.aborted, failed);
|
|
180
193
|
channel?.update({ state: "idle" });
|
|
181
194
|
releaseStorage();
|
|
@@ -257,6 +270,7 @@ userInput, sink, signal) {
|
|
|
257
270
|
// recall plan, supersession). Fallback sicuro al Memory Packet grezzo se il
|
|
258
271
|
// compiler fallisce per qualsiasi motivo, cosi' il turno non si rompe mai.
|
|
259
272
|
const memoryScopeId = config.roomStoreId ?? session.id;
|
|
273
|
+
const sharedProjectMemory = env["TALOS_SHARED_PROJECT_MEMORY"] === "1";
|
|
260
274
|
if (tools.compact) {
|
|
261
275
|
runtimeSystem += `\n\n${COMPACT_TOOL_GUIDANCE}`;
|
|
262
276
|
systemBlocks.push({ key: "tool-protocol", label: "Protocollo strumenti", text: COMPACT_TOOL_GUIDANCE, cacheable: true });
|
|
@@ -332,6 +346,10 @@ userInput, sink, signal) {
|
|
|
332
346
|
systemBlocks.splice(stableBlockCount);
|
|
333
347
|
turnBlocks.length = 0;
|
|
334
348
|
turnBlockRecords.length = 0;
|
|
349
|
+
if (config.turnSystem) {
|
|
350
|
+
turnBlocks.push(config.turnSystem);
|
|
351
|
+
turnBlockRecords.push({ key: "host-instructions", label: "Istruzioni operative dell'host", text: config.turnSystem, cacheable: false });
|
|
352
|
+
}
|
|
335
353
|
if (persistenceReminder)
|
|
336
354
|
addTurnBlock(persistenceReminder, { key: "nudge", label: "Promemoria di persistenza" });
|
|
337
355
|
// Il contesto dell'host non e' parte del system prompt ne' della cronologia:
|
|
@@ -352,12 +370,21 @@ userInput, sink, signal) {
|
|
|
352
370
|
cwd: config.cwd,
|
|
353
371
|
selection: config.vibeMode,
|
|
354
372
|
userText: latestUserText,
|
|
355
|
-
inheritProjectFrame: sessionWasEmpty,
|
|
373
|
+
inheritProjectFrame: sharedProjectMemory && sessionWasEmpty,
|
|
374
|
+
});
|
|
375
|
+
const inheritedFrame = fabric.frame.receipt?.inherited === true;
|
|
376
|
+
// Su una sessione vuota il frame appena creato deriva interamente dal
|
|
377
|
+
// messaggio corrente: reinviarlo come Fabric ripeterebbe lo stesso prompt
|
|
378
|
+
// in missione, contratto e revisioni. Il frame viene comunque persistito
|
|
379
|
+
// e sara' disponibile dal follow-up successivo.
|
|
380
|
+
const fabricBlock = horizonHost
|
|
381
|
+
? renderHorizonFabricBlock(fabric)
|
|
382
|
+
: sessionWasEmpty && !inheritedFrame ? "" : fabric.systemBlock;
|
|
383
|
+
if (fabricBlock)
|
|
384
|
+
addTurnBlock(fabricBlock, {
|
|
385
|
+
key: "fabric",
|
|
386
|
+
label: "Frame di progetto (Context Fabric)",
|
|
356
387
|
});
|
|
357
|
-
addTurnBlock(horizonHost ? renderHorizonFabricBlock(fabric) : fabric.systemBlock, {
|
|
358
|
-
key: "fabric",
|
|
359
|
-
label: "Frame di progetto (Context Fabric)",
|
|
360
|
-
});
|
|
361
388
|
}
|
|
362
389
|
catch {
|
|
363
390
|
// Context Fabric e' un acceleratore, non un single point of failure: uno
|
|
@@ -379,6 +406,7 @@ userInput, sink, signal) {
|
|
|
379
406
|
compileBase = {
|
|
380
407
|
sessionDir: config.sessionDir,
|
|
381
408
|
scopeId: memoryScopeId,
|
|
409
|
+
sharedProjectMemory,
|
|
382
410
|
...(config.planStoreId ? { planScopeId: config.planStoreId } : {}),
|
|
383
411
|
userText: fabric?.rawUserText ?? latestUserText,
|
|
384
412
|
capsuleMarket: capsuleMarketEnabled(env),
|
|
@@ -395,6 +423,7 @@ userInput, sink, signal) {
|
|
|
395
423
|
try {
|
|
396
424
|
snapshot = loadContextStoreSnapshot({
|
|
397
425
|
sessionDir: config.sessionDir, scopeId: memoryScopeId,
|
|
426
|
+
sharedProjectMemory,
|
|
398
427
|
indexed: env["TALOS_CONTEXT_INDEX"] !== "0",
|
|
399
428
|
...(config.planStoreId ? { planScopeId: config.planStoreId } : {}),
|
|
400
429
|
...(fabric ? { projectKey: fabric.frame.projectKey } : {}),
|
|
@@ -462,12 +491,12 @@ userInput, sink, signal) {
|
|
|
462
491
|
// dal turno) e fonde la config migliore nella baseline del progetto. Effetto
|
|
463
492
|
// collaterale utile: lascia lo snapshot degli store caldo per il turno dopo.
|
|
464
493
|
let memoryPacket = "";
|
|
465
|
-
if (!contextBlock || horizonHost) {
|
|
494
|
+
if (!contextBlock || horizonHost || sessionWasEmpty) {
|
|
466
495
|
memoryPacket = compileInput.snapshot.packet(12_000);
|
|
467
496
|
}
|
|
468
497
|
if (!contextBlock)
|
|
469
498
|
contextBlock = memoryPacket;
|
|
470
|
-
if (
|
|
499
|
+
if (!shouldIncludeHorizonContextPack({
|
|
471
500
|
sessionWasEmpty,
|
|
472
501
|
inheritedFrame: fabric?.frame.receipt?.inherited === true,
|
|
473
502
|
hasCompiledEvidence: Boolean(compiledContext?.report.cleaned.length),
|
|
@@ -512,6 +541,9 @@ userInput, sink, signal) {
|
|
|
512
541
|
// Il blocco NON viene persistito nella sessione: la cronologia su disco resta
|
|
513
542
|
// pulita e il prefisso inviato al modello resta byte-identico fra i turni.
|
|
514
543
|
const turnMessage = session.append({ role: "user", content: userParts }).message;
|
|
544
|
+
if (deps.inputMessage)
|
|
545
|
+
sink({ type: "user_message", id: deps.inputMessage.id, text: userText,
|
|
546
|
+
at: Date.now(), ...(deps.inputMessage.immediate ? { immediate: true } : {}) });
|
|
515
547
|
await host?.emit("agent_start", { input: userText });
|
|
516
548
|
// Prende in carico i messaggi arrivati durante il turno: li appende come
|
|
517
549
|
// messaggi utente veri (quindi entrano nel contesto del giro successivo) e
|
|
@@ -519,6 +551,7 @@ userInput, sink, signal) {
|
|
|
519
551
|
// l'interfaccia mostra come "letto". Ritorna true se ne ha consumato almeno
|
|
520
552
|
// uno, cosi' il chiamante sa che c'e' lavoro nuovo da servire.
|
|
521
553
|
const receivedPeerMessages = new Set();
|
|
554
|
+
const notifiedTeamMessages = new Set();
|
|
522
555
|
const consumeInbox = () => {
|
|
523
556
|
const arrivals = deps.inbox?.drain() ?? [];
|
|
524
557
|
if (arrivals.length) {
|
|
@@ -553,7 +586,6 @@ userInput, sink, signal) {
|
|
|
553
586
|
}
|
|
554
587
|
return arrivals.length > 0 || peerArrivals > 0;
|
|
555
588
|
};
|
|
556
|
-
const turnCap = maxTurns(env);
|
|
557
589
|
const requestBudget = new RequestBudget();
|
|
558
590
|
const toolCompress = toolCompressOptions(env);
|
|
559
591
|
// Archivio dei tool result: gli output voluminosi vengono indicizzati appena
|
|
@@ -577,7 +609,7 @@ userInput, sink, signal) {
|
|
|
577
609
|
// Effort routing: i turni di routine (digestione di letture) abbassano il
|
|
578
610
|
// reasoning; il primo turno e quelli con scrittura/errore tengono l'effort pieno.
|
|
579
611
|
const baseEffort = (config.effort ?? "");
|
|
580
|
-
const effortRouting = effortRoutingEnabled(env);
|
|
612
|
+
const effortRouting = effortRoutingEnabled(env, provider.id);
|
|
581
613
|
let routineBatch = false;
|
|
582
614
|
// Auto-continuazione su output troncato: se il modello si ferma per
|
|
583
615
|
// max_tokens (risposta tagliata al tetto), invece di chiudere la stanza con
|
|
@@ -588,6 +620,7 @@ userInput, sink, signal) {
|
|
|
588
620
|
const completionRepairCap = maxCompletionRepairs(env);
|
|
589
621
|
let emptyResponseRepairs = 0;
|
|
590
622
|
let transientGuardContext = "";
|
|
623
|
+
let stepDirective = null;
|
|
591
624
|
let planCompletionRepairs = 0;
|
|
592
625
|
let planTouchedThisRun = false;
|
|
593
626
|
let delegationObserved = false;
|
|
@@ -619,7 +652,28 @@ userInput, sink, signal) {
|
|
|
619
652
|
...openContractSymbols(deps),
|
|
620
653
|
].filter(Boolean)),
|
|
621
654
|
].slice(0, MAX_COMPACTION_ANCHORS);
|
|
622
|
-
for (let turn = 0
|
|
655
|
+
for (let turn = 0;; turn += 1) {
|
|
656
|
+
if (signal.aborted) {
|
|
657
|
+
await archiveCurrentRoom();
|
|
658
|
+
sink({ type: "error", message: "interrotto" });
|
|
659
|
+
await host?.emit("agent_end", {});
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
// I comandi manuali mutano la cronologia solo dopo un batch completo di
|
|
663
|
+
// tool, mai durante provider/tool concorrenti. Non diventano prompt finti.
|
|
664
|
+
const compactions = deps.inbox?.drainCompactions?.() ?? [];
|
|
665
|
+
for (const request of compactions) {
|
|
666
|
+
try {
|
|
667
|
+
const compacted = await compactNow(deps, signal, sink, request.instructions, compactionAnchors());
|
|
668
|
+
contextDirty ||= compacted;
|
|
669
|
+
sink({ type: "status", category: "context", label: compacted
|
|
670
|
+
? "Contesto compattato; proseguo nella stessa sessione."
|
|
671
|
+
: "Nessuna compattazione necessaria; contesto conservato." });
|
|
672
|
+
}
|
|
673
|
+
catch (err) {
|
|
674
|
+
sink({ type: "status", category: "context", label: `Compattazione non riuscita: ${err.message}; contesto conservato.` });
|
|
675
|
+
}
|
|
676
|
+
}
|
|
623
677
|
if (signal.aborted) {
|
|
624
678
|
await archiveCurrentRoom();
|
|
625
679
|
sink({ type: "error", message: "interrotto" });
|
|
@@ -630,6 +684,7 @@ userInput, sink, signal) {
|
|
|
630
684
|
if (env["TALOS_CONTEXT_LIVE"] !== "0") {
|
|
631
685
|
try {
|
|
632
686
|
const currentSnapshot = loadContextStoreSnapshot({ sessionDir: config.sessionDir, scopeId: memoryScopeId,
|
|
687
|
+
sharedProjectMemory,
|
|
633
688
|
...(config.planStoreId ? { planScopeId: config.planStoreId } : {}),
|
|
634
689
|
...(fabric ? { projectKey: fabric.frame.projectKey } : {}), indexed: env["TALOS_CONTEXT_INDEX"] !== "0" });
|
|
635
690
|
contextDirty ||= currentSnapshot.signature !== contextSignature;
|
|
@@ -650,7 +705,7 @@ userInput, sink, signal) {
|
|
|
650
705
|
// persisted, including nested execute calls and updates from collaborators.
|
|
651
706
|
if (completed.length && config.compaction.enabled && config.compaction.perRoom
|
|
652
707
|
&& policy.roomCompaction !== "never") {
|
|
653
|
-
const limits = workingContextLimits(policy, contextWindowFor(config.model, config.contextWindow), config.compaction.reserveTokens);
|
|
708
|
+
const limits = workingContextLimits(policy, contextWindowFor(config.model, config.contextWindow), config.compaction.reserveTokens, resolveMaxTokens(config.maxTokens, config.model), maxInputFor(config.model));
|
|
654
709
|
const activeTokens = contextTokens(deps, runtimeSystem) + estimateTokens(turnContext)
|
|
655
710
|
- planDemotion(session.history()).freedTokens
|
|
656
711
|
- planMediaEviction({ messages: session.history(), policy: mediaBudget }).images * IMAGE_TOKEN_ESTIMATE;
|
|
@@ -683,6 +738,7 @@ userInput, sink, signal) {
|
|
|
683
738
|
// successivo la sessione espone soltanto il riferimento testuale. Senza
|
|
684
739
|
// questa proiezione ogni nuova richiesta reinviava lo stesso base64 (e gli
|
|
685
740
|
// SVG storici potevano far fallire Anthropic per dimensioni nominali >8k).
|
|
741
|
+
messages = boundHistoricalToolResults(messages, outputStore, toolCompress);
|
|
686
742
|
messages = projectHistoricalPromptImages(messages, turnMessage);
|
|
687
743
|
// Budget delle immagini: le schermate vecchie lasciano la finestra. Prima
|
|
688
744
|
// della retrocessione del testo, perche' e' la voce piu' pesante di tutte
|
|
@@ -753,7 +809,10 @@ userInput, sink, signal) {
|
|
|
753
809
|
: {}),
|
|
754
810
|
};
|
|
755
811
|
let streamedText = "";
|
|
756
|
-
|
|
812
|
+
stepDirective = deps.stepPolicy
|
|
813
|
+
? await deps.stepPolicy.beforeStep({ step: turn, tools: tools.names(), signal }).catch(() => null) : null;
|
|
814
|
+
const requestTurnContext = [turnContext, scopeGuidance, transientGuardContext, peerContext, stepDirective?.guidance]
|
|
815
|
+
.filter(Boolean).join("\n\n");
|
|
757
816
|
// I guard interni valgono per una sola richiesta provider e non devono
|
|
758
817
|
// entrare nella cronologia come messaggi utente.
|
|
759
818
|
transientGuardContext = "";
|
|
@@ -776,6 +835,7 @@ userInput, sink, signal) {
|
|
|
776
835
|
onActivity: (label) => sink({ type: "status", label }),
|
|
777
836
|
onReasoning: (delta) => sink({ type: "reasoning", delta }),
|
|
778
837
|
}, ledgerInput);
|
|
838
|
+
sessionContextFrames.set(session, { ...ledgerInput, messages: undefined, now: undefined });
|
|
779
839
|
sink({ type: "context_window", ledger: requestPlan.ledger });
|
|
780
840
|
if (!requestPlan.fits) {
|
|
781
841
|
sink({ type: "error", message: "Il contesto supera la finestra del modello anche dopo la compattazione. La sessione resta recuperabile." });
|
|
@@ -901,12 +961,21 @@ userInput, sink, signal) {
|
|
|
901
961
|
// Ultimo controllo della casella prima di chiudere: se l'utente ha scritto
|
|
902
962
|
// mentre il modello stava componendo la risposta, il turno prosegue invece
|
|
903
963
|
// di terminare e costringerlo a un giro nuovo.
|
|
904
|
-
if (consumeInbox())
|
|
964
|
+
if (consumeInbox() || deps.inbox?.pendingCompactions)
|
|
905
965
|
continue;
|
|
906
966
|
const activeTeam = delegationObserved ? teamForSession(session.id) : null;
|
|
907
967
|
if (activeTeam && !activeTeam.done) {
|
|
908
968
|
sink({ type: "status", label: `team al lavoro: ${pendingMembers(activeTeam).join(", ")}; attendo avanzamento` });
|
|
909
|
-
|
|
969
|
+
const teamBus = resolveKingdomBus(process.env, config.sessionDir);
|
|
970
|
+
await waitForTeamChange(activeTeam, signal, () => {
|
|
971
|
+
if (deps.inbox?.pending || deps.inbox?.pendingCompactions)
|
|
972
|
+
return true;
|
|
973
|
+
const unread = teamBus.peekInbox(activeTeam.id, deps.kingdom?.self || "orchestratore")
|
|
974
|
+
.filter(message => !notifiedTeamMessages.has(`${activeTeam.id}:${message.seq}`));
|
|
975
|
+
for (const message of unread)
|
|
976
|
+
notifiedTeamMessages.add(`${activeTeam.id}:${message.seq}`);
|
|
977
|
+
return unread.length > 0;
|
|
978
|
+
});
|
|
910
979
|
transientGuardContext = "[TALOS_TEAM_GUARD]\nIl team avviato deve ancora essere raccolto e verificato. "
|
|
911
980
|
+ "Leggi team_report e team_inbox, rispondi ai blocchi e verifica i risultati prima della consegna. "
|
|
912
981
|
+ "Non dichiarare concluso il lavoro solo perche' l'ingaggio e' partito.";
|
|
@@ -1042,6 +1111,10 @@ userInput, sink, signal) {
|
|
|
1042
1111
|
// a indice di stanze, cosi' resta sempre compatto. Il dettaglio si
|
|
1043
1112
|
// recupera col tool recall.
|
|
1044
1113
|
await archiveCurrentRoom();
|
|
1114
|
+
// L'archiviazione puo' attendere I/O: non perdere un invio o comando
|
|
1115
|
+
// arrivato dopo il primo controllo della finale e prima del done.
|
|
1116
|
+
if (consumeInbox() || deps.inbox?.pendingCompactions)
|
|
1117
|
+
continue;
|
|
1045
1118
|
sink({ type: "done" });
|
|
1046
1119
|
await host?.emit("agent_end", {});
|
|
1047
1120
|
return;
|
|
@@ -1049,6 +1122,7 @@ userInput, sink, signal) {
|
|
|
1049
1122
|
// Esegue i tool richiesti e raccoglie i risultati in un turno "tool".
|
|
1050
1123
|
const ctx = {
|
|
1051
1124
|
cwd: config.cwd,
|
|
1125
|
+
waitInterrupted: () => Boolean(deps.inbox?.pending || deps.inbox?.pendingCompactions),
|
|
1052
1126
|
...(deps.workspaceSession ? { workspaceSession: deps.workspaceSession } : {}),
|
|
1053
1127
|
...(deps.executionTarget ? { executionTarget: deps.executionTarget } : {}),
|
|
1054
1128
|
...(deps.fsRoots !== undefined ? { fsRoots: deps.fsRoots } : {}),
|
|
@@ -1077,6 +1151,9 @@ userInput, sink, signal) {
|
|
|
1077
1151
|
const executeCall = createToolExecutor(ctx, {
|
|
1078
1152
|
onCall: (call, parentId) => sink({ type: "tool_call", call, ...(parentId ? { parentId } : {}) }),
|
|
1079
1153
|
before: async (call) => {
|
|
1154
|
+
if (stepDirective?.admit && !stepDirective.admit(call.name)) {
|
|
1155
|
+
return { block: true, reason: stepDirective.rejection ?? "tool non ammesso in questo passo" };
|
|
1156
|
+
}
|
|
1080
1157
|
let blocked;
|
|
1081
1158
|
for (const output of await host?.emit("tool_call", { toolName: call.name, input: { ...call.input } }) ?? []) {
|
|
1082
1159
|
if (output.block)
|
|
@@ -1127,6 +1204,9 @@ userInput, sink, signal) {
|
|
|
1127
1204
|
results.push(part);
|
|
1128
1205
|
sink({ type: "tool_result", result: part });
|
|
1129
1206
|
}
|
|
1207
|
+
deps.stepPolicy?.afterStep?.({ step: turn, final: false,
|
|
1208
|
+
tools: toolCalls.map(call => effectiveToolName(call.name, call.input)),
|
|
1209
|
+
errors: results.filter(result => result.isError).length });
|
|
1130
1210
|
if (toolCalls.some((call, index) => PLAN_COMPLETION_TOOLS.has(effectiveToolName(call.name, call.input))
|
|
1131
1211
|
&& results[index]?.isError !== true)) {
|
|
1132
1212
|
planTouchedThisRun = true;
|
|
@@ -1196,9 +1276,6 @@ Il piano ha ${collaborators.length} filoni `
|
|
|
1196
1276
|
transientGuardContext = [transientGuardContext, hint].filter(Boolean).join("\n\n");
|
|
1197
1277
|
}
|
|
1198
1278
|
}
|
|
1199
|
-
await host?.emit("agent_end", {});
|
|
1200
|
-
await archiveCurrentRoom();
|
|
1201
|
-
sink({ type: "error", message: `pausa al tetto di ${turnCap} turni per questo giro (TALOS_MAX_TURNS per alzarlo); la validazione del piano riprende le attivita rimaste` });
|
|
1202
1279
|
}
|
|
1203
1280
|
// Archivia la stanza appena illuminata: costruisce il record Danilov dei nodi
|
|
1204
1281
|
// di questo turno e lo persiste nella memoria delle stanze. L'archiviazione e'
|
|
@@ -1270,7 +1347,7 @@ async function archiveRoom(deps, roomStartHead, sink, policy, signal, anchors) {
|
|
|
1270
1347
|
const firstKeptId = head;
|
|
1271
1348
|
if (policy.roomCompaction === "threshold") {
|
|
1272
1349
|
const window = contextWindowFor(config.model, config.contextWindow);
|
|
1273
|
-
const limits = workingContextLimits(policy, window, config.compaction.reserveTokens);
|
|
1350
|
+
const limits = workingContextLimits(policy, window, config.compaction.reserveTokens, resolveMaxTokens(config.maxTokens, config.model), maxInputFor(config.model));
|
|
1274
1351
|
if (projectedContextTokens(deps) <= limits.checkpoint)
|
|
1275
1352
|
return;
|
|
1276
1353
|
await compactNow(deps, signal, sink, undefined, anchors);
|
|
@@ -1622,6 +1699,9 @@ function projectCompactionMessages(deps, messages) {
|
|
|
1622
1699
|
// Compattazione gia' avviata e non ancora conclusa, per sessione. Single-flight:
|
|
1623
1700
|
// due turni ravvicinati non devono riassumere due volte la stessa cronologia.
|
|
1624
1701
|
const pendingCompaction = new WeakMap();
|
|
1702
|
+
// Ultima composizione runtime: una compattazione idle non deve misurare solo
|
|
1703
|
+
// la cronologia o usare l'usage cumulativo del provider come finestra attiva.
|
|
1704
|
+
const sessionContextFrames = new WeakMap();
|
|
1625
1705
|
// Compatta se la soglia e' superata. No-op se disabilitata o non necessario.
|
|
1626
1706
|
//
|
|
1627
1707
|
// La compattazione e' il nodo piu' costoso del giro: una chiamata al modello
|
|
@@ -1642,7 +1722,7 @@ savedTokens = 0,
|
|
|
1642
1722
|
system = deps.config.system) {
|
|
1643
1723
|
const { compaction } = deps.config;
|
|
1644
1724
|
const inFlight = pendingCompaction.get(deps.session);
|
|
1645
|
-
const limits = workingContextLimits(contextPolicy(deps.env ?? process.env), contextWindowFor(deps.config.model, deps.config.contextWindow), compaction.reserveTokens);
|
|
1725
|
+
const limits = workingContextLimits(contextPolicy(deps.env ?? process.env), contextWindowFor(deps.config.model, deps.config.contextWindow), compaction.reserveTokens, resolveMaxTokens(deps.config.maxTokens, deps.config.model), maxInputFor(deps.config.model));
|
|
1646
1726
|
const move = planCompactionMove({
|
|
1647
1727
|
enabled: compaction.enabled,
|
|
1648
1728
|
// Il pack compilato e il contesto IDE sono effimeri e quindi non vivono
|
|
@@ -1697,8 +1777,8 @@ export async function compactNow(deps, signal, sink, instructions, anchors = [])
|
|
|
1697
1777
|
async function performCompaction(deps, signal, sink, instructions, anchors = []) {
|
|
1698
1778
|
const { session, config } = deps;
|
|
1699
1779
|
const provider = trackSessionUsage(deps.provider, config.sessionDir, session.id, sink);
|
|
1700
|
-
const fullChain = session.nodeChain();
|
|
1701
1780
|
const previousCompaction = session.lastCompaction;
|
|
1781
|
+
const fullChain = session.nodeChain(previousCompaction?.firstKeptId);
|
|
1702
1782
|
const liveStart = previousCompaction
|
|
1703
1783
|
? fullChain.findIndex((node) => node.id === previousCompaction.firstKeptId)
|
|
1704
1784
|
: 0;
|
|
@@ -1727,52 +1807,72 @@ async function performCompaction(deps, signal, sink, instructions, anchors = [])
|
|
|
1727
1807
|
signal,
|
|
1728
1808
|
...(previousCompaction?.summary && liveStart >= 0 ? { previousSummary: previousCompaction.summary } : {}),
|
|
1729
1809
|
};
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
});
|
|
1734
|
-
if (!summary)
|
|
1735
|
-
return false;
|
|
1736
|
-
let missing = verifySummary(summary, anchors, plan.toKeep);
|
|
1737
|
-
if (missing.length) {
|
|
1738
|
-
const repaired = await summarizeMessages(provider, plan.toSummarize, {
|
|
1810
|
+
sink({ type: "compacting" });
|
|
1811
|
+
try {
|
|
1812
|
+
let summary = await summarizeMessages(provider, plan.toSummarize, {
|
|
1739
1813
|
...base,
|
|
1740
|
-
instructions:
|
|
1814
|
+
...(summaryInstructions ? { instructions: summaryInstructions } : {}),
|
|
1741
1815
|
});
|
|
1742
|
-
if (
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
if (stillMissing.length <= missing.length) {
|
|
1746
|
-
summary = repaired;
|
|
1747
|
-
missing = stillMissing;
|
|
1748
|
-
}
|
|
1749
|
-
}
|
|
1750
|
-
}
|
|
1751
|
-
// Ultima rete: cio' che il modello continua a non dire lo diciamo noi, con
|
|
1752
|
-
// le sue stesse parole. Nelle compattazioni successive il summary precedente
|
|
1753
|
-
// precede la live chain: protectedExcerpts cerca dall'ultimo elemento, quindi
|
|
1754
|
-
// il dettaglio live piu' recente vince e il summary resta il fallback storico.
|
|
1755
|
-
if (missing.length) {
|
|
1816
|
+
if (!summary)
|
|
1817
|
+
return false;
|
|
1818
|
+
let missing = verifySummary(summary, anchors, plan.toKeep);
|
|
1756
1819
|
const excerptSources = [
|
|
1757
1820
|
...(previousCompaction?.summary && liveStart >= 0
|
|
1758
1821
|
? [{ role: "user", content: [{ type: "text", text: previousCompaction.summary }] }]
|
|
1759
1822
|
: []),
|
|
1760
1823
|
...plan.toSummarize,
|
|
1761
1824
|
];
|
|
1762
|
-
|
|
1825
|
+
if (missing.length) {
|
|
1826
|
+
// Repair the existing summary with the missing evidence instead of
|
|
1827
|
+
// sending the entire archived transcript to the model a second time.
|
|
1828
|
+
const repairEvidence = [{ role: "user", content: [{
|
|
1829
|
+
type: "text", text: protectedExcerpts(excerptSources, missing).join("\n"),
|
|
1830
|
+
}] }];
|
|
1831
|
+
const repaired = await summarizeMessages(provider, repairEvidence, {
|
|
1832
|
+
...base,
|
|
1833
|
+
previousSummary: summary,
|
|
1834
|
+
instructions: repairInstructions(missing, summaryInstructions),
|
|
1835
|
+
});
|
|
1836
|
+
if (repaired) {
|
|
1837
|
+
const stillMissing = verifySummary(repaired, anchors, plan.toKeep);
|
|
1838
|
+
// Si tiene il tentativo migliore: quello che perde meno ancore.
|
|
1839
|
+
if (stillMissing.length <= missing.length) {
|
|
1840
|
+
summary = repaired;
|
|
1841
|
+
missing = stillMissing;
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
// Ultima rete: cio' che il modello continua a non dire lo diciamo noi, con
|
|
1846
|
+
// le sue stesse parole. Nelle compattazioni successive il summary precedente
|
|
1847
|
+
// precede la live chain: protectedExcerpts cerca dall'ultimo elemento, quindi
|
|
1848
|
+
// il dettaglio live piu' recente vince e il summary resta il fallback storico.
|
|
1849
|
+
if (missing.length) {
|
|
1850
|
+
summary = withProtectedExcerpts(summary, protectedExcerpts(excerptSources, missing));
|
|
1851
|
+
}
|
|
1852
|
+
// A speculative summary may finish after new messages arrive. Keep that tail
|
|
1853
|
+
// intact, but reject a result for a branch that is no longer current.
|
|
1854
|
+
if (signal.aborted || session.lastCompaction !== previousCompaction
|
|
1855
|
+
|| !session.nodeChain(snapshotHead ?? undefined).some(node => node.id === snapshotHead))
|
|
1856
|
+
return false;
|
|
1857
|
+
const checkpoint = compactionCheckpoint(checkpointTasks(config, session.id, deps.kingdom?.self), messages, plan.cutIndex);
|
|
1858
|
+
if (checkpoint)
|
|
1859
|
+
summary += `\n\n${checkpoint}`;
|
|
1860
|
+
session.compact(summary, firstKept.id, tokensBefore);
|
|
1861
|
+
const tokensAfter = projectedContextTokens(deps);
|
|
1862
|
+
sink({ type: "compacted", tokensBefore, tokensAfter, reason: "summary", ...(anchors.length ? { anchors: [...anchors] } : {}) });
|
|
1863
|
+
const frame = sessionContextFrames.get(session);
|
|
1864
|
+
sink({ type: "context_window", ledger: buildContextLedger({
|
|
1865
|
+
model: config.model, contextWindow: config.contextWindow, reserveTokens: config.compaction.reserveTokens,
|
|
1866
|
+
workingContextTokens: contextPolicy(deps.env ?? process.env).workingContextTokens,
|
|
1867
|
+
system: config.system, ...frame, tools: deps.tools.defs(),
|
|
1868
|
+
messages: projectCompactionMessages(deps, session.history()), demoted: null,
|
|
1869
|
+
compaction: { ts: session.lastCompaction.ts, tokensBefore, summary },
|
|
1870
|
+
}) });
|
|
1871
|
+
return true;
|
|
1872
|
+
}
|
|
1873
|
+
finally {
|
|
1874
|
+
sink({ type: "compaction_end" });
|
|
1763
1875
|
}
|
|
1764
|
-
// A speculative summary may finish after new messages arrive. Keep that tail
|
|
1765
|
-
// intact, but reject a result for a branch that is no longer current.
|
|
1766
|
-
if (signal.aborted || session.lastCompaction !== previousCompaction
|
|
1767
|
-
|| !session.nodeChain().some(node => node.id === snapshotHead))
|
|
1768
|
-
return false;
|
|
1769
|
-
const checkpoint = compactionCheckpoint(checkpointTasks(config, session.id, deps.kingdom?.self), messages, plan.cutIndex);
|
|
1770
|
-
if (checkpoint)
|
|
1771
|
-
summary += `\n\n${checkpoint}`;
|
|
1772
|
-
session.compact(summary, firstKept.id, tokensBefore);
|
|
1773
|
-
const tokensAfter = projectedContextTokens(deps);
|
|
1774
|
-
sink({ type: "compacted", tokensBefore, tokensAfter, reason: "summary", ...(anchors.length ? { anchors: [...anchors] } : {}) });
|
|
1775
|
-
return true;
|
|
1776
1876
|
}
|
|
1777
1877
|
// Modello del compattatore: riassumere e' un lavoro meccanico e non ha bisogno
|
|
1778
1878
|
// del modello di punta. TALOS_COMPACT_MODEL permette di dirottarlo su uno piu'
|
|
@@ -13,14 +13,16 @@ export interface BgTask {
|
|
|
13
13
|
}
|
|
14
14
|
interface Foreground {
|
|
15
15
|
readonly taskId: string;
|
|
16
|
+
readonly sessionId?: string;
|
|
16
17
|
background(): void;
|
|
17
18
|
}
|
|
18
19
|
export declare class BackgroundManager {
|
|
19
20
|
private readonly tasks;
|
|
20
21
|
private readonly children;
|
|
21
22
|
private readonly order;
|
|
23
|
+
private readonly completedOrder;
|
|
22
24
|
private seq;
|
|
23
|
-
private
|
|
25
|
+
private readonly foregrounds;
|
|
24
26
|
private readonly changeCbs;
|
|
25
27
|
private readonly finishCbs;
|
|
26
28
|
createTask(command: string): BgTask;
|
|
@@ -29,11 +31,16 @@ export declare class BackgroundManager {
|
|
|
29
31
|
finishTask(id: string, status: BgStatus, exitCode?: number): void;
|
|
30
32
|
setForeground(fg: Foreground): void;
|
|
31
33
|
clearForeground(id?: string): void;
|
|
32
|
-
|
|
34
|
+
private foreground;
|
|
35
|
+
hasForeground(sessionId?: string): boolean;
|
|
33
36
|
markDetached(id: string): void;
|
|
34
|
-
requestBackground(): BgTask | null;
|
|
37
|
+
requestBackground(sessionId?: string): BgTask | null;
|
|
35
38
|
kill(id: string): boolean;
|
|
36
39
|
get(id: string): BgTask | undefined;
|
|
40
|
+
waitForTask(id: string, signal: AbortSignal, options?: {
|
|
41
|
+
readonly timeoutMs?: number;
|
|
42
|
+
readonly interrupted?: () => boolean;
|
|
43
|
+
}): Promise<void>;
|
|
37
44
|
list(): readonly BgTask[];
|
|
38
45
|
running(): readonly BgTask[];
|
|
39
46
|
onChange(cb: () => void): () => void;
|
package/dist/core/background.js
CHANGED
|
@@ -25,8 +25,9 @@ export class BackgroundManager {
|
|
|
25
25
|
tasks = new Map();
|
|
26
26
|
children = new Map();
|
|
27
27
|
order = [];
|
|
28
|
+
completedOrder = [];
|
|
28
29
|
seq = 0;
|
|
29
|
-
|
|
30
|
+
foregrounds = new Map();
|
|
30
31
|
changeCbs = new Set();
|
|
31
32
|
finishCbs = new Set();
|
|
32
33
|
createTask(command) {
|
|
@@ -56,6 +57,7 @@ export class BackgroundManager {
|
|
|
56
57
|
t.endedAt = Date.now();
|
|
57
58
|
if (exitCode !== undefined)
|
|
58
59
|
t.exitCode = exitCode;
|
|
60
|
+
this.completedOrder.push(id);
|
|
59
61
|
this.children.delete(id);
|
|
60
62
|
this.emitChange();
|
|
61
63
|
for (const cb of this.finishCbs)
|
|
@@ -63,10 +65,17 @@ export class BackgroundManager {
|
|
|
63
65
|
this.pruneHistory(id);
|
|
64
66
|
}
|
|
65
67
|
// Registra il comando in primo piano (chiamato dal tool bash all'avvio).
|
|
66
|
-
setForeground(fg) { this.fg
|
|
67
|
-
clearForeground(id) {
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
setForeground(fg) { this.foregrounds.set(fg.taskId, fg); }
|
|
69
|
+
clearForeground(id) {
|
|
70
|
+
if (id)
|
|
71
|
+
this.foregrounds.delete(id);
|
|
72
|
+
else
|
|
73
|
+
this.foregrounds.clear();
|
|
74
|
+
}
|
|
75
|
+
foreground(sessionId) {
|
|
76
|
+
return [...this.foregrounds.values()].findLast(fg => sessionId === undefined || fg.sessionId === sessionId);
|
|
77
|
+
}
|
|
78
|
+
hasForeground(sessionId) { return !!this.foreground(sessionId); }
|
|
70
79
|
markDetached(id) {
|
|
71
80
|
const task = this.tasks.get(id);
|
|
72
81
|
if (task && !task.detached) {
|
|
@@ -76,11 +85,11 @@ export class BackgroundManager {
|
|
|
76
85
|
}
|
|
77
86
|
// Ctrl+B: stacca il comando in primo piano. Ritorna il task o null se non c'e'
|
|
78
87
|
// niente in primo piano.
|
|
79
|
-
requestBackground() {
|
|
80
|
-
const fg = this.
|
|
88
|
+
requestBackground(sessionId) {
|
|
89
|
+
const fg = this.foreground(sessionId);
|
|
81
90
|
if (!fg)
|
|
82
91
|
return null;
|
|
83
|
-
this.fg
|
|
92
|
+
this.foregrounds.delete(fg.taskId);
|
|
84
93
|
const t = this.tasks.get(fg.taskId);
|
|
85
94
|
if (t)
|
|
86
95
|
t.detached = true;
|
|
@@ -97,6 +106,41 @@ export class BackgroundManager {
|
|
|
97
106
|
return true;
|
|
98
107
|
}
|
|
99
108
|
get(id) { return this.tasks.get(id); }
|
|
109
|
+
// Wait in the host, without asking the model to poll unchanged output.
|
|
110
|
+
// Keep the task reference alive even if completed history is pruned.
|
|
111
|
+
async waitForTask(id, signal, options = {}) {
|
|
112
|
+
signal.throwIfAborted();
|
|
113
|
+
const task = this.tasks.get(id);
|
|
114
|
+
if (!task || task.status !== "running" || options.interrupted?.())
|
|
115
|
+
return;
|
|
116
|
+
await new Promise((resolve, reject) => {
|
|
117
|
+
let timeout;
|
|
118
|
+
let interruptCheck;
|
|
119
|
+
const cleanup = () => {
|
|
120
|
+
unsubscribe();
|
|
121
|
+
signal.removeEventListener("abort", abort);
|
|
122
|
+
clearTimeout(timeout);
|
|
123
|
+
clearInterval(interruptCheck);
|
|
124
|
+
};
|
|
125
|
+
const finish = () => { cleanup(); resolve(); };
|
|
126
|
+
const abort = () => { cleanup(); reject(signal.reason); };
|
|
127
|
+
const unsubscribe = this.onFinish(completed => { if (completed.id === id)
|
|
128
|
+
finish(); });
|
|
129
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
130
|
+
if (options.timeoutMs !== undefined)
|
|
131
|
+
timeout = setTimeout(finish, options.timeoutMs);
|
|
132
|
+
// Only check local user input; elapsed time/output never wakes the model.
|
|
133
|
+
if (options.interrupted)
|
|
134
|
+
interruptCheck = setInterval(() => {
|
|
135
|
+
if (options.interrupted?.())
|
|
136
|
+
finish();
|
|
137
|
+
}, 100);
|
|
138
|
+
if (signal.aborted)
|
|
139
|
+
abort();
|
|
140
|
+
else if (task.status !== "running")
|
|
141
|
+
finish();
|
|
142
|
+
});
|
|
143
|
+
}
|
|
100
144
|
list() {
|
|
101
145
|
// Ogni comando shell nasce come candidato al Ctrl+B, ma diventa davvero
|
|
102
146
|
// un'attivita' in background soltanto quando l'utente lo stacca. Esporre
|
|
@@ -111,7 +155,7 @@ export class BackgroundManager {
|
|
|
111
155
|
emitChange() { for (const cb of this.changeCbs)
|
|
112
156
|
cb(); }
|
|
113
157
|
pruneHistory(latestId) {
|
|
114
|
-
const completedDetached = this.
|
|
158
|
+
const completedDetached = this.completedOrder.filter((id) => {
|
|
115
159
|
const task = this.tasks.get(id);
|
|
116
160
|
return Boolean(task?.detached && task.status !== "running");
|
|
117
161
|
});
|
|
@@ -134,6 +178,8 @@ export class BackgroundManager {
|
|
|
134
178
|
}
|
|
135
179
|
const retainedOrder = this.order.filter((id) => keep.has(id));
|
|
136
180
|
this.order.splice(0, this.order.length, ...retainedOrder);
|
|
181
|
+
const retainedCompleted = this.completedOrder.filter(id => keep.has(id));
|
|
182
|
+
this.completedOrder.splice(0, this.completedOrder.length, ...retainedCompleted);
|
|
137
183
|
}
|
|
138
184
|
}
|
|
139
185
|
// Istanza condivisa di processo.
|
package/dist/core/bootstrap.js
CHANGED