@wrongstack/core 0.298.3 → 0.300.0
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/chronicle/index.js +4 -1
- package/dist/coordination/agents/index.js +4 -1
- package/dist/coordination/director.d.ts +8 -0
- package/dist/coordination/fleet-manager.d.ts +48 -3
- package/dist/coordination/ifleet-manager.d.ts +2 -0
- package/dist/coordination/index.js +127 -24
- package/dist/coordination/multi-agent-coordinator.d.ts +1 -0
- package/dist/core/fallback-model.d.ts +48 -0
- package/dist/core/index.d.ts +3 -2
- package/dist/core/index.js +288 -34
- package/dist/core/instruction-template.d.ts +80 -0
- package/dist/core/system-prompt-blocks.d.ts +10 -1
- package/dist/core/system-prompt-builder.d.ts +35 -1
- package/dist/defaults/index.js +358 -117
- package/dist/design/index.js +4 -1
- package/dist/execution/autonomy-brain.d.ts +7 -0
- package/dist/execution/council-brain.d.ts +17 -2
- package/dist/execution/council-orchestrator.d.ts +23 -4
- package/dist/execution/council-personas.d.ts +10 -0
- package/dist/execution/council-prompts.d.ts +12 -1
- package/dist/execution/index.d.ts +1 -1
- package/dist/execution/index.js +412 -145
- package/dist/fleet-notifier.d.ts +9 -2
- package/dist/goal/index.js +4 -1
- package/dist/hooks/index.js +140 -10
- package/dist/hq/exposure.d.ts +0 -11
- package/dist/hq/index.js +34 -8
- package/dist/hq/protocol/client.d.ts +14 -1
- package/dist/hq/protocol/fleet.d.ts +22 -0
- package/dist/hq/protocol.js +12 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1718 -753
- package/dist/infrastructure/index.js +50 -2
- package/dist/infrastructure/mcp-servers.d.ts +35 -0
- package/dist/kernel/events/brain-events.d.ts +9 -0
- package/dist/kernel/events/provider-events.d.ts +49 -2
- package/dist/kernel/events/sdd-events.d.ts +2 -0
- package/dist/models/index.js +1 -1
- package/dist/plugin/api.d.ts +6 -0
- package/dist/plugin/config.d.ts +55 -0
- package/dist/plugin/index.d.ts +1 -1
- package/dist/plugin/index.js +138 -22
- package/dist/security/index.d.ts +1 -1
- package/dist/security/index.js +157 -42
- package/dist/security/permission-helpers.d.ts +23 -6
- package/dist/security/permission-policy.d.ts +16 -0
- package/dist/security/totp.d.ts +14 -0
- package/dist/storage/director-state.d.ts +7 -0
- package/dist/storage/index.js +46 -9
- package/dist/tools/council-tool.d.ts +1 -1
- package/dist/tools/fallback-system-config-view-tool.d.ts +1 -1
- package/dist/tools/index.js +449 -112
- package/dist/types/config/skills-fleet-brain.d.ts +4 -2
- package/dist/types/config/tools.d.ts +99 -0
- package/dist/types/council.d.ts +11 -0
- package/dist/types/index.d.ts +3 -2
- package/dist/types/index.js +3 -3
- package/dist/types/multi-agent.d.ts +10 -0
- package/dist/types/one-shot-llm.d.ts +31 -3
- package/dist/types/plugin.d.ts +28 -0
- package/dist/types/session.d.ts +5 -1
- package/dist/utils/index.js +4 -1
- package/dist/utils/wstack-paths.d.ts +2 -0
- package/dist/worktree/index.js +47 -25
- package/dist/worktree/worktree-manager.d.ts +16 -10
- package/instructions/coordination/subagent-baseline.md +8 -0
- package/instructions/system-lite.md +83 -3
- package/instructions/system-pro.md +286 -97
- package/instructions/system.md +236 -85
- package/package.json +3 -3
package/dist/fleet-notifier.d.ts
CHANGED
|
@@ -5,8 +5,13 @@ export interface FleetNotifierOptions {
|
|
|
5
5
|
projectRoot: string;
|
|
6
6
|
/** This process's pid, so a WebUI never pings itself. */
|
|
7
7
|
selfPid?: number | undefined;
|
|
8
|
-
/**
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Injectable POST for tests. Defaults to a timed `fetch`.
|
|
10
|
+
*
|
|
11
|
+
* The second parameter carries the target's API token (H3). It is optional
|
|
12
|
+
* so an existing `(url) => …` stub stays assignable.
|
|
13
|
+
*/
|
|
14
|
+
post?: ((url: string, token?: string | undefined) => Promise<void>) | undefined;
|
|
10
15
|
}
|
|
11
16
|
export declare class FleetNotifier {
|
|
12
17
|
private readonly baseDir;
|
|
@@ -21,6 +26,8 @@ export declare class FleetNotifier {
|
|
|
21
26
|
notify(): void;
|
|
22
27
|
/** Resolve same-project WebUI ping URLs (cached briefly). Exposed for tests. */
|
|
23
28
|
endpoints(): Promise<string[]>;
|
|
29
|
+
/** Ping targets with their tokens (cached briefly). */
|
|
30
|
+
private targets;
|
|
24
31
|
dispose(): void;
|
|
25
32
|
/** Re-scope notifications after an in-process project switch. */
|
|
26
33
|
setProjectRoot(projectRoot: string): void;
|
package/dist/goal/index.js
CHANGED
|
@@ -2790,7 +2790,9 @@ function safeProfileName(name) {
|
|
|
2790
2790
|
function activeProfileName(globalRoot) {
|
|
2791
2791
|
try {
|
|
2792
2792
|
const parsed = JSON.parse(fs.readFileSync(path4.join(globalRoot, "config.json"), "utf8"));
|
|
2793
|
-
return safeProfileName(
|
|
2793
|
+
return safeProfileName(
|
|
2794
|
+
typeof parsed.activeProfile === "string" ? parsed.activeProfile : void 0
|
|
2795
|
+
);
|
|
2794
2796
|
} catch {
|
|
2795
2797
|
return "default";
|
|
2796
2798
|
}
|
|
@@ -2870,6 +2872,7 @@ function resolveWstackPaths(opts) {
|
|
|
2870
2872
|
projectPlan: path4.join(projectDir, "plan.json"),
|
|
2871
2873
|
projectAutophase: path4.join(projectDir, "autophase"),
|
|
2872
2874
|
projectSddBoards: path4.join(projectDir, "sdd-boards"),
|
|
2875
|
+
projectRequirementIntakes: path4.join(projectDir, "requirement-intakes"),
|
|
2873
2876
|
syncConfig: path4.join(profileDir, "sync.json"),
|
|
2874
2877
|
configHistoryDir: path4.join(globalRoot, "config-history"),
|
|
2875
2878
|
projectStatus: (projectHash2) => path4.join(globalRoot, "projects", projectHash2, "status.json")
|
package/dist/hooks/index.js
CHANGED
|
@@ -1,3 +1,121 @@
|
|
|
1
|
+
// src/utils/ip-guard.ts
|
|
2
|
+
import * as dns from "node:dns/promises";
|
|
3
|
+
import * as net from "node:net";
|
|
4
|
+
function isPrivateIPv4(addr) {
|
|
5
|
+
const parts = addr.split(".").map((p) => Number.parseInt(p, 10));
|
|
6
|
+
if (parts.length !== 4 || parts.some((n) => Number.isNaN(n) || n < 0 || n > 255)) {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
const [a, b, c] = parts;
|
|
10
|
+
if (a === 0) return true;
|
|
11
|
+
if (a === 10) return true;
|
|
12
|
+
if (a === 127) return true;
|
|
13
|
+
if (a === 169 && b === 254) return true;
|
|
14
|
+
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
15
|
+
if (a === 192 && b === 168) return true;
|
|
16
|
+
if (a === 192 && b === 0 && c === 0) return true;
|
|
17
|
+
if (a === 100 && b >= 64 && b <= 127) return true;
|
|
18
|
+
if (a >= 224) return true;
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
function isPrivateIPv6(raw) {
|
|
22
|
+
const lower = raw.toLowerCase();
|
|
23
|
+
if (lower === "::" || lower === "::1") return true;
|
|
24
|
+
const groups = expandIPv6(lower);
|
|
25
|
+
if (!groups) return true;
|
|
26
|
+
if (groups[0] === 100 && groups[1] === 65435 && groups[2] === 1) return true;
|
|
27
|
+
if (groups[0] === 0 && groups[1] === 0 && groups[2] === 0 && groups[3] === 0 && groups[4] === 0 && groups[5] === 65535) {
|
|
28
|
+
const a = (groups[6] ?? 0) >> 8;
|
|
29
|
+
const b = (groups[6] ?? 0) & 255;
|
|
30
|
+
const c = (groups[7] ?? 0) >> 8;
|
|
31
|
+
const d = (groups[7] ?? 0) & 255;
|
|
32
|
+
return isPrivateIPv4(`${a}.${b}.${c}.${d}`);
|
|
33
|
+
}
|
|
34
|
+
const embedded = embeddedIPv4(groups);
|
|
35
|
+
if (embedded) return isPrivateIPv4(embedded);
|
|
36
|
+
const high = groups[0] ?? 0;
|
|
37
|
+
if ((high & 65024) === 64512) return true;
|
|
38
|
+
if ((high & 65472) === 65152) return true;
|
|
39
|
+
if ((high & 65280) === 65280) return true;
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
function quad(hi, lo) {
|
|
43
|
+
return `${(hi ?? 0) >> 8}.${(hi ?? 0) & 255}.${(lo ?? 0) >> 8}.${(lo ?? 0) & 255}`;
|
|
44
|
+
}
|
|
45
|
+
function embeddedIPv4(groups) {
|
|
46
|
+
const g = (i) => groups[i] ?? 0;
|
|
47
|
+
if (g(0) === 100 && g(1) === 65435) {
|
|
48
|
+
if (g(2) === 0 && g(3) === 0 && g(4) === 0 && g(5) === 0) {
|
|
49
|
+
return quad(g(6), g(7));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (g(0) === 8194) return quad(g(1), g(2));
|
|
53
|
+
if (g(0) === 8193 && g(1) === 0) {
|
|
54
|
+
return quad(~g(6) & 65535, ~g(7) & 65535);
|
|
55
|
+
}
|
|
56
|
+
if ((g(4) === 0 || g(4) === 512) && g(5) === 24318) {
|
|
57
|
+
return quad(g(6), g(7));
|
|
58
|
+
}
|
|
59
|
+
const highZero = g(0) === 0 && g(1) === 0 && g(2) === 0 && g(3) === 0;
|
|
60
|
+
if (highZero && g(4) === 65535 && g(5) === 0) return quad(g(6), g(7));
|
|
61
|
+
if (highZero && g(4) === 0 && g(5) === 0) return quad(g(6), g(7));
|
|
62
|
+
return void 0;
|
|
63
|
+
}
|
|
64
|
+
function expandIPv6(addr) {
|
|
65
|
+
const parts = addr.split("::");
|
|
66
|
+
if (parts.length > 2) return null;
|
|
67
|
+
const parseGroups = (s) => {
|
|
68
|
+
if (s === "") return [];
|
|
69
|
+
const out = [];
|
|
70
|
+
for (const g of s.split(":")) {
|
|
71
|
+
if (g.length === 0 || g.length > 4) return null;
|
|
72
|
+
const n = Number.parseInt(g, 16);
|
|
73
|
+
if (Number.isNaN(n) || n < 0 || n > 65535) return null;
|
|
74
|
+
out.push(n);
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
};
|
|
78
|
+
if (parts.length === 1) {
|
|
79
|
+
const groups = parseGroups(parts[0] ?? "");
|
|
80
|
+
if (groups?.length !== 8) return null;
|
|
81
|
+
return groups;
|
|
82
|
+
}
|
|
83
|
+
const head = parseGroups(parts[0] ?? "");
|
|
84
|
+
const tail = parseGroups(parts[1] ?? "");
|
|
85
|
+
if (!head || !tail) return null;
|
|
86
|
+
const fill = 8 - head.length - tail.length;
|
|
87
|
+
if (fill < 0) return null;
|
|
88
|
+
return [...head, ...new Array(fill).fill(0), ...tail];
|
|
89
|
+
}
|
|
90
|
+
async function assertNotPrivateHost(hostname) {
|
|
91
|
+
const host = hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
|
|
92
|
+
if (host === "localhost" || host.endsWith(".localhost")) {
|
|
93
|
+
throw new Error("fetch: blocked localhost target");
|
|
94
|
+
}
|
|
95
|
+
const ipVersion = net.isIP(host);
|
|
96
|
+
if (ipVersion === 4) {
|
|
97
|
+
if (isPrivateIPv4(host)) {
|
|
98
|
+
throw new Error(`fetch: blocked private/loopback address "${host}"`);
|
|
99
|
+
}
|
|
100
|
+
} else if (ipVersion === 6) {
|
|
101
|
+
if (isPrivateIPv6(host)) {
|
|
102
|
+
throw new Error(`fetch: blocked private/loopback address "${host}"`);
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
try {
|
|
106
|
+
const records = await dns.lookup(host, { all: true });
|
|
107
|
+
for (const r of records) {
|
|
108
|
+
const bad = r.family === 4 ? isPrivateIPv4(r.address) : isPrivateIPv6(r.address);
|
|
109
|
+
if (bad) {
|
|
110
|
+
throw new Error(`fetch: resolved to private address ${r.address}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} catch (err) {
|
|
114
|
+
if (err instanceof Error && err.message.startsWith("fetch:")) throw err;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
1
119
|
// src/hooks/shell-executor.ts
|
|
2
120
|
import { spawn } from "node:child_process";
|
|
3
121
|
|
|
@@ -424,18 +542,26 @@ function parseHookOutcome(stdout) {
|
|
|
424
542
|
// src/hooks/http-executor.ts
|
|
425
543
|
var DEFAULT_TIMEOUT_MS2 = 5e3;
|
|
426
544
|
var MAX_OUTPUT_BYTES2 = 64 * 1024;
|
|
427
|
-
function isAllowedUrl(raw) {
|
|
545
|
+
async function isAllowedUrl(raw) {
|
|
546
|
+
let url;
|
|
428
547
|
try {
|
|
429
|
-
|
|
430
|
-
if (url.protocol === "https:") return true;
|
|
431
|
-
if (url.protocol !== "http:") return false;
|
|
432
|
-
return url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]";
|
|
548
|
+
url = new URL(raw);
|
|
433
549
|
} catch {
|
|
434
550
|
return false;
|
|
435
551
|
}
|
|
552
|
+
if (url.protocol === "https:") {
|
|
553
|
+
try {
|
|
554
|
+
await assertNotPrivateHost(url.hostname);
|
|
555
|
+
return true;
|
|
556
|
+
} catch {
|
|
557
|
+
return false;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
if (url.protocol !== "http:") return false;
|
|
561
|
+
return url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]";
|
|
436
562
|
}
|
|
437
563
|
async function runHttpHookDetailed(spec, input, logger, options = {}) {
|
|
438
|
-
if (!isAllowedUrl(spec.url)) {
|
|
564
|
+
if (!await isAllowedUrl(spec.url)) {
|
|
439
565
|
return {
|
|
440
566
|
outcome: null,
|
|
441
567
|
failure: {
|
|
@@ -650,7 +776,11 @@ function hookMatcherMatches(matcher, toolName) {
|
|
|
650
776
|
|
|
651
777
|
// src/hooks/runner.ts
|
|
652
778
|
var DEFAULT_TIMEOUT_MS3 = 5e3;
|
|
779
|
+
var DEFAULT_BACKGROUND_TIMEOUT_MS = 6e4;
|
|
653
780
|
var MAX_TIMEOUT_MS = 10 * 6e4;
|
|
781
|
+
function hookTimeoutMs(entry) {
|
|
782
|
+
return entry.timeoutMs ?? (entry.background ? DEFAULT_BACKGROUND_TIMEOUT_MS : DEFAULT_TIMEOUT_MS3);
|
|
783
|
+
}
|
|
654
784
|
function normalizePreToolOutcome(outcome) {
|
|
655
785
|
if ("action" in outcome) return outcome;
|
|
656
786
|
if (outcome.decision === "block") {
|
|
@@ -835,12 +965,12 @@ var HookRunner = class {
|
|
|
835
965
|
result = await this.invokeInProcess(entry, payload, env);
|
|
836
966
|
} else {
|
|
837
967
|
result = entry.kind === "shell" ? await runShellHookDetailed(
|
|
838
|
-
{ command: entry.command, timeoutMs: entry
|
|
968
|
+
{ command: entry.command, timeoutMs: hookTimeoutMs(entry) },
|
|
839
969
|
payload,
|
|
840
970
|
this.opts.logger,
|
|
841
971
|
{ signal: env.signal }
|
|
842
972
|
) : await runHttpHookDetailed(
|
|
843
|
-
{ url: entry.url, headers: entry.headers, timeoutMs: entry
|
|
973
|
+
{ url: entry.url, headers: entry.headers, timeoutMs: hookTimeoutMs(entry) },
|
|
844
974
|
payload,
|
|
845
975
|
this.opts.logger,
|
|
846
976
|
{ signal: env.signal }
|
|
@@ -857,12 +987,12 @@ var HookRunner = class {
|
|
|
857
987
|
}
|
|
858
988
|
if (!result.failure) return result.outcome;
|
|
859
989
|
this.opts.logger?.warn?.(
|
|
860
|
-
`${payload.event} hook "${entry.name}" failed (${result.failure.kind}): ${result.failure.message}`
|
|
990
|
+
`${payload.event} ${entry.background ? "background " : ""}hook "${entry.name}" failed (${result.failure.kind}): ${result.failure.message}`
|
|
861
991
|
);
|
|
862
992
|
return this.failureOutcome(entry, payload, result.failure);
|
|
863
993
|
}
|
|
864
994
|
async invokeInProcess(entry, payload, env) {
|
|
865
|
-
const timeoutMs = Math.max(1, Math.min(entry
|
|
995
|
+
const timeoutMs = Math.max(1, Math.min(hookTimeoutMs(entry), MAX_TIMEOUT_MS));
|
|
866
996
|
const controller = new AbortController();
|
|
867
997
|
let timedOut = false;
|
|
868
998
|
const onParentAbort = () => controller.abort(env.signal?.reason);
|
package/dist/hq/exposure.d.ts
CHANGED
|
@@ -16,17 +16,6 @@
|
|
|
16
16
|
* ones. Nothing here changes the default bind — it guards the case where the
|
|
17
17
|
* default silently turns a local-only setup into a public one.
|
|
18
18
|
*/
|
|
19
|
-
/**
|
|
20
|
-
* The bind the HQ *CLI* surfaces choose on purpose: HQ is the deliberate
|
|
21
|
-
* cross-machine surface, so `wstack hq` and the launch menu reach every
|
|
22
|
-
* interface without an extra flag.
|
|
23
|
-
*
|
|
24
|
-
* This deliberately does not live in `startHqServer`'s default. A caller
|
|
25
|
-
* that never mentions a host — a test, an embedder — has not asked to be
|
|
26
|
-
* published to its network, and that library default stays loopback. Keep
|
|
27
|
-
* the two apart: collapsing them is what turns "HQ is reachable from my
|
|
28
|
-
* phone" into "every embedder silently binds 0.0.0.0".
|
|
29
|
-
*/
|
|
30
19
|
export declare const HQ_CLI_DEFAULT_HOST = "0.0.0.0";
|
|
31
20
|
/**
|
|
32
21
|
* Thrown when the bind host and the auth configuration combine into an
|
package/dist/hq/index.js
CHANGED
|
@@ -167,7 +167,8 @@ var KNOWN_HQ_CLIENT_FRAME_TYPES = /* @__PURE__ */ new Set([
|
|
|
167
167
|
"client.event",
|
|
168
168
|
"client.command_poll",
|
|
169
169
|
"client.command_ack",
|
|
170
|
-
"client.resume"
|
|
170
|
+
"client.resume",
|
|
171
|
+
"client.event_poll"
|
|
171
172
|
]);
|
|
172
173
|
var HQ_CLIENT_KINDS = /* @__PURE__ */ new Set([
|
|
173
174
|
"tui",
|
|
@@ -491,6 +492,9 @@ function isHqSubagentSummary(x) {
|
|
|
491
492
|
const v = x;
|
|
492
493
|
return typeof v.subagentId === "string" && typeof v.status === "string" && HQ_FLEET_SUBAGENT_STATUS.has(v.status);
|
|
493
494
|
}
|
|
495
|
+
function isOptionalFiniteNumber(x) {
|
|
496
|
+
return x === void 0 || typeof x === "number" && Number.isFinite(x);
|
|
497
|
+
}
|
|
494
498
|
function isHqFleetSnapshotPayload(x) {
|
|
495
499
|
if (typeof x !== "object" || x === null) return false;
|
|
496
500
|
const v = x;
|
|
@@ -500,6 +504,13 @@ function isHqFleetSnapshotPayload(x) {
|
|
|
500
504
|
for (const s of v.subagents) {
|
|
501
505
|
if (!isHqSubagentSummary(s)) return false;
|
|
502
506
|
}
|
|
507
|
+
if (!isOptionalFiniteNumber(v.maxConcurrent)) return false;
|
|
508
|
+
if (!isOptionalFiniteNumber(v.maxSpawns)) return false;
|
|
509
|
+
if (!isOptionalFiniteNumber(v.usedSpawns)) return false;
|
|
510
|
+
if (!isOptionalFiniteNumber(v.remainingSpawns)) return false;
|
|
511
|
+
if (!isOptionalFiniteNumber(v.checkpointMaxSpawns)) return false;
|
|
512
|
+
if (v.effectiveSource !== void 0 && typeof v.effectiveSource !== "string") return false;
|
|
513
|
+
if (v.ceilingMismatch !== void 0 && typeof v.ceilingMismatch !== "boolean") return false;
|
|
503
514
|
return true;
|
|
504
515
|
}
|
|
505
516
|
function isHqFleetEventPayload(x) {
|
|
@@ -2102,7 +2113,9 @@ function safeProfileName(name) {
|
|
|
2102
2113
|
function activeProfileName(globalRoot) {
|
|
2103
2114
|
try {
|
|
2104
2115
|
const parsed = JSON.parse(fs2.readFileSync(path2.join(globalRoot, "config.json"), "utf8"));
|
|
2105
|
-
return safeProfileName(
|
|
2116
|
+
return safeProfileName(
|
|
2117
|
+
typeof parsed.activeProfile === "string" ? parsed.activeProfile : void 0
|
|
2118
|
+
);
|
|
2106
2119
|
} catch {
|
|
2107
2120
|
return "default";
|
|
2108
2121
|
}
|
|
@@ -2182,6 +2195,7 @@ function resolveWstackPaths(opts) {
|
|
|
2182
2195
|
projectPlan: path2.join(projectDir, "plan.json"),
|
|
2183
2196
|
projectAutophase: path2.join(projectDir, "autophase"),
|
|
2184
2197
|
projectSddBoards: path2.join(projectDir, "sdd-boards"),
|
|
2198
|
+
projectRequirementIntakes: path2.join(projectDir, "requirement-intakes"),
|
|
2185
2199
|
syncConfig: path2.join(profileDir, "sync.json"),
|
|
2186
2200
|
configHistoryDir: path2.join(globalRoot, "config-history"),
|
|
2187
2201
|
projectStatus: (projectHash2) => path2.join(globalRoot, "projects", projectHash2, "status.json")
|
|
@@ -2570,9 +2584,6 @@ function watchHqAuthFile(dataDir, onChange, opts = {}) {
|
|
|
2570
2584
|
}
|
|
2571
2585
|
}
|
|
2572
2586
|
});
|
|
2573
|
-
watcher.on("error", (err) => {
|
|
2574
|
-
opts.warn?.(`HQ auth watcher error: ${err.message}`);
|
|
2575
|
-
});
|
|
2576
2587
|
return {
|
|
2577
2588
|
close: () => {
|
|
2578
2589
|
closed = true;
|
|
@@ -3309,7 +3320,14 @@ function startFleetTelemetryBridge(opts) {
|
|
|
3309
3320
|
completedTasks: stats.completed,
|
|
3310
3321
|
failedTasks: stats.stopped,
|
|
3311
3322
|
...stats.totalCostUsd !== void 0 ? { totalCostUsd: stats.totalCostUsd } : {},
|
|
3312
|
-
subagents
|
|
3323
|
+
subagents,
|
|
3324
|
+
...stats.maxConcurrent !== void 0 ? { maxConcurrent: stats.maxConcurrent } : {},
|
|
3325
|
+
...stats.maxSpawns !== void 0 ? { maxSpawns: stats.maxSpawns } : {},
|
|
3326
|
+
...stats.usedSpawns !== void 0 ? { usedSpawns: stats.usedSpawns } : {},
|
|
3327
|
+
...stats.remainingSpawns !== void 0 ? { remainingSpawns: stats.remainingSpawns } : {},
|
|
3328
|
+
...stats.effectiveSource !== void 0 ? { effectiveSource: stats.effectiveSource } : {},
|
|
3329
|
+
...stats.checkpointMaxSpawns !== void 0 ? { checkpointMaxSpawns: stats.checkpointMaxSpawns } : {},
|
|
3330
|
+
...stats.ceilingMismatch ? { ceilingMismatch: true } : {}
|
|
3313
3331
|
};
|
|
3314
3332
|
}
|
|
3315
3333
|
const off = events.on("coordinator.stats", (stats) => {
|
|
@@ -4970,6 +4988,7 @@ var HqBootstrapCodeStore = class {
|
|
|
4970
4988
|
};
|
|
4971
4989
|
|
|
4972
4990
|
// src/hq/exposure.ts
|
|
4991
|
+
import { isIP } from "node:net";
|
|
4973
4992
|
var HQ_CLI_DEFAULT_HOST = "0.0.0.0";
|
|
4974
4993
|
var HqInsecureExposureError = class extends Error {
|
|
4975
4994
|
constructor(message) {
|
|
@@ -4979,8 +4998,15 @@ var HqInsecureExposureError = class extends Error {
|
|
|
4979
4998
|
};
|
|
4980
4999
|
function isLoopbackHost(host) {
|
|
4981
5000
|
const normalized = host.trim().toLowerCase().replace(/^\[/, "").replace(/\]$/, "");
|
|
4982
|
-
if (normalized === "localhost"
|
|
4983
|
-
|
|
5001
|
+
if (normalized === "localhost") return true;
|
|
5002
|
+
const family = isIP(normalized);
|
|
5003
|
+
if (family === 4) {
|
|
5004
|
+
return normalized.startsWith("127.");
|
|
5005
|
+
}
|
|
5006
|
+
if (family === 6) {
|
|
5007
|
+
return normalized === "::1" || /^::ffff:127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(normalized);
|
|
5008
|
+
}
|
|
5009
|
+
return false;
|
|
4984
5010
|
}
|
|
4985
5011
|
function isOpenMode(input) {
|
|
4986
5012
|
return !input.hasBrowserTokens && !input.hasPassword;
|
|
@@ -82,5 +82,18 @@ export interface HqClientResumeMessage {
|
|
|
82
82
|
clientId?: string;
|
|
83
83
|
projectId?: string;
|
|
84
84
|
}
|
|
85
|
-
export type HqClientMessage = HqClientHelloMessage | HqClientEventMessage | HqClientCommandPollMessage | HqClientCommandAckMessage | HqClientResumeMessage;
|
|
85
|
+
export type HqClientMessage = HqClientHelloMessage | HqClientEventMessage | HqClientCommandPollMessage | HqClientCommandAckMessage | HqClientResumeMessage | HqClientEventPollMessage;
|
|
86
|
+
/**
|
|
87
|
+
* Proactive gap-fill: a client can request missed envelopes by `clientId`
|
|
88
|
+
* and `afterSeq` without waiting for the push path. The server replies with
|
|
89
|
+
* `hq.resume_gap` (bounded list) or `hq.snapshot` when the gap is too large.
|
|
90
|
+
* See `docs/plans/hq-evolution-2026-08.md` §2.2.
|
|
91
|
+
*/
|
|
92
|
+
export interface HqClientEventPollMessage {
|
|
93
|
+
type: 'client.event_poll';
|
|
94
|
+
clientId: string;
|
|
95
|
+
projectId: string;
|
|
96
|
+
afterSeq: number;
|
|
97
|
+
limit?: number;
|
|
98
|
+
}
|
|
86
99
|
//# sourceMappingURL=client.d.ts.map
|
|
@@ -7,6 +7,19 @@ export interface HqFleetSnapshotPayload {
|
|
|
7
7
|
failedTasks: number;
|
|
8
8
|
totalCostUsd?: number;
|
|
9
9
|
subagents: readonly HqSubagentSummary[];
|
|
10
|
+
/** Concurrent-subagent ceiling (maxConcurrent). */
|
|
11
|
+
maxConcurrent?: number | undefined;
|
|
12
|
+
/** Lifetime spawn ceiling (maxSpawns). */
|
|
13
|
+
maxSpawns?: number | undefined;
|
|
14
|
+
/** Cumulative spawns consumed this director run. */
|
|
15
|
+
usedSpawns?: number | undefined;
|
|
16
|
+
/** maxSpawns − usedSpawns (clamped ≥ 0). */
|
|
17
|
+
remainingSpawns?: number | undefined;
|
|
18
|
+
/** Winning config source labels, e.g. "maxConcurrent=profile, maxSpawns=env". */
|
|
19
|
+
effectiveSource?: string | undefined;
|
|
20
|
+
/** Historical checkpoint maxSpawns when it disagreed with the live ceiling. */
|
|
21
|
+
checkpointMaxSpawns?: number | undefined;
|
|
22
|
+
ceilingMismatch?: boolean | undefined;
|
|
10
23
|
}
|
|
11
24
|
export interface HqFleetEventPayload {
|
|
12
25
|
runId: string;
|
|
@@ -62,9 +75,18 @@ export interface HqFleetSummary {
|
|
|
62
75
|
failedTasks: number;
|
|
63
76
|
totalCostUsd?: number;
|
|
64
77
|
lastActivityAt: string;
|
|
78
|
+
maxConcurrent?: number | undefined;
|
|
79
|
+
maxSpawns?: number | undefined;
|
|
80
|
+
usedSpawns?: number | undefined;
|
|
81
|
+
remainingSpawns?: number | undefined;
|
|
82
|
+
effectiveSource?: string | undefined;
|
|
83
|
+
checkpointMaxSpawns?: number | undefined;
|
|
84
|
+
ceilingMismatch?: boolean | undefined;
|
|
65
85
|
}
|
|
66
86
|
export interface HqQueuedCommand {
|
|
67
87
|
commandId: string;
|
|
88
|
+
/** Idempotency key deduplicates commands across leader handoffs (§2.2). */
|
|
89
|
+
idempotencyKey?: string;
|
|
68
90
|
type: string;
|
|
69
91
|
createdAt: string;
|
|
70
92
|
payload: unknown;
|
package/dist/hq/protocol.js
CHANGED
|
@@ -167,7 +167,8 @@ var KNOWN_HQ_CLIENT_FRAME_TYPES = /* @__PURE__ */ new Set([
|
|
|
167
167
|
"client.event",
|
|
168
168
|
"client.command_poll",
|
|
169
169
|
"client.command_ack",
|
|
170
|
-
"client.resume"
|
|
170
|
+
"client.resume",
|
|
171
|
+
"client.event_poll"
|
|
171
172
|
]);
|
|
172
173
|
var HQ_CLIENT_KINDS = /* @__PURE__ */ new Set([
|
|
173
174
|
"tui",
|
|
@@ -491,6 +492,9 @@ function isHqSubagentSummary(x) {
|
|
|
491
492
|
const v = x;
|
|
492
493
|
return typeof v.subagentId === "string" && typeof v.status === "string" && HQ_FLEET_SUBAGENT_STATUS.has(v.status);
|
|
493
494
|
}
|
|
495
|
+
function isOptionalFiniteNumber(x) {
|
|
496
|
+
return x === void 0 || typeof x === "number" && Number.isFinite(x);
|
|
497
|
+
}
|
|
494
498
|
function isHqFleetSnapshotPayload(x) {
|
|
495
499
|
if (typeof x !== "object" || x === null) return false;
|
|
496
500
|
const v = x;
|
|
@@ -500,6 +504,13 @@ function isHqFleetSnapshotPayload(x) {
|
|
|
500
504
|
for (const s of v.subagents) {
|
|
501
505
|
if (!isHqSubagentSummary(s)) return false;
|
|
502
506
|
}
|
|
507
|
+
if (!isOptionalFiniteNumber(v.maxConcurrent)) return false;
|
|
508
|
+
if (!isOptionalFiniteNumber(v.maxSpawns)) return false;
|
|
509
|
+
if (!isOptionalFiniteNumber(v.usedSpawns)) return false;
|
|
510
|
+
if (!isOptionalFiniteNumber(v.remainingSpawns)) return false;
|
|
511
|
+
if (!isOptionalFiniteNumber(v.checkpointMaxSpawns)) return false;
|
|
512
|
+
if (v.effectiveSource !== void 0 && typeof v.effectiveSource !== "string") return false;
|
|
513
|
+
if (v.ceilingMismatch !== void 0 && typeof v.ceilingMismatch !== "boolean") return false;
|
|
503
514
|
return true;
|
|
504
515
|
}
|
|
505
516
|
function isHqFleetEventPayload(x) {
|
package/dist/index.d.ts
CHANGED
|
@@ -54,6 +54,7 @@ export type { FallbackChain, FallbackChainEntry, ProviderHealth, } from './core/
|
|
|
54
54
|
export { FallbackProfileManager } from './core/fallback-profile-manager.js';
|
|
55
55
|
export { InputBuilder, type InputBuilderEvent, type InputBuilderOptions, } from './core/input-builder.js';
|
|
56
56
|
export { type InstructionBundle, type InstructionBundlePaths, loadInstructionBundle, mergeInstructionBundle, type SystemInstructionBundle, type SystemInstructionVariant, } from './core/instruction-bundle.js';
|
|
57
|
+
export { type InstructionTemplateContext, renderInstructionLayer, } from './core/instruction-template.js';
|
|
57
58
|
export { buildMailboxBlock, buildMailboxBtwAwarenessBlock, createMailboxChecker, injectPendingMailboxMessages, type MailboxDeliveryMode, type MailboxLoopOptions, } from './core/mailbox-loop.js';
|
|
58
59
|
export { evaluateModelCalendar, logicalCalendarTarget, type ModelBlackoutRule, type ModelCalendarDecision, } from './core/model-availability-calendar.js';
|
|
59
60
|
export { runProviderWithRetry } from './core/provider-runner.js';
|
|
@@ -69,7 +70,7 @@ export { type BrainApplyResult, type BrainConfigPatch, type BrainConfigSnapshot,
|
|
|
69
70
|
export { buildLosslessDigest, buildSmartDigest, type ContentScore, type EliseResult, eliseOldToolResults, estimateMessages, extractText, findPreserveStart, hasTextContent, scoreMessage, } from './execution/compaction-core.js';
|
|
70
71
|
export { COUNCIL_REFUSE_OPTION_ID, type CouncilBrainOptions, type CouncilVoter, createCouncilBrainArbiter, } from './execution/council-brain.js';
|
|
71
72
|
export { COUNCIL_REFUSAL_OPTION_ID, CouncilOrchestrator, type CouncilOrchestratorOptions, DEFAULT_COUNCIL_MAX_CONCURRENCY, MAX_COUNCIL_CONCURRENCY, } from './execution/council-orchestrator.js';
|
|
72
|
-
export { BUILTIN_COUNCIL_PERSONAS, CouncilPersonaRegistry, createCouncilPersonaRegistry, DEFAULT_COUNCIL_PERSONA_REGISTRY, } from './execution/council-personas.js';
|
|
73
|
+
export { BUILTIN_COUNCIL_PERSONA_IDS, BUILTIN_COUNCIL_PERSONAS, CouncilPersonaRegistry, createCouncilPersonaRegistry, DEFAULT_COUNCIL_PERSONA_REGISTRY, } from './execution/council-personas.js';
|
|
73
74
|
export { BUILTIN_COUNCIL_PROFILES, CouncilProfileRegistry, createCouncilProfileRegistry, DEFAULT_COUNCIL_APPROVAL_FRACTION, DEFAULT_COUNCIL_JUDGE_MAX_TOKENS, DEFAULT_COUNCIL_OVERALL_TIMEOUT_MS, DEFAULT_COUNCIL_PER_CALL_TIMEOUT_MS, DEFAULT_COUNCIL_PROFILE_REGISTRY, DEFAULT_COUNCIL_QUORUM_FRACTION, DEFAULT_COUNCIL_VOTER_MAX_TOKENS, normalizeCouncilProfile, resolveCouncilProfile, } from './execution/council-profiles.js';
|
|
74
75
|
export { buildCouncilJudgeSystemPrompt, buildCouncilJudgeUserPrompt, buildCouncilQuestionPrompt, buildCouncilVoterSystemPrompt, buildCouncilVoterUserPrompt, COUNCIL_JUDGE_PROMPT_PATH, COUNCIL_VOTER_PROMPT_PATH, } from './execution/council-prompts.js';
|
|
75
76
|
export { type CouncilResolution, type CouncilResolutionInput, type CouncilResolutionSeat, type CouncilResolutionVote, resolveCouncilVotes, } from './execution/council-resolution.js';
|