la-machina-engine 0.18.0 → 0.19.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/index.cjs CHANGED
@@ -8064,196 +8064,6 @@ function extractDescription(content) {
8064
8064
  return headingText;
8065
8065
  }
8066
8066
 
8067
- // src/prompts/sections/base.ts
8068
- init_cjs_shims();
8069
- function getBaseSection() {
8070
- return `# System
8071
-
8072
- You are an AI assistant running inside la-machina-engine. You complete tasks by using the tools available to you. Your output goes to a programmatic caller (not a human terminal), so focus on correctness and completeness.
8073
-
8074
- IMPORTANT: Assist with authorized security testing, defensive security, CTF challenges, and educational contexts. Refuse requests for destructive techniques, DoS attacks, mass targeting, supply chain compromise, or detection evasion for malicious purposes.
8075
- IMPORTANT: You must NEVER generate or guess URLs for the user unless you are confident they are correct. You may use URLs provided in the task or discovered via tools.`;
8076
- }
8077
-
8078
- // src/prompts/sections/doingTasks.ts
8079
- init_cjs_shims();
8080
- function getDoingTasksSection() {
8081
- return `# Doing tasks
8082
-
8083
- - The caller will request you to perform tasks \u2014 solving bugs, adding features, refactoring, analyzing data, research, and more.
8084
- - You are highly capable and often allow callers to complete ambitious tasks that would otherwise be too complex or take too long. You should defer to the caller's judgement about whether a task is too large to attempt.
8085
- - If you notice the request is based on a misconception, or spot a bug adjacent to what was asked about, say so. You're a collaborator, not just an executor \u2014 callers benefit from your judgment, not just your compliance.
8086
- - In general, do not propose changes to code you haven't read. If asked about or to modify a file, read it first. Understand existing code before suggesting modifications.
8087
- - Do not create files unless they're absolutely necessary for achieving your goal. Generally prefer editing an existing file to creating a new one, as this prevents file bloat and builds on existing work more effectively.
8088
- - Avoid giving time estimates or predictions for how long tasks will take. Focus on what needs to be done, not how long it might take.
8089
- - If an approach fails, diagnose why before switching tactics \u2014 read the error, check your assumptions, try a focused fix. Don't retry the identical action blindly, but don't abandon a viable approach after a single failure either. Escalate only when you're genuinely stuck after investigation, not as a first response to friction.
8090
- - Be careful not to introduce security vulnerabilities such as command injection, XSS, SQL injection, and other OWASP top 10 vulnerabilities. If you notice that you wrote insecure code, immediately fix it. Prioritize writing safe, secure, and correct code.
8091
- - Don't add features, refactor code, or make "improvements" beyond what was asked. A bug fix doesn't need surrounding code cleaned up. A simple feature doesn't need extra configurability. Don't add docstrings, comments, or type annotations to code you didn't change. Only add comments where the logic isn't self-evident.
8092
- - Don't add error handling, fallbacks, or validation for scenarios that can't happen. Trust internal code and framework guarantees. Only validate at system boundaries (user input, external APIs). Don't use feature flags or backwards-compatibility shims when you can just change the code.
8093
- - Don't create helpers, utilities, or abstractions for one-time operations. Don't design for hypothetical future requirements. The right amount of complexity is what the task actually requires \u2014 no speculative abstractions, but no half-finished implementations either. Three similar lines of code is better than a premature abstraction.
8094
- - Default to writing no comments. Only add one when the WHY is non-obvious: a hidden constraint, a subtle invariant, a workaround for a specific bug, behavior that would surprise a reader. If removing the comment wouldn't confuse a future reader, don't write it.
8095
- - Don't explain WHAT the code does, since well-named identifiers already do that. Don't reference the current task, fix, or callers ("used by X", "added for the Y flow", "handles the case from issue #123"), since those belong in the commit message and rot as the codebase evolves.
8096
- - Don't remove existing comments unless you're removing the code they describe or you know they're wrong. A comment that looks pointless to you may encode a constraint or a lesson from a past bug that isn't visible in the current diff.
8097
- - Before reporting a task complete, verify it actually works: run the test, execute the script, check the output. Minimum complexity means no gold-plating, not skipping the finish line. If you can't verify (no test exists, can't run the code), say so explicitly rather than claiming success.
8098
- - Report outcomes faithfully: if tests fail, say so with the relevant output; if you did not run a verification step, say that rather than implying it succeeded. Never claim "all tests pass" when output shows failures, never suppress or simplify failing checks to manufacture a green result, and never characterize incomplete or broken work as done. Equally, when a check did pass or a task is complete, state it plainly \u2014 do not hedge confirmed results with unnecessary disclaimers, downgrade finished work to "partial," or re-verify things you already checked. The goal is an accurate report, not a defensive one.`;
8099
- }
8100
-
8101
- // src/prompts/sections/actions.ts
8102
- init_cjs_shims();
8103
- function getActionsSection() {
8104
- return `# Executing actions with care
8105
-
8106
- Carefully consider the reversibility and blast radius of actions. Generally you can freely take local, reversible actions like editing files or running tests. But for actions that are hard to reverse, affect shared systems beyond your local environment, or could otherwise be risky or destructive, check with the caller before proceeding. The cost of pausing to confirm is low, while the cost of an unwanted action (lost work, unintended messages sent, deleted branches) can be very high. For actions like these, consider the context, the action, and caller instructions, and by default transparently communicate the action and ask for confirmation before proceeding. This default can be changed by caller instructions \u2014 if explicitly asked to operate more autonomously, then you may proceed without confirmation, but still attend to the risks and consequences when taking actions.
8107
-
8108
- Examples of the kind of risky actions that warrant confirmation:
8109
- - Destructive operations: deleting files/branches, dropping database tables, killing processes, rm -rf, overwriting uncommitted changes
8110
- - Hard-to-reverse operations: force-pushing (can overwrite upstream), git reset --hard, amending published commits, removing or downgrading packages/dependencies, modifying CI/CD pipelines
8111
- - Actions visible to others or that affect shared state: pushing code, creating/closing/commenting on PRs or issues, sending messages (Slack, email, GitHub), posting to external services, modifying shared infrastructure or permissions
8112
- - Uploading content to third-party web tools (diagram renderers, pastebins, gists) publishes it \u2014 consider whether it could be sensitive before sending, since it may be cached or indexed even if later deleted.
8113
-
8114
- When you encounter an obstacle, do not use destructive actions as a shortcut to simply make it go away. For instance, try to identify root causes and fix underlying issues rather than bypassing safety checks (e.g. --no-verify). If you discover unexpected state like unfamiliar files, branches, or configuration, investigate before deleting or overwriting, as it may represent in-progress work. For example, typically resolve merge conflicts rather than discarding changes; similarly, if a lock file exists, investigate what process holds it rather than deleting it. In short: only take risky actions carefully, and when in doubt, ask before acting. Follow both the spirit and letter of these instructions \u2014 measure twice, cut once.`;
8115
- }
8116
-
8117
- // src/prompts/sections/usingTools.ts
8118
- init_cjs_shims();
8119
- function getUsingToolsSection(options) {
8120
- const has = (name) => options.registeredToolNames.has(name);
8121
- const items = [];
8122
- items.push(
8123
- `Do NOT use Bash to run commands when a relevant dedicated tool is provided. Using dedicated tools produces clearer, more reviewable output. This is CRITICAL:`
8124
- );
8125
- if (has("Read")) items.push(` - To read files use Read instead of cat, head, tail, or sed`);
8126
- if (has("Edit")) items.push(` - To edit files use Edit instead of sed or awk`);
8127
- if (has("Write"))
8128
- items.push(` - To create files use Write instead of cat with heredoc or echo redirection`);
8129
- if (has("Glob")) items.push(` - To search for files use Glob instead of find or ls`);
8130
- if (has("Grep")) items.push(` - To search the content of files, use Grep instead of grep or rg`);
8131
- items.push(
8132
- ` - Reserve using Bash exclusively for system commands and terminal operations that require shell execution. If you are unsure and there is a relevant dedicated tool, default to using the dedicated tool and only fallback on Bash if it is absolutely necessary.`
8133
- );
8134
- items.push(
8135
- `You can call multiple tools in a single response. If you intend to call multiple tools and there are no dependencies between them, make all independent tool calls in parallel. Maximize use of parallel tool calls where possible to increase efficiency. However, if some tool calls depend on previous calls to inform dependent values, do NOT call these tools in parallel and instead call them sequentially.`
8136
- );
8137
- if (has("Agent")) {
8138
- items.push(
8139
- `Use the Agent tool with specialized agents when the task at hand matches the agent's description. Subagents are valuable for parallelizing independent queries or for protecting the main context window from excessive results, but should not be used excessively when not needed. Importantly, avoid duplicating work that subagents are already doing \u2014 if you delegate research to a subagent, do not also perform the same searches yourself.`
8140
- );
8141
- if (has("Glob") || has("Grep")) {
8142
- items.push(
8143
- `For simple, directed codebase searches (e.g. for a specific file/class/function) use Glob or Grep directly.`
8144
- );
8145
- }
8146
- }
8147
- if (has("SkillPage")) {
8148
- items.push(
8149
- `Skills are surfaced in the system prompt. Use the SkillPage tool to load specific pages from multi-page skills when you need detailed instructions.`
8150
- );
8151
- }
8152
- return `# Using your tools
8153
-
8154
- ${items.map((i) => ` - ${i}`).join("\n")}`;
8155
- }
8156
-
8157
- // src/prompts/sections/toneAndStyle.ts
8158
- init_cjs_shims();
8159
- function getToneAndStyleSection() {
8160
- return `# Tone and style
8161
-
8162
- - Only use emojis if the caller explicitly requests it. Avoid using emojis in all output unless asked.
8163
- - Your responses should be concise and direct. Lead with the answer or action, not the reasoning.
8164
- - When referencing specific functions or pieces of code include the pattern file_path:line_number.
8165
- - When referencing GitHub issues or pull requests, use the owner/repo#123 format.
8166
- - Do not use a colon before tool calls. Your tool calls may not be shown directly in the output, so text like "Let me read the file:" followed by a read tool call should just be "Let me read the file." with a period.
8167
-
8168
- # Output efficiency
8169
-
8170
- Go straight to the point. Try the simplest approach first without going in circles. Do not overdo it. Be extra concise.
8171
-
8172
- Keep your text output brief and direct. Lead with the answer or action, not the reasoning. Skip filler words, preamble, and unnecessary transitions. Do not restate the task \u2014 just do it. When explaining, include only what is necessary.
8173
-
8174
- Focus text output on:
8175
- - Decisions that need input
8176
- - High-level status updates at natural milestones
8177
- - Errors or blockers that change the plan
8178
-
8179
- If you can say it in one sentence, don't use three. Prefer short, direct sentences over long explanations.`;
8180
- }
8181
-
8182
- // src/prompts/sections/environment.ts
8183
- init_cjs_shims();
8184
- async function getEnvironmentSection(options) {
8185
- let platform = "unknown";
8186
- let osRelease = "";
8187
- try {
8188
- const os = await import("os");
8189
- platform = os.platform();
8190
- osRelease = os.release();
8191
- } catch {
8192
- platform = typeof navigator !== "undefined" ? "worker" : "unknown";
8193
- }
8194
- const shell = (typeof process !== "undefined" ? process.env?.SHELL : void 0) ?? (platform === "win32" ? "cmd.exe" : "/bin/sh");
8195
- const osVersion = `${platform}${osRelease ? " " + osRelease : ""}`;
8196
- const cwd = options.cwd ?? (typeof process !== "undefined" && process.cwd ? process.cwd() : "/");
8197
- const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
8198
- const lines = [
8199
- "# Environment",
8200
- "",
8201
- `- Platform: ${platform}`,
8202
- `- Shell: ${shell}`,
8203
- `- OS Version: ${osVersion}`,
8204
- `- Working directory: ${cwd}`,
8205
- `- Model: ${options.modelId} (provider: ${options.provider})`,
8206
- `- Current date: ${date}`
8207
- ];
8208
- if (canSpawnProcesses()) {
8209
- const git = await getGitContext(cwd);
8210
- if (git) {
8211
- lines.push("");
8212
- lines.push("## Git");
8213
- if (git.branch) lines.push(`- Branch: ${git.branch}`);
8214
- if (git.isRepo) lines.push(`- Is git repo: true`);
8215
- if (git.status) lines.push(`- Status:
8216
- ${git.status}`);
8217
- if (git.recentCommits) lines.push(`- Recent commits:
8218
- ${git.recentCommits}`);
8219
- }
8220
- }
8221
- return lines.join("\n");
8222
- }
8223
- async function getGitContext(cwd) {
8224
- try {
8225
- const { execSync } = await import("child_process");
8226
- const opts = {
8227
- cwd,
8228
- stdio: ["ignore", "pipe", "ignore"],
8229
- timeout: 5e3
8230
- };
8231
- try {
8232
- execSync("git rev-parse --is-inside-work-tree", opts);
8233
- } catch {
8234
- return null;
8235
- }
8236
- const branch = tryExec(execSync, "git branch --show-current", opts);
8237
- const status = tryExec(execSync, "git status --short", opts, 2e3);
8238
- const recentCommits = tryExec(execSync, "git log --oneline -5", opts, 2e3);
8239
- const ctx = { isRepo: true };
8240
- if (branch) ctx.branch = branch;
8241
- if (status) ctx.status = status;
8242
- if (recentCommits) ctx.recentCommits = recentCommits;
8243
- return ctx;
8244
- } catch {
8245
- return null;
8246
- }
8247
- }
8248
- function tryExec(execSync, cmd, opts, maxChars = 1e3) {
8249
- try {
8250
- const out = execSync(cmd, opts).toString("utf-8").trim();
8251
- return out.length > maxChars ? out.slice(0, maxChars) + "\n...(truncated)" : out;
8252
- } catch {
8253
- return "";
8254
- }
8255
- }
8256
-
8257
8067
  // src/prompts/sections/mcp.ts
