supipowers 2.0.1 → 2.1.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/README.md +10 -6
- package/package.json +4 -2
- package/skills/harness/SKILL.md +1 -0
- package/src/bootstrap.ts +5 -133
- package/src/commands/clear.ts +6 -6
- package/src/commands/release.ts +3 -1
- package/src/commands/update.ts +1 -1
- package/src/config/defaults.ts +5 -5
- package/src/config/loader.ts +1 -0
- package/src/config/schema.ts +2 -6
- package/src/context/analyzer.ts +104 -35
- package/src/context-mode/knowledge/store.ts +381 -43
- package/src/context-mode/tools.ts +41 -3
- package/src/deps/registry.ts +1 -12
- package/src/fix-pr/assessment.ts +1 -0
- package/src/fix-pr/prompt-builder.ts +1 -0
- package/src/git/commit.ts +76 -18
- package/src/harness/command.ts +103 -6
- package/src/harness/default-agents/docs.md +39 -0
- package/src/harness/docs/config.ts +29 -0
- package/src/harness/docs/glob-match.ts +27 -0
- package/src/harness/docs/index-renderer.ts +82 -0
- package/src/harness/docs/provenance.ts +125 -0
- package/src/harness/docs/regen-decision.ts +167 -0
- package/src/harness/docs/representative-files.ts +175 -0
- package/src/harness/docs/source-hash.ts +106 -0
- package/src/harness/docs/validator.ts +233 -0
- package/src/harness/hooks/layer-context-inject.ts +35 -1
- package/src/harness/hooks/register.ts +24 -3
- package/src/harness/pipeline.ts +20 -5
- package/src/harness/pr-comment/baseline.ts +105 -0
- package/src/harness/pr-comment/ci-env.ts +120 -0
- package/src/harness/pr-comment/gh-poster.ts +227 -0
- package/src/harness/pr-comment/handler.ts +198 -0
- package/src/harness/pr-comment/render.ts +297 -0
- package/src/harness/pr-comment/status.ts +95 -0
- package/src/harness/pr-comment/types.ts +73 -0
- package/src/harness/pr-comment/workflow-summary.ts +47 -0
- package/src/harness/project-paths.ts +95 -0
- package/src/harness/stages/design.ts +1 -0
- package/src/harness/stages/discover.ts +1 -13
- package/src/harness/stages/docs.ts +708 -0
- package/src/harness/stages/implement-apply.ts +877 -0
- package/src/harness/stages/implement.ts +64 -51
- package/src/harness/stages/plan.ts +25 -16
- package/src/harness/stages/validate.ts +370 -0
- package/src/harness/storage.ts +142 -0
- package/src/harness/tools.ts +130 -0
- package/src/mempalace/bridge.ts +207 -41
- package/src/mempalace/config.ts +10 -4
- package/src/mempalace/format.ts +122 -6
- package/src/mempalace/hooks.ts +204 -56
- package/src/mempalace/installer-helper.ts +18 -4
- package/src/mempalace/python/mempalace_bridge.py +128 -3
- package/src/mempalace/runtime.ts +55 -18
- package/src/mempalace/schema.ts +151 -30
- package/src/mempalace/session-summary.ts +5 -0
- package/src/mempalace/tool.ts +17 -4
- package/src/mempalace/upstream-limits.ts +69 -0
- package/src/planning/approval-flow.ts +25 -2
- package/src/planning/planning-ask-tool.ts +34 -4
- package/src/planning/system-prompt.ts +1 -1
- package/src/tool-catalog/active-tool-controller.ts +0 -22
- package/src/tool-catalog/active-tool-planner.ts +0 -26
- package/src/tool-catalog/tool-groups.ts +1 -9
- package/src/types.ts +87 -8
- package/src/ui-design/session.ts +114 -10
- package/src/utils/executable.ts +10 -1
- package/src/workspace/state-paths.ts +1 -1
- package/src/commands/mcp.ts +0 -814
- package/src/mcp/activation.ts +0 -77
- package/src/mcp/config.ts +0 -223
- package/src/mcp/docs.ts +0 -154
- package/src/mcp/gateway.ts +0 -103
- package/src/mcp/lifecycle.ts +0 -79
- package/src/mcp/manager-tool.ts +0 -104
- package/src/mcp/mcpc.ts +0 -113
- package/src/mcp/registry.ts +0 -98
- package/src/mcp/triggers.ts +0 -62
- package/src/mcp/types.ts +0 -95
package/src/mempalace/tool.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { resolveMempalaceConfig, type ResolvedMempalaceConfig } from "./config.j
|
|
|
5
5
|
import { formatMempalaceError, formatMempalaceResult } from "./format.js";
|
|
6
6
|
import { mempalaceToolParameters, validateMempalaceParams, type MempalaceParams } from "./schema.js";
|
|
7
7
|
import {
|
|
8
|
-
|
|
8
|
+
resolveInstalledBridgeScriptPath,
|
|
9
9
|
setupMempalaceRuntime,
|
|
10
10
|
type BridgePathResult,
|
|
11
11
|
type SetupMempalaceRuntimeOptions,
|
|
@@ -55,10 +55,11 @@ async function executeSetup(
|
|
|
55
55
|
resolved: ResolvedMempalaceConfig,
|
|
56
56
|
cwd: string,
|
|
57
57
|
managedBinDir: string,
|
|
58
|
+
defaultResolveBridgeScriptPath: () => BridgePathResult,
|
|
58
59
|
deps: MempalaceToolDeps,
|
|
59
60
|
onUpdate: unknown,
|
|
60
61
|
): Promise<{ content: Array<{ type: "text"; text: string }>; details: Record<string, unknown> }> {
|
|
61
|
-
const bridgePath = (deps.resolveBridgeScriptPath ??
|
|
62
|
+
const bridgePath = (deps.resolveBridgeScriptPath ?? defaultResolveBridgeScriptPath)();
|
|
62
63
|
if (!bridgePath.ok) {
|
|
63
64
|
const formatted = formatMempalaceError(bridgePath.error, {
|
|
64
65
|
ok: false,
|
|
@@ -121,6 +122,10 @@ export function registerMempalaceTool(
|
|
|
121
122
|
return;
|
|
122
123
|
}
|
|
123
124
|
if (!snapshot.ready) return;
|
|
125
|
+
const bridgeRuntime = {
|
|
126
|
+
resolveBridgeScriptPath: () => resolveInstalledBridgeScriptPath(platform.paths),
|
|
127
|
+
};
|
|
128
|
+
|
|
124
129
|
|
|
125
130
|
platform.registerTool({
|
|
126
131
|
name: "mempalace",
|
|
@@ -143,12 +148,20 @@ export function registerMempalaceTool(
|
|
|
143
148
|
const params = validation.params;
|
|
144
149
|
|
|
145
150
|
if (params.action === "setup") {
|
|
146
|
-
return await executeSetup(
|
|
151
|
+
return await executeSetup(
|
|
152
|
+
params,
|
|
153
|
+
resolved,
|
|
154
|
+
cwd,
|
|
155
|
+
platform.paths.global("bin"),
|
|
156
|
+
bridgeRuntime.resolveBridgeScriptPath,
|
|
157
|
+
deps,
|
|
158
|
+
onUpdate,
|
|
159
|
+
);
|
|
147
160
|
}
|
|
148
161
|
|
|
149
162
|
const bridge = deps.createBridge
|
|
150
163
|
? deps.createBridge(resolved, cwd)
|
|
151
|
-
: createMempalaceBridge({ cwd, config: resolved });
|
|
164
|
+
: createMempalaceBridge({ cwd, config: resolved, runtime: bridgeRuntime });
|
|
152
165
|
const result = await bridge.execute(params);
|
|
153
166
|
|
|
154
167
|
if (!result.ok) {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upstream MemPalace pin.
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for the `mempalace` PyPI package version and
|
|
5
|
+
* the parameter limits the upstream package enforces. Keeping these in
|
|
6
|
+
* one place — instead of sprinkling `"3.3.4"` / `500` / `100` / `128`
|
|
7
|
+
* magic literals across the config defaults, schema, hooks, and tests —
|
|
8
|
+
* makes a version bump a one-line edit and guarantees our tool surface
|
|
9
|
+
* advertises the same bounds the upstream MCP server enforces.
|
|
10
|
+
*
|
|
11
|
+
* # Bump procedure
|
|
12
|
+
* 1. Update `MEMPALACE_PACKAGE_VERSION` below.
|
|
13
|
+
* 2. Re-verify each `MEMPALACE_MAX_*` constant against the cited
|
|
14
|
+
* upstream source path. Update any that drifted.
|
|
15
|
+
* 3. If the upstream MCP API surface (function names, parameter names)
|
|
16
|
+
* changed, update the dispatch table in
|
|
17
|
+
* `src/mempalace/python/mempalace_bridge.py` and its header comment.
|
|
18
|
+
* 4. Run `bun ci`. All consumers — including tests — read from these
|
|
19
|
+
* constants, so a mismatch surfaces as a test failure rather than
|
|
20
|
+
* silent runtime drift.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Exact PyPI version installed by the managed setup pipeline. Flows into
|
|
25
|
+
* `DEFAULT_CONFIG.mempalace.packageVersion` and, from there, into the
|
|
26
|
+
* `mempalace==<version>` argument handed to `uv pip install`.
|
|
27
|
+
*/
|
|
28
|
+
export const MEMPALACE_PACKAGE_VERSION = "3.3.5";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Upper bound applied internally by `tool_search` and `tool_list_drawers`
|
|
32
|
+
* to the `limit` argument. Any value above this is silently clamped.
|
|
33
|
+
*
|
|
34
|
+
* Source: `mempalace/mcp_server.py` `_MAX_RESULTS = 100`.
|
|
35
|
+
*/
|
|
36
|
+
export const MEMPALACE_MAX_RESULTS = 100;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Maximum search-query length. `tool_search` runs `sanitize_query`, which
|
|
40
|
+
* truncates anything over this threshold (worst case: keeps only the
|
|
41
|
+
* trailing N characters). Above this, prompt-contamination patterns
|
|
42
|
+
* start dominating the embedding signal — see upstream Issue #333.
|
|
43
|
+
*
|
|
44
|
+
* Source: `mempalace/query_sanitizer.py` `MAX_QUERY_LENGTH = 250`.
|
|
45
|
+
*/
|
|
46
|
+
export const MEMPALACE_MAX_QUERY_LENGTH = 250;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Maximum length for wing / room / predicate / entity-style identifiers.
|
|
50
|
+
* `sanitize_name` and `sanitize_kg_value` raise `ValueError` above this.
|
|
51
|
+
*
|
|
52
|
+
* Source: `mempalace/config.py` `MAX_NAME_LENGTH = 128`.
|
|
53
|
+
*/
|
|
54
|
+
export const MEMPALACE_MAX_NAME_LENGTH = 128;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Maximum drawer / diary content length. `sanitize_content` defaults
|
|
58
|
+
* to this when no explicit override is passed.
|
|
59
|
+
*
|
|
60
|
+
* Source: `mempalace/config.py` `sanitize_content(..., max_length: int = 100_000)`.
|
|
61
|
+
*/
|
|
62
|
+
export const MEMPALACE_MAX_CONTENT_LENGTH = 100_000;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Upper bound applied internally by `tool_traverse_graph` to `max_hops`.
|
|
66
|
+
*
|
|
67
|
+
* Source: `mempalace/mcp_server.py` `tool_traverse_graph` — `max(1, min(max_hops, 10))`.
|
|
68
|
+
*/
|
|
69
|
+
export const MEMPALACE_MAX_HOPS = 10;
|
|
@@ -186,7 +186,7 @@ export function buildTodoWriteOpsForPlan(plan: Plan): { ops: TodoWriteOp[] } {
|
|
|
186
186
|
* When `plan` is provided and has tasks, the prompt also embeds the
|
|
187
187
|
* exact `todo_write` payload the agent must call before doing any work.
|
|
188
188
|
*/
|
|
189
|
-
function buildExecutionPrompt(
|
|
189
|
+
export function buildExecutionPrompt(
|
|
190
190
|
planContent: string,
|
|
191
191
|
planPath: string,
|
|
192
192
|
plan?: Plan,
|
|
@@ -313,7 +313,7 @@ async function executeApproveFlow(
|
|
|
313
313
|
*/
|
|
314
314
|
export function registerPlanApprovalHook(platform: Platform): void {
|
|
315
315
|
platform.on("agent_end", async (_event: any, ctx: any) => {
|
|
316
|
-
if (!planningActive ||
|
|
316
|
+
if (!planningActive || approvalPending) return;
|
|
317
317
|
|
|
318
318
|
// Detect newly written plan files
|
|
319
319
|
const plansNow = listPlans(platform.paths, planCwd);
|
|
@@ -407,6 +407,29 @@ export function registerPlanApprovalHook(platform: Platform): void {
|
|
|
407
407
|
cwd: planCwd,
|
|
408
408
|
});
|
|
409
409
|
} catch {}
|
|
410
|
+
if (!ctx?.hasUI) {
|
|
411
|
+
const message = [
|
|
412
|
+
`Plan saved to \`${planPath}\`.`,
|
|
413
|
+
"Interactive approval is unavailable in this runtime, so no execution was started.",
|
|
414
|
+
`To continue manually, explicitly send: \`Execute the saved plan at ${planPath} step by step; verify each step before proceeding.\``,
|
|
415
|
+
].join("\n");
|
|
416
|
+
debugLogger?.log("approval_flow_no_ui", {
|
|
417
|
+
planName,
|
|
418
|
+
planPath,
|
|
419
|
+
});
|
|
420
|
+
ctx?.ui?.notify?.("Plan saved; interactive approval is required before execution.", "warning");
|
|
421
|
+
platform.sendMessage(
|
|
422
|
+
{
|
|
423
|
+
customType: "supi-plan-awaiting-interactive-approval",
|
|
424
|
+
content: [{ type: "text", text: message }],
|
|
425
|
+
display: true,
|
|
426
|
+
},
|
|
427
|
+
{ deliverAs: "steer", triggerTurn: false },
|
|
428
|
+
);
|
|
429
|
+
cancelPlanTracking();
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
|
|
410
433
|
const approvalOptions = [
|
|
411
434
|
"Approve and execute",
|
|
412
435
|
"Refine plan",
|
|
@@ -4,10 +4,16 @@ import { isUiDesignActive, recordUiDesignReviewApproval } from "../ui-design/ses
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Register a `planning_ask` tool — identical to the built-in `ask` tool
|
|
7
|
-
* but with **no timeout
|
|
8
|
-
* `ask.timeout`
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* but with **no timeout**, regardless of the user's `ask.timeout` setting.
|
|
8
|
+
* OMP 14.9.5 changed the `ask.timeout` default from 30s to 0 (wait
|
|
9
|
+
* indefinitely), but a user-configured non-zero value still applies to the
|
|
10
|
+
* generic `ask` tool; this wrapper keeps planning-mode questions blocking
|
|
11
|
+
* for any such configuration.
|
|
12
|
+
*
|
|
13
|
+
* Also records the chosen option into the ui-design session ledger via
|
|
14
|
+
* `recordUiDesignReviewApproval` and pairs with
|
|
15
|
+
* `registerPlanningAskToolGuard`, which redirects generic `ask` calls back
|
|
16
|
+
* to this tool during planning / ui-design sessions.
|
|
11
17
|
*
|
|
12
18
|
* The tool is always registered (lightweight) but the planning system
|
|
13
19
|
* prompt directs the model to use it only during planning sessions.
|
|
@@ -62,6 +68,22 @@ export function registerPlanningAskTool(platform: Platform): void {
|
|
|
62
68
|
};
|
|
63
69
|
}
|
|
64
70
|
|
|
71
|
+
if (ctx?.hasUI === false || typeof ctx?.ui?.select !== "function") {
|
|
72
|
+
const result = {
|
|
73
|
+
error: "interactive_planning_question_unavailable",
|
|
74
|
+
message: "Interactive planning questions cannot be answered in this runtime. Present this question and its options to the user instead of choosing a default.",
|
|
75
|
+
question: params.question,
|
|
76
|
+
options: labels,
|
|
77
|
+
recommended: params.recommended ?? null,
|
|
78
|
+
};
|
|
79
|
+
return {
|
|
80
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
81
|
+
details: result,
|
|
82
|
+
error: true,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
65
87
|
const choice = await ctx.ui.select(params.question, labels, {
|
|
66
88
|
initialIndex: params.recommended,
|
|
67
89
|
// No timeout — planning decisions need unlimited time
|
|
@@ -107,6 +129,14 @@ function getAskRedirectReason(): string | null {
|
|
|
107
129
|
*/
|
|
108
130
|
export function registerPlanningAskToolGuard(platform: Platform): void {
|
|
109
131
|
platform.on("tool_call", (event) => {
|
|
132
|
+
if (event.toolName === "exit_plan_mode" && isPlanningActive()) {
|
|
133
|
+
return {
|
|
134
|
+
block: true,
|
|
135
|
+
reason:
|
|
136
|
+
"Planning mode: /supi:plan uses a file-based approval hook. Do not call exit_plan_mode because it is OMP's native approval path and bypasses supipowers plan tracking.",
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
110
140
|
if (event.toolName !== "ask") return;
|
|
111
141
|
|
|
112
142
|
const reason = getAskRedirectReason();
|
|
@@ -233,7 +233,7 @@ function buildPlanningCriticalBlock(options: PlanningSystemPromptOptions): strin
|
|
|
233
233
|
"## Plan submission",
|
|
234
234
|
"",
|
|
235
235
|
"This is NOT native OMP plan mode.",
|
|
236
|
-
"You **MUST NOT** call `exit_plan_mode` or `ExitPlanMode` —
|
|
236
|
+
"You **MUST NOT** call `exit_plan_mode` or `ExitPlanMode` — that is OMP's native approval path and bypasses supipowers' file-based approval hook.",
|
|
237
237
|
`You **MUST NOT** write plans to \`local://PLAN.md\` — that is OMP's native plan location and will not trigger the approval flow.`,
|
|
238
238
|
`You **MUST** save the plan to \`${options.plansDir}/YYYY-MM-DD-<feature-name>.md\` using the Write tool.`,
|
|
239
239
|
"After saving, tell the user the plan path, then **stop and yield your turn**.",
|
|
@@ -4,17 +4,11 @@ import { getMetricsStore, getSessionId } from "../context-mode/hooks.js";
|
|
|
4
4
|
import { getProjectStateDir } from "../workspace/state-paths.js";
|
|
5
5
|
import type { Platform } from "../platform/types.js";
|
|
6
6
|
import { normalizeSystemPromptBlocks, systemPromptText } from "../platform/system-prompt.js";
|
|
7
|
-
import type { McpRegistry } from "../mcp/types.js";
|
|
8
7
|
import type { SupipowersConfig } from "../types.js";
|
|
9
8
|
import { planActiveTools } from "./active-tool-planner.js";
|
|
10
9
|
import { detectContextMode } from "../context-mode/detector.js";
|
|
11
10
|
import { getShadowedNativeTools } from "../context-mode/routing.js";
|
|
12
11
|
|
|
13
|
-
export interface ActiveToolControllerDeps {
|
|
14
|
-
loadMcpRegistryForCwd(cwd: string): McpRegistry;
|
|
15
|
-
consumePendingTags(): string[];
|
|
16
|
-
}
|
|
17
|
-
|
|
18
12
|
type BeforeAgentStartEventLike = {
|
|
19
13
|
prompt?: string;
|
|
20
14
|
systemPrompt?: string | string[];
|
|
@@ -33,7 +27,6 @@ type BeforeAgentStartContextLike = {
|
|
|
33
27
|
export function registerActiveToolController(
|
|
34
28
|
platform: Platform,
|
|
35
29
|
config: SupipowersConfig,
|
|
36
|
-
_deps: ActiveToolControllerDeps,
|
|
37
30
|
): void {
|
|
38
31
|
if (!config.contextMode.enabled || !config.contextMode.lazyTools.enabled) return;
|
|
39
32
|
|
|
@@ -46,19 +39,6 @@ export function registerActiveToolController(
|
|
|
46
39
|
if (typeof ctx.getSystemPrompt !== "function") return undefined;
|
|
47
40
|
|
|
48
41
|
const cwd = typeof ctx.cwd === "string" && ctx.cwd.length > 0 ? ctx.cwd : process.cwd();
|
|
49
|
-
let registry: McpRegistry = { schemaVersion: 1, servers: {} };
|
|
50
|
-
try {
|
|
51
|
-
registry = _deps.loadMcpRegistryForCwd(cwd);
|
|
52
|
-
} catch (error) {
|
|
53
|
-
(platform as any).logger?.warn?.("supi-lazy-tools: failed to load MCP registry", error);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
let pendingTags: string[] = [];
|
|
57
|
-
try {
|
|
58
|
-
pendingTags = _deps.consumePendingTags();
|
|
59
|
-
} catch (error) {
|
|
60
|
-
(platform as any).logger?.warn?.("supi-lazy-tools: failed to consume MCP tags", error);
|
|
61
|
-
}
|
|
62
42
|
|
|
63
43
|
let plan;
|
|
64
44
|
try {
|
|
@@ -67,8 +47,6 @@ export function registerActiveToolController(
|
|
|
67
47
|
currentActive: platform.getActiveTools(),
|
|
68
48
|
allTools: platform.getAllTools(),
|
|
69
49
|
lazyTools: config.contextMode.lazyTools,
|
|
70
|
-
mcpServers: registry.servers,
|
|
71
|
-
pendingTags,
|
|
72
50
|
cacheHandlesEnabled: config.contextMode.cacheHandles.enabled,
|
|
73
51
|
});
|
|
74
52
|
} catch (error) {
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import { computeActiveServers } from "../mcp/activation.js";
|
|
2
|
-
import type { ServerConfig } from "../mcp/types.js";
|
|
3
1
|
import type { ContextModeLazyToolsConfig } from "../types.js";
|
|
4
2
|
import {
|
|
5
3
|
BALANCED_KEYWORD_TOOLS,
|
|
6
4
|
CONTEXT_MODE_TOOL_NAMES,
|
|
7
|
-
MCPC_MANAGER_TOOL_NAME,
|
|
8
5
|
isSupiOwnedTool,
|
|
9
6
|
orderOwnedTools,
|
|
10
7
|
} from "./tool-groups.js";
|
|
@@ -12,8 +9,6 @@ import {
|
|
|
12
9
|
export interface ActiveToolPlannerDiagnostics {
|
|
13
10
|
unknownConfiguredTools: string[];
|
|
14
11
|
unavailableTools: string[];
|
|
15
|
-
unmatchedTags: string[];
|
|
16
|
-
missingMcpGatewayTools: string[];
|
|
17
12
|
}
|
|
18
13
|
|
|
19
14
|
export interface PlanActiveToolsInput {
|
|
@@ -21,8 +16,6 @@ export interface PlanActiveToolsInput {
|
|
|
21
16
|
currentActive: string[];
|
|
22
17
|
allTools: string[];
|
|
23
18
|
lazyTools: ContextModeLazyToolsConfig;
|
|
24
|
-
mcpServers?: Record<string, ServerConfig>;
|
|
25
|
-
pendingTags?: string[];
|
|
26
19
|
cacheHandlesEnabled?: boolean;
|
|
27
20
|
}
|
|
28
21
|
|
|
@@ -41,8 +34,6 @@ export function planActiveTools(input: PlanActiveToolsInput): ActiveToolPlan {
|
|
|
41
34
|
const diagnostics: ActiveToolPlannerDiagnostics = {
|
|
42
35
|
unknownConfiguredTools: [],
|
|
43
36
|
unavailableTools: [],
|
|
44
|
-
unmatchedTags: [],
|
|
45
|
-
missingMcpGatewayTools: [],
|
|
46
37
|
};
|
|
47
38
|
|
|
48
39
|
const addRegisteredTool = (toolName: string, source: "config" | "policy"): void => {
|
|
@@ -69,7 +60,6 @@ export function planActiveTools(input: PlanActiveToolsInput): ActiveToolPlan {
|
|
|
69
60
|
for (const toolName of CONTEXT_MODE_TOOL_NAMES) {
|
|
70
61
|
if (!RARE_CONTEXT_TOOLS.has(toolName)) addRegisteredTool(toolName, "policy");
|
|
71
62
|
}
|
|
72
|
-
addRegisteredTool(MCPC_MANAGER_TOOL_NAME, "policy");
|
|
73
63
|
}
|
|
74
64
|
|
|
75
65
|
for (const toolName of getTriggeredTools(input.prompt, BALANCED_KEYWORD_TOOLS)) {
|
|
@@ -81,20 +71,6 @@ export function planActiveTools(input: PlanActiveToolsInput): ActiveToolPlan {
|
|
|
81
71
|
}
|
|
82
72
|
|
|
83
73
|
|
|
84
|
-
const mcpServers = input.mcpServers ?? {};
|
|
85
|
-
const pendingTags = input.pendingTags ?? [];
|
|
86
|
-
const knownServerNames = new Set(Object.keys(mcpServers));
|
|
87
|
-
for (const tag of pendingTags) {
|
|
88
|
-
if (!knownServerNames.has(tag)) diagnostics.unmatchedTags.push(tag);
|
|
89
|
-
}
|
|
90
|
-
for (const serverName of computeActiveServers(mcpServers, input.prompt, pendingTags)) {
|
|
91
|
-
const gatewayToolName = `mcpc_${serverName}`;
|
|
92
|
-
if (registeredOwnedTools.has(gatewayToolName)) {
|
|
93
|
-
selectedOwnedTools.add(gatewayToolName);
|
|
94
|
-
} else {
|
|
95
|
-
diagnostics.missingMcpGatewayTools.push(gatewayToolName);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
74
|
for (const toolName of getCommandAllowlistTools(input.prompt, input.lazyTools.commandAllowlist)) {
|
|
99
75
|
addRegisteredTool(toolName, "config");
|
|
100
76
|
}
|
|
@@ -206,7 +182,5 @@ function dedupeDiagnostics(diagnostics: ActiveToolPlannerDiagnostics): ActiveToo
|
|
|
206
182
|
return {
|
|
207
183
|
unknownConfiguredTools: [...new Set(diagnostics.unknownConfiguredTools)],
|
|
208
184
|
unavailableTools: [...new Set(diagnostics.unavailableTools)],
|
|
209
|
-
unmatchedTags: [...new Set(diagnostics.unmatchedTags)],
|
|
210
|
-
missingMcpGatewayTools: [...new Set(diagnostics.missingMcpGatewayTools)],
|
|
211
185
|
};
|
|
212
186
|
}
|
|
@@ -14,9 +14,6 @@ export const CONTEXT_MODE_TOOL_NAMES = [
|
|
|
14
14
|
|
|
15
15
|
export type ContextModeToolName = (typeof CONTEXT_MODE_TOOL_NAMES)[number];
|
|
16
16
|
|
|
17
|
-
export const MCPC_MANAGER_TOOL_NAME = "mcpc_manager";
|
|
18
|
-
export const MCPC_TOOL_PREFIX = "mcpc_";
|
|
19
|
-
|
|
20
17
|
export const OWNED_TOOL_PRIORITY = [
|
|
21
18
|
"ctx_execute",
|
|
22
19
|
"ctx_search",
|
|
@@ -29,7 +26,6 @@ export const OWNED_TOOL_PRIORITY = [
|
|
|
29
26
|
"ctx_purge",
|
|
30
27
|
"ctx_repomap",
|
|
31
28
|
"ctx_symbol",
|
|
32
|
-
MCPC_MANAGER_TOOL_NAME,
|
|
33
29
|
] as const;
|
|
34
30
|
|
|
35
31
|
const CONTEXT_MODE_TOOL_SET = new Set<string>(CONTEXT_MODE_TOOL_NAMES);
|
|
@@ -81,12 +77,8 @@ export function isContextModeTool(name: string): boolean {
|
|
|
81
77
|
return CONTEXT_MODE_TOOL_SET.has(name);
|
|
82
78
|
}
|
|
83
79
|
|
|
84
|
-
export function isMcpcGatewayTool(name: string): boolean {
|
|
85
|
-
return name.startsWith(MCPC_TOOL_PREFIX) && /^mcpc_[^_].+/.test(name);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
80
|
export function isSupiOwnedTool(name: string): boolean {
|
|
89
|
-
return isContextModeTool(name)
|
|
81
|
+
return isContextModeTool(name);
|
|
90
82
|
}
|
|
91
83
|
|
|
92
84
|
export function orderOwnedTools(names: Iterable<string>): string[] {
|
package/src/types.ts
CHANGED
|
@@ -545,12 +545,6 @@ export interface ContextModeConfig {
|
|
|
545
545
|
memory: ContextModeMemoryConfig;
|
|
546
546
|
}
|
|
547
547
|
|
|
548
|
-
/** MCP management settings */
|
|
549
|
-
export interface McpManagementConfig {
|
|
550
|
-
/** Close mcpc sessions on agent shutdown (default: false) */
|
|
551
|
-
closeSessionsOnExit: boolean;
|
|
552
|
-
}
|
|
553
|
-
|
|
554
548
|
/** MemPalace native integration default wing derivation mode */
|
|
555
549
|
export type MempalaceWingStrategy = "repo-name" | "project-slug" | "explicit";
|
|
556
550
|
|
|
@@ -590,10 +584,19 @@ export interface MempalaceConfig {
|
|
|
590
584
|
* one-line refresher instead. `1` = always inject (legacy behavior).
|
|
591
585
|
*/
|
|
592
586
|
wakeUpInjectionEvery: number;
|
|
587
|
+
/** Minimum cosine similarity (0–1) for a hit to be injected by auto-search. Default 0.55. */
|
|
588
|
+
autoSearchSimilarityFloor: number;
|
|
589
|
+
/** Minimum BM25 score for a hit to be injected by auto-search. Default 0.3. */
|
|
590
|
+
autoSearchBm25Floor: number;
|
|
593
591
|
};
|
|
594
592
|
timeouts: {
|
|
595
593
|
setupMs: number;
|
|
596
594
|
bridgeMs: number;
|
|
595
|
+
/**
|
|
596
|
+
* Per-hook bridge timeout in milliseconds. Keep this at or above 6000
|
|
597
|
+
* when autoSearchOnPrompt is enabled; MemPalace 3.3.5 can sleep before
|
|
598
|
+
* retrying a transient search-index lookup.
|
|
599
|
+
*/
|
|
597
600
|
hookMs: number;
|
|
598
601
|
};
|
|
599
602
|
}
|
|
@@ -665,7 +668,6 @@ export interface SupipowersConfig {
|
|
|
665
668
|
};
|
|
666
669
|
ultraplan: UltraPlanConfig;
|
|
667
670
|
contextMode: ContextModeConfig;
|
|
668
|
-
mcp: McpManagementConfig;
|
|
669
671
|
mempalace: MempalaceConfig;
|
|
670
672
|
}
|
|
671
673
|
|
|
@@ -1542,6 +1544,7 @@ export type HarnessStage =
|
|
|
1542
1544
|
| "design"
|
|
1543
1545
|
| "plan"
|
|
1544
1546
|
| "implement"
|
|
1547
|
+
| "docs"
|
|
1545
1548
|
| "validate";
|
|
1546
1549
|
|
|
1547
1550
|
/** Operational status of a harness stage. Mirrors UltraPlanAuthoringStageStatus. */
|
|
@@ -1682,6 +1685,38 @@ export interface HarnessConfig {
|
|
|
1682
1685
|
backend?: HarnessAntiSlopBackend;
|
|
1683
1686
|
/** Threshold above which Implement defers to ultraplan batch. Default 10. */
|
|
1684
1687
|
implement_in_session_threshold?: number;
|
|
1688
|
+
/** Per-layer agent-docs stage config. Absent → defaults treated as "simple" tier. */
|
|
1689
|
+
docs?: HarnessDocsConfig;
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
/** Per-layer agent-docs configuration. */
|
|
1693
|
+
export interface HarnessDocsConfig {
|
|
1694
|
+
/**
|
|
1695
|
+
* Doc tier toggle. `simple` makes the `docs` stage a no-op (Tier 1 docs unchanged);
|
|
1696
|
+
* `extensive` fans out one subagent per layer to produce `docs/layers/<id>.md` and a
|
|
1697
|
+
* mechanical `docs/README.md` index.
|
|
1698
|
+
*/
|
|
1699
|
+
tier: "simple" | "extensive";
|
|
1700
|
+
/** Hard cap on total LOC per per-layer doc, including frontmatter. Default 150. */
|
|
1701
|
+
max_per_doc_loc: number;
|
|
1702
|
+
/** Hard cap on the `## Agent context` section LOC. Default 30. */
|
|
1703
|
+
agent_context_loc: number;
|
|
1704
|
+
/** Hard cap on `docs/README.md` LOC. Default 50. */
|
|
1705
|
+
max_index_loc: number;
|
|
1706
|
+
/** Defensive cap on the number of layers the stage will process. Default 12. */
|
|
1707
|
+
max_units: number;
|
|
1708
|
+
/**
|
|
1709
|
+
* Concurrency cap for subagent dispatch. `null` = unbounded (bounded only by `max_units`);
|
|
1710
|
+
* any positive integer caps `Promise.all` parallelism.
|
|
1711
|
+
*/
|
|
1712
|
+
max_concurrent_subagents: number | null;
|
|
1713
|
+
/** Validate-stage drift warning toggle. */
|
|
1714
|
+
drift_warning: { enabled: boolean };
|
|
1715
|
+
/**
|
|
1716
|
+
* Minimum stale-layer count before bare-entry Harden surfaces the pre-regen preview.
|
|
1717
|
+
* Default 1 (always show when any layer is stale).
|
|
1718
|
+
*/
|
|
1719
|
+
regen_preview_threshold: number;
|
|
1685
1720
|
}
|
|
1686
1721
|
|
|
1687
1722
|
/** Discover artifact (`<session>/discover.json`). */
|
|
@@ -1704,7 +1739,6 @@ export interface HarnessDiscoverArtifact {
|
|
|
1704
1739
|
hasSupipowers: boolean;
|
|
1705
1740
|
skills: string[];
|
|
1706
1741
|
reviewAgents: string[];
|
|
1707
|
-
mcpServers: string[];
|
|
1708
1742
|
plansCount: number;
|
|
1709
1743
|
};
|
|
1710
1744
|
/** Existing anti-slop tooling. */
|
|
@@ -1762,6 +1796,18 @@ export interface HarnessQualityGate {
|
|
|
1762
1796
|
failSafe: string;
|
|
1763
1797
|
}
|
|
1764
1798
|
|
|
1799
|
+
/** Configuration for the PR sticky comment posted by `/supi:harness pr-comment`. */
|
|
1800
|
+
export interface HarnessPrCommentConfig {
|
|
1801
|
+
/** When false, the workflow step is a no-op (still safe to call). */
|
|
1802
|
+
enabled: boolean;
|
|
1803
|
+
/**
|
|
1804
|
+
* Post cadence:
|
|
1805
|
+
* - "every-push": update the sticky comment on every CI run.
|
|
1806
|
+
* - "on-status-change": only re-post when status (passed/warned/failed) flips.
|
|
1807
|
+
*/
|
|
1808
|
+
mode: "every-push" | "on-status-change";
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1765
1811
|
/** CI and local counterpart wiring chosen during harness design. */
|
|
1766
1812
|
export interface HarnessCiConfig {
|
|
1767
1813
|
provider: "github-actions";
|
|
@@ -1772,6 +1818,14 @@ export interface HarnessCiConfig {
|
|
|
1772
1818
|
localCommand: string;
|
|
1773
1819
|
/** CI workflow path relative to repo root. */
|
|
1774
1820
|
workflowPath: string;
|
|
1821
|
+
/**
|
|
1822
|
+
* Optional PR comment behaviour. When absent the subcommand falls back to built-in
|
|
1823
|
+
* defaults (mode `every-push`); the explicit invocation of `/supi:harness pr-comment`
|
|
1824
|
+
* is treated as the user opt-in. Set `enabled: false` to suppress posting outside
|
|
1825
|
+
* `--dry-run`. The CI workflow permission warning in `ci-local-wiring` is gated on
|
|
1826
|
+
* an explicit truthy `enabled`, so legacy specs do not trip it.
|
|
1827
|
+
*/
|
|
1828
|
+
prComment?: HarnessPrCommentConfig;
|
|
1775
1829
|
}
|
|
1776
1830
|
|
|
1777
1831
|
|
|
@@ -1880,6 +1934,8 @@ export interface HarnessSession {
|
|
|
1880
1934
|
iteration: number;
|
|
1881
1935
|
/** Re-run mode user chose at bare entry (when applicable). */
|
|
1882
1936
|
reRunMode?: HarnessReRunMode;
|
|
1937
|
+
/** Per-layer agent-docs tier resolved at end-of-Design. Absent → treated as "simple". */
|
|
1938
|
+
docsTier?: "simple" | "extensive";
|
|
1883
1939
|
/** Recorded blocker, if any. */
|
|
1884
1940
|
blocker: { code: string; message: string; detectedAt: string } | null;
|
|
1885
1941
|
/** Artifacts produced so far (relative to <session>/). */
|
|
@@ -1895,6 +1951,29 @@ export interface HarnessArtifactRefs {
|
|
|
1895
1951
|
plan?: string;
|
|
1896
1952
|
implementLog?: string;
|
|
1897
1953
|
validateReport?: string;
|
|
1954
|
+
/** Per-layer agent docs (relative to <session>/docs/layers/<id>.md). */
|
|
1955
|
+
docs?: { layerId: string; path: string }[];
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
/**
|
|
1959
|
+
* Metadata describing a single per-layer agent knowledge document. Used by the docs stage
|
|
1960
|
+
* to track provenance, source-hash invalidation, and atomic promotion to the repo. The
|
|
1961
|
+
* canonical rendered doc lives at `docs/layers/<id>.md`; this record is part of the
|
|
1962
|
+
* stage's run result and the session staging artifacts.
|
|
1963
|
+
*/
|
|
1964
|
+
export interface HarnessDocsArtifact {
|
|
1965
|
+
/** Layer id this doc covers (matches HarnessLayerRule.layer). */
|
|
1966
|
+
layerId: string;
|
|
1967
|
+
/** Layer glob list, copied verbatim from the layer rule at render time. */
|
|
1968
|
+
layerGlobs: string[];
|
|
1969
|
+
/** Hash of every input that should invalidate the doc when changed. */
|
|
1970
|
+
sourceHash: string;
|
|
1971
|
+
/** Hash of the doc body after the provenance marker (excludes the marker itself). */
|
|
1972
|
+
contentHash: string;
|
|
1973
|
+
/** ISO timestamp the doc was generated. */
|
|
1974
|
+
generatedAt: string;
|
|
1975
|
+
/** Session that generated the doc. */
|
|
1976
|
+
sessionId: string;
|
|
1898
1977
|
}
|
|
1899
1978
|
|
|
1900
1979
|
/** Append-only pipeline log entry. */
|