@wrongstack/cli 0.295.0 → 0.295.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/auth-menu/auth-menu-audit.d.ts +1 -1
- package/dist/auth-menu/auth-menu-audit.d.ts.map +1 -1
- package/dist/auth-menu/local.d.ts +1 -1
- package/dist/auth-menu/local.d.ts.map +1 -1
- package/dist/auth-menu/shared.d.ts +1 -1
- package/dist/auth-menu/shared.d.ts.map +1 -1
- package/dist/boot/launch-menu.d.ts +1 -1
- package/dist/boot/launch-menu.d.ts.map +1 -1
- package/dist/boot/system-prompt-builder.d.ts +1 -1
- package/dist/boot/system-prompt-builder.d.ts.map +1 -1
- package/dist/boot/tui-coordinator-setup.d.ts +2 -2
- package/dist/boot/tui-coordinator-setup.d.ts.map +1 -1
- package/dist/boot/tui-project-picker-callback.d.ts +1 -1
- package/dist/boot/tui-project-picker-callback.d.ts.map +1 -1
- package/dist/boot/tui-project-switch.d.ts +2 -2
- package/dist/boot/tui-project-switch.d.ts.map +1 -1
- package/dist/boot/tui-settings-adapter.d.ts +2 -2
- package/dist/boot/tui-settings-adapter.d.ts.map +1 -1
- package/dist/boot-config.d.ts +2 -2
- package/dist/boot-config.d.ts.map +1 -1
- package/dist/boot.d.ts +1 -1
- package/dist/boot.d.ts.map +1 -1
- package/dist/cli-eternal-flag.d.ts +2 -2
- package/dist/cli-eternal-flag.d.ts.map +1 -1
- package/dist/cli-main.d.ts.map +1 -1
- package/dist/fleet/routing.d.ts +1 -1
- package/dist/fleet/routing.d.ts.map +1 -1
- package/dist/fleet/status-broadcast.d.ts.map +1 -1
- package/dist/goal-host.d.ts +2 -2
- package/dist/goal-host.d.ts.map +1 -1
- package/dist/hq-publisher.d.ts +2 -2
- package/dist/hq-publisher.d.ts.map +1 -1
- package/dist/hq-server/auth-state.d.ts +1 -1
- package/dist/hq-server/auth-state.d.ts.map +1 -1
- package/dist/hq-server/routes/system-handlers.d.ts +16 -0
- package/dist/hq-server/routes/system-handlers.d.ts.map +1 -0
- package/dist/hq-server/routes.d.ts +5 -5
- package/dist/hq-server/routes.d.ts.map +1 -1
- package/dist/hq-server/ws.d.ts +1 -1
- package/dist/hq-server/ws.d.ts.map +1 -1
- package/dist/index.js +558 -160
- package/dist/index.js.map +4 -4
- package/dist/input-reader.d.ts +1 -1
- package/dist/input-reader.d.ts.map +1 -1
- package/dist/kanban-hq-sync.d.ts.map +1 -1
- package/dist/mcp-serve.d.ts +1 -1
- package/dist/mcp-serve.d.ts.map +1 -1
- package/dist/next-task-predictor.d.ts +1 -1
- package/dist/next-task-predictor.d.ts.map +1 -1
- package/dist/plugin-management.d.ts +1 -1
- package/dist/plugin-management.d.ts.map +1 -1
- package/dist/repl.d.ts.map +1 -1
- package/dist/services/dispatch-classifier.d.ts +1 -1
- package/dist/services/dispatch-classifier.d.ts.map +1 -1
- package/dist/services/project-facts.d.ts.map +1 -1
- package/dist/services/sdd/task-manager.d.ts +1 -1
- package/dist/services/sdd/task-manager.d.ts.map +1 -1
- package/dist/session-event-wiring.d.ts +3 -0
- package/dist/session-event-wiring.d.ts.map +1 -1
- package/dist/settings-menu.d.ts +1 -1
- package/dist/settings-menu.d.ts.map +1 -1
- package/dist/slash-commands/agent-improve.d.ts +6 -5
- package/dist/slash-commands/agent-improve.d.ts.map +1 -1
- package/dist/slash-commands/brain.d.ts.map +1 -1
- package/dist/slash-commands/btw.d.ts +1 -1
- package/dist/slash-commands/btw.d.ts.map +1 -1
- package/dist/slash-commands/clear.d.ts +1 -1
- package/dist/slash-commands/clear.d.ts.map +1 -1
- package/dist/slash-commands/goal-refiner.d.ts +2 -2
- package/dist/slash-commands/goal-refiner.d.ts.map +1 -1
- package/dist/slash-commands/interrupt.d.ts +1 -1
- package/dist/slash-commands/interrupt.d.ts.map +1 -1
- package/dist/slash-commands/kanban.d.ts +1 -1
- package/dist/slash-commands/kanban.d.ts.map +1 -1
- package/dist/slash-commands/mailbox.d.ts +1 -1
- package/dist/slash-commands/mailbox.d.ts.map +1 -1
- package/dist/slash-commands/refiner.d.ts +1 -1
- package/dist/slash-commands/refiner.d.ts.map +1 -1
- package/dist/slash-commands/sdd.d.ts +1 -1
- package/dist/slash-commands/sdd.d.ts.map +1 -1
- package/dist/slash-commands/tool.d.ts +1 -1
- package/dist/slash-commands/tool.d.ts.map +1 -1
- package/dist/slash-commands/working-dir.d.ts +1 -1
- package/dist/slash-commands/working-dir.d.ts.map +1 -1
- package/dist/subcommands/handlers/chronicle.d.ts.map +1 -1
- package/dist/webui-server/connection-handler.d.ts +2 -1
- package/dist/webui-server/connection-handler.d.ts.map +1 -1
- package/dist/webui-server/kanban-host-adapter.d.ts +4 -4
- package/dist/webui-server/kanban-host-adapter.d.ts.map +1 -1
- package/dist/webui-server/prefs-seeding.d.ts.map +1 -1
- package/dist/webui-server/terminal-log-view.d.ts +30 -0
- package/dist/webui-server/terminal-log-view.d.ts.map +1 -0
- package/dist/webui-server.d.ts.map +1 -1
- package/dist/wiring/brain-and-orchestration.d.ts +2 -2
- package/dist/wiring/brain-and-orchestration.d.ts.map +1 -1
- package/dist/wiring/codebase-index.d.ts.map +1 -1
- package/dist/wiring/command-host-adapters.d.ts +2 -2
- package/dist/wiring/command-host-adapters.d.ts.map +1 -1
- package/dist/wiring/command-host-state.d.ts +3 -3
- package/dist/wiring/command-host-state.d.ts.map +1 -1
- package/dist/wiring/dep-watcher.d.ts +1 -1
- package/dist/wiring/dep-watcher.d.ts.map +1 -1
- package/dist/wiring/director-setup.d.ts +4 -4
- package/dist/wiring/director-setup.d.ts.map +1 -1
- package/dist/wiring/eternal-command-handlers.d.ts +5 -5
- package/dist/wiring/eternal-command-handlers.d.ts.map +1 -1
- package/dist/wiring/fleet-command-handlers.d.ts +2 -2
- package/dist/wiring/fleet-command-handlers.d.ts.map +1 -1
- package/dist/wiring/hq-telemetry.d.ts +4 -4
- package/dist/wiring/hq-telemetry.d.ts.map +1 -1
- package/dist/wiring/lifecycle-plugins.d.ts +5 -5
- package/dist/wiring/lifecycle-plugins.d.ts.map +1 -1
- package/dist/wiring/pipeline.d.ts +2 -2
- package/dist/wiring/pipeline.d.ts.map +1 -1
- package/dist/wiring/provider-runtime-setup.d.ts +5 -5
- package/dist/wiring/provider-runtime-setup.d.ts.map +1 -1
- package/dist/wiring/provider-status.d.ts +3 -3
- package/dist/wiring/provider-status.d.ts.map +1 -1
- package/dist/wiring/provider-utility-tools.d.ts +4 -4
- package/dist/wiring/provider-utility-tools.d.ts.map +1 -1
- package/dist/wiring/replay.d.ts +2 -2
- package/dist/wiring/replay.d.ts.map +1 -1
- package/dist/wiring/runtime-controller-deps.d.ts +3 -3
- package/dist/wiring/runtime-controller-deps.d.ts.map +1 -1
- package/dist/wiring/runtime-dispatch-state.d.ts +5 -5
- package/dist/wiring/runtime-dispatch-state.d.ts.map +1 -1
- package/dist/wiring/runtime-picker-deps.d.ts +3 -3
- package/dist/wiring/runtime-picker-deps.d.ts.map +1 -1
- package/dist/wiring/sdd-handlers.d.ts.map +1 -1
- package/dist/wiring/session-command-handlers.d.ts +5 -5
- package/dist/wiring/session-command-handlers.d.ts.map +1 -1
- package/dist/wiring/tools.d.ts +2 -2
- package/dist/wiring/tools.d.ts.map +1 -1
- package/package.json +21 -21
package/dist/index.js
CHANGED
|
@@ -1698,7 +1698,7 @@ function handleClient(ws, clients, browsers, eventLog, auth, snapshotBroadcaster
|
|
|
1698
1698
|
if (ws.readyState === WebSocket2.OPEN) {
|
|
1699
1699
|
ws.send(JSON.stringify({ type: "hq.kanban_snapshot", payload: payload2 }));
|
|
1700
1700
|
}
|
|
1701
|
-
});
|
|
1701
|
+
}).catch(() => void 0);
|
|
1702
1702
|
}
|
|
1703
1703
|
const event = {
|
|
1704
1704
|
id: randomUUID2(),
|
|
@@ -1814,7 +1814,16 @@ function handleClient(ws, clients, browsers, eventLog, auth, snapshotBroadcaster
|
|
|
1814
1814
|
const payload = event.payload;
|
|
1815
1815
|
if (payload.projectId !== client.projectId || persistence === void 0) return;
|
|
1816
1816
|
void persistence.kanban.merge(payload).then((merged) => {
|
|
1817
|
-
const
|
|
1817
|
+
const touched = /* @__PURE__ */ new Set();
|
|
1818
|
+
for (const record of payload.boards) touched.add(record.boardId);
|
|
1819
|
+
for (const record of payload.tombstones) touched.add(record.boardId);
|
|
1820
|
+
const delta = {
|
|
1821
|
+
projectId: merged.projectId,
|
|
1822
|
+
generatedAt: merged.generatedAt,
|
|
1823
|
+
boards: merged.boards.filter((record) => touched.has(record.boardId)),
|
|
1824
|
+
tombstones: merged.tombstones.filter((record) => touched.has(record.boardId))
|
|
1825
|
+
};
|
|
1826
|
+
const message = JSON.stringify({ type: "hq.kanban_snapshot", payload: delta });
|
|
1818
1827
|
for (const peer of clients.values()) {
|
|
1819
1828
|
if (peer.projectId === client.projectId && peer.ws.readyState === WebSocket2.OPEN) {
|
|
1820
1829
|
peer.ws.send(message);
|
|
@@ -2123,6 +2132,98 @@ var init_trust_boundary = __esm({
|
|
|
2123
2132
|
}
|
|
2124
2133
|
});
|
|
2125
2134
|
|
|
2135
|
+
// src/hq-server/routes/system-handlers.ts
|
|
2136
|
+
async function handleApiSystemUpdate(res) {
|
|
2137
|
+
const [{ checkForUpdate: checkForUpdate2 }, { detectUpdatePackageName: detectUpdatePackageName2 }] = await Promise.all([
|
|
2138
|
+
Promise.resolve().then(() => (init_update_check(), update_check_exports)),
|
|
2139
|
+
Promise.resolve().then(() => (init_update(), update_exports))
|
|
2140
|
+
]);
|
|
2141
|
+
const packageName = detectUpdatePackageName2();
|
|
2142
|
+
const info = await checkForUpdate2({ packageName });
|
|
2143
|
+
res.writeHead(200, {
|
|
2144
|
+
"Content-Type": "application/json",
|
|
2145
|
+
"Cache-Control": "no-store"
|
|
2146
|
+
});
|
|
2147
|
+
res.end(
|
|
2148
|
+
JSON.stringify({
|
|
2149
|
+
...info,
|
|
2150
|
+
packageName,
|
|
2151
|
+
command: "wstack update"
|
|
2152
|
+
})
|
|
2153
|
+
);
|
|
2154
|
+
}
|
|
2155
|
+
async function handleApiSystemHealth(res, clients, persistence, eventLog) {
|
|
2156
|
+
const now = Date.now();
|
|
2157
|
+
const eventCount = eventLog.length;
|
|
2158
|
+
let timeseriesStoreStatus = "unknown";
|
|
2159
|
+
let kanbanStoreStatus = "unknown";
|
|
2160
|
+
try {
|
|
2161
|
+
const tsOk = await persistence.timeseries.read(now - 3e5);
|
|
2162
|
+
timeseriesStoreStatus = Array.isArray(tsOk) ? "ok" : "degraded";
|
|
2163
|
+
} catch {
|
|
2164
|
+
timeseriesStoreStatus = "degraded";
|
|
2165
|
+
}
|
|
2166
|
+
try {
|
|
2167
|
+
await persistence.kanban.load("__hq_health_probe__");
|
|
2168
|
+
kanbanStoreStatus = "ok";
|
|
2169
|
+
} catch {
|
|
2170
|
+
kanbanStoreStatus = "degraded";
|
|
2171
|
+
}
|
|
2172
|
+
const inMemoryEventLogActive = true;
|
|
2173
|
+
const allOk = inMemoryEventLogActive && timeseriesStoreStatus === "ok" && kanbanStoreStatus === "ok";
|
|
2174
|
+
const clientCount = clients.size;
|
|
2175
|
+
const connectedClients = [...clients.values()].filter(
|
|
2176
|
+
(c) => Date.now() - new Date(c.lastSeenAt).getTime() < 6e4
|
|
2177
|
+
).length;
|
|
2178
|
+
res.writeHead(200, {
|
|
2179
|
+
"Content-Type": "application/json",
|
|
2180
|
+
"Cache-Control": "no-store"
|
|
2181
|
+
});
|
|
2182
|
+
res.end(
|
|
2183
|
+
JSON.stringify({
|
|
2184
|
+
status: allOk ? "healthy" : "degraded",
|
|
2185
|
+
uptime: {
|
|
2186
|
+
serverTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2187
|
+
eventLogSize: eventCount
|
|
2188
|
+
},
|
|
2189
|
+
stores: {
|
|
2190
|
+
events: "ok",
|
|
2191
|
+
timeseries: timeseriesStoreStatus,
|
|
2192
|
+
kanban: kanbanStoreStatus
|
|
2193
|
+
},
|
|
2194
|
+
connections: {
|
|
2195
|
+
total: clientCount,
|
|
2196
|
+
active: connectedClients,
|
|
2197
|
+
stale: clientCount - connectedClients
|
|
2198
|
+
}
|
|
2199
|
+
})
|
|
2200
|
+
);
|
|
2201
|
+
}
|
|
2202
|
+
async function handleApiCommandsAudit(req2, res, auditLog) {
|
|
2203
|
+
const url = new URL(req2.url ?? "/", "http://localhost");
|
|
2204
|
+
const rawLimit = Number.parseInt(url.searchParams.get("limit") ?? "200", 10);
|
|
2205
|
+
const limit = Math.min(1e3, Math.max(1, Number.isFinite(rawLimit) ? rawLimit : 200));
|
|
2206
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2207
|
+
res.end(JSON.stringify({ commands: auditLog.recent(limit) }));
|
|
2208
|
+
}
|
|
2209
|
+
async function handleApiAlerts(req2, res, alertEngine) {
|
|
2210
|
+
const url = new URL(req2.url ?? "/", "http://localhost");
|
|
2211
|
+
const rawLimit = Number.parseInt(url.searchParams.get("limit") ?? "100", 10);
|
|
2212
|
+
const limit = Math.min(500, Math.max(1, Number.isFinite(rawLimit) ? rawLimit : 100));
|
|
2213
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2214
|
+
res.end(
|
|
2215
|
+
JSON.stringify({
|
|
2216
|
+
active: alertEngine.activeAlerts(),
|
|
2217
|
+
history: alertEngine.recentAlerts(limit)
|
|
2218
|
+
})
|
|
2219
|
+
);
|
|
2220
|
+
}
|
|
2221
|
+
var init_system_handlers = __esm({
|
|
2222
|
+
"src/hq-server/routes/system-handlers.ts"() {
|
|
2223
|
+
"use strict";
|
|
2224
|
+
}
|
|
2225
|
+
});
|
|
2226
|
+
|
|
2126
2227
|
// src/hq-server/routes.ts
|
|
2127
2228
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
2128
2229
|
import * as path18 from "node:path";
|
|
@@ -2142,6 +2243,7 @@ function createHqRouter(deps) {
|
|
|
2142
2243
|
loginAttempts,
|
|
2143
2244
|
clients,
|
|
2144
2245
|
browsers,
|
|
2246
|
+
eventLog,
|
|
2145
2247
|
transcripts,
|
|
2146
2248
|
agentMessages,
|
|
2147
2249
|
alertEngine,
|
|
@@ -2217,6 +2319,10 @@ function createHqRouter(deps) {
|
|
|
2217
2319
|
await handleApiSystemUpdate(res);
|
|
2218
2320
|
return;
|
|
2219
2321
|
}
|
|
2322
|
+
if (url.pathname === "/api/system/health" && req2.method === "GET") {
|
|
2323
|
+
await handleApiSystemHealth(res, clients, persistence, eventLog);
|
|
2324
|
+
return;
|
|
2325
|
+
}
|
|
2220
2326
|
const mailboxGatewayMatch = url.pathname.match(
|
|
2221
2327
|
/^\/api\/projects\/([^/]+)\/mailbox(?:\/(.*))?$/
|
|
2222
2328
|
);
|
|
@@ -2536,25 +2642,6 @@ async function handleApiPassword(req2, res, mutableAuth, sessions, dataDir, secu
|
|
|
2536
2642
|
})
|
|
2537
2643
|
);
|
|
2538
2644
|
}
|
|
2539
|
-
async function handleApiSystemUpdate(res) {
|
|
2540
|
-
const [{ checkForUpdate: checkForUpdate2 }, { detectUpdatePackageName: detectUpdatePackageName2 }] = await Promise.all([
|
|
2541
|
-
Promise.resolve().then(() => (init_update_check(), update_check_exports)),
|
|
2542
|
-
Promise.resolve().then(() => (init_update(), update_exports))
|
|
2543
|
-
]);
|
|
2544
|
-
const packageName = detectUpdatePackageName2();
|
|
2545
|
-
const info = await checkForUpdate2({ packageName });
|
|
2546
|
-
res.writeHead(200, {
|
|
2547
|
-
"Content-Type": "application/json",
|
|
2548
|
-
"Cache-Control": "no-store"
|
|
2549
|
-
});
|
|
2550
|
-
res.end(
|
|
2551
|
-
JSON.stringify({
|
|
2552
|
-
...info,
|
|
2553
|
-
packageName,
|
|
2554
|
-
command: "wstack update"
|
|
2555
|
-
})
|
|
2556
|
-
);
|
|
2557
|
-
}
|
|
2558
2645
|
async function handleMailboxGateway(_req, res, match, authorizeMailboxGateway, getMailboxGateway, dataDir) {
|
|
2559
2646
|
const projectId = decodePathSegment2(match[1]);
|
|
2560
2647
|
if (projectId === null || projectId.length === 0) {
|
|
@@ -2906,25 +2993,6 @@ async function handleMailboxAction(_req, res, match, _mutableAuth, _sessions, da
|
|
|
2906
2993
|
);
|
|
2907
2994
|
}
|
|
2908
2995
|
}
|
|
2909
|
-
async function handleApiCommandsAudit(req2, res, auditLog) {
|
|
2910
|
-
const url = new URL(req2.url ?? "/", "http://localhost");
|
|
2911
|
-
const rawLimit = Number.parseInt(url.searchParams.get("limit") ?? "200", 10);
|
|
2912
|
-
const limit = Math.min(1e3, Math.max(1, Number.isFinite(rawLimit) ? rawLimit : 200));
|
|
2913
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2914
|
-
res.end(JSON.stringify({ commands: auditLog.recent(limit) }));
|
|
2915
|
-
}
|
|
2916
|
-
async function handleApiAlerts(req2, res, alertEngine) {
|
|
2917
|
-
const url = new URL(req2.url ?? "/", "http://localhost");
|
|
2918
|
-
const rawLimit = Number.parseInt(url.searchParams.get("limit") ?? "100", 10);
|
|
2919
|
-
const limit = Math.min(500, Math.max(1, Number.isFinite(rawLimit) ? rawLimit : 100));
|
|
2920
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2921
|
-
res.end(
|
|
2922
|
-
JSON.stringify({
|
|
2923
|
-
active: alertEngine.activeAlerts(),
|
|
2924
|
-
history: alertEngine.recentAlerts(limit)
|
|
2925
|
-
})
|
|
2926
|
-
);
|
|
2927
|
-
}
|
|
2928
2996
|
async function handleApiSessions(res) {
|
|
2929
2997
|
const { SessionRegistry } = await import("@wrongstack/core/storage");
|
|
2930
2998
|
const globalRoot = path18.dirname(resolveHqDataDir2());
|
|
@@ -3125,6 +3193,7 @@ var init_routes = __esm({
|
|
|
3125
3193
|
init_snapshot();
|
|
3126
3194
|
init_utils2();
|
|
3127
3195
|
init_trust_boundary();
|
|
3196
|
+
init_system_handlers();
|
|
3128
3197
|
setHqSecurityHeaders2 = setHqSecurityHeaders;
|
|
3129
3198
|
hasTrustedBrowserOrigin2 = hasTrustedBrowserOrigin;
|
|
3130
3199
|
authenticateBrowserRequest2 = authenticateBrowserRequest;
|
|
@@ -3401,10 +3470,10 @@ import * as http from "node:http";
|
|
|
3401
3470
|
import * as path20 from "node:path";
|
|
3402
3471
|
import { WebSocket as WebSocket3, WebSocketServer as WebSocketServer2 } from "ws";
|
|
3403
3472
|
import { createCompatibilityTrustBoundary } from "@wrongstack/core/security";
|
|
3404
|
-
import { createHqPersistence
|
|
3473
|
+
import { createHqPersistence, toAlertMessage, watchHqAuthFile } from "@wrongstack/core/hq";
|
|
3405
3474
|
import { createMailboxHttpRouter, MAILBOX_HTTP_DEFAULT_MAX_AGE_MS } from "@wrongstack/core/coordination";
|
|
3406
3475
|
import { HqAlertEngine, HqCommandAuditLog } from "@wrongstack/core/hq";
|
|
3407
|
-
import { GlobalMailbox
|
|
3476
|
+
import { GlobalMailbox, MailboxEventEmitter, MailboxHttpRateLimiter } from "@wrongstack/core/coordination";
|
|
3408
3477
|
import { HqInsecureExposureError as HqInsecureExposureError2 } from "@wrongstack/core/hq";
|
|
3409
3478
|
async function startHqServer(options = {}) {
|
|
3410
3479
|
const { host, port, dataDir, firstRunAuth } = await prepareHqServerStart(options, {
|
|
@@ -3443,7 +3512,7 @@ function startHqServerWithAuth(options, host, port, dataDir, firstRunAuth) {
|
|
|
3443
3512
|
const transcripts = /* @__PURE__ */ new Map();
|
|
3444
3513
|
const agentMessages = /* @__PURE__ */ new Map();
|
|
3445
3514
|
const mailboxGateways = /* @__PURE__ */ new Map();
|
|
3446
|
-
const mailboxGatewayRateLimiter = new
|
|
3515
|
+
const mailboxGatewayRateLimiter = new MailboxHttpRateLimiter();
|
|
3447
3516
|
const mailboxGatewayRateLimitCleanup = setInterval(
|
|
3448
3517
|
() => mailboxGatewayRateLimiter.cleanup(),
|
|
3449
3518
|
12e4
|
|
@@ -3464,15 +3533,15 @@ function startHqServerWithAuth(options, host, port, dataDir, firstRunAuth) {
|
|
|
3464
3533
|
}
|
|
3465
3534
|
}, SESSION_CLEANUP_INTERVAL_MS);
|
|
3466
3535
|
sessionCleanupTimer.unref?.();
|
|
3467
|
-
const persistence =
|
|
3536
|
+
const persistence = createHqPersistence(dataDir);
|
|
3468
3537
|
const auditLog = new HqCommandAuditLog(1e3, (entry) => persistence.commandLog.append(entry));
|
|
3469
3538
|
void (async () => {
|
|
3470
3539
|
const projectsDir = path20.join(path20.dirname(dataDir), "projects");
|
|
3471
3540
|
const entries = await fs20.readdir(projectsDir, { withFileTypes: true }).catch(() => []);
|
|
3472
|
-
const { GlobalMailbox:
|
|
3541
|
+
const { GlobalMailbox: GlobalMailbox10 } = await import("@wrongstack/core/coordination");
|
|
3473
3542
|
await Promise.all(
|
|
3474
3543
|
entries.filter((entry) => entry.isDirectory()).map(async (entry) => {
|
|
3475
|
-
const mailbox = new
|
|
3544
|
+
const mailbox = new GlobalMailbox10(path20.join(projectsDir, entry.name));
|
|
3476
3545
|
await Promise.all([mailbox.getAgentStatuses(), mailbox.purgeClients()]);
|
|
3477
3546
|
})
|
|
3478
3547
|
);
|
|
@@ -3499,7 +3568,7 @@ function startHqServerWithAuth(options, host, port, dataDir, firstRunAuth) {
|
|
|
3499
3568
|
const existing = mailboxGateways.get(projectDir);
|
|
3500
3569
|
if (existing) return existing;
|
|
3501
3570
|
const eventEmitter = new MailboxEventEmitter();
|
|
3502
|
-
const mailbox = new
|
|
3571
|
+
const mailbox = new GlobalMailbox(projectDir, void 0, void 0, eventEmitter);
|
|
3503
3572
|
const router = createMailboxHttpRouter({
|
|
3504
3573
|
mailbox,
|
|
3505
3574
|
eventEmitter,
|
|
@@ -3526,8 +3595,8 @@ function startHqServerWithAuth(options, host, port, dataDir, firstRunAuth) {
|
|
|
3526
3595
|
},
|
|
3527
3596
|
onPersist: (alert) => persistence.alertLog.append(alert)
|
|
3528
3597
|
});
|
|
3529
|
-
void persistence.eventLog.hydrate();
|
|
3530
|
-
void persistence.timeseries.load();
|
|
3598
|
+
void persistence.eventLog.hydrate().catch(() => void 0);
|
|
3599
|
+
void persistence.timeseries.load().catch(() => void 0);
|
|
3531
3600
|
persistence.eventLog.recent(MAX_EVENT_LOG2).then((prior) => {
|
|
3532
3601
|
for (let i = prior.length - 1; i >= 0; i--) eventLog.push(prior[i]);
|
|
3533
3602
|
}).catch(() => {
|
|
@@ -3850,6 +3919,12 @@ import {
|
|
|
3850
3919
|
readBoard,
|
|
3851
3920
|
writeBoard
|
|
3852
3921
|
} from "@wrongstack/kanban";
|
|
3922
|
+
function warnSyncFailure(error) {
|
|
3923
|
+
process.emitWarning(
|
|
3924
|
+
`WrongStack kanban HQ sync failed (best-effort, will retry on next change): ${error instanceof Error ? error.message : String(error)}`,
|
|
3925
|
+
{ code: "WRONGSTACK_HQ_KANBAN_SYNC_FAILED" }
|
|
3926
|
+
);
|
|
3927
|
+
}
|
|
3853
3928
|
function createKanbanHqSync(projectRoot, projectId = deriveHqProjectId2(projectRoot)) {
|
|
3854
3929
|
const dir = getKanbanDir(projectRoot);
|
|
3855
3930
|
const statePath = path21.join(dir, SYNC_STATE_FILE);
|
|
@@ -3861,6 +3936,8 @@ function createKanbanHqSync(projectRoot, projectId = deriveHqProjectId2(projectR
|
|
|
3861
3936
|
let applyingRemote = false;
|
|
3862
3937
|
let stopped = false;
|
|
3863
3938
|
let operationChain = Promise.resolve();
|
|
3939
|
+
let pendingRemote = null;
|
|
3940
|
+
let remoteApplyQueued = false;
|
|
3864
3941
|
const runExclusive = (operation) => {
|
|
3865
3942
|
let resolveResult;
|
|
3866
3943
|
let rejectResult;
|
|
@@ -3877,21 +3954,28 @@ function createKanbanHqSync(projectRoot, projectId = deriveHqProjectId2(projectR
|
|
|
3877
3954
|
});
|
|
3878
3955
|
return result;
|
|
3879
3956
|
};
|
|
3880
|
-
const
|
|
3881
|
-
if (stopped ||
|
|
3882
|
-
if (boardId === null) fullRescanPending = true;
|
|
3883
|
-
else pendingBoardIds.add(boardId);
|
|
3884
|
-
if (timer !== void 0) clearTimeout(timer);
|
|
3957
|
+
const scheduleNext = () => {
|
|
3958
|
+
if (stopped || timer !== void 0 || applyingRemote) return;
|
|
3885
3959
|
timer = setTimeout(() => {
|
|
3886
3960
|
timer = void 0;
|
|
3887
3961
|
const rescan = fullRescanPending;
|
|
3888
3962
|
const dirtyBoardIds = [...pendingBoardIds];
|
|
3889
3963
|
fullRescanPending = false;
|
|
3890
3964
|
pendingBoardIds.clear();
|
|
3891
|
-
|
|
3965
|
+
runExclusive(() => publishLocal(false, rescan ? void 0 : dirtyBoardIds)).catch(
|
|
3966
|
+
warnSyncFailure
|
|
3967
|
+
);
|
|
3892
3968
|
}, 100);
|
|
3893
3969
|
timer.unref?.();
|
|
3894
3970
|
};
|
|
3971
|
+
const schedulePublish = (boardId) => {
|
|
3972
|
+
if (stopped || applyingRemote || publisher === void 0) return;
|
|
3973
|
+
if (boardId === null) fullRescanPending = true;
|
|
3974
|
+
else pendingBoardIds.add(boardId);
|
|
3975
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
3976
|
+
timer = void 0;
|
|
3977
|
+
scheduleNext();
|
|
3978
|
+
};
|
|
3895
3979
|
const publishLocal = async (fullSnapshot, dirtyBoardIds) => {
|
|
3896
3980
|
const target = publisher;
|
|
3897
3981
|
if (target === void 0 || stopped) return;
|
|
@@ -3901,7 +3985,15 @@ function createKanbanHqSync(projectRoot, projectId = deriveHqProjectId2(projectR
|
|
|
3901
3985
|
const boards = [];
|
|
3902
3986
|
const tombstones = [];
|
|
3903
3987
|
const fullScan = fullSnapshot || dirtyBoardIds === void 0;
|
|
3904
|
-
const
|
|
3988
|
+
const allBoardIds = fullScan ? await listBoardIds(projectRoot) : dirtyBoardIds;
|
|
3989
|
+
let boardIds = allBoardIds;
|
|
3990
|
+
if (!fullScan && boardIds.length > MAX_BOARDS_PER_PUBLISH) {
|
|
3991
|
+
for (const id of boardIds.slice(MAX_BOARDS_PER_PUBLISH)) {
|
|
3992
|
+
pendingBoardIds.add(id);
|
|
3993
|
+
}
|
|
3994
|
+
scheduleNext();
|
|
3995
|
+
boardIds = boardIds.slice(0, MAX_BOARDS_PER_PUBLISH);
|
|
3996
|
+
}
|
|
3905
3997
|
const present = /* @__PURE__ */ new Set();
|
|
3906
3998
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3907
3999
|
for (const boardId of boardIds) {
|
|
@@ -3978,6 +4070,7 @@ function createKanbanHqSync(projectRoot, projectId = deriveHqProjectId2(projectR
|
|
|
3978
4070
|
schedulePublish(null);
|
|
3979
4071
|
return;
|
|
3980
4072
|
}
|
|
4073
|
+
if (filename.length > MAX_BOARD_ID_LENGTH + 5) return;
|
|
3981
4074
|
if (filename === SYNC_STATE_FILE || filename.endsWith(".events.jsonl")) return;
|
|
3982
4075
|
if (!filename.endsWith(".json")) return;
|
|
3983
4076
|
const boardId = filename.slice(0, -".json".length);
|
|
@@ -4035,14 +4128,27 @@ function createKanbanHqSync(projectRoot, projectId = deriveHqProjectId2(projectR
|
|
|
4035
4128
|
applyingRemote = false;
|
|
4036
4129
|
}
|
|
4037
4130
|
};
|
|
4131
|
+
const handleRemote = (snapshot) => {
|
|
4132
|
+
if (snapshot.projectId !== projectId || stopped) return Promise.resolve();
|
|
4133
|
+
pendingRemote = pendingRemote === null ? snapshot : coalesceRemoteSnapshots(pendingRemote, snapshot);
|
|
4134
|
+
if (remoteApplyQueued) return Promise.resolve();
|
|
4135
|
+
remoteApplyQueued = true;
|
|
4136
|
+
return runExclusive(async () => {
|
|
4137
|
+
remoteApplyQueued = false;
|
|
4138
|
+
const batch = pendingRemote;
|
|
4139
|
+
pendingRemote = null;
|
|
4140
|
+
if (batch !== null) await applyRemote(batch);
|
|
4141
|
+
}).catch(warnSyncFailure);
|
|
4142
|
+
};
|
|
4038
4143
|
return {
|
|
4039
4144
|
attachPublisher,
|
|
4040
|
-
handleRemote
|
|
4145
|
+
handleRemote,
|
|
4041
4146
|
stop: () => {
|
|
4042
4147
|
stopped = true;
|
|
4043
4148
|
if (timer !== void 0) clearTimeout(timer);
|
|
4044
4149
|
pendingBoardIds.clear();
|
|
4045
4150
|
fullRescanPending = false;
|
|
4151
|
+
pendingRemote = null;
|
|
4046
4152
|
watcher?.close();
|
|
4047
4153
|
watcher = void 0;
|
|
4048
4154
|
publisher = void 0;
|
|
@@ -4106,6 +4212,37 @@ function chunkSnapshotPayload(projectId, generatedAt, boards, tombstones) {
|
|
|
4106
4212
|
pushCurrent();
|
|
4107
4213
|
return chunks.length > 0 ? chunks : [emptyPayload()];
|
|
4108
4214
|
}
|
|
4215
|
+
function coalesceRemoteSnapshots(older, newer) {
|
|
4216
|
+
const byBoardId = /* @__PURE__ */ new Map();
|
|
4217
|
+
const put = (candidate, boardId) => {
|
|
4218
|
+
const existing = byBoardId.get(boardId);
|
|
4219
|
+
if (existing === void 0 || compareVersions(existing.revision, existing.time, candidate.revision, candidate.time) <= 0) {
|
|
4220
|
+
byBoardId.set(boardId, candidate);
|
|
4221
|
+
}
|
|
4222
|
+
};
|
|
4223
|
+
for (const snapshot of [older, newer]) {
|
|
4224
|
+
for (const record of snapshot.boards) {
|
|
4225
|
+
put({ kind: "board", revision: record.revision, time: record.updatedAt, record }, record.boardId);
|
|
4226
|
+
}
|
|
4227
|
+
for (const record of snapshot.tombstones) {
|
|
4228
|
+
put(
|
|
4229
|
+
{ kind: "tombstone", revision: record.revision, time: record.deletedAt, record },
|
|
4230
|
+
record.boardId
|
|
4231
|
+
);
|
|
4232
|
+
}
|
|
4233
|
+
}
|
|
4234
|
+
const merged = {
|
|
4235
|
+
projectId: newer.projectId,
|
|
4236
|
+
generatedAt: newer.generatedAt,
|
|
4237
|
+
boards: [],
|
|
4238
|
+
tombstones: []
|
|
4239
|
+
};
|
|
4240
|
+
for (const entry of byBoardId.values()) {
|
|
4241
|
+
if (entry.kind === "board") merged.boards.push(entry.record);
|
|
4242
|
+
else merged.tombstones.push(entry.record);
|
|
4243
|
+
}
|
|
4244
|
+
return merged;
|
|
4245
|
+
}
|
|
4109
4246
|
function compareVersions(aRevision, aTime, bRevision, bTime) {
|
|
4110
4247
|
if (aRevision !== bRevision) return aRevision - bRevision;
|
|
4111
4248
|
return aTime.localeCompare(bTime);
|
|
@@ -4139,13 +4276,15 @@ async function writeState(filePath, state) {
|
|
|
4139
4276
|
mode: 384
|
|
4140
4277
|
});
|
|
4141
4278
|
}
|
|
4142
|
-
var SYNC_STATE_FILE, MAX_KANBAN_SNAPSHOT_PAYLOAD_BYTES, KANBAN_TOMBSTONE_RETENTION_MS;
|
|
4279
|
+
var SYNC_STATE_FILE, MAX_KANBAN_SNAPSHOT_PAYLOAD_BYTES, KANBAN_TOMBSTONE_RETENTION_MS, MAX_BOARDS_PER_PUBLISH, MAX_BOARD_ID_LENGTH;
|
|
4143
4280
|
var init_kanban_hq_sync = __esm({
|
|
4144
4281
|
"src/kanban-hq-sync.ts"() {
|
|
4145
4282
|
"use strict";
|
|
4146
4283
|
SYNC_STATE_FILE = ".hq-sync.json";
|
|
4147
4284
|
MAX_KANBAN_SNAPSHOT_PAYLOAD_BYTES = 512 * 1024;
|
|
4148
4285
|
KANBAN_TOMBSTONE_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
4286
|
+
MAX_BOARDS_PER_PUBLISH = 50;
|
|
4287
|
+
MAX_BOARD_ID_LENGTH = 128;
|
|
4149
4288
|
}
|
|
4150
4289
|
});
|
|
4151
4290
|
|
|
@@ -4202,7 +4341,14 @@ function startCliHqConnection(options) {
|
|
|
4202
4341
|
kanbanSync = createKanbanHqSync(options.projectRoot, next.project.projectId);
|
|
4203
4342
|
}
|
|
4204
4343
|
next.connect();
|
|
4205
|
-
if (kanbanSync !== void 0)
|
|
4344
|
+
if (kanbanSync !== void 0) {
|
|
4345
|
+
kanbanSync.attachPublisher(next).catch((error) => {
|
|
4346
|
+
process.emitWarning(
|
|
4347
|
+
`WrongStack kanban HQ sync attach failed (best-effort, retried on reconnect): ${error instanceof Error ? error.message : String(error)}`,
|
|
4348
|
+
{ code: "WRONGSTACK_HQ_KANBAN_SYNC_FAILED" }
|
|
4349
|
+
);
|
|
4350
|
+
});
|
|
4351
|
+
}
|
|
4206
4352
|
options.onConnect?.(next);
|
|
4207
4353
|
};
|
|
4208
4354
|
tryConnect();
|
|
@@ -5089,7 +5235,7 @@ import {
|
|
|
5089
5235
|
createConnectionLifecycle,
|
|
5090
5236
|
verifyClient as verifyWsClient
|
|
5091
5237
|
} from "@wrongstack/webui-server";
|
|
5092
|
-
import { decodeProtocolFrame } from "@wrongstack/webui-server/protocol";
|
|
5238
|
+
import { buildReplayPayload, decodeProtocolFrame } from "@wrongstack/webui-server/protocol";
|
|
5093
5239
|
function createConnectionHandler(deps) {
|
|
5094
5240
|
const clientHandlers = [
|
|
5095
5241
|
deps.goalHandler,
|
|
@@ -5157,13 +5303,7 @@ function createConnectionHandler(deps) {
|
|
|
5157
5303
|
const payload = { ...await deps.buildSessionStartPayload({}, deps.needsSetup) };
|
|
5158
5304
|
try {
|
|
5159
5305
|
const replay = await deps.loadReplay?.();
|
|
5160
|
-
if (replay
|
|
5161
|
-
payload["replayMessages"] = replay.messages.length > REPLAY_MESSAGE_CAP ? replay.messages.slice(-REPLAY_MESSAGE_CAP) : replay.messages;
|
|
5162
|
-
}
|
|
5163
|
-
const usage3 = replay?.usage;
|
|
5164
|
-
if (usage3 && usage3.input + usage3.output + (usage3.cacheRead ?? 0) + (usage3.cacheWrite ?? 0) > 0) {
|
|
5165
|
-
payload["replayUsage"] = usage3;
|
|
5166
|
-
}
|
|
5306
|
+
if (replay) Object.assign(payload, buildReplayPayload(replay));
|
|
5167
5307
|
} catch {
|
|
5168
5308
|
console.debug("[WebUI] Failed to load replay");
|
|
5169
5309
|
}
|
|
@@ -5177,11 +5317,9 @@ function createConnectionHandler(deps) {
|
|
|
5177
5317
|
}
|
|
5178
5318
|
});
|
|
5179
5319
|
}
|
|
5180
|
-
var REPLAY_MESSAGE_CAP;
|
|
5181
5320
|
var init_connection_handler = __esm({
|
|
5182
5321
|
"src/webui-server/connection-handler.ts"() {
|
|
5183
5322
|
"use strict";
|
|
5184
|
-
REPLAY_MESSAGE_CAP = 2e3;
|
|
5185
5323
|
}
|
|
5186
5324
|
});
|
|
5187
5325
|
|
|
@@ -5421,8 +5559,7 @@ async function seedConfigToMeta(opts) {
|
|
|
5421
5559
|
return;
|
|
5422
5560
|
}
|
|
5423
5561
|
}
|
|
5424
|
-
|
|
5425
|
-
seedContextMeta(config, { meta });
|
|
5562
|
+
seedContextMeta(config, { meta: opts.agent.ctx.meta });
|
|
5426
5563
|
}
|
|
5427
5564
|
function createPrefsSeeding(opts) {
|
|
5428
5565
|
const writeLock = { lock: Promise.resolve() };
|
|
@@ -5709,6 +5846,76 @@ var init_stream_coalescer = __esm({
|
|
|
5709
5846
|
}
|
|
5710
5847
|
});
|
|
5711
5848
|
|
|
5849
|
+
// src/webui-server/terminal-log-view.ts
|
|
5850
|
+
import { format } from "node:util";
|
|
5851
|
+
function startBoundedTerminalLogView(options = {}) {
|
|
5852
|
+
const isTTY = options.isTTY ?? process.stdout.isTTY === true;
|
|
5853
|
+
if (!isTTY) {
|
|
5854
|
+
return { enabled: false, redraw: () => {
|
|
5855
|
+
}, stop: () => {
|
|
5856
|
+
} };
|
|
5857
|
+
}
|
|
5858
|
+
const maxLines = Math.max(1, Math.trunc(options.maxLines ?? DEFAULT_MAX_LINES));
|
|
5859
|
+
const refreshMs = Math.max(100, Math.trunc(options.refreshMs ?? DEFAULT_REFRESH_MS));
|
|
5860
|
+
const target = options.consoleTarget ?? console;
|
|
5861
|
+
const write = options.write ?? ((chunk) => process.stdout.write(chunk));
|
|
5862
|
+
const setIntervalFn = options.setIntervalFn ?? setInterval;
|
|
5863
|
+
const clearIntervalFn = options.clearIntervalFn ?? clearInterval;
|
|
5864
|
+
const lines = [];
|
|
5865
|
+
let stopped = false;
|
|
5866
|
+
const originals = {
|
|
5867
|
+
log: target.log,
|
|
5868
|
+
info: target.info,
|
|
5869
|
+
warn: target.warn,
|
|
5870
|
+
error: target.error,
|
|
5871
|
+
debug: target.debug
|
|
5872
|
+
};
|
|
5873
|
+
const wrappers = {};
|
|
5874
|
+
const remember = (args) => {
|
|
5875
|
+
for (const line of format(...args).split(/\r?\n/u)) {
|
|
5876
|
+
lines.push(line);
|
|
5877
|
+
if (lines.length > maxLines) lines.shift();
|
|
5878
|
+
}
|
|
5879
|
+
};
|
|
5880
|
+
for (const level of Object.keys(originals)) {
|
|
5881
|
+
const original = originals[level];
|
|
5882
|
+
const wrapper = (...args) => {
|
|
5883
|
+
remember(args);
|
|
5884
|
+
original.apply(target, args);
|
|
5885
|
+
};
|
|
5886
|
+
wrappers[level] = wrapper;
|
|
5887
|
+
target[level] = wrapper;
|
|
5888
|
+
}
|
|
5889
|
+
const redraw = () => {
|
|
5890
|
+
if (stopped || lines.length === 0) return;
|
|
5891
|
+
write(`${CLEAR_VISIBLE_TERMINAL}${lines.join("\n")}
|
|
5892
|
+
`);
|
|
5893
|
+
};
|
|
5894
|
+
const timer = setIntervalFn(redraw, refreshMs);
|
|
5895
|
+
timer.unref?.();
|
|
5896
|
+
return {
|
|
5897
|
+
enabled: true,
|
|
5898
|
+
redraw,
|
|
5899
|
+
stop: () => {
|
|
5900
|
+
if (stopped) return;
|
|
5901
|
+
stopped = true;
|
|
5902
|
+
clearIntervalFn(timer);
|
|
5903
|
+
for (const level of Object.keys(originals)) {
|
|
5904
|
+
if (target[level] === wrappers[level]) target[level] = originals[level];
|
|
5905
|
+
}
|
|
5906
|
+
}
|
|
5907
|
+
};
|
|
5908
|
+
}
|
|
5909
|
+
var DEFAULT_MAX_LINES, DEFAULT_REFRESH_MS, CLEAR_VISIBLE_TERMINAL;
|
|
5910
|
+
var init_terminal_log_view = __esm({
|
|
5911
|
+
"src/webui-server/terminal-log-view.ts"() {
|
|
5912
|
+
"use strict";
|
|
5913
|
+
DEFAULT_MAX_LINES = 5;
|
|
5914
|
+
DEFAULT_REFRESH_MS = 5e3;
|
|
5915
|
+
CLEAR_VISIBLE_TERMINAL = "\x1B[2J\x1B[H";
|
|
5916
|
+
}
|
|
5917
|
+
});
|
|
5918
|
+
|
|
5712
5919
|
// src/webui-server.ts
|
|
5713
5920
|
var webui_server_exports = {};
|
|
5714
5921
|
__export(webui_server_exports, {
|
|
@@ -6316,6 +6523,7 @@ async function runWebUI(opts) {
|
|
|
6316
6523
|
terminalHandler,
|
|
6317
6524
|
kanbanHostRoutes
|
|
6318
6525
|
});
|
|
6526
|
+
const terminalLogView = startBoundedTerminalLogView();
|
|
6319
6527
|
const stopped = new Promise((resolve14) => {
|
|
6320
6528
|
let listeningAnnounced = false;
|
|
6321
6529
|
const announceListening = () => {
|
|
@@ -6359,7 +6567,7 @@ async function runWebUI(opts) {
|
|
|
6359
6567
|
await activeSession.flush().catch(() => void 0);
|
|
6360
6568
|
if (opts.sessionStore) {
|
|
6361
6569
|
const data = await opts.sessionStore.load(activeSession.id);
|
|
6362
|
-
return { messages: data.messages, usage: data.usage };
|
|
6570
|
+
return { messages: data.messages, events: data.events, usage: data.usage };
|
|
6363
6571
|
}
|
|
6364
6572
|
const usage3 = opts.agent.ctx.tokenCounter.total();
|
|
6365
6573
|
return { messages: opts.agent.ctx.messages, usage: usage3 };
|
|
@@ -6405,7 +6613,11 @@ async function runWebUI(opts) {
|
|
|
6405
6613
|
wss,
|
|
6406
6614
|
pid: process.pid,
|
|
6407
6615
|
registryBaseDir,
|
|
6408
|
-
onStopped:
|
|
6616
|
+
onStopped: () => {
|
|
6617
|
+
terminalLogView.redraw();
|
|
6618
|
+
terminalLogView.stop();
|
|
6619
|
+
resolve14();
|
|
6620
|
+
}
|
|
6409
6621
|
});
|
|
6410
6622
|
registerWebuiSignalHandlers(signalShutdown);
|
|
6411
6623
|
});
|
|
@@ -6416,6 +6628,8 @@ async function runWebUI(opts) {
|
|
|
6416
6628
|
}
|
|
6417
6629
|
function shutdown() {
|
|
6418
6630
|
console.log("[WebUI] Shutting down...");
|
|
6631
|
+
terminalLogView.redraw();
|
|
6632
|
+
terminalLogView.stop();
|
|
6419
6633
|
credentialWatcherClose?.();
|
|
6420
6634
|
flushAllStreamBuffers();
|
|
6421
6635
|
worktreeHandler.dispose();
|
|
@@ -6441,7 +6655,7 @@ async function runWebUI(opts) {
|
|
|
6441
6655
|
function sendResult(ws, success, message) {
|
|
6442
6656
|
send(ws, { type: "key.operation_result", payload: { success, message } });
|
|
6443
6657
|
}
|
|
6444
|
-
return stopped;
|
|
6658
|
+
return stopped.finally(() => terminalLogView.stop());
|
|
6445
6659
|
}
|
|
6446
6660
|
var init_webui_server = __esm({
|
|
6447
6661
|
"src/webui-server.ts"() {
|
|
@@ -6459,6 +6673,7 @@ var init_webui_server = __esm({
|
|
|
6459
6673
|
init_setup_events();
|
|
6460
6674
|
init_static_serve();
|
|
6461
6675
|
init_stream_coalescer();
|
|
6676
|
+
init_terminal_log_view();
|
|
6462
6677
|
}
|
|
6463
6678
|
});
|
|
6464
6679
|
|
|
@@ -7842,9 +8057,9 @@ var init_plugins = __esm({
|
|
|
7842
8057
|
|
|
7843
8058
|
// src/cli-main.ts
|
|
7844
8059
|
import { execFile as execFile3 } from "node:child_process";
|
|
7845
|
-
import * as
|
|
8060
|
+
import * as fs55 from "node:fs/promises";
|
|
7846
8061
|
import * as path71 from "node:path";
|
|
7847
|
-
import { attachDepWatcherBridge, GlobalMailbox as
|
|
8062
|
+
import { attachDepWatcherBridge, GlobalMailbox as GlobalMailbox9, mailboxSessionTag as mailboxSessionTag3 } from "@wrongstack/core/coordination";
|
|
7848
8063
|
import { createFallbackManageTools, createPluginManagerTool } from "@wrongstack/core/tools";
|
|
7849
8064
|
import { FLEET_ROSTER as FLEET_ROSTER4 } from "@wrongstack/core/coordination";
|
|
7850
8065
|
import { gatedEnhancerReasoning } from "@wrongstack/core/execution";
|
|
@@ -9559,10 +9774,10 @@ ${color6.dim(` ${CODEX_LEGACY_NOTE}`)}
|
|
|
9559
9774
|
async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => process.env.HOME ?? os2.homedir()) {
|
|
9560
9775
|
try {
|
|
9561
9776
|
const { atomicWrite: atomicWrite22 } = await import("@wrongstack/core/utils");
|
|
9562
|
-
const
|
|
9777
|
+
const fs56 = await import("node:fs/promises");
|
|
9563
9778
|
let existing = {};
|
|
9564
9779
|
try {
|
|
9565
|
-
const raw = await
|
|
9780
|
+
const raw = await fs56.readFile(configPath2, "utf8");
|
|
9566
9781
|
existing = JSON.parse(raw);
|
|
9567
9782
|
} catch {
|
|
9568
9783
|
}
|
|
@@ -13190,6 +13405,7 @@ import { color as color14, toErrorMessage as toErrorMessage4 } from "@wrongstack
|
|
|
13190
13405
|
// src/services/project-facts.ts
|
|
13191
13406
|
import * as fs8 from "node:fs/promises";
|
|
13192
13407
|
import * as path6 from "node:path";
|
|
13408
|
+
import { DEFAULT_WALK_IGNORE_DIRS } from "@wrongstack/core/utils";
|
|
13193
13409
|
import {
|
|
13194
13410
|
detectLanguageWorkspaces,
|
|
13195
13411
|
languageProfileRegistry,
|
|
@@ -13307,26 +13523,14 @@ var EXT_LANG = {
|
|
|
13307
13523
|
".svelte": "Svelte"
|
|
13308
13524
|
};
|
|
13309
13525
|
var SCAN_IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
13310
|
-
|
|
13311
|
-
"node_modules",
|
|
13312
|
-
"dist",
|
|
13313
|
-
"build",
|
|
13526
|
+
...DEFAULT_WALK_IGNORE_DIRS,
|
|
13314
13527
|
"out",
|
|
13315
|
-
"target",
|
|
13316
13528
|
"vendor",
|
|
13317
|
-
".venv",
|
|
13318
|
-
"venv",
|
|
13319
13529
|
"env",
|
|
13320
|
-
"__pycache__",
|
|
13321
|
-
".next",
|
|
13322
|
-
".nuxt",
|
|
13323
|
-
".cache",
|
|
13324
|
-
"coverage",
|
|
13325
13530
|
".wrongstack",
|
|
13326
13531
|
".idea",
|
|
13327
13532
|
".vscode",
|
|
13328
13533
|
"obj",
|
|
13329
|
-
".gradle",
|
|
13330
13534
|
".dart_tool",
|
|
13331
13535
|
"Pods"
|
|
13332
13536
|
]);
|
|
@@ -15331,14 +15535,14 @@ var auditCmd = async (args, deps) => {
|
|
|
15331
15535
|
return verify.ok ? 0 : 1;
|
|
15332
15536
|
};
|
|
15333
15537
|
async function listAudits(log, dir, deps) {
|
|
15334
|
-
const
|
|
15538
|
+
const fs56 = await import("node:fs/promises");
|
|
15335
15539
|
const path72 = await import("node:path");
|
|
15336
15540
|
const out = [];
|
|
15337
15541
|
let foundRoot = true;
|
|
15338
15542
|
const scan = async (scanDir, prefix, depth) => {
|
|
15339
15543
|
let entries;
|
|
15340
15544
|
try {
|
|
15341
|
-
entries = await
|
|
15545
|
+
entries = await fs56.readdir(scanDir, { withFileTypes: true });
|
|
15342
15546
|
} catch {
|
|
15343
15547
|
if (depth === 0) foundRoot = false;
|
|
15344
15548
|
return;
|
|
@@ -16140,7 +16344,7 @@ async function benchList(_args, deps) {
|
|
|
16140
16344
|
}
|
|
16141
16345
|
|
|
16142
16346
|
// src/subcommands/handlers/chronicle.ts
|
|
16143
|
-
import { ChronicleJournal, ChronicleQueryEngine, resolveChronicleRuntimeLocation } from "@wrongstack/core/chronicle";
|
|
16347
|
+
import { ChronicleJournal, ChronicleMetricsStore, ChronicleQueryEngine, resolveChronicleRuntimeLocation } from "@wrongstack/core/chronicle";
|
|
16144
16348
|
import { resolveWstackPaths as resolveWstackPaths5 } from "@wrongstack/core/utils";
|
|
16145
16349
|
import { statSync } from "node:fs";
|
|
16146
16350
|
import * as path13 from "node:path";
|
|
@@ -16214,6 +16418,40 @@ var chronicleCmd = async (args, deps) => {
|
|
|
16214
16418
|
}
|
|
16215
16419
|
return result2.errors.length > 0 ? 1 : 0;
|
|
16216
16420
|
}
|
|
16421
|
+
if (operation === "metrics") {
|
|
16422
|
+
const hasView = Boolean(args[1] && !args[1].includes("="));
|
|
16423
|
+
const view = hasView ? args[1] : "summary";
|
|
16424
|
+
const filters = parseQuery(args.slice(hasView ? 2 : 1));
|
|
16425
|
+
let store;
|
|
16426
|
+
try {
|
|
16427
|
+
store = ChronicleMetricsStore.open(path13.join(paths.projectDir, "chronicle"));
|
|
16428
|
+
} catch (error) {
|
|
16429
|
+
deps.renderer.writeError(`${error instanceof Error ? error.message : String(error)}
|
|
16430
|
+
`);
|
|
16431
|
+
return 1;
|
|
16432
|
+
}
|
|
16433
|
+
try {
|
|
16434
|
+
const refreshed = await store.refresh();
|
|
16435
|
+
const payload = view === "providers" ? store.providerDaily({ ...filters.from ? { from: filters.from } : {}, ...filters.to ? { to: filters.to } : {} }) : view === "tasks" ? store.taskOutcomes({
|
|
16436
|
+
...filters.sessionId ? { sessionId: filters.sessionId } : {},
|
|
16437
|
+
...filters.limit ? { limit: filters.limit } : {}
|
|
16438
|
+
}) : view === "files" ? store.fileLineage({
|
|
16439
|
+
...filters.path ? { path: filters.path } : {},
|
|
16440
|
+
...filters.taskId ? { taskId: filters.taskId } : {},
|
|
16441
|
+
...filters.sessionId ? { sessionId: filters.sessionId } : {},
|
|
16442
|
+
...filters.limit ? { limit: filters.limit } : {}
|
|
16443
|
+
}) : view === "summary" ? store.summary() : void 0;
|
|
16444
|
+
if (payload === void 0) {
|
|
16445
|
+
deps.renderer.writeError(`Unknown metrics view: ${view}. Use providers|tasks|files|summary.
|
|
16446
|
+
`);
|
|
16447
|
+
return 1;
|
|
16448
|
+
}
|
|
16449
|
+
deps.renderer.write(JSON.stringify({ refreshed, [view]: payload }, null, 2) + "\n");
|
|
16450
|
+
return 0;
|
|
16451
|
+
} finally {
|
|
16452
|
+
store.close();
|
|
16453
|
+
}
|
|
16454
|
+
}
|
|
16217
16455
|
if (operation === "facet") {
|
|
16218
16456
|
const engine2 = await ChronicleQueryEngine.fromDirectory(path13.join(paths.projectDir, "chronicle"));
|
|
16219
16457
|
const field = args[1];
|
|
@@ -16247,9 +16485,15 @@ function parseQuery(tokens) {
|
|
|
16247
16485
|
else if (key === "resourceKind") query.resourceKind = value;
|
|
16248
16486
|
else if (key === "line" || key === "limit") query[key] = Number(value);
|
|
16249
16487
|
else if (key === "order" && (value === "asc" || value === "desc")) query.order = value;
|
|
16250
|
-
else if (key.startsWith("tag."))
|
|
16251
|
-
|
|
16252
|
-
|
|
16488
|
+
else if (key.startsWith("tag.")) {
|
|
16489
|
+
const tags = query.tags ?? {};
|
|
16490
|
+
tags[key.slice(4)] = value;
|
|
16491
|
+
query.tags = tags;
|
|
16492
|
+
} else if (key.startsWith("attr.")) {
|
|
16493
|
+
const attributes = query.attributes ?? {};
|
|
16494
|
+
attributes[key.slice(5)] = parseValue(value);
|
|
16495
|
+
query.attributes = attributes;
|
|
16496
|
+
} else if (isStringQueryKey(key)) query[key] = value;
|
|
16253
16497
|
}
|
|
16254
16498
|
return query;
|
|
16255
16499
|
}
|
|
@@ -16281,7 +16525,7 @@ function parseValue(value) {
|
|
|
16281
16525
|
}
|
|
16282
16526
|
}
|
|
16283
16527
|
function usage() {
|
|
16284
|
-
return "Usage:\n wstack chronicle query [field=value ...]\n wstack chronicle facet <field> [field=value ...]\n wstack chronicle prune [--days N] [--dry-run]\n\nExamples:\n wstack chronicle query path=src/app.ts line=42\n wstack chronicle query providerId=openai outcome=failure\n wstack chronicle query eventType=tool.executed sessionId=<id> limit=50\n wstack chronicle query attr.tool.name=read\n wstack chronicle facet modelId from=2026-07-01T00:00:00Z\n wstack chronicle prune --days 7 --dry-run\n wstack chronicle prune --days 30\n";
|
|
16528
|
+
return "Usage:\n wstack chronicle query [field=value ...]\n wstack chronicle facet <field> [field=value ...]\n wstack chronicle metrics [providers|tasks|files|summary] [field=value ...]\n wstack chronicle prune [--days N] [--dry-run]\n\nExamples:\n wstack chronicle query path=src/app.ts line=42\n wstack chronicle query providerId=openai outcome=failure\n wstack chronicle query eventType=tool.executed sessionId=<id> limit=50\n wstack chronicle query attr.tool.name=read\n wstack chronicle facet modelId from=2026-07-01T00:00:00Z\n wstack chronicle metrics providers from=2026-07-01\n wstack chronicle metrics files path=src/app.ts\n wstack chronicle metrics tasks limit=20\n wstack chronicle prune --days 7 --dry-run\n wstack chronicle prune --days 30\n";
|
|
16285
16529
|
}
|
|
16286
16530
|
function formatBytes(bytes) {
|
|
16287
16531
|
if (bytes < 1024) return `${bytes} B`;
|
|
@@ -16469,7 +16713,7 @@ var exportCmd = async (args, deps) => {
|
|
|
16469
16713
|
deps.renderer.writeError("No session store configured.");
|
|
16470
16714
|
return 1;
|
|
16471
16715
|
}
|
|
16472
|
-
let
|
|
16716
|
+
let format2 = "markdown";
|
|
16473
16717
|
let output;
|
|
16474
16718
|
let includeTools = true;
|
|
16475
16719
|
let includeDiagnostics = true;
|
|
@@ -16482,7 +16726,7 @@ var exportCmd = async (args, deps) => {
|
|
|
16482
16726
|
deps.renderer.writeError(`Unknown --format ${v}. Use markdown, json, or text.`);
|
|
16483
16727
|
return 1;
|
|
16484
16728
|
}
|
|
16485
|
-
|
|
16729
|
+
format2 = v;
|
|
16486
16730
|
} else if (a === "--out" || a === "-o") output = args[++i];
|
|
16487
16731
|
else if (a === "--no-tools") includeTools = false;
|
|
16488
16732
|
else if (a === "--no-diagnostics") includeDiagnostics = false;
|
|
@@ -16500,7 +16744,7 @@ var exportCmd = async (args, deps) => {
|
|
|
16500
16744
|
const reader = new DefaultSessionReader({ store: deps.sessionStore });
|
|
16501
16745
|
let rendered;
|
|
16502
16746
|
try {
|
|
16503
|
-
rendered = await reader.export(sessionId, { format, includeTools, includeDiagnostics });
|
|
16747
|
+
rendered = await reader.export(sessionId, { format: format2, includeTools, includeDiagnostics });
|
|
16504
16748
|
} catch (err) {
|
|
16505
16749
|
deps.renderer.writeError(`Export failed: ${toErrorMessage7(err)}`);
|
|
16506
16750
|
return 1;
|
|
@@ -16890,7 +17134,7 @@ function resolveTokenTtl(args, deps) {
|
|
|
16890
17134
|
if (inline) {
|
|
16891
17135
|
return parseTokenTtlValue(inline.slice("--ttl=".length));
|
|
16892
17136
|
}
|
|
16893
|
-
const idx = args.
|
|
17137
|
+
const idx = args.indexOf("--ttl");
|
|
16894
17138
|
if (idx >= 0) {
|
|
16895
17139
|
const raw = args[idx + 1];
|
|
16896
17140
|
if (raw === void 0 || raw.startsWith("-")) {
|
|
@@ -17081,7 +17325,7 @@ var initCmd = async (_args, deps) => {
|
|
|
17081
17325
|
init_hq_publisher();
|
|
17082
17326
|
import { createServer as createServer3 } from "node:http";
|
|
17083
17327
|
import * as path22 from "node:path";
|
|
17084
|
-
import { authorizeMailboxBearerToken, GlobalMailbox as
|
|
17328
|
+
import { authorizeMailboxBearerToken, GlobalMailbox as GlobalMailbox2, MailboxEventEmitter as MailboxEventEmitter2, MailboxHttpRateLimiter as MailboxHttpRateLimiter2, resolveProjectDir as resolveProjectDir2 } from "@wrongstack/core/coordination";
|
|
17085
17329
|
import { createMailboxHttpRouter as createMailboxHttpRouter2, MAILBOX_HTTP_DEFAULT_MAX_AGE_MS as MAILBOX_HTTP_DEFAULT_MAX_AGE_MS2 } from "@wrongstack/core/coordination";
|
|
17086
17330
|
import { wstackGlobalRoot } from "@wrongstack/core/utils";
|
|
17087
17331
|
import { acquireOrJoin, finalize, release as release2 } from "@wrongstack/core/coordination";
|
|
@@ -17148,13 +17392,13 @@ or stop the conflicting process and retry.
|
|
|
17148
17392
|
const tentative = acquireResult.lock;
|
|
17149
17393
|
const eventEmitter = new MailboxEventEmitter2();
|
|
17150
17394
|
let hqConnection;
|
|
17151
|
-
const mailbox = new
|
|
17395
|
+
const mailbox = new GlobalMailbox2(
|
|
17152
17396
|
projectDir,
|
|
17153
17397
|
void 0,
|
|
17154
17398
|
() => hqConnection?.getPublisher(),
|
|
17155
17399
|
eventEmitter
|
|
17156
17400
|
);
|
|
17157
|
-
const rateLimiter = new
|
|
17401
|
+
const rateLimiter = new MailboxHttpRateLimiter2();
|
|
17158
17402
|
const rateLimitCleanup = setInterval(() => rateLimiter.cleanup(), 12e4);
|
|
17159
17403
|
rateLimitCleanup.unref?.();
|
|
17160
17404
|
const router = createMailboxHttpRouter2({
|
|
@@ -23779,7 +24023,7 @@ import * as path46 from "node:path";
|
|
|
23779
24023
|
import { toErrorMessage as toErrorMessage13 } from "@wrongstack/core/utils";
|
|
23780
24024
|
import { color as color51, estimateRequestTokensCalibrated, expectDefined as expectDefined11, hasOpenTodos, wstackGlobalRoot as wstackGlobalRoot4 } from "@wrongstack/core/utils";
|
|
23781
24025
|
import { detectContinueIntent, InputBuilder, resolveContinuation } from "@wrongstack/core/agent";
|
|
23782
|
-
import { GlobalMailbox as
|
|
24026
|
+
import { GlobalMailbox as GlobalMailbox3, resolveProjectDir as resolveProjectDir3 } from "@wrongstack/core/coordination";
|
|
23783
24027
|
import { goalFilePath, loadGoal, summarizeUsage } from "@wrongstack/core/goal";
|
|
23784
24028
|
import { readClipboardImage, routeImagesForModel } from "@wrongstack/runtime";
|
|
23785
24029
|
import { parseNextSteps } from "@wrongstack/tools/next-steps";
|
|
@@ -23812,7 +24056,8 @@ function clearSuggestions() {
|
|
|
23812
24056
|
|
|
23813
24057
|
// src/repl.ts
|
|
23814
24058
|
import {
|
|
23815
|
-
createAutoProceedLoopGuard
|
|
24059
|
+
createAutoProceedLoopGuard,
|
|
24060
|
+
GROUNDED_NO_PROGRESS_STEER
|
|
23816
24061
|
} from "@wrongstack/tools/auto-proceed-loop-guard";
|
|
23817
24062
|
var DEFAULT_MAX_CONSECUTIVE_AUTO_PROCEED = 50;
|
|
23818
24063
|
function parseSuggestionsFromOutput(finalText, todos) {
|
|
@@ -23893,7 +24138,7 @@ async function runRepl(opts) {
|
|
|
23893
24138
|
appConfig: opts.appConfig,
|
|
23894
24139
|
capabilities: ["telemetry.publish", "mailbox.summary"]
|
|
23895
24140
|
});
|
|
23896
|
-
const clientMailbox = new
|
|
24141
|
+
const clientMailbox = new GlobalMailbox3(projectDir, void 0, () => hqConnection.getPublisher());
|
|
23897
24142
|
let clientHeartbeat;
|
|
23898
24143
|
clientMailbox.registerClient({
|
|
23899
24144
|
clientId,
|
|
@@ -24061,6 +24306,7 @@ ${lines.join("\n")}
|
|
|
24061
24306
|
}
|
|
24062
24307
|
if (mode === "auto") {
|
|
24063
24308
|
let top;
|
|
24309
|
+
let groundedTodo = false;
|
|
24064
24310
|
if (suggestions.length > 0) {
|
|
24065
24311
|
const isYolo = opts.getYolo?.() ?? false;
|
|
24066
24312
|
const autoSuggestions = opts.getAutoSuggestions?.() ?? [];
|
|
@@ -24070,6 +24316,7 @@ ${lines.join("\n")}
|
|
|
24070
24316
|
const todos = opts.agent.ctx.todos ?? [];
|
|
24071
24317
|
const resolved = resolveContinuation({ todos, suggestions: [] });
|
|
24072
24318
|
top = resolved.source === "todo" ? resolved.text : "";
|
|
24319
|
+
groundedTodo = top !== "";
|
|
24073
24320
|
}
|
|
24074
24321
|
if (!top) {
|
|
24075
24322
|
} else {
|
|
@@ -24095,7 +24342,8 @@ ${lines.join("\n")}
|
|
|
24095
24342
|
promptToFeed,
|
|
24096
24343
|
delay,
|
|
24097
24344
|
ctrl,
|
|
24098
|
-
autoProceedLoopGuard
|
|
24345
|
+
autoProceedLoopGuard,
|
|
24346
|
+
groundedTodo
|
|
24099
24347
|
);
|
|
24100
24348
|
if (submitted) {
|
|
24101
24349
|
autoIterCount++;
|
|
@@ -24579,7 +24827,7 @@ async function renderGoalBanner(opts) {
|
|
|
24579
24827
|
}
|
|
24580
24828
|
opts.renderer.write("\n");
|
|
24581
24829
|
}
|
|
24582
|
-
async function runAutoProceed(opts, suggestion, delayMs, ctrl, loopGuard) {
|
|
24830
|
+
async function runAutoProceed(opts, suggestion, delayMs, ctrl, loopGuard, grounded = false) {
|
|
24583
24831
|
const truncated = suggestion.length > 80 ? `${suggestion.slice(0, 77)}\u2026` : suggestion;
|
|
24584
24832
|
console.log(
|
|
24585
24833
|
JSON.stringify({
|
|
@@ -24612,7 +24860,7 @@ async function runAutoProceed(opts, suggestion, delayMs, ctrl, loopGuard) {
|
|
|
24612
24860
|
);
|
|
24613
24861
|
return false;
|
|
24614
24862
|
}
|
|
24615
|
-
const repetition = loopGuard.record(suggestion);
|
|
24863
|
+
const repetition = grounded ? loopGuard.recordGrounded(suggestion) : loopGuard.record(suggestion);
|
|
24616
24864
|
if (repetition.shouldHalt) {
|
|
24617
24865
|
setSuggestions([]);
|
|
24618
24866
|
setAutoSuggestions([]);
|
|
@@ -24622,7 +24870,20 @@ async function runAutoProceed(opts, suggestion, delayMs, ctrl, loopGuard) {
|
|
|
24622
24870
|
);
|
|
24623
24871
|
return false;
|
|
24624
24872
|
}
|
|
24625
|
-
const
|
|
24873
|
+
const steered = "action" in repetition && repetition.action === "steer";
|
|
24874
|
+
if (steered) {
|
|
24875
|
+
opts.renderer.writeWarning(
|
|
24876
|
+
"Todo board unchanged since the last automatic turn \u2014 steering the agent to update the board before continuing."
|
|
24877
|
+
);
|
|
24878
|
+
}
|
|
24879
|
+
const runBlocks = [
|
|
24880
|
+
{
|
|
24881
|
+
type: "text",
|
|
24882
|
+
text: steered ? `${suggestion}
|
|
24883
|
+
|
|
24884
|
+
${GROUNDED_NO_PROGRESS_STEER}` : suggestion
|
|
24885
|
+
}
|
|
24886
|
+
];
|
|
24626
24887
|
const runResult = await opts.agent.run(runBlocks, { signal: ctrl.signal });
|
|
24627
24888
|
console.log(
|
|
24628
24889
|
JSON.stringify({
|
|
@@ -26359,6 +26620,14 @@ import { DefaultSecretScrubber as DefaultSecretScrubber5 } from "@wrongstack/cor
|
|
|
26359
26620
|
import { createSessionEventBridge, resolveSessionLoggingConfig } from "@wrongstack/core/storage";
|
|
26360
26621
|
import { recordFileAction } from "@wrongstack/core/coordination";
|
|
26361
26622
|
import { startNetworkTelemetryMonitor } from "@wrongstack/core/observability";
|
|
26623
|
+
var DEFAULT_CHRONICLE_RETENTION_DAYS = 30;
|
|
26624
|
+
var MIN_CHRONICLE_RETENTION_DAYS = 7;
|
|
26625
|
+
function resolveChronicleRetentionDays(config) {
|
|
26626
|
+
const raw = config.chronicle?.retentionDays;
|
|
26627
|
+
if (typeof raw !== "number" || !Number.isFinite(raw)) return DEFAULT_CHRONICLE_RETENTION_DAYS;
|
|
26628
|
+
if (raw <= 0) return 0;
|
|
26629
|
+
return Math.max(MIN_CHRONICLE_RETENTION_DAYS, raw);
|
|
26630
|
+
}
|
|
26362
26631
|
function wireSessionEvents(deps) {
|
|
26363
26632
|
const { evOn, config, context, session, sessionRef, wpaths, projectRoot } = deps;
|
|
26364
26633
|
const sessionConfig = resolveSessionLoggingConfig(
|
|
@@ -26392,7 +26661,10 @@ function wireSessionEvents(deps) {
|
|
|
26392
26661
|
projectId: wpaths.projectHash ?? wpaths.projectSlug,
|
|
26393
26662
|
projectDir: wpaths.projectDir
|
|
26394
26663
|
});
|
|
26395
|
-
chronicleJournal = new ChronicleJournal2({
|
|
26664
|
+
chronicleJournal = new ChronicleJournal2({
|
|
26665
|
+
filePath: location.journalPath,
|
|
26666
|
+
retentionDays: resolveChronicleRetentionDays(config)
|
|
26667
|
+
});
|
|
26396
26668
|
const chronicleContext = createChronicleContext({
|
|
26397
26669
|
installationId: location.installationId,
|
|
26398
26670
|
machineId: location.machineId,
|
|
@@ -27214,7 +27486,20 @@ function stripQuotes(s) {
|
|
|
27214
27486
|
|
|
27215
27487
|
// src/slash-commands/agent-improve.ts
|
|
27216
27488
|
import { color as color54 } from "@wrongstack/core/utils";
|
|
27217
|
-
import {
|
|
27489
|
+
import {
|
|
27490
|
+
buildConsolidationInstruction,
|
|
27491
|
+
captureLearnedFromAgentOutput,
|
|
27492
|
+
getProjectAgentLearnStats,
|
|
27493
|
+
isConsolidated,
|
|
27494
|
+
loadConsolidationMetadata,
|
|
27495
|
+
loadProjectAgentConfig,
|
|
27496
|
+
loadProjectAgentIdentity,
|
|
27497
|
+
loadProjectAgentLearned,
|
|
27498
|
+
loadRoleKnowledgeManifest,
|
|
27499
|
+
resetProjectAgentIdentity,
|
|
27500
|
+
refreshProjectAgentIdentity,
|
|
27501
|
+
updateProjectAgentIdentity
|
|
27502
|
+
} from "@wrongstack/core/agent-catalog";
|
|
27218
27503
|
function buildAgentImproveCommand(opts) {
|
|
27219
27504
|
const projectRoot = opts.projectRoot;
|
|
27220
27505
|
function listRoles2() {
|
|
@@ -27238,6 +27523,7 @@ function buildAgentImproveCommand(opts) {
|
|
|
27238
27523
|
" /agent-improve [role] update Update identity + learned content",
|
|
27239
27524
|
" /agent-improve [role] refresh Reset identity + learned to empty templates",
|
|
27240
27525
|
" /agent-improve [role] capture Scan last output for ## LEARNED blocks and persist them",
|
|
27526
|
+
" /agent-improve [role] consolidate Optimize raw learned entries into a consolidated document",
|
|
27241
27527
|
" /agent-improve [role] reset Delete ALL custom files for this role",
|
|
27242
27528
|
" /agent-improve * reset Delete ALL custom files for every role",
|
|
27243
27529
|
"",
|
|
@@ -27247,6 +27533,8 @@ function buildAgentImproveCommand(opts) {
|
|
|
27247
27533
|
" Agents output a ## LEARNED section in their response to persist",
|
|
27248
27534
|
" project-specific patterns. The runtime captures these automatically.",
|
|
27249
27535
|
' Use "capture" to manually re-scan any text for LEARNED blocks.',
|
|
27536
|
+
' Use "consolidate" to synthesize all raw entries into a single',
|
|
27537
|
+
" narrowly-scoped document that replaces raw entries in the agent prompt.",
|
|
27250
27538
|
"",
|
|
27251
27539
|
"Files live under: .wrongstack/agents/<role>/"
|
|
27252
27540
|
].join("\n"),
|
|
@@ -27329,6 +27617,24 @@ Or edit files directly under .wrongstack/agents/${role}/`
|
|
|
27329
27617
|
message: `No ## LEARNED blocks found in recent output for role "${role}".`
|
|
27330
27618
|
};
|
|
27331
27619
|
}
|
|
27620
|
+
case "consolidate": {
|
|
27621
|
+
const stats = getProjectAgentLearnStats(role, projectRoot);
|
|
27622
|
+
if (stats.entryCount === 0) {
|
|
27623
|
+
const msg = `No raw learned entries for role "${role}" to consolidate.`;
|
|
27624
|
+
opts.renderer.write(msg);
|
|
27625
|
+
return { message: msg };
|
|
27626
|
+
}
|
|
27627
|
+
const { instruction } = buildConsolidationInstruction(role, projectRoot);
|
|
27628
|
+
const prompt = `Optimize what the "${role}" agent has learned for its skills. ${instruction}`;
|
|
27629
|
+
const consolidatedAlready = isConsolidated(role, projectRoot);
|
|
27630
|
+
const meta = loadConsolidationMetadata(role, projectRoot);
|
|
27631
|
+
const summary = `${color54.cyan(role)}: ${stats.entryCount} raw entries (${stats.totalBytes}B)` + (consolidatedAlready && meta ? ` \xB7 last consolidated ${meta.consolidatedAt.slice(0, 10)} (${meta.sourceBytes}B \u2192 ${meta.consolidatedBytes}B)` : " \xB7 no prior consolidation") + "\n\nSending consolidation instruction to the agent...";
|
|
27632
|
+
opts.renderer.write(summary);
|
|
27633
|
+
return {
|
|
27634
|
+
message: summary,
|
|
27635
|
+
runText: prompt
|
|
27636
|
+
};
|
|
27637
|
+
}
|
|
27332
27638
|
case "reset": {
|
|
27333
27639
|
const removed = resetProjectAgentIdentity(
|
|
27334
27640
|
role === "*" ? void 0 : role,
|
|
@@ -27346,7 +27652,7 @@ Or edit files directly under .wrongstack/agents/${role}/`
|
|
|
27346
27652
|
}
|
|
27347
27653
|
default:
|
|
27348
27654
|
return {
|
|
27349
|
-
message: `Unknown action "${action}". Use: show, update, refresh, capture, reset, or reset-all.`
|
|
27655
|
+
message: `Unknown action "${action}". Use: show, update, refresh, capture, consolidate, reset, or reset-all.`
|
|
27350
27656
|
};
|
|
27351
27657
|
}
|
|
27352
27658
|
}
|
|
@@ -27786,6 +28092,12 @@ function parseRefEntry(ref) {
|
|
|
27786
28092
|
function compactEntry2(entry) {
|
|
27787
28093
|
return entry.provider ? `${entry.provider}/${entry.model}` : entry.model;
|
|
27788
28094
|
}
|
|
28095
|
+
function judgeSummary(s) {
|
|
28096
|
+
if (!s.judgeLabel) return "";
|
|
28097
|
+
const seated = s.councilLabels.some((label) => label.startsWith(`${s.judgeLabel} (`));
|
|
28098
|
+
const origin = s.council.judge ? "" : " [derived]";
|
|
28099
|
+
return `, judge: ${s.judgeLabel}${origin}${seated ? " \u26A0 also a voter" : ""}`;
|
|
28100
|
+
}
|
|
27789
28101
|
function parseSeat(token) {
|
|
27790
28102
|
const parts = token.split(":");
|
|
27791
28103
|
const mods = [];
|
|
@@ -28035,7 +28347,7 @@ function buildBrainCommand(opts) {
|
|
|
28035
28347
|
}
|
|
28036
28348
|
if (subcommand === "council") {
|
|
28037
28349
|
const op = (rest[0] ?? "").toLowerCase();
|
|
28038
|
-
const councilSummary = (s) => s.council.enabled ? `council ${color57.cyan("convened")}: ${s.councilLabels.join(", ")} ${color57.dim(`(minRisk: ${s.council.minRisk}${
|
|
28350
|
+
const councilSummary = (s) => s.council.enabled ? `council ${color57.cyan("convened")}: ${s.councilLabels.join(", ")} ${color57.dim(`(minRisk: ${s.council.minRisk}${judgeSummary(s)})`)}` : `council ${color57.dim("disabled")}`;
|
|
28039
28351
|
if (!op) {
|
|
28040
28352
|
const snapshot = opts.brainRuntime?.getSnapshot();
|
|
28041
28353
|
const msg3 = snapshot ? `Brain ${councilSummary(snapshot)}` : "The Brain runtime is not available in this session.";
|
|
@@ -28070,7 +28382,7 @@ function buildBrainCommand(opts) {
|
|
|
28070
28382
|
opts.renderer.writeWarning(msg3);
|
|
28071
28383
|
return { message: msg3 };
|
|
28072
28384
|
}
|
|
28073
|
-
return applyPatch({ council: { judge: ref.toLowerCase() === "auto" ? null : ref } }, (s) => `Brain council judge: ${color57.cyan(s.council.judge ? compactEntry2(s.council.judge) : "auto (
|
|
28385
|
+
return applyPatch({ council: { judge: ref.toLowerCase() === "auto" ? null : ref } }, (s) => `Brain council judge: ${color57.cyan(s.council.judge ? compactEntry2(s.council.judge) : "auto")}${s.council.judge ? "" : color57.dim(judgeSummary(s).replace(/^, judge: /, " \u2192 "))}`);
|
|
28074
28386
|
}
|
|
28075
28387
|
if (op === "quorum" || op === "approval") {
|
|
28076
28388
|
const value = Number(rest[1]);
|
|
@@ -28205,7 +28517,7 @@ ${color57.dim(decision.rationale)}` : ""}`;
|
|
|
28205
28517
|
const councilSeats = opts.brainSettings?.councilLabels ?? [];
|
|
28206
28518
|
if (councilSeats.length > 0) {
|
|
28207
28519
|
lines.push(
|
|
28208
|
-
` council: ${color57.cyan(councilSeats.join(", "))}${snapshot ? color57.dim(` (minRisk: ${snapshot.council.minRisk}${
|
|
28520
|
+
` council: ${color57.cyan(councilSeats.join(", "))}${snapshot ? color57.dim(` (minRisk: ${snapshot.council.minRisk}${judgeSummary(snapshot)})`) : ""}`
|
|
28209
28521
|
);
|
|
28210
28522
|
} else if (snapshot) {
|
|
28211
28523
|
lines.push(` council: ${color57.dim("disabled (/brain council on + voters)")}`);
|
|
@@ -33689,6 +34001,7 @@ function buildKanbanCommand(opts) {
|
|
|
33689
34001
|
" /kanban duplicate <boardId> [title] Duplicate a board",
|
|
33690
34002
|
" /kanban show <boardId> Show board details (columns + tasks)",
|
|
33691
34003
|
" /kanban delete <boardId> Delete a board",
|
|
34004
|
+
" /kanban prune [days] [--all] [--yes] Delete stale boards (dry-run without --yes)",
|
|
33692
34005
|
" /kanban rename <boardId> <t> Rename a board",
|
|
33693
34006
|
" /kanban snapshot [boardId] Show agent queue snapshot",
|
|
33694
34007
|
"",
|
|
@@ -33778,6 +34091,60 @@ function buildKanbanCommand(opts) {
|
|
|
33778
34091
|
message: removed ? color74.green(`\u2705 Board deleted: ${rest[0]}`) : color74.red(`Board not found: ${rest[0]}`)
|
|
33779
34092
|
};
|
|
33780
34093
|
}
|
|
34094
|
+
if (cmd === "prune") {
|
|
34095
|
+
const flags = new Set(rest.filter((t) => t.startsWith("--") || t === "-y"));
|
|
34096
|
+
const positional = rest.filter((t) => !t.startsWith("--") && t !== "-y");
|
|
34097
|
+
const days = positional[0] ? Number(positional[0]) : 7;
|
|
34098
|
+
if (!Number.isFinite(days) || days < 0) {
|
|
34099
|
+
return { message: color74.red("Usage: /kanban prune [days>=0] [--all] [--yes]") };
|
|
34100
|
+
}
|
|
34101
|
+
const includeUnfinished = flags.has("--all");
|
|
34102
|
+
const apply = flags.has("--yes") || flags.has("-y");
|
|
34103
|
+
const cutoff = Date.now() - days * 864e5;
|
|
34104
|
+
const boards = await listBoards(projectRoot);
|
|
34105
|
+
const stale = boards.filter((b) => {
|
|
34106
|
+
const updatedAt = Date.parse(b.updatedAt);
|
|
34107
|
+
if (!Number.isFinite(updatedAt) || updatedAt > cutoff) return false;
|
|
34108
|
+
if (includeUnfinished) return true;
|
|
34109
|
+
return b.taskCount === 0 || b.completedTaskCount === b.taskCount;
|
|
34110
|
+
});
|
|
34111
|
+
if (stale.length === 0) {
|
|
34112
|
+
return {
|
|
34113
|
+
message: color74.green(
|
|
34114
|
+
`Nothing to prune: no ${includeUnfinished ? "" : "finished/empty "}boards idle \u2265 ${days}d (of ${boards.length} total).`
|
|
34115
|
+
)
|
|
34116
|
+
};
|
|
34117
|
+
}
|
|
34118
|
+
if (!apply) {
|
|
34119
|
+
const preview = stale.slice(0, 20).map(
|
|
34120
|
+
(b) => ` ${LABEL(b.id.slice(0, 8))} ${b.title.slice(0, 48)} ${DIM(`${b.taskCount} tasks \xB7 updated ${fmtAge2(b.updatedAt)}`)}`
|
|
34121
|
+
);
|
|
34122
|
+
if (stale.length > 20) preview.push(DIM(` \u2026 and ${stale.length - 20} more`));
|
|
34123
|
+
return {
|
|
34124
|
+
message: [
|
|
34125
|
+
HEADING(
|
|
34126
|
+
`Would prune ${stale.length} of ${boards.length} boards (idle \u2265 ${days}d${includeUnfinished ? ", including unfinished" : ", finished/empty only"})`
|
|
34127
|
+
),
|
|
34128
|
+
...preview,
|
|
34129
|
+
"",
|
|
34130
|
+
DIM(`Run /kanban prune ${positional[0] ?? days}${includeUnfinished ? " --all" : ""} --yes (or -y) to delete.`)
|
|
34131
|
+
].join("\n")
|
|
34132
|
+
};
|
|
34133
|
+
}
|
|
34134
|
+
let removed = 0;
|
|
34135
|
+
let failed = 0;
|
|
34136
|
+
for (const b of stale) {
|
|
34137
|
+
if (await removeBoard(projectRoot, b.id)) removed++;
|
|
34138
|
+
else failed++;
|
|
34139
|
+
}
|
|
34140
|
+
const parts = [
|
|
34141
|
+
`\u2705 Pruned ${removed} board${removed === 1 ? "" : "s"} (${boards.length - removed} remain).`
|
|
34142
|
+
];
|
|
34143
|
+
if (failed > 0) {
|
|
34144
|
+
parts.push(color74.yellow(`${failed} board${failed === 1 ? "" : "s"} could not be deleted \u2014 check file permissions or HQ sync locks.`));
|
|
34145
|
+
}
|
|
34146
|
+
return { message: color74.green(parts.join(" ")) };
|
|
34147
|
+
}
|
|
33781
34148
|
if (cmd === "rename") {
|
|
33782
34149
|
if (rest.length < 2)
|
|
33783
34150
|
return { message: color74.red("Usage: /kanban rename <boardId> <title>") };
|
|
@@ -33838,6 +34205,7 @@ function buildKanbanCommand(opts) {
|
|
|
33838
34205
|
"duplicate",
|
|
33839
34206
|
"show",
|
|
33840
34207
|
"delete",
|
|
34208
|
+
"prune",
|
|
33841
34209
|
"rename",
|
|
33842
34210
|
"generate",
|
|
33843
34211
|
"snapshot",
|
|
@@ -34449,11 +34817,11 @@ function stripQuotes2(value) {
|
|
|
34449
34817
|
|
|
34450
34818
|
// src/slash-commands/mailbox.ts
|
|
34451
34819
|
import { color as color75, wstackGlobalRoot as wstackGlobalRoot5 } from "@wrongstack/core/utils";
|
|
34452
|
-
import { GlobalMailbox as
|
|
34820
|
+
import { GlobalMailbox as GlobalMailbox4, mailboxSessionTag, resolveMailboxIdentity, resolveProjectDir as resolveProjectDir4 } from "@wrongstack/core/coordination";
|
|
34453
34821
|
function buildMailbox(opts) {
|
|
34454
34822
|
const projectDir = opts.paths?.projectDir ?? resolveProjectDir4(opts.projectRoot, wstackGlobalRoot5());
|
|
34455
34823
|
try {
|
|
34456
|
-
return new
|
|
34824
|
+
return new GlobalMailbox4(projectDir);
|
|
34457
34825
|
} catch {
|
|
34458
34826
|
return null;
|
|
34459
34827
|
}
|
|
@@ -34772,13 +35140,13 @@ function buildMailboxCommand(opts) {
|
|
|
34772
35140
|
}
|
|
34773
35141
|
|
|
34774
35142
|
// src/slash-commands/mailbox-demo.ts
|
|
34775
|
-
import { GlobalMailbox as
|
|
35143
|
+
import { GlobalMailbox as GlobalMailbox5, resolveProjectDir as resolveProjectDir5 } from "@wrongstack/core/coordination";
|
|
34776
35144
|
import { wstackGlobalRoot as wstackGlobalRoot6 } from "@wrongstack/core/utils";
|
|
34777
35145
|
var DEMO_AGENT_ID = "mailbox-demo";
|
|
34778
35146
|
function buildMailbox2(opts) {
|
|
34779
35147
|
const projectDir = opts.paths?.projectDir ?? resolveProjectDir5(opts.projectRoot, wstackGlobalRoot6());
|
|
34780
35148
|
try {
|
|
34781
|
-
return new
|
|
35149
|
+
return new GlobalMailbox5(projectDir);
|
|
34782
35150
|
} catch {
|
|
34783
35151
|
return null;
|
|
34784
35152
|
}
|
|
@@ -46429,7 +46797,7 @@ import {
|
|
|
46429
46797
|
FleetManager,
|
|
46430
46798
|
FleetSupervisor,
|
|
46431
46799
|
formatSubagentStructuredReport,
|
|
46432
|
-
GlobalMailbox as
|
|
46800
|
+
GlobalMailbox as GlobalMailbox6,
|
|
46433
46801
|
HARD_MAX_SPAWN_DEPTH,
|
|
46434
46802
|
mailboxSessionTag as mailboxSessionTag2,
|
|
46435
46803
|
makeDirectorSessionFactory,
|
|
@@ -46609,7 +46977,19 @@ function createFleetStatusBroadcaster(opts) {
|
|
|
46609
46977
|
// A retired subagent's id will never be seen again, so its one-shot dedup
|
|
46610
46978
|
// guards and throttle timestamp are dead weight. Prune them so a long-lived
|
|
46611
46979
|
// fleet that spawns thousands of subagents does not leak one entry each.
|
|
46980
|
+
//
|
|
46981
|
+
// Deregister from the mailbox agent registry so the retirement is visible
|
|
46982
|
+
// cross-process immediately. Without this, the registry entry lingers with
|
|
46983
|
+
// status 'idle' (the last heartbeat from the task_completed handler) until
|
|
46984
|
+
// the 60s lazy prune fires — leaking dead agents in fleet_status and the
|
|
46985
|
+
// fleet pulse for up to a minute. deregisterAgent is used instead of
|
|
46986
|
+
// heartbeat({ status: 'offline' }) because (a) RegisteredAgent.status
|
|
46987
|
+
// does not include 'offline' (only the read-side MailboxAgentStatus does),
|
|
46988
|
+
// and (b) the heartbeat path is throttled to 1/5s so it silently drops
|
|
46989
|
+
// when fired right after the task_completed idle heartbeat.
|
|
46612
46990
|
opts.events.on("subagent.removed", (e) => {
|
|
46991
|
+
void mb().deregisterAgent(e.subagentId).catch(() => {
|
|
46992
|
+
});
|
|
46613
46993
|
announcedSpawn.delete(e.subagentId);
|
|
46614
46994
|
lastSentAt.delete(e.subagentId);
|
|
46615
46995
|
for (const key of announcedBudget) {
|
|
@@ -46922,7 +47302,7 @@ var MultiAgentHost = class _MultiAgentHost {
|
|
|
46922
47302
|
}
|
|
46923
47303
|
this.statusBroadcaster = createFleetStatusBroadcaster({
|
|
46924
47304
|
events: this.deps.events,
|
|
46925
|
-
mailboxFactory: () => new
|
|
47305
|
+
mailboxFactory: () => new GlobalMailbox6(this.mailboxProjectDir(), this.deps.events),
|
|
46926
47306
|
sessionTag: () => mailboxSessionTag2(this.deps.session.id),
|
|
46927
47307
|
subagentName: (id) => this.director?.status().subagents.find((s) => s.id === id)?.name,
|
|
46928
47308
|
config: config.fleet?.statusBroadcasts
|
|
@@ -47309,7 +47689,7 @@ ${message} Falling back to the assigned checkout.` : `${message} Falling back to
|
|
|
47309
47689
|
}
|
|
47310
47690
|
let onlineAgents = [];
|
|
47311
47691
|
try {
|
|
47312
|
-
const subagentMailbox = new
|
|
47692
|
+
const subagentMailbox = new GlobalMailbox6(this.mailboxProjectDir());
|
|
47313
47693
|
onlineAgents = await subagentMailbox.getAgentStatuses();
|
|
47314
47694
|
} catch {
|
|
47315
47695
|
}
|
|
@@ -47833,7 +48213,7 @@ ${resolved.join("\n\n---\n\n")}` : void 0
|
|
|
47833
48213
|
const supervisorCfg = config.fleet?.supervisor;
|
|
47834
48214
|
if (!director || !brain || supervisorCfg?.enabled === false) return;
|
|
47835
48215
|
const supTag = () => mailboxSessionTag2(this.deps.session.id);
|
|
47836
|
-
const mailbox = () => new
|
|
48216
|
+
const mailbox = () => new GlobalMailbox6(this.mailboxProjectDir(), this.deps.events);
|
|
47837
48217
|
this.fleetSupervisor = new FleetSupervisor({
|
|
47838
48218
|
events: this.deps.events,
|
|
47839
48219
|
fleet: director.fleet,
|
|
@@ -47908,7 +48288,7 @@ ${resolved.join("\n\n---\n\n")}` : void 0
|
|
|
47908
48288
|
async reportTaskResultToLeader(n) {
|
|
47909
48289
|
try {
|
|
47910
48290
|
const leaderId2 = this.opts.getLeaderMailboxId?.() ?? `leader@${mailboxSessionTag2(this.deps.session.id)}`;
|
|
47911
|
-
const mailbox = new
|
|
48291
|
+
const mailbox = new GlobalMailbox6(this.mailboxProjectDir(), this.deps.events);
|
|
47912
48292
|
const ok = n.status === "success";
|
|
47913
48293
|
const MAX_BODY = 700;
|
|
47914
48294
|
const failureText = [
|
|
@@ -48848,14 +49228,14 @@ async function spawnAgent2(director, role, task, maxIterations) {
|
|
|
48848
49228
|
// src/wiring/dep-watcher.ts
|
|
48849
49229
|
import * as path62 from "node:path";
|
|
48850
49230
|
import { startTechStackConsumer } from "@wrongstack/core/coordination";
|
|
48851
|
-
import { GlobalMailbox as
|
|
49231
|
+
import { GlobalMailbox as GlobalMailbox7, startPackageOutdatedWatcher } from "@wrongstack/core/coordination";
|
|
48852
49232
|
function setupDepWatcherConsumers(deps) {
|
|
48853
49233
|
const { dwCfg, globalRoot, projectSlug, events, multiAgentHost, sessionId, logger, teardownHandlers, projectRoot } = deps;
|
|
48854
49234
|
let techStackConsumerDispose;
|
|
48855
49235
|
if (dwCfg?.["enabled"] === true) {
|
|
48856
49236
|
try {
|
|
48857
49237
|
const projectDir = path62.join(globalRoot, "projects", projectSlug);
|
|
48858
|
-
const tsMailbox = new
|
|
49238
|
+
const tsMailbox = new GlobalMailbox7(projectDir, events);
|
|
48859
49239
|
const fileAuthorOpts = {
|
|
48860
49240
|
storageDir: projectDir,
|
|
48861
49241
|
projectRoot
|
|
@@ -48888,7 +49268,7 @@ function setupDepWatcherConsumers(deps) {
|
|
|
48888
49268
|
if (dwCfg?.["enabled"] === true) {
|
|
48889
49269
|
try {
|
|
48890
49270
|
const projectDir = path62.join(globalRoot, "projects", projectSlug);
|
|
48891
|
-
const pkgMailbox = new
|
|
49271
|
+
const pkgMailbox = new GlobalMailbox7(projectDir, events);
|
|
48892
49272
|
const pkgTrackerOpts = {
|
|
48893
49273
|
storageDir: projectDir,
|
|
48894
49274
|
projectRoot
|
|
@@ -49631,7 +50011,7 @@ function setupHqTelemetry(deps) {
|
|
|
49631
50011
|
import * as path68 from "node:path";
|
|
49632
50012
|
import { allServers as allServers2 } from "@wrongstack/core/infrastructure";
|
|
49633
50013
|
import { countShellHooks, HookRegistry, HookRunner, shellHooksEqual } from "@wrongstack/core/hooks";
|
|
49634
|
-
import { GlobalMailbox as
|
|
50014
|
+
import { GlobalMailbox as GlobalMailbox8 } from "@wrongstack/core/coordination";
|
|
49635
50015
|
import { NotifierImpl } from "@wrongstack/core/notifications";
|
|
49636
50016
|
import { normalizeTokenSavingTier as normalizeTokenSavingTier6 } from "@wrongstack/core/types";
|
|
49637
50017
|
import { SlashCommandRegistry } from "@wrongstack/core/registry";
|
|
@@ -50377,7 +50757,7 @@ async function setupLifecycleAndPlugins(deps) {
|
|
|
50377
50757
|
registerMcpObservability(healthRegistry, metricsSink, mcpRegistry);
|
|
50378
50758
|
const slashRegistry = new SlashCommandRegistry();
|
|
50379
50759
|
const hqPublisherRef = { current: void 0 };
|
|
50380
|
-
const brainMailbox = new
|
|
50760
|
+
const brainMailbox = new GlobalMailbox8(wpaths.projectDir, events, () => hqPublisherRef.current);
|
|
50381
50761
|
const notifier = new NotifierImpl();
|
|
50382
50762
|
const pluginHost = await setupPlugins({
|
|
50383
50763
|
config,
|
|
@@ -51108,8 +51488,11 @@ init_utils();
|
|
|
51108
51488
|
import { getToolDescriptionMode as getToolDescriptionMode3 } from "@wrongstack/core/utils";
|
|
51109
51489
|
|
|
51110
51490
|
// src/wiring/codebase-index.ts
|
|
51111
|
-
import * as fs55 from "node:fs";
|
|
51112
51491
|
import * as path69 from "node:path";
|
|
51492
|
+
import {
|
|
51493
|
+
DEFAULT_WALK_IGNORE_SET,
|
|
51494
|
+
watchProjectTree
|
|
51495
|
+
} from "@wrongstack/core/utils";
|
|
51113
51496
|
import {
|
|
51114
51497
|
cancelPendingReindexes,
|
|
51115
51498
|
enqueueReindex,
|
|
@@ -51118,19 +51501,8 @@ import {
|
|
|
51118
51501
|
shutdownCodebaseIndexHost
|
|
51119
51502
|
} from "@wrongstack/tools";
|
|
51120
51503
|
var FILE_EDIT_TOOLS = /* @__PURE__ */ new Set(["write", "edit"]);
|
|
51121
|
-
var IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
51122
|
-
"node_modules",
|
|
51123
|
-
".git",
|
|
51124
|
-
"dist",
|
|
51125
|
-
"build",
|
|
51126
|
-
".next",
|
|
51127
|
-
"coverage",
|
|
51128
|
-
".turbo",
|
|
51129
|
-
"__snapshots__",
|
|
51130
|
-
".nyc_output"
|
|
51131
|
-
]);
|
|
51132
51504
|
function isIgnored(rel) {
|
|
51133
|
-
return rel.split(/[/\\]/).some((seg) =>
|
|
51505
|
+
return rel.split(/[/\\]/).some((seg) => DEFAULT_WALK_IGNORE_SET.has(seg));
|
|
51134
51506
|
}
|
|
51135
51507
|
async function setupCodebaseIndexing(deps) {
|
|
51136
51508
|
const { config, context, pipelines, projectRoot, logger } = deps;
|
|
@@ -51180,16 +51552,18 @@ async function setupCodebaseIndexing(deps) {
|
|
|
51180
51552
|
let watcher;
|
|
51181
51553
|
if (idx.watchExternal) {
|
|
51182
51554
|
try {
|
|
51183
|
-
watcher =
|
|
51184
|
-
|
|
51185
|
-
|
|
51186
|
-
|
|
51187
|
-
|
|
51188
|
-
|
|
51189
|
-
|
|
51190
|
-
|
|
51191
|
-
|
|
51192
|
-
|
|
51555
|
+
watcher = watchProjectTree(
|
|
51556
|
+
projectRoot,
|
|
51557
|
+
(event) => {
|
|
51558
|
+
if (!event.filename) return;
|
|
51559
|
+
const rel = event.filename;
|
|
51560
|
+
if (isIgnored(rel)) return;
|
|
51561
|
+
const abs = path69.resolve(projectRoot, rel);
|
|
51562
|
+
if (!isIndexableFile(abs)) return;
|
|
51563
|
+
enqueueReindex({ projectRoot, files: [abs], debounceMs, onError });
|
|
51564
|
+
},
|
|
51565
|
+
{ onError: (err) => logger.debug(`codebase index watcher error: ${err}`) }
|
|
51566
|
+
);
|
|
51193
51567
|
} catch (err) {
|
|
51194
51568
|
logger.debug(
|
|
51195
51569
|
`codebase index watcher unavailable: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -51688,12 +52062,32 @@ function createSddHandlers(deps) {
|
|
|
51688
52062
|
}
|
|
51689
52063
|
}
|
|
51690
52064
|
const boardStore = new sddApi.SddBoardStore({ baseDir: wpaths.projectSddBoards });
|
|
51691
|
-
const verifyTask = sddApi.makeCommandVerifier();
|
|
51692
52065
|
const sddSupervisor = new sddApi.SddSupervisor({
|
|
51693
52066
|
brain,
|
|
51694
52067
|
reassignModels: agentCore.effectiveFallbackChain(config)
|
|
51695
52068
|
});
|
|
51696
52069
|
const sddSubagentFactory = multiAgentHost.makeSubagentFactory(config);
|
|
52070
|
+
let verifySeq = 0;
|
|
52071
|
+
const verifyTask = sddApi.makeCompositeVerifier([
|
|
52072
|
+
sddApi.makeCommandVerifier(),
|
|
52073
|
+
sddApi.makeAcceptanceCriteriaVerifier({
|
|
52074
|
+
run: async (prompt) => {
|
|
52075
|
+
const r = await sddSubagentFactory({
|
|
52076
|
+
id: `sdd-acceptance-${verifySeq++}`,
|
|
52077
|
+
role: "executor",
|
|
52078
|
+
name: "Acceptance Reviewer",
|
|
52079
|
+
disabledTools: ["delegate", "write", "edit", "patch", "bash", "exec"],
|
|
52080
|
+
allowedCapabilities: ["fs.read"]
|
|
52081
|
+
});
|
|
52082
|
+
try {
|
|
52083
|
+
const res = await r.agent.run([{ type: "text", text: prompt }]);
|
|
52084
|
+
return res.finalText ?? "";
|
|
52085
|
+
} finally {
|
|
52086
|
+
await r.dispose?.();
|
|
52087
|
+
}
|
|
52088
|
+
}
|
|
52089
|
+
})
|
|
52090
|
+
]);
|
|
51697
52091
|
const conflictMode = process.env["WRONGSTACK_SDD_CONFLICT_RESOLVER"];
|
|
51698
52092
|
const conflictResolver = conflictMode === "prefer-incoming" ? sddApi.makePreferSideConflictResolver("incoming") : conflictMode === "prefer-base" ? sddApi.makePreferSideConflictResolver("base") : conflictMode === "llm" ? sddApi.makeLlmConflictResolver({
|
|
51699
52093
|
run: async (prompt) => {
|
|
@@ -52093,10 +52487,10 @@ async function main(argv) {
|
|
|
52093
52487
|
const fallbackManageTools = createFallbackManageTools({
|
|
52094
52488
|
getConfig: () => configStore.get(),
|
|
52095
52489
|
updateConfig: async (mutate) => {
|
|
52096
|
-
const raw = await
|
|
52490
|
+
const raw = await fs55.readFile(profileConfigPath, "utf8").catch(() => "{}");
|
|
52097
52491
|
const parsed = JSON.parse(raw);
|
|
52098
52492
|
mutate(parsed);
|
|
52099
|
-
await
|
|
52493
|
+
await fs55.writeFile(profileConfigPath, JSON.stringify(parsed, null, 2), { mode: 384 });
|
|
52100
52494
|
configStore.update(parsed);
|
|
52101
52495
|
},
|
|
52102
52496
|
// Interactive key entry for REPL mode — reads a line from stdin without echo.
|
|
@@ -52194,7 +52588,7 @@ ${prompt}
|
|
|
52194
52588
|
let onlineAgents = [];
|
|
52195
52589
|
if (flags["simpleui"] !== true) {
|
|
52196
52590
|
try {
|
|
52197
|
-
const systemMailbox = new
|
|
52591
|
+
const systemMailbox = new GlobalMailbox9(wpaths.projectDir);
|
|
52198
52592
|
onlineAgents = await systemMailbox.getAgentStatuses();
|
|
52199
52593
|
} catch {
|
|
52200
52594
|
}
|
|
@@ -52311,7 +52705,9 @@ ${prompt}
|
|
|
52311
52705
|
});
|
|
52312
52706
|
const stats = new SessionStats(events, tokenCounter);
|
|
52313
52707
|
let activeReasoningConfig;
|
|
52708
|
+
const warnedRuntimeMessages = /* @__PURE__ */ new Set();
|
|
52314
52709
|
const refreshActiveReasoningConfig = async (providerId, modelId) => {
|
|
52710
|
+
warnedRuntimeMessages.clear();
|
|
52315
52711
|
try {
|
|
52316
52712
|
const resolved = await modelsRegistry.getModel(providerId, modelId);
|
|
52317
52713
|
activeReasoningConfig = resolved?.capabilities.reasoningConfig;
|
|
@@ -52328,6 +52724,8 @@ ${prompt}
|
|
|
52328
52724
|
getReasoningConfig: () => activeReasoningConfig,
|
|
52329
52725
|
getCapabilities: () => provider.capabilities,
|
|
52330
52726
|
onWarning: (message) => {
|
|
52727
|
+
if (warnedRuntimeMessages.has(message)) return;
|
|
52728
|
+
warnedRuntimeMessages.add(message);
|
|
52331
52729
|
logger.warn(`model-runtime: ${message}`);
|
|
52332
52730
|
}
|
|
52333
52731
|
}
|
|
@@ -52377,7 +52775,7 @@ ${prompt}
|
|
|
52377
52775
|
if (dwCfg?.["enabled"] === true) {
|
|
52378
52776
|
try {
|
|
52379
52777
|
const projectDir = path71.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
|
|
52380
|
-
const dwMailbox = new
|
|
52778
|
+
const dwMailbox = new GlobalMailbox9(projectDir, events);
|
|
52381
52779
|
depWatcherDispose = attachDepWatcherBridge({
|
|
52382
52780
|
events,
|
|
52383
52781
|
mailbox: dwMailbox,
|