@wrongstack/core 0.303.0 → 0.305.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chronicle/project-server.js +18 -48
- package/dist/coordination/agents/index.js +530 -130
- package/dist/coordination/agents/project-agent-consolidation.d.ts +5 -0
- package/dist/coordination/agents/project-agent-directive-outcome.d.ts +57 -0
- package/dist/coordination/agents/project-agent-identity.d.ts +26 -12
- package/dist/coordination/agents/project-agent-learning-policy.d.ts +22 -1
- package/dist/coordination/agents/project-agent-learning-structured.d.ts +46 -1
- package/dist/coordination/agents/project-agent-quarantine.d.ts +63 -0
- package/dist/coordination/agents/project-agent-skill-layer.d.ts +55 -10
- package/dist/coordination/agents/types.d.ts +10 -2
- package/dist/coordination/director-prompts.d.ts +19 -6
- package/dist/coordination/director-tools.d.ts +2 -2
- package/dist/coordination/fleet.d.ts +0 -6
- package/dist/coordination/index.d.ts +2 -1
- package/dist/coordination/index.js +1581 -955
- package/dist/coordination/mailbox-project-server.js +28 -57
- package/dist/core/agent-types.d.ts +4 -2
- package/dist/core/agent.d.ts +1 -0
- package/dist/core/context.d.ts +15 -0
- package/dist/core/conversation-state.d.ts +14 -0
- package/dist/core/fallback-profile-manager.d.ts +70 -2
- package/dist/core/index.js +308 -108
- package/dist/core/system-prompt-blocks.d.ts +1 -1
- package/dist/core/system-prompt-builder.d.ts +13 -1
- package/dist/core/system-prompt-glossary.d.ts +73 -0
- package/dist/core/system-prompt-memory-skills.d.ts +2 -2
- package/dist/defaults/index.js +910 -693
- package/dist/execution/council-orchestrator.d.ts +3 -13
- package/dist/execution/index.js +211 -75
- package/dist/execution/one-shot-llm.d.ts +5 -0
- package/dist/hq/index.js +17 -7
- package/dist/hq/protocol/kanban.d.ts +21 -0
- package/dist/hq/protocol.js +5 -1
- package/dist/hq/redaction.d.ts +14 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3505 -2539
- package/dist/infrastructure/index.js +247 -122
- package/dist/plugin/index.js +101 -3
- package/dist/registry/index.js +11 -0
- package/dist/registry/tool-registry.d.ts +8 -0
- package/dist/replay/hash.d.ts +9 -0
- package/dist/replay/index.js +14 -4
- package/dist/replay/replay-provider-runner.d.ts +31 -1
- package/dist/security/index.js +25 -20
- package/dist/security/secret-vault.d.ts +2 -0
- package/dist/session-catalog/index.js +62 -8
- package/dist/session-catalog/project-server.js +109 -78
- package/dist/session-catalog/protocol.d.ts +11 -4
- package/dist/session-catalog/store.d.ts +2 -2
- package/dist/storage/index.js +224 -67
- package/dist/storage/memory-consolidator.d.ts +4 -2
- package/dist/storage/session-resume-validation.d.ts +24 -0
- package/dist/storage/session-store/directory-scan.d.ts +5 -1
- package/dist/storage/session-store/fork-session.d.ts +13 -1
- package/dist/storage/session-store/load-cache.d.ts +11 -0
- package/dist/storage/session-store/prune-helpers.d.ts +5 -0
- package/dist/storage/session-store.d.ts +18 -0
- package/dist/tools/index.js +174 -74
- package/dist/types/config/mcp-features.d.ts +31 -1
- package/dist/types/config/root.d.ts +12 -0
- package/dist/types/config/tools.d.ts +22 -0
- package/dist/types/config/ui.d.ts +7 -4
- package/dist/types/default-config.d.ts +1 -0
- package/dist/types/index.js +24 -1
- package/dist/types/session.d.ts +9 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +214 -76
- package/dist/utils/project-state-guard.d.ts +21 -0
- package/dist/utils/session-scoped-path.d.ts +17 -0
- package/dist/utils/todos-format.d.ts +20 -0
- package/instructions/leader-after-task.md +3 -4
- package/instructions/system-lite.md +10 -13
- package/instructions/system-pro.md +18 -25
- package/instructions/system.md +18 -23
- package/package.json +3 -3
- package/skills/wrongstack-kanban/SKILL.md +95 -124
|
@@ -57,6 +57,18 @@ export declare class DefaultSessionStore implements SessionStore {
|
|
|
57
57
|
private ensureShardDir;
|
|
58
58
|
create(meta: Omit<SessionMetadata, 'startedAt'>): Promise<SessionWriter>;
|
|
59
59
|
fork(id: string, opts?: SessionForkOptions): Promise<ForkedSession>;
|
|
60
|
+
/**
|
|
61
|
+
* Implements {@link SessionForkHost.readRawEvents} — the parent stream a fork
|
|
62
|
+
* inherits, unmodified.
|
|
63
|
+
*
|
|
64
|
+
* Deliberately NOT `load()`: that loader empties superseded snapshot payloads
|
|
65
|
+
* in place and front-drops events past its retention budget, both of which
|
|
66
|
+
* are correct for reconstructing a conversation and wrong for copying a
|
|
67
|
+
* journal prefix into a child. Streaming with an accept-everything predicate
|
|
68
|
+
* keeps the scrubbing contract (`searchEvents` scrubs each line the same way
|
|
69
|
+
* `load()` does) without either transformation.
|
|
70
|
+
*/
|
|
71
|
+
readRawEvents(id: string): Promise<SessionEvent[]>;
|
|
60
72
|
/**
|
|
61
73
|
* Capture the deterministic post-tool workspace identity through the store-owned CAS.
|
|
62
74
|
*/
|
|
@@ -167,6 +179,12 @@ export declare class DefaultSessionStore implements SessionStore {
|
|
|
167
179
|
private listFromDirectoryScan;
|
|
168
180
|
private collectShardKeys;
|
|
169
181
|
private readOrBuildShardManifest;
|
|
182
|
+
/**
|
|
183
|
+
* Shard manifests are invalidated by other store processes via atomic
|
|
184
|
+
* delete/rebuild. Validate the in-memory projection against the persisted
|
|
185
|
+
* file so one long-lived process cannot retain another process's stale view.
|
|
186
|
+
*/
|
|
187
|
+
private freshShardManifestCacheEntry;
|
|
170
188
|
private collectSessionFilesInShard;
|
|
171
189
|
private collectSessionFiles;
|
|
172
190
|
/** Recursively collect session IDs from date-shard subdirectories.
|
package/dist/tools/index.js
CHANGED
|
@@ -614,17 +614,6 @@ var CouncilOrchestrator = class {
|
|
|
614
614
|
fallbackProfileManager;
|
|
615
615
|
seatCaller;
|
|
616
616
|
judgeCaller;
|
|
617
|
-
/**
|
|
618
|
-
* Normalized ad-hoc profiles keyed by the caller's config object identity.
|
|
619
|
-
* The Brain adapter reuses ONE profile object for every decision, so this
|
|
620
|
-
* avoids re-validating + re-freezing it on every ask() without caching
|
|
621
|
-
* string-keyed registry lookups (those are already O(1)).
|
|
622
|
-
*
|
|
623
|
-
* Hosts must treat ad-hoc profile configs as IMMUTABLE once passed to
|
|
624
|
-
* ask(): the cache is keyed by object identity and never invalidated, so
|
|
625
|
-
* mutating a cached profile would silently serve the first snapshot.
|
|
626
|
-
*/
|
|
627
|
-
profileCache = /* @__PURE__ */ new WeakMap();
|
|
628
617
|
constructor(opts) {
|
|
629
618
|
if (!opts.caller && !opts.seatCaller && !opts.judgeCaller) {
|
|
630
619
|
throw new Error("CouncilOrchestrator: provide `caller`, `seatCaller`, or `judgeCaller`.");
|
|
@@ -644,8 +633,9 @@ var CouncilOrchestrator = class {
|
|
|
644
633
|
/**
|
|
645
634
|
* Resolve the effective profile for a question. String ids and the default
|
|
646
635
|
* go through the registry (already O(1)); ad-hoc config objects are
|
|
647
|
-
* normalized
|
|
648
|
-
*
|
|
636
|
+
* normalized on each call. Callers are allowed to reuse and mutate an
|
|
637
|
+
* ad-hoc object between decisions, so object-identity caching would serve a
|
|
638
|
+
* stale snapshot.
|
|
649
639
|
*/
|
|
650
640
|
resolveProfile(profile) {
|
|
651
641
|
if (typeof profile === "string" || profile === void 0) {
|
|
@@ -655,15 +645,11 @@ var CouncilOrchestrator = class {
|
|
|
655
645
|
defaultProfile: this.defaultProfile
|
|
656
646
|
});
|
|
657
647
|
}
|
|
658
|
-
|
|
659
|
-
if (cached) return cached;
|
|
660
|
-
const resolved = resolveCouncilProfile(profile, {
|
|
648
|
+
return resolveCouncilProfile(profile, {
|
|
661
649
|
registry: this.profiles,
|
|
662
650
|
personas: this.personas,
|
|
663
651
|
defaultProfile: this.defaultProfile
|
|
664
652
|
});
|
|
665
|
-
this.profileCache.set(profile, resolved);
|
|
666
|
-
return resolved;
|
|
667
653
|
}
|
|
668
654
|
async ask(question) {
|
|
669
655
|
const startedAt = Date.now();
|
|
@@ -2164,12 +2150,97 @@ var FallbackProfileManager = class {
|
|
|
2164
2150
|
return this.resolveRefs([ref], exclude);
|
|
2165
2151
|
}
|
|
2166
2152
|
/**
|
|
2167
|
-
* Resolve every usable configured target as
|
|
2153
|
+
* Resolve every usable configured target as a bounded last-resort chain.
|
|
2168
2154
|
* Normal smart defaults stay bounded; callers append this only after the
|
|
2169
|
-
* preferred chain and only when automatic fallback is enabled.
|
|
2155
|
+
* preferred chain and only when automatic fallback is enabled. The cap
|
|
2156
|
+
* ({@link MAX_LAST_RESORT_CANDIDATES}) prevents a config with many providers
|
|
2157
|
+
* from producing a degenerate chain of doomed requests during a systemic
|
|
2158
|
+
* outage — by this point the smart default, bridge, and default profile
|
|
2159
|
+
* have already failed.
|
|
2170
2160
|
*/
|
|
2171
2161
|
resolveAllConfigured(exclude) {
|
|
2172
|
-
return this.smartDefault(exclude,
|
|
2162
|
+
return this.smartDefault(exclude, this.lastResortCap());
|
|
2163
|
+
}
|
|
2164
|
+
/**
|
|
2165
|
+
* Effective cap for the last-resort append. Reads the user-configurable
|
|
2166
|
+
* {@link Config.fallbackMaxLastResortCandidates} when set and valid;
|
|
2167
|
+
* otherwise falls back to the compiled-in default
|
|
2168
|
+
* {@link MAX_LAST_RESORT_CANDIDATES}.
|
|
2169
|
+
*/
|
|
2170
|
+
lastResortCap() {
|
|
2171
|
+
const configured = this.config.fallbackMaxLastResortCandidates;
|
|
2172
|
+
if (typeof configured === "number" && Number.isFinite(configured) && configured >= 0) {
|
|
2173
|
+
return Math.floor(configured);
|
|
2174
|
+
}
|
|
2175
|
+
return MAX_LAST_RESORT_CANDIDATES;
|
|
2176
|
+
}
|
|
2177
|
+
/**
|
|
2178
|
+
* Build the complete fallback candidate chain shared by the agent-loop
|
|
2179
|
+
* extension and the one-shot orchestrator. Centralizes the bridge → primary →
|
|
2180
|
+
* selected → default-profile → all-configured ladder and the fromExplicitSource
|
|
2181
|
+
* gate so both consumers produce identical ordering and depth semantics.
|
|
2182
|
+
*
|
|
2183
|
+
* Layering (each step deduped against all prior):
|
|
2184
|
+
* 1. Bridge (emergency continuity route).
|
|
2185
|
+
* 2. Configured primary, when the live context drifted from it.
|
|
2186
|
+
* 3. The selected chain (explicit refs → named profile → smart default),
|
|
2187
|
+
* via {@link resolveEffective}.
|
|
2188
|
+
* 4. The "default" profile — extra depth, ONLY when the chain was auto-derived.
|
|
2189
|
+
* 5. Every other configured provider — last resort, ONLY when the chain was
|
|
2190
|
+
* auto-derived AND `effectiveFallbackAuto` is true.
|
|
2191
|
+
*
|
|
2192
|
+
* Returns the empty chain when `closedWorld` is true and no explicit
|
|
2193
|
+
* refs/profile resolved — a model allowlist never leaks to unlisted models.
|
|
2194
|
+
*
|
|
2195
|
+
* @internal caller-aware options (primary, closedWorld) are accepted because
|
|
2196
|
+
* the agent loop has context the manager does not own; the resolution
|
|
2197
|
+
* pipeline itself is identical for both callers.
|
|
2198
|
+
*/
|
|
2199
|
+
resolveCandidates(current, opts = {}) {
|
|
2200
|
+
const configuredPrimary = opts.primary ?? {
|
|
2201
|
+
providerId: this.config.provider,
|
|
2202
|
+
model: this.config.model
|
|
2203
|
+
};
|
|
2204
|
+
const configFallbackAuto = this.config.fallbackAuto;
|
|
2205
|
+
const effectiveFallbackAuto = configFallbackAuto !== void 0 && configFallbackAuto !== null ? configFallbackAuto : !opts.closedWorld;
|
|
2206
|
+
const explicitRefs = opts.fallbackModels ?? this.config.fallbackModels;
|
|
2207
|
+
const explicitUsable = explicitRefs !== void 0 && explicitRefs.length > 0 && this.resolveRefs(explicitRefs, current).length > 0;
|
|
2208
|
+
const profileUsable = opts.fallbackProfile !== void 0 && this.hasProfile(opts.fallbackProfile) && this.resolve(opts.fallbackProfile, { exclude: current }).length > 0;
|
|
2209
|
+
const fromExplicitSource = explicitUsable || profileUsable;
|
|
2210
|
+
const selectedChain = opts.closedWorld ? explicitRefs && explicitRefs.length > 0 ? this.resolveRefs(explicitRefs, current) : opts.fallbackProfile ? this.resolve(opts.fallbackProfile, { exclude: current }) : FREEZER_EMPTY : this.resolveEffective({
|
|
2211
|
+
fallbackModels: explicitRefs,
|
|
2212
|
+
fallbackProfile: opts.fallbackProfile,
|
|
2213
|
+
fallbackAuto: effectiveFallbackAuto,
|
|
2214
|
+
exclude: current
|
|
2215
|
+
});
|
|
2216
|
+
const candidates = [];
|
|
2217
|
+
if (opts.closedWorld) {
|
|
2218
|
+
candidates.push(...selectedChain);
|
|
2219
|
+
} else {
|
|
2220
|
+
candidates.push(...this.resolveBridge(current));
|
|
2221
|
+
if (!(configuredPrimary.providerId === current.providerId && configuredPrimary.model === current.model)) {
|
|
2222
|
+
candidates.push({
|
|
2223
|
+
providerId: configuredPrimary.providerId,
|
|
2224
|
+
model: configuredPrimary.model,
|
|
2225
|
+
providerSwitched: configuredPrimary.providerId !== current.providerId
|
|
2226
|
+
});
|
|
2227
|
+
}
|
|
2228
|
+
candidates.push(...selectedChain);
|
|
2229
|
+
if (!fromExplicitSource && effectiveFallbackAuto && opts.fallbackProfile !== "default") {
|
|
2230
|
+
candidates.push(...this.resolve("default", { exclude: current }));
|
|
2231
|
+
}
|
|
2232
|
+
if (!fromExplicitSource && effectiveFallbackAuto) {
|
|
2233
|
+
const cap = this.lastResortCap();
|
|
2234
|
+
if (cap > 0) {
|
|
2235
|
+
const usedKeys = new Set(
|
|
2236
|
+
candidates.map((c) => `${c.providerId}/${c.model}`)
|
|
2237
|
+
);
|
|
2238
|
+
const lastResort = this.smartDefault(current, Number.POSITIVE_INFINITY).filter((c) => !usedKeys.has(`${c.providerId}/${c.model}`)).slice(0, cap);
|
|
2239
|
+
candidates.push(...lastResort);
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
return dedupeChain(candidates, current);
|
|
2173
2244
|
}
|
|
2174
2245
|
// ── Provider availability (read-only) ──────────────────────────────────
|
|
2175
2246
|
checkProvider(providerId) {
|
|
@@ -2293,7 +2364,20 @@ var FallbackProfileManager = class {
|
|
|
2293
2364
|
);
|
|
2294
2365
|
}
|
|
2295
2366
|
};
|
|
2367
|
+
var MAX_LAST_RESORT_CANDIDATES = 12;
|
|
2296
2368
|
var FREEZER_EMPTY = Object.freeze([]);
|
|
2369
|
+
function dedupeChain(entries, current) {
|
|
2370
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2371
|
+
const currentKey = `${current.providerId}/${current.model}`;
|
|
2372
|
+
return Object.freeze(
|
|
2373
|
+
entries.filter((entry) => {
|
|
2374
|
+
const key = `${entry.providerId}/${entry.model}`;
|
|
2375
|
+
if (key === currentKey || seen.has(key)) return false;
|
|
2376
|
+
seen.add(key);
|
|
2377
|
+
return true;
|
|
2378
|
+
})
|
|
2379
|
+
);
|
|
2380
|
+
}
|
|
2297
2381
|
|
|
2298
2382
|
// src/core/fallback-model.ts
|
|
2299
2383
|
function fallbackProfileChain(config, profileName) {
|
|
@@ -2869,6 +2953,12 @@ function normalizeForComparison(text) {
|
|
|
2869
2953
|
}
|
|
2870
2954
|
|
|
2871
2955
|
// src/coordination/agents/project-agent-learning-structured.ts
|
|
2956
|
+
function directiveTrials(entry) {
|
|
2957
|
+
const count = (n) => typeof n === "number" && Number.isFinite(n) && n >= 0 ? Math.floor(n) : 0;
|
|
2958
|
+
const applied = count(entry.applied);
|
|
2959
|
+
const wins = Math.min(applied, count(entry.wins));
|
|
2960
|
+
return { applied, wins, losses: applied - wins };
|
|
2961
|
+
}
|
|
2872
2962
|
function parseLearnedEntryStamp(entry) {
|
|
2873
2963
|
const structuredMatch = entry.match(
|
|
2874
2964
|
/<!--\s*learned-stamp:\s*category=([\w-]+);\s*capturedAt=([^;]+?)\s*-->/
|
|
@@ -2987,6 +3077,10 @@ function parseStructuredLearnedEntriesFromContent(raw, legacyEntries = splitLear
|
|
|
2987
3077
|
const category = parseLearnedCategory(attributes["category"]) ?? "fact";
|
|
2988
3078
|
const capturedAt = attributes["capturedAt"] ?? "";
|
|
2989
3079
|
const skill = attributes["skill"];
|
|
3080
|
+
const trials = directiveTrials({
|
|
3081
|
+
applied: Number(attributes["applied"]),
|
|
3082
|
+
wins: Number(attributes["wins"])
|
|
3083
|
+
});
|
|
2990
3084
|
const start = (stamp.index ?? 0) + stamp[0].length;
|
|
2991
3085
|
const end = stamps[index + 1]?.index ?? raw.length;
|
|
2992
3086
|
const parsed = parseEntryBody(raw.slice(start, end));
|
|
@@ -2998,7 +3092,8 @@ function parseStructuredLearnedEntriesFromContent(raw, legacyEntries = splitLear
|
|
|
2998
3092
|
why: parsed.why || WHY_BY_CATEGORY[category],
|
|
2999
3093
|
how: parsed.how,
|
|
3000
3094
|
capturedAt,
|
|
3001
|
-
...skill ? { skill } : {}
|
|
3095
|
+
...skill ? { skill } : {},
|
|
3096
|
+
...trials.applied > 0 ? { applied: trials.applied, wins: trials.wins } : {}
|
|
3002
3097
|
});
|
|
3003
3098
|
}
|
|
3004
3099
|
if (structured.length === 0) {
|
|
@@ -3128,7 +3223,8 @@ function loadProjectAgentLearningPolicy(role, projectRoot) {
|
|
|
3128
3223
|
enabled: parsed.enabled !== false,
|
|
3129
3224
|
lifetimeCaptureCount: typeof parsed.lifetimeCaptureCount === "number" && Number.isInteger(parsed.lifetimeCaptureCount) && parsed.lifetimeCaptureCount >= 0 ? parsed.lifetimeCaptureCount : 0,
|
|
3130
3225
|
...typeof parsed.lastCaptureAt === "string" ? { lastCaptureAt: parsed.lastCaptureAt } : {},
|
|
3131
|
-
...parsed.lastCaptureSource === "automatic" || parsed.lastCaptureSource === "manual" || parsed.lastCaptureSource === "taught" ? { lastCaptureSource: parsed.lastCaptureSource } : {}
|
|
3226
|
+
...parsed.lastCaptureSource === "automatic" || parsed.lastCaptureSource === "manual" || parsed.lastCaptureSource === "taught" ? { lastCaptureSource: parsed.lastCaptureSource } : {},
|
|
3227
|
+
...typeof parsed.lastOptimizeAt === "string" ? { lastOptimizeAt: parsed.lastOptimizeAt } : {}
|
|
3132
3228
|
};
|
|
3133
3229
|
} catch {
|
|
3134
3230
|
return { ...DEFAULT_LEARNING_POLICY };
|
|
@@ -3538,6 +3634,8 @@ The file below stores **learning data for this project's "${role}" agent** \u201
|
|
|
3538
3634
|
- **Self-contained** \u2014 understandable without the surrounding session context.
|
|
3539
3635
|
- **Front-load concrete anchors** \u2014 commands in backticks, package names like \`@wrongstack/core\`, file paths like \`packages/core/src/.../foo.ts\`. The structured-list renderer extracts these as the "how" for the entry.
|
|
3540
3636
|
|
|
3637
|
+
**Your directives are scored against real outcomes.** After every task the runtime checks which stored directives were actually exercised \u2014 it matches their anchors against the report \u2014 and folds that task's success or failure into each one's record. A directive that keeps correlating with success outlives newer arrivals and survives rewording; one that has been exercised repeatedly and kept correlating with failure is retired and stops being injected. Two consequences for how you write them: anchors are what make a directive *measurable*, not just runnable, so an anchorless directive can never earn a record; and a directive you are unsure about costs nothing to write, because the loop will find out.
|
|
3638
|
+
|
|
3541
3639
|
**Tag the skill you are developing.** When a directive refines one of your skills, mark it: \`## LEARNED [skill: testing]\`. Tagged directives are distilled into that skill's project addendum, so the lesson arrives as part of the skill itself on every future run instead of as a loose fact. Untagged directives are routed automatically when the wording makes the target obvious, and stay role-level otherwise.
|
|
3542
3640
|
|
|
3543
3641
|
**Bad** (session log \u2014 rejected at capture time):
|
|
@@ -3822,8 +3920,16 @@ var TOOLS = {
|
|
|
3822
3920
|
"test",
|
|
3823
3921
|
"mailbox"
|
|
3824
3922
|
],
|
|
3825
|
-
/**
|
|
3826
|
-
|
|
3923
|
+
/**
|
|
3924
|
+
* Version control.
|
|
3925
|
+
*
|
|
3926
|
+
* `mailbox` is in every preset on purpose: a subagent that hits a wall must
|
|
3927
|
+
* be able to say so. This was the one preset without it, which left the `git`
|
|
3928
|
+
* and `release` roles able to fail but not to ask — the two roles whose work
|
|
3929
|
+
* most often needs a decision from the leader (force-push, tag collision,
|
|
3930
|
+
* dirty tree) and least often has a safe default.
|
|
3931
|
+
*/
|
|
3932
|
+
vcs: ["read", "grep", "glob", "git", "diff", "mailbox"],
|
|
3827
3933
|
/** Dependency management + CVE audit. */
|
|
3828
3934
|
deps: ["read", "grep", "glob", "install", "outdated", "audit", "json", "mailbox"],
|
|
3829
3935
|
/** Documentation authoring. */
|
|
@@ -6992,7 +7098,9 @@ ${content}`);
|
|
|
6992
7098
|
}
|
|
6993
7099
|
if (typeof config.maxConcurrent === "number") {
|
|
6994
7100
|
if (!Number.isFinite(config.maxConcurrent) || config.maxConcurrent < 0) {
|
|
6995
|
-
issues.push(
|
|
7101
|
+
issues.push(
|
|
7102
|
+
`maxConcurrent must be a non-negative number (got ${config.maxConcurrent})`
|
|
7103
|
+
);
|
|
6996
7104
|
} else if (config.maxConcurrent === 0) {
|
|
6997
7105
|
warnings.push("maxConcurrent is 0 \u2014 subagent concurrency effectively disabled");
|
|
6998
7106
|
} else {
|
|
@@ -7004,12 +7112,28 @@ ${content}`);
|
|
|
7004
7112
|
const v = fleetBudget[key];
|
|
7005
7113
|
if (v === void 0) continue;
|
|
7006
7114
|
if (typeof v !== "number" || !Number.isFinite(v) || v < 0) {
|
|
7007
|
-
issues.push(
|
|
7115
|
+
issues.push(
|
|
7116
|
+
`fleet.budget.${key} must be a non-negative number (got ${JSON.stringify(v)})`
|
|
7117
|
+
);
|
|
7008
7118
|
} else {
|
|
7009
7119
|
ok.push(`fleet.budget.${key} ${v}`);
|
|
7010
7120
|
}
|
|
7011
7121
|
}
|
|
7012
7122
|
}
|
|
7123
|
+
const lastResortCap = config.fallbackMaxLastResortCandidates;
|
|
7124
|
+
if (lastResortCap !== void 0) {
|
|
7125
|
+
if (typeof lastResortCap !== "number" || !Number.isFinite(lastResortCap) || lastResortCap < 0) {
|
|
7126
|
+
issues.push(
|
|
7127
|
+
`fallbackMaxLastResortCandidates must be a non-negative number (got ${String(lastResortCap)})`
|
|
7128
|
+
);
|
|
7129
|
+
} else if (Math.floor(lastResortCap) === 0) {
|
|
7130
|
+
warnings.push(
|
|
7131
|
+
`fallbackMaxLastResortCandidates is ${lastResortCap} \u2014 floors to 0, last-resort auto-discovery append is disabled`
|
|
7132
|
+
);
|
|
7133
|
+
} else {
|
|
7134
|
+
ok.push(`fallbackMaxLastResortCandidates ${Math.floor(lastResortCap)}`);
|
|
7135
|
+
}
|
|
7136
|
+
}
|
|
7013
7137
|
for (const [key, entry] of Object.entries(matrix)) {
|
|
7014
7138
|
const eProvider = entry.provider ?? config.provider;
|
|
7015
7139
|
const eModel = entry.model;
|
|
@@ -8281,19 +8405,23 @@ function badge(state) {
|
|
|
8281
8405
|
var GOVERNED_TOOL_EXECUTOR_META_KEY = "toolExecutor.executeGoverned";
|
|
8282
8406
|
|
|
8283
8407
|
// src/utils/tool-name.ts
|
|
8408
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
8284
8409
|
var WIRE_TOOL_NAME_MAX_LENGTH = 128;
|
|
8285
|
-
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
|
|
8410
|
+
var MCP_SERVER_SEGMENT_MAX_LENGTH = 48;
|
|
8411
|
+
var MCP_IDENTITY_HASH_LENGTH = 10;
|
|
8412
|
+
function collisionSafeMcpSegment(value, maxLength) {
|
|
8413
|
+
const replaced = value.replace(/[^a-zA-Z0-9_-]/g, "_") || "tool";
|
|
8414
|
+
if (replaced === value && replaced.length <= maxLength) return replaced;
|
|
8415
|
+
const suffix = `_${createHash2("sha256").update(value, "utf8").digest("hex").slice(0, MCP_IDENTITY_HASH_LENGTH)}`;
|
|
8416
|
+
return `${replaced.slice(0, Math.max(1, maxLength - suffix.length))}${suffix}`;
|
|
8289
8417
|
}
|
|
8290
8418
|
function mcpServerToolPrefix(serverName) {
|
|
8291
|
-
return `mcp__${
|
|
8419
|
+
return `mcp__${collisionSafeMcpSegment(serverName, MCP_SERVER_SEGMENT_MAX_LENGTH)}__`;
|
|
8292
8420
|
}
|
|
8293
8421
|
function mcpQualifiedToolName(serverName, toolName) {
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8422
|
+
const prefix = mcpServerToolPrefix(serverName);
|
|
8423
|
+
const toolSegment = collisionSafeMcpSegment(toolName, WIRE_TOOL_NAME_MAX_LENGTH - prefix.length);
|
|
8424
|
+
return `${prefix}${toolSegment}`;
|
|
8297
8425
|
}
|
|
8298
8426
|
|
|
8299
8427
|
// src/tools/mcp-use.ts
|
|
@@ -8565,52 +8693,24 @@ var OneShotOrchestrator = class {
|
|
|
8565
8693
|
}
|
|
8566
8694
|
/**
|
|
8567
8695
|
* Build the fallback model chain from input + config + current target.
|
|
8696
|
+
* Delegates to {@link FallbackProfileManager.resolveCandidates} — the
|
|
8697
|
+
* shared constructor used by both the agent loop and the one-shot path,
|
|
8698
|
+
* so both produce identical ordering, depth, and fromExplicitSource
|
|
8699
|
+
* semantics.
|
|
8700
|
+
*
|
|
8568
8701
|
* The injected {@link OneShotOrchestratorOptions.fallbackProfileManager}
|
|
8569
8702
|
* is the only allowed manager — OneShot never owns a private snapshot
|
|
8570
8703
|
* so a live `ConfigStore` change reaches every call without rebuilding
|
|
8571
8704
|
* the manager.
|
|
8572
8705
|
*/
|
|
8573
8706
|
resolveFallbackChain(input, config, target) {
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
|
|
8707
|
+
return this.opts.fallbackProfileManager.resolveCandidates(target, {
|
|
8708
|
+
fallbackModels: input.fallbackModels && input.fallbackModels.length > 0 ? input.fallbackModels : config.fallbackModels,
|
|
8709
|
+
// The one-shot already tried `target` as the primary before entering
|
|
8710
|
+
// the chain. Passing it as `primary` suppresses the primary-insertion
|
|
8711
|
+
// step in resolveCandidates (primary === current → not pushed).
|
|
8712
|
+
primary: target
|
|
8579
8713
|
});
|
|
8580
|
-
if (input.fallbackModels && input.fallbackModels.length > 0) {
|
|
8581
|
-
const resolved = mgr.resolveEffective({
|
|
8582
|
-
fallbackModels: input.fallbackModels,
|
|
8583
|
-
fallbackAuto: false,
|
|
8584
|
-
exclude: target
|
|
8585
|
-
});
|
|
8586
|
-
selected = resolved;
|
|
8587
|
-
fromExplicitSource = resolved.length > 0;
|
|
8588
|
-
} else if (config.fallbackModels && config.fallbackModels.length > 0) {
|
|
8589
|
-
const resolved = mgr.resolveEffective({
|
|
8590
|
-
fallbackModels: config.fallbackModels,
|
|
8591
|
-
fallbackAuto: false,
|
|
8592
|
-
exclude: target
|
|
8593
|
-
});
|
|
8594
|
-
selected = resolved;
|
|
8595
|
-
fromExplicitSource = resolved.length > 0;
|
|
8596
|
-
} else if (config.fallbackAuto !== false) {
|
|
8597
|
-
selected = mgr.resolveEffective({
|
|
8598
|
-
fallbackAuto: true,
|
|
8599
|
-
exclude: target
|
|
8600
|
-
});
|
|
8601
|
-
}
|
|
8602
|
-
if (config.fallbackAuto === false) return selected;
|
|
8603
|
-
if (fromExplicitSource) return selected;
|
|
8604
|
-
const combined = [...selected, ...mgr.resolveAllConfigured(target)];
|
|
8605
|
-
const seen = /* @__PURE__ */ new Set();
|
|
8606
|
-
return Object.freeze(
|
|
8607
|
-
combined.filter((entry) => {
|
|
8608
|
-
const key = `${entry.providerId}/${entry.model}`;
|
|
8609
|
-
if (seen.has(key)) return false;
|
|
8610
|
-
seen.add(key);
|
|
8611
|
-
return true;
|
|
8612
|
-
})
|
|
8613
|
-
);
|
|
8614
8714
|
}
|
|
8615
8715
|
/** Attempt a provider call while preserving the actual failure for callers. */
|
|
8616
8716
|
async tryCall(provider, request, signal, providerId, model) {
|
|
@@ -281,10 +281,40 @@ export interface SageConfig {
|
|
|
281
281
|
* many days. Undefined/0 disables purge (default).
|
|
282
282
|
*/
|
|
283
283
|
purgeDeletedAfterDays?: number | undefined;
|
|
284
|
+
/**
|
|
285
|
+
* How deep periodic hygiene verifies anchors.
|
|
286
|
+
* - `existence` (default): file/path still exists (`fs.access` only).
|
|
287
|
+
* - `content`: also content-hash / symbol / command resolution.
|
|
288
|
+
* - `git`: content checks plus git blob hash comparison when available.
|
|
289
|
+
*/
|
|
290
|
+
verifyDepth?: 'existence' | 'content' | 'git' | undefined;
|
|
284
291
|
} | undefined;
|
|
285
292
|
embeddings?: {
|
|
286
|
-
/**
|
|
293
|
+
/**
|
|
294
|
+
* When true, retrieval re-ranks lexical candidates with the offline
|
|
295
|
+
* hashing embedding provider (hybrid). Default: false. Fail-open:
|
|
296
|
+
* lexical results are always available if embeddings fail.
|
|
297
|
+
*/
|
|
287
298
|
enabled?: boolean | undefined;
|
|
288
299
|
} | undefined;
|
|
300
|
+
/**
|
|
301
|
+
* Opt-in automatic capture of durable tool outcomes into SAGE.
|
|
302
|
+
* Off by default — operators enable per-project when they want
|
|
303
|
+
* command success notes recorded without an explicit `remember` call.
|
|
304
|
+
*/
|
|
305
|
+
capture?: {
|
|
306
|
+
/** Persist successful command/tool outcomes as `tool_outcome` memories. Default: false. */
|
|
307
|
+
toolOutcomes?: boolean | undefined;
|
|
308
|
+
/** Persist recurring error signatures as `error_pattern` memories. Default: false. */
|
|
309
|
+
errorPatterns?: boolean | undefined;
|
|
310
|
+
} | undefined;
|
|
311
|
+
/**
|
|
312
|
+
* Optional automated triage scheduling. When true, `setupSage` schedules a
|
|
313
|
+
* daily job (1h after boot, then every 24h): hygiene + bounded triage that
|
|
314
|
+
* files review proposals (never auto-deletes). Default: false.
|
|
315
|
+
*/
|
|
316
|
+
triage?: {
|
|
317
|
+
dailyDryRun?: boolean | undefined;
|
|
318
|
+
} | undefined;
|
|
289
319
|
}
|
|
290
320
|
//# sourceMappingURL=mcp-features.d.ts.map
|
|
@@ -144,6 +144,18 @@ export interface Config {
|
|
|
144
144
|
primaryProbeInterval?: number | undefined;
|
|
145
145
|
stickyFallbackTurns?: number | undefined;
|
|
146
146
|
} | undefined;
|
|
147
|
+
/**
|
|
148
|
+
* Maximum number of candidates appended by the last-resort fallback sweep
|
|
149
|
+
* (`resolveAllConfigured`). When the smart default, bridge, and named/default
|
|
150
|
+
* profiles have all failed, every other configured provider is appended —
|
|
151
|
+
* this cap bounds the chain so a config with many providers does not produce
|
|
152
|
+
* a degenerate sequence of doomed requests during a systemic outage.
|
|
153
|
+
*
|
|
154
|
+
* Set to a higher value for rich multi-provider configs that want maximum
|
|
155
|
+
* diversity; set to 0 to disable the last-resort append entirely. The
|
|
156
|
+
* compiled-in default is exposed as `MAX_LAST_RESORT_CANDIDATES` (12).
|
|
157
|
+
*/
|
|
158
|
+
fallbackMaxLastResortCandidates?: number | undefined;
|
|
147
159
|
/**
|
|
148
160
|
* Lifecycle command/HTTP hooks, keyed by event. Commands receive HookInput
|
|
149
161
|
* JSON on stdin; HTTP hooks receive the same object as a POST body. A typed
|
|
@@ -39,6 +39,28 @@ export interface ToolsConfig {
|
|
|
39
39
|
* `/settings` ("Filesystem access").
|
|
40
40
|
*/
|
|
41
41
|
restrictToProjectRoot?: boolean | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* Require a ready, running **managed** Kanban card before any product
|
|
44
|
+
* mutation. Default: **false** — Kanban records work, it does not permit it,
|
|
45
|
+
* and that is the shipped contract the system prompt and the `kanban` tool
|
|
46
|
+
* description both state.
|
|
47
|
+
*
|
|
48
|
+
* Turning this on inverts that for this installation: a mutating tool
|
|
49
|
+
* (`write`, `edit`, `exec`, …) is refused until the run is bound to a card
|
|
50
|
+
* that is on a managed board, passes `evaluateContractGraphReadiness`, and
|
|
51
|
+
* sits in Running with a live assignment. Control tools (`kanban`, `todo`,
|
|
52
|
+
* `plan`, `task`) are always exempt so the agent can always record evidence
|
|
53
|
+
* or start the next card.
|
|
54
|
+
*
|
|
55
|
+
* Non-managed boards — session mirrors, SDD mirrors, plain imports — are
|
|
56
|
+
* skipped rather than blocked: they structurally cannot carry a lifecycle,
|
|
57
|
+
* so demanding one would deadlock every mutation with no reachable remedy.
|
|
58
|
+
* They still fall through to their path-scoped `boundary` policy.
|
|
59
|
+
*
|
|
60
|
+
* Independent of the two checks that are ALWAYS on regardless of this flag:
|
|
61
|
+
* the dispatch lease fence and the board/task filesystem `boundary`.
|
|
62
|
+
*/
|
|
63
|
+
kanbanGovernance?: boolean | undefined;
|
|
42
64
|
/**
|
|
43
65
|
* Per-command policy for the `exec` tool's allowlist. The tool ships a
|
|
44
66
|
* curated default allowlist of dev/build commands; this extends or trims it.
|
|
@@ -5,10 +5,13 @@
|
|
|
5
5
|
* the TUI runtime can all reference the same string union without
|
|
6
6
|
* importing the TUI package (which would invert the dependency direction).
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* This array is the CANONICAL list. The CLI `/theme` command and the boot
|
|
9
|
+
* theme adapter both derive their valid-id sets from it, so adding an entry
|
|
10
|
+
* here needs exactly ONE follow-up: a matching palette + `THEME_OPTIONS` row
|
|
11
|
+
* in `packages/tui/src/theme.ts`. Both are compile-enforced — `themePresets`
|
|
12
|
+
* is typed `Record<ThemePresetId, Theme>` (no cast) and the CLI's `THEME_META`
|
|
13
|
+
* is a total record — so a missing preset fails `tsc`, not the runtime.
|
|
11
14
|
*/
|
|
12
|
-
export declare const THEME_PRESET_IDS: readonly ['catppuccin', 'tokyo-night', 'nord', 'cyberpunk', 'dracula', 'gruvbox-dark', 'solarized-dark', 'one-dark', 'monokai', 'rose-pine', 'kanagawa', 'ayu-dark', 'everforest', 'night-owl', 'synthwave'];
|
|
15
|
+
export declare const THEME_PRESET_IDS: readonly ['catppuccin', 'tokyo-night', 'nord', 'cyberpunk', 'dracula', 'gruvbox-dark', 'solarized-dark', 'one-dark', 'monokai', 'rose-pine', 'kanagawa', 'ayu-dark', 'everforest', 'night-owl', 'synthwave', 'github-dark', 'material-ocean', 'nightfox', 'oxocarbon', 'catppuccin-macchiato', 'catppuccin-frappe', 'gruvbox-material', 'tokyo-night-storm', 'rose-pine-moon', 'zenburn', 'palenight', 'horizon', 'sonokai', 'edge-dark', 'moonfly', 'melange', 'poimandres', 'vitesse-dark', 'aura', 'dark-plus'];
|
|
13
16
|
export type ThemePresetId = (typeof THEME_PRESET_IDS)[number];
|
|
14
17
|
//# sourceMappingURL=ui.d.ts.map
|
|
@@ -14,6 +14,7 @@ export declare const DEFAULT_TOOLS_CONFIG: Readonly<{
|
|
|
14
14
|
disabledTools: readonly string[];
|
|
15
15
|
autoExtendLimit: true;
|
|
16
16
|
restrictToProjectRoot: true;
|
|
17
|
+
kanbanGovernance: false;
|
|
17
18
|
loopDetection: Readonly<{
|
|
18
19
|
mode: 'steer-then-cut' | 'cut' | 'off';
|
|
19
20
|
steerThreshold: number;
|
package/dist/types/index.js
CHANGED
|
@@ -591,7 +591,27 @@ var THEME_PRESET_IDS = [
|
|
|
591
591
|
"ayu-dark",
|
|
592
592
|
"everforest",
|
|
593
593
|
"night-owl",
|
|
594
|
-
"synthwave"
|
|
594
|
+
"synthwave",
|
|
595
|
+
"github-dark",
|
|
596
|
+
"material-ocean",
|
|
597
|
+
"nightfox",
|
|
598
|
+
"oxocarbon",
|
|
599
|
+
"catppuccin-macchiato",
|
|
600
|
+
"catppuccin-frappe",
|
|
601
|
+
"gruvbox-material",
|
|
602
|
+
"tokyo-night-storm",
|
|
603
|
+
"rose-pine-moon",
|
|
604
|
+
"zenburn",
|
|
605
|
+
"palenight",
|
|
606
|
+
"horizon",
|
|
607
|
+
"sonokai",
|
|
608
|
+
"edge-dark",
|
|
609
|
+
"moonfly",
|
|
610
|
+
"melange",
|
|
611
|
+
"poimandres",
|
|
612
|
+
"vitesse-dark",
|
|
613
|
+
"aura",
|
|
614
|
+
"dark-plus"
|
|
595
615
|
];
|
|
596
616
|
|
|
597
617
|
// src/types/session-markers.ts
|
|
@@ -734,6 +754,9 @@ var DEFAULT_TOOLS_CONFIG = Object.freeze({
|
|
|
734
754
|
disabledTools: Object.freeze([]),
|
|
735
755
|
autoExtendLimit: true,
|
|
736
756
|
restrictToProjectRoot: true,
|
|
757
|
+
// Off by default: the board is a record of the work, not a permit for it.
|
|
758
|
+
// See ToolsConfig.kanbanGovernance for what turning it on costs and gates.
|
|
759
|
+
kanbanGovernance: false,
|
|
737
760
|
loopDetection: Object.freeze({
|
|
738
761
|
mode: "steer-then-cut",
|
|
739
762
|
steerThreshold: 3,
|
package/dist/types/session.d.ts
CHANGED
|
@@ -8,7 +8,15 @@ export interface SessionMetadata {
|
|
|
8
8
|
provider?: string | undefined;
|
|
9
9
|
startedAt: string;
|
|
10
10
|
endedAt?: string | undefined;
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Tool calls the previous run had issued but not resolved when it closed,
|
|
13
|
+
* as recorded on `session_end`.
|
|
14
|
+
*
|
|
15
|
+
* Diagnostic only. Resume does NOT restore pending state from this: it
|
|
16
|
+
* derives the same fact from the replayed conversation
|
|
17
|
+
* ({@link SessionData.pendingToolUseCount}), which also covers the crash
|
|
18
|
+
* case where no `session_end` was ever written. Nothing re-executes these.
|
|
19
|
+
*/
|
|
12
20
|
pendingToolUses?: string[] | undefined;
|
|
13
21
|
/** Parent journal metadata when this session was created by fork(). */
|
|
14
22
|
forkedFrom?: {
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ export { isSafePathSegment, MAX_PATH_SEGMENT_LENGTH, resolveContainedPath, } fro
|
|
|
31
31
|
export { getPerfProfile, indexParallelBatchSize, isFrugalPerf, type PerfProfile, SageCachePragmas, sqliteCachePragmas, tuiStreamFlushMs, useDaemonPerfDefaults, } from './perf-profile.js';
|
|
32
32
|
export { isPidAlive } from './pid.js';
|
|
33
33
|
export * from './project-identity.js';
|
|
34
|
+
export { activateProjectStateGuard, type ProjectStateGuard, type ProjectStateGuardOptions, startProjectStateGuard, } from './project-state-guard.js';
|
|
34
35
|
export { type ProjectWatchEvent, type ProjectWatchSubscription, watchProjectTree, } from './project-watch.js';
|
|
35
36
|
export { capSubject, type CompileFail, type CompileResult, compileUserRegex, MAX_SUBJECT_LEN, } from './regex-guard.js';
|
|
36
37
|
export * from './safe-json.js';
|