@wrongstack/core 0.260.0 → 0.265.1
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/dist/{agent-bridge-BbskZ7HH.d.ts → agent-bridge-DrkBxszZ.d.ts} +1 -1
- package/dist/{agent-subagent-runner-BNIGZx18.d.ts → agent-subagent-runner-DM2pP-B6.d.ts} +116 -12
- package/dist/{brain-C2yDd7Lw.d.ts → brain-BXd_61kQ.d.ts} +32 -3
- package/dist/{compactor-t0R_AIt_.d.ts → compactor-B8pOf45Y.d.ts} +1 -1
- package/dist/{config-FG6As4H5.d.ts → config-BMCj_XDs.d.ts} +86 -12
- package/dist/{context-JFOVvu6z.d.ts → context-MRk5PhNv.d.ts} +26 -1
- package/dist/coordination/index.d.ts +1737 -15
- package/dist/coordination/index.js +3152 -494
- package/dist/coordination/index.js.map +1 -1
- package/dist/{default-config-CXsDvOmP.d.ts → default-config-B0cj-Hry.d.ts} +11 -1
- package/dist/defaults/index.d.ts +28 -28
- package/dist/defaults/index.js +1804 -1363
- package/dist/defaults/index.js.map +1 -1
- package/dist/dispatcher-types.d-BBeXBQgS.d.ts +66 -0
- package/dist/execution/index.d.ts +16 -16
- package/dist/execution/index.js +933 -672
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/prompt-enhancer.d.ts +1 -1
- package/dist/execution/prompt-enhancer.js +7 -1
- package/dist/execution/prompt-enhancer.js.map +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/extension/index.js.map +1 -1
- package/dist/{goal-preamble-B1IXJtLX.d.ts → goal-preamble-DvHDSKSe.d.ts} +26 -10
- package/dist/{goal-store-CPXz6Mml.d.ts → goal-store-DtLMySNb.d.ts} +1 -1
- package/dist/{index-CebbJB94.d.ts → index-B-ch8K9C.d.ts} +8 -8
- package/dist/{index-BPcg4N3M.d.ts → index-CEDeNodM.d.ts} +5 -5
- package/dist/index.d.ts +189 -104
- package/dist/index.js +24693 -21162
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/infrastructure/index.js +12 -8
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +9 -9
- package/dist/kernel/index.js +7 -2
- package/dist/kernel/index.js.map +1 -1
- package/dist/{llm-selector-DXxI2tlu.d.ts → llm-selector-C0tfTCUe.d.ts} +14 -2
- package/dist/{mcp-servers-OwNHo43-.d.ts → mcp-servers-2x4w6Jn9.d.ts} +3 -3
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +80 -31
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-Djlmq4uB.d.ts → models-registry-DmJlKuNp.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-CEmrSCMJ.d.ts → multi-agent-coordinator-DyCkCZnU.d.ts} +2 -2
- package/dist/{null-fleet-bus-DT92xqgJ.d.ts → null-fleet-bus-CG9QY2aP.d.ts} +6 -6
- package/dist/observability/index.d.ts +2 -2
- package/dist/observability/index.js +8 -3
- package/dist/observability/index.js.map +1 -1
- package/dist/{parallel-eternal-engine-0SItuq5r.d.ts → parallel-eternal-engine-Jw9uhEoT.d.ts} +9 -9
- package/dist/{path-resolver-DKBh6Jlo.d.ts → path-resolver-Dy2ej-gE.d.ts} +3 -3
- package/dist/{permission-BJ7eO9Vl.d.ts → permission-B9SB45lp.d.ts} +1 -1
- package/dist/{permission-policy-DEXOfnpm.d.ts → permission-policy-CkjSXabK.d.ts} +2 -2
- package/dist/{pipeline-zflkI2dp.d.ts → pipeline-DPDxH_7m.d.ts} +59 -4
- package/dist/{plan-templates-BFXyRkEK.d.ts → plan-templates-CzD9GnAU.d.ts} +32 -8
- package/dist/{provider-runner-BC-uywtT.d.ts → provider-runner-DMa70ODu.d.ts} +3 -3
- package/dist/{retry-policy-Cavrzmtk.d.ts → retry-policy-CN0khdlj.d.ts} +1 -1
- package/dist/sdd/index.d.ts +8 -8
- package/dist/sdd/index.js +313 -122
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-vault-CDvDYXWX.d.ts → secret-vault-B2yw84VT.d.ts} +43 -4
- package/dist/secret-vault-BAKpgFw_.d.ts +57 -0
- package/dist/security/index.d.ts +5 -5
- package/dist/security/index.js +411 -225
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-B7AivHsu.d.ts → selector-CzHh_igB.d.ts} +1 -1
- package/dist/{session-event-bridge-BmIDxdJd.d.ts → session-event-bridge-BUI6Jf-4.d.ts} +8 -2
- package/dist/{session-reader-DtofsB-2.d.ts → session-reader-CMgdMSRP.d.ts} +1 -1
- package/dist/skills/index.js +67 -64
- package/dist/skills/index.js.map +1 -1
- package/dist/storage/index.d.ts +132 -16
- package/dist/storage/index.js +851 -432
- package/dist/storage/index.js.map +1 -1
- package/dist/tools/index.d.ts +57 -0
- package/dist/tools/index.js +411 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/types/index.d.ts +21 -21
- package/dist/types/index.js +928 -711
- package/dist/types/index.js.map +1 -1
- package/dist/utils/error.d.ts +7 -0
- package/dist/utils/error.js +8 -0
- package/dist/utils/error.js.map +1 -0
- package/dist/utils/index.d.ts +8 -68
- package/dist/utils/index.js +20 -10
- package/dist/utils/index.js.map +1 -1
- package/dist/{wstack-paths-CJjEwPXn.d.ts → wstack-paths-hOpNLmvf.d.ts} +2 -0
- package/package.json +5 -1
- package/skills/api-design/SKILL.md +1 -1
- package/skills/audit-log/SKILL.md +6 -6
- package/skills/bug-hunter/SKILL.md +5 -5
- package/skills/chimera/SKILL.md +4 -4
- package/skills/docker-deploy/SKILL.md +1 -1
- package/skills/git-flow/SKILL.md +3 -3
- package/skills/multi-agent/SKILL.md +3 -3
- package/skills/node-modern/SKILL.md +1 -0
- package/skills/observability/SKILL.md +2 -2
- package/skills/output-standards/SKILL.md +51 -28
- package/skills/refactor-planner/SKILL.md +3 -3
- package/skills/security-scanner/SKILL.md +4 -3
- package/skills/tech-stack/SKILL.md +1 -2
- package/dist/package-outdated-watcher-C70ag2G9.d.ts +0 -581
- package/dist/secret-vault-BJDY28ev.d.ts +0 -25
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
export {
|
|
3
|
-
import {
|
|
4
|
-
export {
|
|
5
|
-
|
|
6
|
-
export {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export {
|
|
10
|
-
import '../
|
|
1
|
+
import { E as EventBus, B as BrainArbiter, f as BrainDecisionOption, k as BrainRisk, h as BrainDecisionRequest, e as BrainDecision, i as BrainDecisionSource } from '../brain-BXd_61kQ.js';
|
|
2
|
+
export { g as BrainDecisionQueue, j as BrainFallback, D as DefaultBrainArbiter, l as DefaultBrainArbiterOptions, H as HumanEscalatingBrainArbiter, O as ObservableBrainArbiter, w as formatHumanPrompt } from '../brain-BXd_61kQ.js';
|
|
3
|
+
import { H as FleetManager, s as Director } from '../null-fleet-bus-CG9QY2aP.js';
|
|
4
|
+
export { A as ACP_AGENTS, a as AGENTS_BY_PHASE, b as AGENT_CATALOG, c as ALL_AGENT_DEFINITIONS, d as ALL_FLEET_AGENTS, e as AUDIT_LOG_AGENT, f as AutoExtendCeiling, g as AutoExtendPolicy, B as BUG_HUNTER_AGENT, h as BugFinding, C as CollabBudgetConfig, i as CollabBudgetOverrides, j as CollabBudgetWarningPayload, k as CollabDebugReport, l as CollabSession, m as CollabSessionOptions, n as CreateDelegateToolOptions, o as CriticConcern, p as CriticEvaluation, D as DEFAULT_DIRECTOR_PREAMBLE, q as DEFAULT_SUBAGENT_BASELINE, r as DelegateHost, t as DirectorAlert, u as DirectorAlertLevel, v as DirectorCancelCollabPayload, w as DirectorPromptParts, x as DirectorSessionFactory, y as DirectorSessionFactoryOptions, F as FLEET_ROSTER, z as FLEET_ROSTER_BUDGETS, E as FLEET_ROSTER_WITHACP, G as FleetCostCapError, I as FleetManagerOptions, J as FleetRosterBudget, K as FleetSpawnBudgetError, L as ICoordinator, M as IFleetManager, N as LargeAnswerStore, O as NULL_FLEET_BUS, R as REFACTOR_PLANNER_AGENT, P as RefactorPhase, Q as RefactorPlan, S as SECURITY_SCANNER_AGENT, T as SharedFileEntry, U as SharedFileSnapshot, V as SubagentPromptParts, W as applyRosterBudget, X as attachAutoExtend, Y as composeDirectorPrompt, Z as composeSubagentPrompt, _ as createDelegateTool, $ as getAgentDefinition, a0 as makeAskResultTool, a1 as makeAskTool, a2 as makeAssignTool, a3 as makeAwaitTasksTool, a4 as makeCollabDebugTool, a5 as makeDirectorSessionFactory, a6 as makeFleetEmitTool, a7 as makeFleetHealthTool, a8 as makeFleetSessionTool, a9 as makeFleetStatusTool, aa as makeFleetUsageTool, ab as makeRollUpTool, ac as makeSpawnTool, ad as makeTerminateTool, ae as makeWorkCompleteTool, af as rosterSummaryFromConfigs } from '../null-fleet-bus-CG9QY2aP.js';
|
|
5
|
+
import { b as AgentDefinition } from '../multi-agent-coordinator-DyCkCZnU.js';
|
|
6
|
+
export { T as AGENT_TOOL_PRESETS, c as AgentBudgetTier, d as AgentCapability, A as AgentPhase, e as DEFAULT_DISPATCH_ROLE, a as DefaultMultiAgentCoordinator, f as DispatchCandidate, D as DispatchClassifier, g as DispatchMethod, h as DispatchOptions, i as DispatchResult, H as HEAVY_BUDGET, L as LIGHT_BUDGET, M as MEDIUM_BUDGET, j as MultiAgentCoordinatorOptions, k as dispatchAgent, m as makeLLMClassifier, s as scoreAgents } from '../multi-agent-coordinator-DyCkCZnU.js';
|
|
7
|
+
import { F as FleetBus } from '../agent-subagent-runner-DM2pP-B6.js';
|
|
8
|
+
export { h as AgentFactory, v as AgentFactoryResult, w as AgentRunnerOptions, x as BudgetExceededError, y as BudgetKind, z as BudgetLimits, E as BudgetNegotiationMode, G as BudgetThresholdDecision, H as BudgetThresholdHandler, I as BudgetThresholdSignal, J as BudgetUsage, L as DECISION_TIMEOUT_MS, N as FleetEvent, O as FleetHandler, i as FleetUsage, j as FleetUsageAggregator, Q as SubagentBudget, Y as SubagentUsageSnapshot, Z as TIMEOUT_PREEMPT_FRACTION, $ as makeAgentSubagentRunner, a0 as withDisabledToolFiltering } from '../agent-subagent-runner-DM2pP-B6.js';
|
|
9
|
+
export { I as InMemoryAgentBridge, a as InMemoryBridgeTransport, c as createMessage } from '../agent-bridge-DrkBxszZ.js';
|
|
10
|
+
import { c as Mailbox, k as MailboxSendInput, d as MailboxMessage, j as MailboxQuery, g as MailboxAckInput, s as MailboxAckBatchInput, h as MailboxAgentStatus, e as AgentRegistrationInput, A as AgentHeartbeatInput, t as PurgeOptions, u as PurgeResult, v as ClientRegistrationInput, w as ClientHeartbeatInput, x as ClientStatus } from '../pipeline-DPDxH_7m.js';
|
|
11
|
+
export { i as MailboxMessageType, l as MailboxTaskContext, o as ReadReceipts, p as RegisteredAgent, r as normalizeRecipient } from '../pipeline-DPDxH_7m.js';
|
|
12
|
+
import { C as Context, T as Tool } from '../context-MRk5PhNv.js';
|
|
11
13
|
import 'node:events';
|
|
12
14
|
import '../director-state-BfeCUbmk.js';
|
|
13
|
-
import '../config-
|
|
14
|
-
import '../index-
|
|
15
|
+
import '../config-BMCj_XDs.js';
|
|
16
|
+
import '../index-CEDeNodM.js';
|
|
15
17
|
import '../logger-B63L5bTg.js';
|
|
16
18
|
import '../observability-D-HZN_mF.js';
|
|
17
|
-
import '../permission-
|
|
18
|
-
import '../retry-policy-
|
|
19
|
+
import '../permission-B9SB45lp.js';
|
|
20
|
+
import '../retry-policy-CN0khdlj.js';
|
|
19
21
|
|
|
20
22
|
/** Phase 1 · Discovery — map the territory before any work begins. */
|
|
21
23
|
declare const DISCOVERY_AGENTS: AgentDefinition[];
|
|
@@ -44,6 +46,471 @@ declare const DELIVERY_AGENTS: AgentDefinition[];
|
|
|
44
46
|
/** Phase 9 · Meta — agents that improve the agent system itself. */
|
|
45
47
|
declare const META_AGENTS: AgentDefinition[];
|
|
46
48
|
|
|
49
|
+
/**
|
|
50
|
+
* DefaultMailbox — append-only JSONL inter-agent mailbox (per-session).
|
|
51
|
+
*
|
|
52
|
+
* Stores messages under `<sessionDir>/_mailbox.jsonl`. Every send appends
|
|
53
|
+
* one line. Query reads and filters all lines. Ack rewrites changed
|
|
54
|
+
* messages in-place via atomic write.
|
|
55
|
+
*
|
|
56
|
+
* For cross-session communication, use GlobalMailbox instead.
|
|
57
|
+
*
|
|
58
|
+
* @module DefaultMailbox
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
declare class DefaultMailbox implements Mailbox {
|
|
62
|
+
private readonly filePath;
|
|
63
|
+
constructor(sessionDir: string);
|
|
64
|
+
get mailboxPath(): string;
|
|
65
|
+
send(input: MailboxSendInput): Promise<MailboxMessage>;
|
|
66
|
+
query(q: MailboxQuery): Promise<MailboxMessage[]>;
|
|
67
|
+
ack(input: MailboxAckInput): Promise<MailboxMessage | null>;
|
|
68
|
+
ackMany(input: MailboxAckBatchInput): Promise<MailboxMessage[]>;
|
|
69
|
+
getAgentStatuses(): Promise<MailboxAgentStatus[]>;
|
|
70
|
+
getOnlineAgents(): Promise<MailboxAgentStatus[]>;
|
|
71
|
+
registerAgent(_input: AgentRegistrationInput): Promise<void>;
|
|
72
|
+
heartbeat(_input: AgentHeartbeatInput): Promise<void>;
|
|
73
|
+
unreadCount(forAgentId: string): Promise<number>;
|
|
74
|
+
close(): Promise<void>;
|
|
75
|
+
clearAll(): Promise<void>;
|
|
76
|
+
purgeStale(opts?: PurgeOptions): Promise<PurgeResult>;
|
|
77
|
+
registerClient(_input: ClientRegistrationInput): Promise<void>;
|
|
78
|
+
clientHeartbeat(_input: ClientHeartbeatInput): Promise<void>;
|
|
79
|
+
getClientStatuses(): Promise<ClientStatus[]>;
|
|
80
|
+
private _readAll;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* BrainMonitor — the Brain's SELF-ACTIVATION layer.
|
|
85
|
+
*
|
|
86
|
+
* The BrainArbiter alone is reactive: subsystems (director, autophase,
|
|
87
|
+
* eternal engine) ask it questions. The monitor closes the loop the other
|
|
88
|
+
* way — it WATCHES the live EventBus for distress signals, consults the
|
|
89
|
+
* Brain proactively, and when the decision calls for it, INTERVENES in the
|
|
90
|
+
* running work by delivering a corrective steer to the working agent
|
|
91
|
+
* (steers are folded into the agent's conversation before its next step
|
|
92
|
+
* via the mailbox loop, so no new plumbing is needed).
|
|
93
|
+
*
|
|
94
|
+
* Watched signals (v1):
|
|
95
|
+
* - tool-failure streak — the same tool failing N times consecutively
|
|
96
|
+
* (default 3). Classic stuck-loop: the agent keeps retrying an
|
|
97
|
+
* approach that does not work.
|
|
98
|
+
* - error storm — N `error` events within a sliding window (default
|
|
99
|
+
* 4 in 60s). Something is systematically wrong.
|
|
100
|
+
*
|
|
101
|
+
* Decision contract: every consultation offers [steer | continue] with
|
|
102
|
+
* fallback `continue`, at `medium` risk. Degradation is safe by design:
|
|
103
|
+
* - tiered brain with an LLM layer → a real judgement call, with the
|
|
104
|
+
* LLM's rationale becoming the steer text;
|
|
105
|
+
* - policy-only brain → fallback `continue` → observe, never interfere.
|
|
106
|
+
*
|
|
107
|
+
* Every engagement (whether or not it intervened) emits
|
|
108
|
+
* `brain.intervention` for the TUI/WebUI surfaces, and is rate-limited by
|
|
109
|
+
* a per-signal cooldown so the Brain never spams the agent.
|
|
110
|
+
*
|
|
111
|
+
* @module brain-monitor
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
interface BrainInterventionInput {
|
|
115
|
+
subject: string;
|
|
116
|
+
body: string;
|
|
117
|
+
}
|
|
118
|
+
interface BrainMonitorOptions {
|
|
119
|
+
events: EventBus;
|
|
120
|
+
brain: BrainArbiter;
|
|
121
|
+
/**
|
|
122
|
+
* Deliver a corrective steer to the working agent(s). Hosts typically
|
|
123
|
+
* send a `steer` mail to this session's leader via the project
|
|
124
|
+
* GlobalMailbox — the agent loop injects it before the next LLM call.
|
|
125
|
+
*/
|
|
126
|
+
intervene: (input: BrainInterventionInput) => Promise<void>;
|
|
127
|
+
/** Consecutive failures of the SAME tool before engaging. Default 3. */
|
|
128
|
+
toolFailureStreak?: number | undefined;
|
|
129
|
+
/** Number of `error` events within the window before engaging. Default 4. */
|
|
130
|
+
errorStormCount?: number | undefined;
|
|
131
|
+
/** Sliding window for the error storm signal (ms). Default 60_000. */
|
|
132
|
+
errorStormWindowMs?: number | undefined;
|
|
133
|
+
/** Minimum gap between engagements of the same signal kind (ms). Default 120_000. */
|
|
134
|
+
cooldownMs?: number | undefined;
|
|
135
|
+
}
|
|
136
|
+
declare class BrainMonitor {
|
|
137
|
+
private readonly opts;
|
|
138
|
+
private readonly failStreaks;
|
|
139
|
+
private errorTimestamps;
|
|
140
|
+
private readonly lastEngagedAt;
|
|
141
|
+
private readonly unsubscribers;
|
|
142
|
+
private engaging;
|
|
143
|
+
private readonly toolFailureStreak;
|
|
144
|
+
private readonly errorStormCount;
|
|
145
|
+
private readonly errorStormWindowMs;
|
|
146
|
+
private readonly cooldownMs;
|
|
147
|
+
constructor(opts: BrainMonitorOptions);
|
|
148
|
+
start(): void;
|
|
149
|
+
stop(): void;
|
|
150
|
+
private engage;
|
|
151
|
+
private maybeIntervene;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* GlobalMailbox — project-level inter-agent mailbox with cross-session support.
|
|
156
|
+
*
|
|
157
|
+
* Stores messages at `~/.wrongstack/projects/<slug>/_mailbox.jsonl` so all
|
|
158
|
+
* sessions (terminals, WebUIs) working on the same project share one inbox.
|
|
159
|
+
*
|
|
160
|
+
* Features:
|
|
161
|
+
* - Agent registration + heartbeat (agents go stale after 60s without heartbeat)
|
|
162
|
+
* - Per-recipient read receipts (readBy[agentId] = ISO8601)
|
|
163
|
+
* - Atomic file-locking for concurrent multi-process writes
|
|
164
|
+
* - Unread count for new-mail notifications
|
|
165
|
+
* - Online agent list
|
|
166
|
+
*
|
|
167
|
+
* @module GlobalMailbox
|
|
168
|
+
*/
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Derive the project-level mailbox directory path.
|
|
172
|
+
*
|
|
173
|
+
* Delegates to the CANONICAL `projectSlug()` from wstack-paths so every
|
|
174
|
+
* surface (CLI, TUI, WebUI, mailbox tool, loop checker) lands in the exact
|
|
175
|
+
* same `~/.wrongstack/projects/<slug>/` directory. A previous inline copy
|
|
176
|
+
* skipped the leading/trailing-hyphen strip, which silently split agents
|
|
177
|
+
* working on projects with non-alphanumeric name edges into TWO mailboxes.
|
|
178
|
+
*
|
|
179
|
+
* @param projectRoot — absolute path to the project root
|
|
180
|
+
* @param globalRoot — `~/.wrongstack` (or custom global root)
|
|
181
|
+
*/
|
|
182
|
+
declare function resolveProjectDir(projectRoot: string, globalRoot: string): string;
|
|
183
|
+
declare class GlobalMailbox implements Mailbox {
|
|
184
|
+
/** Path to the JSONL message file. */
|
|
185
|
+
readonly messagePath: string;
|
|
186
|
+
/** Path to the JSON agent registry file. */
|
|
187
|
+
readonly registryPath: string;
|
|
188
|
+
/** Path to the JSON client registry file. */
|
|
189
|
+
readonly clientRegistryPath: string;
|
|
190
|
+
/** Optional event bus for emitting agent registration/heartbeat events. */
|
|
191
|
+
private readonly _events?;
|
|
192
|
+
/**
|
|
193
|
+
* Local cache of the agent registry to avoid re-reading on every call.
|
|
194
|
+
* Time-bounded: the registry file is shared ACROSS PROCESSES (that's the
|
|
195
|
+
* whole point of GlobalMailbox), so a cache served forever would never see
|
|
196
|
+
* agents registered by other sessions. Writers always bypass it.
|
|
197
|
+
*/
|
|
198
|
+
private _registryCache;
|
|
199
|
+
/** When the registry cache was last refreshed from disk (epoch ms). */
|
|
200
|
+
private _registryCacheAt;
|
|
201
|
+
/**
|
|
202
|
+
* Local cache of the client registry to avoid re-reading on every call.
|
|
203
|
+
* Same reasoning as agent registry cache.
|
|
204
|
+
*/
|
|
205
|
+
private _clientRegistryCache;
|
|
206
|
+
/** When the client registry cache was last refreshed from disk (epoch ms). */
|
|
207
|
+
private _clientRegistryCacheAt;
|
|
208
|
+
/** Last time each local agent sent a heartbeat (throttle). */
|
|
209
|
+
private _lastHeartbeat;
|
|
210
|
+
/** Last time each local client sent a heartbeat (throttle). */
|
|
211
|
+
private _lastClientHeartbeat;
|
|
212
|
+
/**
|
|
213
|
+
* In-memory mirror of the JSONL message file. The mailbox is shared
|
|
214
|
+
* ACROSS PROCESSES, so reads cannot trust the cache blindly — we pair it
|
|
215
|
+
* with an mtime check. The file lock serializes every write, so a
|
|
216
|
+
* changed mtimeMs is a definitive signal that another process (or this
|
|
217
|
+
* one) wrote; an unchanged mtimeMs guarantees no write happened and the
|
|
218
|
+
* cache is current. This collapses the per-iteration `query()` cost from
|
|
219
|
+
* O(file_size) disk + parse to O(messages) in memory.
|
|
220
|
+
*/
|
|
221
|
+
private _messageCache;
|
|
222
|
+
/** mtimeMs of the file when `_messageCache` was populated. */
|
|
223
|
+
private _messageCacheMtime;
|
|
224
|
+
/** Size of the file when `_messageCache` was populated (extra guard). */
|
|
225
|
+
private _messageCacheSize;
|
|
226
|
+
/**
|
|
227
|
+
* @param projectDir — `~/.wrongstack/projects/<slug>/`
|
|
228
|
+
* @param events — optional EventBus for real-time TUI/WebUI notifications
|
|
229
|
+
*/
|
|
230
|
+
constructor(projectDir: string, events?: EventBus);
|
|
231
|
+
send(input: MailboxSendInput): Promise<MailboxMessage>;
|
|
232
|
+
query(q: MailboxQuery): Promise<MailboxMessage[]>;
|
|
233
|
+
ack(input: MailboxAckInput): Promise<MailboxMessage | null>;
|
|
234
|
+
ackMany(input: MailboxAckBatchInput): Promise<MailboxMessage[]>;
|
|
235
|
+
unreadCount(forAgentId: string): Promise<number>;
|
|
236
|
+
registerAgent(input: AgentRegistrationInput): Promise<void>;
|
|
237
|
+
heartbeat(input: AgentHeartbeatInput): Promise<void>;
|
|
238
|
+
getAgentStatuses(): Promise<MailboxAgentStatus[]>;
|
|
239
|
+
getOnlineAgents(): Promise<MailboxAgentStatus[]>;
|
|
240
|
+
registerClient(input: ClientRegistrationInput): Promise<void>;
|
|
241
|
+
clientHeartbeat(input: ClientHeartbeatInput): Promise<void>;
|
|
242
|
+
getClientStatuses(): Promise<ClientStatus[]>;
|
|
243
|
+
close(): Promise<void>;
|
|
244
|
+
clearAll(): Promise<void>;
|
|
245
|
+
purgeStale(opts?: PurgeOptions): Promise<PurgeResult>;
|
|
246
|
+
/**
|
|
247
|
+
* Read all messages from the JSONL file. Always reads + parses the file.
|
|
248
|
+
* Callers that can tolerate a stale-by-mtime view should use
|
|
249
|
+
* {@link _readMessagesCached}; writers that need the post-lock truth
|
|
250
|
+
* should call this directly (it's what {@link _readMessagesFresh} aliases).
|
|
251
|
+
*/
|
|
252
|
+
private _readMessages;
|
|
253
|
+
/**
|
|
254
|
+
* Read messages, then adopt the result as the in-memory cache. Use this
|
|
255
|
+
* from writers that just took the file lock — the read reflects the
|
|
256
|
+
* authoritative post-lock state and should be served to subsequent
|
|
257
|
+
* queries without re-reading.
|
|
258
|
+
*/
|
|
259
|
+
private _readMessagesFresh;
|
|
260
|
+
/**
|
|
261
|
+
* Read messages, consulting the mtime-bounded in-memory cache first.
|
|
262
|
+
* The mailbox file is shared across processes; every `send`/`ack`/
|
|
263
|
+
* `clearAll`/`purgeStale` takes the file lock, so writes are serialized
|
|
264
|
+
* and a changed mtimeMs is a definitive freshness signal. When the
|
|
265
|
+
* stat matches the cached mtime+size we return the cached array — no
|
|
266
|
+
* file read and no JSON.parse — collapsing the per-iteration query
|
|
267
|
+
* cost on the mailbox-loop hot path.
|
|
268
|
+
*/
|
|
269
|
+
private _readMessagesCached;
|
|
270
|
+
/**
|
|
271
|
+
* Replace the in-memory cache. Caller is responsible for guaranteeing
|
|
272
|
+
* that `messages` reflects the current on-disk state (e.g. they just
|
|
273
|
+
* read or wrote it under the file lock).
|
|
274
|
+
*/
|
|
275
|
+
private _setMessageCache;
|
|
276
|
+
/**
|
|
277
|
+
* Append a single just-sent message to the in-memory cache without
|
|
278
|
+
* re-reading the file. The caller must hold the file lock (or have
|
|
279
|
+
* just released it after a successful append) so the cache stays
|
|
280
|
+
* consistent with on-disk state.
|
|
281
|
+
*/
|
|
282
|
+
private _pushToCache;
|
|
283
|
+
private _ensureRegistry;
|
|
284
|
+
private _readRegistry;
|
|
285
|
+
private _pruneStaleInPlace;
|
|
286
|
+
private _writeRegistry;
|
|
287
|
+
private _ensureClientRegistry;
|
|
288
|
+
private _readClientRegistry;
|
|
289
|
+
private _pruneStaleClientsInPlace;
|
|
290
|
+
private _writeClientRegistry;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* mailbox-tool — Tool that exposes the inter-agent mailbox to agents.
|
|
295
|
+
*
|
|
296
|
+
* Sub-commands: check, send, ack, query, status, online, unread
|
|
297
|
+
*
|
|
298
|
+
* Uses the project-level GlobalMailbox for cross-session communication.
|
|
299
|
+
* Agents are auto-registered on first use with heartbeat tracking.
|
|
300
|
+
* Read receipts track who read each message and when.
|
|
301
|
+
*
|
|
302
|
+
* @module mailbox-tool
|
|
303
|
+
*/
|
|
304
|
+
|
|
305
|
+
type MailboxResolver = (ctx: Context) => Mailbox;
|
|
306
|
+
interface MailboxToolOptions {
|
|
307
|
+
/**
|
|
308
|
+
* How to obtain a Mailbox instance given the execution Context.
|
|
309
|
+
* Default: derives project dir from ctx and creates a GlobalMailbox.
|
|
310
|
+
*/
|
|
311
|
+
resolveMailbox?: MailboxResolver | undefined;
|
|
312
|
+
/**
|
|
313
|
+
* Agent id of the caller — used as default "from" on send.
|
|
314
|
+
* Default: 'leader' for the main agent, or derived from ctx.meta.
|
|
315
|
+
*/
|
|
316
|
+
agentId?: string | undefined;
|
|
317
|
+
/** Session id for cross-session communication. Default: derived from ctx. */
|
|
318
|
+
sessionId?: string | undefined;
|
|
319
|
+
/**
|
|
320
|
+
* Project directory where the mailbox is stored.
|
|
321
|
+
* Default: derived from ctx.projectRoot (may differ from wpaths.projectDir).
|
|
322
|
+
* For correct cross-session sharing, pass `wpaths.projectDir` from the caller.
|
|
323
|
+
*/
|
|
324
|
+
projectDir?: string | undefined;
|
|
325
|
+
/**
|
|
326
|
+
* EventBus for emitting mailbox.agent_registered and mailbox.agent_heartbeat
|
|
327
|
+
* events so the TUI/WebUI can update the online agent count in the status bar.
|
|
328
|
+
* When omitted, events are not emitted and the status bar count stays at 0.
|
|
329
|
+
*/
|
|
330
|
+
events?: EventBus | undefined;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Compact, deterministic tag for a session id — 8 hex chars of its sha256.
|
|
334
|
+
* Session ids are date-sharded paths ("2026-06-11/10-48-34Z_model_e66c");
|
|
335
|
+
* the tag keeps mailbox identities short, filesystem-safe, and stable for
|
|
336
|
+
* the lifetime of the session (including across process restarts/resumes).
|
|
337
|
+
*/
|
|
338
|
+
declare function mailboxSessionTag(sessionId: string): string;
|
|
339
|
+
/**
|
|
340
|
+
* Resolve the caller's mailbox identity from the execution Context.
|
|
341
|
+
*
|
|
342
|
+
* Shared by the `mailbox` power-tool, the thin `mail_send`/`mail_inbox`
|
|
343
|
+
* tools, the agent-loop checker, and the /mailbox slash command so every
|
|
344
|
+
* surface agrees on who is talking:
|
|
345
|
+
* - base id: ctx.meta.agentId → ctx.agentId field (subagents) → fallback
|
|
346
|
+
* - unique id: `<base>@<sessionTag>` — SESSION-bound, not pid-bound. Every
|
|
347
|
+
* session has its own id, so two leader sessions on the same project
|
|
348
|
+
* never collide (pids can be recycled by the OS), and a resumed session
|
|
349
|
+
* keeps its identity: read state survives a restart instead of
|
|
350
|
+
* re-flooding old broadcasts. Derived LIVE from ctx.session.id so an
|
|
351
|
+
* in-process session swap (resume / session.new / project switch) moves
|
|
352
|
+
* the identity with it. `ctx.meta.globalAgentId` remains an explicit
|
|
353
|
+
* override for hosts that manage identity themselves.
|
|
354
|
+
*/
|
|
355
|
+
declare function resolveMailboxIdentity(ctx: Context, fallbackBase?: string): {
|
|
356
|
+
baseId: string;
|
|
357
|
+
callerId: string;
|
|
358
|
+
name: string;
|
|
359
|
+
role?: string | undefined;
|
|
360
|
+
sessionId: string;
|
|
361
|
+
};
|
|
362
|
+
declare function makeMailboxTool(opts?: MailboxToolOptions): Tool;
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* mail-tools — thin, high-affordance wrappers over the project mailbox.
|
|
366
|
+
*
|
|
367
|
+
* The multi-action `mailbox` tool is the power surface; these two exist
|
|
368
|
+
* because explicit verbs ("send a mail", "read my inbox") are what makes
|
|
369
|
+
* agents USE the mailbox autonomously — a model reaches for `mail_send`
|
|
370
|
+
* mid-task far more readily than for `mailbox action=send ...`.
|
|
371
|
+
*
|
|
372
|
+
* mail_send — message one agent (`to: "leader@a1b2c3d4"`), every leader
|
|
373
|
+
* (`to: "leader"`), or everyone (`to: "*"`)
|
|
374
|
+
* mail_inbox — read unread mail (unique id + base alias + broadcasts),
|
|
375
|
+
* marking it read so it isn't re-injected next iteration
|
|
376
|
+
*
|
|
377
|
+
* Both share the identity convention with the agent-loop checker
|
|
378
|
+
* (`<base>@<sessionTag>`, see mailbox-attach) via `resolveMailboxIdentity`.
|
|
379
|
+
*
|
|
380
|
+
* @module mail-tools
|
|
381
|
+
*/
|
|
382
|
+
|
|
383
|
+
interface MailToolsOptions {
|
|
384
|
+
/** How to obtain a Mailbox given the execution Context (tests). */
|
|
385
|
+
resolveMailbox?: MailboxResolver | undefined;
|
|
386
|
+
/** Project dir for the shared mailbox. Prefer wpaths.projectDir. */
|
|
387
|
+
projectDir?: string | undefined;
|
|
388
|
+
/** EventBus for mailbox.agent_registered / heartbeat surface events. */
|
|
389
|
+
events?: EventBus | undefined;
|
|
390
|
+
}
|
|
391
|
+
declare function makeMailSendTool(opts?: MailToolsOptions): Tool;
|
|
392
|
+
declare function makeMailInboxTool(opts?: MailToolsOptions): Tool;
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* dep-watcher — File-change → Mailbox bridge for dependency monitoring.
|
|
396
|
+
*
|
|
397
|
+
* Watches dependency manifest files (package.json, go.mod, Cargo.toml, etc.)
|
|
398
|
+
* and when they change (create/update), posts a message to the inter-agent
|
|
399
|
+
* mailbox. A tech-stack analysis agent can then pick up the message and
|
|
400
|
+
* run a full tech-stack validation, feeding results back to the coding LLM.
|
|
401
|
+
*
|
|
402
|
+
* This module is a *config factory*, not a watcher itself. It produces
|
|
403
|
+
* configuration that the file-watcher plugin (`watch_start`) can consume,
|
|
404
|
+
* plus a callback that posts to a Mailbox instance.
|
|
405
|
+
*
|
|
406
|
+
* Usage:
|
|
407
|
+
* const cfg = makeDependencyWatcherConfig({
|
|
408
|
+
* projectRoot: '/path/to/project',
|
|
409
|
+
* mailbox,
|
|
410
|
+
* targetAgent: 'tech-stack-agent',
|
|
411
|
+
* });
|
|
412
|
+
* // cfg.watchPaths → pass to watch_start
|
|
413
|
+
* // cfg.onChange → call on file-watcher:changed events
|
|
414
|
+
*
|
|
415
|
+
* @module dep-watcher
|
|
416
|
+
*/
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Files that declare project dependencies. When any of these change
|
|
420
|
+
* (create/update), a mailbox message triggers a tech-stack audit.
|
|
421
|
+
*/
|
|
422
|
+
declare const DEPENDENCY_FILE_PATTERNS: ReadonlyArray<string>;
|
|
423
|
+
interface DepWatchEntry {
|
|
424
|
+
/** Relative path from project root that changed. */
|
|
425
|
+
path: string;
|
|
426
|
+
/** Event type from the file watcher: 'change', 'add', 'delete' (rare). */
|
|
427
|
+
event: string;
|
|
428
|
+
/** ISO8601 timestamp of when the change was detected. */
|
|
429
|
+
timestamp: string;
|
|
430
|
+
}
|
|
431
|
+
interface DependencyWatcherConfig {
|
|
432
|
+
/** Paths to pass to `watch_start` — the project-root-relative dependency files. */
|
|
433
|
+
watchPaths: string[];
|
|
434
|
+
/** Callback to invoke when a dependency file changes. Posts to mailbox. */
|
|
435
|
+
onChange: (entry: DepWatchEntry) => Promise<void>;
|
|
436
|
+
/** Debounce window in ms — multiple changes to the same file within this window are collapsed. */
|
|
437
|
+
debounceMs: number;
|
|
438
|
+
/** Cancel all in-flight debounce timers. Call when the file watcher is
|
|
439
|
+
* stopped (session end / project switch) so pending setTimeouts — each
|
|
440
|
+
* holding a closure over the mailbox + entry — don't leak. */
|
|
441
|
+
dispose: () => void;
|
|
442
|
+
}
|
|
443
|
+
interface DependencyWatcherOptions {
|
|
444
|
+
/** Absolute path to the project root. */
|
|
445
|
+
projectRoot: string;
|
|
446
|
+
/** The mailbox instance where messages will be posted. */
|
|
447
|
+
mailbox: Mailbox;
|
|
448
|
+
/** Agent id that should receive the tech-stack audit task. */
|
|
449
|
+
targetAgent?: string | undefined;
|
|
450
|
+
/** Agent id of the watcher (sender). */
|
|
451
|
+
watcherAgentId?: string | undefined;
|
|
452
|
+
/** Debounce window in ms. Default: 3000 (3 seconds). */
|
|
453
|
+
debounceMs?: number | undefined;
|
|
454
|
+
/** Only watch these specific patterns. Defaults to DEPENDENCY_FILE_PATTERNS. */
|
|
455
|
+
patterns?: string[] | undefined;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Build a dependency watcher configuration. The returned `watchPaths` can be
|
|
459
|
+
* passed directly to the `watch_start` tool, and `onChange` should be wired
|
|
460
|
+
* to the `file-watcher:changed` custom event.
|
|
461
|
+
*
|
|
462
|
+
* When a dependency file changes, `onChange` posts a high-priority `assign`
|
|
463
|
+
* message to the mailbox targeting the tech-stack agent, with the changed
|
|
464
|
+
* file path and event type in the body.
|
|
465
|
+
*/
|
|
466
|
+
declare function makeDependencyWatcherConfig(opts: DependencyWatcherOptions): DependencyWatcherConfig;
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* dep-watcher-bridge — Bridges the file-watcher plugin's custom events
|
|
470
|
+
* to the dependency watcher → mailbox pipeline.
|
|
471
|
+
*
|
|
472
|
+
* The file-watcher plugin emits `file-watcher:changed` custom events
|
|
473
|
+
* when files change. This module subscribes to those events, filters
|
|
474
|
+
* for dependency manifests (package.json, go.mod, etc.), and posts
|
|
475
|
+
* assign messages to the inter-agent mailbox for tech-stack audit.
|
|
476
|
+
*
|
|
477
|
+
* Returns a dispose function that unsubscribes from the event bus.
|
|
478
|
+
*
|
|
479
|
+
* @module dep-watcher-bridge
|
|
480
|
+
*/
|
|
481
|
+
|
|
482
|
+
interface DepWatcherBridgeOptions {
|
|
483
|
+
/** The event bus to subscribe to (same bus the file-watcher plugin emits on). */
|
|
484
|
+
events: EventBus;
|
|
485
|
+
/** The mailbox instance where dep-change notifications will be posted. */
|
|
486
|
+
mailbox: Mailbox;
|
|
487
|
+
/** Absolute project root — used to build watch paths and match file patterns. */
|
|
488
|
+
projectRoot: string;
|
|
489
|
+
/** Agent id the tech-stack audit tasks should target. Default: 'tech-stack'. */
|
|
490
|
+
targetAgent?: string | undefined;
|
|
491
|
+
/** Agent id of the watcher/sender. Default: 'dep-watcher'. */
|
|
492
|
+
watcherAgentId?: string | undefined;
|
|
493
|
+
/** Debounce window in ms. Default: 3000 (3 seconds). */
|
|
494
|
+
debounceMs?: number | undefined;
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Wire the file-watcher's `file-watcher:changed` events into the
|
|
498
|
+
* dependency watcher → mailbox pipeline.
|
|
499
|
+
*
|
|
500
|
+
* Returns a dispose function. Call it to unsubscribe when the
|
|
501
|
+
* session ends or the watcher is no longer needed.
|
|
502
|
+
*
|
|
503
|
+
* Usage:
|
|
504
|
+
* const dispose = attachDepWatcherBridge({
|
|
505
|
+
* events: ctx.events,
|
|
506
|
+
* mailbox: new DefaultMailbox(sessionDir),
|
|
507
|
+
* projectRoot: ctx.projectRoot,
|
|
508
|
+
* });
|
|
509
|
+
* // ... session runs ...
|
|
510
|
+
* dispose(); // clean up on exit
|
|
511
|
+
*/
|
|
512
|
+
declare function attachDepWatcherBridge(opts: DepWatcherBridgeOptions): () => void;
|
|
513
|
+
|
|
47
514
|
/**
|
|
48
515
|
* mailbox-hooks — Tool-execution hooks for mailbox integration.
|
|
49
516
|
*
|
|
@@ -99,4 +566,1259 @@ declare function createMailboxHooks(opts: MailboxHooksOptions): {
|
|
|
99
566
|
reset(): void;
|
|
100
567
|
};
|
|
101
568
|
|
|
102
|
-
|
|
569
|
+
/**
|
|
570
|
+
* package-author-tracker — Tracks which agent added which package to which manifest.
|
|
571
|
+
*
|
|
572
|
+
* Stores a per-project JSON log in the global project directory:
|
|
573
|
+
* ~/.wrongstack/projects/<slug-hash>/package-authors.json
|
|
574
|
+
*
|
|
575
|
+
* Each entry records: manifest path, package name, version range at time of add,
|
|
576
|
+
* agent id/name, timestamp, and session id.
|
|
577
|
+
*
|
|
578
|
+
* Used by the tech-stack agent and outdated-watcher to route outdated-package
|
|
579
|
+
* notifications back to the agent that originally added the package.
|
|
580
|
+
*
|
|
581
|
+
* @module package-author-tracker
|
|
582
|
+
*/
|
|
583
|
+
interface PackageAuthorEntry {
|
|
584
|
+
/** Absolute or relative path to the manifest (package.json, go.mod, etc.). */
|
|
585
|
+
manifestPath: string;
|
|
586
|
+
/** Exact package name as it appears in the manifest. */
|
|
587
|
+
packageName: string;
|
|
588
|
+
/** Version specifier at time of install (e.g. "^1.2.0", "latest", "file:..."). */
|
|
589
|
+
versionSpec: string;
|
|
590
|
+
/** Ecosystem: 'npm', 'cargo', 'go', 'pip', 'gem', 'composer', 'nuget', etc. */
|
|
591
|
+
ecosystem: string;
|
|
592
|
+
/** Agent id that performed the install (e.g. 'leader', 'executor', 'tech-stack'). */
|
|
593
|
+
agentId: string;
|
|
594
|
+
/** Human-readable agent name. */
|
|
595
|
+
agentName?: string | undefined;
|
|
596
|
+
/** Session that performed the install. */
|
|
597
|
+
sessionId?: string | undefined;
|
|
598
|
+
/** ISO8601 timestamp. */
|
|
599
|
+
timestamp: string;
|
|
600
|
+
/** Whether this package is currently flagged as outdated. */
|
|
601
|
+
outdated?: boolean | undefined;
|
|
602
|
+
/** Latest version available (set by outdated checker). */
|
|
603
|
+
latestVersion?: string | undefined;
|
|
604
|
+
}
|
|
605
|
+
interface PackageAuthorLog {
|
|
606
|
+
/** Project root this log belongs to. */
|
|
607
|
+
projectRoot: string;
|
|
608
|
+
/** All entries, newest last. */
|
|
609
|
+
entries: PackageAuthorEntry[];
|
|
610
|
+
/** Last time the log was compacted. */
|
|
611
|
+
lastCompactedAt?: string | undefined;
|
|
612
|
+
}
|
|
613
|
+
interface PackageAuthorTrackerOptions {
|
|
614
|
+
/** Directory where the JSON log is stored. Usually the global project dir. */
|
|
615
|
+
storageDir: string;
|
|
616
|
+
/** Project root for reference. */
|
|
617
|
+
projectRoot: string;
|
|
618
|
+
/** Max entries before auto-compaction. Default: 10000. */
|
|
619
|
+
maxEntries?: number | undefined;
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Detect the ecosystem from a manifest filename.
|
|
623
|
+
*/
|
|
624
|
+
declare function detectEcosystem(manifestPath: string): string;
|
|
625
|
+
/**
|
|
626
|
+
* Record that an agent added (or updated) a package to a manifest file.
|
|
627
|
+
*
|
|
628
|
+
* If the same (manifestPath, packageName) entry already exists, the previous
|
|
629
|
+
* entry is kept (for audit trail) and a new entry is appended.
|
|
630
|
+
*/
|
|
631
|
+
declare function recordPackageAction(opts: PackageAuthorTrackerOptions, entry: Omit<PackageAuthorEntry, 'timestamp'>): Promise<void>;
|
|
632
|
+
/**
|
|
633
|
+
* Get the most recent author entry for a given (manifest, package) pair.
|
|
634
|
+
* Returns undefined if no entry exists.
|
|
635
|
+
*/
|
|
636
|
+
declare function getPackageAuthor(opts: Pick<PackageAuthorTrackerOptions, 'storageDir' | 'projectRoot'>, manifestPath: string, packageName: string): Promise<PackageAuthorEntry | undefined>;
|
|
637
|
+
/**
|
|
638
|
+
* Get all packages in a manifest that have an author on record.
|
|
639
|
+
*/
|
|
640
|
+
declare function getManifestPackages(opts: Pick<PackageAuthorTrackerOptions, 'storageDir' | 'projectRoot'>, manifestPath: string): Promise<PackageAuthorEntry[]>;
|
|
641
|
+
/**
|
|
642
|
+
* Get all packages last tracked by a specific agent.
|
|
643
|
+
* Returns a Map from (manifestPath, packageName) → entry.
|
|
644
|
+
*/
|
|
645
|
+
declare function getPackagesByAgent(opts: Pick<PackageAuthorTrackerOptions, 'storageDir' | 'projectRoot'>, agentId: string): Promise<Map<string, PackageAuthorEntry>>;
|
|
646
|
+
/**
|
|
647
|
+
* Update the outdated status of a package entry (adds or replaces the entry
|
|
648
|
+
* for the given manifest+package, appending to the log for audit).
|
|
649
|
+
*/
|
|
650
|
+
declare function updatePackageOutdatedStatus(opts: PackageAuthorTrackerOptions, manifestPath: string, packageName: string, outdated: boolean, latestVersion?: string | undefined): Promise<void>;
|
|
651
|
+
/**
|
|
652
|
+
* Return the full log (for debugging/auditing).
|
|
653
|
+
*/
|
|
654
|
+
declare function getFullPackageLog(opts: Pick<PackageAuthorTrackerOptions, 'storageDir' | 'projectRoot'>): Promise<PackageAuthorLog>;
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* package-outdated-watcher — Periodically checks installed packages for outdated
|
|
658
|
+
* versions and notifies the agent that originally added each package.
|
|
659
|
+
*
|
|
660
|
+
* Architecture:
|
|
661
|
+
* 1. Polls the mailbox for `assign` messages from the tech-stack agent
|
|
662
|
+
* containing outdated package results.
|
|
663
|
+
* 2. For each outdated package, looks up the original author via
|
|
664
|
+
* `package-author-tracker`.
|
|
665
|
+
* 3. Sends a high-priority `note` message to the original author (or
|
|
666
|
+
* broadcasts to `*` if the author is unknown or no longer online).
|
|
667
|
+
*
|
|
668
|
+
* The watcher can also be triggered directly via `checkOutdated` for
|
|
669
|
+
* on-demand checks (e.g. on a timer, or when the user requests it).
|
|
670
|
+
*
|
|
671
|
+
* Usage:
|
|
672
|
+
* const dispose = startPackageOutdatedWatcher({
|
|
673
|
+
* mailbox,
|
|
674
|
+
* storageDir: wpaths.globalDir,
|
|
675
|
+
* projectRoot,
|
|
676
|
+
* pollIntervalMs: 60 * 60 * 1000, // 1 hour
|
|
677
|
+
* onNotify: async (msg) => mailbox.send(msg),
|
|
678
|
+
* onLog: (m) => console.log(`[pkg-outdated-watcher] ${m}`),
|
|
679
|
+
* });
|
|
680
|
+
*
|
|
681
|
+
* @module package-outdated-watcher
|
|
682
|
+
*/
|
|
683
|
+
|
|
684
|
+
interface PackageOutdatedEntry {
|
|
685
|
+
/** Package name. */
|
|
686
|
+
name: string;
|
|
687
|
+
/** Currently installed version. */
|
|
688
|
+
currentVersion: string;
|
|
689
|
+
/** Latest stable version available. */
|
|
690
|
+
latestVersion: string;
|
|
691
|
+
/** semver major.minor.patch wanted range (from lockfile). */
|
|
692
|
+
wantedVersion: string;
|
|
693
|
+
/** Manifest file this package belongs to. */
|
|
694
|
+
manifestPath: string;
|
|
695
|
+
/** Ecosystem: 'npm', 'cargo', 'go', etc. */
|
|
696
|
+
ecosystem: string;
|
|
697
|
+
}
|
|
698
|
+
interface PackageOutdatedResult {
|
|
699
|
+
/** All outdated entries. */
|
|
700
|
+
outdated: PackageOutdatedEntry[];
|
|
701
|
+
/** Packages that are up-to-date. */
|
|
702
|
+
upToDate: string[];
|
|
703
|
+
/** Whether the check failed. */
|
|
704
|
+
checkFailed: boolean;
|
|
705
|
+
}
|
|
706
|
+
interface PackageOutdatedWatcherOptions {
|
|
707
|
+
/** The mailbox for sending notifications and receiving tech-stack results. */
|
|
708
|
+
mailbox: Mailbox;
|
|
709
|
+
/** Package-author-tracker options. */
|
|
710
|
+
packageTrackerOpts: Pick<PackageAuthorTrackerOptions, 'storageDir' | 'projectRoot'>;
|
|
711
|
+
/** Polling interval in ms. Default: 60 * 60 * 1000 (1 hour). */
|
|
712
|
+
pollIntervalMs?: number | undefined;
|
|
713
|
+
/** Agent id that runs this watcher. Default: 'pkg-outdated-watcher'. */
|
|
714
|
+
watcherAgentId?: string | undefined;
|
|
715
|
+
/** Agent id of the tech-stack agent to watch for results. Default: 'tech-stack'. */
|
|
716
|
+
techStackAgentId?: string | undefined;
|
|
717
|
+
/** Called to send a notification to an agent. */
|
|
718
|
+
onNotify: (msg: OutdatedNotifyMessage) => Promise<void>;
|
|
719
|
+
/** Called for log output. */
|
|
720
|
+
onLog?: ((msg: string) => void) | undefined;
|
|
721
|
+
/** Called on errors. */
|
|
722
|
+
onError?: ((err: unknown) => void) | undefined;
|
|
723
|
+
}
|
|
724
|
+
interface OutdatedNotifyMessage {
|
|
725
|
+
from: string;
|
|
726
|
+
to: string;
|
|
727
|
+
subject: string;
|
|
728
|
+
body: string;
|
|
729
|
+
priority: 'high' | 'normal' | 'low';
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Start the package outdated watcher.
|
|
733
|
+
*
|
|
734
|
+
* Returns a dispose function that stops polling and cleans up.
|
|
735
|
+
*/
|
|
736
|
+
declare function startPackageOutdatedWatcher(opts: PackageOutdatedWatcherOptions): () => void;
|
|
737
|
+
|
|
738
|
+
type NodeType = 'fact' | 'goal' | 'decision' | 'change' | 'vote';
|
|
739
|
+
type FactCategory = 'bug' | 'refactor' | 'security' | 'test' | 'perf' | 'deps' | 'architecture' | 'quality';
|
|
740
|
+
type GoalStatus = 'pending' | 'in_progress' | 'blocked' | 'done' | 'failed';
|
|
741
|
+
type GoalPriority = 'critical' | 'high' | 'medium' | 'low';
|
|
742
|
+
type ChangeStatus = 'proposed' | 'approved' | 'rejected' | 'applied' | 'rolled_back';
|
|
743
|
+
type VoteValue = 'approve' | 'reject' | 'abstain';
|
|
744
|
+
type DecisionType = 'spawn' | 'assign' | 'approve_change' | 'reject_change' | 'escalate' | 'rollback' | 'merge_results';
|
|
745
|
+
interface FactNode {
|
|
746
|
+
id: string;
|
|
747
|
+
type: 'fact';
|
|
748
|
+
category: FactCategory;
|
|
749
|
+
subject: string;
|
|
750
|
+
detail: string;
|
|
751
|
+
file?: string;
|
|
752
|
+
line?: number;
|
|
753
|
+
severity?: 'critical' | 'high' | 'medium' | 'low';
|
|
754
|
+
discoveredBy: string;
|
|
755
|
+
discoveredAt: string;
|
|
756
|
+
tags: string[];
|
|
757
|
+
/** Stable key — dedup facts about the same subject */
|
|
758
|
+
key: string;
|
|
759
|
+
/** References to other nodes this fact relates to */
|
|
760
|
+
related: string[];
|
|
761
|
+
}
|
|
762
|
+
interface GoalNode {
|
|
763
|
+
id: string;
|
|
764
|
+
type: 'goal';
|
|
765
|
+
title: string;
|
|
766
|
+
description: string;
|
|
767
|
+
status: GoalStatus;
|
|
768
|
+
priority: GoalPriority;
|
|
769
|
+
assignee?: string;
|
|
770
|
+
blockedBy: string[];
|
|
771
|
+
dependsOn: string[];
|
|
772
|
+
createdBy: string;
|
|
773
|
+
createdAt: string;
|
|
774
|
+
updatedAt: string;
|
|
775
|
+
tags: string[];
|
|
776
|
+
/** Sub-goals spawned from this goal */
|
|
777
|
+
children: string[];
|
|
778
|
+
/** The top-level goal this belongs to (for hierarchy) */
|
|
779
|
+
parentGoal?: string;
|
|
780
|
+
result?: string;
|
|
781
|
+
}
|
|
782
|
+
interface DecisionNode {
|
|
783
|
+
id: string;
|
|
784
|
+
type: 'decision';
|
|
785
|
+
decisionType: DecisionType;
|
|
786
|
+
question: string;
|
|
787
|
+
options: {
|
|
788
|
+
id: string;
|
|
789
|
+
label: string;
|
|
790
|
+
risk?: string;
|
|
791
|
+
}[];
|
|
792
|
+
chosen: string;
|
|
793
|
+
rationale: string;
|
|
794
|
+
madeBy: string;
|
|
795
|
+
madeAt: string;
|
|
796
|
+
context?: string;
|
|
797
|
+
}
|
|
798
|
+
interface ChangeNode {
|
|
799
|
+
id: string;
|
|
800
|
+
type: 'change';
|
|
801
|
+
title: string;
|
|
802
|
+
description: string;
|
|
803
|
+
files: {
|
|
804
|
+
path: string;
|
|
805
|
+
action: 'create' | 'modify' | 'delete';
|
|
806
|
+
}[];
|
|
807
|
+
status: ChangeStatus;
|
|
808
|
+
proposedBy: string;
|
|
809
|
+
proposedAt: string;
|
|
810
|
+
approvedBy: string[];
|
|
811
|
+
rejectedBy: string[];
|
|
812
|
+
appliedAt?: string;
|
|
813
|
+
rolledBackAt?: string;
|
|
814
|
+
rollbackReason?: string;
|
|
815
|
+
votes: VoteRecord[];
|
|
816
|
+
qualityGate: QualityGateResult;
|
|
817
|
+
/** Goals satisfied by this change */
|
|
818
|
+
satisfiesGoals: string[];
|
|
819
|
+
}
|
|
820
|
+
interface VoteRecord {
|
|
821
|
+
agentId: string;
|
|
822
|
+
agentName: string;
|
|
823
|
+
value: VoteValue;
|
|
824
|
+
rationale?: string | undefined;
|
|
825
|
+
votedAt: string;
|
|
826
|
+
}
|
|
827
|
+
interface QualityGateResult {
|
|
828
|
+
passed: boolean;
|
|
829
|
+
checks: QualityCheck[];
|
|
830
|
+
}
|
|
831
|
+
interface QualityCheck {
|
|
832
|
+
name: string;
|
|
833
|
+
passed: boolean;
|
|
834
|
+
detail?: string;
|
|
835
|
+
}
|
|
836
|
+
interface VoteNode {
|
|
837
|
+
id: string;
|
|
838
|
+
type: 'vote';
|
|
839
|
+
changeId: string;
|
|
840
|
+
voterId: string;
|
|
841
|
+
voterName: string;
|
|
842
|
+
value: VoteValue;
|
|
843
|
+
rationale?: string;
|
|
844
|
+
votedAt: string;
|
|
845
|
+
}
|
|
846
|
+
type GraphNode = FactNode | GoalNode | DecisionNode | ChangeNode | VoteNode;
|
|
847
|
+
interface GraphSubscription {
|
|
848
|
+
id: string;
|
|
849
|
+
agentId: string;
|
|
850
|
+
/** JSONPath-like filter */
|
|
851
|
+
filter: NodeFilter;
|
|
852
|
+
/** Channel for this specific subscription */
|
|
853
|
+
channel: string;
|
|
854
|
+
}
|
|
855
|
+
interface NodeFilter {
|
|
856
|
+
type?: NodeType;
|
|
857
|
+
category?: FactCategory;
|
|
858
|
+
status?: GoalStatus | ChangeStatus;
|
|
859
|
+
tags?: string[];
|
|
860
|
+
assignee?: string;
|
|
861
|
+
discoveredBy?: string;
|
|
862
|
+
proposedBy?: string;
|
|
863
|
+
/** Only nodes added after this timestamp */
|
|
864
|
+
since?: string;
|
|
865
|
+
}
|
|
866
|
+
declare class KnowledgeGraph {
|
|
867
|
+
private readonly nodes;
|
|
868
|
+
private readonly index;
|
|
869
|
+
private readonly subs;
|
|
870
|
+
private readonly pendingDeliveries;
|
|
871
|
+
private readonly filePath;
|
|
872
|
+
private readonly graphFilePath;
|
|
873
|
+
/** Exposed for unit-testing only: read current index contents. */
|
|
874
|
+
getIndex(): ReadonlyMap<string, ReadonlySet<string>>;
|
|
875
|
+
constructor(sessionDir: string);
|
|
876
|
+
/**
|
|
877
|
+
* Add a node. Fires to all matching subscriptions synchronously.
|
|
878
|
+
* Returns the node with its assigned id.
|
|
879
|
+
*/
|
|
880
|
+
add(node: Omit<GraphNode, 'id'>): Promise<GraphNode>;
|
|
881
|
+
/** Update an existing node by id. Returns updated node or null if not found. */
|
|
882
|
+
update(id: string, patch: Partial<GraphNode>): Promise<GraphNode | null>;
|
|
883
|
+
get(id: string): GraphNode | undefined;
|
|
884
|
+
getAll(filter?: NodeFilter): GraphNode[];
|
|
885
|
+
getGoals(filter?: Partial<{
|
|
886
|
+
status: GoalStatus;
|
|
887
|
+
assignee: string;
|
|
888
|
+
priority: GoalPriority;
|
|
889
|
+
}>): GoalNode[];
|
|
890
|
+
getFacts(filter?: Partial<{
|
|
891
|
+
category: FactCategory;
|
|
892
|
+
severity: string;
|
|
893
|
+
}>): FactNode[];
|
|
894
|
+
getChanges(filter?: Partial<{
|
|
895
|
+
status: ChangeStatus;
|
|
896
|
+
}>): ChangeNode[];
|
|
897
|
+
getOpenGoals(): GoalNode[];
|
|
898
|
+
getTopLevelGoals(): GoalNode[];
|
|
899
|
+
getBlockedGoals(): GoalNode[];
|
|
900
|
+
getPendingChanges(): ChangeNode[];
|
|
901
|
+
getDecisions(since?: string): DecisionNode[];
|
|
902
|
+
searchFacts(query: string): FactNode[];
|
|
903
|
+
getRelatedFacts(factId: string): FactNode[];
|
|
904
|
+
/**
|
|
905
|
+
* Subscribe to nodes matching a filter. Returns a channel id that can be
|
|
906
|
+
* used to poll for new nodes since the last check.
|
|
907
|
+
*/
|
|
908
|
+
subscribe(agentId: string, filter: NodeFilter): string;
|
|
909
|
+
/**
|
|
910
|
+
* Poll for new nodes delivered to a channel since last check.
|
|
911
|
+
* Clears the delivery buffer after reading.
|
|
912
|
+
*/
|
|
913
|
+
poll(channel: string): GraphNode[];
|
|
914
|
+
unsubscribe(channel: string): void;
|
|
915
|
+
/**
|
|
916
|
+
* Create a quality gate result. Call this when a change is being proposed
|
|
917
|
+
* so the change node carries the gate result.
|
|
918
|
+
*/
|
|
919
|
+
static makeQualityGate(checks: {
|
|
920
|
+
name: string;
|
|
921
|
+
passed: boolean;
|
|
922
|
+
detail?: string;
|
|
923
|
+
}[]): QualityGateResult;
|
|
924
|
+
/** Pure: compute the set of index keys a node would belong to. */
|
|
925
|
+
private _indexKeys;
|
|
926
|
+
/** Mutate the index: add a node's id to every set for the given keys. */
|
|
927
|
+
private _addToIndex;
|
|
928
|
+
/** Remove a node's id from all index sets for the given keys. */
|
|
929
|
+
private _removeFromIndex;
|
|
930
|
+
private _matches;
|
|
931
|
+
private _deliver;
|
|
932
|
+
private _persist;
|
|
933
|
+
private _append;
|
|
934
|
+
/** Rebuild in-memory state from the log file. Call on startup. */
|
|
935
|
+
load(): Promise<void>;
|
|
936
|
+
/** Snapshot for serialization. */
|
|
937
|
+
snapshot(): {
|
|
938
|
+
nodes: GraphNode[];
|
|
939
|
+
subs: number;
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
/**
|
|
944
|
+
* TaskDAG — Directed Acyclic Graph of tasks with fork/join semantics.
|
|
945
|
+
*
|
|
946
|
+
* Replaces the Director's flat `awaitTasks()` with a proper dependency graph.
|
|
947
|
+
* Each task has explicit dependencies; the DAG resolves which tasks are
|
|
948
|
+
* runnable at any moment and manages blocking/unblocking as tasks complete.
|
|
949
|
+
*
|
|
950
|
+
* Key features:
|
|
951
|
+
* - Fork: one parent spawns multiple children that run in parallel
|
|
952
|
+
* - Join: a task can wait for multiple children before continuing
|
|
953
|
+
* - Dynamic: tasks can be added dynamically; the graph re-evaluates runnable set
|
|
954
|
+
* - Cycle detection: inserting a dependency that would create a cycle throws
|
|
955
|
+
* - Priority queue: within a "runnable" set, tasks are ordered by priority
|
|
956
|
+
* - Deadlock detection: if no tasks are runnable and no tasks are complete,
|
|
957
|
+
* the DAG is in deadlock — agents are notified
|
|
958
|
+
*
|
|
959
|
+
* @module task-dag
|
|
960
|
+
*/
|
|
961
|
+
/** Represents a node in the DAG. */
|
|
962
|
+
interface DAGNode {
|
|
963
|
+
id: string;
|
|
964
|
+
description: string;
|
|
965
|
+
role?: string;
|
|
966
|
+
priority: number;
|
|
967
|
+
status: DAGNodeStatus;
|
|
968
|
+
deps: string[];
|
|
969
|
+
dependents: string[];
|
|
970
|
+
result?: unknown;
|
|
971
|
+
error?: string;
|
|
972
|
+
spawnedAt?: string;
|
|
973
|
+
completedAt?: string;
|
|
974
|
+
assignedTo?: string;
|
|
975
|
+
tags: string[];
|
|
976
|
+
}
|
|
977
|
+
type DAGNodeStatus = 'pending' | 'ready' | 'running' | 'done' | 'failed' | 'skipped';
|
|
978
|
+
/** Event emitted by the DAG on state changes. */
|
|
979
|
+
type DAGEdgeEvent = {
|
|
980
|
+
type: 'node:ready';
|
|
981
|
+
nodeId: string;
|
|
982
|
+
deps: string[];
|
|
983
|
+
} | {
|
|
984
|
+
type: 'node:started';
|
|
985
|
+
nodeId: string;
|
|
986
|
+
assignedTo: string;
|
|
987
|
+
} | {
|
|
988
|
+
type: 'node:completed';
|
|
989
|
+
nodeId: string;
|
|
990
|
+
result: unknown;
|
|
991
|
+
blockers: string[];
|
|
992
|
+
} | {
|
|
993
|
+
type: 'node:failed';
|
|
994
|
+
nodeId: string;
|
|
995
|
+
error: string;
|
|
996
|
+
blockers: string[];
|
|
997
|
+
} | {
|
|
998
|
+
type: 'node:skipped';
|
|
999
|
+
nodeId: string;
|
|
1000
|
+
reason: string;
|
|
1001
|
+
} | {
|
|
1002
|
+
type: 'deadlock';
|
|
1003
|
+
blocked: string[];
|
|
1004
|
+
} | {
|
|
1005
|
+
type: 'graph:done';
|
|
1006
|
+
allDone: boolean;
|
|
1007
|
+
};
|
|
1008
|
+
type DAGEdgeHandler = (event: DAGEdgeEvent) => void;
|
|
1009
|
+
/** Callback invoked when the DAG produces a set of runnable tasks. */
|
|
1010
|
+
type RunnablesHandler = (nodes: DAGNode[]) => void;
|
|
1011
|
+
declare class TaskDAG {
|
|
1012
|
+
private readonly nodes;
|
|
1013
|
+
private readonly handlers;
|
|
1014
|
+
private readonly runnablesHandlers;
|
|
1015
|
+
private runnableCache;
|
|
1016
|
+
/**
|
|
1017
|
+
* Add a task node. Dependencies are validated for cycles.
|
|
1018
|
+
* Throws if adding a dep would create a cycle.
|
|
1019
|
+
*/
|
|
1020
|
+
addNode(id: string, description: string, deps?: string[], opts?: {
|
|
1021
|
+
role?: string;
|
|
1022
|
+
priority?: number;
|
|
1023
|
+
tags?: string[];
|
|
1024
|
+
}): void;
|
|
1025
|
+
/**
|
|
1026
|
+
* Remove a node and all edges to/from it.
|
|
1027
|
+
* Skips any dependents that would become dangling.
|
|
1028
|
+
*/
|
|
1029
|
+
removeNode(id: string): void;
|
|
1030
|
+
/**
|
|
1031
|
+
* Mark a task as running. Returns true if the transition was valid
|
|
1032
|
+
* (task was in 'ready' state), false otherwise.
|
|
1033
|
+
*/
|
|
1034
|
+
start(id: string, assignedTo: string): boolean;
|
|
1035
|
+
/**
|
|
1036
|
+
* Mark a task as completed. Unblocks all dependents; they become 'ready'
|
|
1037
|
+
* if all their deps are done.
|
|
1038
|
+
*/
|
|
1039
|
+
complete(id: string, result: unknown): void;
|
|
1040
|
+
/**
|
|
1041
|
+
* Mark a task as failed. Unblocks dependents but they remain 'pending'
|
|
1042
|
+
* (they may still be runnable if other deps succeeded).
|
|
1043
|
+
*/
|
|
1044
|
+
fail(id: string, error: string): void;
|
|
1045
|
+
/**
|
|
1046
|
+
* Skip a task (e.g., it was deemed unnecessary by an earlier step).
|
|
1047
|
+
* Treats it as done for dependency purposes.
|
|
1048
|
+
*/
|
|
1049
|
+
skip(id: string, reason: string): void;
|
|
1050
|
+
getNode(id: string): DAGNode | undefined;
|
|
1051
|
+
getAll(): DAGNode[];
|
|
1052
|
+
getReady(): DAGNode[];
|
|
1053
|
+
getRunning(): DAGNode[];
|
|
1054
|
+
getPending(): DAGNode[];
|
|
1055
|
+
getDone(): DAGNode[];
|
|
1056
|
+
getFailed(): DAGNode[];
|
|
1057
|
+
getCompleted(): DAGNode[];
|
|
1058
|
+
isDone(): boolean;
|
|
1059
|
+
isFailed(): boolean;
|
|
1060
|
+
/** All tasks that are currently blocked (pending but not ready). */
|
|
1061
|
+
getBlocked(): DAGNode[];
|
|
1062
|
+
/** Topological sort — tasks in dependency order. */
|
|
1063
|
+
getTopologicalOrder(): DAGNode[];
|
|
1064
|
+
/** Check for deadlock: no runnable tasks but not done. */
|
|
1065
|
+
hasDeadlock(): boolean;
|
|
1066
|
+
/** Stats snapshot for reporting. */
|
|
1067
|
+
stats(): {
|
|
1068
|
+
total: number;
|
|
1069
|
+
pending: number;
|
|
1070
|
+
ready: number;
|
|
1071
|
+
running: number;
|
|
1072
|
+
done: number;
|
|
1073
|
+
failed: number;
|
|
1074
|
+
skipped: number;
|
|
1075
|
+
progress: number;
|
|
1076
|
+
};
|
|
1077
|
+
onEvent(handler: DAGEdgeHandler): () => void;
|
|
1078
|
+
onRunnable(handler: RunnablesHandler): () => void;
|
|
1079
|
+
private _transition;
|
|
1080
|
+
private _emit;
|
|
1081
|
+
private _emitReady;
|
|
1082
|
+
private invalidateCache;
|
|
1083
|
+
/**
|
|
1084
|
+
* DFS cycle detection. Adding edge (id → dep) creates a cycle if
|
|
1085
|
+
* there already exists a path from dep to id.
|
|
1086
|
+
*/
|
|
1087
|
+
private _wouldCycle;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
/**
|
|
1091
|
+
* ConsensusProtocol — agent voting on proposed changes.
|
|
1092
|
+
*
|
|
1093
|
+
* Enables autonomous approval of code changes without a human. Agents register
|
|
1094
|
+
* as voters with a role weight; changes gather votes; the protocol resolves
|
|
1095
|
+
* the outcome based on configured quorum rules.
|
|
1096
|
+
*
|
|
1097
|
+
* Voting rules (configurable):
|
|
1098
|
+
* - Quorum: minimum fraction of eligible voters required (default: 0.5)
|
|
1099
|
+
* - Approval threshold: minimum fraction of cast votes that must be approve (default: 0.6)
|
|
1100
|
+
* - Veto roles: roles whose 'reject' vote is fatal regardless of count
|
|
1101
|
+
* - Auto-approve: changes with severity=critical bypass voting if the proposer is trusted
|
|
1102
|
+
*
|
|
1103
|
+
* The protocol is stateless — it reads from the KnowledgeGraph and writes results
|
|
1104
|
+
* back to it. This makes it naturally consistent with the shared knowledge model.
|
|
1105
|
+
*
|
|
1106
|
+
* @module consensus-protocol
|
|
1107
|
+
*/
|
|
1108
|
+
|
|
1109
|
+
interface VoterConfig {
|
|
1110
|
+
agentId: string;
|
|
1111
|
+
agentName: string;
|
|
1112
|
+
role: string;
|
|
1113
|
+
/** Weight multiplier for this voter's vote. Default: 1. */
|
|
1114
|
+
weight: number;
|
|
1115
|
+
/** If true, a 'reject' vote from this role is a hard veto. Default: false. */
|
|
1116
|
+
veto?: boolean;
|
|
1117
|
+
/**
|
|
1118
|
+
* @deprecated Not yet implemented. Auto-approve of low-risk changes is planned
|
|
1119
|
+
* but not wired up in the vote resolution logic.
|
|
1120
|
+
*/
|
|
1121
|
+
autoApprovesLowRisk?: boolean;
|
|
1122
|
+
}
|
|
1123
|
+
interface QuorumRule {
|
|
1124
|
+
/** Fraction of eligible voters required (0-1). Default: 0.5. */
|
|
1125
|
+
quorumFraction: number;
|
|
1126
|
+
/** Fraction of cast votes that must be approve (0-1). Default: 0.6. */
|
|
1127
|
+
approvalFraction: number;
|
|
1128
|
+
/** Roles whose reject vote is a hard veto regardless of count. */
|
|
1129
|
+
vetoRoles: string[];
|
|
1130
|
+
/** Minimum total weight of approve votes to pass (0-1 of total eligible weight). */
|
|
1131
|
+
approvalWeightFraction?: number | undefined;
|
|
1132
|
+
}
|
|
1133
|
+
interface ConsensusResult {
|
|
1134
|
+
changeId: string;
|
|
1135
|
+
outcome: 'approved' | 'rejected' | 'pending' | 'vetoed' | 'quorum_not_met';
|
|
1136
|
+
votes: VoteRecord[];
|
|
1137
|
+
approveCount: number;
|
|
1138
|
+
rejectCount: number;
|
|
1139
|
+
abstainCount: number;
|
|
1140
|
+
totalWeightApprove: number;
|
|
1141
|
+
totalWeightReject: number;
|
|
1142
|
+
eligibleVoters: string[];
|
|
1143
|
+
quorumMet: boolean;
|
|
1144
|
+
approvalMet: boolean;
|
|
1145
|
+
vetoedBy?: string;
|
|
1146
|
+
rationale: string;
|
|
1147
|
+
}
|
|
1148
|
+
interface ConsensusOptions {
|
|
1149
|
+
rules?: Partial<QuorumRule>;
|
|
1150
|
+
voters: VoterConfig[];
|
|
1151
|
+
graph: KnowledgeGraph;
|
|
1152
|
+
fleet?: FleetBus | undefined;
|
|
1153
|
+
}
|
|
1154
|
+
/**
|
|
1155
|
+
* ConsensusProtocol manages voting on ChangeNodes in the KnowledgeGraph.
|
|
1156
|
+
* It is instantiated once per autonomous session and used to initiate votes,
|
|
1157
|
+
* cast votes, and resolve outcomes.
|
|
1158
|
+
*/
|
|
1159
|
+
declare class ConsensusProtocol {
|
|
1160
|
+
private readonly graph;
|
|
1161
|
+
private readonly fleet?;
|
|
1162
|
+
private readonly rules;
|
|
1163
|
+
private readonly voters;
|
|
1164
|
+
constructor(opts: ConsensusOptions);
|
|
1165
|
+
/**
|
|
1166
|
+
* Initiate a vote on a proposed change. Updates the change node's status
|
|
1167
|
+
* to 'proposed' and notifies eligible voters via FleetBus.
|
|
1168
|
+
*/
|
|
1169
|
+
initiateVote(changeId: string): void;
|
|
1170
|
+
/**
|
|
1171
|
+
* Cast a vote. Updates the change node in the graph and re-evaluates
|
|
1172
|
+
* consensus. If the vote triggers a resolution, updates the change status.
|
|
1173
|
+
*/
|
|
1174
|
+
castVote(changeId: string, voterId: string, value: VoteValue, rationale?: string): ConsensusResult;
|
|
1175
|
+
/**
|
|
1176
|
+
* Resolve the current vote without waiting for all eligible voters.
|
|
1177
|
+
* Useful when a timeout fires or an agent decides to finalize early.
|
|
1178
|
+
*/
|
|
1179
|
+
resolveNow(changeId: string): ConsensusResult;
|
|
1180
|
+
/**
|
|
1181
|
+
* Register or update a voter's configuration.
|
|
1182
|
+
*/
|
|
1183
|
+
registerVoter(config: VoterConfig): void;
|
|
1184
|
+
/**
|
|
1185
|
+
* Get the current vote status for a change.
|
|
1186
|
+
*/
|
|
1187
|
+
getStatus(changeId: string): ConsensusResult | null;
|
|
1188
|
+
private _eligibleVoters;
|
|
1189
|
+
private _resolve;
|
|
1190
|
+
private _toChangeStatus;
|
|
1191
|
+
private _notifyVoters;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
/**
|
|
1195
|
+
* ChangeManager — autonomous code change lifecycle management.
|
|
1196
|
+
*
|
|
1197
|
+
* Manages the full lifecycle of proposed code changes:
|
|
1198
|
+
* PROPOSE → REVIEW (consensus) → APPLY → VERIFY → (ROLLBACK on failure)
|
|
1199
|
+
*
|
|
1200
|
+
* The manager does NOT write files directly — it publishes change nodes to the
|
|
1201
|
+
* KnowledgeGraph and uses the ConsensusProtocol for approvals. File mutations
|
|
1202
|
+
* are performed by agents acting on approved change nodes.
|
|
1203
|
+
*
|
|
1204
|
+
* Quality gates run automatically before approval:
|
|
1205
|
+
* - Tests must pass (or be explicitly waived)
|
|
1206
|
+
* - TypeScript must compile
|
|
1207
|
+
* - No new critical/high security findings
|
|
1208
|
+
* - Lint must pass (or be explicitly ignored)
|
|
1209
|
+
*
|
|
1210
|
+
* Rollback: on failure detection, the manager can propose a rollback change
|
|
1211
|
+
* that reverses the applied change. Rollback changes also go through consensus.
|
|
1212
|
+
*
|
|
1213
|
+
* @module change-manager
|
|
1214
|
+
*/
|
|
1215
|
+
|
|
1216
|
+
interface ChangeFile {
|
|
1217
|
+
path: string;
|
|
1218
|
+
action: 'create' | 'modify' | 'delete';
|
|
1219
|
+
/** For modify: unified diff string */
|
|
1220
|
+
diff?: string;
|
|
1221
|
+
/** For create/modify: full file content */
|
|
1222
|
+
content?: string;
|
|
1223
|
+
}
|
|
1224
|
+
interface ChangeProposal {
|
|
1225
|
+
title: string;
|
|
1226
|
+
description: string;
|
|
1227
|
+
files: ChangeFile[];
|
|
1228
|
+
proposedBy: string;
|
|
1229
|
+
satisfiesGoals: string[];
|
|
1230
|
+
tags: string[];
|
|
1231
|
+
/** Quality gate waivers (e.g., ['no-tests', 'lint-errors']) */
|
|
1232
|
+
waivers?: string[];
|
|
1233
|
+
/** Skip consensus vote (for automated/internal refactors only) */
|
|
1234
|
+
skipVote?: boolean;
|
|
1235
|
+
}
|
|
1236
|
+
interface ApplyResult {
|
|
1237
|
+
changeId: string;
|
|
1238
|
+
success: boolean;
|
|
1239
|
+
appliedAt: string;
|
|
1240
|
+
filesTouched: string[];
|
|
1241
|
+
verificationResult?: QualityGateResult;
|
|
1242
|
+
rollbackChangeId?: string | undefined;
|
|
1243
|
+
error?: string | undefined;
|
|
1244
|
+
}
|
|
1245
|
+
interface RollbackResult {
|
|
1246
|
+
originalChangeId: string;
|
|
1247
|
+
rollbackChangeId: string;
|
|
1248
|
+
success: boolean;
|
|
1249
|
+
rolledBackAt: string;
|
|
1250
|
+
error?: string;
|
|
1251
|
+
}
|
|
1252
|
+
/** Quality checks performed before applying a change. */
|
|
1253
|
+
interface QualityGateChecks {
|
|
1254
|
+
runTests: boolean;
|
|
1255
|
+
runTypecheck: boolean;
|
|
1256
|
+
runLint: boolean;
|
|
1257
|
+
runSecurityScan: boolean;
|
|
1258
|
+
checkTestCoverage: boolean;
|
|
1259
|
+
minCoveragePercent?: number;
|
|
1260
|
+
}
|
|
1261
|
+
interface ChangeManagerOptions {
|
|
1262
|
+
graph: KnowledgeGraph;
|
|
1263
|
+
consensus: ConsensusProtocol;
|
|
1264
|
+
fleet?: FleetBus | undefined;
|
|
1265
|
+
checks?: Partial<QualityGateChecks> | undefined;
|
|
1266
|
+
}
|
|
1267
|
+
/**
|
|
1268
|
+
* Default quality gate: tests + typecheck + security scan.
|
|
1269
|
+
* Lint and coverage are informational warnings, not blockers.
|
|
1270
|
+
*/
|
|
1271
|
+
declare const DEFAULT_QUALITY_CHECKS: QualityGateChecks;
|
|
1272
|
+
/**
|
|
1273
|
+
* ChangeManager orchestrates the full change lifecycle.
|
|
1274
|
+
*
|
|
1275
|
+
* ## Workflow
|
|
1276
|
+
* ```
|
|
1277
|
+
* propose() → knowledge graph (proposed)
|
|
1278
|
+
* → consensus.vote() (approved/rejected)
|
|
1279
|
+
* → apply() → knowledge graph (applied)
|
|
1280
|
+
* → verify() → on failure: proposeRollback()
|
|
1281
|
+
* ```
|
|
1282
|
+
*/
|
|
1283
|
+
declare class ChangeManager {
|
|
1284
|
+
private readonly graph;
|
|
1285
|
+
private readonly consensus;
|
|
1286
|
+
private readonly fleet?;
|
|
1287
|
+
private readonly checks;
|
|
1288
|
+
/** Track applied changes for rollback lookup. */
|
|
1289
|
+
private readonly appliedChanges;
|
|
1290
|
+
constructor(opts: ChangeManagerOptions);
|
|
1291
|
+
/**
|
|
1292
|
+
* Propose a new code change. Creates a ChangeNode in the knowledge graph.
|
|
1293
|
+
* Does NOT automatically initiate voting — call `submitForReview()` for that.
|
|
1294
|
+
*/
|
|
1295
|
+
propose(input: ChangeProposal): Promise<ChangeNode>;
|
|
1296
|
+
/**
|
|
1297
|
+
* Submit an approved change for application.
|
|
1298
|
+
* Returns the change node — actual file mutations are performed by agents
|
|
1299
|
+
* acting on this node's data from the knowledge graph.
|
|
1300
|
+
*/
|
|
1301
|
+
submitForReview(changeId: string): Promise<void>;
|
|
1302
|
+
/**
|
|
1303
|
+
* Apply an approved change. Updates the change node to 'applied'.
|
|
1304
|
+
* Agents should watch for 'applied' status and perform the actual file mutations.
|
|
1305
|
+
*/
|
|
1306
|
+
markApplied(changeId: string, appliedAt: string): Promise<ChangeNode | null>;
|
|
1307
|
+
/**
|
|
1308
|
+
* Mark a change as applied and trigger rollback for any satisfied goal
|
|
1309
|
+
* that turns out to be broken.
|
|
1310
|
+
*/
|
|
1311
|
+
markAppliedWithVerification(changeId: string, verify: () => Promise<QualityGateResult>): Promise<ApplyResult>;
|
|
1312
|
+
/**
|
|
1313
|
+
* Propose a rollback for an applied change. Creates a new change that
|
|
1314
|
+
* reverses the original. Goes through full consensus.
|
|
1315
|
+
*/
|
|
1316
|
+
proposeRollback(appliedChangeId: string, reason: string): Promise<ChangeNode | null>;
|
|
1317
|
+
/**
|
|
1318
|
+
* Mark a change as rolled back.
|
|
1319
|
+
*/
|
|
1320
|
+
markRolledBack(changeId: string, rolledBackAt: string): Promise<ChangeNode | null>;
|
|
1321
|
+
getPendingReviews(): ChangeNode[];
|
|
1322
|
+
getAppliedChanges(): ChangeNode[];
|
|
1323
|
+
getChange(id: string): ChangeNode | undefined;
|
|
1324
|
+
getChangesForGoal(goalId: string): ChangeNode[];
|
|
1325
|
+
/**
|
|
1326
|
+
* Run quality gate checks. This is informational — actual test/lint/typecheck
|
|
1327
|
+
* execution is done by agents spawned for this purpose. This method stores
|
|
1328
|
+
* the result in the change node.
|
|
1329
|
+
*/
|
|
1330
|
+
private _runQualityGate;
|
|
1331
|
+
/**
|
|
1332
|
+
* Update quality gate result for a change. Called by verify agents
|
|
1333
|
+
* after running their checks.
|
|
1334
|
+
*/
|
|
1335
|
+
updateQualityGate(changeId: string, checkName: string, result: {
|
|
1336
|
+
passed: boolean;
|
|
1337
|
+
detail?: string;
|
|
1338
|
+
}): Promise<void>;
|
|
1339
|
+
private _emit;
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
type AutonomousDecisionType = 'spawn' | 'approve_change' | 'reject_change' | 'prioritize_goals' | 'escalate_task' | 'rollback_change' | 'retry_task' | 'merge_results' | 'decompose_goal' | 'assign_task';
|
|
1343
|
+
interface AutonomousDecisionRequest {
|
|
1344
|
+
id: string;
|
|
1345
|
+
source: BrainDecisionSource;
|
|
1346
|
+
decisionType: AutonomousDecisionType;
|
|
1347
|
+
question: string;
|
|
1348
|
+
context: {
|
|
1349
|
+
/** Relevant facts from the knowledge graph */
|
|
1350
|
+
facts?: FactNode[];
|
|
1351
|
+
/** Goals relevant to this decision */
|
|
1352
|
+
goals?: GoalNode[];
|
|
1353
|
+
/** Change being reviewed (for approval decisions) */
|
|
1354
|
+
change?: ChangeNode;
|
|
1355
|
+
/** Current fleet status */
|
|
1356
|
+
fleetStatus?: {
|
|
1357
|
+
running: number;
|
|
1358
|
+
idle: number;
|
|
1359
|
+
total: number;
|
|
1360
|
+
costSoFar: number;
|
|
1361
|
+
};
|
|
1362
|
+
/** Task that triggered this decision */
|
|
1363
|
+
taskDescription?: string;
|
|
1364
|
+
/** Error that triggered escalation, if any */
|
|
1365
|
+
error?: string;
|
|
1366
|
+
/** Number of times this task has been attempted */
|
|
1367
|
+
attempts?: number;
|
|
1368
|
+
};
|
|
1369
|
+
options: BrainDecisionOption[];
|
|
1370
|
+
risk: BrainRisk;
|
|
1371
|
+
/** Whether this decision requires consensus */
|
|
1372
|
+
requiresConsensus: boolean;
|
|
1373
|
+
}
|
|
1374
|
+
interface SpawnDecision {
|
|
1375
|
+
role: string;
|
|
1376
|
+
budget: {
|
|
1377
|
+
timeoutMs?: number;
|
|
1378
|
+
maxIterations?: number;
|
|
1379
|
+
maxToolCalls?: number;
|
|
1380
|
+
maxCostUsd?: number;
|
|
1381
|
+
};
|
|
1382
|
+
rationale: string;
|
|
1383
|
+
}
|
|
1384
|
+
interface ApprovalDecision {
|
|
1385
|
+
approved: boolean;
|
|
1386
|
+
rationale: string;
|
|
1387
|
+
waivers?: string[];
|
|
1388
|
+
conditions?: string[];
|
|
1389
|
+
}
|
|
1390
|
+
interface PrioritizationDecision {
|
|
1391
|
+
orderedGoals: string[];
|
|
1392
|
+
rationale: string;
|
|
1393
|
+
}
|
|
1394
|
+
interface EscalationDecision {
|
|
1395
|
+
action: 'retry' | 'delegate' | 'mark_failed' | 'ask_for_help';
|
|
1396
|
+
rationale: string;
|
|
1397
|
+
budgetAdjustment?: {
|
|
1398
|
+
increaseFactor?: number;
|
|
1399
|
+
newTimeoutMs?: number;
|
|
1400
|
+
addModel?: string;
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
interface AutonomousBrainOptions {
|
|
1404
|
+
/** The LLM provider for making decisions */
|
|
1405
|
+
llmProvider: LLMProvider;
|
|
1406
|
+
graph: KnowledgeGraph;
|
|
1407
|
+
fleet?: FleetBus | undefined;
|
|
1408
|
+
/** Maximum retries before a task is marked failed. Default: 3. */
|
|
1409
|
+
maxRetries?: number | undefined;
|
|
1410
|
+
/** Risk threshold above which consensus is required. Default: 'high'. */
|
|
1411
|
+
consensusRiskThreshold?: BrainRisk | undefined;
|
|
1412
|
+
/** Self-improve: track decision history for learning. Default: true. */
|
|
1413
|
+
selfImprove?: boolean | undefined;
|
|
1414
|
+
}
|
|
1415
|
+
interface LLMProvider {
|
|
1416
|
+
/**
|
|
1417
|
+
* Generate a decision. Receives the full context as a structured prompt.
|
|
1418
|
+
* Returns the chosen option id and rationale.
|
|
1419
|
+
*/
|
|
1420
|
+
decide(prompt: DecisionPrompt): Promise<{
|
|
1421
|
+
optionId: string;
|
|
1422
|
+
rationale: string;
|
|
1423
|
+
}>;
|
|
1424
|
+
}
|
|
1425
|
+
interface DecisionPrompt {
|
|
1426
|
+
decisionType: AutonomousDecisionType;
|
|
1427
|
+
question: string;
|
|
1428
|
+
context: string;
|
|
1429
|
+
options: BrainDecisionOption[];
|
|
1430
|
+
risk: BrainRisk;
|
|
1431
|
+
decisionHistory: DecisionNode[];
|
|
1432
|
+
/** Hints derived from self-improvement data */
|
|
1433
|
+
selfImproveHints?: string[];
|
|
1434
|
+
}
|
|
1435
|
+
declare class AutonomousBrain implements BrainArbiter {
|
|
1436
|
+
private readonly graph;
|
|
1437
|
+
private readonly fleetBus?;
|
|
1438
|
+
private readonly llmProvider;
|
|
1439
|
+
private readonly maxRetries;
|
|
1440
|
+
private readonly consensusRiskThreshold;
|
|
1441
|
+
private readonly selfImprove;
|
|
1442
|
+
/** Decision history for self-improvement and audit. */
|
|
1443
|
+
private decisionHistory;
|
|
1444
|
+
/** Tracks failure patterns for self-improvement. */
|
|
1445
|
+
private failurePatterns;
|
|
1446
|
+
private readonly RISK_ORDER;
|
|
1447
|
+
private _emit;
|
|
1448
|
+
constructor(opts: AutonomousBrainOptions);
|
|
1449
|
+
/** Implements BrainArbiter — bridges standard brain.ts interface to autonomous engine. */
|
|
1450
|
+
decide(request: BrainDecisionRequest): Promise<BrainDecision>;
|
|
1451
|
+
/**
|
|
1452
|
+
* Primary autonomous decision engine — receives AutonomousDecisionRequest,
|
|
1453
|
+
* queries the LLM, records the decision, and returns a BrainDecision.
|
|
1454
|
+
*
|
|
1455
|
+
* Specialized methods (decideSpawn, decideApproval, etc.) should call this
|
|
1456
|
+
* directly with a pre-built AutonomousDecisionRequest.
|
|
1457
|
+
*/
|
|
1458
|
+
decideAuto(request: AutonomousDecisionRequest): Promise<BrainDecision>;
|
|
1459
|
+
/**
|
|
1460
|
+
* Decide whether to spawn a subagent, which role to use, and what budget.
|
|
1461
|
+
*/
|
|
1462
|
+
decideSpawn(source: BrainDecisionSource, taskDescription: string, availableFacts: FactNode[], fleetStatus: {
|
|
1463
|
+
running: number;
|
|
1464
|
+
idle: number;
|
|
1465
|
+
total: number;
|
|
1466
|
+
costSoFar: number;
|
|
1467
|
+
}): Promise<BrainDecision>;
|
|
1468
|
+
/**
|
|
1469
|
+
* Decide whether to approve a proposed change.
|
|
1470
|
+
*/
|
|
1471
|
+
decideApproval(source: BrainDecisionSource, change: ChangeNode, relevantFacts: FactNode[]): Promise<BrainDecision>;
|
|
1472
|
+
/**
|
|
1473
|
+
* Decide how to handle a failed task.
|
|
1474
|
+
*/
|
|
1475
|
+
decideEscalation(source: BrainDecisionSource, taskId: string, error: string, attempts: number): Promise<BrainDecision>;
|
|
1476
|
+
/**
|
|
1477
|
+
* Record the outcome of a decision for self-improvement.
|
|
1478
|
+
* Call this after a spawned agent completes or a change is applied.
|
|
1479
|
+
*/
|
|
1480
|
+
recordOutcome(decisionId: string, outcome: 'success' | 'failure', _detail?: string): void;
|
|
1481
|
+
private _getSelfImproveHints;
|
|
1482
|
+
private _toAutonomous;
|
|
1483
|
+
private _inferDecisionType;
|
|
1484
|
+
private _serializeContext;
|
|
1485
|
+
private _loadHistory;
|
|
1486
|
+
private _recordDecision;
|
|
1487
|
+
private _inferRoles;
|
|
1488
|
+
private _changeRisk;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
interface TaskBid {
|
|
1492
|
+
id: string;
|
|
1493
|
+
taskId: string;
|
|
1494
|
+
agentId: string;
|
|
1495
|
+
agentName: string;
|
|
1496
|
+
agentRole: string;
|
|
1497
|
+
/** Dispatcher score for this task */
|
|
1498
|
+
score: number;
|
|
1499
|
+
/** Why this agent is a good fit */
|
|
1500
|
+
rationale: string;
|
|
1501
|
+
submittedAt: string;
|
|
1502
|
+
}
|
|
1503
|
+
interface TaskAuctionOptions {
|
|
1504
|
+
graph: KnowledgeGraph;
|
|
1505
|
+
fleet?: FleetBus | undefined;
|
|
1506
|
+
mailbox?: Mailbox | undefined;
|
|
1507
|
+
selfAgentId?: string | undefined;
|
|
1508
|
+
/** How long a bid window stays open before auto-awarding. Default: 30s */
|
|
1509
|
+
bidWindowMs?: number | undefined;
|
|
1510
|
+
/** Maximum concurrent tasks per agent. Default: 3 */
|
|
1511
|
+
maxTasksPerAgent?: number | undefined;
|
|
1512
|
+
/** Minimum confidence threshold for dispatcher scoring. Default: 0.3 */
|
|
1513
|
+
minConfidence?: number | undefined;
|
|
1514
|
+
/**
|
|
1515
|
+
* Maximum times a task can be republished when no bids are received.
|
|
1516
|
+
* After this, the task is marked as 'failed' with reason 'no_bids'.
|
|
1517
|
+
* Default: 3.
|
|
1518
|
+
*/
|
|
1519
|
+
maxBidRetries?: number | undefined;
|
|
1520
|
+
}
|
|
1521
|
+
declare class TaskAuctioneer {
|
|
1522
|
+
private readonly graph;
|
|
1523
|
+
private readonly fleet?;
|
|
1524
|
+
private readonly mailbox?;
|
|
1525
|
+
private readonly selfAgentId;
|
|
1526
|
+
private readonly bidWindowMs;
|
|
1527
|
+
private readonly maxTasksPerAgent;
|
|
1528
|
+
private readonly minConfidence;
|
|
1529
|
+
private readonly maxBidRetries;
|
|
1530
|
+
/** Pending bids keyed by taskId. */
|
|
1531
|
+
private readonly pendingBids;
|
|
1532
|
+
/** Active bid windows keyed by taskId. */
|
|
1533
|
+
private readonly bidTimers;
|
|
1534
|
+
/** FleetBus subscription disposers, detached in dispose(). */
|
|
1535
|
+
private readonly unsubs;
|
|
1536
|
+
/** How many times a task has been republished with no bids received. */
|
|
1537
|
+
private readonly bidRetryCounts;
|
|
1538
|
+
/** Agent → current task count (from graph + in-flight). */
|
|
1539
|
+
private readonly agentTaskCounts;
|
|
1540
|
+
constructor(opts: TaskAuctionOptions);
|
|
1541
|
+
/**
|
|
1542
|
+
* Detach all FleetBus subscriptions and cancel any open bid-window timers.
|
|
1543
|
+
* Call when the owning coordinator stops/restarts so handlers and timers
|
|
1544
|
+
* don't accumulate across cycles.
|
|
1545
|
+
*/
|
|
1546
|
+
dispose(): void;
|
|
1547
|
+
/**
|
|
1548
|
+
* Publish a new task to the auction. Creates a GoalNode and broadcasts
|
|
1549
|
+
* it to all online agents. Returns the goal id.
|
|
1550
|
+
*
|
|
1551
|
+
* If `targetAgent` is specified, the task is assigned directly without auction.
|
|
1552
|
+
*/
|
|
1553
|
+
publishTask(input: {
|
|
1554
|
+
title: string;
|
|
1555
|
+
description: string;
|
|
1556
|
+
priority?: GoalPriority;
|
|
1557
|
+
tags?: string[];
|
|
1558
|
+
targetAgent?: string;
|
|
1559
|
+
parentGoal?: string;
|
|
1560
|
+
satisfiesGoals?: string[];
|
|
1561
|
+
/** Goal ids that must reach 'done' before this goal becomes workable. */
|
|
1562
|
+
blockedBy?: string[];
|
|
1563
|
+
deadline?: string;
|
|
1564
|
+
reward?: string;
|
|
1565
|
+
}): Promise<string>;
|
|
1566
|
+
/**
|
|
1567
|
+
* Submit a bid for a task. Called by agents who want to work on it.
|
|
1568
|
+
* Returns true if the bid was accepted, false if the task was already claimed.
|
|
1569
|
+
*/
|
|
1570
|
+
bid(taskId: string, agent: {
|
|
1571
|
+
agentId: string;
|
|
1572
|
+
agentName: string;
|
|
1573
|
+
agentRole: string;
|
|
1574
|
+
}, rationale: string): Promise<boolean>;
|
|
1575
|
+
/**
|
|
1576
|
+
* Award a task to a specific agent. Called internally by the bid window
|
|
1577
|
+
* expiry, or can be called directly to force an award.
|
|
1578
|
+
*/
|
|
1579
|
+
claim(taskId: string, agentId: string, agentName: string): Promise<boolean>;
|
|
1580
|
+
/**
|
|
1581
|
+
* Mark a task as done. Called by the agent when it finishes.
|
|
1582
|
+
*/
|
|
1583
|
+
complete(taskId: string, _result?: string): Promise<void>;
|
|
1584
|
+
/**
|
|
1585
|
+
* Mark a task as failed. Optionally spawn a retry.
|
|
1586
|
+
*/
|
|
1587
|
+
fail(taskId: string, error: string): Promise<void>;
|
|
1588
|
+
/**
|
|
1589
|
+
* Find the best available tasks for an agent based on its capabilities.
|
|
1590
|
+
* Returns tasks sorted by match score (best first).
|
|
1591
|
+
*/
|
|
1592
|
+
findWork(_agentId: string, agentRole: string, limit?: number): Promise<{
|
|
1593
|
+
task: GoalNode;
|
|
1594
|
+
score: number;
|
|
1595
|
+
bids: number;
|
|
1596
|
+
}[]>;
|
|
1597
|
+
/** Get all pending tasks (available for bidding). */
|
|
1598
|
+
getPendingTasks(): GoalNode[];
|
|
1599
|
+
/** Get tasks assigned to a specific agent. */
|
|
1600
|
+
getTasksForAgent(agentId: string): GoalNode[];
|
|
1601
|
+
/** Get the current bid count for a task. */
|
|
1602
|
+
getBidCount(taskId: string): number;
|
|
1603
|
+
/** Get bids for a task. */
|
|
1604
|
+
getBids(taskId: string): TaskBid[];
|
|
1605
|
+
/** Get task stats for a project-wide dashboard. */
|
|
1606
|
+
getStats(): {
|
|
1607
|
+
total: number;
|
|
1608
|
+
pending: number;
|
|
1609
|
+
in_progress: number;
|
|
1610
|
+
done: number;
|
|
1611
|
+
failed: number;
|
|
1612
|
+
totalBids: number;
|
|
1613
|
+
avgBidsPerTask: number;
|
|
1614
|
+
};
|
|
1615
|
+
private _emit;
|
|
1616
|
+
private _broadcastTask;
|
|
1617
|
+
private _mailboxPublish;
|
|
1618
|
+
private _notifyAgent;
|
|
1619
|
+
private _startBidWindow;
|
|
1620
|
+
private _cancelBidWindow;
|
|
1621
|
+
private _evaluateBids;
|
|
1622
|
+
private _assignDirect;
|
|
1623
|
+
private _onBidEvent;
|
|
1624
|
+
private _onClaimedEvent;
|
|
1625
|
+
private _getAgentTaskCount;
|
|
1626
|
+
private agentTaskCount;
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
/**
|
|
1630
|
+
* CoordinatorEvent — union of all event types emitted by the AutonomousCoordinator.
|
|
1631
|
+
* Consumed by the TUI to drive coordinator panel state and the reducer.
|
|
1632
|
+
*/
|
|
1633
|
+
type CoordinatorEvent = {
|
|
1634
|
+
type: 'goal:added';
|
|
1635
|
+
goalId: string;
|
|
1636
|
+
title?: string;
|
|
1637
|
+
text?: string;
|
|
1638
|
+
participants?: string[];
|
|
1639
|
+
} | {
|
|
1640
|
+
type: 'goal:completed';
|
|
1641
|
+
goalId: string;
|
|
1642
|
+
text?: string;
|
|
1643
|
+
participants?: string[];
|
|
1644
|
+
} | {
|
|
1645
|
+
type: 'goal:failed';
|
|
1646
|
+
goalId: string;
|
|
1647
|
+
text?: string;
|
|
1648
|
+
} | {
|
|
1649
|
+
type: 'task:ready';
|
|
1650
|
+
goalId: string;
|
|
1651
|
+
taskId: string;
|
|
1652
|
+
title?: string;
|
|
1653
|
+
assignedTo?: string;
|
|
1654
|
+
text?: string;
|
|
1655
|
+
} | {
|
|
1656
|
+
type: 'task:completed';
|
|
1657
|
+
goalId: string;
|
|
1658
|
+
taskId: string;
|
|
1659
|
+
text?: string;
|
|
1660
|
+
} | {
|
|
1661
|
+
type: 'knowledge:added';
|
|
1662
|
+
knowledgeId: string;
|
|
1663
|
+
title?: string;
|
|
1664
|
+
text?: string;
|
|
1665
|
+
} | {
|
|
1666
|
+
type: 'consensus:reached';
|
|
1667
|
+
goalId: string;
|
|
1668
|
+
text?: string;
|
|
1669
|
+
participants?: string[];
|
|
1670
|
+
} | {
|
|
1671
|
+
type: 'deadlock:detected';
|
|
1672
|
+
goalId: string;
|
|
1673
|
+
text?: string;
|
|
1674
|
+
} | {
|
|
1675
|
+
type: 'coordinator:mode';
|
|
1676
|
+
mode: 'standalone' | 'fleet';
|
|
1677
|
+
};
|
|
1678
|
+
|
|
1679
|
+
interface AutonomousCoordinatorOptions {
|
|
1680
|
+
sessionDir: string;
|
|
1681
|
+
selfAgentId: string;
|
|
1682
|
+
selfAgentName: string;
|
|
1683
|
+
fleet?: FleetBus | undefined;
|
|
1684
|
+
fleetManager?: FleetManager | undefined;
|
|
1685
|
+
director?: Director | undefined;
|
|
1686
|
+
mailbox?: Mailbox | undefined;
|
|
1687
|
+
events?: EventBus | undefined;
|
|
1688
|
+
llmProvider: LLMProvider;
|
|
1689
|
+
/** Disable self-improvement. Default: false. */
|
|
1690
|
+
disableSelfImprove?: boolean;
|
|
1691
|
+
/** Max concurrent subagents. Default: 5. */
|
|
1692
|
+
maxConcurrentAgents?: number;
|
|
1693
|
+
/**
|
|
1694
|
+
* Called with every CoordinatorEvent so the caller (e.g. execution.ts)
|
|
1695
|
+
* can forward it to the TUI coordinator panel timeline.
|
|
1696
|
+
*/
|
|
1697
|
+
onCoordinatorEvent?: (event: CoordinatorEvent) => void;
|
|
1698
|
+
}
|
|
1699
|
+
interface RunOptions {
|
|
1700
|
+
/** Top-level goal description. Default: "Improve the codebase". */
|
|
1701
|
+
goal?: string;
|
|
1702
|
+
/** If true, the loop runs until all goals are done (no timeout). Default: false. */
|
|
1703
|
+
runUntilComplete?: boolean;
|
|
1704
|
+
/** Max iterations. Default: 100. */
|
|
1705
|
+
maxIterations?: number;
|
|
1706
|
+
/** Stop if cost exceeds this. Default: no limit. */
|
|
1707
|
+
maxCostUsd?: number;
|
|
1708
|
+
}
|
|
1709
|
+
interface CoordinatorStats {
|
|
1710
|
+
goals: {
|
|
1711
|
+
total: number;
|
|
1712
|
+
done: number;
|
|
1713
|
+
pending: number;
|
|
1714
|
+
failed: number;
|
|
1715
|
+
progress: number;
|
|
1716
|
+
};
|
|
1717
|
+
dag: ReturnType<TaskDAG['stats']>;
|
|
1718
|
+
auction: ReturnType<TaskAuctioneer['getStats']>;
|
|
1719
|
+
changes: {
|
|
1720
|
+
proposed: number;
|
|
1721
|
+
approved: number;
|
|
1722
|
+
applied: number;
|
|
1723
|
+
rejected: number;
|
|
1724
|
+
};
|
|
1725
|
+
decisions: number;
|
|
1726
|
+
costSoFar?: number | undefined;
|
|
1727
|
+
}
|
|
1728
|
+
/**
|
|
1729
|
+
* AutonomousCoordinator — wires all coordination components into one engine.
|
|
1730
|
+
*
|
|
1731
|
+
* ## Quick start
|
|
1732
|
+
*
|
|
1733
|
+
* ```typescript
|
|
1734
|
+
* const coord = new AutonomousCoordinator({
|
|
1735
|
+
* sessionDir: '/tmp/session',
|
|
1736
|
+
* selfAgentId: 'director-1',
|
|
1737
|
+
* selfAgentName: 'Director',
|
|
1738
|
+
* llmProvider: myLLMProvider,
|
|
1739
|
+
* fleet: myFleetBus,
|
|
1740
|
+
* mailbox: myMailbox,
|
|
1741
|
+
* });
|
|
1742
|
+
*
|
|
1743
|
+
* // Run a self-directing session
|
|
1744
|
+
* await coord.run({ goal: 'Audit and fix all security issues in the auth module' });
|
|
1745
|
+
* ```
|
|
1746
|
+
*/
|
|
1747
|
+
declare class AutonomousCoordinator {
|
|
1748
|
+
readonly graph: KnowledgeGraph;
|
|
1749
|
+
readonly dag: TaskDAG;
|
|
1750
|
+
readonly auction: TaskAuctioneer;
|
|
1751
|
+
readonly consensus: ConsensusProtocol;
|
|
1752
|
+
readonly changes: ChangeManager;
|
|
1753
|
+
readonly brain: AutonomousBrain;
|
|
1754
|
+
private readonly selfAgentId;
|
|
1755
|
+
private readonly fleet?;
|
|
1756
|
+
private readonly fleetManager?;
|
|
1757
|
+
private readonly director?;
|
|
1758
|
+
private readonly mailbox?;
|
|
1759
|
+
private readonly events?;
|
|
1760
|
+
private readonly onCoordinatorEvent?;
|
|
1761
|
+
private running;
|
|
1762
|
+
private iterationCount;
|
|
1763
|
+
/** Tasks already handled by _onSubagentTerminated (to avoid double goal:failed on fleet event). */
|
|
1764
|
+
private readonly _handledBySubagent;
|
|
1765
|
+
/** FleetBus subscription disposers, detached in dispose(). */
|
|
1766
|
+
private readonly unsubs;
|
|
1767
|
+
constructor(opts: AutonomousCoordinatorOptions);
|
|
1768
|
+
/**
|
|
1769
|
+
* Run the autonomous loop until the goal is satisfied or max iterations reached.
|
|
1770
|
+
* This is the main entry point for a fully autonomous session.
|
|
1771
|
+
*/
|
|
1772
|
+
run(opts?: RunOptions): Promise<CoordinatorStats>;
|
|
1773
|
+
/** Stop the autonomous loop. */
|
|
1774
|
+
stop(): void;
|
|
1775
|
+
/**
|
|
1776
|
+
* Tear down the coordinator for good: stop the loop and detach all FleetBus
|
|
1777
|
+
* subscriptions (this coordinator's + the auctioneer's) plus any open bid
|
|
1778
|
+
* timers. Call this when discarding the instance (e.g. `/coordinator stop`
|
|
1779
|
+
* that recreates a fresh coordinator on the next start) so handlers and
|
|
1780
|
+
* timers don't accumulate across cycles. `stop()` only pauses the loop.
|
|
1781
|
+
*/
|
|
1782
|
+
dispose(): void;
|
|
1783
|
+
/** Get a stats snapshot. */
|
|
1784
|
+
getStats(): CoordinatorStats;
|
|
1785
|
+
/**
|
|
1786
|
+
* Publish a fact discovered by an agent. Facts are immutable and form
|
|
1787
|
+
* the basis for other agents' decisions.
|
|
1788
|
+
*/
|
|
1789
|
+
publishFact(input: {
|
|
1790
|
+
category: FactCategory;
|
|
1791
|
+
subject: string;
|
|
1792
|
+
detail: string;
|
|
1793
|
+
file?: string;
|
|
1794
|
+
line?: number;
|
|
1795
|
+
severity?: 'critical' | 'high' | 'medium' | 'low';
|
|
1796
|
+
tags?: string[];
|
|
1797
|
+
}): Promise<FactNode>;
|
|
1798
|
+
/**
|
|
1799
|
+
* Publish a goal and add it to the DAG.
|
|
1800
|
+
*/
|
|
1801
|
+
createGoal(input: {
|
|
1802
|
+
title: string;
|
|
1803
|
+
description: string;
|
|
1804
|
+
priority?: 'critical' | 'high' | 'medium' | 'low';
|
|
1805
|
+
deps?: string[];
|
|
1806
|
+
tags?: string[];
|
|
1807
|
+
}): Promise<GoalNode>;
|
|
1808
|
+
private _decomposeGoal;
|
|
1809
|
+
private _inferCategory;
|
|
1810
|
+
private _processGoal;
|
|
1811
|
+
private _handlePendingChange;
|
|
1812
|
+
private _onDagEvent;
|
|
1813
|
+
private _onSubagentTerminated;
|
|
1814
|
+
private _fleetStatus;
|
|
1815
|
+
private _buildVoters;
|
|
1816
|
+
private _goalToOptions;
|
|
1817
|
+
private _optionToGoal;
|
|
1818
|
+
private _dagPriorityToGoal;
|
|
1819
|
+
private _mailboxBroadcast;
|
|
1820
|
+
/** Emit a CoordinatorEvent to the subscriber (e.g. TUI panel timeline). */
|
|
1821
|
+
private _emit;
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
export { AgentDefinition, AgentHeartbeatInput, AgentRegistrationInput, type ApplyResult, type ApprovalDecision, AutonomousBrain, type AutonomousBrainOptions, AutonomousCoordinator, type AutonomousCoordinatorOptions, type AutonomousDecisionRequest, type AutonomousDecisionType, BUILD_AGENTS, BrainArbiter, BrainDecision, BrainDecisionOption, BrainDecisionRequest, BrainDecisionSource, type BrainInterventionInput, BrainMonitor, type BrainMonitorOptions, BrainRisk, type ChangeFile, ChangeManager, type ChangeManagerOptions, type ChangeNode, type ChangeProposal, type ChangeStatus, type ConsensusOptions, ConsensusProtocol, type ConsensusResult, type CoordinatorEvent, type CoordinatorStats, type DAGEdgeEvent, type DAGEdgeHandler, type DAGNode, type DAGNodeStatus, DEFAULT_QUALITY_CHECKS, DELIVERY_AGENTS, DEPENDENCY_FILE_PATTERNS, DISCOVERY_AGENTS, DOMAIN_AGENTS, type DecisionNode, type DecisionPrompt, DefaultMailbox, type DepWatchEntry, type DepWatcherBridgeOptions, type DependencyWatcherConfig, Director, type EscalationDecision, type FactCategory, type FactNode, FleetBus, FleetManager, GlobalMailbox, type GoalNode, type GoalPriority, type GoalStatus, type GraphSubscription, KNOWLEDGE_AGENTS, KnowledgeGraph, type LLMProvider, META_AGENTS, type MailToolsOptions, Mailbox, MailboxAckInput, MailboxAgentStatus, type MailboxHooksOptions, MailboxMessage, MailboxQuery, type MailboxResolver, MailboxSendInput, type MailboxToolOptions, type NodeFilter, type NodeType, type OutdatedNotifyMessage, PLANNING_AGENTS, type PackageAuthorEntry, type PackageAuthorLog, type PackageAuthorTrackerOptions, type PackageOutdatedEntry, type PackageOutdatedResult, type PackageOutdatedWatcherOptions, type PrioritizationDecision, type QualityCheck, type QualityGateChecks, type QualityGateResult, type QuorumRule, REVIEW_AGENTS, type RollbackResult, type RunOptions, type RunnablesHandler, type SpawnDecision, type TaskAuctionOptions, TaskAuctioneer, type TaskBid, TaskDAG, VERIFY_AGENTS, type VoteNode, type VoteRecord, type VoteValue, type VoterConfig, attachDepWatcherBridge, createMailboxHooks, detectEcosystem, getFullPackageLog, getManifestPackages, getPackageAuthor, getPackagesByAgent, mailboxSessionTag, makeDependencyWatcherConfig, makeMailInboxTool, makeMailSendTool, makeMailboxTool, recordPackageAction, resolveMailboxIdentity, resolveProjectDir, startPackageOutdatedWatcher, updatePackageOutdatedStatus };
|