@wrongstack/cli 1.1.4 → 1.1.6
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/{acp-MZ5MQBFN.js → acp-CZBSFQB7.js} +6 -5
- package/dist/acp-leader/acp-leader-confirm.d.ts +2 -0
- package/dist/acp-leader/acp-leader-host-binding.d.ts +1 -0
- package/dist/acp-leader/acp-leader-host.d.ts +9 -0
- package/dist/acp-leader/acp-leader-picker.d.ts +6 -3
- package/dist/acp-leader/acp-leader-provider.d.ts +10 -0
- package/dist/acp-leader/acp-leader-skills.d.ts +5 -0
- package/dist/acp-leader/acp-leader-stream.d.ts +9 -0
- package/dist/{auth-IHQS6AFW.js → auth-2ZG5DBHS.js} +4 -4
- package/dist/boot/launch-menu-arrow.d.ts +20 -0
- package/dist/boot/launch-menu-types.d.ts +36 -0
- package/dist/boot/launch-menu.d.ts +1 -57
- package/dist/boot/tui-session-resume-rollback.d.ts +31 -0
- package/dist/{boot-auto-thin-7D4OVEWA.js → boot-auto-thin-N2CIGTXE.js} +3 -3
- package/dist/{chunk-TZPKOS5G.js → chunk-2XQ6SRKG.js} +2 -2
- package/dist/{chunk-3EXRPTAU.js → chunk-5TQ65PXQ.js} +17 -17
- package/dist/{chunk-VAQXOZKP.js → chunk-AJM4EFCM.js} +139 -136
- package/dist/{chunk-C2BBH4S4.js → chunk-E3MJFAFR.js} +2 -2
- package/dist/{chunk-COGI7EQU.js → chunk-EAATL6FL.js} +713 -21
- package/dist/{chunk-AWRZWPJ3.js → chunk-FWUIKCCO.js} +152 -123
- package/dist/{chunk-TAE3EGBM.js → chunk-H5DCQFUI.js} +5 -5
- package/dist/{chunk-BIGSVBPI.js → chunk-KNYIEQAY.js} +64 -61
- package/dist/chunk-LYWOV4OX.js +252 -0
- package/dist/{chunk-75OLD563.js → chunk-M7PSENGF.js} +3 -1
- package/dist/{chunk-I5LJLKA3.js → chunk-MKMM5RIW.js} +12 -5
- package/dist/{chunk-ZF4HRR5F.js → chunk-P7MEEUGT.js} +275 -250
- package/dist/chunk-PGC44T7N.js +255 -0
- package/dist/chunk-QLYPIJ7X.js +258 -0
- package/dist/{chunk-JKTJSLUT.js → chunk-QRWXHGLB.js} +242 -18
- package/dist/chunk-RN5CFAVQ.js +292 -0
- package/dist/{chunk-WDXNBYGD.js → chunk-TDP7CZGE.js} +6 -1
- package/dist/{chunk-6VPWQ4TG.js → chunk-UROB7MK5.js} +2 -1
- package/dist/{chunk-IJLIAPCW.js → chunk-VPSAKGLK.js} +47 -47
- package/dist/{chunk-2LTHDKZX.js → chunk-W7X23HI6.js} +18 -18
- package/dist/{chunk-GKGWPFW2.js → chunk-X4DEQLAZ.js} +1 -1
- package/dist/{cli-context-E44HQ4ZO.js → cli-context-6KS3HV5R.js} +247 -236
- package/dist/{cli-main-OT63I2FX.js → cli-main-NF3YGDME.js} +1779 -1494
- package/dist/cli-main-boot.d.ts +1 -1
- package/dist/config-doctor-known-keys.d.ts +33 -0
- package/dist/config-doctor.d.ts +2 -47
- package/dist/config-history-support.d.ts +36 -0
- package/dist/config-history.d.ts +1 -1
- package/dist/{config-transfer-Y7XJMJRN.js → config-transfer-B4GOZTFC.js} +2 -2
- package/dist/{execution-OK34CJKJ.js → execution-RVDUXLNT.js} +6522 -6426
- package/dist/execution-tui.d.ts +30 -0
- package/dist/fleet/acp-wrongstack-bridge.d.ts +11 -3
- package/dist/fleet/host-acp.d.ts +3 -0
- package/dist/fleet/wrongforge-client.d.ts +116 -0
- package/dist/fleet/wrongforge-gateway.d.ts +20 -0
- package/dist/fleet/wrongforge-info.d.ts +32 -0
- package/dist/fleet/wrongforge-quota.d.ts +10 -0
- package/dist/fleet/wrongforge-runner.d.ts +37 -0
- package/dist/fleet/wrongforge-runtime.d.ts +10 -0
- package/dist/{hq-JD5AZVMG.js → hq-V2M4IYCI.js} +320 -306
- package/dist/hq-server/login-attempt-store.d.ts +2 -0
- package/dist/hq-server/sweeps.d.ts +28 -0
- package/dist/{hq-server-BI23EPM7.js → hq-server-QM7EIVGJ.js} +3 -3
- package/dist/index.js +5 -5
- package/dist/mcp-22CFBX4H.js +236 -0
- package/dist/mcp-serve.d.ts +0 -63
- package/dist/{modeldiag-HDIKG37X.js → modeldiag-5PBB777J.js} +4 -4
- package/dist/{per-subcommand-help-4GYSCMLU.js → per-subcommand-help-MQGERD4E.js} +2 -2
- package/dist/plugin-llm-command.d.ts +9 -0
- package/dist/{plugin-usage-TYF5HY4K.js → plugin-usage-UWKYMNQU.js} +3 -3
- package/dist/{plugins-XGAXDLPT.js → plugins-VP7JQH47.js} +4 -4
- package/dist/{sdd-runtime-6MFUXWJK.js → sdd-runtime-HIJ4Z5TF.js} +2 -2
- package/dist/services/sdd/state.d.ts +2 -0
- package/dist/session-event-wiring-types.d.ts +62 -0
- package/dist/session-event-wiring.d.ts +2 -52
- package/dist/{sessions-config-SJVQXCIE.js → sessions-config-7MK742OW.js} +2 -2
- package/dist/short-circuit-flags-276OLPIN.js +11 -0
- package/dist/slash-commands/command-context-fleet.d.ts +187 -0
- package/dist/slash-commands/command-context-sdd-goal.d.ts +102 -0
- package/dist/slash-commands/command-context.d.ts +3 -282
- package/dist/slash-commands/project-switch-interactive.d.ts +11 -0
- package/dist/slash-commands/setmodel-helpers.d.ts +36 -0
- package/dist/subcommands/handlers/hq-token.d.ts +3 -0
- package/dist/subcommands/handlers/hq.d.ts +0 -21
- package/dist/subcommands/handlers/per-subcommand-help-operations.d.ts +8 -0
- package/dist/subcommands/handlers/tools-cli-args.d.ts +57 -0
- package/dist/subcommands/handlers/tools-cli.d.ts +5 -0
- package/dist/subcommands/handlers/tools-serve.d.ts +4 -0
- package/dist/{subcommands-YADUV7IJ.js → subcommands-SKJPUJ7N.js} +2 -2
- package/dist/tool-gateway-http.d.ts +30 -0
- package/dist/tool-gateway.d.ts +185 -0
- package/dist/tools-cli-44MLIUQO.js +509 -0
- package/dist/tools-serve-TRCET2M7.js +102 -0
- package/dist/{tools-skills-55QVTMSK.js → tools-skills-WDTMCE46.js} +7 -2
- package/dist/tuneup-runtime-checks.d.ts +7 -0
- package/dist/tuneup-types.d.ts +118 -0
- package/dist/tuneup.d.ts +3 -122
- package/dist/{typesafe-YBEADNAH.js → typesafe-QLSMFU7J.js} +1 -1
- package/dist/{version-help-RBZC5GZZ.js → version-help-X6CC4CFY.js} +2 -2
- package/dist/{webui-server-CB64CFDG.js → webui-server-6I6QOECV.js} +44 -6
- package/dist/wiring/provider-runtime-setup.d.ts +3 -1
- package/dist/wiring/session-runtime.d.ts +1 -1
- package/dist/wrongforge-gateway-UOFBK56J.js +92 -0
- package/dist/wrongforge-leader/wrongforge-leader-gateway.d.ts +9 -0
- package/dist/wrongforge-leader/wrongforge-leader-picker.d.ts +12 -0
- package/dist/wrongforge-leader/wrongforge-leader-provider.d.ts +56 -0
- package/package.json +1 -1
- package/dist/mcp-W7A5HUXV.js +0 -560
- package/dist/short-circuit-flags-QEXUS3BY.js +0 -11
|
@@ -1,9 +1,27 @@
|
|
|
1
1
|
import {
|
|
2
|
+
parseWrongForgeInfo,
|
|
3
|
+
reportWrongForgeQuota,
|
|
4
|
+
writeWrongForgeInfoCache,
|
|
2
5
|
wrongstackBridgeSetup
|
|
3
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-QRWXHGLB.js";
|
|
4
7
|
import {
|
|
5
8
|
isRestrictedMode
|
|
6
9
|
} from "./chunk-3LEIFO5D.js";
|
|
10
|
+
import {
|
|
11
|
+
WRONGFORGE_AGENT_ID,
|
|
12
|
+
WRONGFORGE_READ_SIDE_TOOLS,
|
|
13
|
+
WfAborted,
|
|
14
|
+
WfHttpError,
|
|
15
|
+
cancelTask,
|
|
16
|
+
chooseModel,
|
|
17
|
+
followTask,
|
|
18
|
+
resolveWrongForgeEndpoint,
|
|
19
|
+
safeWfId,
|
|
20
|
+
terminalTaskRecord,
|
|
21
|
+
toProgress,
|
|
22
|
+
wfTokens,
|
|
23
|
+
wrongForgeCall
|
|
24
|
+
} from "./chunk-PGC44T7N.js";
|
|
7
25
|
import {
|
|
8
26
|
buildProviderForId,
|
|
9
27
|
resolveProviderCfgWithProxy
|
|
@@ -12,7 +30,7 @@ import {
|
|
|
12
30
|
ACP_LEADER_PROVIDER_ID,
|
|
13
31
|
acpLeaderHost,
|
|
14
32
|
trackAcpLeader
|
|
15
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-X4DEQLAZ.js";
|
|
16
34
|
|
|
17
35
|
// src/acp-agent-list.ts
|
|
18
36
|
import { REGISTRY_ID_ALIASES } from "@wrongstack/acp";
|
|
@@ -449,6 +467,7 @@ function startAcpTurnReminders(options) {
|
|
|
449
467
|
|
|
450
468
|
// src/acp-leader/acp-leader-confirm.ts
|
|
451
469
|
import { confirmObserverCount } from "@wrongstack/core/agent";
|
|
470
|
+
import { DEFAULT_ALWAYS_TRUST_TTL_MS, scopedApprovalPattern } from "@wrongstack/core/security";
|
|
452
471
|
var ACP_CONFIRM_TIMEOUT_MS = 12e4;
|
|
453
472
|
async function confirmThroughEvents(req) {
|
|
454
473
|
if (req.events.listenerCount("tool.confirm_needed") - confirmObserverCount() <= 0) return false;
|
|
@@ -489,10 +508,22 @@ async function confirmThroughEvents(req) {
|
|
|
489
508
|
});
|
|
490
509
|
});
|
|
491
510
|
if (answer === "abort" || answer === "no") return false;
|
|
492
|
-
const rule = { tool: req.tool.name, pattern: req.suggestedPattern };
|
|
493
511
|
try {
|
|
494
|
-
if (answer === "deny")
|
|
495
|
-
|
|
512
|
+
if (answer === "deny") {
|
|
513
|
+
await req.permission?.deny({ tool: req.tool.name, pattern: req.suggestedPattern });
|
|
514
|
+
} else if (answer !== "yes") {
|
|
515
|
+
await req.permission?.trust({
|
|
516
|
+
tool: req.tool.name,
|
|
517
|
+
pattern: scopedApprovalPattern(
|
|
518
|
+
answer,
|
|
519
|
+
req.tool,
|
|
520
|
+
req.input,
|
|
521
|
+
req.context,
|
|
522
|
+
req.suggestedPattern
|
|
523
|
+
),
|
|
524
|
+
ttlMs: DEFAULT_ALWAYS_TRUST_TTL_MS
|
|
525
|
+
});
|
|
526
|
+
}
|
|
496
527
|
} catch {
|
|
497
528
|
}
|
|
498
529
|
return answer !== "deny";
|
|
@@ -538,6 +569,7 @@ function publishLeaderContext(events, sessionId, reading, fallbackMaxContext) {
|
|
|
538
569
|
|
|
539
570
|
// src/acp-leader/acp-leader-fleet.ts
|
|
540
571
|
import { randomBytes } from "node:crypto";
|
|
572
|
+
import { toErrorMessage as toErrorMessage2 } from "@wrongstack/core/utils";
|
|
541
573
|
import { MCPServer, serveHttp } from "@wrongstack/mcp";
|
|
542
574
|
var FLEET_SERVER_NAME = "wrongstack-fleet";
|
|
543
575
|
var FLEET_TOOLS = [
|
|
@@ -548,7 +580,10 @@ var FLEET_TOOLS = [
|
|
|
548
580
|
"roll_up",
|
|
549
581
|
"fleet_status",
|
|
550
582
|
"session_note",
|
|
551
|
-
"terminate_subagent"
|
|
583
|
+
"terminate_subagent",
|
|
584
|
+
// Not a fleet tool, but it too must run in this process: WrongStack's
|
|
585
|
+
// skills (a /bughunt playbook) reach the agent only through it.
|
|
586
|
+
"skill"
|
|
552
587
|
];
|
|
553
588
|
var MCP_PREFIX = `mcp__${FLEET_SERVER_NAME}__`;
|
|
554
589
|
function fleetToolName(mcpName) {
|
|
@@ -574,7 +609,11 @@ async function startFleetEndpoint(host, run) {
|
|
|
574
609
|
if (!offered.has(name)) {
|
|
575
610
|
return { content: `Tool "${name}" is not offered here`, isError: true };
|
|
576
611
|
}
|
|
577
|
-
|
|
612
|
+
try {
|
|
613
|
+
return await (run ? host.runTool(name, args, run) : host.runTool(name, args));
|
|
614
|
+
} catch (err) {
|
|
615
|
+
return { content: `Failed to execute ${name}: ${toErrorMessage2(err)}`, isError: true };
|
|
616
|
+
}
|
|
578
617
|
}
|
|
579
618
|
}
|
|
580
619
|
});
|
|
@@ -600,7 +639,11 @@ function fleetNote(tools) {
|
|
|
600
639
|
...has("fleet_status") ? ["- fleet_status: what is running and its progress."] : [],
|
|
601
640
|
...has("roll_up") ? ["- roll_up: the full result of finished tasks."] : [],
|
|
602
641
|
...has("session_note") ? ["- session_note: steer a running worker."] : [],
|
|
603
|
-
...has("terminate_subagent") ? ["- terminate_subagent: stop a worker."] : []
|
|
642
|
+
...has("terminate_subagent") ? ["- terminate_subagent: stop a worker."] : [],
|
|
643
|
+
...has("skill") ? [
|
|
644
|
+
"- skill: load one of WrongStack's skills by name (follow nextOffset to the last page);",
|
|
645
|
+
" a task that names WrongStack skills means these."
|
|
646
|
+
] : []
|
|
604
647
|
].join("\n");
|
|
605
648
|
}
|
|
606
649
|
|
|
@@ -643,9 +686,28 @@ function writeLeaderSession(stateDir, sessionId, record) {
|
|
|
643
686
|
return writing.catch(() => void 0);
|
|
644
687
|
}
|
|
645
688
|
|
|
689
|
+
// src/acp-leader/acp-leader-skills.ts
|
|
690
|
+
import { parseRequiredSkillsMarker } from "@wrongstack/core/skills";
|
|
691
|
+
var MARKERS = /<!--\s*wrongstack:required-skills\s+[\s\S]*?-->/g;
|
|
692
|
+
function externalSkillLoader(offered, prefix) {
|
|
693
|
+
return offered?.includes("skill") ? `${prefix}skill` : void 0;
|
|
694
|
+
}
|
|
695
|
+
function externalSkillDirective(text, loader) {
|
|
696
|
+
const skills = parseRequiredSkillsMarker(text);
|
|
697
|
+
if (!skills) return text;
|
|
698
|
+
const list = skills.join(", ");
|
|
699
|
+
const line = loader ? `[WrongStack] This task's playbook skills: ${list}. They are WrongStack skills: load each with the \`${loader}\` tool, following nextOffset to the last page, before changing code. Where the text below names the \`skill\` tool or a $name, it means that tool and these skills.` : `[WrongStack] This task's playbook skills: ${list}. This route offers no WrongStack skill tool: load them with your own skill tool if you have them, otherwise follow the protocol below without them.`;
|
|
700
|
+
let first = true;
|
|
701
|
+
return text.replace(MARKERS, () => {
|
|
702
|
+
if (!first) return "";
|
|
703
|
+
first = false;
|
|
704
|
+
return line;
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
|
|
646
708
|
// src/acp-leader/acp-leader-stream.ts
|
|
647
709
|
import { ProviderError } from "@wrongstack/core/types";
|
|
648
|
-
import { toErrorMessage as
|
|
710
|
+
import { toErrorMessage as toErrorMessage3 } from "@wrongstack/core/utils";
|
|
649
711
|
var StreamQueue = class {
|
|
650
712
|
items = [];
|
|
651
713
|
wake;
|
|
@@ -711,10 +773,7 @@ function clip(text, max) {
|
|
|
711
773
|
if (last >= 55296 && last <= 56319) end -= 1;
|
|
712
774
|
return `${text.slice(0, end)}\u2026`;
|
|
713
775
|
}
|
|
714
|
-
function
|
|
715
|
-
const raw = call.rawOutput;
|
|
716
|
-
const text = call.output ?? (raw ? typeof raw.output === "string" ? raw.output : JSON.stringify(raw) : void 0);
|
|
717
|
-
if (text === void 0) return void 0;
|
|
776
|
+
function cardOutputForText(text) {
|
|
718
777
|
const output = clip(text, OUTPUT_PREVIEW_CHARS);
|
|
719
778
|
return {
|
|
720
779
|
output,
|
|
@@ -723,6 +782,12 @@ function cardOutput(call) {
|
|
|
723
782
|
outputBytes: Buffer.byteLength(text)
|
|
724
783
|
};
|
|
725
784
|
}
|
|
785
|
+
function cardOutput(call) {
|
|
786
|
+
const raw = call.rawOutput;
|
|
787
|
+
const text = call.output ?? (raw ? typeof raw.output === "string" ? raw.output : JSON.stringify(raw) : void 0);
|
|
788
|
+
if (text === void 0) return void 0;
|
|
789
|
+
return cardOutputForText(text);
|
|
790
|
+
}
|
|
726
791
|
var ToolCardRelay = class {
|
|
727
792
|
constructor(events, meta) {
|
|
728
793
|
this.events = events;
|
|
@@ -744,7 +809,7 @@ var ToolCardRelay = class {
|
|
|
744
809
|
name,
|
|
745
810
|
id,
|
|
746
811
|
input: call.rawInput,
|
|
747
|
-
provider: "acp",
|
|
812
|
+
provider: this.meta.provider ?? "acp",
|
|
748
813
|
model: this.meta.model
|
|
749
814
|
});
|
|
750
815
|
}
|
|
@@ -855,7 +920,7 @@ function agentTurnError(err, agentId, classify) {
|
|
|
855
920
|
};
|
|
856
921
|
}
|
|
857
922
|
const kind = err?.kind;
|
|
858
|
-
const message = `${agentId}: ${
|
|
923
|
+
const message = `${agentId}: ${toErrorMessage3(err)}`;
|
|
859
924
|
if (kind === "stalled") {
|
|
860
925
|
return {
|
|
861
926
|
error: new ProviderError(message, 408, false, "acp", { kind: "timeout", cause: err }),
|
|
@@ -910,8 +975,11 @@ function parseSideModel(ref) {
|
|
|
910
975
|
if (provider === ACP_LEADER_PROVIDER_ID) return void 0;
|
|
911
976
|
return { provider, model: ref.slice(slash + 1) };
|
|
912
977
|
}
|
|
978
|
+
function explicitLeaderSideModel(config) {
|
|
979
|
+
return parseSideModel(config.acp?.leader?.sideModel);
|
|
980
|
+
}
|
|
913
981
|
function leaderSideModel(config) {
|
|
914
|
-
return
|
|
982
|
+
return explicitLeaderSideModel(config) ?? parseSideModel(config.fallbackBridge) ?? parseSideModel(config.fallbackModels?.[0]);
|
|
915
983
|
}
|
|
916
984
|
function isLeaderTurn(req) {
|
|
917
985
|
const { sessionId, threadId } = req.cache ?? {};
|
|
@@ -1029,7 +1097,10 @@ var AcpLeaderProvider = class {
|
|
|
1029
1097
|
}
|
|
1030
1098
|
/** The turn's prompt; an agent joining a conversation under way gets the turns before it. */
|
|
1031
1099
|
promptText(leader, req) {
|
|
1032
|
-
const latest =
|
|
1100
|
+
const latest = externalSkillDirective(
|
|
1101
|
+
latestUserText(req.messages),
|
|
1102
|
+
externalSkillLoader(leader.fleet?.tools, `mcp__${FLEET_SERVER_NAME}__`)
|
|
1103
|
+
);
|
|
1033
1104
|
const earlier = leader.joined ? void 0 : earlierConversation(req.messages);
|
|
1034
1105
|
leader.joined = true;
|
|
1035
1106
|
const prompt = earlier ? `${earlier}
|
|
@@ -1105,8 +1176,20 @@ ${withNotes}` : withNotes;
|
|
|
1105
1176
|
this.acp ??= await import("@wrongstack/acp");
|
|
1106
1177
|
return this.acp;
|
|
1107
1178
|
}
|
|
1179
|
+
dropFleetGrants(sessionId) {
|
|
1180
|
+
for (const grantKey of this.fleetGrants.keys()) {
|
|
1181
|
+
try {
|
|
1182
|
+
const parsed = JSON.parse(grantKey);
|
|
1183
|
+
if (Array.isArray(parsed) && parsed[0] === sessionId) {
|
|
1184
|
+
this.fleetGrants.delete(grantKey);
|
|
1185
|
+
}
|
|
1186
|
+
} catch {
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1108
1190
|
drop(key, leader) {
|
|
1109
1191
|
this.sessions.delete(key);
|
|
1192
|
+
this.dropFleetGrants(key);
|
|
1110
1193
|
void leader.session.close().catch(() => {
|
|
1111
1194
|
});
|
|
1112
1195
|
void leader.fleet?.close().catch(() => {
|
|
@@ -1176,7 +1259,8 @@ ${withNotes}` : withNotes;
|
|
|
1176
1259
|
toolUseId,
|
|
1177
1260
|
suggestedPattern,
|
|
1178
1261
|
signal: request.signal,
|
|
1179
|
-
permission: host.permission(sessionId)
|
|
1262
|
+
permission: host.permission(sessionId),
|
|
1263
|
+
context: host.context(sessionId)
|
|
1180
1264
|
});
|
|
1181
1265
|
}
|
|
1182
1266
|
async startSession(key, agentId) {
|
|
@@ -1280,6 +1364,7 @@ ${fleetNote(fleet.tools)}` : "")
|
|
|
1280
1364
|
async closeAll() {
|
|
1281
1365
|
const all = [...this.sessions.values()];
|
|
1282
1366
|
this.sessions.clear();
|
|
1367
|
+
this.fleetGrants.clear();
|
|
1283
1368
|
await Promise.all(
|
|
1284
1369
|
all.map(
|
|
1285
1370
|
(p) => p.then((leader) => {
|
|
@@ -1302,6 +1387,24 @@ function createAcpLeaderProviderFactory(opts) {
|
|
|
1302
1387
|
};
|
|
1303
1388
|
}
|
|
1304
1389
|
|
|
1390
|
+
// src/fleet/wrongforge-runtime.ts
|
|
1391
|
+
var WRONGFORGE_REASONING = {
|
|
1392
|
+
default: "adaptive",
|
|
1393
|
+
disableSupported: true,
|
|
1394
|
+
effortSupported: true,
|
|
1395
|
+
effortLevels: ["none", "minimal", "low", "medium", "high", "xhigh", "max"],
|
|
1396
|
+
preserveThinking: "unsupported"
|
|
1397
|
+
};
|
|
1398
|
+
function wrongForgeRuntime(reasoning, cache, info) {
|
|
1399
|
+
const effort = reasoning?.effort;
|
|
1400
|
+
return {
|
|
1401
|
+
...reasoning?.enabled === false || effort === "none" ? { effort: "off" } : effort ? {
|
|
1402
|
+
effort: effort === "minimal" ? "low" : (effort === "xhigh" || effort === "max") && !info?.effortLevels?.includes(effort) ? "high" : effort
|
|
1403
|
+
} : {},
|
|
1404
|
+
...cache?.ttl ? { cache: cache.ttl } : {}
|
|
1405
|
+
};
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1305
1408
|
// src/wiring/provider.ts
|
|
1306
1409
|
import { ProviderAuthRegistry, ProviderRegistry } from "@wrongstack/core/registry";
|
|
1307
1410
|
import { ConfigError } from "@wrongstack/core/types";
|
|
@@ -1317,6 +1420,577 @@ import {
|
|
|
1317
1420
|
withCatalogCapabilities
|
|
1318
1421
|
} from "@wrongstack/providers";
|
|
1319
1422
|
import { registerBuiltinProviderAuthStrategies } from "@wrongstack/providers/oauth";
|
|
1423
|
+
|
|
1424
|
+
// src/wrongforge-leader/wrongforge-leader-provider.ts
|
|
1425
|
+
import { consumeBtwNotes as consumeBtwNotes3 } from "@wrongstack/core/agent";
|
|
1426
|
+
import { ProviderError as ProviderError3 } from "@wrongstack/core/types";
|
|
1427
|
+
import { toErrorMessage as toErrorMessage5 } from "@wrongstack/core/utils";
|
|
1428
|
+
|
|
1429
|
+
// src/wrongforge-leader/wrongforge-leader-gateway.ts
|
|
1430
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
1431
|
+
import http from "node:http";
|
|
1432
|
+
import {
|
|
1433
|
+
compactToolDefinitionForWire,
|
|
1434
|
+
subjectForToolInput as subjectForToolInput2,
|
|
1435
|
+
toErrorMessage as toErrorMessage4
|
|
1436
|
+
} from "@wrongstack/core/utils";
|
|
1437
|
+
import { timingSafeTokenEqual } from "@wrongstack/primitives";
|
|
1438
|
+
var NOT_OFFERED = /* @__PURE__ */ new Set([
|
|
1439
|
+
"read",
|
|
1440
|
+
"glob",
|
|
1441
|
+
"grep",
|
|
1442
|
+
"fetch",
|
|
1443
|
+
"read_url_content",
|
|
1444
|
+
"tree",
|
|
1445
|
+
"clarify",
|
|
1446
|
+
"tool_use",
|
|
1447
|
+
"tool_script",
|
|
1448
|
+
"tool_search"
|
|
1449
|
+
]);
|
|
1450
|
+
var MAX_BODY = 4 * 1024 * 1024;
|
|
1451
|
+
async function askUser(host, sessionId, tool, input) {
|
|
1452
|
+
const suggestedPattern = subjectForToolInput2(tool.name, input, tool.subjectKey, tool.subjectFields) ?? tool.name;
|
|
1453
|
+
const toolUseId = `wrongforge-${randomBytes2(6).toString("hex")}`;
|
|
1454
|
+
if (host.confirmAwaiter) {
|
|
1455
|
+
const answer = await host.confirmAwaiter(tool, input, toolUseId, suggestedPattern);
|
|
1456
|
+
return answer !== "no" && answer !== "deny";
|
|
1457
|
+
}
|
|
1458
|
+
if (!host.confirmThroughEvents) return false;
|
|
1459
|
+
return confirmThroughEvents({
|
|
1460
|
+
events: host.events,
|
|
1461
|
+
sessionId,
|
|
1462
|
+
tool,
|
|
1463
|
+
input,
|
|
1464
|
+
toolUseId,
|
|
1465
|
+
suggestedPattern,
|
|
1466
|
+
signal: new AbortController().signal,
|
|
1467
|
+
permission: host.permission(sessionId),
|
|
1468
|
+
context: host.context(sessionId)
|
|
1469
|
+
});
|
|
1470
|
+
}
|
|
1471
|
+
async function startLeaderGateway(host, sessionId) {
|
|
1472
|
+
const tools = (host.listTools?.() ?? []).filter((t) => !NOT_OFFERED.has(t.name));
|
|
1473
|
+
if (tools.length === 0) return void 0;
|
|
1474
|
+
const byName = new Map(tools.map((t) => [t.name, t]));
|
|
1475
|
+
const wireTools = tools.map(
|
|
1476
|
+
(t) => compactToolDefinitionForWire({
|
|
1477
|
+
name: t.name,
|
|
1478
|
+
description: t.description,
|
|
1479
|
+
inputSchema: t.inputSchema ?? { type: "object" }
|
|
1480
|
+
})
|
|
1481
|
+
);
|
|
1482
|
+
const token = randomBytes2(24).toString("hex");
|
|
1483
|
+
const expected = `Bearer ${token}`;
|
|
1484
|
+
const authorized = (req) => timingSafeTokenEqual(req.headers.authorization, expected);
|
|
1485
|
+
const send = (res, status, body) => {
|
|
1486
|
+
const text = JSON.stringify(body);
|
|
1487
|
+
res.writeHead(status, {
|
|
1488
|
+
"content-type": "application/json; charset=utf-8",
|
|
1489
|
+
"content-length": Buffer.byteLength(text)
|
|
1490
|
+
});
|
|
1491
|
+
res.end(text);
|
|
1492
|
+
};
|
|
1493
|
+
const server = http.createServer((req, res) => {
|
|
1494
|
+
if (!authorized(req)) return send(res, 401, { ok: false, error: "unauthorized" });
|
|
1495
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
1496
|
+
if (req.method === "GET" && url.pathname === "/v1/tools") {
|
|
1497
|
+
return send(res, 200, {
|
|
1498
|
+
tools: wireTools.map(({ name: name2, description, inputSchema }) => ({
|
|
1499
|
+
name: name2,
|
|
1500
|
+
description,
|
|
1501
|
+
input_schema: inputSchema
|
|
1502
|
+
}))
|
|
1503
|
+
});
|
|
1504
|
+
}
|
|
1505
|
+
const m = /^\/v1\/tools\/([^/]+)\/call$/.exec(url.pathname);
|
|
1506
|
+
if (req.method !== "POST" || !m) return send(res, 404, { ok: false, error: "not_found" });
|
|
1507
|
+
const name = decodeURIComponent(m[1] ?? "");
|
|
1508
|
+
const tool = byName.get(name);
|
|
1509
|
+
if (!tool) {
|
|
1510
|
+
return send(res, 404, { ok: false, tool: name, error: "not_offered" });
|
|
1511
|
+
}
|
|
1512
|
+
const chunks = [];
|
|
1513
|
+
let size = 0;
|
|
1514
|
+
req.on("data", (c) => {
|
|
1515
|
+
size += c.length;
|
|
1516
|
+
if (size > MAX_BODY) req.destroy();
|
|
1517
|
+
else chunks.push(c);
|
|
1518
|
+
});
|
|
1519
|
+
req.on("end", () => {
|
|
1520
|
+
let input = {};
|
|
1521
|
+
try {
|
|
1522
|
+
const body = JSON.parse(Buffer.concat(chunks).toString("utf8") || "{}");
|
|
1523
|
+
if (body.input && typeof body.input === "object" && !Array.isArray(body.input)) {
|
|
1524
|
+
input = body.input;
|
|
1525
|
+
}
|
|
1526
|
+
} catch {
|
|
1527
|
+
return send(res, 400, { ok: false, tool: name, error: "invalid_json" });
|
|
1528
|
+
}
|
|
1529
|
+
host.runTool(name, input, {
|
|
1530
|
+
sessionId,
|
|
1531
|
+
approve: (t, i) => askUser(host, sessionId, t, i),
|
|
1532
|
+
// The provider draws no card for a `wstack_*` call (its events are
|
|
1533
|
+
// marked external), so the session's card is settled here.
|
|
1534
|
+
announce: true
|
|
1535
|
+
}).then(
|
|
1536
|
+
(r) => send(res, 200, { ok: !r.isError, tool: name, content: r.content }),
|
|
1537
|
+
(err) => send(res, 200, {
|
|
1538
|
+
ok: false,
|
|
1539
|
+
tool: name,
|
|
1540
|
+
error: "tool_failed",
|
|
1541
|
+
message: toErrorMessage4(err)
|
|
1542
|
+
})
|
|
1543
|
+
);
|
|
1544
|
+
});
|
|
1545
|
+
});
|
|
1546
|
+
await new Promise((resolve, reject) => {
|
|
1547
|
+
server.once("error", reject);
|
|
1548
|
+
server.listen(0, "127.0.0.1", () => {
|
|
1549
|
+
server.removeListener("error", reject);
|
|
1550
|
+
resolve();
|
|
1551
|
+
});
|
|
1552
|
+
});
|
|
1553
|
+
server.unref();
|
|
1554
|
+
const port = server.address().port;
|
|
1555
|
+
return {
|
|
1556
|
+
spec: {
|
|
1557
|
+
url: `http://127.0.0.1:${port}`,
|
|
1558
|
+
token,
|
|
1559
|
+
list_path: "/v1/tools",
|
|
1560
|
+
call_path: "/v1/tools/{name}/call",
|
|
1561
|
+
name_prefix: "wstack_"
|
|
1562
|
+
},
|
|
1563
|
+
tools: [...byName.keys()],
|
|
1564
|
+
close: () => new Promise((resolve) => server.close(() => resolve()))
|
|
1565
|
+
};
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
// src/wrongforge-leader/wrongforge-leader-provider.ts
|
|
1569
|
+
var WRONGFORGE_LEADER_PROVIDER_ID = WRONGFORGE_AGENT_ID;
|
|
1570
|
+
var LEADER_MAX_STEPS = 150;
|
|
1571
|
+
var LEADER_TURN_TIMEOUT_MS = 2 * 60 * 6e4;
|
|
1572
|
+
var CAPABILITIES2 = {
|
|
1573
|
+
tools: true,
|
|
1574
|
+
parallelTools: false,
|
|
1575
|
+
vision: false,
|
|
1576
|
+
streaming: true,
|
|
1577
|
+
promptCache: true,
|
|
1578
|
+
systemPrompt: true,
|
|
1579
|
+
jsonMode: false,
|
|
1580
|
+
reasoning: true,
|
|
1581
|
+
// Learned from WrongForge's first task result (its model's window).
|
|
1582
|
+
maxContext: 0,
|
|
1583
|
+
maxOutput: 32e3,
|
|
1584
|
+
cacheControl: "native",
|
|
1585
|
+
topK: false,
|
|
1586
|
+
frequencyPenalty: false,
|
|
1587
|
+
presencePenalty: false,
|
|
1588
|
+
seed: false,
|
|
1589
|
+
structuredOutput: false,
|
|
1590
|
+
logprobs: false,
|
|
1591
|
+
audio: false,
|
|
1592
|
+
multipleCompletions: false,
|
|
1593
|
+
// WrongForge keeps the conversation (its session) and compacts it itself.
|
|
1594
|
+
keepsOwnContext: true
|
|
1595
|
+
};
|
|
1596
|
+
function leaderAppend(routed, tools) {
|
|
1597
|
+
const has = (n) => tools.includes(n);
|
|
1598
|
+
return [
|
|
1599
|
+
"In this task you LEAD a WrongStack session: a human user talks to you through",
|
|
1600
|
+
"WrongStack, and your reply goes to them, not to an orchestrator. Answer them directly,",
|
|
1601
|
+
"ask when something is truly ambiguous, and end a turn when the work or the answer is done.",
|
|
1602
|
+
"The worker-only rules of the WrongStack guide (no human watching, reply = report to a",
|
|
1603
|
+
"leader) do not apply to you; the rest (stay in the workdir, no git history rewrites, no",
|
|
1604
|
+
"commit or push unless asked) does.",
|
|
1605
|
+
...routed ? [
|
|
1606
|
+
"Your own write/edit/shell tools are off: change files with WrongStack's `wstack_edit` /",
|
|
1607
|
+
"`wstack_write` / `wstack_patch` and run commands with `wstack_bash` (or `wstack_test`,",
|
|
1608
|
+
"`wstack_lint`, `wstack_typecheck`). They run in the user's session, under their rules."
|
|
1609
|
+
] : [],
|
|
1610
|
+
...has("delegate") ? [
|
|
1611
|
+
"`wstack_delegate` hands work to WrongStack workers (scope and outOfScope required); the",
|
|
1612
|
+
"result arrives later as a [DELEGATION RESULT] message, so end your turn or keep working."
|
|
1613
|
+
] : [],
|
|
1614
|
+
...has("remember") || has("memory_search") ? ["`wstack_memory_search` / `wstack_remember` are the project memory: check it, add to it."] : []
|
|
1615
|
+
].join(" ");
|
|
1616
|
+
}
|
|
1617
|
+
function flatten(req) {
|
|
1618
|
+
const text = (m) => typeof m.content === "string" ? m.content : m.content.map((b) => "text" in b && typeof b.text === "string" ? b.text : "").filter(Boolean).join("\n");
|
|
1619
|
+
const system = (req.system ?? []).map((b) => b.text).join("\n\n");
|
|
1620
|
+
const turns = req.messages.map((m) => `[${m.role}]
|
|
1621
|
+
${text(m)}`).join("\n\n");
|
|
1622
|
+
return [
|
|
1623
|
+
system ? `<instructions>
|
|
1624
|
+
${system}
|
|
1625
|
+
</instructions>` : "",
|
|
1626
|
+
turns,
|
|
1627
|
+
"Reply to the last message, following the instructions above. Use no tools."
|
|
1628
|
+
].filter(Boolean).join("\n\n");
|
|
1629
|
+
}
|
|
1630
|
+
function providerError(err, id) {
|
|
1631
|
+
if (err instanceof ProviderError3) return err;
|
|
1632
|
+
const make = (message, status, retryable, kind) => new ProviderError3(`WrongForge: ${message}`, status, retryable, id, { kind });
|
|
1633
|
+
if (err instanceof WfAborted) return make("the turn was cancelled", 499, false, "unknown");
|
|
1634
|
+
if (err instanceof WfHttpError) {
|
|
1635
|
+
if (err.status === 401 || err.status === 403 || err.code === "not_signed_in")
|
|
1636
|
+
return make(err.message, 401, false, "auth");
|
|
1637
|
+
if (err.status === 0) return make(err.message, 0, true, "network");
|
|
1638
|
+
if (err.status >= 500) return make(err.message, err.status, true, "server");
|
|
1639
|
+
return make(`${err.code}: ${err.message}`, err.status, false, "invalid_request");
|
|
1640
|
+
}
|
|
1641
|
+
return make(toErrorMessage5(err), 0, false, "unknown");
|
|
1642
|
+
}
|
|
1643
|
+
function taskError(record, id) {
|
|
1644
|
+
const make = (message, status, retryable, kind) => new ProviderError3(`WrongForge: ${message}`, status, retryable, id, { kind });
|
|
1645
|
+
switch (record.status) {
|
|
1646
|
+
case "completed":
|
|
1647
|
+
return void 0;
|
|
1648
|
+
case "cancelled":
|
|
1649
|
+
return make(
|
|
1650
|
+
`the turn was cancelled${record.cancel_reason ? ` (${record.cancel_reason})` : ""}`,
|
|
1651
|
+
499,
|
|
1652
|
+
false,
|
|
1653
|
+
"unknown"
|
|
1654
|
+
);
|
|
1655
|
+
case "timed_out":
|
|
1656
|
+
return make("the turn hit its timeout", 408, false, "timeout");
|
|
1657
|
+
default: {
|
|
1658
|
+
const code = record.error?.code ?? "internal_error";
|
|
1659
|
+
const message = `${code}: ${record.error?.message ?? "no detail"}`;
|
|
1660
|
+
if (code === "context_overflow") return make(message, 413, false, "context_overflow");
|
|
1661
|
+
if (/rate.?limit|429/i.test(message)) return make(message, 429, true, "rate_limit");
|
|
1662
|
+
if (/quota|usage limit|extra usage/i.test(message))
|
|
1663
|
+
return make(message, 402, false, "quota_exhausted");
|
|
1664
|
+
return make(message, 500, code === "server_restarted", "server");
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
var WrongForgeLeaderProvider = class {
|
|
1669
|
+
constructor(opts) {
|
|
1670
|
+
this.opts = opts;
|
|
1671
|
+
this.id = opts.id ?? WRONGFORGE_LEADER_PROVIDER_ID;
|
|
1672
|
+
trackAcpLeader(this);
|
|
1673
|
+
}
|
|
1674
|
+
opts;
|
|
1675
|
+
id;
|
|
1676
|
+
capabilities = { ...CAPABILITIES2 };
|
|
1677
|
+
sessions = /* @__PURE__ */ new Map();
|
|
1678
|
+
sideProviders = /* @__PURE__ */ new Map();
|
|
1679
|
+
config() {
|
|
1680
|
+
return acpLeaderHost()?.getConfig() ?? this.opts.bootConfig;
|
|
1681
|
+
}
|
|
1682
|
+
client() {
|
|
1683
|
+
const ep = resolveWrongForgeEndpoint();
|
|
1684
|
+
if (!ep) {
|
|
1685
|
+
throw new ProviderError3(
|
|
1686
|
+
"WrongForge: no API key found. Start it once with `npm run serve` (it saves ~/.wrongforge/api-key) or set WRONGFORGE_API_KEY.",
|
|
1687
|
+
401,
|
|
1688
|
+
false,
|
|
1689
|
+
this.id,
|
|
1690
|
+
{ kind: "auth" }
|
|
1691
|
+
);
|
|
1692
|
+
}
|
|
1693
|
+
void reportWrongForgeQuota({
|
|
1694
|
+
endpoint: ep,
|
|
1695
|
+
providerId: this.id,
|
|
1696
|
+
fetchImpl: this.opts.fetchImpl
|
|
1697
|
+
});
|
|
1698
|
+
return wrongForgeCall(ep, this.opts.fetchImpl);
|
|
1699
|
+
}
|
|
1700
|
+
async info(call, signal) {
|
|
1701
|
+
try {
|
|
1702
|
+
const info = parseWrongForgeInfo(await call("GET", "/v1/info", void 0, signal));
|
|
1703
|
+
writeWrongForgeInfoCache(info);
|
|
1704
|
+
return info;
|
|
1705
|
+
} catch {
|
|
1706
|
+
return void 0;
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
model(requested, info) {
|
|
1710
|
+
const choice = chooseModel(requested, void 0, info);
|
|
1711
|
+
if (choice.error) {
|
|
1712
|
+
throw new ProviderError3(choice.error, 400, false, this.id, { kind: "invalid_request" });
|
|
1713
|
+
}
|
|
1714
|
+
return choice.id;
|
|
1715
|
+
}
|
|
1716
|
+
/**
|
|
1717
|
+
* A side model for a turn WrongForge does not lead. `explicitOnly` (a side
|
|
1718
|
+
* request through `complete()`) takes only the model the user named in
|
|
1719
|
+
* `acp.leader.sideModel`: WrongForge answers those itself (`oneShot`), and
|
|
1720
|
+
* handing them to `fallbackModels[0]` let that model take over the session.
|
|
1721
|
+
* A worker's turn may still fall back to the fallback chain, since `oneShot`
|
|
1722
|
+
* would leave it without tools.
|
|
1723
|
+
*/
|
|
1724
|
+
sideProvider(explicitOnly = false) {
|
|
1725
|
+
const config = this.config();
|
|
1726
|
+
const ref = explicitOnly ? explicitLeaderSideModel(config) : leaderSideModel(config);
|
|
1727
|
+
if (!ref || ref.provider === this.id) return void 0;
|
|
1728
|
+
let provider = this.sideProviders.get(ref.provider);
|
|
1729
|
+
if (!provider) {
|
|
1730
|
+
provider = this.opts.buildProvider(ref.provider, config);
|
|
1731
|
+
if (provider) this.sideProviders.set(ref.provider, provider);
|
|
1732
|
+
}
|
|
1733
|
+
return provider ? { provider, model: ref.model } : void 0;
|
|
1734
|
+
}
|
|
1735
|
+
async complete(req, opts) {
|
|
1736
|
+
const side = this.sideProvider(true);
|
|
1737
|
+
if (side) return side.provider.complete({ ...req, model: side.model }, opts);
|
|
1738
|
+
return this.oneShot(req, opts.signal);
|
|
1739
|
+
}
|
|
1740
|
+
/** WrongForge as a plain model: one step, no tools, its own throwaway session. */
|
|
1741
|
+
async oneShot(req, signal) {
|
|
1742
|
+
const call = this.client();
|
|
1743
|
+
try {
|
|
1744
|
+
const info = await this.info(call, signal);
|
|
1745
|
+
const model = this.model(req.model, info);
|
|
1746
|
+
const host = acpLeaderHost();
|
|
1747
|
+
const { task } = await call(
|
|
1748
|
+
"POST",
|
|
1749
|
+
"/v1/tasks",
|
|
1750
|
+
{
|
|
1751
|
+
prompt: flatten(req),
|
|
1752
|
+
workdir: host?.projectRoot ?? process.cwd(),
|
|
1753
|
+
...model ? { model } : {},
|
|
1754
|
+
max_steps: 1,
|
|
1755
|
+
builtin_tools: [],
|
|
1756
|
+
...wrongForgeRuntime(req.reasoning, req.cache, info),
|
|
1757
|
+
wait: true,
|
|
1758
|
+
cancel_on_disconnect: true
|
|
1759
|
+
},
|
|
1760
|
+
signal
|
|
1761
|
+
);
|
|
1762
|
+
const failure = taskError(task, this.id);
|
|
1763
|
+
if (failure) throw failure;
|
|
1764
|
+
const usage = wfTokens(task.result?.usage);
|
|
1765
|
+
return {
|
|
1766
|
+
model: model ?? req.model,
|
|
1767
|
+
content: [{ type: "text", text: task.result?.text ?? "" }],
|
|
1768
|
+
stopReason: "end_turn",
|
|
1769
|
+
usage
|
|
1770
|
+
};
|
|
1771
|
+
} catch (err) {
|
|
1772
|
+
throw providerError(err, this.id);
|
|
1773
|
+
} finally {
|
|
1774
|
+
void reportWrongForgeQuota({ providerId: this.id, fetchImpl: this.opts.fetchImpl });
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
async *stream(req, opts) {
|
|
1778
|
+
if (!isLeaderTurn(req)) {
|
|
1779
|
+
const side = this.sideProvider();
|
|
1780
|
+
if (side) {
|
|
1781
|
+
yield* side.provider.stream({ ...req, model: side.model }, opts);
|
|
1782
|
+
return;
|
|
1783
|
+
}
|
|
1784
|
+
const res = await this.complete(req, opts);
|
|
1785
|
+
yield { type: "message_start", model: res.model };
|
|
1786
|
+
const text = res.content.map((b) => "text" in b ? b.text : "").join("");
|
|
1787
|
+
if (text) {
|
|
1788
|
+
yield { type: "content_block_start", kind: "text" };
|
|
1789
|
+
yield { type: "text_delta", text };
|
|
1790
|
+
yield { type: "content_block_stop", index: 0 };
|
|
1791
|
+
}
|
|
1792
|
+
yield { type: "message_stop", stopReason: "end_turn", usage: res.usage };
|
|
1793
|
+
return;
|
|
1794
|
+
}
|
|
1795
|
+
const call = this.client();
|
|
1796
|
+
const host = acpLeaderHost();
|
|
1797
|
+
const key = req.cache?.sessionId ?? "default";
|
|
1798
|
+
const leader = this.sessionFor(key);
|
|
1799
|
+
let info;
|
|
1800
|
+
let model;
|
|
1801
|
+
let gateway;
|
|
1802
|
+
let joinedBefore;
|
|
1803
|
+
try {
|
|
1804
|
+
info = await this.info(call, opts.signal);
|
|
1805
|
+
model = this.model(req.model, info);
|
|
1806
|
+
if (host && !leader.gateway) {
|
|
1807
|
+
leader.gateway = startLeaderGateway(host, key).catch(() => {
|
|
1808
|
+
leader.gateway = void 0;
|
|
1809
|
+
return void 0;
|
|
1810
|
+
});
|
|
1811
|
+
}
|
|
1812
|
+
gateway = await leader.gateway;
|
|
1813
|
+
if (!gateway) {
|
|
1814
|
+
leader.gateway = void 0;
|
|
1815
|
+
}
|
|
1816
|
+
joinedBefore = leader.joined ?? await this.wfSessionExists(call, leader, opts.signal);
|
|
1817
|
+
} catch (err) {
|
|
1818
|
+
throw providerError(err, this.id);
|
|
1819
|
+
}
|
|
1820
|
+
const routed = gateway !== void 0 && (info?.features.includes("builtin_tools") ?? false);
|
|
1821
|
+
const queue = new StreamQueue();
|
|
1822
|
+
const blocks = new TextBlocks(queue);
|
|
1823
|
+
const cards = new ToolCardRelay(host?.events, {
|
|
1824
|
+
sessionId: req.cache?.sessionId,
|
|
1825
|
+
model: model ?? req.model,
|
|
1826
|
+
provider: this.id
|
|
1827
|
+
});
|
|
1828
|
+
yield { type: "message_start", model: model ?? req.model };
|
|
1829
|
+
let taskId;
|
|
1830
|
+
const onAbort = () => {
|
|
1831
|
+
if (taskId) cancelTask(call, taskId, "interrupted in WrongStack");
|
|
1832
|
+
};
|
|
1833
|
+
opts.signal.addEventListener("abort", onAbort, { once: true });
|
|
1834
|
+
const run = (async () => {
|
|
1835
|
+
const { task } = await call(
|
|
1836
|
+
"POST",
|
|
1837
|
+
"/v1/tasks",
|
|
1838
|
+
{
|
|
1839
|
+
prompt: this.promptText(req, joinedBefore, gateway),
|
|
1840
|
+
session_id: leader.wfSessionId,
|
|
1841
|
+
workdir: host?.projectRoot ?? process.cwd(),
|
|
1842
|
+
...model ? { model } : {},
|
|
1843
|
+
max_steps: LEADER_MAX_STEPS,
|
|
1844
|
+
timeout_ms: LEADER_TURN_TIMEOUT_MS,
|
|
1845
|
+
...wrongForgeRuntime(req.reasoning, req.cache, info),
|
|
1846
|
+
system_append: leaderAppend(routed, gateway?.tools ?? []),
|
|
1847
|
+
context: { wrongstack: { sessionId: key, role: "leader" } },
|
|
1848
|
+
...gateway ? { gateway: { ...gateway.spec, optional: !routed } } : {},
|
|
1849
|
+
...routed ? { builtin_tools: WRONGFORGE_READ_SIDE_TOOLS } : {}
|
|
1850
|
+
},
|
|
1851
|
+
opts.signal
|
|
1852
|
+
);
|
|
1853
|
+
taskId = task.id;
|
|
1854
|
+
leader.joined = true;
|
|
1855
|
+
if (opts.signal.aborted) {
|
|
1856
|
+
cancelTask(call, taskId, "interrupted in WrongStack");
|
|
1857
|
+
throw new WfAborted();
|
|
1858
|
+
}
|
|
1859
|
+
for await (const ev of followTask(call, task.id, opts.signal)) {
|
|
1860
|
+
if (ev.type === "task.heartbeat" || ev.type === "step.finished") {
|
|
1861
|
+
void reportWrongForgeQuota({ providerId: this.id, fetchImpl: this.opts.fetchImpl });
|
|
1862
|
+
}
|
|
1863
|
+
const external = ev.data["external"] === true;
|
|
1864
|
+
for (const p of toProgress(ev)) {
|
|
1865
|
+
if (p.type === "message") blocks.text(p.text);
|
|
1866
|
+
else if (p.type === "tool_call") {
|
|
1867
|
+
blocks.close();
|
|
1868
|
+
if (!external) cards.update(p.toolCall);
|
|
1869
|
+
} else if (p.type === "tool_call_update" && !external) cards.update(p.toolCall);
|
|
1870
|
+
}
|
|
1871
|
+
if (ev.type === "step.finished" && typeof ev.data["context_tokens"] === "number") {
|
|
1872
|
+
this.observeContext(leader, req.cache?.sessionId, {
|
|
1873
|
+
used: ev.data["context_tokens"],
|
|
1874
|
+
size: this.capabilities.maxContext
|
|
1875
|
+
});
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
const done = await terminalTaskRecord(call, task.id, opts.signal);
|
|
1879
|
+
blocks.close();
|
|
1880
|
+
const failure = taskError(done, this.id);
|
|
1881
|
+
if (failure) throw failure;
|
|
1882
|
+
const window = done.result?.context_window ?? 0;
|
|
1883
|
+
if (window > 0) this.capabilities.maxContext = window;
|
|
1884
|
+
if (typeof done.result?.context_tokens === "number") {
|
|
1885
|
+
this.observeContext(leader, req.cache?.sessionId, {
|
|
1886
|
+
used: done.result.context_tokens,
|
|
1887
|
+
size: window
|
|
1888
|
+
});
|
|
1889
|
+
}
|
|
1890
|
+
queue.push({
|
|
1891
|
+
type: "message_stop",
|
|
1892
|
+
stopReason: done.result?.finish_reason === "length" ? "max_tokens" : "end_turn",
|
|
1893
|
+
usage: wfTokens(done.result?.usage),
|
|
1894
|
+
...leader.context ? { agentContext: leader.context } : {}
|
|
1895
|
+
});
|
|
1896
|
+
queue.end();
|
|
1897
|
+
})().catch((err) => {
|
|
1898
|
+
blocks.close();
|
|
1899
|
+
queue.fail(providerError(err, this.id));
|
|
1900
|
+
});
|
|
1901
|
+
try {
|
|
1902
|
+
yield* queue.drain();
|
|
1903
|
+
} finally {
|
|
1904
|
+
opts.signal.removeEventListener("abort", onAbort);
|
|
1905
|
+
await run;
|
|
1906
|
+
void reportWrongForgeQuota({ providerId: this.id, fetchImpl: this.opts.fetchImpl });
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
/** The turn's prompt; WrongForge joining a conversation under way gets the turns before it. */
|
|
1910
|
+
promptText(req, joined, gateway) {
|
|
1911
|
+
const latest = externalSkillDirective(
|
|
1912
|
+
latestUserText(req.messages),
|
|
1913
|
+
gateway ? externalSkillLoader(gateway.tools, gateway.spec.name_prefix ?? "") : void 0
|
|
1914
|
+
);
|
|
1915
|
+
const earlier = joined ? void 0 : earlierConversation(req.messages);
|
|
1916
|
+
const prompt = earlier ? `${earlier}
|
|
1917
|
+
|
|
1918
|
+
${latest}` : latest;
|
|
1919
|
+
const context = acpLeaderHost()?.context(req.cache?.sessionId);
|
|
1920
|
+
const notes = context?.meta ? consumeBtwNotes3(context) : [];
|
|
1921
|
+
return notes.length > 0 ? `${prompt}
|
|
1922
|
+
|
|
1923
|
+
${acpBtwBlock(notes)}` : prompt;
|
|
1924
|
+
}
|
|
1925
|
+
async wfSessionExists(call, leader, signal) {
|
|
1926
|
+
try {
|
|
1927
|
+
await call(
|
|
1928
|
+
"GET",
|
|
1929
|
+
`/v1/sessions/${encodeURIComponent(leader.wfSessionId)}`,
|
|
1930
|
+
void 0,
|
|
1931
|
+
signal
|
|
1932
|
+
);
|
|
1933
|
+
return true;
|
|
1934
|
+
} catch (err) {
|
|
1935
|
+
if (err instanceof WfHttpError && err.status === 404) return false;
|
|
1936
|
+
throw err;
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
sessionFor(key) {
|
|
1940
|
+
let leader = this.sessions.get(key);
|
|
1941
|
+
if (!leader) {
|
|
1942
|
+
leader = { wfSessionId: `wsl-${safeWfId(key)}` };
|
|
1943
|
+
this.sessions.set(key, leader);
|
|
1944
|
+
}
|
|
1945
|
+
return leader;
|
|
1946
|
+
}
|
|
1947
|
+
observeContext(leader, sessionId, reading) {
|
|
1948
|
+
leader.context = { used: reading.used, size: reading.size || this.capabilities.maxContext };
|
|
1949
|
+
publishLeaderContext(
|
|
1950
|
+
acpLeaderHost()?.events,
|
|
1951
|
+
sessionId,
|
|
1952
|
+
leader.context,
|
|
1953
|
+
this.capabilities.maxContext
|
|
1954
|
+
);
|
|
1955
|
+
}
|
|
1956
|
+
/** `/compact` on this route: WrongForge compacts its own session. */
|
|
1957
|
+
async compactOwnContext(opts) {
|
|
1958
|
+
const leader = this.sessions.get(opts.sessionId);
|
|
1959
|
+
if (!leader) return void 0;
|
|
1960
|
+
const call = this.client();
|
|
1961
|
+
try {
|
|
1962
|
+
await call(
|
|
1963
|
+
"POST",
|
|
1964
|
+
`/v1/sessions/${encodeURIComponent(leader.wfSessionId)}/compact`,
|
|
1965
|
+
{},
|
|
1966
|
+
opts.signal
|
|
1967
|
+
);
|
|
1968
|
+
} catch (err) {
|
|
1969
|
+
throw providerError(err, this.id);
|
|
1970
|
+
}
|
|
1971
|
+
leader.context = void 0;
|
|
1972
|
+
return void 0;
|
|
1973
|
+
}
|
|
1974
|
+
/** Close every session gateway (process shutdown). */
|
|
1975
|
+
async closeAll() {
|
|
1976
|
+
const all = [...this.sessions.values()];
|
|
1977
|
+
this.sessions.clear();
|
|
1978
|
+
await Promise.all(all.map((s) => s.gateway?.then((g) => g?.close()).catch(() => void 0)));
|
|
1979
|
+
}
|
|
1980
|
+
};
|
|
1981
|
+
function createWrongForgeLeaderProviderFactory(opts) {
|
|
1982
|
+
return {
|
|
1983
|
+
type: WRONGFORGE_LEADER_PROVIDER_ID,
|
|
1984
|
+
// Routing metadata only: the provider does no wire conversion.
|
|
1985
|
+
family: "openai-compatible",
|
|
1986
|
+
create: (cfg) => new WrongForgeLeaderProvider({
|
|
1987
|
+
...opts,
|
|
1988
|
+
id: cfg.type || WRONGFORGE_LEADER_PROVIDER_ID
|
|
1989
|
+
})
|
|
1990
|
+
};
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
// src/wiring/provider.ts
|
|
1320
1994
|
async function setupProvider(params) {
|
|
1321
1995
|
const { config, modelsRegistry, logger } = params;
|
|
1322
1996
|
const providerAuthRegistry = new ProviderAuthRegistry();
|
|
@@ -1333,7 +2007,16 @@ async function setupProvider(params) {
|
|
|
1333
2007
|
}
|
|
1334
2008
|
const savedProviderCfg = config.providers?.[config.provider];
|
|
1335
2009
|
const acpLeader = config.provider === ACP_LEADER_PROVIDER_ID;
|
|
1336
|
-
|
|
2010
|
+
const wrongForgeLeader = config.provider === WRONGFORGE_LEADER_PROVIDER_ID;
|
|
2011
|
+
let resolvedProvider = wrongForgeLeader ? {
|
|
2012
|
+
id: WRONGFORGE_LEADER_PROVIDER_ID,
|
|
2013
|
+
name: "WrongForge",
|
|
2014
|
+
family: "openai-compatible",
|
|
2015
|
+
apiBase: void 0,
|
|
2016
|
+
envVars: [],
|
|
2017
|
+
models: [{ id: config.model, name: config.model }],
|
|
2018
|
+
npm: void 0
|
|
2019
|
+
} : acpLeader ? {
|
|
1337
2020
|
id: ACP_LEADER_PROVIDER_ID,
|
|
1338
2021
|
name: "ACP agent",
|
|
1339
2022
|
family: "openai-compatible",
|
|
@@ -1393,6 +2076,12 @@ async function setupProvider(params) {
|
|
|
1393
2076
|
buildProvider: (providerId, live) => buildProviderForId({ config: live, providerRegistry, logger }, providerId)
|
|
1394
2077
|
})
|
|
1395
2078
|
);
|
|
2079
|
+
providerRegistry.register(
|
|
2080
|
+
createWrongForgeLeaderProviderFactory({
|
|
2081
|
+
bootConfig: config,
|
|
2082
|
+
buildProvider: (providerId, live) => buildProviderForId({ config: live, providerRegistry, logger }, providerId)
|
|
2083
|
+
})
|
|
2084
|
+
);
|
|
1396
2085
|
if (config.features.modelsRegistry) {
|
|
1397
2086
|
try {
|
|
1398
2087
|
const factories = await buildProviderFactoriesFromRegistry({
|
|
@@ -1433,7 +2122,7 @@ Try \`wstack models refresh\` once you have network access.`,
|
|
|
1433
2122
|
cause: err
|
|
1434
2123
|
});
|
|
1435
2124
|
}
|
|
1436
|
-
if (config.features.modelsRegistry && !acpLeader) {
|
|
2125
|
+
if (config.features.modelsRegistry && !acpLeader && !wrongForgeLeader) {
|
|
1437
2126
|
provider = await withCatalogCapabilities(
|
|
1438
2127
|
modelsRegistry,
|
|
1439
2128
|
config.provider,
|
|
@@ -1452,7 +2141,10 @@ export {
|
|
|
1452
2141
|
wrongstackAcpPermissionPolicy,
|
|
1453
2142
|
recordAcpRateLimit,
|
|
1454
2143
|
startAcpTurnReminders,
|
|
2144
|
+
cardOutputForText,
|
|
2145
|
+
WRONGFORGE_REASONING,
|
|
2146
|
+
wrongForgeRuntime,
|
|
1455
2147
|
setupProvider,
|
|
1456
2148
|
createGracefulShutdown
|
|
1457
2149
|
};
|
|
1458
|
-
//# sourceMappingURL=chunk-
|
|
2150
|
+
//# sourceMappingURL=chunk-EAATL6FL.js.map
|