8258
8068
  init_cjs_shims();
8259
8069
  function getMcpSection(options) {
@@ -8339,14 +8149,21 @@ function getApiServicesSection(opts) {
8339
8149
  lines.push("");
8340
8150
  }
8341
8151
  } else {
8342
- lines.push(
8343
- "Configured external HTTP APIs. Use `ApiCall` to invoke, but first call `DescribeService(service)` to fetch that service's endpoint catalog. Auth is injected automatically."
8344
- );
8152
+ const hasDescribe = opts.hasDescribeService !== false;
8153
+ if (hasDescribe) {
8154
+ lines.push(
8155
+ "Configured external HTTP APIs. Use `ApiCall` to invoke, but first call `DescribeService(service)` to fetch that service's endpoint catalog. Auth is injected automatically."
8156
+ );
8157
+ } else {
8158
+ lines.push(
8159
+ "Configured external HTTP APIs. Use `ApiCall` to invoke. Auth is injected automatically."
8160
+ );
8161
+ }
8345
8162
  lines.push("");
8346
8163
  appendStrictApiToolRules(
8347
8164
  lines,
8348
8165
  /* hasLazy */
8349
- true
8166
+ hasDescribe
8350
8167
  );
8351
8168
  for (const svc of withEndpoints) {
8352
8169
  const count = svc.endpoints.length;
@@ -8401,33 +8218,20 @@ function resolveEffectiveMode(services, requested, threshold) {
8401
8218
  // src/prompts/systemPrompt.ts
8402
8219
  async function buildSystemPrompt(options) {
8403
8220
  const sections = [];
8221
+ const visibleToolNames = options.visibleToolNames ?? /* @__PURE__ */ new Set();
8404
8222
  if (options.coordinatorMode) {
8405
- } else if (options.staticBase !== void 0 && options.staticBase.length > 0) {
8223
+ } else if (options.staticBase !== void 0 && options.staticBase.trim().length > 0) {
8406
8224
  sections.push(options.staticBase);
8407
- } else {
8408
- sections.push(getBaseSection());
8409
- sections.push(getDoingTasksSection());
8410
- sections.push(getActionsSection());
8411
- if (options.registeredToolNames !== void 0 && options.registeredToolNames.size > 0) {
8412
- sections.push(getUsingToolsSection({ registeredToolNames: options.registeredToolNames }));
8413
- }
8414
- sections.push(getToneAndStyleSection());
8415
- }
8416
- sections.push(
8417
- await getEnvironmentSection({
8418
- modelId: options.modelId ?? "unknown",
8419
- provider: options.provider ?? "unknown",
8420
- cwd: options.cwd
8421
- })
8422
- );
8225
+ }
8423
8226
  if (options.mcpTools !== void 0 && options.mcpTools.length > 0) {
8424
8227
  const mcpSection = getMcpSection({ mcpTools: options.mcpTools });
8425
8228
  if (mcpSection !== null) sections.push(mcpSection);
8426
8229
  }
8427
- if (options.apiServices !== void 0 && options.apiServices.length > 0) {
8230
+ if (options.apiServices !== void 0 && options.apiServices.length > 0 && visibleToolNames.has("ApiCall")) {
8428
8231
  const apiSection = getApiServicesSection({
8429
8232
  services: options.apiServices,
8430
8233
  mode: options.apiCatalogMode ?? "lazy",
8234
+ hasDescribeService: visibleToolNames.has("DescribeService"),
8431
8235
  ...options.apiLazyTokenThreshold !== void 0 ? { lazyTokenThreshold: options.apiLazyTokenThreshold } : {}
8432
8236
  });
8433
8237
  if (apiSection !== null) sections.push(apiSection);
@@ -8448,7 +8252,7 @@ ${rules}`);
8448
8252
  ${lessons}`);
8449
8253
  }
8450
8254
  const effectiveSkillList = options.skillList !== void 0 ? options.skillList : options.skillsAutoload ? await collectSkills(options.storage, options.skillsDir ?? "skills") : void 0;
8451
- if (effectiveSkillList !== void 0 && effectiveSkillList.length > 0) {
8255
+ if (effectiveSkillList !== void 0 && effectiveSkillList.length > 0 && visibleToolNames.has("SkillPage")) {
8452
8256
  const lines = ["# Skills"];
8453
8257
  for (const skill of effectiveSkillList) {
8454
8258
  lines.push(`- ${skill.name}: ${skill.description}`);
@@ -10870,6 +10674,18 @@ function scrubRunOptions(opts) {
10870
10674
  if (opts.tools !== void 0) out.tools = [...opts.tools];
10871
10675
  if (opts.toolChoice !== void 0) out.toolChoice = opts.toolChoice;
10872
10676
  if (opts.tokenBudget !== void 0) out.tokenBudget = opts.tokenBudget;
10677
+ if (opts.systemPromptBase !== void 0 && opts.systemPromptBase.length > 0) {
10678
+ out.systemPromptBase = {
10679
+ present: true,
10680
+ chars: opts.systemPromptBase.length
10681
+ };
10682
+ }
10683
+ if (opts.systemPromptAppend !== void 0 && opts.systemPromptAppend.length > 0) {
10684
+ out.systemPromptAppend = {
10685
+ present: true,
10686
+ chars: opts.systemPromptAppend.length
10687
+ };
10688
+ }
10873
10689
  if (opts.knowledge !== void 0) {
10874
10690
  const k = {};
10875
10691
  if (opts.knowledge.folders !== void 0) k.folders = [...opts.knowledge.folders];
@@ -11393,60 +11209,36 @@ var Engine = class {
11393
11209
  const memory = createSmartMemory({ storage, config: this.config.memory });
11394
11210
  const agents = await this.resolveAgents(storage);
11395
11211
  const mcpTools = await this.mcpManager.getTools();
11396
- const toolNameSet = this.collectToolNames(mcpTools);
11397
11212
  const coordinatorBase = isCoordinatorMode(this.config) ? getCoordinatorBasePrompt() : void 0;
11398
11213
  const skillSource = this.resolveSkillSource(options.skills, storage);
11399
11214
  const skillList = skillSource !== void 0 ? await skillSource.list() : void 0;
11400
11215
  const apiConfig = this.resolveApiConfig(options.api);
11401
11216
  const offloadConfig = this.resolveOffloadConfig(options.compaction?.toolResultOffload);
11402
11217
  const knowledgeRuntime = this.resolveKnowledgeRuntime(options.knowledge, storage);
11403
- let systemPrompt = await buildSystemPrompt({
11404
- ...coordinatorBase !== void 0 ? { base: coordinatorBase } : {},
11405
- ...options.systemPromptBase !== void 0 ? { staticBase: options.systemPromptBase } : {},
11406
- ...options.systemPromptAppend !== void 0 && options.systemPromptAppend.length > 0 ? { platformAppend: options.systemPromptAppend } : {},
11407
- memory,
11408
- storage,
11409
- // When an override was supplied, skip the legacy disk-scan path.
11410
- skillsAutoload: options.skills !== void 0 ? false : this.config.skills.autoload,
11411
- ...this.config.skills.path !== void 0 ? { skillsDir: this.config.skills.path } : {},
11412
- ...skillList !== void 0 ? { skillList } : {},
11413
- modelId: this.config.model.modelId,
11414
- provider: this.config.model.provider,
11415
- registeredToolNames: toolNameSet,
11416
- mcpTools,
11417
- coordinatorMode: isCoordinatorMode(this.config),
11418
- // Plan 047 — render API services catalog (lazy by default).
11419
- ...apiConfig !== void 0 && apiConfig.services.length > 0 ? {
11420
- apiServices: apiConfig.services,
11421
- apiCatalogMode: apiConfig.mode ?? "lazy",
11422
- ...apiConfig.lazyTokenThreshold !== void 0 ? { apiLazyTokenThreshold: apiConfig.lazyTokenThreshold } : {}
11423
- } : {}
11424
- });
11425
- if (options.outputFormat === "json") {
11426
- systemPrompt += "\n\n" + buildSchemaPrompt(options.outputSchema);
11427
- }
11428
11218
  const gate = this.resolveGate();
11429
11219
  const inspect = this.buildInspectWriter(storage.workspace, logPath);
11430
- const registry = buildToolRegistry({
11431
- config: this.config,
11220
+ const { systemPrompt: assembledPrompt, registry } = await this.buildPromptAndRegistry({
11221
+ runOptions: options,
11222
+ coordinatorBase,
11432
11223
  storage,
11433
11224
  client,
11434
- parentLogPath: logPath,
11435
- parentAgentId: null,
11225
+ logPath,
11436
11226
  subagentRegistry,
11437
- system: systemPrompt,
11438
11227
  agents,
11439
11228
  mcpTools,
11440
11229
  memory,
11441
11230
  inspect,
11442
- ...this.config.hooks.propagateGateToSubagents === true && gate !== void 0 ? { subagentGate: gate } : {},
11443
- ...skillSource !== void 0 ? { skillSource } : {},
11444
- ...apiConfig !== void 0 ? { apiConfig } : {},
11445
- ...offloadConfig !== void 0 ? { toolResultOffload: offloadConfig } : {},
11446
- ...knowledgeRuntime !== void 0 ? { knowledge: knowledgeRuntime } : {},
11447
- ...this.internals.fetch !== void 0 ? { fetch: this.internals.fetch } : {}
11231
+ gate,
11232
+ skillSource,
11233
+ skillList,
11234
+ apiConfig,
11235
+ offloadConfig,
11236
+ knowledgeRuntime
11448
11237
  });
11449
- applyRunToolFilter(registry, options);
11238
+ let systemPrompt = assembledPrompt;
11239
+ if (options.outputFormat === "json") {
11240
+ systemPrompt += "\n\n" + buildSchemaPrompt(options.outputSchema);
11241
+ }
11450
11242
  await inspect.writeStartSnapshot({
11451
11243
  systemPrompt,
11452
11244
  tools: this.snapshotTools(registry, mcpTools),
@@ -11615,68 +11407,55 @@ var Engine = class {
11615
11407
  const memory = createSmartMemory({ storage, config: this.config.memory });
11616
11408
  const agents = await this.resolveAgents(storage);
11617
11409
  const mcpTools = await this.mcpManager.getTools();
11618
- const toolNameSet = this.collectToolNames(mcpTools);
11619
11410
  const coordinatorBase = isCoordinatorMode(this.config) ? getCoordinatorBasePrompt() : void 0;
11620
11411
  const skillSource = this.resolveSkillSource(options.skills, storage);
11621
11412
  const skillList = skillSource !== void 0 ? await skillSource.list() : void 0;
11622
11413
  const apiConfig = this.resolveApiConfig(options.api);
11623
11414
  const offloadConfig = this.resolveOffloadConfig(options.compaction?.toolResultOffload);
11624
11415
  const knowledgeRuntime = this.resolveKnowledgeRuntime(options.knowledge, storage);
11625
- let systemPrompt = await buildSystemPrompt({
11626
- ...coordinatorBase !== void 0 ? { base: coordinatorBase } : {},
11627
- ...options.systemPromptBase !== void 0 ? { staticBase: options.systemPromptBase } : {},
11628
- ...options.systemPromptAppend !== void 0 && options.systemPromptAppend.length > 0 ? { platformAppend: options.systemPromptAppend } : {},
11629
- memory,
11630
- storage,
11631
- // When an override was supplied, skip the legacy disk-scan path.
11632
- skillsAutoload: options.skills !== void 0 ? false : this.config.skills.autoload,
11633
- ...this.config.skills.path !== void 0 ? { skillsDir: this.config.skills.path } : {},
11634
- ...skillList !== void 0 ? { skillList } : {},
11635
- modelId: this.config.model.modelId,
11636
- provider: this.config.model.provider,
11637
- registeredToolNames: toolNameSet,
11638
- mcpTools,
11639
- coordinatorMode: isCoordinatorMode(this.config),
11640
- // Plan 047 — render API services catalog (lazy by default).
11641
- ...apiConfig !== void 0 && apiConfig.services.length > 0 ? {
11642
- apiServices: apiConfig.services,
11643
- apiCatalogMode: apiConfig.mode ?? "lazy",
11644
- ...apiConfig.lazyTokenThreshold !== void 0 ? { apiLazyTokenThreshold: apiConfig.lazyTokenThreshold } : {}
11645
- } : {}
11646
- });
11647
- if (options.outputFormat === "json") {
11648
- systemPrompt += "\n\n" + buildSchemaPrompt(options.outputSchema);
11649
- }
11650
11416
  const gate = this.resolveGate();
11651
11417
  const inspect = this.buildInspectWriter(storage.workspace, logPath);
11652
- const registry = buildToolRegistry({
11653
- config: this.config,
11418
+ const { systemPrompt: assembledPrompt, registry } = await this.buildPromptAndRegistry({
11419
+ runOptions: options,
11420
+ coordinatorBase,
11654
11421
  storage,
11655
11422
  client,
11656
- parentLogPath: logPath,
11657
- parentAgentId: null,
11423
+ logPath,
11658
11424
  subagentRegistry,
11659
- system: systemPrompt,
11660
11425
  agents,
11661
11426
  mcpTools,
11662
11427
  memory,
11663
11428
  inspect,
11664
- ...this.config.hooks.propagateGateToSubagents === true && gate !== void 0 ? { subagentGate: gate } : {},
11665
- ...skillSource !== void 0 ? { skillSource } : {},
11666
- ...apiConfig !== void 0 ? { apiConfig } : {},
11667
- ...offloadConfig !== void 0 ? { toolResultOffload: offloadConfig } : {},
11668
- ...knowledgeRuntime !== void 0 ? { knowledge: knowledgeRuntime } : {},
11669
- ...this.internals.fetch !== void 0 ? { fetch: this.internals.fetch } : {}
11429
+ gate,
11430
+ skillSource,
11431
+ skillList,
11432
+ apiConfig,
11433
+ offloadConfig,
11434
+ knowledgeRuntime
11670
11435
  });
11436
+ let systemPrompt = assembledPrompt;
11437
+ if (options.outputFormat === "json") {
11438
+ systemPrompt += "\n\n" + buildSchemaPrompt(options.outputSchema);
11439
+ }
11671
11440
  await inspect.writeStartSnapshot({
11672
11441
  systemPrompt,
11673
11442
  tools: this.snapshotTools(registry, mcpTools),
11443
+ // Plan 052 review fix — surface the resume-time effective
11444
+ // policy in `run-options.json` so post-hoc debuggers see
11445
+ // exactly what gating drove the resume's prompt + tool
11446
+ // surface. Otherwise resume's inspect bundle looked like a
11447
+ // fresh run with no restrictions, masking the silent-widening
11448
+ // failure mode documented on `ResumeOptions.tools`.
11674
11449
  runOptions: scrubRunOptions({
11675
11450
  runId: snapshot.runId,
11676
11451
  nodeId: snapshot.nodeId,
11677
11452
  task: "[resumed run \u2014 original task in transcript]",
11678
11453
  ...options.outputFormat !== void 0 ? { outputFormat: options.outputFormat } : {},
11679
- ...options.outputSchema !== void 0 ? { outputSchema: options.outputSchema } : {}
11454
+ ...options.outputSchema !== void 0 ? { outputSchema: options.outputSchema } : {},
11455
+ ...options.tools !== void 0 ? { tools: options.tools } : {},
11456
+ ...options.toolChoice !== void 0 ? { toolChoice: options.toolChoice } : {},
11457
+ ...options.systemPromptBase !== void 0 ? { systemPromptBase: options.systemPromptBase } : {},
11458
+ ...options.systemPromptAppend !== void 0 ? { systemPromptAppend: options.systemPromptAppend } : {}
11680
11459
  }),
11681
11460
  modelConfig: scrubModelConfig(
11682
11461
  {
@@ -12325,72 +12104,97 @@ ${inputJson}
12325
12104
  });
12326
12105
  }
12327
12106
  /**
12328
- * Collect the names of all tools that will be registered — used to
12329
- * populate the system prompt's tool-specific instructions BEFORE
12330
- * building the registry itself (the prompt goes into the registry's
12331
- * Agent tool as the child system prompt, so the prompt must exist
12332
- * first).
12107
+ * Plan 052 two-pass system-prompt + tool-registry build.
12108
+ *
12109
+ * The prompt's API / skill / MCP sections describe only the tools
12110
+ * the model will actually see, so we must know the post-filter
12111
+ * tool set before assembling the prompt. The registry, however,
12112
+ * is what produces that set, AND its `Agent` tool needs the
12113
+ * final system prompt baked in for subagent dispatch.
12114
+ *
12115
+ * The chicken-and-egg is resolved by building twice:
12116
+ *
12117
+ * Pass 1 — placeholder prompt → registry → applyRunToolFilter
12118
+ * → snapshot of visible tool names + visible MCP tools.
12119
+ * Pass 2 — final prompt assembled from those visible surfaces
12120
+ * → rebuild registry → applyRunToolFilter again.
12121
+ *
12122
+ * `applyRunToolFilter` is deterministic given options, so the
12123
+ * second filter pass is identical to the first; the second
12124
+ * registry is the authoritative one returned to the caller.
12125
+ *
12126
+ * Pass 1's prompt is intentionally minimal (placeholder string)
12127
+ * because the only thing we use the first registry for is its
12128
+ * post-filter tool list. The transient prompt never reaches the
12129
+ * model.
12333
12130
  */
12334
- collectToolNames(mcpTools) {
12335
- const names = /* @__PURE__ */ new Set();
12336
- const builtins = [
12337
- // Bash is no longer registered by the engine (Plan 020) — but
12338
- // we keep it in the prompt-generation list so any caller that
12339
- // adds it via `tools.custom` (or its capabilityStub equivalent)
12340
- // gets a consistent prompt mention.
12341
- "Bash",
12342
- "Read",
12343
- "Write",
12344
- "Edit",
12345
- "Glob",
12346
- "Grep",
12347
- "WebFetch",
12348
- "WebSearch",
12349
- "Agent",
12350
- "SkillPage",
12351
- "Sleep",
12352
- "NotebookEdit",
12353
- "TaskCreate",
12354
- "TaskGet",
12355
- "TaskList",
12356
- "TaskUpdate",
12357
- "Memorize",
12358
- "Recall",
12359
- "ToolSearch"
12360
- ];
12361
- const enabled = new Set(this.config.tools.enabled);
12362
- const disabled = new Set(this.config.tools.disabled);
12363
- const wantAll = enabled.has("*");
12364
- for (const name of builtins) {
12365
- if (disabled.has(name)) continue;
12366
- if (wantAll || enabled.has(name)) names.add(name);
12367
- }
12368
- if ((this.config.api?.services.length ?? 0) > 0) {
12369
- if (!disabled.has("ApiCall") && (wantAll || enabled.has("ApiCall"))) {
12370
- names.add("ApiCall");
12371
- }
12372
- }
12373
- if (this.config.compaction.toolResultOffload?.enabled === true) {
12374
- if (!disabled.has("FetchData") && (wantAll || enabled.has("FetchData"))) {
12375
- names.add("FetchData");
12376
- }
12377
- }
12378
- if (this.config.knowledge?.enabled === true) {
12379
- if (!disabled.has("SearchKnowledge") && (wantAll || enabled.has("SearchKnowledge"))) {
12380
- names.add("SearchKnowledge");
12381
- }
12382
- if (!disabled.has("ReadKnowledge") && (wantAll || enabled.has("ReadKnowledge"))) {
12383
- names.add("ReadKnowledge");
12384
- }
12385
- }
12386
- for (const tool of this.config.tools.custom) {
12387
- names.add(tool.name);
12388
- }
12389
- for (const tool of mcpTools) {
12390
- if (disabled.has(tool.name)) continue;
12391
- if (wantAll || enabled.has(tool.name)) names.add(tool.name);
12392
- }
12393
- return names;
12131
+ async buildPromptAndRegistry(args) {
12132
+ const {
12133
+ runOptions,
12134
+ coordinatorBase,
12135
+ storage,
12136
+ client,
12137
+ logPath,
12138
+ subagentRegistry,
12139
+ agents,
12140
+ mcpTools,
12141
+ memory,
12142
+ inspect,
12143
+ gate,
12144
+ skillSource,
12145
+ skillList,
12146
+ apiConfig,
12147
+ offloadConfig,
12148
+ knowledgeRuntime
12149
+ } = args;
12150
+ const baseRegistryArgs = {
12151
+ config: this.config,
12152
+ storage,
12153
+ client,
12154
+ parentLogPath: logPath,
12155
+ parentAgentId: null,
12156
+ subagentRegistry,
12157
+ agents,
12158
+ mcpTools,
12159
+ memory,
12160
+ inspect,
12161
+ ...this.config.hooks.propagateGateToSubagents === true && gate !== void 0 ? { subagentGate: gate } : {},
12162
+ ...skillSource !== void 0 ? { skillSource } : {},
12163
+ ...apiConfig !== void 0 ? { apiConfig } : {},
12164
+ ...offloadConfig !== void 0 ? { toolResultOffload: offloadConfig } : {},
12165
+ ...knowledgeRuntime !== void 0 ? { knowledge: knowledgeRuntime } : {},
12166
+ ...this.internals.fetch !== void 0 ? { fetch: this.internals.fetch } : {}
12167
+ };
12168
+ const tempRegistry = buildToolRegistry({ ...baseRegistryArgs, system: "" });
12169
+ applyRunToolFilter(tempRegistry, runOptions);
12170
+ const visibleToolNames = new Set(tempRegistry.list().map((t) => t.name));
12171
+ const visibleMcpTools = mcpTools.filter((t) => visibleToolNames.has(t.name));
12172
+ const systemPrompt = await buildSystemPrompt({
12173
+ ...coordinatorBase !== void 0 ? { base: coordinatorBase } : {},
12174
+ ...runOptions.systemPromptBase !== void 0 ? { staticBase: runOptions.systemPromptBase } : {},
12175
+ ...runOptions.systemPromptAppend !== void 0 && runOptions.systemPromptAppend.length > 0 ? { platformAppend: runOptions.systemPromptAppend } : {},
12176
+ memory,
12177
+ storage,
12178
+ // When an override was supplied, skip the legacy disk-scan path.
12179
+ skillsAutoload: skillSource !== void 0 ? false : this.config.skills.autoload,
12180
+ ...this.config.skills.path !== void 0 ? { skillsDir: this.config.skills.path } : {},
12181
+ ...skillList !== void 0 ? { skillList } : {},
12182
+ // Plan 052 — pass the FINAL post-filter visible tool surface
12183
+ // so prompt sections gate correctly.
12184
+ visibleToolNames,
12185
+ mcpTools: visibleMcpTools,
12186
+ coordinatorMode: isCoordinatorMode(this.config),
12187
+ // Plan 047 — render API services catalog (lazy by default).
12188
+ // Gating on `ApiCall` visibility lives inside buildSystemPrompt.
12189
+ ...apiConfig !== void 0 && apiConfig.services.length > 0 ? {
12190
+ apiServices: apiConfig.services,
12191
+ apiCatalogMode: apiConfig.mode ?? "lazy",
12192
+ ...apiConfig.lazyTokenThreshold !== void 0 ? { apiLazyTokenThreshold: apiConfig.lazyTokenThreshold } : {}
12193
+ } : {}
12194
+ });
12195
+ const registry = buildToolRegistry({ ...baseRegistryArgs, system: systemPrompt });
12196
+ applyRunToolFilter(registry, runOptions);
12197
+ return { systemPrompt, registry };
12394
12198
  }
12395
12199
  /**
12396
12200
  * Resolve the subagent catalogue the Agent tool will dispatch against.