@wrongstack/core 0.298.2 → 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/index.js
CHANGED
|
@@ -3207,7 +3207,9 @@ function safeProfileName(name) {
|
|
|
3207
3207
|
function activeProfileName(globalRoot) {
|
|
3208
3208
|
try {
|
|
3209
3209
|
const parsed = JSON.parse(fs4.readFileSync(path5.join(globalRoot, "config.json"), "utf8"));
|
|
3210
|
-
return safeProfileName(
|
|
3210
|
+
return safeProfileName(
|
|
3211
|
+
typeof parsed.activeProfile === "string" ? parsed.activeProfile : void 0
|
|
3212
|
+
);
|
|
3211
3213
|
} catch {
|
|
3212
3214
|
return "default";
|
|
3213
3215
|
}
|
|
@@ -3287,6 +3289,7 @@ function resolveWstackPaths(opts) {
|
|
|
3287
3289
|
projectPlan: path5.join(projectDir, "plan.json"),
|
|
3288
3290
|
projectAutophase: path5.join(projectDir, "autophase"),
|
|
3289
3291
|
projectSddBoards: path5.join(projectDir, "sdd-boards"),
|
|
3292
|
+
projectRequirementIntakes: path5.join(projectDir, "requirement-intakes"),
|
|
3290
3293
|
syncConfig: path5.join(profileDir, "sync.json"),
|
|
3291
3294
|
configHistoryDir: path5.join(globalRoot, "config-history"),
|
|
3292
3295
|
projectStatus: (projectHash2) => path5.join(globalRoot, "projects", projectHash2, "status.json")
|
|
@@ -3795,6 +3798,15 @@ var IN_PROJECT_DENIED_PATHS = [
|
|
|
3795
3798
|
reason: "Extends the exec allow-list; a repo could authorise its own binaries."
|
|
3796
3799
|
},
|
|
3797
3800
|
{ path: "tools.exec.danger", reason: "Weakens the destructive-command banner." },
|
|
3801
|
+
{
|
|
3802
|
+
// The whole subtree, not just the dangerous leaves: a persona's
|
|
3803
|
+
// `instruction` is rendered into the voter SYSTEM prompt, a profile seat
|
|
3804
|
+
// may pin providerId/model, and `defaultProfile` selects which of those
|
|
3805
|
+
// runs when the agent names no profile. Denying the parent leaves no leaf
|
|
3806
|
+
// to reclassify wrongly later.
|
|
3807
|
+
path: "tools.council",
|
|
3808
|
+
reason: "Council tool panel definitions: persona instructions are injected into the voter SYSTEM prompt and profile seats can pin an attacker-chosen providerId/model."
|
|
3809
|
+
},
|
|
3798
3810
|
{ path: "skills.extraDirs", reason: "Loads skill definitions from repo-chosen directories." },
|
|
3799
3811
|
{ path: "skills.registryUrl", reason: "Redirects skill installs to a repo-chosen host." },
|
|
3800
3812
|
// Deliberately NOT denied: skills.mode and skills.eagerMaxChars. The audit
|
|
@@ -29664,7 +29676,7 @@ var ROUTE_SCOPED_QUOTA_RE = /\b(?:for|on)\s+(?:(?:this|the)\s+)?(?:route|model)\
|
|
|
29664
29676
|
function effectiveInputTokens(usage) {
|
|
29665
29677
|
return usage.input + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0);
|
|
29666
29678
|
}
|
|
29667
|
-
var CONTEXT_OVERFLOW_RE = /context.length|context.window|maximum context|max.*tokens?.*exceeded|prompt
|
|
29679
|
+
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;
|
|
29668
29680
|
var CONTENT_FILTER_RE = /content.(filter|policy|moderation)|safety (system|filter)/i;
|
|
29669
29681
|
var RATE_LIMIT_EXCEEDED_RE = /rate[-_\s]*limit[-_\s]*exceeded/i;
|
|
29670
29682
|
function classifyProviderError(status, body, message) {
|
|
@@ -29674,7 +29686,7 @@ function classifyProviderError(status, body, message) {
|
|
|
29674
29686
|
if (status === 408) return "timeout";
|
|
29675
29687
|
if (status === 599) return "stream_hang";
|
|
29676
29688
|
if (status === 402 || QUOTA_EXHAUSTED_RE.test(text2)) return "quota_exhausted";
|
|
29677
|
-
if (status === 429 && body?.message && RATE_LIMIT_EXCEEDED_RE.test(body.message)) {
|
|
29689
|
+
if (status === 429 && body?.message && body.type !== "rate_limit_exceeded" && RATE_LIMIT_EXCEEDED_RE.test(body.message)) {
|
|
29678
29690
|
return "quota_exhausted";
|
|
29679
29691
|
}
|
|
29680
29692
|
if (type === "rate_limit_error" || status === 429) return "rate_limit";
|
|
@@ -29810,7 +29822,7 @@ var ProviderError = class extends WrongStackError {
|
|
|
29810
29822
|
const e = err;
|
|
29811
29823
|
const name = e.name;
|
|
29812
29824
|
if (typeof name !== "string" || !name.endsWith("Error")) return false;
|
|
29813
|
-
return typeof e.status === "number" && typeof e.retryable === "boolean" && typeof e.kind === "string";
|
|
29825
|
+
return typeof e.status === "number" && typeof e.retryable === "boolean" && typeof e.kind === "string" && typeof e.describe === "function";
|
|
29814
29826
|
}
|
|
29815
29827
|
constructor(message, status, retryable, providerId, opts = {}) {
|
|
29816
29828
|
const kind = opts.kind ?? classifyProviderError(status, opts.body, message);
|
|
@@ -47391,7 +47403,8 @@ var KNOWN_HQ_CLIENT_FRAME_TYPES = /* @__PURE__ */ new Set([
|
|
|
47391
47403
|
"client.event",
|
|
47392
47404
|
"client.command_poll",
|
|
47393
47405
|
"client.command_ack",
|
|
47394
|
-
"client.resume"
|
|
47406
|
+
"client.resume",
|
|
47407
|
+
"client.event_poll"
|
|
47395
47408
|
]);
|
|
47396
47409
|
var HQ_CLIENT_KINDS = /* @__PURE__ */ new Set([
|
|
47397
47410
|
"tui",
|
|
@@ -49918,6 +49931,16 @@ function hash3(value) {
|
|
|
49918
49931
|
|
|
49919
49932
|
// src/core/provider-runner.ts
|
|
49920
49933
|
import { randomUUID as randomUUID31 } from "node:crypto";
|
|
49934
|
+
function scrubProviderBody(body) {
|
|
49935
|
+
if (!body) return void 0;
|
|
49936
|
+
return {
|
|
49937
|
+
...body,
|
|
49938
|
+
...body.type !== void 0 ? { type: scrubErrorText(body.type) } : {},
|
|
49939
|
+
...body.message !== void 0 ? { message: scrubErrorText(body.message) } : {},
|
|
49940
|
+
...body.raw !== void 0 ? { raw: scrubErrorText(body.raw) } : {},
|
|
49941
|
+
...body.requestId !== void 0 ? { requestId: scrubErrorText(body.requestId) } : {}
|
|
49942
|
+
};
|
|
49943
|
+
}
|
|
49921
49944
|
function providerLogCtx(p, r) {
|
|
49922
49945
|
return {
|
|
49923
49946
|
providerId: p.id,
|
|
@@ -50002,7 +50025,10 @@ async function runProviderWithRetry(opts) {
|
|
|
50002
50025
|
const isProviderErr = err instanceof ProviderError || ProviderError.isProviderError(err);
|
|
50003
50026
|
const errAsErr = err instanceof Error ? err : new Error(String(err));
|
|
50004
50027
|
const canRetry = retry.shouldRetry(isProviderErr ? err : errAsErr, attempt);
|
|
50005
|
-
const
|
|
50028
|
+
const providerErrorBody = isProviderErr ? scrubProviderBody(err.body) : void 0;
|
|
50029
|
+
const description = scrubErrorText(
|
|
50030
|
+
isProviderErr ? err.describe() : errAsErr.message
|
|
50031
|
+
);
|
|
50006
50032
|
const delay3 = canRetry ? Math.round(retry.delayMs(attempt, isProviderErr ? err : errAsErr)) : void 0;
|
|
50007
50033
|
events.emit("provider.attempt.failed", {
|
|
50008
50034
|
...correlation,
|
|
@@ -50015,7 +50041,8 @@ async function runProviderWithRetry(opts) {
|
|
|
50015
50041
|
retryable: canRetry,
|
|
50016
50042
|
retryScheduled: canRetry,
|
|
50017
50043
|
...delay3 !== void 0 ? { retryDelayMs: delay3 } : {},
|
|
50018
|
-
...
|
|
50044
|
+
...providerErrorBody?.requestId ? { providerRequestId: providerErrorBody.requestId } : {},
|
|
50045
|
+
...providerErrorBody ? { errorBody: providerErrorBody } : {}
|
|
50019
50046
|
});
|
|
50020
50047
|
if (!canRetry) {
|
|
50021
50048
|
events.emit("provider.error", {
|
|
@@ -50023,13 +50050,15 @@ async function runProviderWithRetry(opts) {
|
|
|
50023
50050
|
providerId: isProviderErr ? err.providerId : provider.id,
|
|
50024
50051
|
status: isProviderErr ? err.status : 0,
|
|
50025
50052
|
description,
|
|
50026
|
-
retryable: false
|
|
50053
|
+
retryable: false,
|
|
50054
|
+
...providerErrorBody ? { errorBody: providerErrorBody } : {}
|
|
50027
50055
|
});
|
|
50028
50056
|
logger.error(`Provider call failed after ${attempt + 1} attempt(s) \u2014 ${description}`, {
|
|
50029
50057
|
...providerLogCtx(provider, request),
|
|
50030
50058
|
attempts: attempt + 1,
|
|
50031
50059
|
errorDescription: description,
|
|
50032
50060
|
status: isProviderErr ? err.status : void 0,
|
|
50061
|
+
errorBody: providerErrorBody,
|
|
50033
50062
|
errorName: err instanceof Error ? err.name : void 0,
|
|
50034
50063
|
errorStack: err instanceof Error ? err.stack?.split("\n").slice(0, 3).join("\n") : void 0
|
|
50035
50064
|
});
|
|
@@ -50043,7 +50072,8 @@ async function runProviderWithRetry(opts) {
|
|
|
50043
50072
|
maxAttempts,
|
|
50044
50073
|
delayMs: delay3,
|
|
50045
50074
|
errorDescription: description,
|
|
50046
|
-
status: isProviderErr ? err.status : void 0
|
|
50075
|
+
status: isProviderErr ? err.status : void 0,
|
|
50076
|
+
errorBody: providerErrorBody
|
|
50047
50077
|
});
|
|
50048
50078
|
events.emit("provider.retry", {
|
|
50049
50079
|
sessionId: resolveEventSessionId(ctx),
|
|
@@ -50051,7 +50081,8 @@ async function runProviderWithRetry(opts) {
|
|
|
50051
50081
|
attempt: attemptNum,
|
|
50052
50082
|
delayMs: delay3,
|
|
50053
50083
|
status: isProviderErr ? err.status : 0,
|
|
50054
|
-
description
|
|
50084
|
+
description,
|
|
50085
|
+
...providerErrorBody ? { errorBody: providerErrorBody } : {}
|
|
50055
50086
|
});
|
|
50056
50087
|
await new Promise((resolve50, reject) => {
|
|
50057
50088
|
let settled = false;
|
|
@@ -60665,6 +60696,38 @@ var sshManagerServer = () => ({
|
|
|
60665
60696
|
permission: "confirm",
|
|
60666
60697
|
requestTimeoutMs: 18e4
|
|
60667
60698
|
});
|
|
60699
|
+
var requirementIntakeServer = () => ({
|
|
60700
|
+
name: "requirement-intake",
|
|
60701
|
+
description: "WrongStack Requirements Intake \u2014 list intake records and file new ones (project-scoped, --writable)",
|
|
60702
|
+
transport: "stdio",
|
|
60703
|
+
command: "wstack-requirement-intake-mcp",
|
|
60704
|
+
args: ["--project-root", ".", "--writable"],
|
|
60705
|
+
permission: "auto"
|
|
60706
|
+
});
|
|
60707
|
+
var kanbanServer = () => ({
|
|
60708
|
+
name: "kanban",
|
|
60709
|
+
description: "WrongStack Kanban \u2014 inspect and manage project work boards (project-scoped, manage tier, no destructive ops)",
|
|
60710
|
+
transport: "stdio",
|
|
60711
|
+
command: "wstack-kanban-mcp",
|
|
60712
|
+
args: ["--project-root", ".", "--writable"],
|
|
60713
|
+
permission: "confirm"
|
|
60714
|
+
});
|
|
60715
|
+
var mailboxServer = () => ({
|
|
60716
|
+
name: "mailbox",
|
|
60717
|
+
description: "WrongStack Mailbox \u2014 read and send project agent mail (project-scoped, no admin/credentials)",
|
|
60718
|
+
transport: "stdio",
|
|
60719
|
+
command: "wstack-mailbox-mcp",
|
|
60720
|
+
args: ["--project-root", ".", "--actor", "external-agent", "--writable"],
|
|
60721
|
+
permission: "auto"
|
|
60722
|
+
});
|
|
60723
|
+
var codebaseIndexServer = () => ({
|
|
60724
|
+
name: "codebase-index",
|
|
60725
|
+
description: "WrongStack Codebase Index \u2014 symbol search and dependency graphs (project-scoped, --writable)",
|
|
60726
|
+
transport: "stdio",
|
|
60727
|
+
command: "wstack-codebase-index-mcp",
|
|
60728
|
+
args: ["--project-root", ".", "--writable"],
|
|
60729
|
+
permission: "auto"
|
|
60730
|
+
});
|
|
60668
60731
|
var allServers = () => ({
|
|
60669
60732
|
filesystem: { ...filesystemServer(), enabled: false },
|
|
60670
60733
|
github: { ...githubServer(), enabled: false },
|
|
@@ -60679,7 +60742,11 @@ var allServers = () => ({
|
|
|
60679
60742
|
"zai-vision": { ...zaiVisionServer(), enabled: false },
|
|
60680
60743
|
"minimax-vision": { ...miniMaxVisionServer(), enabled: false },
|
|
60681
60744
|
playwright: { ...playwrightServer(), enabled: false },
|
|
60682
|
-
ssh: { ...sshManagerServer(), enabled: false }
|
|
60745
|
+
ssh: { ...sshManagerServer(), enabled: false },
|
|
60746
|
+
kanban: { ...kanbanServer(), enabled: false },
|
|
60747
|
+
mailbox: { ...mailboxServer(), enabled: false },
|
|
60748
|
+
"codebase-index": { ...codebaseIndexServer(), enabled: false },
|
|
60749
|
+
"requirement-intake": { ...requirementIntakeServer(), enabled: false }
|
|
60683
60750
|
});
|
|
60684
60751
|
|
|
60685
60752
|
// src/models/codex-catalog.ts
|
|
@@ -65416,6 +65483,9 @@ var BUILTIN_COUNCIL_PERSONAS = Object.freeze([
|
|
|
65416
65483
|
tags: ["users", "usability", "accessibility"]
|
|
65417
65484
|
})
|
|
65418
65485
|
]);
|
|
65486
|
+
var BUILTIN_COUNCIL_PERSONA_IDS = Object.freeze(
|
|
65487
|
+
BUILTIN_COUNCIL_PERSONAS.map((persona) => persona.id)
|
|
65488
|
+
);
|
|
65419
65489
|
var CouncilPersonaRegistry = class _CouncilPersonaRegistry {
|
|
65420
65490
|
byId;
|
|
65421
65491
|
constructor(personas = []) {
|
|
@@ -66537,6 +66607,44 @@ function errorMessage3(error2) {
|
|
|
66537
66607
|
}
|
|
66538
66608
|
|
|
66539
66609
|
// src/execution/council-brain.ts
|
|
66610
|
+
var MAX_CUSTOM_PERSONA_ID_CHARS = 48;
|
|
66611
|
+
function customPersonaId(raw, index) {
|
|
66612
|
+
const slug = raw.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, MAX_CUSTOM_PERSONA_ID_CHARS).replace(/-+$/g, "");
|
|
66613
|
+
return slug ? `custom-${slug}` : `custom-${index + 1}`;
|
|
66614
|
+
}
|
|
66615
|
+
function resolvePersonaRegistry(voters) {
|
|
66616
|
+
let registry2 = DEFAULT_COUNCIL_PERSONA_REGISTRY;
|
|
66617
|
+
const personaIds = [];
|
|
66618
|
+
const byRawText = /* @__PURE__ */ new Map();
|
|
66619
|
+
voters.forEach((voter, index) => {
|
|
66620
|
+
const raw = voter.persona?.trim();
|
|
66621
|
+
if (!raw) {
|
|
66622
|
+
personaIds.push("executor");
|
|
66623
|
+
return;
|
|
66624
|
+
}
|
|
66625
|
+
if (registry2.has(raw)) {
|
|
66626
|
+
personaIds.push(raw);
|
|
66627
|
+
return;
|
|
66628
|
+
}
|
|
66629
|
+
const existing = byRawText.get(raw);
|
|
66630
|
+
if (existing) {
|
|
66631
|
+
personaIds.push(existing);
|
|
66632
|
+
return;
|
|
66633
|
+
}
|
|
66634
|
+
let id = customPersonaId(raw, index);
|
|
66635
|
+
let suffix = 2;
|
|
66636
|
+
while (registry2.has(id)) id = `${customPersonaId(raw, index)}-${suffix++}`;
|
|
66637
|
+
registry2 = registry2.with({
|
|
66638
|
+
id,
|
|
66639
|
+
name: raw.length <= 40 ? raw : `${raw.slice(0, 39)}\u2026`,
|
|
66640
|
+
description: "Custom decision lens supplied by the host configuration.",
|
|
66641
|
+
instruction: raw
|
|
66642
|
+
});
|
|
66643
|
+
byRawText.set(raw, id);
|
|
66644
|
+
personaIds.push(id);
|
|
66645
|
+
});
|
|
66646
|
+
return { registry: registry2, personaIds };
|
|
66647
|
+
}
|
|
66540
66648
|
function createCouncilBrainArbiter(opts) {
|
|
66541
66649
|
if (opts.voters.length === 0) {
|
|
66542
66650
|
throw new Error("createCouncilBrainArbiter: at least one voter is required.");
|
|
@@ -66602,14 +66710,15 @@ function createCouncilBrainArbiter(opts) {
|
|
|
66602
66710
|
}
|
|
66603
66711
|
return makeSeatCallerForVoter(voter);
|
|
66604
66712
|
};
|
|
66713
|
+
const { registry: personas, personaIds } = resolvePersonaRegistry(opts.voters);
|
|
66605
66714
|
const seats = opts.voters.map((voter, i) => ({
|
|
66606
66715
|
id: `voter-${i}`,
|
|
66607
66716
|
label: voter.label ?? voter.model,
|
|
66608
|
-
persona:
|
|
66717
|
+
persona: personaIds[i] ?? "executor",
|
|
66609
66718
|
target: {
|
|
66610
66719
|
providerId: voter.provider.id,
|
|
66611
66720
|
model: voter.model,
|
|
66612
|
-
role:
|
|
66721
|
+
role: personaIds[i] ?? "executor"
|
|
66613
66722
|
},
|
|
66614
66723
|
weight: voter.weight,
|
|
66615
66724
|
veto: voter.veto
|
|
@@ -66631,6 +66740,9 @@ function createCouncilBrainArbiter(opts) {
|
|
|
66631
66740
|
};
|
|
66632
66741
|
const orchestrator = new CouncilOrchestrator({
|
|
66633
66742
|
defaultProfile: "brain-council-adapter",
|
|
66743
|
+
// Carries the ad-hoc lenses registered above; without it the orchestrator
|
|
66744
|
+
// falls back to the built-in registry and rejects every custom persona.
|
|
66745
|
+
personas,
|
|
66634
66746
|
// Previously never passed, so the panel was pinned at the orchestrator's
|
|
66635
66747
|
// default of 3 concurrent seats no matter how many voters were configured.
|
|
66636
66748
|
...opts.maxConcurrency !== void 0 ? { maxConcurrency: opts.maxConcurrency } : {},
|
|
@@ -67984,7 +68096,8 @@ var OneShotOrchestrator = class {
|
|
|
67984
68096
|
resolveTarget(input, config) {
|
|
67985
68097
|
if (input.role && this.opts.modelRouter) {
|
|
67986
68098
|
const pick = this.opts.modelRouter.pickForTask(input.role, "");
|
|
67987
|
-
|
|
68099
|
+
const hasExplicitTarget = Boolean(input.providerId || input.model);
|
|
68100
|
+
if (pick && (pick.fromMatrix === true || !hasExplicitTarget)) {
|
|
67988
68101
|
return { providerId: pick.provider, model: pick.model };
|
|
67989
68102
|
}
|
|
67990
68103
|
}
|
|
@@ -71265,18 +71378,26 @@ function parseHookOutcome(stdout) {
|
|
|
71265
71378
|
// src/hooks/http-executor.ts
|
|
71266
71379
|
var DEFAULT_TIMEOUT_MS6 = 5e3;
|
|
71267
71380
|
var MAX_OUTPUT_BYTES2 = 64 * 1024;
|
|
71268
|
-
function isAllowedUrl(raw) {
|
|
71381
|
+
async function isAllowedUrl(raw) {
|
|
71382
|
+
let url;
|
|
71269
71383
|
try {
|
|
71270
|
-
|
|
71271
|
-
if (url.protocol === "https:") return true;
|
|
71272
|
-
if (url.protocol !== "http:") return false;
|
|
71273
|
-
return url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]";
|
|
71384
|
+
url = new URL(raw);
|
|
71274
71385
|
} catch {
|
|
71275
71386
|
return false;
|
|
71276
71387
|
}
|
|
71388
|
+
if (url.protocol === "https:") {
|
|
71389
|
+
try {
|
|
71390
|
+
await assertNotPrivateHost(url.hostname);
|
|
71391
|
+
return true;
|
|
71392
|
+
} catch {
|
|
71393
|
+
return false;
|
|
71394
|
+
}
|
|
71395
|
+
}
|
|
71396
|
+
if (url.protocol !== "http:") return false;
|
|
71397
|
+
return url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]";
|
|
71277
71398
|
}
|
|
71278
71399
|
async function runHttpHookDetailed(spec, input, logger, options = {}) {
|
|
71279
|
-
if (!isAllowedUrl(spec.url)) {
|
|
71400
|
+
if (!await isAllowedUrl(spec.url)) {
|
|
71280
71401
|
return {
|
|
71281
71402
|
outcome: null,
|
|
71282
71403
|
failure: {
|
|
@@ -74070,6 +74191,7 @@ var HqBootstrapCodeStore = class {
|
|
|
74070
74191
|
};
|
|
74071
74192
|
|
|
74072
74193
|
// src/hq/exposure.ts
|
|
74194
|
+
import { isIP as isIP2 } from "node:net";
|
|
74073
74195
|
var HQ_CLI_DEFAULT_HOST = "0.0.0.0";
|
|
74074
74196
|
var HqInsecureExposureError = class extends Error {
|
|
74075
74197
|
constructor(message) {
|
|
@@ -74079,8 +74201,15 @@ var HqInsecureExposureError = class extends Error {
|
|
|
74079
74201
|
};
|
|
74080
74202
|
function isLoopbackHost(host) {
|
|
74081
74203
|
const normalized = host.trim().toLowerCase().replace(/^\[/, "").replace(/\]$/, "");
|
|
74082
|
-
if (normalized === "localhost"
|
|
74083
|
-
|
|
74204
|
+
if (normalized === "localhost") return true;
|
|
74205
|
+
const family = isIP2(normalized);
|
|
74206
|
+
if (family === 4) {
|
|
74207
|
+
return normalized.startsWith("127.");
|
|
74208
|
+
}
|
|
74209
|
+
if (family === 6) {
|
|
74210
|
+
return normalized === "::1" || /^::ffff:127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(normalized);
|
|
74211
|
+
}
|
|
74212
|
+
return false;
|
|
74084
74213
|
}
|
|
74085
74214
|
function isOpenMode(input) {
|
|
74086
74215
|
return !input.hasBrowserTokens && !input.hasPassword;
|
|
@@ -85082,7 +85211,7 @@ var INPUT_SCHEMA = {
|
|
|
85082
85211
|
},
|
|
85083
85212
|
profile: {
|
|
85084
85213
|
type: "string",
|
|
85085
|
-
description:
|
|
85214
|
+
description: 'Registered Council profile id (e.g. "balanced", "fast", "risk-review"). Defaults to the host-configured profile.'
|
|
85086
85215
|
}
|
|
85087
85216
|
},
|
|
85088
85217
|
required: ["question"],
|
|
@@ -85093,10 +85222,12 @@ function createCouncilTool(opts) {
|
|
|
85093
85222
|
...opts,
|
|
85094
85223
|
fallbackProfileManager: opts.fallbackProfileManager
|
|
85095
85224
|
});
|
|
85225
|
+
const profiles = opts.profiles ?? DEFAULT_COUNCIL_PROFILE_REGISTRY;
|
|
85226
|
+
const profileIds = profiles.list().map((profile) => profile.id);
|
|
85096
85227
|
return {
|
|
85097
85228
|
name: COUNCIL_TOOL_NAME,
|
|
85098
85229
|
description: "Ask an independent, multi-persona Council to evaluate a decision or synthesize an answer. Uses bounded parallel voters, quorum/veto/weighted resolution, optional judging, model routing, fallback chains, and cancellation.",
|
|
85099
|
-
usageHint:
|
|
85230
|
+
usageHint: `Use for consequential or disputed decisions that benefit from independent lenses. Provide \`options\` for a vote or omit them for an open answer. Keep context evidence-focused; the Council treats it as untrusted data. Available profiles: ${profileIds.join(", ")}.`,
|
|
85100
85231
|
category: "meta",
|
|
85101
85232
|
inputSchema: INPUT_SCHEMA,
|
|
85102
85233
|
permission: "auto",
|
|
@@ -85114,11 +85245,17 @@ function createCouncilTool(opts) {
|
|
|
85114
85245
|
};
|
|
85115
85246
|
return orchestrator.ask(question);
|
|
85116
85247
|
},
|
|
85117
|
-
validate: validateCouncilToolInput
|
|
85248
|
+
validate: (input) => validateCouncilToolInput(input, profileIds)
|
|
85118
85249
|
};
|
|
85119
85250
|
}
|
|
85120
|
-
function validateCouncilToolInput(input) {
|
|
85251
|
+
function validateCouncilToolInput(input, profileIds) {
|
|
85121
85252
|
const errors = [];
|
|
85253
|
+
if (typeof input.profile === "string" && input.profile.trim()) {
|
|
85254
|
+
const id = input.profile.trim();
|
|
85255
|
+
if (!profileIds.includes(id)) {
|
|
85256
|
+
errors.push(`Unknown \`profile\` "${id}". Available: ${profileIds.join(", ")}.`);
|
|
85257
|
+
}
|
|
85258
|
+
}
|
|
85122
85259
|
const question = input.question?.trim() ?? "";
|
|
85123
85260
|
if (!question) errors.push("`question` must not be empty.");
|
|
85124
85261
|
if (question.length > MAX_COUNCIL_QUESTION_CHARS) {
|
|
@@ -87075,7 +87212,7 @@ var DEFAULT_SPEC_TEMPLATE = {
|
|
|
87075
87212
|
|
|
87076
87213
|
// src/worktree/worktree-manager.ts
|
|
87077
87214
|
init_error();
|
|
87078
|
-
import { mkdir as mkdir31 } from "node:fs/promises";
|
|
87215
|
+
import { mkdir as mkdir31, readFile as readFile66 } from "node:fs/promises";
|
|
87079
87216
|
import { join as join85, resolve as resolve49, sep as sep8 } from "node:path";
|
|
87080
87217
|
|
|
87081
87218
|
// src/worktree/worktree-git.ts
|
|
@@ -87456,6 +87593,10 @@ ${merged.stderr}`);
|
|
|
87456
87593
|
* Safe because SDD merges are serialized — no other commit lands in between.
|
|
87457
87594
|
*/
|
|
87458
87595
|
async revertBaseTo(handle, sha) {
|
|
87596
|
+
const status = await this.runGit(["status", "--porcelain"], this.projectRoot);
|
|
87597
|
+
if (status.code !== 0) return false;
|
|
87598
|
+
const hasTrackedDirt = status.stdout.split("\n").some((line) => line.trim().length > 0 && !line.startsWith("??"));
|
|
87599
|
+
if (hasTrackedDirt) return false;
|
|
87459
87600
|
const co = await this.runGit(["checkout", handle.baseBranch], this.projectRoot);
|
|
87460
87601
|
if (co.code !== 0) return false;
|
|
87461
87602
|
const reset = await this.runGit(["reset", "--hard", sha], this.projectRoot);
|
|
@@ -87670,29 +87811,47 @@ ${merged.stderr}`);
|
|
|
87670
87811
|
/**
|
|
87671
87812
|
* True when staged content still carries conflict markers.
|
|
87672
87813
|
*
|
|
87673
|
-
*
|
|
87674
|
-
*
|
|
87675
|
-
*
|
|
87676
|
-
*
|
|
87677
|
-
*
|
|
87678
|
-
*
|
|
87814
|
+
* Reads the working-tree files directly and matches a full marker line
|
|
87815
|
+
* (`<<<<<<< `, `=======`, `>>>>>>> `, `||||||| `) with a lenient regex
|
|
87816
|
+
* (`{7,}`) that stays platform- and config-independent — unlike the old
|
|
87817
|
+
* `git diff --check` (only fires when `core.whitespace` opts in) and
|
|
87818
|
+
* `git grep` (assumes the default 7-char markers).
|
|
87819
|
+
*
|
|
87820
|
+
* Scan set: when the caller supplies the conflicted paths (the normal
|
|
87821
|
+
* path from {@link tryResolveConflict}), ONLY those files are scanned so
|
|
87822
|
+
* an unrelated `=======` setext-heading underline in a staged document
|
|
87823
|
+
* can't false-positive and discard valid resolution work. The staged-file
|
|
87824
|
+
* listing (`git diff --cached --name-only`) is used only as a fallback
|
|
87825
|
+
* when no paths are given.
|
|
87679
87826
|
*
|
|
87680
|
-
*
|
|
87681
|
-
*
|
|
87682
|
-
*
|
|
87683
|
-
*
|
|
87827
|
+
* CRLF caveat: conflicted files checked out with CRLF (`core.autocrlf=true`,
|
|
87828
|
+
* `.gitattributes eol=crlf`) carry bare markers as `=======\r\n`. Because
|
|
87829
|
+
* `$` in multiline mode matches only before `\n`, carriage returns are
|
|
87830
|
+
* stripped before matching so `\r` never defeats the end-of-line anchor.
|
|
87684
87831
|
*/
|
|
87685
87832
|
async hasConflictMarkers(files) {
|
|
87686
|
-
const
|
|
87687
|
-
const
|
|
87688
|
-
|
|
87689
|
-
|
|
87690
|
-
)
|
|
87691
|
-
|
|
87692
|
-
|
|
87693
|
-
|
|
87694
|
-
|
|
87695
|
-
|
|
87833
|
+
const listed = files ?? [];
|
|
87834
|
+
const paths = listed.length > 0 ? listed : (await this.runGit(["diff", "--cached", "--name-only", "-z"], this.projectRoot)).stdout.split("\0").filter(Boolean);
|
|
87835
|
+
const marker = /^(?:<{7,}(?: |$)|={7,}$|>{7,}(?: |$)|\|{7,}(?: |$))/m;
|
|
87836
|
+
const startMarker = /^(?:<{7,}(?: |$)|\|{7,}(?: |$))/m;
|
|
87837
|
+
for (const rel of paths) {
|
|
87838
|
+
try {
|
|
87839
|
+
const content = (await readFile66(join85(this.projectRoot, rel), "utf8")).replace(/\r/g, "");
|
|
87840
|
+
const lines = content.split("\n");
|
|
87841
|
+
let seenStart = false;
|
|
87842
|
+
for (const line of lines) {
|
|
87843
|
+
if (!marker.test(line)) continue;
|
|
87844
|
+
if (line.startsWith("=====")) {
|
|
87845
|
+
if (!seenStart) continue;
|
|
87846
|
+
} else if (startMarker.test(line)) {
|
|
87847
|
+
seenStart = true;
|
|
87848
|
+
}
|
|
87849
|
+
return true;
|
|
87850
|
+
}
|
|
87851
|
+
} catch {
|
|
87852
|
+
}
|
|
87853
|
+
}
|
|
87854
|
+
return false;
|
|
87696
87855
|
}
|
|
87697
87856
|
/**
|
|
87698
87857
|
* Remove the worktree + branch. Conflicted/failed handles (or `keep:true`)
|
|
@@ -87826,6 +87985,7 @@ export {
|
|
|
87826
87985
|
BUG_HUNTER_AGENT,
|
|
87827
87986
|
BUILD_AGENTS,
|
|
87828
87987
|
BUILTIN_COUNCIL_PERSONAS,
|
|
87988
|
+
BUILTIN_COUNCIL_PERSONA_IDS,
|
|
87829
87989
|
BUILTIN_COUNCIL_PROFILES,
|
|
87830
87990
|
BUILTIN_PROMPT_CATEGORIES,
|
|
87831
87991
|
BrainCircuitBreaker,
|
|
@@ -1397,6 +1397,38 @@ var sshManagerServer = () => ({
|
|
|
1397
1397
|
permission: "confirm",
|
|
1398
1398
|
requestTimeoutMs: 18e4
|
|
1399
1399
|
});
|
|
1400
|
+
var requirementIntakeServer = () => ({
|
|
1401
|
+
name: "requirement-intake",
|
|
1402
|
+
description: "WrongStack Requirements Intake \u2014 list intake records and file new ones (project-scoped, --writable)",
|
|
1403
|
+
transport: "stdio",
|
|
1404
|
+
command: "wstack-requirement-intake-mcp",
|
|
1405
|
+
args: ["--project-root", ".", "--writable"],
|
|
1406
|
+
permission: "auto"
|
|
1407
|
+
});
|
|
1408
|
+
var kanbanServer = () => ({
|
|
1409
|
+
name: "kanban",
|
|
1410
|
+
description: "WrongStack Kanban \u2014 inspect and manage project work boards (project-scoped, manage tier, no destructive ops)",
|
|
1411
|
+
transport: "stdio",
|
|
1412
|
+
command: "wstack-kanban-mcp",
|
|
1413
|
+
args: ["--project-root", ".", "--writable"],
|
|
1414
|
+
permission: "confirm"
|
|
1415
|
+
});
|
|
1416
|
+
var mailboxServer = () => ({
|
|
1417
|
+
name: "mailbox",
|
|
1418
|
+
description: "WrongStack Mailbox \u2014 read and send project agent mail (project-scoped, no admin/credentials)",
|
|
1419
|
+
transport: "stdio",
|
|
1420
|
+
command: "wstack-mailbox-mcp",
|
|
1421
|
+
args: ["--project-root", ".", "--actor", "external-agent", "--writable"],
|
|
1422
|
+
permission: "auto"
|
|
1423
|
+
});
|
|
1424
|
+
var codebaseIndexServer = () => ({
|
|
1425
|
+
name: "codebase-index",
|
|
1426
|
+
description: "WrongStack Codebase Index \u2014 symbol search and dependency graphs (project-scoped, --writable)",
|
|
1427
|
+
transport: "stdio",
|
|
1428
|
+
command: "wstack-codebase-index-mcp",
|
|
1429
|
+
args: ["--project-root", ".", "--writable"],
|
|
1430
|
+
permission: "auto"
|
|
1431
|
+
});
|
|
1400
1432
|
var allServers = () => ({
|
|
1401
1433
|
filesystem: { ...filesystemServer(), enabled: false },
|
|
1402
1434
|
github: { ...githubServer(), enabled: false },
|
|
@@ -1411,7 +1443,11 @@ var allServers = () => ({
|
|
|
1411
1443
|
"zai-vision": { ...zaiVisionServer(), enabled: false },
|
|
1412
1444
|
"minimax-vision": { ...miniMaxVisionServer(), enabled: false },
|
|
1413
1445
|
playwright: { ...playwrightServer(), enabled: false },
|
|
1414
|
-
ssh: { ...sshManagerServer(), enabled: false }
|
|
1446
|
+
ssh: { ...sshManagerServer(), enabled: false },
|
|
1447
|
+
kanban: { ...kanbanServer(), enabled: false },
|
|
1448
|
+
mailbox: { ...mailboxServer(), enabled: false },
|
|
1449
|
+
"codebase-index": { ...codebaseIndexServer(), enabled: false },
|
|
1450
|
+
"requirement-intake": { ...requirementIntakeServer(), enabled: false }
|
|
1415
1451
|
});
|
|
1416
1452
|
|
|
1417
1453
|
// src/utils/expect-defined.ts
|
|
@@ -2900,7 +2936,9 @@ function safeProfileName(name) {
|
|
|
2900
2936
|
function activeProfileName(globalRoot) {
|
|
2901
2937
|
try {
|
|
2902
2938
|
const parsed = JSON.parse(fs4.readFileSync(path5.join(globalRoot, "config.json"), "utf8"));
|
|
2903
|
-
return safeProfileName(
|
|
2939
|
+
return safeProfileName(
|
|
2940
|
+
typeof parsed.activeProfile === "string" ? parsed.activeProfile : void 0
|
|
2941
|
+
);
|
|
2904
2942
|
} catch {
|
|
2905
2943
|
return "default";
|
|
2906
2944
|
}
|
|
@@ -2980,6 +3018,7 @@ function resolveWstackPaths(opts) {
|
|
|
2980
3018
|
projectPlan: path5.join(projectDir, "plan.json"),
|
|
2981
3019
|
projectAutophase: path5.join(projectDir, "autophase"),
|
|
2982
3020
|
projectSddBoards: path5.join(projectDir, "sdd-boards"),
|
|
3021
|
+
projectRequirementIntakes: path5.join(projectDir, "requirement-intakes"),
|
|
2983
3022
|
syncConfig: path5.join(profileDir, "sync.json"),
|
|
2984
3023
|
configHistoryDir: path5.join(globalRoot, "config-history"),
|
|
2985
3024
|
projectStatus: (projectHash2) => path5.join(globalRoot, "projects", projectHash2, "status.json")
|
|
@@ -3413,6 +3452,15 @@ var IN_PROJECT_DENIED_PATHS = [
|
|
|
3413
3452
|
reason: "Extends the exec allow-list; a repo could authorise its own binaries."
|
|
3414
3453
|
},
|
|
3415
3454
|
{ path: "tools.exec.danger", reason: "Weakens the destructive-command banner." },
|
|
3455
|
+
{
|
|
3456
|
+
// The whole subtree, not just the dangerous leaves: a persona's
|
|
3457
|
+
// `instruction` is rendered into the voter SYSTEM prompt, a profile seat
|
|
3458
|
+
// may pin providerId/model, and `defaultProfile` selects which of those
|
|
3459
|
+
// runs when the agent names no profile. Denying the parent leaves no leaf
|
|
3460
|
+
// to reclassify wrongly later.
|
|
3461
|
+
path: "tools.council",
|
|
3462
|
+
reason: "Council tool panel definitions: persona instructions are injected into the voter SYSTEM prompt and profile seats can pin an attacker-chosen providerId/model."
|
|
3463
|
+
},
|
|
3416
3464
|
{ path: "skills.extraDirs", reason: "Loads skill definitions from repo-chosen directories." },
|
|
3417
3465
|
{ path: "skills.registryUrl", reason: "Redirects skill installs to a repo-chosen host." },
|
|
3418
3466
|
// Deliberately NOT denied: skills.mode and skills.eagerMaxChars. The audit
|
|
@@ -78,6 +78,41 @@ export declare const miniMaxVisionServer: () => MCPServerConfig;
|
|
|
78
78
|
* env/TOML config (for example SSH_SERVER_<NAME>_HOST, USER, KEYPATH/PASSWORD) or ssh-agent.
|
|
79
79
|
*/
|
|
80
80
|
export declare const sshManagerServer: () => MCPServerConfig;
|
|
81
|
+
/**
|
|
82
|
+
* WrongStack Requirements Intake — list intake records and file + submit new
|
|
83
|
+
* ones against the project's intake store (`wstack-requirement-intake-mcp`).
|
|
84
|
+
* Project-scoped: resolves the project from the spawn cwd (`--project-root .`);
|
|
85
|
+
* customize the arg for absolute paths. Tools are structured domain tools
|
|
86
|
+
* (list/submit) with no file, shell, or network surface, so the permission
|
|
87
|
+
* defaults to `auto`.
|
|
88
|
+
*/
|
|
89
|
+
export declare const requirementIntakeServer: () => MCPServerConfig;
|
|
90
|
+
/**
|
|
91
|
+
* WrongStack Kanban — inspect and manage durable project work through the
|
|
92
|
+
* project-scoped Kanban IPC owner (`wstack-kanban-mcp`). The preset exposes
|
|
93
|
+
* the writable manage tier (create/update/transition tasks, splitting,
|
|
94
|
+
* assignment) but never `--destructive` (delete/merge/transfer). Mutations
|
|
95
|
+
* touch durable project board state, so the permission defaults to `confirm`.
|
|
96
|
+
*/
|
|
97
|
+
export declare const kanbanServer: () => MCPServerConfig;
|
|
98
|
+
/**
|
|
99
|
+
* WrongStack Mailbox — coordinate with project agents through the existing
|
|
100
|
+
* project-scoped mailbox IPC owner (`wstack-mailbox-mcp`). The preset exposes
|
|
101
|
+
* the writable tier (send, receipts, self-presence) but never `--admin`
|
|
102
|
+
* (maintenance + credential operations).
|
|
103
|
+
*
|
|
104
|
+
* `--actor` is mandatory: the child CLI exits with code 2 without it. The
|
|
105
|
+
* default `external-agent` identity should be customized per external agent
|
|
106
|
+
* (it is the authoritative identity for sends, receipts, and registration).
|
|
107
|
+
*/
|
|
108
|
+
export declare const mailboxServer: () => MCPServerConfig;
|
|
109
|
+
/**
|
|
110
|
+
* WrongStack Codebase Index — symbol search and dependency graphs over the
|
|
111
|
+
* project's codebase index (`wstack-codebase-index-mcp`). The preset exposes
|
|
112
|
+
* `--writable` so `codebase_index` (incremental/full rebuild) is available;
|
|
113
|
+
* index rebuilds are CPU/disk work with no semantic risk.
|
|
114
|
+
*/
|
|
115
|
+
export declare const codebaseIndexServer: () => MCPServerConfig;
|
|
81
116
|
/** Everything bundled — full set of built-in servers. Useful for `wstack mcp add --all`. */
|
|
82
117
|
export declare const allServers: () => Record<string, MCPServerConfig>;
|
|
83
118
|
//# sourceMappingURL=mcp-servers.d.ts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ChroniclePromptManifest } from '../../chronicle/prompt-manifest.js';
|
|
2
2
|
import type { Context } from '../../core/context.js';
|
|
3
3
|
import type { ContentBlock } from '../../types/blocks.js';
|
|
4
|
-
import type { Usage } from '../../types/provider.js';
|
|
4
|
+
import type { ProviderErrorBody, Usage } from '../../types/provider.js';
|
|
5
5
|
export interface ProviderEventMap {
|
|
6
6
|
/** Fired before every physical provider/model attempt, including attempt zero. */
|
|
7
7
|
'provider.attempt.started': {
|
|
@@ -63,6 +63,8 @@ export interface ProviderEventMap {
|
|
|
63
63
|
retryScheduled: boolean;
|
|
64
64
|
retryDelayMs?: number | undefined;
|
|
65
65
|
providerRequestId?: string | undefined;
|
|
66
|
+
/** Scrubbed provider failure envelope/body retained for diagnostics. */
|
|
67
|
+
errorBody?: ProviderErrorBody | undefined;
|
|
66
68
|
/** Kanban task this provider call belongs to, when known. */
|
|
67
69
|
taskId?: string | undefined;
|
|
68
70
|
/** Kanban board this provider call belongs to, when known. */
|
|
@@ -123,6 +125,8 @@ export interface ProviderEventMap {
|
|
|
123
125
|
delayMs: number;
|
|
124
126
|
status: number;
|
|
125
127
|
description: string;
|
|
128
|
+
/** Scrubbed provider failure envelope/body retained for diagnostics. */
|
|
129
|
+
errorBody?: ProviderErrorBody | undefined;
|
|
126
130
|
};
|
|
127
131
|
/**
|
|
128
132
|
* Fired once when a provider call ultimately fails (retries exhausted, or
|
|
@@ -134,6 +138,8 @@ export interface ProviderEventMap {
|
|
|
134
138
|
status: number;
|
|
135
139
|
description: string;
|
|
136
140
|
retryable: boolean;
|
|
141
|
+
/** Scrubbed provider failure envelope/body retained for diagnostics. */
|
|
142
|
+
errorBody?: ProviderErrorBody | undefined;
|
|
137
143
|
};
|
|
138
144
|
/**
|
|
139
145
|
* Fired by the fallback-model extension when the primary model is overloaded
|
|
@@ -17,6 +17,8 @@ export interface SddEventMap {
|
|
|
17
17
|
completed: number;
|
|
18
18
|
failed: number;
|
|
19
19
|
stopped: boolean;
|
|
20
|
+
/** Fatal, non-recoverable error that hard-stopped the run (implies `stopped: true`). */
|
|
21
|
+
fatalError?: string | undefined;
|
|
20
22
|
};
|
|
21
23
|
/** A task began executing on a worker (carries who + which worktree). */
|
|
22
24
|
'sdd.task.started': {
|