@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,7 +1,7 @@
|
|
|
1
1
|
import { runTurn } from "../core/agent.js";
|
|
2
2
|
import { Session } from "../core/session.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { createWorkerInbox } from "./kingdom-live.js";
|
|
4
|
+
import { createWorkerSession } from "./worker-session.js";
|
|
5
5
|
import { disjointAreas } from "./team-areas.js";
|
|
6
6
|
import { fileActor, withFileActor } from "../core/file-coordination.js";
|
|
7
7
|
import { launchGranular, granularConcurrency, formatGranularReport, } from "./granular-launcher.js";
|
|
@@ -39,6 +39,8 @@ function nestedTaskFields(slug) {
|
|
|
39
39
|
function nestedActivity(slug, event) {
|
|
40
40
|
if (event.type === "kingdom")
|
|
41
41
|
return event;
|
|
42
|
+
if (event.type === "usage")
|
|
43
|
+
return { ...event, record: { ...event.record, actor: event.record.actor ?? slug } };
|
|
42
44
|
if (event.type === "turn_start")
|
|
43
45
|
return {
|
|
44
46
|
type: "kingdom", phase: "vassal", slug, state: "active", label: "Pianifica il task", ...nestedTaskFields(slug),
|
|
@@ -63,9 +65,13 @@ function nestedActivity(slug, event) {
|
|
|
63
65
|
};
|
|
64
66
|
}
|
|
65
67
|
if (event.type === "tool_call")
|
|
66
|
-
return {
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
return { type: "kingdom", phase: "tool", slug,
|
|
69
|
+
callId: event.call.id, tool: event.call.name, args: JSON.stringify(event.call.input).slice(0, 2400),
|
|
70
|
+
state: "start", at: Date.now() };
|
|
71
|
+
if (event.type === "tool_result")
|
|
72
|
+
return { type: "kingdom", phase: "tool", slug,
|
|
73
|
+
callId: event.result.callId, tool: "", output: event.result.output.slice(0, 12_000),
|
|
74
|
+
state: event.result.isError ? "error" : "ok", at: Date.now() };
|
|
69
75
|
if (event.type === "done")
|
|
70
76
|
return {
|
|
71
77
|
type: "kingdom", phase: "vassal", slug, state: "done", ...nestedTaskFields(slug),
|
|
@@ -108,7 +114,7 @@ OBIETTIVO_UNITA'
|
|
|
108
114
|
}
|
|
109
115
|
// Costruisce il TaskRunner che esegue una unita' come sessione di agente.
|
|
110
116
|
export function makeAgentUnitRunner(opts) {
|
|
111
|
-
const { harness, kingdomId, castleSlug,
|
|
117
|
+
const { harness, kingdomId, castleSlug, confirm, cwd, isolated } = opts;
|
|
112
118
|
const peers = opts.units.map((u) => u.id);
|
|
113
119
|
const depth = opts.depth ?? 2;
|
|
114
120
|
const maxDepth = maxTeamDepth(opts);
|
|
@@ -144,6 +150,8 @@ export function makeAgentUnitRunner(opts) {
|
|
|
144
150
|
...(cwd ? { cwd } : {}),
|
|
145
151
|
};
|
|
146
152
|
const self = `${castleSlug}/${unit.id}`;
|
|
153
|
+
let controller = new AbortController();
|
|
154
|
+
const signal = controller.signal;
|
|
147
155
|
const spawnKingdom = depth < maxDepth ? async (specs) => {
|
|
148
156
|
const units = nestedUnits(specs);
|
|
149
157
|
if (!units.length)
|
|
@@ -173,8 +181,16 @@ export function makeAgentUnitRunner(opts) {
|
|
|
173
181
|
...(route[1] ? { taskRef: route[1] } : {}),
|
|
174
182
|
});
|
|
175
183
|
}
|
|
184
|
+
const textBuffers = new Map();
|
|
185
|
+
const flushText = (slug) => {
|
|
186
|
+
const text = textBuffers.get(slug)?.trim();
|
|
187
|
+
textBuffers.delete(slug);
|
|
188
|
+
if (text)
|
|
189
|
+
ctx.emit({ type: "kingdom", phase: "say", slug, text: text.slice(0, 12_000), at: Date.now() });
|
|
190
|
+
};
|
|
176
191
|
const report = await launchGranularKingdom({
|
|
177
192
|
...opts,
|
|
193
|
+
signal: controller.signal,
|
|
178
194
|
harness: { ...harness, config: runConfig, tools: workerTools },
|
|
179
195
|
castleSlug: self,
|
|
180
196
|
units,
|
|
@@ -182,7 +198,21 @@ export function makeAgentUnitRunner(opts) {
|
|
|
182
198
|
maxDepth,
|
|
183
199
|
markCastle: undefined,
|
|
184
200
|
onEvent: (id, event) => {
|
|
185
|
-
const
|
|
201
|
+
const slug = `${self}/${id}`;
|
|
202
|
+
// Preserve raw child events for the host before the compact parent summary.
|
|
203
|
+
if (event.type !== "kingdom" && event.type !== "usage")
|
|
204
|
+
opts.onEvent?.(`${unit.id}/${id}`, event);
|
|
205
|
+
if (event.type === "text") {
|
|
206
|
+
const text = (textBuffers.get(slug) ?? "") + event.delta;
|
|
207
|
+
textBuffers.set(slug, text);
|
|
208
|
+
if (text.length >= 480)
|
|
209
|
+
flushText(slug);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (event.type === "tool_call" || event.type === "turn_end" || event.type === "done" || event.type === "error"
|
|
213
|
+
|| (event.type === "kingdom" && event.phase === "session" && event.state === "despawned"))
|
|
214
|
+
flushText(slug);
|
|
215
|
+
const mapped = nestedActivity(slug, event);
|
|
186
216
|
if (mapped)
|
|
187
217
|
ctx.emit(mapped);
|
|
188
218
|
},
|
|
@@ -192,13 +222,14 @@ export function makeAgentUnitRunner(opts) {
|
|
|
192
222
|
} : undefined;
|
|
193
223
|
// Anche il sotto-collaboratore ha la sua casella: la scheda laterale non
|
|
194
224
|
// distingue i livelli, e nemmeno chi ci scrive dentro deve farlo.
|
|
195
|
-
const
|
|
196
|
-
|
|
225
|
+
const binding = createWorkerInbox();
|
|
226
|
+
const inbox = binding.inbox;
|
|
197
227
|
const deps = {
|
|
198
228
|
provider: resolved.provider,
|
|
199
229
|
tools: workerTools,
|
|
200
230
|
session,
|
|
201
231
|
config: runConfig,
|
|
232
|
+
env: harness.env,
|
|
202
233
|
confirm,
|
|
203
234
|
ask: opts.ask,
|
|
204
235
|
host: undefined,
|
|
@@ -227,18 +258,22 @@ export function makeAgentUnitRunner(opts) {
|
|
|
227
258
|
+ (unit.assignment?.instructions ? `\nISTRUZIONI_AGGIUNTIVE\n${unit.assignment.instructions}\n` : "")
|
|
228
259
|
+ (ctx.prerequisites?.length ? "\nPREREQUISITI_COMPLETATI (risultati da verificare)\n"
|
|
229
260
|
+ ctx.prerequisites.map((result) => `[${result.id}] ${result.summary}`).join("\n").slice(0, 6000) + "\n" : "") + unit.goal;
|
|
261
|
+
const worker = createWorkerSession({ kingdomId, slug: self, sessionId: session.id, goal: unit.goal,
|
|
262
|
+
parentSignal: ctx.signal, controller, binding, deps, sink, retain: !isolated ? opts.retainSessions ?? false : false,
|
|
263
|
+
onController: next => { controller = next; } });
|
|
230
264
|
try {
|
|
231
|
-
|
|
265
|
+
sink({ type: "kingdom", phase: "session", kingdomId, sessionId: session.id,
|
|
266
|
+
slug: self, state: "spawned", goal: unit.goal, at: Date.now() });
|
|
267
|
+
await runTurn(deps, input, sink, signal);
|
|
268
|
+
if (signal.aborted)
|
|
269
|
+
throw new Error("interrotta");
|
|
270
|
+
if (runError || !completed)
|
|
271
|
+
throw new Error(runError || "unita' terminata senza completamento verificato");
|
|
272
|
+
return { summary: summary.trim() };
|
|
232
273
|
}
|
|
233
274
|
finally {
|
|
234
|
-
|
|
235
|
-
kingdomLive.release(kingdomId, self);
|
|
275
|
+
worker.finish();
|
|
236
276
|
}
|
|
237
|
-
if (ctx.signal.aborted)
|
|
238
|
-
throw new Error("interrotta");
|
|
239
|
-
if (runError || !completed)
|
|
240
|
-
throw new Error(runError || "unita' terminata senza completamento verificato");
|
|
241
|
-
return { summary: summary.trim() };
|
|
242
277
|
};
|
|
243
278
|
}
|
|
244
279
|
// Endpoint completo: lancia le unita' granulari di un castello in parallelo.
|
|
@@ -19,6 +19,7 @@ export declare class KingdomBus {
|
|
|
19
19
|
private seq;
|
|
20
20
|
post(kingdomId: string, from: string, to: string | null, text: string, ts: number): KingdomMessage;
|
|
21
21
|
inbox(kingdomId: string, me: string, filter?: (message: KingdomMessage) => boolean): KingdomMessage[];
|
|
22
|
+
peekInbox(kingdomId: string, me: string): readonly KingdomMessage[];
|
|
22
23
|
history(kingdomId: string): readonly KingdomMessage[];
|
|
23
24
|
clear(kingdomId: string): void;
|
|
24
25
|
}
|
|
@@ -41,6 +41,9 @@ export class KingdomBus {
|
|
|
41
41
|
return read.messages;
|
|
42
42
|
}
|
|
43
43
|
// Tutti i messaggi del regno (per il riepilogo/log), senza toccare i cursori.
|
|
44
|
+
peekInbox(kingdomId, me) {
|
|
45
|
+
return consumeInbox(this.boards.get(kingdomId) ?? [], me, this.cursors.get(`${kingdomId}::${me}`) ?? { last: 0 }).messages;
|
|
46
|
+
}
|
|
44
47
|
history(kingdomId) {
|
|
45
48
|
return this.boards.get(kingdomId) ?? [];
|
|
46
49
|
}
|
|
@@ -8,6 +8,7 @@ export declare class PersistentKingdomChannel {
|
|
|
8
8
|
constructor(sessionDir: string, opts?: KingdomChannelOptions);
|
|
9
9
|
post(kingdomId: string, from: string, to: string | null, text: string, ts: number): KingdomMessage;
|
|
10
10
|
inbox(kingdomId: string, me: string, filter?: (message: KingdomMessage) => boolean): KingdomMessage[];
|
|
11
|
+
peekInbox(kingdomId: string, me: string): readonly KingdomMessage[];
|
|
11
12
|
history(kingdomId: string): readonly KingdomMessage[];
|
|
12
13
|
clear(kingdomId: string): void;
|
|
13
14
|
}
|
|
@@ -15,6 +16,7 @@ export declare function getKingdomChannel(sessionDir: string, opts?: KingdomChan
|
|
|
15
16
|
export interface KingdomBusLike {
|
|
16
17
|
post(kingdomId: string, from: string, to: string | null, text: string, ts: number): KingdomMessage;
|
|
17
18
|
inbox(kingdomId: string, me: string, filter?: (message: KingdomMessage) => boolean): KingdomMessage[];
|
|
19
|
+
peekInbox(kingdomId: string, me: string): readonly KingdomMessage[];
|
|
18
20
|
history(kingdomId: string): readonly KingdomMessage[];
|
|
19
21
|
clear(kingdomId: string): void;
|
|
20
22
|
}
|
|
@@ -127,6 +127,9 @@ export class PersistentKingdomChannel {
|
|
|
127
127
|
});
|
|
128
128
|
}
|
|
129
129
|
// Tutti i messaggi del regno (per riepilogo/log), senza toccare i cursori.
|
|
130
|
+
peekInbox(kingdomId, me) {
|
|
131
|
+
return consumeInbox(readBoard(busFile(this.sessionDir, kingdomId)), me, readCursors(cursorsFile(this.sessionDir, kingdomId))[me] ?? { last: 0 }).messages;
|
|
132
|
+
}
|
|
130
133
|
history(kingdomId) {
|
|
131
134
|
return readBoard(busFile(this.sessionDir, kingdomId));
|
|
132
135
|
}
|
|
@@ -1,9 +1,20 @@
|
|
|
1
|
-
import type
|
|
2
|
-
|
|
1
|
+
import { type PendingUserMessage, type UserInbox } from "../core/user-inbox.js";
|
|
2
|
+
import type { ContentPart } from "../core/types.js";
|
|
3
|
+
export interface LiveVassalOptions {
|
|
4
|
+
readonly contextSource?: Pick<import("../core/agent.js").AgentDeps, "config" | "env" | "session" | "tools">;
|
|
5
|
+
readonly stop?: () => void;
|
|
6
|
+
readonly wake?: () => void;
|
|
7
|
+
readonly bindInbox?: (inbox: UserInbox) => void;
|
|
8
|
+
readonly close?: () => void;
|
|
9
|
+
}
|
|
10
|
+
export interface LiveVassal extends LiveVassalOptions {
|
|
3
11
|
readonly kingdomId: string;
|
|
4
12
|
readonly slug: string;
|
|
5
13
|
readonly inbox: UserInbox;
|
|
6
14
|
readonly since: number;
|
|
15
|
+
readonly stop?: () => void;
|
|
16
|
+
stopping?: boolean;
|
|
17
|
+
active?: boolean;
|
|
7
18
|
}
|
|
8
19
|
export interface DeliveryOutcome {
|
|
9
20
|
readonly ok: boolean;
|
|
@@ -12,9 +23,14 @@ export interface DeliveryOutcome {
|
|
|
12
23
|
}
|
|
13
24
|
export declare class KingdomLive {
|
|
14
25
|
private readonly vassals;
|
|
15
|
-
register(kingdomId: string, slug: string, inbox: UserInbox, now?: number): void;
|
|
26
|
+
register(kingdomId: string, slug: string, inbox: UserInbox, now?: number, options?: LiveVassalOptions): void;
|
|
16
27
|
release(kingdomId: string, slug: string): void;
|
|
17
28
|
has(kingdomId: string, slug: string): boolean;
|
|
29
|
+
reachable(kingdomId: string, slug: string): boolean;
|
|
30
|
+
contextSource(kingdomId: string, slug: string): LiveVassalOptions["contextSource"];
|
|
31
|
+
setActive(kingdomId: string, slug: string, active: boolean): void;
|
|
32
|
+
bindInbox(kingdomId: string, slug: string, inbox: UserInbox): DeliveryOutcome;
|
|
33
|
+
wake(kingdomId: string, slug: string): DeliveryOutcome;
|
|
18
34
|
/** Slug dei collaboratori che stanno lavorando adesso in questo regno. */
|
|
19
35
|
slugs(kingdomId: string): readonly string[];
|
|
20
36
|
/**
|
|
@@ -22,7 +38,19 @@ export declare class KingdomLive {
|
|
|
22
38
|
* letto: ritorna l'id, cosi' l'host puo' dire "in attesa" e poi "letto"
|
|
23
39
|
* quando arriva l'evento `user_message` di quel vassallo.
|
|
24
40
|
*/
|
|
25
|
-
deliver(kingdomId: string, slug: string, text: string, messageId?: string): DeliveryOutcome;
|
|
41
|
+
deliver(kingdomId: string, slug: string, text: string, messageId?: string, parts?: readonly ContentPart[], immediate?: boolean): DeliveryOutcome;
|
|
42
|
+
/** Accoda la compattazione reale; runTurn la esegue al prossimo confine sicuro. */
|
|
43
|
+
compact(kingdomId: string, slug: string, instructions?: string): DeliveryOutcome;
|
|
44
|
+
/** Richiede l'arresto del solo worker. Il finally del runtime rilascia il registro. */
|
|
45
|
+
stop(kingdomId: string, slug: string): DeliveryOutcome;
|
|
26
46
|
clear(kingdomId: string): void;
|
|
47
|
+
clearAll(): void;
|
|
27
48
|
}
|
|
28
49
|
export declare const kingdomLive: KingdomLive;
|
|
50
|
+
/** Casella stabile del loop, anche quando l'host collega la sua coda interattiva. */
|
|
51
|
+
export declare function createWorkerInbox(): {
|
|
52
|
+
inbox: UserInbox;
|
|
53
|
+
bind: (inbox: UserInbox) => void;
|
|
54
|
+
take: () => PendingUserMessage | undefined;
|
|
55
|
+
dispose: () => void;
|
|
56
|
+
};
|
|
@@ -8,54 +8,190 @@
|
|
|
8
8
|
// chat principale), che l'agente svuota da solo al primo confine sicuro, subito
|
|
9
9
|
// dopo gli esiti dei tool.
|
|
10
10
|
//
|
|
11
|
-
// Registro in-memory di processo, chiave `${kingdomId}::${slug}`.
|
|
12
|
-
//
|
|
13
|
-
//
|
|
11
|
+
// Registro in-memory di processo, chiave `${kingdomId}::${slug}`. Il runtime
|
|
12
|
+
// rilascia i worker effimeri al finally; gli host possono conservare sessioni
|
|
13
|
+
// idle con un vero controller di ripresa, fino a clear o abort del padre.
|
|
14
|
+
import { createUserInbox } from "../core/user-inbox.js";
|
|
14
15
|
const key = (kingdomId, slug) => `${kingdomId}::${slug}`;
|
|
15
16
|
export class KingdomLive {
|
|
16
17
|
vassals = new Map();
|
|
17
|
-
register(kingdomId, slug, inbox, now = Date.now()) {
|
|
18
|
+
register(kingdomId, slug, inbox, now = Date.now(), options = {}) {
|
|
18
19
|
const id = String(kingdomId || "").trim();
|
|
19
20
|
const who = String(slug || "").trim();
|
|
20
21
|
if (!id || !who)
|
|
21
22
|
return;
|
|
22
|
-
this.
|
|
23
|
+
this.release(id, who);
|
|
24
|
+
this.vassals.set(key(id, who), { kingdomId: id, slug: who, inbox, since: now, active: true, ...options });
|
|
23
25
|
}
|
|
24
26
|
release(kingdomId, slug) {
|
|
25
|
-
|
|
27
|
+
const id = key(String(kingdomId || "").trim(), String(slug || "").trim());
|
|
28
|
+
const live = this.vassals.get(id);
|
|
29
|
+
this.vassals.delete(id);
|
|
30
|
+
live?.close?.();
|
|
26
31
|
}
|
|
27
32
|
has(kingdomId, slug) {
|
|
33
|
+
return Boolean(this.vassals.get(key(String(kingdomId || "").trim(), String(slug || "").trim()))?.active);
|
|
34
|
+
}
|
|
35
|
+
reachable(kingdomId, slug) {
|
|
28
36
|
return this.vassals.has(key(String(kingdomId || "").trim(), String(slug || "").trim()));
|
|
29
37
|
}
|
|
38
|
+
contextSource(kingdomId, slug) {
|
|
39
|
+
return this.vassals.get(key(kingdomId, slug))?.contextSource;
|
|
40
|
+
}
|
|
41
|
+
setActive(kingdomId, slug, active) {
|
|
42
|
+
const live = this.vassals.get(key(String(kingdomId || "").trim(), String(slug || "").trim()));
|
|
43
|
+
if (live) {
|
|
44
|
+
live.active = active;
|
|
45
|
+
live.stopping = false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
bindInbox(kingdomId, slug, inbox) {
|
|
49
|
+
const live = this.vassals.get(key(String(kingdomId || "").trim(), String(slug || "").trim()));
|
|
50
|
+
if (!live)
|
|
51
|
+
return { ok: false, error: "collaboratore non piu' attivo" };
|
|
52
|
+
if (!live.bindInbox)
|
|
53
|
+
return { ok: false, error: "coda esterna non disponibile" };
|
|
54
|
+
try {
|
|
55
|
+
live.bindInbox(inbox);
|
|
56
|
+
return { ok: true };
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
return { ok: false, error: err.message };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
wake(kingdomId, slug) {
|
|
63
|
+
const live = this.vassals.get(key(String(kingdomId || "").trim(), String(slug || "").trim()));
|
|
64
|
+
if (!live || live.stopping)
|
|
65
|
+
return { ok: false, error: "collaboratore non piu' attivo" };
|
|
66
|
+
try {
|
|
67
|
+
live.wake?.();
|
|
68
|
+
return { ok: true };
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
return { ok: false, error: err.message };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
30
74
|
/** Slug dei collaboratori che stanno lavorando adesso in questo regno. */
|
|
31
75
|
slugs(kingdomId) {
|
|
32
76
|
const id = String(kingdomId || "").trim();
|
|
33
|
-
return [...this.vassals.values()].filter((v) => v.kingdomId === id).map((v) => v.slug);
|
|
77
|
+
return [...this.vassals.values()].filter((v) => v.kingdomId === id && v.active).map((v) => v.slug);
|
|
34
78
|
}
|
|
35
79
|
/**
|
|
36
80
|
* Consegna un messaggio dell'utente a un collaboratore. Non attende che sia
|
|
37
81
|
* letto: ritorna l'id, cosi' l'host puo' dire "in attesa" e poi "letto"
|
|
38
82
|
* quando arriva l'evento `user_message` di quel vassallo.
|
|
39
83
|
*/
|
|
40
|
-
deliver(kingdomId, slug, text, messageId) {
|
|
84
|
+
deliver(kingdomId, slug, text, messageId, parts, immediate = false) {
|
|
41
85
|
const body = String(text ?? "").trim();
|
|
42
|
-
|
|
86
|
+
const content = parts ?? body;
|
|
87
|
+
if (typeof content === "string" ? !content : !content.some((part) => part.type !== "text" || part.text.trim())) {
|
|
43
88
|
return { ok: false, error: "messaggio vuoto" };
|
|
89
|
+
}
|
|
44
90
|
const live = this.vassals.get(key(String(kingdomId || "").trim(), String(slug || "").trim()));
|
|
45
|
-
if (!live)
|
|
91
|
+
if (!live || live.stopping)
|
|
92
|
+
return { ok: false, error: "collaboratore non piu' attivo" };
|
|
93
|
+
try {
|
|
94
|
+
const id = live.inbox.push(content, messageId);
|
|
95
|
+
if (immediate)
|
|
96
|
+
live.inbox.promote(id);
|
|
97
|
+
live.wake?.();
|
|
98
|
+
return { ok: true, messageId: id };
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
return { ok: false, error: err.message };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** Accoda la compattazione reale; runTurn la esegue al prossimo confine sicuro. */
|
|
105
|
+
compact(kingdomId, slug, instructions) {
|
|
106
|
+
const live = this.vassals.get(key(String(kingdomId || "").trim(), String(slug || "").trim()));
|
|
107
|
+
if (!live || live.stopping)
|
|
46
108
|
return { ok: false, error: "collaboratore non piu' attivo" };
|
|
109
|
+
if (!live.inbox.requestCompact)
|
|
110
|
+
return { ok: false, error: "compattazione non disponibile per questo collaboratore" };
|
|
47
111
|
try {
|
|
48
|
-
|
|
112
|
+
live.inbox.requestCompact(instructions);
|
|
113
|
+
live.wake?.();
|
|
114
|
+
return { ok: true };
|
|
49
115
|
}
|
|
50
116
|
catch (err) {
|
|
51
117
|
return { ok: false, error: err.message };
|
|
52
118
|
}
|
|
53
119
|
}
|
|
120
|
+
/** Richiede l'arresto del solo worker. Il finally del runtime rilascia il registro. */
|
|
121
|
+
stop(kingdomId, slug) {
|
|
122
|
+
const live = this.vassals.get(key(String(kingdomId || "").trim(), String(slug || "").trim()));
|
|
123
|
+
if (!live || live.stopping)
|
|
124
|
+
return { ok: false, error: "collaboratore non piu' attivo" };
|
|
125
|
+
if (!live.active)
|
|
126
|
+
return { ok: false, error: "collaboratore gia' inattivo" };
|
|
127
|
+
if (!live.stop)
|
|
128
|
+
return { ok: false, error: "arresto non disponibile per questo collaboratore" };
|
|
129
|
+
live.stopping = true;
|
|
130
|
+
try {
|
|
131
|
+
live.stop();
|
|
132
|
+
return { ok: true };
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
live.stopping = false;
|
|
136
|
+
return { ok: false, error: err.message };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
54
139
|
clear(kingdomId) {
|
|
55
140
|
const id = String(kingdomId || "").trim();
|
|
56
|
-
for (const
|
|
141
|
+
for (const v of [...this.vassals.values()])
|
|
57
142
|
if (v.kingdomId === id)
|
|
58
|
-
this.
|
|
143
|
+
this.release(v.kingdomId, v.slug);
|
|
144
|
+
}
|
|
145
|
+
clearAll() {
|
|
146
|
+
for (const v of [...this.vassals.values()])
|
|
147
|
+
this.release(v.kingdomId, v.slug);
|
|
59
148
|
}
|
|
60
149
|
}
|
|
61
150
|
export const kingdomLive = new KingdomLive();
|
|
151
|
+
/** Casella stabile del loop, anche quando l'host collega la sua coda interattiva. */
|
|
152
|
+
export function createWorkerInbox() {
|
|
153
|
+
let taken = [];
|
|
154
|
+
let current = createUserInbox();
|
|
155
|
+
const controls = createUserInbox();
|
|
156
|
+
const subscriptions = new Map();
|
|
157
|
+
const inbox = {
|
|
158
|
+
push: (input, id) => current.push(input, id),
|
|
159
|
+
promote: id => current.promote(id),
|
|
160
|
+
get pending() { return taken.length + current.pending; },
|
|
161
|
+
get queued() { return taken.length + (current.queued ?? current.pending); },
|
|
162
|
+
drain: () => { const pending = [...taken, ...current.drain()]; taken = []; return pending; },
|
|
163
|
+
onImmediate(listener) {
|
|
164
|
+
subscriptions.set(listener, current.onImmediate?.(listener) ?? (() => { }));
|
|
165
|
+
return () => { subscriptions.get(listener)?.(); subscriptions.delete(listener); };
|
|
166
|
+
},
|
|
167
|
+
requestCompact: instructions => controls.requestCompact(instructions),
|
|
168
|
+
drainCompactions: () => controls.drainCompactions(),
|
|
169
|
+
get pendingCompactions() { return controls.pendingCompactions; },
|
|
170
|
+
};
|
|
171
|
+
return { inbox,
|
|
172
|
+
take() { taken.push(...current.drain()); return taken.shift(); },
|
|
173
|
+
bind(next) {
|
|
174
|
+
if (next === current || next === inbox)
|
|
175
|
+
return;
|
|
176
|
+
const pending = current.drain();
|
|
177
|
+
for (const off of subscriptions.values())
|
|
178
|
+
off();
|
|
179
|
+
current = next;
|
|
180
|
+
for (const message of pending) {
|
|
181
|
+
current.push(message.parts, message.id);
|
|
182
|
+
if (message.immediate)
|
|
183
|
+
current.promote(message.id);
|
|
184
|
+
}
|
|
185
|
+
for (const listener of subscriptions.keys())
|
|
186
|
+
subscriptions.set(listener, current.onImmediate?.(listener) ?? (() => { }));
|
|
187
|
+
},
|
|
188
|
+
dispose() {
|
|
189
|
+
for (const off of subscriptions.values())
|
|
190
|
+
off();
|
|
191
|
+
subscriptions.clear();
|
|
192
|
+
taken = [];
|
|
193
|
+
current = createUserInbox();
|
|
194
|
+
controls.drainCompactions?.();
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import type { EventSink, ToolContext } from "../core/types.js";
|
|
1
|
+
import type { AgentEvent, EventSink, ToolContext } from "../core/types.js";
|
|
2
2
|
import type { Harness } from "../core/bootstrap.js";
|
|
3
3
|
import { type CastleSpec, type VassalReport } from "./kingdom.js";
|
|
4
4
|
import type { FileCoordinationContext } from "../core/file-coordination.js";
|
|
5
5
|
export interface KingdomRunInput {
|
|
6
|
+
readonly kingdomId?: string;
|
|
7
|
+
readonly resumeSessions?: Readonly<Record<string, string>>;
|
|
8
|
+
readonly retainSessions?: boolean | "queued";
|
|
9
|
+
readonly onWorkerEvent?: (slug: string, event: AgentEvent) => void;
|
|
6
10
|
readonly harness: Harness;
|
|
7
11
|
readonly castles: readonly CastleSpec[];
|
|
8
12
|
readonly signal: AbortSignal;
|
|
@@ -35,7 +35,7 @@ export async function runKingdomForHost(input) {
|
|
|
35
35
|
// La sessione dura quanto la conversazione, uno swarm no. Un id riusato
|
|
36
36
|
// faceva fondere nella UI il regno concluso con quello successivo.
|
|
37
37
|
kingdomRunSequence = (kingdomRunSequence + 1) % 0xfffff;
|
|
38
|
-
const kingdomId = `kw-${harness.session.id}-${Date.now().toString(36)}-${kingdomRunSequence.toString(36)}`;
|
|
38
|
+
const kingdomId = input.kingdomId ?? `kw-${harness.session.id}-${Date.now().toString(36)}-${kingdomRunSequence.toString(36)}`;
|
|
39
39
|
const concurrency = warmConcurrency(env);
|
|
40
40
|
const worktree = worktreeMode(env);
|
|
41
41
|
// Bus pulito per il nuovo regno (in memoria e sul canale persistente).
|
|
@@ -89,6 +89,7 @@ export async function runKingdomForHost(input) {
|
|
|
89
89
|
sink({ type: "kingdom", phase: "say", slug, text: buffered.slice(0, SAY_MAX), at: Date.now() });
|
|
90
90
|
};
|
|
91
91
|
const onEvent = (slug, event) => {
|
|
92
|
+
input.onWorkerEvent?.(slug, event);
|
|
92
93
|
if (event.type === "usage") {
|
|
93
94
|
sink({ ...event, record: { ...event.record, actor: event.record.actor ?? slug } });
|
|
94
95
|
return;
|
|
@@ -312,6 +313,8 @@ export async function runKingdomForHost(input) {
|
|
|
312
313
|
signal,
|
|
313
314
|
confirm,
|
|
314
315
|
ask: input.ask,
|
|
316
|
+
retainSessions: input.retainSessions,
|
|
317
|
+
resumeSessions: input.resumeSessions,
|
|
315
318
|
concurrency,
|
|
316
319
|
onEvent,
|
|
317
320
|
onMemberState: (slug, status, report) => {
|
|
@@ -364,7 +367,8 @@ export async function runKingdomForHost(input) {
|
|
|
364
367
|
// Ultime parole rimaste nel buffer di chi ha finito senza un confine di turno.
|
|
365
368
|
for (const slug of [...saying.keys()])
|
|
366
369
|
flushSay(slug);
|
|
367
|
-
|
|
370
|
+
if (!input.retainSessions)
|
|
371
|
+
kingdomLive.clear(kingdomId);
|
|
368
372
|
for (const report of reports) {
|
|
369
373
|
updateTeamMember(kingdomId, report.slug, { status: report.ok ? "completed" : "failed", summary: report.summary, error: report.error, artifacts: report.artifacts });
|
|
370
374
|
sink({
|
|
@@ -45,6 +45,9 @@ export interface ConflictEntry {
|
|
|
45
45
|
export declare function collectConflicts(reports: readonly VassalReport[]): ConflictEntry[];
|
|
46
46
|
export declare function formatConflictList(conflicts: readonly ConflictEntry[], base?: string): string;
|
|
47
47
|
export interface KingdomWarmOptions {
|
|
48
|
+
/** Explicit persisted worker sessions for hosts that resume a named worker. */
|
|
49
|
+
readonly resumeSessions?: Readonly<Record<string, string>>;
|
|
50
|
+
readonly retainSessions?: boolean | "queued";
|
|
48
51
|
readonly harness: Harness;
|
|
49
52
|
readonly kingdomId: string;
|
|
50
53
|
readonly castles: readonly CastleSpec[];
|
package/dist/vascend/kingdom.js
CHANGED
|
@@ -4,8 +4,8 @@ import { existsSync } from "node:fs";
|
|
|
4
4
|
import { resolve, sep } from "node:path";
|
|
5
5
|
import { runTurn } from "../core/agent.js";
|
|
6
6
|
import { Session } from "../core/session.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { createWorkerInbox } from "./kingdom-live.js";
|
|
8
|
+
import { createWorkerSession } from "./worker-session.js";
|
|
9
9
|
import { fileActor, withFileActor } from "../core/file-coordination.js";
|
|
10
10
|
import { RoomMemory } from "./room-memory.js";
|
|
11
11
|
import { VascendPlanStore } from "./plan-store.js";
|
|
@@ -661,6 +661,7 @@ function subTeamSpawner(opts, castle, mandate, iso, planStoreId) {
|
|
|
661
661
|
const report = await launchGranularKingdom({
|
|
662
662
|
harness: opts.harness,
|
|
663
663
|
kingdomId: opts.kingdomId,
|
|
664
|
+
retainSessions: opts.retainSessions,
|
|
664
665
|
castleSlug: castle.slug,
|
|
665
666
|
units,
|
|
666
667
|
signal: opts.signal,
|
|
@@ -691,10 +692,14 @@ function subTeamSpawner(opts, castle, mandate, iso, planStoreId) {
|
|
|
691
692
|
};
|
|
692
693
|
}
|
|
693
694
|
async function runVassal(opts, provider, modelId, displayModel, castle, peers, iso, prerequisites = []) {
|
|
694
|
-
const { harness, kingdomId, signal, confirm, onEvent } = opts;
|
|
695
|
+
const { harness, kingdomId, signal: parentSignal, confirm, onEvent } = opts;
|
|
696
|
+
let controller = new AbortController();
|
|
697
|
+
const signal = controller.signal;
|
|
695
698
|
// Sessione (conversazione) dedicata al vassallo; store memoria CONDIVISO del
|
|
696
699
|
// regno (kingdomId) con id stanze namespaced sullo slug del castello.
|
|
697
|
-
const
|
|
700
|
+
const resumeId = opts.resumeSessions?.[castle.slug];
|
|
701
|
+
const session = resumeId ? Session.load(harness.config.sessionDir, resumeId)
|
|
702
|
+
: Session.create(harness.config.sessionDir, `${kingdomId}-${castle.slug}`);
|
|
698
703
|
const effort = ["low", "medium", "high", "max"].includes(String(castle.effort ?? "").toLowerCase())
|
|
699
704
|
? String(castle.effort).toLowerCase()
|
|
700
705
|
: "";
|
|
@@ -719,8 +724,8 @@ async function runVassal(opts, provider, modelId, displayModel, castle, peers, i
|
|
|
719
724
|
// Casella del vassallo: l'utente puo' scrivergli mentre lavora dalla sua
|
|
720
725
|
// scheda, e il messaggio entra nel contesto al primo confine sicuro come in
|
|
721
726
|
// chat principale. Senza, l'unico modo di correggerlo era fermare il regno.
|
|
722
|
-
const
|
|
723
|
-
|
|
727
|
+
const binding = createWorkerInbox();
|
|
728
|
+
const inbox = binding.inbox;
|
|
724
729
|
const workerTools = harness.tools.scoped(castle.tools);
|
|
725
730
|
for (const name of ["team_say", "team_inbox", "team_wait", "team_report", "vascend_status"])
|
|
726
731
|
workerTools.promote(name);
|
|
@@ -729,6 +734,9 @@ async function runVassal(opts, provider, modelId, displayModel, castle, peers, i
|
|
|
729
734
|
tools: workerTools,
|
|
730
735
|
session,
|
|
731
736
|
config: runConfig,
|
|
737
|
+
env: harness.env,
|
|
738
|
+
fsRoots: harness.fsRoots,
|
|
739
|
+
executionTarget: harness.executionTarget,
|
|
732
740
|
confirm,
|
|
733
741
|
ask: opts.ask,
|
|
734
742
|
host: undefined,
|
|
@@ -739,7 +747,8 @@ async function runVassal(opts, provider, modelId, displayModel, castle, peers, i
|
|
|
739
747
|
// Delega di secondo livello: un castello grande si gestisce formando un team
|
|
740
748
|
// sulle proprie stanze. Prima questo campo mancava, e kingdoms_warm dentro
|
|
741
749
|
// un vassallo rispondeva "non disponibile": la gerarchia era ferma a 1.
|
|
742
|
-
spawnKingdom: subTeamSpawner({ ...opts,
|
|
750
|
+
spawnKingdom: (specs) => subTeamSpawner({ ...opts, signal: controller.signal,
|
|
751
|
+
harness: { ...harness, config: runConfig, tools: workerTools } }, castle, mandate, iso, planStoreId)(specs),
|
|
743
752
|
// isolated abilita il git nel vassallo (worktree proprio); altrimenti il
|
|
744
753
|
// bash tool blocca il git che muta (working dir condivisa).
|
|
745
754
|
kingdom: { id: kingdomId, self: castle.slug, isolated: iso.isolated, peers },
|
|
@@ -772,8 +781,13 @@ async function runVassal(opts, provider, modelId, displayModel, castle, peers, i
|
|
|
772
781
|
completed = true;
|
|
773
782
|
onEvent?.(castle.slug, event);
|
|
774
783
|
};
|
|
784
|
+
const worker = createWorkerSession({ kingdomId, slug: castle.slug, sessionId: session.id, goal: castle.goal,
|
|
785
|
+
parentSignal, controller, binding, deps, sink, retain: !iso.isolated ? opts.retainSessions ?? false : false,
|
|
786
|
+
onController: next => { controller = next; } });
|
|
775
787
|
try {
|
|
776
788
|
const role = castle.role?.trim() || "collaboratore";
|
|
789
|
+
sink({ type: "kingdom", phase: "session", kingdomId, sessionId: session.id,
|
|
790
|
+
slug: castle.slug, state: "spawned", goal: castle.goal, at: Date.now() });
|
|
777
791
|
const parent = castle.parent?.trim() || "orchestratore";
|
|
778
792
|
const instructions = (castle.area?.length ? `\nAREA_ASSEGNATA: ${castle.area.join(", ")}. Coordina modifiche fuori area prima di eseguirle.\n` : "") + (castle.instructions?.trim()
|
|
779
793
|
? `\nISTRUZIONI_AGGIUNTIVE\n${castle.instructions.trim()}\n\n`
|
|
@@ -825,10 +839,7 @@ async function runVassal(opts, provider, modelId, displayModel, castle, peers, i
|
|
|
825
839
|
};
|
|
826
840
|
}
|
|
827
841
|
finally {
|
|
828
|
-
|
|
829
|
-
// Il collaboratore non c'e' piu': la UI deve saperlo subito, altrimenti
|
|
830
|
-
// continuerebbe a offrire un campo di scrittura che non consegna nulla.
|
|
831
|
-
kingdomLive.release(kingdomId, castle.slug);
|
|
842
|
+
worker.finish();
|
|
832
843
|
}
|
|
833
844
|
}
|
|
834
845
|
function mandateCompletion(sessionDir, planStoreId, mandate) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ContextStoreReader } from "./context-store-reader.js";
|
|
1
2
|
export interface MemoryPacketInput {
|
|
2
3
|
readonly sessionDir: string;
|
|
3
4
|
readonly scopeId: string;
|
|
@@ -9,5 +10,5 @@ export interface MemoryPacketInput {
|
|
|
9
10
|
/** Preselected dependencies supplied by a coherent snapshot. */
|
|
10
11
|
readonly projectScopes?: readonly string[];
|
|
11
12
|
}
|
|
12
|
-
export declare function buildMemoryPacket(input: MemoryPacketInput): string;
|
|
13
|
+
export declare function buildMemoryPacket(input: MemoryPacketInput, reader?: ContextStoreReader): string;
|
|
13
14
|
export declare function clampMemoryPacket(value: string, max: number): string;
|