@wrongstack/cli 0.303.0 → 0.305.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/{acp-AQMGKW6O.js → acp-5ZLGFHWP.js} +4 -2
- package/dist/boot/system-prompt-builder.d.ts +24 -1
- package/dist/boot/tui-project-switch.d.ts +1 -1
- package/dist/boot/tui-runtime-state.d.ts +14 -0
- package/dist/{chunk-JW75HY4F.js → chunk-FKWHSFX4.js} +33 -4
- package/dist/{chunk-LUAFQIXT.js → chunk-RHLQT3EV.js} +11 -1
- package/dist/{cli-main-37XO26GK.js → cli-main-W3T56YCY.js} +390 -299
- package/dist/execute-deps.d.ts +16 -0
- package/dist/{execution-MVOH6PAQ.js → execution-YMBD7YWC.js} +68 -18
- package/dist/fleet/host-context.d.ts +2 -0
- package/dist/fleet/host-session-writer.d.ts +33 -0
- package/dist/fleet/host.d.ts +10 -0
- package/dist/{hq-CD77GVSS.js → hq-5G6D5YIU.js} +2 -2
- package/dist/{hq-server-DME6EWIO.js → hq-server-IVZLVCFV.js} +2 -2
- package/dist/index.js +13 -56
- package/dist/{mailbox-serve-DBOOOOWZ.js → mailbox-serve-V3QIJBDH.js} +2 -2
- package/dist/{mcp-AX6ZAFJR.js → mcp-MSARYOPN.js} +4 -2
- package/dist/replay-XU6ZD7GM.js +34 -0
- package/dist/slash-commands/memory-triage.d.ts +4 -17
- package/dist/{webui-server-E3OPEKU5.js → webui-server-FTG7JAZU.js} +5 -5
- package/dist/wiring/domain-glossary.d.ts +58 -0
- package/dist/wiring/domain-terms-mirror.d.ts +52 -0
- package/dist/wiring/pipeline.d.ts +2 -0
- package/dist/wiring/replay.d.ts +11 -1
- package/dist/wiring/sage.d.ts +4 -16
- package/package.json +23 -23
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
} from "./chunk-V3XH6XBJ.js";
|
|
30
30
|
import {
|
|
31
31
|
startCliHqConnection
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-FKWHSFX4.js";
|
|
33
33
|
import {
|
|
34
34
|
advanceToNextTask,
|
|
35
35
|
findSpec,
|
|
@@ -117,7 +117,7 @@ import { gatedEnhancerReasoning } from "@wrongstack/core/execution";
|
|
|
117
117
|
import { TOKENS as TOKENS9 } from "@wrongstack/core/kernel";
|
|
118
118
|
import { ToolRegistry as ToolRegistry2 } from "@wrongstack/core/registry";
|
|
119
119
|
import { getSessionRegistry } from "@wrongstack/core/storage";
|
|
120
|
-
import { startSharedHeapWatchdog, writeErr as
|
|
120
|
+
import { startSharedHeapWatchdog, writeErr as writeErr3 } from "@wrongstack/core/utils";
|
|
121
121
|
|
|
122
122
|
// src/auth-menu/panel-service.ts
|
|
123
123
|
var ANSI_RE = /\u001b\[[0-9;]*m/g;
|
|
@@ -900,6 +900,12 @@ function bindSystemPromptBuilder(deps) {
|
|
|
900
900
|
// full-shape interfaces; the helper just needs the
|
|
901
901
|
// passthrough.
|
|
902
902
|
memoryStore: deps.memoryStore,
|
|
903
|
+
// Thread the narrow domain-term adapter so the builder emits a
|
|
904
|
+
// compact `[Project Jargon Dictionary]` block. `deps.domainGlossary`
|
|
905
|
+
// is an optional closure over the resolved SAGE `memoryStore`
|
|
906
|
+
// that returns only entries tagged `domain-term`; when omitted
|
|
907
|
+
// (e.g. in tests or subagent prompts) the builder emits no block.
|
|
908
|
+
domainGlossary: deps.domainGlossary,
|
|
903
909
|
// SAGE's turn middleware is the single memory-injection channel.
|
|
904
910
|
// Disable the builder's static "# Relevant Memory" section so memories
|
|
905
911
|
// are injected once, per-turn, relevance-scored — not duplicated here.
|
|
@@ -2904,6 +2910,38 @@ import {
|
|
|
2904
2910
|
} from "@wrongstack/core/types";
|
|
2905
2911
|
import { wstackGlobalRoot } from "@wrongstack/core/utils";
|
|
2906
2912
|
|
|
2913
|
+
// src/services/dispatch-classifier.ts
|
|
2914
|
+
import { makeLLMClassifier } from "@wrongstack/core/coordination";
|
|
2915
|
+
function makeProviderClassifier(provider, model) {
|
|
2916
|
+
return makeLLMClassifier(async (prompt) => {
|
|
2917
|
+
const ctrl = new AbortController();
|
|
2918
|
+
const timeout = setTimeout(() => ctrl.abort(), 15e3);
|
|
2919
|
+
try {
|
|
2920
|
+
const resp = await provider.complete(
|
|
2921
|
+
{
|
|
2922
|
+
model,
|
|
2923
|
+
system: [
|
|
2924
|
+
{
|
|
2925
|
+
type: "text",
|
|
2926
|
+
text: 'You are an agent router. Choose the single best agent for the task. Reply with ONLY a compact JSON object {"role":"...","reason":"..."}.'
|
|
2927
|
+
}
|
|
2928
|
+
],
|
|
2929
|
+
messages: [{ role: "user", content: [{ type: "text", text: prompt }] }],
|
|
2930
|
+
maxTokens: 120,
|
|
2931
|
+
temperature: 0
|
|
2932
|
+
},
|
|
2933
|
+
{ signal: ctrl.signal }
|
|
2934
|
+
);
|
|
2935
|
+
const content = resp.content;
|
|
2936
|
+
return Array.isArray(content) ? content[0]?.text ?? "" : "";
|
|
2937
|
+
} catch {
|
|
2938
|
+
return "";
|
|
2939
|
+
} finally {
|
|
2940
|
+
clearTimeout(timeout);
|
|
2941
|
+
}
|
|
2942
|
+
});
|
|
2943
|
+
}
|
|
2944
|
+
|
|
2907
2945
|
// src/fleet/host-acp.ts
|
|
2908
2946
|
import {
|
|
2909
2947
|
ACP_AGENT_COMMANDS,
|
|
@@ -3536,6 +3574,7 @@ function createHostStatusBroadcaster(input) {
|
|
|
3536
3574
|
// src/fleet/host-learning.ts
|
|
3537
3575
|
import {
|
|
3538
3576
|
captureLearnedFromAgentOutputDetailed,
|
|
3577
|
+
recordDirectiveOutcomes,
|
|
3539
3578
|
recordSkillOutcome
|
|
3540
3579
|
} from "@wrongstack/core/agent-catalog";
|
|
3541
3580
|
import { TOKENS as TOKENS2 } from "@wrongstack/core/kernel";
|
|
@@ -3543,20 +3582,38 @@ function captureCompletedTaskLearningForHost(result, deps, subjects, onCaptured)
|
|
|
3543
3582
|
const subject = subjects.get(result.subagentId);
|
|
3544
3583
|
if (!subject) return;
|
|
3545
3584
|
const logger = deps.container.safeResolve(TOKENS2.Logger);
|
|
3546
|
-
|
|
3585
|
+
const graded = result.status !== "stopped";
|
|
3586
|
+
const succeeded = result.status === "success";
|
|
3587
|
+
if (graded && subject.skills.length > 0) {
|
|
3547
3588
|
try {
|
|
3548
|
-
recordSkillOutcome(
|
|
3549
|
-
subject.role,
|
|
3550
|
-
subject.skills,
|
|
3551
|
-
result.status === "success",
|
|
3552
|
-
deps.projectRoot
|
|
3553
|
-
);
|
|
3589
|
+
recordSkillOutcome(subject.role, subject.skills, succeeded, deps.projectRoot);
|
|
3554
3590
|
} catch (error) {
|
|
3555
|
-
logger?.debug?.(
|
|
3591
|
+
logger?.debug?.(
|
|
3592
|
+
`skill affinity update failed for role "${subject.role}": ${describe(error)}`
|
|
3593
|
+
);
|
|
3556
3594
|
}
|
|
3557
3595
|
}
|
|
3558
3596
|
const finalText = typeof result.result === "string" ? result.result : result.partial?.text;
|
|
3559
3597
|
if (!finalText) return;
|
|
3598
|
+
if (graded) {
|
|
3599
|
+
try {
|
|
3600
|
+
const outcome = recordDirectiveOutcomes(subject.role, finalText, succeeded, deps.projectRoot);
|
|
3601
|
+
if (outcome.attributed > 0) {
|
|
3602
|
+
logger?.debug?.(
|
|
3603
|
+
`directive outcomes: ${outcome.attributed} applied for role "${subject.role}" (${succeeded ? "success" : "failure"})`
|
|
3604
|
+
);
|
|
3605
|
+
}
|
|
3606
|
+
for (const retired of outcome.quarantined) {
|
|
3607
|
+
logger?.info?.(
|
|
3608
|
+
`retired a directive for role "${subject.role}" after repeated failures: ${retired.slice(0, 120)}`
|
|
3609
|
+
);
|
|
3610
|
+
}
|
|
3611
|
+
} catch (error) {
|
|
3612
|
+
logger?.debug?.(
|
|
3613
|
+
`directive outcome update failed for role "${subject.role}": ${describe(error)}`
|
|
3614
|
+
);
|
|
3615
|
+
}
|
|
3616
|
+
}
|
|
3560
3617
|
try {
|
|
3561
3618
|
const capture = captureLearnedFromAgentOutputDetailed(
|
|
3562
3619
|
finalText,
|
|
@@ -3636,6 +3693,84 @@ function applyFleetRootDefaults(opts) {
|
|
|
3636
3693
|
}
|
|
3637
3694
|
}
|
|
3638
3695
|
|
|
3696
|
+
// src/fleet/host-provider.ts
|
|
3697
|
+
import { makeProviderFromConfig, withCatalogCapabilities } from "@wrongstack/providers";
|
|
3698
|
+
async function buildHostSubagentProvider(deps, config, overrideId, model) {
|
|
3699
|
+
const requestedProviderId = overrideId ?? config.provider;
|
|
3700
|
+
const providerId = requestedProviderId === config.provider || config.providers?.[requestedProviderId] !== void 0 || deps.providerRegistry.has(requestedProviderId) ? requestedProviderId : config.provider;
|
|
3701
|
+
const newCfg = config.providers?.[providerId] ?? {
|
|
3702
|
+
type: providerId,
|
|
3703
|
+
apiKey: config.apiKey,
|
|
3704
|
+
baseUrl: config.baseUrl
|
|
3705
|
+
};
|
|
3706
|
+
const cfgWithType = {
|
|
3707
|
+
...newCfg,
|
|
3708
|
+
type: providerId,
|
|
3709
|
+
...model ? { model } : {}
|
|
3710
|
+
};
|
|
3711
|
+
let provider = deps.providerRegistry.has(providerId) ? deps.providerRegistry.create(cfgWithType) : makeProviderFromConfig(providerId, cfgWithType);
|
|
3712
|
+
if (deps.modelsRegistry) {
|
|
3713
|
+
const resolvedModel = model ?? config.model;
|
|
3714
|
+
provider = await withCatalogCapabilities(deps.modelsRegistry, providerId, provider, {
|
|
3715
|
+
...cfgWithType,
|
|
3716
|
+
model: resolvedModel
|
|
3717
|
+
});
|
|
3718
|
+
await refreshRuntimeModelCatalog({
|
|
3719
|
+
modelsRegistry: deps.modelsRegistry,
|
|
3720
|
+
reason: `${providerId}/${resolvedModel}`
|
|
3721
|
+
});
|
|
3722
|
+
const mc = await resolveRuntimeMaxContext({
|
|
3723
|
+
modelsRegistry: deps.modelsRegistry,
|
|
3724
|
+
config,
|
|
3725
|
+
provider,
|
|
3726
|
+
runtimeProviderConfig: cfgWithType,
|
|
3727
|
+
providerId,
|
|
3728
|
+
modelId: resolvedModel
|
|
3729
|
+
});
|
|
3730
|
+
if (mc && mc > 0) provider.capabilities.maxContext = mc;
|
|
3731
|
+
}
|
|
3732
|
+
return provider;
|
|
3733
|
+
}
|
|
3734
|
+
async function resolveHostSubagentReasoningConfig(deps, providerId, modelId) {
|
|
3735
|
+
if (!deps.modelsRegistry) return void 0;
|
|
3736
|
+
try {
|
|
3737
|
+
return (await deps.modelsRegistry.getModel(providerId, modelId))?.capabilities.reasoningConfig;
|
|
3738
|
+
} catch {
|
|
3739
|
+
return void 0;
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3742
|
+
function resolveHostSubagentModelSelection(liveConfig, effectiveCfg, matrixTarget) {
|
|
3743
|
+
let effProvider = effectiveCfg.provider ?? matrixTarget?.provider ?? liveConfig.provider;
|
|
3744
|
+
let effModel = effectiveCfg.model ?? matrixTarget?.model ?? liveConfig.model;
|
|
3745
|
+
const modelPolicy = effectiveCfg.modelPolicy;
|
|
3746
|
+
const closedModelPolicy = modelPolicy?.strict === true;
|
|
3747
|
+
const allowedModels = modelPolicy?.allowed ?? [];
|
|
3748
|
+
if (modelPolicy && !allowedModels.some((target) => target.provider === effProvider && target.model === effModel)) {
|
|
3749
|
+
const firstAllowed = allowedModels[0];
|
|
3750
|
+
if (!firstAllowed) throw new Error(`Agent "${effectiveCfg.role}" has no allowed models.`);
|
|
3751
|
+
effProvider = firstAllowed.provider;
|
|
3752
|
+
effModel = firstAllowed.model;
|
|
3753
|
+
}
|
|
3754
|
+
const fallbackProfile = modelPolicy ? void 0 : effectiveCfg.fallbackProfile ?? matrixTarget?.fallbackProfile;
|
|
3755
|
+
const runtimeOverride = effectiveCfg.modelRuntime ?? matrixTarget?.modelRuntime;
|
|
3756
|
+
const startupTargets = modelPolicy ? [
|
|
3757
|
+
{ provider: effProvider, model: effModel },
|
|
3758
|
+
...modelPolicy.fallbacks ?? [],
|
|
3759
|
+
...closedModelPolicy || effProvider === liveConfig.provider && effModel === liveConfig.model ? [] : [{ provider: liveConfig.provider, model: liveConfig.model }]
|
|
3760
|
+
] : [
|
|
3761
|
+
{ provider: effProvider, model: effModel },
|
|
3762
|
+
...effProvider === liveConfig.provider && effModel === liveConfig.model ? [] : [{ provider: liveConfig.provider, model: liveConfig.model }]
|
|
3763
|
+
];
|
|
3764
|
+
return {
|
|
3765
|
+
effProvider,
|
|
3766
|
+
effModel,
|
|
3767
|
+
fallbackProfile,
|
|
3768
|
+
runtimeOverride,
|
|
3769
|
+
closedModelPolicy,
|
|
3770
|
+
startupTargets
|
|
3771
|
+
};
|
|
3772
|
+
}
|
|
3773
|
+
|
|
3639
3774
|
// src/fleet/host-shadow-pass.ts
|
|
3640
3775
|
async function runHostShadowPass(ctx, reason) {
|
|
3641
3776
|
try {
|
|
@@ -3801,84 +3936,6 @@ function aggregateFleetUsage(completed) {
|
|
|
3801
3936
|
return { rows, totals };
|
|
3802
3937
|
}
|
|
3803
3938
|
|
|
3804
|
-
// src/fleet/host-provider.ts
|
|
3805
|
-
import { makeProviderFromConfig, withCatalogCapabilities } from "@wrongstack/providers";
|
|
3806
|
-
async function buildHostSubagentProvider(deps, config, overrideId, model) {
|
|
3807
|
-
const requestedProviderId = overrideId ?? config.provider;
|
|
3808
|
-
const providerId = requestedProviderId === config.provider || config.providers?.[requestedProviderId] !== void 0 || deps.providerRegistry.has(requestedProviderId) ? requestedProviderId : config.provider;
|
|
3809
|
-
const newCfg = config.providers?.[providerId] ?? {
|
|
3810
|
-
type: providerId,
|
|
3811
|
-
apiKey: config.apiKey,
|
|
3812
|
-
baseUrl: config.baseUrl
|
|
3813
|
-
};
|
|
3814
|
-
const cfgWithType = {
|
|
3815
|
-
...newCfg,
|
|
3816
|
-
type: providerId,
|
|
3817
|
-
...model ? { model } : {}
|
|
3818
|
-
};
|
|
3819
|
-
let provider = deps.providerRegistry.has(providerId) ? deps.providerRegistry.create(cfgWithType) : makeProviderFromConfig(providerId, cfgWithType);
|
|
3820
|
-
if (deps.modelsRegistry) {
|
|
3821
|
-
const resolvedModel = model ?? config.model;
|
|
3822
|
-
provider = await withCatalogCapabilities(deps.modelsRegistry, providerId, provider, {
|
|
3823
|
-
...cfgWithType,
|
|
3824
|
-
model: resolvedModel
|
|
3825
|
-
});
|
|
3826
|
-
await refreshRuntimeModelCatalog({
|
|
3827
|
-
modelsRegistry: deps.modelsRegistry,
|
|
3828
|
-
reason: `${providerId}/${resolvedModel}`
|
|
3829
|
-
});
|
|
3830
|
-
const mc = await resolveRuntimeMaxContext({
|
|
3831
|
-
modelsRegistry: deps.modelsRegistry,
|
|
3832
|
-
config,
|
|
3833
|
-
provider,
|
|
3834
|
-
runtimeProviderConfig: cfgWithType,
|
|
3835
|
-
providerId,
|
|
3836
|
-
modelId: resolvedModel
|
|
3837
|
-
});
|
|
3838
|
-
if (mc && mc > 0) provider.capabilities.maxContext = mc;
|
|
3839
|
-
}
|
|
3840
|
-
return provider;
|
|
3841
|
-
}
|
|
3842
|
-
async function resolveHostSubagentReasoningConfig(deps, providerId, modelId) {
|
|
3843
|
-
if (!deps.modelsRegistry) return void 0;
|
|
3844
|
-
try {
|
|
3845
|
-
return (await deps.modelsRegistry.getModel(providerId, modelId))?.capabilities.reasoningConfig;
|
|
3846
|
-
} catch {
|
|
3847
|
-
return void 0;
|
|
3848
|
-
}
|
|
3849
|
-
}
|
|
3850
|
-
function resolveHostSubagentModelSelection(liveConfig, effectiveCfg, matrixTarget) {
|
|
3851
|
-
let effProvider = effectiveCfg.provider ?? matrixTarget?.provider ?? liveConfig.provider;
|
|
3852
|
-
let effModel = effectiveCfg.model ?? matrixTarget?.model ?? liveConfig.model;
|
|
3853
|
-
const modelPolicy = effectiveCfg.modelPolicy;
|
|
3854
|
-
const closedModelPolicy = modelPolicy?.strict === true;
|
|
3855
|
-
const allowedModels = modelPolicy?.allowed ?? [];
|
|
3856
|
-
if (modelPolicy && !allowedModels.some((target) => target.provider === effProvider && target.model === effModel)) {
|
|
3857
|
-
const firstAllowed = allowedModels[0];
|
|
3858
|
-
if (!firstAllowed) throw new Error(`Agent "${effectiveCfg.role}" has no allowed models.`);
|
|
3859
|
-
effProvider = firstAllowed.provider;
|
|
3860
|
-
effModel = firstAllowed.model;
|
|
3861
|
-
}
|
|
3862
|
-
const fallbackProfile = modelPolicy ? void 0 : effectiveCfg.fallbackProfile ?? matrixTarget?.fallbackProfile;
|
|
3863
|
-
const runtimeOverride = effectiveCfg.modelRuntime ?? matrixTarget?.modelRuntime;
|
|
3864
|
-
const startupTargets = modelPolicy ? [
|
|
3865
|
-
{ provider: effProvider, model: effModel },
|
|
3866
|
-
...modelPolicy.fallbacks ?? [],
|
|
3867
|
-
...closedModelPolicy || effProvider === liveConfig.provider && effModel === liveConfig.model ? [] : [{ provider: liveConfig.provider, model: liveConfig.model }]
|
|
3868
|
-
] : [
|
|
3869
|
-
{ provider: effProvider, model: effModel },
|
|
3870
|
-
...effProvider === liveConfig.provider && effModel === liveConfig.model ? [] : [{ provider: liveConfig.provider, model: liveConfig.model }]
|
|
3871
|
-
];
|
|
3872
|
-
return {
|
|
3873
|
-
effProvider,
|
|
3874
|
-
effModel,
|
|
3875
|
-
fallbackProfile,
|
|
3876
|
-
runtimeOverride,
|
|
3877
|
-
closedModelPolicy,
|
|
3878
|
-
startupTargets
|
|
3879
|
-
};
|
|
3880
|
-
}
|
|
3881
|
-
|
|
3882
3939
|
// src/fleet/host-subagent-factory.ts
|
|
3883
3940
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
3884
3941
|
import { existsSync, statSync } from "node:fs";
|
|
@@ -3914,6 +3971,7 @@ import { AutoApprovePermissionPolicy } from "@wrongstack/core/security";
|
|
|
3914
3971
|
|
|
3915
3972
|
// src/fleet/host-context.ts
|
|
3916
3973
|
import {
|
|
3974
|
+
DEFAULT_EAGER_SKILL_LIMIT,
|
|
3917
3975
|
loadProjectSkillAugmentation,
|
|
3918
3976
|
missingRequiredRuntimeTools,
|
|
3919
3977
|
missingRuntimeCapabilities,
|
|
@@ -3924,7 +3982,8 @@ import {
|
|
|
3924
3982
|
} from "@wrongstack/core/agent-catalog";
|
|
3925
3983
|
import { TOKENS as TOKENS3 } from "@wrongstack/core/kernel";
|
|
3926
3984
|
import { getSageRetrieval } from "@wrongstack/sage";
|
|
3927
|
-
var EAGER_SKILL_LIMIT =
|
|
3985
|
+
var EAGER_SKILL_LIMIT = DEFAULT_EAGER_SKILL_LIMIT;
|
|
3986
|
+
var MIN_TRIMMED_BODY_CHARS = 800;
|
|
3928
3987
|
async function resolveHostSubagentSkillResolution(deps, roster, subCfg, availableToolNames = []) {
|
|
3929
3988
|
const role = subCfg.role;
|
|
3930
3989
|
const rosterEntry = role ? roster[role] : void 0;
|
|
@@ -3940,10 +3999,11 @@ async function resolveHostSubagentSkillResolution(deps, roster, subCfg, availabl
|
|
|
3940
3999
|
];
|
|
3941
4000
|
const skillNames = role ? rankRoleSkills(role, pool, deps.projectRoot, EAGER_SKILL_LIMIT) : pool.slice(0, EAGER_SKILL_LIMIT);
|
|
3942
4001
|
if (skillNames.length === 0 || !deps.skillLoader) {
|
|
3943
|
-
return { content: directContent ?? "", selected: [], dropped };
|
|
4002
|
+
return { content: directContent ?? "", selected: [], dropped, trimmed: [] };
|
|
3944
4003
|
}
|
|
3945
4004
|
const resolved = [];
|
|
3946
4005
|
const selected = [];
|
|
4006
|
+
const trimmed = [];
|
|
3947
4007
|
let usedChars = 0;
|
|
3948
4008
|
const maxChars = 16e3;
|
|
3949
4009
|
const maxCharsPerSkill = 4e3;
|
|
@@ -3972,10 +4032,12 @@ async function resolveHostSubagentSkillResolution(deps, roster, subCfg, availabl
|
|
|
3972
4032
|
continue;
|
|
3973
4033
|
}
|
|
3974
4034
|
const augmentation = role ? loadProjectSkillAugmentation(role, skillName, deps.projectRoot) : "";
|
|
3975
|
-
const
|
|
4035
|
+
const compose = (bodyChars) => [
|
|
3976
4036
|
`## Skill: ${skillName}`,
|
|
3977
4037
|
"",
|
|
3978
|
-
body.slice(0,
|
|
4038
|
+
body.length > bodyChars ? `${body.slice(0, bodyChars).trimEnd()}
|
|
4039
|
+
|
|
4040
|
+
_(body trimmed)_` : body,
|
|
3979
4041
|
...augmentation ? [
|
|
3980
4042
|
"",
|
|
3981
4043
|
`### Project practice for \`${skillName}\``,
|
|
@@ -3985,9 +4047,16 @@ async function resolveHostSubagentSkillResolution(deps, roster, subCfg, availabl
|
|
|
3985
4047
|
augmentation
|
|
3986
4048
|
] : []
|
|
3987
4049
|
].join("\n");
|
|
4050
|
+
let entry = compose(maxCharsPerSkill);
|
|
3988
4051
|
if (usedChars + entry.length > maxChars) {
|
|
3989
|
-
|
|
3990
|
-
|
|
4052
|
+
const overhead = entry.length - Math.min(body.length, maxCharsPerSkill);
|
|
4053
|
+
const room = maxChars - usedChars - overhead;
|
|
4054
|
+
if (!augmentation || room < MIN_TRIMMED_BODY_CHARS) {
|
|
4055
|
+
dropped[skillName] = "budget";
|
|
4056
|
+
continue;
|
|
4057
|
+
}
|
|
4058
|
+
entry = compose(room);
|
|
4059
|
+
trimmed.push(skillName);
|
|
3991
4060
|
}
|
|
3992
4061
|
resolved.push(entry);
|
|
3993
4062
|
selected.push(skillName);
|
|
@@ -4010,7 +4079,7 @@ Apply these skills first for this assignment.
|
|
|
4010
4079
|
|
|
4011
4080
|
${resolved.join("\n\n---\n\n")}` : void 0
|
|
4012
4081
|
].filter((section) => Boolean(section));
|
|
4013
|
-
return { content: sections.join("\n\n"), selected, dropped };
|
|
4082
|
+
return { content: sections.join("\n\n"), selected, dropped, trimmed };
|
|
4014
4083
|
}
|
|
4015
4084
|
async function retrieveHostSubagentMemory(deps, getLeaderMode, subCfg, taskContext) {
|
|
4016
4085
|
const memoryPort = deps.container.safeResolve(TOKENS3.MemoryStore);
|
|
@@ -4222,10 +4291,8 @@ function createParentSubagentSessionWriter(parentSession) {
|
|
|
4222
4291
|
flush: () => parentSession.flush(),
|
|
4223
4292
|
close: async () => {
|
|
4224
4293
|
},
|
|
4225
|
-
recordFileChange: () =>
|
|
4226
|
-
|
|
4227
|
-
recordSideEffect: () => {
|
|
4228
|
-
},
|
|
4294
|
+
recordFileChange: (input) => parentSession.recordFileChange(input),
|
|
4295
|
+
recordSideEffect: (input) => parentSession.recordSideEffect(input),
|
|
4229
4296
|
writeCheckpoint: async () => {
|
|
4230
4297
|
},
|
|
4231
4298
|
writeFileSnapshot: async () => {
|
|
@@ -4239,6 +4306,21 @@ function createParentSubagentSessionWriter(parentSession) {
|
|
|
4239
4306
|
}
|
|
4240
4307
|
};
|
|
4241
4308
|
}
|
|
4309
|
+
function withParentFileSnapshots(subagentSession, parentSession) {
|
|
4310
|
+
if (subagentSession === parentSession) return subagentSession;
|
|
4311
|
+
return new Proxy(subagentSession, {
|
|
4312
|
+
get(target, property, receiver) {
|
|
4313
|
+
if (property === "recordFileChange") {
|
|
4314
|
+
return (input) => {
|
|
4315
|
+
target.recordFileChange(input);
|
|
4316
|
+
parentSession.recordFileChange(input);
|
|
4317
|
+
};
|
|
4318
|
+
}
|
|
4319
|
+
const value = Reflect.get(target, property, receiver);
|
|
4320
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
4321
|
+
}
|
|
4322
|
+
});
|
|
4323
|
+
}
|
|
4242
4324
|
|
|
4243
4325
|
// src/fleet/host-subagent-factory.ts
|
|
4244
4326
|
function createHostSubagentFactory(config, host) {
|
|
@@ -4370,12 +4452,13 @@ ${audienceMemory.map((text) => `- ${text}`).join("\n")}`
|
|
|
4370
4452
|
baseSystem.push({ type: "text", text: skillResolution.content });
|
|
4371
4453
|
}
|
|
4372
4454
|
const droppedSkills = Object.entries(skillResolution.dropped);
|
|
4373
|
-
if (droppedSkills.length > 0) {
|
|
4455
|
+
if (droppedSkills.length > 0 || skillResolution.trimmed.length > 0) {
|
|
4374
4456
|
host.deps.events.emit("subagent.skills.dropped", {
|
|
4375
4457
|
sessionId: host.deps.session.id,
|
|
4376
4458
|
role: effectiveCfg.role,
|
|
4377
4459
|
selected: skillResolution.selected,
|
|
4378
|
-
dropped: Object.fromEntries(droppedSkills)
|
|
4460
|
+
dropped: Object.fromEntries(droppedSkills),
|
|
4461
|
+
...skillResolution.trimmed.length > 0 ? { trimmed: skillResolution.trimmed } : {}
|
|
4379
4462
|
});
|
|
4380
4463
|
}
|
|
4381
4464
|
const rawRolePrompt = effectiveCfg.systemPromptOverride ?? effectiveCfg.prompt ?? (effectiveCfg.role ? host.roster[effectiveCfg.role]?.prompt : void 0);
|
|
@@ -4391,12 +4474,15 @@ ${audienceMemory.map((text) => `- ${text}`).join("\n")}`
|
|
|
4391
4474
|
}
|
|
4392
4475
|
let subSession;
|
|
4393
4476
|
if (host.sessionFactory) {
|
|
4394
|
-
subSession =
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4477
|
+
subSession = withParentFileSnapshots(
|
|
4478
|
+
await host.sessionFactory.createSubagentSession({
|
|
4479
|
+
subagentId: subagentName,
|
|
4480
|
+
provider: effProvider,
|
|
4481
|
+
model: effModel,
|
|
4482
|
+
title: `subagent: ${subagentName}`
|
|
4483
|
+
}),
|
|
4484
|
+
host.deps.session
|
|
4485
|
+
);
|
|
4400
4486
|
} else {
|
|
4401
4487
|
subSession = createParentSubagentSessionWriter(host.deps.session);
|
|
4402
4488
|
}
|
|
@@ -4445,7 +4531,11 @@ ${audienceMemory.map((text) => `- ${text}`).join("\n")}`
|
|
|
4445
4531
|
maxToolTimeoutMs: config.tools?.maxToolTimeoutMs ?? 3e5,
|
|
4446
4532
|
perIterationOutputCapBytes: config.tools?.perIterationOutputCapBytes ?? 1e5,
|
|
4447
4533
|
tracer: void 0,
|
|
4448
|
-
|
|
4534
|
+
// Kanban tracks work; it does not gate it. Off unless the operator opts
|
|
4535
|
+
// in — and then subagents inherit it, because a worker dispatched by
|
|
4536
|
+
// `kanban_queue` already carries board/task/lease identity in ctx.meta
|
|
4537
|
+
// and can satisfy the same gate its leader was held to.
|
|
4538
|
+
requireKanbanGovernance: config.tools?.kanbanGovernance ?? false
|
|
4449
4539
|
});
|
|
4450
4540
|
const subagentConfigStore = host.deps.configStore;
|
|
4451
4541
|
const pipelines = createDefaultPipelines();
|
|
@@ -4836,6 +4926,12 @@ var MultiAgentHost = class {
|
|
|
4836
4926
|
// pass so director.fleetManager is never undefined
|
|
4837
4927
|
brain: this.opts.brain,
|
|
4838
4928
|
roster: this.roster,
|
|
4929
|
+
// The dispatcher is two-stage by design — keyword heuristic, then a model
|
|
4930
|
+
// to break the tie — but nothing ever supplied stage two here, so every
|
|
4931
|
+
// description the keywords could not resolve fell through to the
|
|
4932
|
+
// `executor` generalist. That is one half of why a 77-role roster was
|
|
4933
|
+
// being served by a handful of agents.
|
|
4934
|
+
dispatchClassifier: (task, candidates) => this.classifyDispatch(task, candidates),
|
|
4839
4935
|
// Fire-and-forget report-back: when an assign_task completes with no
|
|
4840
4936
|
// pending await, post the result to this session's leader via the
|
|
4841
4937
|
// project mailbox (injected inline before the leader's next step).
|
|
@@ -5324,6 +5420,28 @@ var MultiAgentHost = class {
|
|
|
5324
5420
|
}
|
|
5325
5421
|
return this.learningOptimizer;
|
|
5326
5422
|
}
|
|
5423
|
+
/**
|
|
5424
|
+
* Break a dispatch tie with a model when the keyword heuristic is ambiguous.
|
|
5425
|
+
*
|
|
5426
|
+
* Routes through the `dispatcher` model-matrix slot when one is configured —
|
|
5427
|
+
* picking a role is a short classification, so it belongs on a cheap fast
|
|
5428
|
+
* model rather than the leader's — and the session default otherwise.
|
|
5429
|
+
* Returning `null` on any failure leaves the dispatcher on its heuristic
|
|
5430
|
+
* result, so routing degrades rather than breaking a spawn.
|
|
5431
|
+
*/
|
|
5432
|
+
async classifyDispatch(task, candidates) {
|
|
5433
|
+
try {
|
|
5434
|
+
const config = this.deps.configStore.get();
|
|
5435
|
+
const target = resolveSubagentModelTarget2(config, "dispatcher");
|
|
5436
|
+
const providerId = target?.provider ?? config.provider;
|
|
5437
|
+
const model = target?.model ?? config.model;
|
|
5438
|
+
if (!providerId || !model) return null;
|
|
5439
|
+
const provider = await buildHostSubagentProvider(this.deps, config, providerId, model);
|
|
5440
|
+
return await makeProviderClassifier(provider, model)(task, candidates);
|
|
5441
|
+
} catch {
|
|
5442
|
+
return null;
|
|
5443
|
+
}
|
|
5444
|
+
}
|
|
5327
5445
|
/**
|
|
5328
5446
|
* Resolve a model for the distillation pass. Uses the `memory-curator` slot
|
|
5329
5447
|
* of the model matrix when one is configured — curating learned knowledge is
|
|
@@ -6417,6 +6535,58 @@ function setupDirectorAndAutonomy(deps) {
|
|
|
6417
6535
|
};
|
|
6418
6536
|
}
|
|
6419
6537
|
|
|
6538
|
+
// src/wiring/domain-glossary.ts
|
|
6539
|
+
import { getSageService } from "@wrongstack/sage";
|
|
6540
|
+
var DOMAIN_GLOSSARY_LIMIT = 16;
|
|
6541
|
+
function createDomainGlossaryAdapter(memoryStore) {
|
|
6542
|
+
return {
|
|
6543
|
+
async list(scope, limit) {
|
|
6544
|
+
try {
|
|
6545
|
+
const service = getSageService(memoryStore);
|
|
6546
|
+
if (!service) return [];
|
|
6547
|
+
if (scope !== "project-memory") return [];
|
|
6548
|
+
const effectiveLimit = Math.min(
|
|
6549
|
+
Math.max(1, limit ?? DOMAIN_GLOSSARY_LIMIT),
|
|
6550
|
+
DOMAIN_GLOSSARY_LIMIT
|
|
6551
|
+
);
|
|
6552
|
+
const hits = await service.searchSage("domain-term", { limit: effectiveLimit });
|
|
6553
|
+
return hits.map((hit) => sageToGlossaryEntry(hit));
|
|
6554
|
+
} catch {
|
|
6555
|
+
return [];
|
|
6556
|
+
}
|
|
6557
|
+
}
|
|
6558
|
+
};
|
|
6559
|
+
}
|
|
6560
|
+
function sageToGlossaryEntry(sage) {
|
|
6561
|
+
const priority = sage.importance >= 0.9 ? "critical" : sage.importance >= 0.75 ? "high" : sage.importance >= 0.4 ? "medium" : "low";
|
|
6562
|
+
return {
|
|
6563
|
+
scope: "project-memory",
|
|
6564
|
+
text: sage.text,
|
|
6565
|
+
ts: sage.updatedAt || sage.createdAt,
|
|
6566
|
+
type: "reference",
|
|
6567
|
+
tags: sage.tags.slice(),
|
|
6568
|
+
priority,
|
|
6569
|
+
confidence: sage.confidence,
|
|
6570
|
+
lastAccessed: sage.lastAccessedAt
|
|
6571
|
+
};
|
|
6572
|
+
}
|
|
6573
|
+
|
|
6574
|
+
// src/wiring/domain-terms-mirror.ts
|
|
6575
|
+
import { writeErr as writeErr2 } from "@wrongstack/core/utils";
|
|
6576
|
+
import { SageDomainTermExtractor } from "@wrongstack/sage";
|
|
6577
|
+
async function refreshDomainTermsMirror(options) {
|
|
6578
|
+
try {
|
|
6579
|
+
const extractor = new SageDomainTermExtractor();
|
|
6580
|
+
return await extractor.writeDomainTermsFile(options.projectRoot, options.memoryStore);
|
|
6581
|
+
} catch (err) {
|
|
6582
|
+
writeErr2(
|
|
6583
|
+
`[wrongstack] domain-terms mirror refresh failed: ${err instanceof Error ? err.message : String(err)}
|
|
6584
|
+
`
|
|
6585
|
+
);
|
|
6586
|
+
return null;
|
|
6587
|
+
}
|
|
6588
|
+
}
|
|
6589
|
+
|
|
6420
6590
|
// src/wiring/eternal-command-handlers.ts
|
|
6421
6591
|
import { EternalAutonomyEngine as EternalAutonomyEngine2, ParallelEternalEngine } from "@wrongstack/core/execution";
|
|
6422
6592
|
function createEternalCommandHandlers(input) {
|
|
@@ -7750,7 +7920,20 @@ function createAgent(params) {
|
|
|
7750
7920
|
tracer: params.tracer,
|
|
7751
7921
|
logger,
|
|
7752
7922
|
hookRunner: params.hookRunner,
|
|
7753
|
-
|
|
7923
|
+
// Kanban tracks work; it does not gate it. When this was hard-wired `true`
|
|
7924
|
+
// a mutating tool was refused until a managed card existed, carried a
|
|
7925
|
+
// description and acceptance criteria, and had been started — so the
|
|
7926
|
+
// session spent its effort on card ceremony instead of the work the card
|
|
7927
|
+
// describes. Cards still advance and boards still mirror; path-scoped
|
|
7928
|
+
// `boundary` policies (the actual access control) are unaffected and still
|
|
7929
|
+
// enforced.
|
|
7930
|
+
//
|
|
7931
|
+
// It stays OFF by default, but is now an operator switch rather than a
|
|
7932
|
+
// literal: `tools.kanbanGovernance: true` opts an installation in. The
|
|
7933
|
+
// four hosts (this pipeline, mcp-serve, acp-server-agent, and the fleet
|
|
7934
|
+
// subagent factory) must resolve it identically, or a subagent would run
|
|
7935
|
+
// under a different contract than the leader that dispatched it.
|
|
7936
|
+
requireKanbanGovernance: params.config.tools.kanbanGovernance ?? false
|
|
7754
7937
|
};
|
|
7755
7938
|
const toolExecutor = new ToolExecutor2(params.tools, toolExecutorOptions);
|
|
7756
7939
|
void bootstrapMailboxBridgeAtStartup({
|
|
@@ -7771,6 +7954,7 @@ function createAgent(params) {
|
|
|
7771
7954
|
events: params.events,
|
|
7772
7955
|
pipelines: params.pipelines,
|
|
7773
7956
|
context: params.context,
|
|
7957
|
+
refreshSystemPrompt: true,
|
|
7774
7958
|
maxIterations: params.config.tools.maxIterations,
|
|
7775
7959
|
iterationTimeoutMs: params.config.tools.iterationTimeoutMs,
|
|
7776
7960
|
executionStrategy: params.config.tools.defaultExecutionStrategy,
|
|
@@ -8262,7 +8446,7 @@ import {
|
|
|
8262
8446
|
watchProviderConfig
|
|
8263
8447
|
} from "@wrongstack/core/storage";
|
|
8264
8448
|
import { withCatalogCapabilities as withCatalogCapabilities2 } from "@wrongstack/providers";
|
|
8265
|
-
import { getSageService } from "@wrongstack/sage";
|
|
8449
|
+
import { getSageService as getSageService2 } from "@wrongstack/sage";
|
|
8266
8450
|
|
|
8267
8451
|
// src/wiring/fallback-gate.ts
|
|
8268
8452
|
function createFallbackGate(_events) {
|
|
@@ -8401,7 +8585,7 @@ function setupProviderRuntime(deps) {
|
|
|
8401
8585
|
})
|
|
8402
8586
|
);
|
|
8403
8587
|
if (cfg.features.memory && cfg.features.memoryConsolidation !== false) {
|
|
8404
|
-
const consSage =
|
|
8588
|
+
const consSage = getSageService2(memoryStore);
|
|
8405
8589
|
agent.extensions.register(
|
|
8406
8590
|
new SessionMemoryConsolidator({
|
|
8407
8591
|
memoryStore,
|
|
@@ -8818,88 +9002,11 @@ async function setupCodebaseIndexing(deps) {
|
|
|
8818
9002
|
|
|
8819
9003
|
// src/wiring/sage.ts
|
|
8820
9004
|
import {
|
|
8821
|
-
|
|
8822
|
-
|
|
8823
|
-
|
|
8824
|
-
|
|
8825
|
-
InjectionTracker
|
|
9005
|
+
AUTO_HYGIENE_INTERVAL_MS,
|
|
9006
|
+
_resetAutoHygieneThrottleForTesting,
|
|
9007
|
+
sageHygieneOptionsFromConfig,
|
|
9008
|
+
setupSage
|
|
8826
9009
|
} from "@wrongstack/sage";
|
|
8827
|
-
var lastAutoHygieneAt = 0;
|
|
8828
|
-
var AUTO_HYGIENE_INTERVAL_MS = 60 * 6e4;
|
|
8829
|
-
function setupSage(deps) {
|
|
8830
|
-
const cfg = deps.config.Sage;
|
|
8831
|
-
const noop = async () => {
|
|
8832
|
-
};
|
|
8833
|
-
if (deps.config.features.memory === false) return noop;
|
|
8834
|
-
if (cfg?.enabled === false) return noop;
|
|
8835
|
-
if (!deps.memoryStore) return noop;
|
|
8836
|
-
const memoryStore = deps.memoryStore;
|
|
8837
|
-
const retrieval = getSageRetrieval2(memoryStore);
|
|
8838
|
-
if (!retrieval) {
|
|
8839
|
-
deps.logger.debug("sage middleware skipped: memory store does not support retrieval");
|
|
8840
|
-
return noop;
|
|
8841
|
-
}
|
|
8842
|
-
const injectionTracker = new InjectionTracker();
|
|
8843
|
-
if (cfg?.inject?.toolResults !== false) {
|
|
8844
|
-
deps.pipelines.toolCall.use(
|
|
8845
|
-
createSageToolCallMiddleware({
|
|
8846
|
-
memory: retrieval,
|
|
8847
|
-
maxHintsPerTool: cfg?.inject?.maxHintsPerTool,
|
|
8848
|
-
maxCharsPerTool: cfg?.inject?.maxCharsPerTool,
|
|
8849
|
-
taskAware: cfg?.inject?.taskAware,
|
|
8850
|
-
minScore: cfg?.inject?.minScore,
|
|
8851
|
-
minImportance: cfg?.inject?.minImportance,
|
|
8852
|
-
relationFloor: cfg?.inject?.relationFloor,
|
|
8853
|
-
repeatCooldownMs: cfg?.inject?.repeatCooldownMs,
|
|
8854
|
-
verifyOnMutation: cfg?.hygiene?.autoOnFileChange,
|
|
8855
|
-
triggers: cfg?.inject?.triggers,
|
|
8856
|
-
tracker: injectionTracker,
|
|
8857
|
-
events: deps.events,
|
|
8858
|
-
getSessionId: deps.getSessionId
|
|
8859
|
-
})
|
|
8860
|
-
);
|
|
8861
|
-
}
|
|
8862
|
-
if (cfg?.inject?.turnContext === true) {
|
|
8863
|
-
deps.pipelines.request.use(
|
|
8864
|
-
createSageTurnMiddleware({
|
|
8865
|
-
memory: retrieval,
|
|
8866
|
-
maxMemories: cfg?.inject?.maxTurnMemories,
|
|
8867
|
-
maxChars: cfg?.inject?.maxCharsPerTurn,
|
|
8868
|
-
minScore: cfg?.inject?.minScore,
|
|
8869
|
-
metadataWeight: cfg?.retrieval?.metadataWeight,
|
|
8870
|
-
tracker: injectionTracker,
|
|
8871
|
-
getSessionId: deps.getSessionId
|
|
8872
|
-
})
|
|
8873
|
-
);
|
|
8874
|
-
}
|
|
8875
|
-
deps.pipelines.request.use(
|
|
8876
|
-
createSageContextMonitorMiddleware({
|
|
8877
|
-
tracker: injectionTracker,
|
|
8878
|
-
events: deps.events,
|
|
8879
|
-
getSessionId: deps.getSessionId
|
|
8880
|
-
})
|
|
8881
|
-
);
|
|
8882
|
-
return async () => {
|
|
8883
|
-
await retrieval.flushPendingCounters?.();
|
|
8884
|
-
if (cfg?.hygiene?.autoAfterSession === false) return;
|
|
8885
|
-
const now = Date.now();
|
|
8886
|
-
if (now - lastAutoHygieneAt < AUTO_HYGIENE_INTERVAL_MS) {
|
|
8887
|
-
deps.logger.debug(
|
|
8888
|
-
`sage auto-hygiene skipped: last run ${Math.round((now - lastAutoHygieneAt) / 1e3)}s ago (throttle: ${AUTO_HYGIENE_INTERVAL_MS / 1e3}s)`
|
|
8889
|
-
);
|
|
8890
|
-
return;
|
|
8891
|
-
}
|
|
8892
|
-
await memoryStore.hygiene?.({
|
|
8893
|
-
retentionDays: cfg?.hygiene?.retentionDays,
|
|
8894
|
-
sessionRetentionDays: cfg?.hygiene?.sessionRetentionDays,
|
|
8895
|
-
archiveLowConfidenceAfterDays: cfg?.hygiene?.archiveLowConfidenceAfterDays,
|
|
8896
|
-
archiveUnusedAfterDays: cfg?.hygiene?.archiveUnusedAfterDays,
|
|
8897
|
-
unusedMinInjections: cfg?.hygiene?.unusedMinInjections,
|
|
8898
|
-
purgeDeletedAfterDays: cfg?.hygiene?.purgeDeletedAfterDays
|
|
8899
|
-
});
|
|
8900
|
-
lastAutoHygieneAt = now;
|
|
8901
|
-
};
|
|
8902
|
-
}
|
|
8903
9010
|
|
|
8904
9011
|
// src/wiring/runtime-dispatch-state.ts
|
|
8905
9012
|
async function prepareRuntimeDispatch(input) {
|
|
@@ -8909,7 +9016,8 @@ async function prepareRuntimeDispatch(input) {
|
|
|
8909
9016
|
memoryStore: input.memoryStore,
|
|
8910
9017
|
logger: input.logger,
|
|
8911
9018
|
events: input.events,
|
|
8912
|
-
getSessionId: () => input.agent.ctx.session.id
|
|
9019
|
+
getSessionId: () => input.agent.ctx.session.id,
|
|
9020
|
+
projectRoot: input.projectRoot
|
|
8913
9021
|
});
|
|
8914
9022
|
const disposeIndexing = await setupCodebaseIndexing({
|
|
8915
9023
|
config: input.getConfig(),
|
|
@@ -8954,6 +9062,7 @@ async function prepareRuntimeDispatch(input) {
|
|
|
8954
9062
|
owner,
|
|
8955
9063
|
category: tool.category ?? "Other",
|
|
8956
9064
|
enabled: !input.toolRegistry.isDisabled(tool.name),
|
|
9065
|
+
exposure: input.toolRegistry.isDisabled(tool.name) ? "disabled" : input.toolRegistry.isExposedToProvider(tool.name) ? "direct" : "lazy",
|
|
8957
9066
|
mutating: tool.mutating,
|
|
8958
9067
|
permission: tool.permission,
|
|
8959
9068
|
descMode: getToolDescriptionMode(input.toolRegistry, tool.name),
|
|
@@ -9688,38 +9797,6 @@ ${diff}`;
|
|
|
9688
9797
|
return "chore: update";
|
|
9689
9798
|
}
|
|
9690
9799
|
|
|
9691
|
-
// src/services/dispatch-classifier.ts
|
|
9692
|
-
import { makeLLMClassifier } from "@wrongstack/core/coordination";
|
|
9693
|
-
function makeProviderClassifier(provider, model) {
|
|
9694
|
-
return makeLLMClassifier(async (prompt) => {
|
|
9695
|
-
const ctrl = new AbortController();
|
|
9696
|
-
const timeout = setTimeout(() => ctrl.abort(), 15e3);
|
|
9697
|
-
try {
|
|
9698
|
-
const resp = await provider.complete(
|
|
9699
|
-
{
|
|
9700
|
-
model,
|
|
9701
|
-
system: [
|
|
9702
|
-
{
|
|
9703
|
-
type: "text",
|
|
9704
|
-
text: 'You are an agent router. Choose the single best agent for the task. Reply with ONLY a compact JSON object {"role":"...","reason":"..."}.'
|
|
9705
|
-
}
|
|
9706
|
-
],
|
|
9707
|
-
messages: [{ role: "user", content: [{ type: "text", text: prompt }] }],
|
|
9708
|
-
maxTokens: 120,
|
|
9709
|
-
temperature: 0
|
|
9710
|
-
},
|
|
9711
|
-
{ signal: ctrl.signal }
|
|
9712
|
-
);
|
|
9713
|
-
const content = resp.content;
|
|
9714
|
-
return Array.isArray(content) ? content[0]?.text ?? "" : "";
|
|
9715
|
-
} catch {
|
|
9716
|
-
return "";
|
|
9717
|
-
} finally {
|
|
9718
|
-
clearTimeout(timeout);
|
|
9719
|
-
}
|
|
9720
|
-
});
|
|
9721
|
-
}
|
|
9722
|
-
|
|
9723
9800
|
// src/services/mcp-management.ts
|
|
9724
9801
|
import {
|
|
9725
9802
|
color as color8,
|
|
@@ -10681,10 +10758,17 @@ ${lines.join("\n")}`;
|
|
|
10681
10758
|
const learned = loadProjectAgentLearned(role, projectRoot);
|
|
10682
10759
|
const kn = loadRoleKnowledgeManifest(role, projectRoot);
|
|
10683
10760
|
const developedSkills = listProjectSkillAugmentations(role, projectRoot);
|
|
10761
|
+
const stats = getProjectAgentLearnStats(role, projectRoot);
|
|
10684
10762
|
const lines = [`${color11.bold(role)} project identity:`];
|
|
10685
10763
|
if (developedSkills.length > 0) {
|
|
10686
10764
|
lines.push(` skills developed: ${developedSkills.join(", ")}`);
|
|
10687
10765
|
}
|
|
10766
|
+
if (stats.entryCount > 0) {
|
|
10767
|
+
const hitRate = stats.directiveHitRate === null ? "no directive exercised yet" : `${Math.round(stats.directiveHitRate * 100)}% hit rate over ${stats.appliedEntryCount} exercised`;
|
|
10768
|
+
lines.push(
|
|
10769
|
+
` directives: ${stats.entryCount} buffered \xB7 ${hitRate} \xB7 ${stats.deadEntryCount} never used`
|
|
10770
|
+
);
|
|
10771
|
+
}
|
|
10688
10772
|
if (cfg) lines.push(` config: ${JSON.stringify(cfg)}`);
|
|
10689
10773
|
if (kn)
|
|
10690
10774
|
lines.push(
|
|
@@ -12886,6 +12970,7 @@ var SOURCE_LABELS = {
|
|
|
12886
12970
|
"leader-after-task": "leader-after-task (post-tool summary)",
|
|
12887
12971
|
contributor: "contributor",
|
|
12888
12972
|
ledger: "completed-work ledger",
|
|
12973
|
+
glossary: "project jargon dictionary",
|
|
12889
12974
|
nextsteps: "next-steps gate",
|
|
12890
12975
|
other: "other (untagged)"
|
|
12891
12976
|
};
|
|
@@ -13697,6 +13782,8 @@ var KNOWN_TOP_LEVEL_KEYS = [
|
|
|
13697
13782
|
"fallbackBridge",
|
|
13698
13783
|
"fallbackProfiles",
|
|
13699
13784
|
"fallbackAuto",
|
|
13785
|
+
"fallbackMaxLastResortCandidates",
|
|
13786
|
+
"fallbackStickiness",
|
|
13700
13787
|
"hooks",
|
|
13701
13788
|
"plugins",
|
|
13702
13789
|
"pluginManager",
|
|
@@ -13842,6 +13929,37 @@ function diagnoseConfig(cfg, plugins = []) {
|
|
|
13842
13929
|
}
|
|
13843
13930
|
}
|
|
13844
13931
|
}
|
|
13932
|
+
if ("fallbackMaxLastResortCandidates" in fixed) {
|
|
13933
|
+
const v = fixed["fallbackMaxLastResortCandidates"];
|
|
13934
|
+
const n = coerceNumber(v);
|
|
13935
|
+
if (n === void 0) {
|
|
13936
|
+
delete fixed["fallbackMaxLastResortCandidates"];
|
|
13937
|
+
findings.push({
|
|
13938
|
+
path: "fallbackMaxLastResortCandidates",
|
|
13939
|
+
problem: `expected a non-negative number, got ${JSON.stringify(v)}`,
|
|
13940
|
+
severity: "error",
|
|
13941
|
+
fix: "removed (built-in default of 12 applies)"
|
|
13942
|
+
});
|
|
13943
|
+
} else {
|
|
13944
|
+
const clamped = Math.max(0, Math.floor(n));
|
|
13945
|
+
if (clamped !== v) {
|
|
13946
|
+
fixed["fallbackMaxLastResortCandidates"] = clamped;
|
|
13947
|
+
findings.push({
|
|
13948
|
+
path: "fallbackMaxLastResortCandidates",
|
|
13949
|
+
problem: `expected a non-negative integer, got ${JSON.stringify(v)}`,
|
|
13950
|
+
severity: "error",
|
|
13951
|
+
fix: `set to ${clamped}`
|
|
13952
|
+
});
|
|
13953
|
+
} else if (clamped === 0) {
|
|
13954
|
+
findings.push({
|
|
13955
|
+
path: "fallbackMaxLastResortCandidates",
|
|
13956
|
+
problem: "is 0 \u2014 last-resort auto-discovery append is disabled",
|
|
13957
|
+
severity: "warning",
|
|
13958
|
+
fix: "no change (explicit user choice)"
|
|
13959
|
+
});
|
|
13960
|
+
}
|
|
13961
|
+
}
|
|
13962
|
+
}
|
|
13845
13963
|
if ("fleet" in fixed) {
|
|
13846
13964
|
if (!isPlainObject(fixed["fleet"])) {
|
|
13847
13965
|
findings.push({
|
|
@@ -14673,6 +14791,9 @@ function buildFallbackCommand(opts) {
|
|
|
14673
14791
|
const favorites = config.favoriteModels ?? [];
|
|
14674
14792
|
const bridge = config.fallbackBridge?.trim();
|
|
14675
14793
|
const auto = config.fallbackAuto !== false;
|
|
14794
|
+
const rawCap = config.fallbackMaxLastResortCandidates;
|
|
14795
|
+
const capValue = typeof rawCap === "number" && Number.isFinite(rawCap) && rawCap >= 0 ? Math.floor(rawCap) : 12;
|
|
14796
|
+
const capLabel = capValue === 0 ? color24.dim("disabled") : color24.green(String(capValue));
|
|
14676
14797
|
const filteredReason = (ref) => refInactiveReason(ref, config);
|
|
14677
14798
|
const lines = [
|
|
14678
14799
|
`${color24.bold("WrongStack")} ${color24.dim("\u2014 Fallback chain")}`,
|
|
@@ -14710,6 +14831,7 @@ function buildFallbackCommand(opts) {
|
|
|
14710
14831
|
"",
|
|
14711
14832
|
` ${color24.bold("auto")} ${auto ? color24.green("on") : color24.dim("off")} ${color24.dim("/fallback auto on|off")}`,
|
|
14712
14833
|
` ${color24.bold("favorites only")} ${config.favoriteModelsOnly ? color24.green("on") : color24.dim("off")} ${color24.dim("/fallback fav only on|off")}`,
|
|
14834
|
+
` ${color24.bold("last-resort cap")} ${capLabel} ${color24.dim("(max auto-discovered models appended, 0=disabled)")}`,
|
|
14713
14835
|
"",
|
|
14714
14836
|
` ${color24.bold("profiles")} ${Object.keys(profiles).length ? "" : color24.dim("(none)")}`,
|
|
14715
14837
|
...Object.entries(profiles).sort(([a], [b]) => a.localeCompare(b)).flatMap(([name, chain]) => {
|
|
@@ -17298,7 +17420,6 @@ function buildIntakeCommand(opts) {
|
|
|
17298
17420
|
import { color as color32 } from "@wrongstack/core/utils";
|
|
17299
17421
|
import {
|
|
17300
17422
|
addCheckToTask,
|
|
17301
|
-
addColumn,
|
|
17302
17423
|
addDependency,
|
|
17303
17424
|
addGoalMetricToTask,
|
|
17304
17425
|
addNoteToTask,
|
|
@@ -17324,7 +17445,6 @@ import {
|
|
|
17324
17445
|
parseLinesIntoTasks,
|
|
17325
17446
|
releaseTaskClaim,
|
|
17326
17447
|
removeBoard,
|
|
17327
|
-
removeColumn,
|
|
17328
17448
|
removeTask,
|
|
17329
17449
|
setTaskChain,
|
|
17330
17450
|
splitTask,
|
|
@@ -17752,9 +17872,6 @@ var KANBAN_COMMAND_HELP = [
|
|
|
17752
17872
|
"",
|
|
17753
17873
|
" /kanban task check add <boardId> <taskId> <desc> Add success check",
|
|
17754
17874
|
"",
|
|
17755
|
-
" /kanban column add <boardId> <title> Add column",
|
|
17756
|
-
" /kanban column rm <boardId> <colId> Remove column",
|
|
17757
|
-
"",
|
|
17758
17875
|
" /kanban generate <description> Auto-generate board from text",
|
|
17759
17876
|
" /kanban export <boardId> Export board as markdown",
|
|
17760
17877
|
" /kanban graph export <boardId> [graphId] Save board as SDD TaskGraph",
|
|
@@ -17930,9 +18047,6 @@ function buildKanbanCommand(opts) {
|
|
|
17930
18047
|
if (cmd === "task" || cmd === "t") {
|
|
17931
18048
|
return handleTaskSubcommand(opts, projectRoot, rest, showHelp);
|
|
17932
18049
|
}
|
|
17933
|
-
if (cmd === "column" || cmd === "col" || cmd === "c") {
|
|
17934
|
-
return handleColumnSubcommand(projectRoot, rest, showHelp);
|
|
17935
|
-
}
|
|
17936
18050
|
return {
|
|
17937
18051
|
message: unknownSubcommand(
|
|
17938
18052
|
cmd,
|
|
@@ -18612,27 +18726,6 @@ ${summary}` };
|
|
|
18612
18726
|
}
|
|
18613
18727
|
return showHelp();
|
|
18614
18728
|
}
|
|
18615
|
-
async function handleColumnSubcommand(projectRoot, args, showHelp) {
|
|
18616
|
-
const [sub, boardId, ...rest] = args;
|
|
18617
|
-
if (!sub || !boardId) return showHelp();
|
|
18618
|
-
if (sub === "add") {
|
|
18619
|
-
const title = rest.join(" ");
|
|
18620
|
-
if (!title) return { message: color32.red("Usage: /kanban column add <boardId> <title>") };
|
|
18621
|
-
const result = await addColumn(projectRoot, boardId, { title });
|
|
18622
|
-
if (!result) return { message: color32.red(`Board not found: ${boardId}`) };
|
|
18623
|
-
return { message: color32.green(`\u2705 Column added: ${result.column.title}`) };
|
|
18624
|
-
}
|
|
18625
|
-
if (sub === "rm" || sub === "remove" || sub === "delete") {
|
|
18626
|
-
const colId = rest[0];
|
|
18627
|
-
if (!colId) return { message: color32.red("Usage: /kanban column rm <boardId> <columnId>") };
|
|
18628
|
-
const updated = await removeColumn(projectRoot, boardId, colId, {
|
|
18629
|
-
moveTasksToColumnId: rest[1]
|
|
18630
|
-
});
|
|
18631
|
-
if (!updated) return { message: color32.red(`Column not found: ${colId}`) };
|
|
18632
|
-
return { message: color32.green(`\u2705 Column removed: ${colId}`) };
|
|
18633
|
-
}
|
|
18634
|
-
return showHelp();
|
|
18635
|
-
}
|
|
18636
18729
|
function extractSpawnedSubagentId(summary) {
|
|
18637
18730
|
return summary.match(/Spawned subagent\s+([^\s]+)/)?.[1];
|
|
18638
18731
|
}
|
|
@@ -18650,12 +18743,6 @@ function formatLifecycleDiagnosis(err, action) {
|
|
|
18650
18743
|
if (field === "assignee") {
|
|
18651
18744
|
return `\u274C /kanban task ${action} needs an assignee. Run \`/kanban task assign <boardId> <taskId> <agent>\` first.`;
|
|
18652
18745
|
}
|
|
18653
|
-
if (field === "dueDate") {
|
|
18654
|
-
return `\u274C /kanban task ${action} needs a valid due date. Add one via the \`kanban.update_task\` tool action (patch.dueDate = "YYYY-MM-DD").`;
|
|
18655
|
-
}
|
|
18656
|
-
if (field === "labels") {
|
|
18657
|
-
return `\u274C /kanban task ${action} needs at least one label. Add one via the \`kanban.update_task\` tool action (patch.labels = ["..."]).`;
|
|
18658
|
-
}
|
|
18659
18746
|
if (field === "childTaskIds") {
|
|
18660
18747
|
return `\u274C /kanban task ${action} is an atomic parent with no persisted children. Decompose it via the kanban tool (\`split_atomic\`) before moving it forward.`;
|
|
18661
18748
|
}
|
|
@@ -20253,7 +20340,11 @@ function formatSageShow(stats, memories) {
|
|
|
20253
20340
|
|
|
20254
20341
|
// src/slash-commands/memory-triage.ts
|
|
20255
20342
|
import { toErrorMessage as toErrorMessage17 } from "@wrongstack/core/utils";
|
|
20256
|
-
import {
|
|
20343
|
+
import {
|
|
20344
|
+
fileTriageProposals,
|
|
20345
|
+
formatTriageReport,
|
|
20346
|
+
runTriage
|
|
20347
|
+
} from "@wrongstack/sage";
|
|
20257
20348
|
var DEFAULT_MAX_PHASE3 = 1e3;
|
|
20258
20349
|
var DEFAULT_MAX_PHASE4_PAIRS = 50;
|
|
20259
20350
|
async function runTriageCommand(opts, args) {
|
|
@@ -20450,41 +20541,7 @@ async function applyDispatch(Sage, report) {
|
|
|
20450
20541
|
return lines.join("\n");
|
|
20451
20542
|
}
|
|
20452
20543
|
async function fileProposals(Sage, proposals) {
|
|
20453
|
-
|
|
20454
|
-
const failures = [];
|
|
20455
|
-
let filed = 0;
|
|
20456
|
-
for (const proposal of proposals) {
|
|
20457
|
-
const input = {
|
|
20458
|
-
text: proposal.memoryText,
|
|
20459
|
-
targetMemoryId: proposal.memoryId,
|
|
20460
|
-
reviewReason: proposal.reason,
|
|
20461
|
-
suggestedAction: proposal.suggestedAction,
|
|
20462
|
-
kind: "memory_review",
|
|
20463
|
-
scope: "project",
|
|
20464
|
-
importance: 0.5,
|
|
20465
|
-
confidence: 0.9,
|
|
20466
|
-
tags: ["triage"],
|
|
20467
|
-
anchors: [],
|
|
20468
|
-
sources: [{ type: "project_instruction" }]
|
|
20469
|
-
};
|
|
20470
|
-
inputs.push(input);
|
|
20471
|
-
try {
|
|
20472
|
-
await Sage.createCandidate(input);
|
|
20473
|
-
filed++;
|
|
20474
|
-
} catch (err) {
|
|
20475
|
-
failures.push({
|
|
20476
|
-
memoryId: proposal.memoryId,
|
|
20477
|
-
error: toErrorMessage17(err)
|
|
20478
|
-
});
|
|
20479
|
-
}
|
|
20480
|
-
}
|
|
20481
|
-
return {
|
|
20482
|
-
filed,
|
|
20483
|
-
failed: failures.length,
|
|
20484
|
-
total: proposals.length,
|
|
20485
|
-
failures,
|
|
20486
|
-
inputs
|
|
20487
|
-
};
|
|
20544
|
+
return fileTriageProposals(Sage, proposals);
|
|
20488
20545
|
}
|
|
20489
20546
|
|
|
20490
20547
|
// src/slash-commands/memory.ts
|
|
@@ -31404,7 +31461,7 @@ async function runInteractive(cliCtx) {
|
|
|
31404
31461
|
activeMode
|
|
31405
31462
|
});
|
|
31406
31463
|
if (modeResult.kind === "exit") {
|
|
31407
|
-
|
|
31464
|
+
writeErr3(`${modeResult.message}
|
|
31408
31465
|
`);
|
|
31409
31466
|
await reader.close();
|
|
31410
31467
|
return modeResult.code;
|
|
@@ -31425,6 +31482,15 @@ async function runInteractive(cliCtx) {
|
|
|
31425
31482
|
container,
|
|
31426
31483
|
modeStore,
|
|
31427
31484
|
memoryStore,
|
|
31485
|
+
// Provide a narrow domain-term adapter so the prompt builder emits a
|
|
31486
|
+
// compact `[Project Jargon Dictionary]` block. Closes over the resolved
|
|
31487
|
+
// SAGE `memoryStore` (a `MemoryPort`) and uses the typed
|
|
31488
|
+
// `SageServiceLike` capability (NOT `@wrongstack/sage-mcp` — the
|
|
31489
|
+
// in-process consumer rule from `packages/sage/docs/direct-icp-usage.md`).
|
|
31490
|
+
// Returns only memories tagged `domain-term` via the capability's
|
|
31491
|
+
// `searchSage` op, which performs the tag-filter at the SQL layer rather
|
|
31492
|
+
// than scanning the whole corpus on every prompt build.
|
|
31493
|
+
domainGlossary: createDomainGlossaryAdapter(memoryStore),
|
|
31428
31494
|
skillLoader,
|
|
31429
31495
|
sessionRef,
|
|
31430
31496
|
autonomyModeRef,
|
|
@@ -31445,6 +31511,7 @@ async function runInteractive(cliCtx) {
|
|
|
31445
31511
|
pathJoiner: { join: (a, b) => path31.join(a, b) },
|
|
31446
31512
|
systemPromptBuilderToken: TOKENS9.SystemPromptBuilder
|
|
31447
31513
|
});
|
|
31514
|
+
await refreshDomainTermsMirror({ projectRoot, memoryStore });
|
|
31448
31515
|
const toolRegistry = new ToolRegistry2();
|
|
31449
31516
|
registerBuiltinTools({
|
|
31450
31517
|
toolRegistry,
|
|
@@ -31540,7 +31607,26 @@ async function runInteractive(cliCtx) {
|
|
|
31540
31607
|
}
|
|
31541
31608
|
});
|
|
31542
31609
|
const { context, planPath, session } = sessResult;
|
|
31610
|
+
context.meta["promptOnlineAgents"] = onlineAgents;
|
|
31543
31611
|
sessionRef.current = session;
|
|
31612
|
+
const replayFlag = flags["replay"];
|
|
31613
|
+
const recordFlag = flags["record"];
|
|
31614
|
+
if (typeof replayFlag === "string" || recordFlag === true) {
|
|
31615
|
+
const { bindReplayToContainer } = await import("./replay-XU6ZD7GM.js");
|
|
31616
|
+
const mode = recordFlag === true ? "record" : "replay";
|
|
31617
|
+
bindReplayToContainer({
|
|
31618
|
+
container,
|
|
31619
|
+
wpaths,
|
|
31620
|
+
// Recording follows the live session across `/resume` and new-session
|
|
31621
|
+
// swaps; replaying is pinned to the id the user named.
|
|
31622
|
+
sessionId: typeof replayFlag === "string" ? replayFlag : () => sessionRef.current?.id ?? "",
|
|
31623
|
+
mode,
|
|
31624
|
+
logger
|
|
31625
|
+
});
|
|
31626
|
+
logger.info(
|
|
31627
|
+
`replay: ProviderRunner bound in '${mode}' mode for session ${typeof replayFlag === "string" ? replayFlag : session.id}`
|
|
31628
|
+
);
|
|
31629
|
+
}
|
|
31544
31630
|
const governanceHandle = await setupCliGovernance({
|
|
31545
31631
|
projectRoot,
|
|
31546
31632
|
projectId: wpaths.projectSlug,
|
|
@@ -32075,7 +32161,7 @@ async function runInteractive(cliCtx) {
|
|
|
32075
32161
|
onEvent: evOn
|
|
32076
32162
|
});
|
|
32077
32163
|
const savedProviderCfg = config.providers?.[config.provider];
|
|
32078
|
-
const { execute } = await import("./execution-
|
|
32164
|
+
const { execute } = await import("./execution-YMBD7YWC.js");
|
|
32079
32165
|
const stopHeapWatchdog = startSharedHeapWatchdog({
|
|
32080
32166
|
collectStats: () => {
|
|
32081
32167
|
const hqQueue = hqPublisherRef.current?.getQueueStats();
|
|
@@ -32126,6 +32212,11 @@ async function runInteractive(cliCtx) {
|
|
|
32126
32212
|
events,
|
|
32127
32213
|
slashRegistry,
|
|
32128
32214
|
tokenCounter,
|
|
32215
|
+
// Forward the boot sessionRef so `resumeSession` can repoint
|
|
32216
|
+
// it when an in-process `/resume` swaps the active writer.
|
|
32217
|
+
// Without this, provider-side `getSessionId` callbacks and the
|
|
32218
|
+
// record-mode binding keep referring to the boot session.
|
|
32219
|
+
sessionRef,
|
|
32129
32220
|
activateSessionIdentity: activateSession,
|
|
32130
32221
|
config,
|
|
32131
32222
|
configStore,
|
|
@@ -32308,4 +32399,4 @@ export {
|
|
|
32308
32399
|
CLI_VERSION,
|
|
32309
32400
|
runInteractive
|
|
32310
32401
|
};
|
|
32311
|
-
//# sourceMappingURL=cli-main-
|
|
32402
|
+
//# sourceMappingURL=cli-main-W3T56YCY.js.map
|