@pellux/goodvibes-tui 1.24.0 → 1.24.2
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/CHANGELOG.md +15 -0
- package/README.md +1 -1
- package/docs/foundation-artifacts/operator-contract.json +1 -1
- package/package.json +7 -7
- package/src/daemon/cli.ts +10 -8
- package/src/daemon/handlers/inbox/cursor-store.ts +12 -1
- package/src/daemon/handlers/inbox/poller.ts +13 -3
- package/src/daemon/handlers/index.ts +9 -0
- package/src/runtime/agent-graph-composition.ts +78 -0
- package/src/runtime/disposal-wiring.ts +17 -0
- package/src/runtime/services.ts +25 -28
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@ All notable changes to GoodVibes TUI.
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [1.24.2] - 2026-07-27
|
|
8
|
+
|
|
9
|
+
### Changes
|
|
10
|
+
|
|
11
|
+
- Ship the browser driver with the agent binary.
|
|
12
|
+
- install.sh now fetches, checksum-verifies and places the Playwright driver archive beside the agent binary, and the update swap path refreshes it in lockstep so an in-place update never leaves a stale or missing driver. Without this a binary install had nothing to resolve and browser control could not work at all.
|
|
13
|
+
|
|
14
|
+
## [1.24.1] - 2026-07-27
|
|
15
|
+
|
|
16
|
+
### Changes
|
|
17
|
+
|
|
18
|
+
- Fixed: stopping the daemon left work running. The inbox retention sweep and the per-account inbound pollers kept ticking after shutdown, and a daemon that never finished starting — a port already in use, a short-lived run — left everything its construction had started running with nothing to stop it.
|
|
19
|
+
- Fixed: agent runs the daemon was hosting are now cancelled when it shuts down. One left running had nowhere to report to and nothing to write into: the registries and event bus it reports through are already gone by then. It also keeps an in-flight provider call alive, sleeping out a retry it no longer has any reason to finish.
|
|
20
|
+
- Fixed: a close arriving while the inbox cursor store was still opening could race it.
|
|
21
|
+
|
|
7
22
|
## [1.24.0] - 2026-07-27
|
|
8
23
|
|
|
9
24
|
### Changes
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/mgd34msu/goodvibes-tui/actions/workflows/ci.yml)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
[](https://github.com/mgd34msu/goodvibes-tui)
|
|
6
6
|
|
|
7
7
|
GoodVibes is a terminal console for coding and operations work with an AI model. You run `goodvibes` in a project directory and get a full-screen terminal app where you talk to a model that can read and edit your files, run shell commands, search the web, and hand work off to background agents — asking your permission before anything that writes or executes. It talks to many model providers (OpenAI, Anthropic, Gemini, Bedrock, Copilot, OpenRouter and other OpenAI-compatible gateways, plus local servers like Ollama and LM Studio that it finds on startup), keeps its settings, sessions, and secrets on your own machine, and shows you the token count and running cost of every turn. Alongside the conversation, panels turn background work — running agents, git state, diffs, tokens, cost — into live control rooms.
|
|
8
8
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-tui",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.2",
|
|
4
4
|
"description": "Terminal-native GoodVibes product for coding, operations, automation, knowledge, channels, and daemon-backed control-plane workflows.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/main.ts",
|
|
@@ -106,8 +106,8 @@
|
|
|
106
106
|
"@anthropic-ai/vertex-sdk": "^0.16.0",
|
|
107
107
|
"@ast-grep/napi": "^0.42.0",
|
|
108
108
|
"@aws/bedrock-token-generator": "^1.1.0",
|
|
109
|
-
"@pellux/goodvibes-sdk": "1.17.
|
|
110
|
-
"@pellux/goodvibes-terminal-shell": "1.17.
|
|
109
|
+
"@pellux/goodvibes-sdk": "1.17.2",
|
|
110
|
+
"@pellux/goodvibes-terminal-shell": "1.17.2",
|
|
111
111
|
"bash-language-server": "^5.6.0",
|
|
112
112
|
"fuse.js": "^7.1.0",
|
|
113
113
|
"graphql": "^16.13.2",
|
|
@@ -133,10 +133,10 @@
|
|
|
133
133
|
"typescript": "^5.9.3"
|
|
134
134
|
},
|
|
135
135
|
"optionalDependencies": {
|
|
136
|
-
"@pellux/goodvibes-tui-linux-x64": "1.
|
|
137
|
-
"@pellux/goodvibes-tui-linux-arm64": "1.
|
|
138
|
-
"@pellux/goodvibes-tui-darwin-x64": "1.
|
|
139
|
-
"@pellux/goodvibes-tui-darwin-arm64": "1.
|
|
136
|
+
"@pellux/goodvibes-tui-linux-x64": "1.24.2",
|
|
137
|
+
"@pellux/goodvibes-tui-linux-arm64": "1.24.2",
|
|
138
|
+
"@pellux/goodvibes-tui-darwin-x64": "1.24.2",
|
|
139
|
+
"@pellux/goodvibes-tui-darwin-arm64": "1.24.2"
|
|
140
140
|
},
|
|
141
141
|
"trustedDependencies": [],
|
|
142
142
|
"overrides": {
|
package/src/daemon/cli.ts
CHANGED
|
@@ -426,15 +426,17 @@ async function main(): Promise<void> {
|
|
|
426
426
|
// daemon.stop() drives the ordered inbound teardown: it stops every gated
|
|
427
427
|
// consumer and only then broadcasts the resignation, so another node on
|
|
428
428
|
// this network takes over in about a second instead of waiting out the
|
|
429
|
-
// crash timeout.
|
|
430
|
-
//
|
|
431
|
-
//
|
|
429
|
+
// crash timeout.
|
|
430
|
+
//
|
|
431
|
+
// This process built the runtime graph and handed it to DaemonServer, so by
|
|
432
|
+
// the SDK's ownership rule the facade leaves it alone — nothing else stops
|
|
433
|
+
// these pollers. Without dispose() the config watch, fleet tick, memory
|
|
434
|
+
// governor, watcher registry and six more kept ticking until process exit.
|
|
435
|
+
// The handler surfaces (inbox store + its poll timers, catalog handlers) are
|
|
436
|
+
// the FIRST thing dispose() unwinds — they are on the disposal owner list
|
|
437
|
+
// now rather than sequenced by hand here, which is what makes every other
|
|
438
|
+
// shutdown path stop them too instead of only this one.
|
|
432
439
|
const stop = Promise.allSettled([listener.stop(), daemon.stop()])
|
|
433
|
-
.then(() => runtimeServices.daemonHandlers.unregister())
|
|
434
|
-
// This process built the runtime graph and handed it to DaemonServer, so
|
|
435
|
-
// by the SDK's ownership rule the facade leaves it alone — nothing else
|
|
436
|
-
// stops these pollers. Without this the config watch, fleet tick, memory
|
|
437
|
-
// governor, watcher registry and six more kept ticking until process exit.
|
|
438
440
|
.then(() => { runtimeServices.dispose(); })
|
|
439
441
|
.then(() => 'done' as const);
|
|
440
442
|
const result = await Promise.race([stop, timeout]);
|
|
@@ -140,6 +140,15 @@ export class InboxCursorStore {
|
|
|
140
140
|
private readonly setIntervalImpl: typeof setInterval;
|
|
141
141
|
private readonly clearIntervalImpl: typeof clearInterval;
|
|
142
142
|
private sweepTimer: ReturnType<typeof setInterval> | null = null;
|
|
143
|
+
/**
|
|
144
|
+
* Set by close(). `init()` is async and arms the retention timer only after
|
|
145
|
+
* two awaits, so a surface that is torn down while its bootstrap is still in
|
|
146
|
+
* flight would otherwise arm a timer AFTER the only thing that could clear it
|
|
147
|
+
* had already run. That is not hypothetical: registerInboxMethods() kicks
|
|
148
|
+
* init() off without awaiting it, so any shutdown inside the first tick of a
|
|
149
|
+
* daemon's life hits exactly this window.
|
|
150
|
+
*/
|
|
151
|
+
private closed = false;
|
|
143
152
|
|
|
144
153
|
constructor(
|
|
145
154
|
workingDirectory: string,
|
|
@@ -201,7 +210,7 @@ export class InboxCursorStore {
|
|
|
201
210
|
}
|
|
202
211
|
|
|
203
212
|
private startSweepTimer(): void {
|
|
204
|
-
if (this.sweepTimer !== null || this.sweepIntervalMs <= 0) return;
|
|
213
|
+
if (this.closed || this.sweepTimer !== null || this.sweepIntervalMs <= 0) return;
|
|
205
214
|
const handle = this.setIntervalImpl(() => {
|
|
206
215
|
void this.runSweep();
|
|
207
216
|
}, this.sweepIntervalMs);
|
|
@@ -415,6 +424,8 @@ export class InboxCursorStore {
|
|
|
415
424
|
|
|
416
425
|
/** Stop the retention timer, flush (best-effort), then close the database. */
|
|
417
426
|
async close(): Promise<void> {
|
|
427
|
+
if (this.closed) return;
|
|
428
|
+
this.closed = true;
|
|
418
429
|
this.stopSweepTimer();
|
|
419
430
|
try {
|
|
420
431
|
await this.flush();
|
|
@@ -51,6 +51,15 @@ export class InboundPoller {
|
|
|
51
51
|
private readonly statuses = new Map<string, ProviderStatus>();
|
|
52
52
|
private readonly inFlight = new Set<string>();
|
|
53
53
|
private started = false;
|
|
54
|
+
/**
|
|
55
|
+
* Set by stop(), which the surface teardown calls and nothing else — unlike
|
|
56
|
+
* stopProvider(), which a leadership handover uses and which must stay
|
|
57
|
+
* resumable. Once the surface is released no interval may be armed again,
|
|
58
|
+
* including by work that was already in flight: registerInboxMethods() starts
|
|
59
|
+
* the store bootstrap without awaiting it, so a teardown during that window
|
|
60
|
+
* would otherwise be followed by a start() that nothing is left to undo.
|
|
61
|
+
*/
|
|
62
|
+
private released = false;
|
|
54
63
|
|
|
55
64
|
constructor(options: PollerOptions) {
|
|
56
65
|
this.adapters = options.adapters;
|
|
@@ -66,7 +75,7 @@ export class InboundPoller {
|
|
|
66
75
|
|
|
67
76
|
/** Begin per-provider interval loops. Idempotent. Does NOT poll immediately. */
|
|
68
77
|
start(): void {
|
|
69
|
-
if (this.started) return;
|
|
78
|
+
if (this.released || this.started) return;
|
|
70
79
|
this.started = true;
|
|
71
80
|
for (const id of this.adapters.keys()) this.startProvider(id);
|
|
72
81
|
}
|
|
@@ -81,7 +90,7 @@ export class InboundPoller {
|
|
|
81
90
|
* addressable per provider, and the blanket calls are fan-outs over it.
|
|
82
91
|
*/
|
|
83
92
|
startProvider(id: string): void {
|
|
84
|
-
if (this.timers.has(id)) return;
|
|
93
|
+
if (this.released || this.timers.has(id)) return;
|
|
85
94
|
const adapter = this.adapters.get(id);
|
|
86
95
|
if (!adapter) return;
|
|
87
96
|
const handle = this.setIntervalImpl(() => {
|
|
@@ -187,8 +196,9 @@ export class InboundPoller {
|
|
|
187
196
|
});
|
|
188
197
|
}
|
|
189
198
|
|
|
190
|
-
/**
|
|
199
|
+
/** Release the poller for good: stop every interval loop, and refuse to arm another. Idempotent. */
|
|
191
200
|
stop(): void {
|
|
201
|
+
this.released = true;
|
|
192
202
|
for (const handle of this.timers.values()) {
|
|
193
203
|
this.clearIntervalImpl(handle);
|
|
194
204
|
}
|
|
@@ -88,7 +88,16 @@ export function registerDaemonHandlers(
|
|
|
88
88
|
const remote = providers.registerRemote(ctx);
|
|
89
89
|
teardowns.push(remote.unregister);
|
|
90
90
|
|
|
91
|
+
// Idempotent: teardown is reachable from more than one shutdown path now that
|
|
92
|
+
// the runtime disposal scope owns it, and the surfaces underneath are not all
|
|
93
|
+
// safe to unwind twice — the inbox surface closes a SQLite handle, which a
|
|
94
|
+
// second pass would close again inside a floating promise (an unhandled
|
|
95
|
+
// rejection, not a caught one). Running once is also simply the honest
|
|
96
|
+
// meaning of "release these surfaces".
|
|
97
|
+
let torn = false;
|
|
91
98
|
const unregister: Unregister = () => {
|
|
99
|
+
if (torn) return;
|
|
100
|
+
torn = true;
|
|
92
101
|
for (let i = teardowns.length - 1; i >= 0; i -= 1) {
|
|
93
102
|
try {
|
|
94
103
|
teardowns[i]!();
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agent-graph-composition.ts — the graph that runs agents.
|
|
3
|
+
*
|
|
4
|
+
* Six collaborators that are only meaningful as a set, so they are built as
|
|
5
|
+
* one: a message bus, the archetype loader, the orchestrator that executes a
|
|
6
|
+
* run, the manager that owns the records, the context-accounting holder, and
|
|
7
|
+
* the WRFC controller. Every one of them holds a reference to at least one
|
|
8
|
+
* other, and two of the links are circular — the orchestrator writes
|
|
9
|
+
* conversation snapshots back through the manager, and the manager drives the
|
|
10
|
+
* WRFC controller which was built from the manager. Assembled anywhere but in
|
|
11
|
+
* one place, a half-wired graph looks correct and silently drops either the
|
|
12
|
+
* snapshot bridge or the review loop.
|
|
13
|
+
*
|
|
14
|
+
* It is also the graph whose runs `cancelHostedAgentRuns` cancels at disposal:
|
|
15
|
+
* the manager returned here is the one the runtime hands to the SDK's
|
|
16
|
+
* `cancelAllAgentRuns`.
|
|
17
|
+
*/
|
|
18
|
+
import { join } from 'node:path';
|
|
19
|
+
import { AgentMessageBus, AgentOrchestrator, ArchetypeLoader, WrfcController } from '@pellux/goodvibes-sdk/platform/agents';
|
|
20
|
+
import { AgentManager, ContextAccountingHolder } from '@pellux/goodvibes-sdk/platform/tools';
|
|
21
|
+
import type { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
|
|
22
|
+
import type { ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers';
|
|
23
|
+
import type { RuntimeEventBus } from '@/runtime/index.ts';
|
|
24
|
+
|
|
25
|
+
export interface AgentGraph {
|
|
26
|
+
readonly agentMessageBus: AgentMessageBus;
|
|
27
|
+
readonly archetypeLoader: ArchetypeLoader;
|
|
28
|
+
readonly agentOrchestrator: AgentOrchestrator;
|
|
29
|
+
readonly agentManager: AgentManager;
|
|
30
|
+
readonly contextAccountingHolder: ContextAccountingHolder;
|
|
31
|
+
readonly wrfcController: WrfcController;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Build the agent-execution graph, fully wired in both directions. */
|
|
35
|
+
export function createAgentGraph(options: {
|
|
36
|
+
readonly runtimeBus: RuntimeEventBus;
|
|
37
|
+
readonly workingDirectory: string;
|
|
38
|
+
readonly configManager: ConfigManager;
|
|
39
|
+
readonly providerRegistry: ProviderRegistry;
|
|
40
|
+
}): AgentGraph {
|
|
41
|
+
const agentMessageBus = new AgentMessageBus();
|
|
42
|
+
agentMessageBus.setRuntimeBus(options.runtimeBus);
|
|
43
|
+
const archetypeLoader = new ArchetypeLoader(join(options.workingDirectory, '.goodvibes', 'agents'));
|
|
44
|
+
const agentOrchestrator = new AgentOrchestrator({
|
|
45
|
+
messageBus: agentMessageBus,
|
|
46
|
+
});
|
|
47
|
+
agentOrchestrator.setRuntimeBus(options.runtimeBus);
|
|
48
|
+
const agentManager = new AgentManager({
|
|
49
|
+
archetypeLoader,
|
|
50
|
+
messageBus: agentMessageBus,
|
|
51
|
+
executor: agentOrchestrator,
|
|
52
|
+
configManager: options.configManager,
|
|
53
|
+
// The live registry lets a bare model id in a spawn() override resolve
|
|
54
|
+
// through the shared resolver instead of being rejected as unqualified.
|
|
55
|
+
providerRegistry: options.providerRegistry,
|
|
56
|
+
});
|
|
57
|
+
const contextAccountingHolder = new ContextAccountingHolder();
|
|
58
|
+
// Conversation-snapshot bridge (mirrors the SDK's own createRuntimeServices).
|
|
59
|
+
agentOrchestrator.setConversationSink({
|
|
60
|
+
register: (agentId, source) => agentManager.registerConversationSource(agentId, source),
|
|
61
|
+
release: (agentId) => agentManager.releaseConversationSource(agentId),
|
|
62
|
+
});
|
|
63
|
+
agentManager.setRuntimeBus(options.runtimeBus);
|
|
64
|
+
const wrfcController = new WrfcController(options.runtimeBus, agentMessageBus, {
|
|
65
|
+
agentManager,
|
|
66
|
+
configManager: options.configManager,
|
|
67
|
+
projectRoot: options.workingDirectory,
|
|
68
|
+
});
|
|
69
|
+
agentManager.setWrfcController(wrfcController);
|
|
70
|
+
return {
|
|
71
|
+
agentMessageBus,
|
|
72
|
+
archetypeLoader,
|
|
73
|
+
agentOrchestrator,
|
|
74
|
+
agentManager,
|
|
75
|
+
contextAccountingHolder,
|
|
76
|
+
wrfcController,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
@@ -38,6 +38,15 @@ export { createDisposalScope };
|
|
|
38
38
|
export interface SurfaceRuntimePollerOwners extends Omit<RuntimePollerOwners, 'stopConfigWatch'> {
|
|
39
39
|
/** Fork-only: the repeating crash-residue sweep (durability-services.ts). */
|
|
40
40
|
readonly stopDurabilityHousekeeping: () => void;
|
|
41
|
+
/**
|
|
42
|
+
* Fork-only: the daemon handler surfaces (daemon-handler-composition.ts).
|
|
43
|
+
*
|
|
44
|
+
* `unregister()` detaches the gateway handlers AND stops the two pollers this
|
|
45
|
+
* product's inbox surface owns — the retention sweep inside `InboxCursorStore`
|
|
46
|
+
* and the per-provider `InboundPoller` intervals. The SDK does not know either
|
|
47
|
+
* exists, so if this surface does not stop them nothing does.
|
|
48
|
+
*/
|
|
49
|
+
readonly daemonHandlers: { readonly unregister: () => void };
|
|
41
50
|
}
|
|
42
51
|
|
|
43
52
|
/**
|
|
@@ -63,4 +72,12 @@ export function registerSurfaceRuntimePollers(
|
|
|
63
72
|
): void {
|
|
64
73
|
registerRuntimePollers(registry, { ...services, stopConfigWatch: extras.stopConfigWatch });
|
|
65
74
|
registry.add('durability housekeeping', services.stopDurabilityHousekeeping);
|
|
75
|
+
// Registered LAST so it tears down FIRST (the scope unwinds in reverse), which
|
|
76
|
+
// is the order daemon/cli.ts already used by hand: release the handler surfaces
|
|
77
|
+
// — closing the inbox store and stopping its poll timers — before the pollers
|
|
78
|
+
// the rest of the graph owns. Being on this list is what makes a plain
|
|
79
|
+
// `dispose()` total: every shutdown path (daemon/cli.ts, main.ts's teardown
|
|
80
|
+
// registry, the one-shot CLI commands) now stops these two, not just the one
|
|
81
|
+
// that remembered to call `unregister()` itself.
|
|
82
|
+
registry.add('daemon handler surfaces', () => services.daemonHandlers.unregister());
|
|
66
83
|
}
|
package/src/runtime/services.ts
CHANGED
|
@@ -20,7 +20,7 @@ import { createKnowledgeServices } from './knowledge-services.ts';
|
|
|
20
20
|
import { MediaProviderRegistry, ensureBuiltinMediaProviders } from '@pellux/goodvibes-sdk/platform/media';
|
|
21
21
|
import { MultimodalService } from '@pellux/goodvibes-sdk/platform/multimodal';
|
|
22
22
|
import { AgentMessageBus, AgentOrchestrator, ArchetypeLoader, WrfcController } from '@pellux/goodvibes-sdk/platform/agents';
|
|
23
|
-
import { AgentManager, ContextAccountingHolder, OverflowHandler, ProcessManager, createWorkflowServices, type WorkflowServices } from '@pellux/goodvibes-sdk/platform/tools';
|
|
23
|
+
import { AgentManager, ContextAccountingHolder, OverflowHandler, ProcessManager, cancelAllAgentRuns, createWorkflowServices, type WorkflowServices } from '@pellux/goodvibes-sdk/platform/tools';
|
|
24
24
|
import { FileStateCache, FileUndoManager, MemoryConsolidationScheduler, MemoryEmbeddingProviderRegistry, MemoryRegistry, MemoryStore, ModeManager, ProjectIndex, resolveCanonicalMemoryDbPath, type CodeIndexStore, type CodeIndexReindexScheduler } from '@pellux/goodvibes-sdk/platform/state';
|
|
25
25
|
import type { StoreSnapshotScheduler } from '@pellux/goodvibes-sdk/platform/state/store-snapshots';
|
|
26
26
|
import type { UserPermissionRuleStore } from '@pellux/goodvibes-sdk/platform/permissions';
|
|
@@ -56,6 +56,7 @@ import { ApiTokenAuditor, UserAuthManager } from '@pellux/goodvibes-sdk/platform
|
|
|
56
56
|
import { WebhookNotifier } from '@pellux/goodvibes-sdk/platform/integrations';
|
|
57
57
|
import { McpRegistry } from '@pellux/goodvibes-sdk/platform/mcp';
|
|
58
58
|
import { createRemoteExecutionServices } from './remote-execution-composition.ts';
|
|
59
|
+
import { createAgentGraph } from './agent-graph-composition.ts';
|
|
59
60
|
import { BenchmarkStore, CacheHitTracker, FavoritesStore, ModelLimitsService, ProviderCapabilityRegistry, ProviderOptimizer, ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers';
|
|
60
61
|
import { KeybindingsManager } from '../input/keybindings.ts';
|
|
61
62
|
import { AdaptivePlanner, DeterministicReplayEngine, ExecutionPlanManager, SessionLineageTracker, SessionMemoryStore } from '@pellux/goodvibes-sdk/platform/core';
|
|
@@ -232,6 +233,15 @@ export interface RuntimeServices {
|
|
|
232
233
|
readonly integrationHelpers: IntegrationHelperService;
|
|
233
234
|
/** Re-root path-bound stores (MemoryStore, ProjectIndex) to a new working directory, called by WorkspaceSwapManager after verification; stores needing a process restart just warn-log and keep serving the old path until the daemon restarts with the new --working-dir. */
|
|
234
235
|
rerootStores(newWorkingDir: string): Promise<void>;
|
|
236
|
+
/**
|
|
237
|
+
* Cancel the agent runs this graph is hosting, returning how many.
|
|
238
|
+
*
|
|
239
|
+
* Required by the SDK's RuntimePollerOwners: by dispose() time the fleet
|
|
240
|
+
* registry, orchestration engine, process registry and bus these runs report
|
|
241
|
+
* through are already down, so a run still described as "running" is orphaned
|
|
242
|
+
* rather than preserved.
|
|
243
|
+
*/
|
|
244
|
+
cancelHostedAgentRuns(): number;
|
|
235
245
|
dispose(): void; // Stop every poller this graph started; best-effort, total, idempotent. This surface owns its graph — see disposal-wiring.ts.
|
|
236
246
|
}
|
|
237
247
|
|
|
@@ -342,34 +352,14 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
342
352
|
runtimeStore: options.runtimeStore,
|
|
343
353
|
runtimeBus: options.runtimeBus,
|
|
344
354
|
});
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
const agentManager = new AgentManager({
|
|
353
|
-
archetypeLoader,
|
|
354
|
-
messageBus: agentMessageBus,
|
|
355
|
-
executor: agentOrchestrator,
|
|
356
|
-
configManager,
|
|
357
|
-
// The live registry lets a bare model id in a spawn() override resolve
|
|
358
|
-
// through the shared resolver instead of being rejected as unqualified.
|
|
359
|
-
providerRegistry,
|
|
360
|
-
});
|
|
361
|
-
const contextAccountingHolder = new ContextAccountingHolder();
|
|
362
|
-
agentOrchestrator.setConversationSink({ // Conversation-snapshot bridge (mirrors the SDK's own createRuntimeServices)
|
|
363
|
-
register: (agentId, source) => agentManager.registerConversationSource(agentId, source),
|
|
364
|
-
release: (agentId) => agentManager.releaseConversationSource(agentId),
|
|
365
|
-
});
|
|
366
|
-
agentManager.setRuntimeBus(options.runtimeBus);
|
|
367
|
-
const wrfcController = new WrfcController(options.runtimeBus, agentMessageBus, {
|
|
368
|
-
agentManager,
|
|
369
|
-
configManager,
|
|
370
|
-
projectRoot: workingDirectory,
|
|
355
|
+
// The agent-execution graph, wired in both directions; see
|
|
356
|
+
// agent-graph-composition.ts for why the six are built as one.
|
|
357
|
+
const {
|
|
358
|
+
agentMessageBus, archetypeLoader, agentOrchestrator,
|
|
359
|
+
agentManager, contextAccountingHolder, wrfcController,
|
|
360
|
+
} = createAgentGraph({
|
|
361
|
+
runtimeBus: options.runtimeBus, workingDirectory, configManager, providerRegistry,
|
|
371
362
|
});
|
|
372
|
-
agentManager.setWrfcController(wrfcController);
|
|
373
363
|
const hookDispatcher = new HookDispatcher({ agentManager, toolLLM, projectRoot: workingDirectory }, hookActivityTracker);
|
|
374
364
|
configManager.attachHookDispatcher(hookDispatcher);
|
|
375
365
|
const hookWorkbench = createHookWorkbench({
|
|
@@ -794,6 +784,13 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
794
784
|
workspaceCheckpointManager,
|
|
795
785
|
integrationHelpers,
|
|
796
786
|
rerootStores: createStoreRerooter({ codeIndexStore, projectIndex }),
|
|
787
|
+
// Cancels the agent runs this graph was hosting. By dispose() time the
|
|
788
|
+
// fleet registry, orchestration engine, process registry and bus these runs
|
|
789
|
+
// report through are already down, so a run still described as "running" is
|
|
790
|
+
// orphaned rather than preserved — and this is the only shutdown-reachable
|
|
791
|
+
// way to abort its in-flight provider call instead of letting it sleep out
|
|
792
|
+
// a retry backoff nobody is waiting on.
|
|
793
|
+
cancelHostedAgentRuns: () => cancelAllAgentRuns(agentManager),
|
|
797
794
|
dispose: (): void => disposalScope.dispose(),
|
|
798
795
|
};
|
|
799
796
|
registerSurfaceRuntimePollers(disposalScope.registry, services, { stopConfigWatch }); return services;
|
package/src/version.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { join } from 'node:path';
|
|
|
6
6
|
// The prebuild script updates the fallback value before compilation.
|
|
7
7
|
// Uses import.meta.dir (Bun) to locate package.json relative to this file,
|
|
8
8
|
// which is correct regardless of the process working directory.
|
|
9
|
-
let _version = '1.24.
|
|
9
|
+
let _version = '1.24.2';
|
|
10
10
|
try {
|
|
11
11
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8'));
|
|
12
12
|
// Only trust a version read from OUR OWN package.json. A Bun single-file
|