@wrongstack/core 0.298.3 → 0.299.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/index.js +7 -4
- package/dist/core/index.js +62 -8
- package/dist/defaults/index.js +120 -18
- package/dist/design/index.js +4 -1
- package/dist/execution/council-brain.d.ts +6 -2
- package/dist/execution/council-personas.d.ts +10 -0
- package/dist/execution/index.d.ts +1 -1
- package/dist/execution/index.js +57 -7
- package/dist/goal/index.js +4 -1
- package/dist/hooks/index.js +132 -6
- package/dist/hq/exposure.d.ts +0 -11
- package/dist/hq/index.js +16 -4
- package/dist/hq/protocol/client.d.ts +14 -1
- package/dist/hq/protocol/fleet.d.ts +2 -0
- package/dist/hq/protocol.js +2 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +207 -47
- package/dist/infrastructure/index.js +50 -2
- package/dist/infrastructure/mcp-servers.d.ts +35 -0
- package/dist/kernel/events/provider-events.d.ts +7 -1
- package/dist/kernel/events/sdd-events.d.ts +2 -0
- package/dist/plugin/index.js +4 -1
- package/dist/storage/index.js +13 -1
- package/dist/tools/council-tool.d.ts +1 -1
- package/dist/tools/index.js +61 -10
- package/dist/types/config/skills-fleet-brain.d.ts +4 -2
- package/dist/types/config/tools.d.ts +99 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.js +3 -3
- package/dist/types/one-shot-llm.d.ts +22 -3
- 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 +43 -21
- package/dist/worktree/worktree-manager.d.ts +16 -10
- package/instructions/coordination/subagent-baseline.md +8 -0
- package/instructions/system-lite.md +2 -0
- package/instructions/system-pro.md +14 -10
- package/instructions/system.md +11 -7
- package/package.json +3 -3
package/dist/execution/index.js
CHANGED
|
@@ -4193,7 +4193,7 @@ function truncate(s, max) {
|
|
|
4193
4193
|
var QUOTA_EXHAUSTED_RE = /(?:insufficient|exhausted|depleted|exceeded|no|not enough)[-_\s]*(?:quota|credit|balance)|(?:quota|credit|balance)(?:\s+(?:has|have))?(?:\s+been)?[-_\s]*(?:exhausted|depleted|exceeded|insufficient)|billing[_\s-]*(?:hard[_\s-]*)?limit|payment required|spending limit|plan limit|usage[-_\s]*limit[-_\s]*(?:reached|exceeded)/i;
|
|
4194
4194
|
|
|
4195
4195
|
// src/types/provider.ts
|
|
4196
|
-
var CONTEXT_OVERFLOW_RE = /context.length|context.window|maximum context|max.*tokens?.*exceeded|prompt
|
|
4196
|
+
var CONTEXT_OVERFLOW_RE = /context.length|context.window|maximum context|max.*tokens?.*exceeded|(?:prompt|request|input|messages?).{0,12}too (?:large|long)|exceeds the context|\btokens\b.*exceed|too many tokens|reduce the length|resulted in \d+ tokens|context_length_exceeded/i;
|
|
4197
4197
|
var CONTENT_FILTER_RE = /content.(filter|policy|moderation)|safety (system|filter)/i;
|
|
4198
4198
|
var RATE_LIMIT_EXCEEDED_RE = /rate[-_\s]*limit[-_\s]*exceeded/i;
|
|
4199
4199
|
function classifyProviderError(status, body, message) {
|
|
@@ -4203,7 +4203,7 @@ function classifyProviderError(status, body, message) {
|
|
|
4203
4203
|
if (status === 408) return "timeout";
|
|
4204
4204
|
if (status === 599) return "stream_hang";
|
|
4205
4205
|
if (status === 402 || QUOTA_EXHAUSTED_RE.test(text)) return "quota_exhausted";
|
|
4206
|
-
if (status === 429 && body?.message && RATE_LIMIT_EXCEEDED_RE.test(body.message)) {
|
|
4206
|
+
if (status === 429 && body?.message && body.type !== "rate_limit_exceeded" && RATE_LIMIT_EXCEEDED_RE.test(body.message)) {
|
|
4207
4207
|
return "quota_exhausted";
|
|
4208
4208
|
}
|
|
4209
4209
|
if (type === "rate_limit_error" || status === 429) return "rate_limit";
|
|
@@ -4280,7 +4280,7 @@ var ProviderError = class extends WrongStackError {
|
|
|
4280
4280
|
const e = err;
|
|
4281
4281
|
const name = e.name;
|
|
4282
4282
|
if (typeof name !== "string" || !name.endsWith("Error")) return false;
|
|
4283
|
-
return typeof e.status === "number" && typeof e.retryable === "boolean" && typeof e.kind === "string";
|
|
4283
|
+
return typeof e.status === "number" && typeof e.retryable === "boolean" && typeof e.kind === "string" && typeof e.describe === "function";
|
|
4284
4284
|
}
|
|
4285
4285
|
constructor(message, status, retryable, providerId, opts = {}) {
|
|
4286
4286
|
const kind = opts.kind ?? classifyProviderError(status, opts.body, message);
|
|
@@ -4785,6 +4785,9 @@ var BUILTIN_COUNCIL_PERSONAS = Object.freeze([
|
|
|
4785
4785
|
tags: ["users", "usability", "accessibility"]
|
|
4786
4786
|
})
|
|
4787
4787
|
]);
|
|
4788
|
+
var BUILTIN_COUNCIL_PERSONA_IDS = Object.freeze(
|
|
4789
|
+
BUILTIN_COUNCIL_PERSONAS.map((persona) => persona.id)
|
|
4790
|
+
);
|
|
4788
4791
|
var CouncilPersonaRegistry = class _CouncilPersonaRegistry {
|
|
4789
4792
|
byId;
|
|
4790
4793
|
constructor(personas = []) {
|
|
@@ -5906,6 +5909,44 @@ function errorMessage(error) {
|
|
|
5906
5909
|
}
|
|
5907
5910
|
|
|
5908
5911
|
// src/execution/council-brain.ts
|
|
5912
|
+
var MAX_CUSTOM_PERSONA_ID_CHARS = 48;
|
|
5913
|
+
function customPersonaId(raw, index) {
|
|
5914
|
+
const slug = raw.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, MAX_CUSTOM_PERSONA_ID_CHARS).replace(/-+$/g, "");
|
|
5915
|
+
return slug ? `custom-${slug}` : `custom-${index + 1}`;
|
|
5916
|
+
}
|
|
5917
|
+
function resolvePersonaRegistry(voters) {
|
|
5918
|
+
let registry = DEFAULT_COUNCIL_PERSONA_REGISTRY;
|
|
5919
|
+
const personaIds = [];
|
|
5920
|
+
const byRawText = /* @__PURE__ */ new Map();
|
|
5921
|
+
voters.forEach((voter, index) => {
|
|
5922
|
+
const raw = voter.persona?.trim();
|
|
5923
|
+
if (!raw) {
|
|
5924
|
+
personaIds.push("executor");
|
|
5925
|
+
return;
|
|
5926
|
+
}
|
|
5927
|
+
if (registry.has(raw)) {
|
|
5928
|
+
personaIds.push(raw);
|
|
5929
|
+
return;
|
|
5930
|
+
}
|
|
5931
|
+
const existing = byRawText.get(raw);
|
|
5932
|
+
if (existing) {
|
|
5933
|
+
personaIds.push(existing);
|
|
5934
|
+
return;
|
|
5935
|
+
}
|
|
5936
|
+
let id = customPersonaId(raw, index);
|
|
5937
|
+
let suffix = 2;
|
|
5938
|
+
while (registry.has(id)) id = `${customPersonaId(raw, index)}-${suffix++}`;
|
|
5939
|
+
registry = registry.with({
|
|
5940
|
+
id,
|
|
5941
|
+
name: raw.length <= 40 ? raw : `${raw.slice(0, 39)}\u2026`,
|
|
5942
|
+
description: "Custom decision lens supplied by the host configuration.",
|
|
5943
|
+
instruction: raw
|
|
5944
|
+
});
|
|
5945
|
+
byRawText.set(raw, id);
|
|
5946
|
+
personaIds.push(id);
|
|
5947
|
+
});
|
|
5948
|
+
return { registry, personaIds };
|
|
5949
|
+
}
|
|
5909
5950
|
function createCouncilBrainArbiter(opts) {
|
|
5910
5951
|
if (opts.voters.length === 0) {
|
|
5911
5952
|
throw new Error("createCouncilBrainArbiter: at least one voter is required.");
|
|
@@ -5971,14 +6012,15 @@ function createCouncilBrainArbiter(opts) {
|
|
|
5971
6012
|
}
|
|
5972
6013
|
return makeSeatCallerForVoter(voter);
|
|
5973
6014
|
};
|
|
6015
|
+
const { registry: personas, personaIds } = resolvePersonaRegistry(opts.voters);
|
|
5974
6016
|
const seats = opts.voters.map((voter, i) => ({
|
|
5975
6017
|
id: `voter-${i}`,
|
|
5976
6018
|
label: voter.label ?? voter.model,
|
|
5977
|
-
persona:
|
|
6019
|
+
persona: personaIds[i] ?? "executor",
|
|
5978
6020
|
target: {
|
|
5979
6021
|
providerId: voter.provider.id,
|
|
5980
6022
|
model: voter.model,
|
|
5981
|
-
role:
|
|
6023
|
+
role: personaIds[i] ?? "executor"
|
|
5982
6024
|
},
|
|
5983
6025
|
weight: voter.weight,
|
|
5984
6026
|
veto: voter.veto
|
|
@@ -6000,6 +6042,9 @@ function createCouncilBrainArbiter(opts) {
|
|
|
6000
6042
|
};
|
|
6001
6043
|
const orchestrator = new CouncilOrchestrator({
|
|
6002
6044
|
defaultProfile: "brain-council-adapter",
|
|
6045
|
+
// Carries the ad-hoc lenses registered above; without it the orchestrator
|
|
6046
|
+
// falls back to the built-in registry and rejects every custom persona.
|
|
6047
|
+
personas,
|
|
6003
6048
|
// Previously never passed, so the panel was pinned at the orchestrator's
|
|
6004
6049
|
// default of 3 concurrent seats no matter how many voters were configured.
|
|
6005
6050
|
...opts.maxConcurrency !== void 0 ? { maxConcurrency: opts.maxConcurrency } : {},
|
|
@@ -7820,7 +7865,9 @@ function safeProfileName(name) {
|
|
|
7820
7865
|
function activeProfileName(globalRoot) {
|
|
7821
7866
|
try {
|
|
7822
7867
|
const parsed = JSON.parse(fs.readFileSync(path4.join(globalRoot, "config.json"), "utf8"));
|
|
7823
|
-
return safeProfileName(
|
|
7868
|
+
return safeProfileName(
|
|
7869
|
+
typeof parsed.activeProfile === "string" ? parsed.activeProfile : void 0
|
|
7870
|
+
);
|
|
7824
7871
|
} catch {
|
|
7825
7872
|
return "default";
|
|
7826
7873
|
}
|
|
@@ -7900,6 +7947,7 @@ function resolveWstackPaths(opts) {
|
|
|
7900
7947
|
projectPlan: path4.join(projectDir, "plan.json"),
|
|
7901
7948
|
projectAutophase: path4.join(projectDir, "autophase"),
|
|
7902
7949
|
projectSddBoards: path4.join(projectDir, "sdd-boards"),
|
|
7950
|
+
projectRequirementIntakes: path4.join(projectDir, "requirement-intakes"),
|
|
7903
7951
|
syncConfig: path4.join(profileDir, "sync.json"),
|
|
7904
7952
|
configHistoryDir: path4.join(globalRoot, "config-history"),
|
|
7905
7953
|
projectStatus: (projectHash2) => path4.join(globalRoot, "projects", projectHash2, "status.json")
|
|
@@ -20469,7 +20517,8 @@ var OneShotOrchestrator = class {
|
|
|
20469
20517
|
resolveTarget(input, config) {
|
|
20470
20518
|
if (input.role && this.opts.modelRouter) {
|
|
20471
20519
|
const pick = this.opts.modelRouter.pickForTask(input.role, "");
|
|
20472
|
-
|
|
20520
|
+
const hasExplicitTarget = Boolean(input.providerId || input.model);
|
|
20521
|
+
if (pick && (pick.fromMatrix === true || !hasExplicitTarget)) {
|
|
20473
20522
|
return { providerId: pick.provider, model: pick.model };
|
|
20474
20523
|
}
|
|
20475
20524
|
}
|
|
@@ -21142,6 +21191,7 @@ export {
|
|
|
21142
21191
|
AutonomousRunner,
|
|
21143
21192
|
BRAIN_EVALUATION_CASE_VERSION,
|
|
21144
21193
|
BUILTIN_COUNCIL_PERSONAS,
|
|
21194
|
+
BUILTIN_COUNCIL_PERSONA_IDS,
|
|
21145
21195
|
BUILTIN_COUNCIL_PROFILES,
|
|
21146
21196
|
COUNCIL_JUDGE_PROMPT_PATH,
|
|
21147
21197
|
COUNCIL_REFUSAL_OPTION_ID,
|
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: {
|
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",
|
|
@@ -2102,7 +2103,9 @@ function safeProfileName(name) {
|
|
|
2102
2103
|
function activeProfileName(globalRoot) {
|
|
2103
2104
|
try {
|
|
2104
2105
|
const parsed = JSON.parse(fs2.readFileSync(path2.join(globalRoot, "config.json"), "utf8"));
|
|
2105
|
-
return safeProfileName(
|
|
2106
|
+
return safeProfileName(
|
|
2107
|
+
typeof parsed.activeProfile === "string" ? parsed.activeProfile : void 0
|
|
2108
|
+
);
|
|
2106
2109
|
} catch {
|
|
2107
2110
|
return "default";
|
|
2108
2111
|
}
|
|
@@ -2182,6 +2185,7 @@ function resolveWstackPaths(opts) {
|
|
|
2182
2185
|
projectPlan: path2.join(projectDir, "plan.json"),
|
|
2183
2186
|
projectAutophase: path2.join(projectDir, "autophase"),
|
|
2184
2187
|
projectSddBoards: path2.join(projectDir, "sdd-boards"),
|
|
2188
|
+
projectRequirementIntakes: path2.join(projectDir, "requirement-intakes"),
|
|
2185
2189
|
syncConfig: path2.join(profileDir, "sync.json"),
|
|
2186
2190
|
configHistoryDir: path2.join(globalRoot, "config-history"),
|
|
2187
2191
|
projectStatus: (projectHash2) => path2.join(globalRoot, "projects", projectHash2, "status.json")
|
|
@@ -4970,6 +4974,7 @@ var HqBootstrapCodeStore = class {
|
|
|
4970
4974
|
};
|
|
4971
4975
|
|
|
4972
4976
|
// src/hq/exposure.ts
|
|
4977
|
+
import { isIP } from "node:net";
|
|
4973
4978
|
var HQ_CLI_DEFAULT_HOST = "0.0.0.0";
|
|
4974
4979
|
var HqInsecureExposureError = class extends Error {
|
|
4975
4980
|
constructor(message) {
|
|
@@ -4979,8 +4984,15 @@ var HqInsecureExposureError = class extends Error {
|
|
|
4979
4984
|
};
|
|
4980
4985
|
function isLoopbackHost(host) {
|
|
4981
4986
|
const normalized = host.trim().toLowerCase().replace(/^\[/, "").replace(/\]$/, "");
|
|
4982
|
-
if (normalized === "localhost"
|
|
4983
|
-
|
|
4987
|
+
if (normalized === "localhost") return true;
|
|
4988
|
+
const family = isIP(normalized);
|
|
4989
|
+
if (family === 4) {
|
|
4990
|
+
return normalized.startsWith("127.");
|
|
4991
|
+
}
|
|
4992
|
+
if (family === 6) {
|
|
4993
|
+
return normalized === "::1" || /^::ffff:127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(normalized);
|
|
4994
|
+
}
|
|
4995
|
+
return false;
|
|
4984
4996
|
}
|
|
4985
4997
|
function isOpenMode(input) {
|
|
4986
4998
|
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
|
|
@@ -65,6 +65,8 @@ export interface HqFleetSummary {
|
|
|
65
65
|
}
|
|
66
66
|
export interface HqQueuedCommand {
|
|
67
67
|
commandId: string;
|
|
68
|
+
/** Idempotency key deduplicates commands across leader handoffs (§2.2). */
|
|
69
|
+
idempotencyKey?: string;
|
|
68
70
|
type: string;
|
|
69
71
|
createdAt: string;
|
|
70
72
|
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",
|
package/dist/index.d.ts
CHANGED
|
@@ -69,7 +69,7 @@ export { type BrainApplyResult, type BrainConfigPatch, type BrainConfigSnapshot,
|
|
|
69
69
|
export { buildLosslessDigest, buildSmartDigest, type ContentScore, type EliseResult, eliseOldToolResults, estimateMessages, extractText, findPreserveStart, hasTextContent, scoreMessage, } from './execution/compaction-core.js';
|
|
70
70
|
export { COUNCIL_REFUSE_OPTION_ID, type CouncilBrainOptions, type CouncilVoter, createCouncilBrainArbiter, } from './execution/council-brain.js';
|
|
71
71
|
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';
|
|
72
|
+
export { BUILTIN_COUNCIL_PERSONA_IDS, BUILTIN_COUNCIL_PERSONAS, CouncilPersonaRegistry, createCouncilPersonaRegistry, DEFAULT_COUNCIL_PERSONA_REGISTRY, } from './execution/council-personas.js';
|
|
73
73
|
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
74
|
export { buildCouncilJudgeSystemPrompt, buildCouncilJudgeUserPrompt, buildCouncilQuestionPrompt, buildCouncilVoterSystemPrompt, buildCouncilVoterUserPrompt, COUNCIL_JUDGE_PROMPT_PATH, COUNCIL_VOTER_PROMPT_PATH, } from './execution/council-prompts.js';
|
|
75
75
|
export { type CouncilResolution, type CouncilResolutionInput, type CouncilResolutionSeat, type CouncilResolutionVote, resolveCouncilVotes, } from './execution/council-resolution.js';
|