blun-king-cli 9.1.527 → 9.1.536

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.
Files changed (43) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/LIESMICH.txt +36 -1
  3. package/README.md +35 -1
  4. package/bin/agent-resume-snapshot.cjs +31 -0
  5. package/bin/assistant-message-offload-policy.cjs +21 -2
  6. package/bin/codebase-search-runtime.cjs +23 -0
  7. package/bin/empty-response-retry-policy.cjs +29 -0
  8. package/bin/fredrik-glm-provider.cjs +256 -0
  9. package/bin/history-offload-pressure-policy.cjs +33 -0
  10. package/bin/programmatic-context-isolation.cjs +25 -0
  11. package/bin/programmatic-tool-runtime.mjs +301 -0
  12. package/bin/skill-activation-performance-policy.cjs +9 -0
  13. package/bin/structured-subagent-output.cjs +252 -0
  14. package/bin/telegram-direct-focus-policy.cjs +25 -1
  15. package/bin/todo-list-turn-policy.cjs +111 -1
  16. package/bin/tool-result-offload-policy.cjs +29 -0
  17. package/bin/turn-thinking-policy.cjs +6 -15
  18. package/bin/turn-tool-performance-policy.cjs +5 -4
  19. package/bin/user-message-offload-policy.cjs +10 -1
  20. package/blun.mjs +656 -127
  21. package/codebase-index/README.md +70 -0
  22. package/codebase-index/codebase_index.py +358 -0
  23. package/fredrik-glm-profile.toml.example +26 -0
  24. package/package.json +24 -3
  25. package/scripts/check-active-work-steer-regression.js +46 -0
  26. package/scripts/check-codebase-search-packaging-regression.js +92 -0
  27. package/scripts/check-current-turn-read-pin-mutation-regression.js +72 -0
  28. package/scripts/check-current-turn-read-pin-regression.js +94 -0
  29. package/scripts/check-deepseek-native-max-regression.js +49 -0
  30. package/scripts/check-empty-response-effort-downgrade-regression.js +48 -0
  31. package/scripts/check-fredrik-glm-mutation-regression.js +18 -0
  32. package/scripts/check-fredrik-glm-regression.js +169 -0
  33. package/scripts/check-history-pressure-offload-regression.js +77 -0
  34. package/scripts/check-programmatic-context-isolation-regression.js +193 -0
  35. package/scripts/check-programmatic-tool-regression.js +294 -0
  36. package/scripts/check-resume-replay-regression.js +2 -0
  37. package/scripts/check-startup-swarm-command-regression.js +24 -0
  38. package/scripts/check-structured-subagent-output-regression.js +331 -0
  39. package/scripts/check-telegram-direct-work-resume-regression.js +53 -0
  40. package/scripts/check-todo-progress-regression.js +416 -0
  41. package/scripts/check-tool-schema-capacity-regression.js +40 -0
  42. package/scripts/programmatic-tool-runtime.test.mjs +365 -0
  43. package/scripts/structured-subagent-output.test.cjs +170 -0
package/blun.mjs CHANGED
@@ -16,6 +16,7 @@ import sessionScrollbackArchive from "./bin/session-scrollback-archive.cjs";
16
16
  import sessionReplayWindowPolicy from "./bin/session-replay-window-policy.cjs";
17
17
  import globPatternPolicy from "./bin/glob-pattern-policy.cjs";
18
18
  import userHomePathPolicy from "./bin/user-home-path-policy.cjs";
19
+ import fredrikGlmProviderPolicy from "./bin/fredrik-glm-provider.cjs";
19
20
  import crypto$1, { createHash, randomBytes, randomInt, randomUUID, timingSafeEqual } from "node:crypto";
20
21
  import * as fs$16 from "node:fs";
21
22
  import Kt, { accessSync, appendFileSync, chmodSync, closeSync, constants, copyFileSync, createReadStream, createWriteStream, existsSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, promises, readFileSync, readSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
@@ -56,6 +57,18 @@ const { SessionScrollbackArchive, sessionScrollbackArchiveDirectory } = sessionS
56
57
  const { replayWindowStartIndex, resolveReplayActiveTurnCount } = sessionReplayWindowPolicy;
57
58
  const { globPatternError } = globPatternPolicy;
58
59
  const { unsupportedUserHomePathError } = userHomePathPolicy;
60
+ const {
61
+ PROVIDER_ID: FREDRIK_GLM_PROVIDER_ID,
62
+ PROVIDER_TYPE: FREDRIK_GLM_PROVIDER_TYPE,
63
+ MODEL_EFFORTS: FREDRIK_GLM_EFFORTS,
64
+ configuredModels: configuredFredrikGlmModels,
65
+ configuredProvider: configuredFredrikGlmProvider,
66
+ isAllowedRuntimeAlias: isAllowedFredrikRuntimeAlias,
67
+ isConfiguredAlias: isConfiguredFredrikGlmAlias,
68
+ normalizeEffort: normalizeFredrikGlmEffort,
69
+ requestControls: fredrikGlmRequestControls,
70
+ readApiKeyFile: readFredrikGlmApiKeyFile
71
+ } = fredrikGlmProviderPolicy;
59
72
  import { EventEmitter as EventEmitter$1 } from "node:events";
60
73
  import { StringDecoder } from "node:string_decoder";
61
74
  import co from "node:assert";
@@ -2720,6 +2733,7 @@ var init_blun = __esmMin((() => {
2720
2733
  _defaultHeaders;
2721
2734
  _generationKwargs;
2722
2735
  _supportEfforts;
2736
+ _openAICompatible;
2723
2737
  _client;
2724
2738
  _clientFactory;
2725
2739
  _files;
@@ -2733,6 +2747,7 @@ var init_blun = __esmMin((() => {
2733
2747
  this._stream = options.stream ?? true;
2734
2748
  this._generationKwargs = { ...options.generationKwargs };
2735
2749
  this._supportEfforts = options.supportEfforts ?? [];
2750
+ this._openAICompatible = options.openAICompatible === true;
2736
2751
  this._client = this._apiKey === void 0 ? void 0 : createFetchHttpClient({
2737
2752
  apiKey: this._apiKey,
2738
2753
  baseUrl: this._baseUrl,
@@ -2844,8 +2859,17 @@ var init_blun = __esmMin((() => {
2844
2859
  delete requestExtraBody["model"];
2845
2860
  delete requestExtraBody["messages"];
2846
2861
  if (!thinkingDisabled && thinking?.keep !== void 0) requestExtraBody["thinking"] = { keep: thinking.keep };
2847
- const effectiveTemplateKwargs = requestKwargs["chat_template_kwargs"];
2848
- const reasoningRequested = typeof effectiveTemplateKwargs === "object" && effectiveTemplateKwargs !== null && effectiveTemplateKwargs.thinking === true;
2862
+ let reasoningRequested;
2863
+ if (this._openAICompatible) {
2864
+ delete requestKwargs["chat_template_kwargs"];
2865
+ const controls = fredrikGlmRequestControls(effectiveThinkingEffort ?? "max");
2866
+ requestExtraBody["thinking"] = controls.thinking;
2867
+ requestExtraBody["reasoning_effort"] = controls.reasoning_effort;
2868
+ reasoningRequested = true;
2869
+ } else {
2870
+ const effectiveTemplateKwargs = requestKwargs["chat_template_kwargs"];
2871
+ reasoningRequested = typeof effectiveTemplateKwargs === "object" && effectiveTemplateKwargs !== null && effectiveTemplateKwargs.thinking === true;
2872
+ }
2849
2873
  const createParams = {
2850
2874
  model: this._model,
2851
2875
  messages,
@@ -9989,7 +10013,7 @@ var init_schema = __esmMin((() => {
9989
10013
  init_matches_rule();
9990
10014
  init_errors$8();
9991
10015
  init_zod$1();
9992
- ProviderTypeSchema = literal("blun");
10016
+ ProviderTypeSchema = union([literal("blun"), literal("openai_compatible")]);
9993
10017
  OAuthRefSchema = object({
9994
10018
  storage: _enum(["file", "keyring"]),
9995
10019
  key: string().min(1),
@@ -9999,6 +10023,7 @@ var init_schema = __esmMin((() => {
9999
10023
  ProviderConfigSchema = object({
10000
10024
  type: ProviderTypeSchema,
10001
10025
  apiKey: string().optional(),
10026
+ apiKeyFile: string().optional(),
10002
10027
  baseUrl: string().optional(),
10003
10028
  defaultModel: string().optional(),
10004
10029
  oauth: OAuthRefSchema.optional(),
@@ -28546,6 +28571,7 @@ var init_agent$3 = __esmMin((() => {
28546
28571
  agent_default$1 = "name: agent\ndescription: Default BLUN King agent\n\nsystemPromptPath: ./system.md\npromptVars:\n roleAdditional: ''\n\ntools:\n - Read\n - Write\n - Edit\n - Grep\n - Glob\n - Bash\n - TaskList\n - TaskOutput\n - TaskUpdate\n - TaskStop\n - CronCreate\n - CronList\n - CronDelete\n - ReadMediaFile\n - TodoList\n - Skill\n - WebSearch\n - Agent\n - AgentSwarm\n - FetchURL\n - GenerateImage\n - GenerateVideo\n - GenerateSpeech\n - UnderstandImage\n - UnderstandVideo\n - DubVideo\n - LipSyncMedia\n - GetMedia\n - AskUserQuestion\n - MistakeRecord\n - CodebaseSearch\n - EnterPlanMode\n - ExitPlanMode\n - CreateGoal\n - GetGoal\n - SetGoalBudget\n - UpdateGoal\n - mcp__*\n\nsubagents:\n coder:\n description: General software engineering agent — the only subagent type with file-editing tools; use it for any delegated task that must modify code.\n explore:\n description: Fast codebase exploration with prompt-enforced read-only behavior.\n plan:\n description: Read-only implementation planning and architecture design.\n";
28547
28572
  agent_default$1 = agent_default$1.replace(" - Read\n", " - Read\n - ReadBatch\n");
28548
28573
  agent_default$1 = agent_default$1.replace(" - TodoList\n", " - TodoList\n - CompactConversation\n");
28574
+ agent_default$1 = agent_default$1.replace(" - CodebaseSearch\n", " - CodebaseSearch\n - ProgrammaticTool\n");
28549
28575
  }));
28550
28576
  //#endregion
28551
28577
  //#region ../../packages/agent-core/src/profile/default/coder.yaml?raw
@@ -28553,6 +28579,7 @@ var coder_default;
28553
28579
  var init_coder = __esmMin((() => {
28554
28580
  coder_default = "extends: agent\nname: coder\npromptVars:\n roleAdditional: |\n You are now running as a subagent. All the `user` messages are sent by the main agent. The main agent cannot see your context, it can only see your last message when you finish the task. You must treat the parent agent as your caller. Do not directly ask the end user questions. If something is unclear, explain the ambiguity in your final summary to the parent agent.\n\n Your final message is the entire handoff — the parent sees nothing else from your run. Make it technically complete: what you changed and why, the path of every file you touched, how you verified the change (tests or commands run, with results), and anything left undone or worth follow-up. A final message of only a sentence or two is treated as too brief and sent back to you for expansion, costing an extra turn.\nwhenToUse: |\n Use this agent for non-trivial software engineering work that may require reading files, editing code, running commands, and returning a compact but technically complete summary to the parent agent.\ntools:\n - Bash\n - Read\n - ReadMediaFile\n - Glob\n - Grep\n - Write\n - Edit\n - MistakeRecord\n - WebSearch\n - FetchURL\n - mcp__*\n";
28555
28581
  coder_default = coder_default.replace(" - Read\n", " - Read\n - ReadBatch\n");
28582
+ coder_default = coder_default.replace(" - ReadBatch\n", " - ReadBatch\n - ProgrammaticTool\n");
28556
28583
  }));
28557
28584
  //#endregion
28558
28585
  //#region ../../packages/agent-core/src/profile/default/explore.yaml?raw
@@ -28560,6 +28587,7 @@ var explore_default;
28560
28587
  var init_explore = __esmMin((() => {
28561
28588
  explore_default = "extends: agent\nname: explore\npromptVars:\n roleAdditional: |\n You are now running as a subagent. All the `user` messages are sent by the main agent. The main agent cannot see your context, it can only see your last message when you finish the task. You must treat the parent agent as your caller. Do not directly ask the end user questions. If something is unclear, explain the ambiguity in your final summary to the parent agent.\n\n You are a codebase exploration specialist. Your role is EXCLUSIVELY to search, read, and analyze existing code and resources. You do NOT have access to project file editing tools. MistakeRecord is the sole write-capable exception: it may record a concrete refutation in shared BLUN learning memory, never modify project files.\n\n Your strengths:\n - Rapidly finding files using glob patterns\n - Searching code and text with powerful regex patterns\n - Reading and analyzing file contents\n - Running read-only shell commands (git log, git diff, ls, find, etc.)\n\n Guidelines:\n - Use Glob for broad file pattern matching. Prefer patterns with a literal anchor (extension or subdirectory); pure wildcards like `*` or `**/*` are allowed but usually truncate at the match cap.\n - Use Grep for searching file contents with regex\n - Use Read when you know the specific file path\n - Use Bash ONLY for read-only operations (ls, git status, git log, git diff, find)\n - NEVER use Bash for any file creation or modification commands\n - Use WebSearch or FetchURL when a question needs external context (library documentation, error messages, upstream APIs); the local codebase remains your primary domain\n - Adapt your search depth based on the thoroughness level specified by the caller\n - Wherever possible, spawn multiple parallel tool calls for grepping and reading files to maximize speed\n\n If the prompt includes a <git-context> block, use it to orient yourself about the repository state before starting your investigation.\n\n You are meant to be a fast agent. Complete the search request efficiently and report your findings clearly in a structured format.\nwhenToUse: |\n Fast agent specialized for exploring codebases. Use this when you need to quickly find files by patterns (e.g. \"src/**/*.yaml\"), search code for keywords (e.g. \"database connection\"), or answer questions about the codebase (e.g. \"how does the auth module work?\"). When calling this agent, specify the desired thoroughness level: \"quick\" for basic searches, \"medium\" for moderate exploration, or \"thorough\" for comprehensive analysis across multiple locations and naming conventions. Use this agent for any read-only exploration that will clearly require more than 3 search queries. Prefer launching multiple explore agents concurrently when investigating independent questions.\ntools:\n - Bash\n - Read\n - ReadMediaFile\n - Glob\n - Grep\n - MistakeRecord\n - WebSearch\n - FetchURL\n";
28562
28589
  explore_default = explore_default.replace(" - Read\n", " - Read\n - ReadBatch\n");
28590
+ explore_default = explore_default.replace(" - ReadBatch\n", " - ReadBatch\n - ProgrammaticTool\n");
28563
28591
  }));
28564
28592
  //#endregion
28565
28593
  //#region ../../packages/agent-core/src/profile/default/init.md?raw
@@ -28573,6 +28601,7 @@ var plan_default;
28573
28601
  var init_plan$1 = __esmMin((() => {
28574
28602
  plan_default = "extends: agent\nname: plan\npromptVars:\n roleAdditional: |\n You are now running as a subagent. All the `user` messages are sent by the main agent. The main agent cannot see your context, it can only see your last message when you finish the task. You must treat the parent agent as your caller. Do not directly ask the end user questions. If something is unclear, explain the ambiguity in your final summary to the parent agent.\n\n Before designing your implementation plan, consider whether you fully understand the codebase areas relevant to the task. If not, recommend the parent agent to use the explore agent (subagent_type=\"explore\") to investigate key questions first. In your response, clearly state:\n 1. What you already know from the information provided\n 2. What questions remain unanswered that would benefit from explore agent investigation\n 3. Your implementation plan (either preliminary if questions remain, or final if sufficient context exists)\n\n You are a read-only planning agent: you can read and search files (Read, Glob, Grep, ReadMediaFile) and consult the web (WebSearch, FetchURL), but you have no shell and no project file-editing tools. MistakeRecord is the sole write-capable exception: it may record a concrete refutation in shared BLUN learning memory, never modify project files. Where the general instructions tell you to make changes with tools, that does not apply to you — do not attempt to run commands or modify project files. Your deliverable is the plan itself, returned as your final message.\nwhenToUse: |\n Use this agent when the parent agent needs a step-by-step implementation plan, key file identification, and architectural trade-off analysis before code changes are made.\ntools:\n - Read\n - ReadMediaFile\n - Glob\n - Grep\n - MistakeRecord\n - WebSearch\n - FetchURL\n";
28575
28603
  plan_default = plan_default.replace(" - Read\n", " - Read\n - ReadBatch\n");
28604
+ plan_default = plan_default.replace(" - ReadBatch\n", " - ReadBatch\n - ProgrammaticTool\n");
28576
28605
  }));
28577
28606
  //#endregion
28578
28607
  //#region ../../packages/agent-core/src/profile/default/system.md?raw
@@ -29102,6 +29131,7 @@ var init_agent_task = __esmMin((() => {
29102
29131
  continuationTimeoutMs;
29103
29132
  runInBackground;
29104
29133
  parentToolCallId;
29134
+ responseFormat;
29105
29135
  kind = "agent";
29106
29136
  idPrefix = "agent";
29107
29137
  agentId;
@@ -29114,6 +29144,7 @@ var init_agent_task = __esmMin((() => {
29114
29144
  this.continuationTimeoutMs = options.timeoutMs;
29115
29145
  this.runInBackground = options.runInBackground;
29116
29146
  this.parentToolCallId = options.parentToolCallId;
29147
+ this.responseFormat = options.responseFormat;
29117
29148
  this.agentId = handle.agentId;
29118
29149
  this.subagentType = handle.profileName;
29119
29150
  }
@@ -29133,6 +29164,7 @@ var init_agent_task = __esmMin((() => {
29133
29164
  prompt: continuation.kind === "transient_provider" ? "Continue from the preserved context after a transient provider interruption. Do not repeat completed work; inspect the preserved wire and continue from the last durable boundary." : "Continue from the preserved context after the wall-clock interval.",
29134
29165
  description: this.description,
29135
29166
  runInBackground: this.runInBackground,
29167
+ responseFormat: this.responseFormat,
29136
29168
  signal: this.abortController.signal
29137
29169
  });
29138
29170
  return this.handle;
@@ -30334,6 +30366,7 @@ var init_completion_budget = __esmMin((() => {
30334
30366
  }));
30335
30367
  //#endregion
30336
30368
  //#region ../../packages/agent-core/src/loop/retry.ts
30369
+ var { nextThinkingEffortForExhaustedEmpty } = createRequire(import.meta.url)("./bin/empty-response-retry-policy.cjs");
30337
30370
  async function chatWithRetry(input) {
30338
30371
  const maxAttempts = input.maxAttempts ?? 3;
30339
30372
  if (input.llm.isRetryableError === void 0 || maxAttempts <= 1) {
@@ -30347,9 +30380,10 @@ async function chatWithRetry(input) {
30347
30380
  }
30348
30381
  const delays = retryBackoffDelays(maxAttempts);
30349
30382
  let completionBudgetRetry;
30383
+ let thinkingEffortRetry;
30350
30384
  let emptyRetryKind;
30351
30385
  for (let attempt = 1;; attempt += 1) try {
30352
- return await input.llm.chat(paramsForAttempt(input, attempt, maxAttempts, completionBudgetRetry));
30386
+ return await input.llm.chat(paramsForAttempt(input, attempt, maxAttempts, completionBudgetRetry, thinkingEffortRetry));
30353
30387
  } catch (error) {
30354
30388
  if (error instanceof APIEmptyResponseError && (emptyRetryKind !== void 0 || error.emptyResponseKind === "length" || error.emptyResponseKind === "stop")) {
30355
30389
  logEmptyResponse(input, error, attempt);
@@ -30359,6 +30393,12 @@ async function chatWithRetry(input) {
30359
30393
  throw terminal;
30360
30394
  }
30361
30395
  completionBudgetRetry = completionBudgetRetryForEmpty(error);
30396
+ thinkingEffortRetry = nextThinkingEffortForExhaustedEmpty({
30397
+ emptyResponseKind: error.emptyResponseKind,
30398
+ maxCompletionTokens: error.maxCompletionTokens,
30399
+ completionTokens: error.completionTokens,
30400
+ thinkingEffort: input.llm.thinkingEffort
30401
+ });
30362
30402
  emptyRetryKind = error.emptyResponseKind;
30363
30403
  input.params.signal.throwIfAborted();
30364
30404
  input.dispatchEvent({
@@ -30415,11 +30455,12 @@ function logRequestFailure(input, error, attempt, maxAttempts) {
30415
30455
  ...retryErrorFields(error)
30416
30456
  });
30417
30457
  }
30418
- function paramsForAttempt(input, attempt, maxAttempts, completionBudgetRetry) {
30458
+ function paramsForAttempt(input, attempt, maxAttempts, completionBudgetRetry, thinkingEffortRetry) {
30419
30459
  const turnStep = `${input.turnId}.${String(input.currentStep)}`;
30420
30460
  return {
30421
30461
  ...input.params,
30422
30462
  completionBudgetRetry,
30463
+ thinkingEffortRetry,
30423
30464
  requestLogFields: attempt === 1 ? { turnStep } : {
30424
30465
  turnStep,
30425
30466
  attempt: `${String(attempt)}/${String(maxAttempts)}`
@@ -30427,12 +30468,19 @@ function paramsForAttempt(input, attempt, maxAttempts, completionBudgetRetry) {
30427
30468
  };
30428
30469
  }
30429
30470
  function logEmptyResponse(input, error, attempt) {
30471
+ const retryThinkingEffort = nextThinkingEffortForExhaustedEmpty({
30472
+ emptyResponseKind: error.emptyResponseKind,
30473
+ maxCompletionTokens: error.maxCompletionTokens,
30474
+ completionTokens: error.completionTokens,
30475
+ thinkingEffort: input.llm.thinkingEffort
30476
+ });
30430
30477
  input.log?.warn(`[leer] fall=${error.emptyResponseKind}`, {
30431
30478
  turnStep: `${input.turnId}.${String(input.currentStep)}`,
30432
30479
  attempt,
30433
30480
  budget: error.maxCompletionTokens,
30434
30481
  completion: error.completionTokens,
30435
- reasoningLength: error.reasoningLength
30482
+ reasoningLength: error.reasoningLength,
30483
+ ...retryThinkingEffort === void 0 ? {} : { retryThinkingEffort }
30436
30484
  });
30437
30485
  }
30438
30486
  function retryBackoffDelays(maxAttempts) {
@@ -30599,6 +30647,96 @@ var init_todo_list = __esmMin((() => {
30599
30647
  };
30600
30648
  }));
30601
30649
  //#endregion
30650
+ //#region ../../packages/agent-core/src/tools/builtin/state/programmatic-tool.ts
30651
+ var PROGRAMMATIC_TOOL_NAME, ProgrammaticTool;
30652
+ var init_programmatic_tool = __esmMin((() => {
30653
+ PROGRAMMATIC_TOOL_NAME = "ProgrammaticTool";
30654
+ ProgrammaticTool = class {
30655
+ name = PROGRAMMATIC_TOOL_NAME;
30656
+ description = "Run bounded JavaScript in an isolated QuickJS interpreter to coordinate several sequential BLUN tool calls without returning every intermediate value to the model context. Declare every callable tool explicitly with its current BLUN name and a JavaScript alias. Calls still pass through the normal schema, hook, permission, scheduler, event, and result pipeline; denied or unavailable tools fail inside the program. The interpreter has no host filesystem, network, process, package loader, clock, or dynamic eval. Return a JSON-serializable result and use the JSON state object only for explicit continuation.";
30657
+ parameters = {
30658
+ type: "object",
30659
+ properties: {
30660
+ code: {
30661
+ type: "string",
30662
+ maxLength: 32e3,
30663
+ description: "JavaScript function body. Use state for explicit JSON state and tools.<alias>(args) for declared sequential tool calls."
30664
+ },
30665
+ state: {
30666
+ type: "object",
30667
+ description: "Optional JSON-only state snapshot from a previous run."
30668
+ },
30669
+ tools: {
30670
+ type: "array",
30671
+ maxItems: 16,
30672
+ description: "Explicit allowlist for this program. Each name must be a tool available in the current turn.",
30673
+ items: {
30674
+ type: "object",
30675
+ properties: {
30676
+ name: {
30677
+ type: "string",
30678
+ minLength: 1,
30679
+ description: "Exact current BLUN tool name."
30680
+ },
30681
+ alias: {
30682
+ type: "string",
30683
+ pattern: "^[A-Za-z_$][A-Za-z0-9_$]*$",
30684
+ description: "JavaScript identifier exposed under tools."
30685
+ }
30686
+ },
30687
+ required: ["name", "alias"],
30688
+ additionalProperties: false
30689
+ }
30690
+ }
30691
+ },
30692
+ required: ["code"],
30693
+ additionalProperties: false
30694
+ };
30695
+ resolveExecution(args) {
30696
+ const declaredTools = Array.isArray(args.tools) ? args.tools : [];
30697
+ return {
30698
+ description: `Running bounded programmatic tool plan (${declaredTools.length} allowed tool${declaredTools.length === 1 ? "" : "s"})`,
30699
+ approvalRule: this.name,
30700
+ execute: async (context) => {
30701
+ if (typeof context.invokeTool !== "function") return {
30702
+ output: "Programmatic tool invocation boundary is unavailable.",
30703
+ isError: true
30704
+ };
30705
+ try {
30706
+ const { createProgrammaticToolRuntime } = await import(new URL("./bin/programmatic-tool-runtime.mjs", import.meta.url).href);
30707
+ const runtime = createProgrammaticToolRuntime({
30708
+ allowedTools: declaredTools,
30709
+ invokeTool: context.invokeTool,
30710
+ onEvent: (event) => context.onUpdate({
30711
+ kind: "programmatic_tool",
30712
+ event
30713
+ })
30714
+ });
30715
+ const outcome = await runtime.run({
30716
+ code: args.code,
30717
+ state: args.state ?? {},
30718
+ signal: context.signal
30719
+ });
30720
+ const result = {
30721
+ output: JSON.stringify(outcome),
30722
+ isError: false
30723
+ };
30724
+ if (context.invokeTool.stopTurn === true) result.stopTurn = true;
30725
+ return result;
30726
+ } catch (error) {
30727
+ const result = {
30728
+ output: error instanceof Error ? error.message : String(error),
30729
+ isError: true
30730
+ };
30731
+ if (context.invokeTool.stopTurn === true) result.stopTurn = true;
30732
+ return result;
30733
+ }
30734
+ }
30735
+ };
30736
+ }
30737
+ };
30738
+ }));
30739
+ //#endregion
30602
30740
  //#region ../../packages/agent-core/src/tools/support/file-type.ts
30603
30741
  function toBuffer$2(data) {
30604
30742
  return Buffer.isBuffer(data) ? data : Buffer.from(data.buffer, data.byteOffset, data.byteLength);
@@ -74747,6 +74885,9 @@ var init_kosong_llm = __esmMin((() => {
74747
74885
  this.visionReader = config.visionReader;
74748
74886
  this.onVisionUsage = config.onVisionUsage;
74749
74887
  }
74888
+ get thinkingEffort() {
74889
+ return this.provider.thinkingEffort;
74890
+ }
74750
74891
  notifyMediaDropped(dropped) {
74751
74892
  if (this.onMediaDropped === void 0) return;
74752
74893
  const fresh = dropped.filter((part) => {
@@ -74789,6 +74930,7 @@ var init_kosong_llm = __esmMin((() => {
74789
74930
  let result;
74790
74931
  let completionBudget;
74791
74932
  try {
74933
+ const requestProvider = params.thinkingEffortRetry === void 0 ? this.provider : this.provider.withThinking(params.thinkingEffortRetry);
74792
74934
  const enrichedMessages = this.visionReader === void 0 ? params.messages : await enrichMessagesWithVision(params.messages, this.visionReader, params.signal, this.onVisionUsage);
74793
74935
  const tools = [...params.tools];
74794
74936
  const outgoingMessages = downgradeUnsupportedMedia(enrichedMessages, this.capability, (dropped) => {
@@ -74798,11 +74940,11 @@ var init_kosong_llm = __esmMin((() => {
74798
74940
  const reportedContextTokens = this.reportedContextTokens?.() ?? 0;
74799
74941
  const usedContextTokens = Math.max(outgoingRequestTokens, reportedContextTokens);
74800
74942
  completionBudget = applyCompletionBudgetWithDetails({
74801
- provider: this.provider,
74943
+ provider: requestProvider,
74802
74944
  budget: this.completionBudgetConfig,
74803
74945
  capability: this.capability,
74804
74946
  usedContextTokens,
74805
- retry: params.completionBudgetRetry ?? (thinkingEnabled(this.provider.thinkingEffort) ? {
74947
+ retry: params.completionBudgetRetry ?? (thinkingEnabled(requestProvider.thinkingEffort) ? {
74806
74948
  minimumCompletionTokens: 1024,
74807
74949
  multiplier: 1
74808
74950
  } : void 0)
@@ -76041,8 +76183,9 @@ var init_full = __esmMin((() => {
76041
76183
  }));
76042
76184
  //#endregion
76043
76185
  //#region ../../packages/agent-core/src/agent/compaction/micro.ts
76044
- var selectMicroCompactionCutoff, MICRO_COMPACTION_RECENT_MESSAGES, MICRO_COMPACTION_TOOL_ARGUMENTS_ENABLED, MICRO_COMPACTION_TOOL_ARGUMENT_MIN_TOKENS, isPersistedToolResultReference, freshToolResultIds, projectHistoricalUnaddressedTelegramMessages, dedupeRepeatedUserMessages, projectRepeatedAssistantResponses, compactHistoricalSkillActivations, dedupeRecurringCronWakeups, dedupeRepeatedInjections, projectLoopEventForRecord, projectUsageModelForRecord, projectUsageForRecord, resolveCompletedStepUuid, resolveStepEventUuid, restoreUsageFromRecord, restoreUsageModelFromRecord, DEFAULT_CONFIG, MicroCompaction;
76186
+ var selectMicroCompactionCutoff, MICRO_COMPACTION_RECENT_MESSAGES, MICRO_COMPACTION_TOOL_ARGUMENTS_ENABLED, MICRO_COMPACTION_TOOL_ARGUMENT_MIN_TOKENS, isPersistedToolResultReference, freshToolResultIds, projectHistoricalUnaddressedTelegramMessages, dedupeRepeatedUserMessages, projectRepeatedAssistantResponses, compactHistoricalSkillActivations, dedupeRecurringCronWakeups, dedupeRepeatedInjections, projectLoopEventForRecord, projectUsageModelForRecord, projectUsageForRecord, resolveCompletedStepUuid, resolveStepEventUuid, restoreUsageFromRecord, restoreUsageModelFromRecord, isWireOnlyProgrammaticEvent, DEFAULT_CONFIG, MicroCompaction;
76045
76187
  var init_micro = __esmMin((() => {
76188
+ ({ isWireOnlyProgrammaticEvent } = createRequire(import.meta.url)("./bin/programmatic-context-isolation.cjs"));
76046
76189
  ({ selectMicroCompactionCutoff, MICRO_COMPACTION_RECENT_MESSAGES, MICRO_COMPACTION_TOOL_ARGUMENTS_ENABLED, MICRO_COMPACTION_TOOL_ARGUMENT_MIN_TOKENS } = createRequire(import.meta.url)("./bin/micro-compaction-policy.cjs"));
76047
76190
  ({ isPersistedToolResultReference, freshToolResultIds } = createRequire(import.meta.url)("./bin/tool-result-offload-policy.cjs"));
76048
76191
  ({ projectHistoricalUnaddressedTelegramMessages } = createRequire(import.meta.url)("./bin/telegram-context-projection-policy.cjs"));
@@ -79191,6 +79334,7 @@ var init_context$2 = __esmMin((() => {
79191
79334
  type: "context.append_loop_event",
79192
79335
  event: projectLoopEventForRecord(event)
79193
79336
  });
79337
+ if (isWireOnlyProgrammaticEvent(event)) return;
79194
79338
  switch (event.type) {
79195
79339
  case "step.begin": {
79196
79340
  const closed = this.closePendingToolResults();
@@ -233363,6 +233507,7 @@ var init_default_tool_approve = __esmMin((() => {
233363
233507
  "Agent",
233364
233508
  "AskUserQuestion",
233365
233509
  "Skill",
233510
+ "ProgrammaticTool",
233366
233511
  "GetGoal",
233367
233512
  "SetGoalBudget",
233368
233513
  "UpdateGoal"
@@ -244071,6 +244216,49 @@ async function runToolCallBatch(step, response) {
244071
244216
  }
244072
244217
  return { stopTurn };
244073
244218
  }
244219
+ function createNestedToolInvoker(step, parentToolCall) {
244220
+ let nestedSequence = 0;
244221
+ const invoke = async ({ name, args, callId, signal }) => {
244222
+ if (invoke.stopTurn === true) throw new Error("No nested tool calls are allowed after a turn-stop result.");
244223
+ if (name === PROGRAMMATIC_TOOL_NAME) throw new Error("Nested ProgrammaticTool calls are not allowed.");
244224
+ const nestedSignal = signal ?? step.signal;
244225
+ nestedSignal.throwIfAborted();
244226
+ nestedSequence += 1;
244227
+ const safeCallId = typeof callId === "string" && callId.length > 0 ? callId : `call_${nestedSequence}`;
244228
+ const nestedToolCall = {
244229
+ id: `${parentToolCall.id}:ptc:${safeCallId}`,
244230
+ name,
244231
+ arguments: JSON.stringify(args ?? {})
244232
+ };
244233
+ const nestedStep = {
244234
+ ...step,
244235
+ signal: nestedSignal,
244236
+ toolCalls: [nestedToolCall],
244237
+ dispatchEvent: (event) => step.dispatchEvent(markProgrammaticNestedEvent(event))
244238
+ };
244239
+ const preflight = preflightToolCall(nestedStep, nestedToolCall);
244240
+ const prepared = await prepareToolCall(nestedStep, preflight);
244241
+ const started = await prepared.task.start();
244242
+ const pendingResult = await started.result;
244243
+ const finalized = await finalizePendingToolResult(nestedStep, pendingResult);
244244
+ if (finalized.stopTurn === true) invoke.stopTurn = true;
244245
+ await nestedStep.dispatchEvent({
244246
+ type: "tool.result",
244247
+ parentUuid: nestedToolCall.id,
244248
+ toolCallId: nestedToolCall.id,
244249
+ result: finalized.result
244250
+ });
244251
+ if (finalized.result.isError === true) throw new Error(nestedToolResultText(finalized.result));
244252
+ return finalized.result;
244253
+ };
244254
+ invoke.stopTurn = false;
244255
+ return invoke;
244256
+ }
244257
+ function nestedToolResultText(result) {
244258
+ if (typeof result.output === "string") return result.output;
244259
+ const text = result.output.filter((part) => part.type === "text").map((part) => part.text).join("");
244260
+ return text.length > 0 ? text : "Nested tool call failed.";
244261
+ }
244074
244262
  /**
244075
244263
  * Provider-order validation pass. It does not run hooks, spawn tools, or write
244076
244264
  * events. Validator compilation may populate the local cache.
@@ -244326,11 +244514,13 @@ async function finalizePendingToolResult(step, pendingResult) {
244326
244514
  async function executeTool(step, execution, toolCall, toolName, metadata) {
244327
244515
  const { dispatchEvent, signal, turnId } = step;
244328
244516
  signal.throwIfAborted();
244517
+ const invokeTool = toolName === PROGRAMMATIC_TOOL_NAME ? createNestedToolInvoker(step, toolCall) : void 0;
244329
244518
  return raceExecuteWithGraceTimeout(execution.execute({
244330
244519
  turnId,
244331
244520
  toolCallId: toolCall.id,
244332
244521
  metadata,
244333
244522
  signal,
244523
+ invokeTool,
244334
244524
  onUpdate: (update) => {
244335
244525
  if (signal.aborted) return;
244336
244526
  dispatchEvent({
@@ -244462,8 +244652,9 @@ async function dispatchToolCall(step, call, args, displayFields) {
244462
244652
  display: displayFields?.display
244463
244653
  });
244464
244654
  }
244465
- var GRACE_TIMEOUT_MS, TOOL_OUTPUT_EMPTY, TOOL_OUTPUT_NON_TEXT, validators;
244655
+ var markProgrammaticNestedEvent, GRACE_TIMEOUT_MS, TOOL_OUTPUT_EMPTY, TOOL_OUTPUT_NON_TEXT, validators;
244466
244656
  var init_tool_call = __esmMin((() => {
244657
+ ({ markProgrammaticNestedEvent } = createRequire(import.meta.url)("./bin/programmatic-context-isolation.cjs"));
244467
244658
  init_args_validator();
244468
244659
  init_path_access();
244469
244660
  init_abort();
@@ -251417,6 +251608,7 @@ var init_task_output = __esmMin((() => {
251417
251608
  init_zod$1();
251418
251609
  init_background();
251419
251610
  init_input_schema();
251611
+ init_args_validator();
251420
251612
  init_rule_match();
251421
251613
  init_format();
251422
251614
  init_task_output$1();
@@ -252467,12 +252659,29 @@ function lastAssistantText(agent) {
252467
252659
  }
252468
252660
  return "";
252469
252661
  }
252662
+ async function completeStructuredSubagentResult(child, childId, profileName, responseFormat, signal) {
252663
+ let validation = validateStructuredSubagentOutput(lastAssistantText(child), responseFormat);
252664
+ if (validation.validatorException === true) throw new Error(validation.error);
252665
+ if (!validation.ok) {
252666
+ signal.throwIfAborted();
252667
+ if (child.turn.prompt([{
252668
+ type: "text",
252669
+ text: buildStructuredResponseRepairPrompt(responseFormat, validation.error)
252670
+ }], SUBAGENT_PROMPT_ORIGIN) === null) throw new Error("Subagent could not start its structured-output repair turn.");
252671
+ await completeChildTurnWithMaxTokensHandoff(child, signal);
252672
+ signal.throwIfAborted();
252673
+ validation = validateStructuredSubagentOutput(lastAssistantText(child), responseFormat);
252674
+ if (validation.validatorException === true) throw new Error(validation.error);
252675
+ if (!validation.ok) throw new Error(`${STRUCTURED_SUBAGENT_OUTPUT_ERROR}: ${validation.error}. Resume with Agent(resume="${childId}", prompt="return the corrected JSON object", response_format=...) using the same response format.`);
252676
+ }
252677
+ return validation.value;
252678
+ }
252470
252679
  function shouldSuppressQueuedAttemptFailureEvent(options, error) {
252471
252680
  if (options.suppressRateLimitFailureEvent !== true) return false;
252472
252681
  if (isProviderRateLimitError(error)) return true;
252473
252682
  return isAbortError$4(error) || options.signal.aborted;
252474
252683
  }
252475
- var DEFAULT_SUBAGENT_TIMEOUT_MS, resolveSubagentTimeoutMs, runAgentWithTimeoutContinuation, buildSubagentUsageDelta, inheritSubagentAllowedTools, inheritSubagentLifecycleTools, SUBAGENT_MAX_TOKENS_ERROR, SUBAGENT_MAX_TOKENS_HANDOFF_PROMPT, buildSubagentMaxTokensFailure, isSubagentMaxTokensTurnError, shouldRequestSubagentHandoff, SUMMARY_MIN_LENGTH, SUMMARY_CONTINUATION_ATTEMPTS, HOOK_TEXT_PREVIEW_LENGTH, TOOL_CALL_DISABLED_MESSAGE, SUBAGENT_PROMPT_ORIGIN, SIDE_QUESTION_SYSTEM_REMINDER, SessionSubagentHost;
252684
+ var DEFAULT_SUBAGENT_TIMEOUT_MS, resolveSubagentTimeoutMs, runAgentWithTimeoutContinuation, buildSubagentUsageDelta, inheritSubagentAllowedTools, inheritSubagentLifecycleTools, STRUCTURED_SUBAGENT_OUTPUT_ERROR, prepareStructuredResponseFormat, validateStructuredSubagentOutput, buildStructuredResponseFormatReminder, buildStructuredResponseRepairPrompt, buildStructuredSubagentEnvelope, SUBAGENT_MAX_TOKENS_ERROR, SUBAGENT_MAX_TOKENS_HANDOFF_PROMPT, buildSubagentMaxTokensFailure, isSubagentMaxTokensTurnError, shouldRequestSubagentHandoff, SUMMARY_MIN_LENGTH, SUMMARY_CONTINUATION_ATTEMPTS, HOOK_TEXT_PREVIEW_LENGTH, TOOL_CALL_DISABLED_MESSAGE, SUBAGENT_PROMPT_ORIGIN, SIDE_QUESTION_SYSTEM_REMINDER, SessionSubagentHost;
252476
252685
  var init_subagent_host = __esmMin((() => {
252477
252686
  init_src$4();
252478
252687
  init_errors$8();
@@ -252487,6 +252696,14 @@ var init_subagent_host = __esmMin((() => {
252487
252696
  ({ DEFAULT_SUBAGENT_TIMEOUT_MS, resolveSubagentTimeoutMs, runAgentWithTimeoutContinuation } = createRequire(import.meta.url)("./bin/subagent-timeout-policy.cjs"));
252488
252697
  ({ buildSubagentUsageDelta } = createRequire(import.meta.url)("./bin/subagent-usage-rollup-policy.cjs"));
252489
252698
  ({ inheritSubagentAllowedTools, inheritSubagentLifecycleTools } = createRequire(import.meta.url)("./bin/subagent-tool-policy.cjs"));
252699
+ ({
252700
+ STRUCTURED_SUBAGENT_OUTPUT_ERROR,
252701
+ prepareStructuredResponseFormat,
252702
+ validateStructuredSubagentOutput,
252703
+ buildStructuredResponseFormatReminder,
252704
+ buildStructuredResponseRepairPrompt,
252705
+ buildStructuredSubagentEnvelope
252706
+ } = createRequire(import.meta.url)("./bin/structured-subagent-output.cjs"));
252490
252707
  ({
252491
252708
  SUBAGENT_MAX_TOKENS_ERROR,
252492
252709
  SUBAGENT_MAX_TOKENS_HANDOFF_PROMPT,
@@ -252751,6 +252968,10 @@ IMPORTANT:
252751
252968
  const gitContext = await collectGitContext(child.kaos, child.config.cwd);
252752
252969
  if (gitContext) childPrompt = `${gitContext}\n\n${childPrompt}`;
252753
252970
  }
252971
+ if (options.responseFormat !== void 0) child.context.appendSystemReminder(buildStructuredResponseFormatReminder(options.responseFormat), {
252972
+ kind: "system_trigger",
252973
+ name: "subagent_response_format"
252974
+ });
252754
252975
  this.emitSubagentStarted(parent, childId);
252755
252976
  if (child.turn.prompt([{
252756
252977
  type: "text",
@@ -252761,17 +252982,29 @@ IMPORTANT:
252761
252982
  }
252762
252983
  async waitForChildCompletion(parent, childId, child, profileName, options) {
252763
252984
  await completeChildTurnWithMaxTokensHandoff(child, options.signal);
252764
- let result = lastAssistantText(child);
252765
- let remainingContinuations = SUMMARY_CONTINUATION_ATTEMPTS;
252766
- while (remainingContinuations > 0 && result.length < SUMMARY_MIN_LENGTH) {
252767
- remainingContinuations -= 1;
252768
- options.signal.throwIfAborted();
252769
- child.turn.prompt([{
252770
- type: "text",
252771
- text: summary_continuation_default
252772
- }], SUBAGENT_PROMPT_ORIGIN);
252773
- await completeChildTurnWithMaxTokensHandoff(child, options.signal);
252985
+ let result;
252986
+ if (options.responseFormat !== void 0) {
252987
+ const structuredResult = await completeStructuredSubagentResult(child, childId, profileName, options.responseFormat, options.signal);
252988
+ result = buildStructuredSubagentEnvelope({
252989
+ agentId: childId,
252990
+ profileName,
252991
+ responseFormat: options.responseFormat,
252992
+ result: structuredResult,
252993
+ usage: child.usage.data().total
252994
+ });
252995
+ } else {
252774
252996
  result = lastAssistantText(child);
252997
+ let remainingContinuations = SUMMARY_CONTINUATION_ATTEMPTS;
252998
+ while (remainingContinuations > 0 && result.length < SUMMARY_MIN_LENGTH) {
252999
+ remainingContinuations -= 1;
253000
+ options.signal.throwIfAborted();
253001
+ child.turn.prompt([{
253002
+ type: "text",
253003
+ text: summary_continuation_default
253004
+ }], SUBAGENT_PROMPT_ORIGIN);
253005
+ await completeChildTurnWithMaxTokensHandoff(child, options.signal);
253006
+ result = lastAssistantText(child);
253007
+ }
252775
253008
  }
252776
253009
  const usage = child.usage.data().total;
252777
253010
  parent.emitEvent({
@@ -252868,7 +253101,7 @@ var init_agent_background_disabled = __esmMin((() => {
252868
253101
  //#region ../../packages/agent-core/src/tools/builtin/collaboration/agent-background-enabled.md?raw
252869
253102
  var agent_background_enabled_default;
252870
253103
  var init_agent_background_enabled = __esmMin((() => {
252871
- agent_background_enabled_default = "Subagents run detached whenever task management is available, so the parent remains responsive while delegated work continues. Completion arrives automatically in a later turn. Do not poll, sleep, or block on TaskOutput. Continue with other work or respond to the user. Use TaskUpdate to steer the running subagent and TaskStop only when it must be cancelled. Never fabricate or predict its result.";
253104
+ agent_background_enabled_default = "Subagents run detached whenever task management is available, so the parent remains responsive while delegated work continues. A call with `response_format` is the deliberate exception: it runs in the foreground so BLUN can validate and, at most once, repair the final JSON before returning it. Detached completion arrives automatically in a later turn. Do not poll, sleep, or block on TaskOutput. Continue with other work or respond to the user. Use TaskUpdate to steer a running detached subagent and TaskStop only when it must be cancelled. Never fabricate or predict its result.";
252872
253105
  }));
252873
253106
  //#endregion
252874
253107
  //#region ../../packages/agent-core/src/tools/builtin/collaboration/agent.md?raw
@@ -252910,7 +253143,7 @@ function formatForegroundAgentFailure(handle, message, timedOut) {
252910
253143
  "",
252911
253144
  `subagent error: ${message}`
252912
253145
  ];
252913
- if (timedOut || message.includes(SUBAGENT_MAX_TOKENS_ERROR)) lines.push(`resume_hint: Continue with Agent(resume="${handle.agentId}", prompt="continue"). Use agent_id only; do not set subagent_type. The subagent retains its prior context; redo any unfinished tool call if its result was lost.`);
253146
+ if (timedOut || message.includes(SUBAGENT_MAX_TOKENS_ERROR) || message.includes(STRUCTURED_SUBAGENT_OUTPUT_ERROR)) lines.push(`resume_hint: Continue with Agent(resume="${handle.agentId}", prompt="continue"). Use agent_id only; do not set subagent_type. The subagent retains its prior context; redo any unfinished tool call if its result was lost.`);
252914
253147
  return lines.join("\n");
252915
253148
  }
252916
253149
  function launchErrorMessage(error, signal) {
@@ -252954,8 +253187,12 @@ var init_agent$1 = __esmMin((() => {
252954
253187
  description: string().describe("Short task description (3-5 words) for UI display"),
252955
253188
  subagent_type: string().optional().describe("One of the available agent types (see \"Available agent types\" in this tool description). Defaults to \"coder\" when omitted."),
252956
253189
  resume: string().optional().describe("Optional agent ID to resume instead of creating a new instance. When set, do not also pass subagent_type — the resumed agent keeps its own type, and supplying both is rejected."),
252957
- run_in_background: boolean$1().optional().describe("Compatibility option. When task management is available, subagents always run detached so the parent remains responsive."),
252958
- timeout_minutes: number$1().int().min(0).max(10080).optional().describe("Wall-clock interval before the same agent is continued automatically. Defaults to 240 minutes. Set 0 to disable the interval for this run.")
253190
+ run_in_background: boolean$1().optional().describe("Compatibility option. Normal subagents run detached when task management is available. A response_format call requires this to be false or omitted and runs in the foreground for validation."),
253191
+ timeout_minutes: number$1().int().min(0).max(10080).optional().describe("Wall-clock interval before the same agent is continued automatically. Defaults to 240 minutes. Set 0 to disable the interval for this run."),
253192
+ response_format: object({
253193
+ name: string().min(1).max(64).describe("Stable name for the structured result"),
253194
+ schema: record(string(), unknown()).describe("Restricted JSON Schema for one object result")
253195
+ }).optional().describe("Require a validated JSON object from a foreground subagent. Structured background agents are not supported yet.")
252959
253196
  }));
252960
253197
  object({
252961
253198
  result: string().describe("Aggregated text output from the subagent"),
@@ -252967,7 +253204,7 @@ var init_agent$1 = __esmMin((() => {
252967
253204
  }).describe("Cumulative token usage")
252968
253205
  });
252969
253206
  BACKGROUND_AGENT_UNAVAILABLE = "Background agent execution is not available for this agent because TaskList, TaskOutput, and TaskStop are not enabled.";
252970
- agent_default = "Launch a subagent for substantial, multi-step work in its own context and wire file. It returns a final report while keeping intermediate file contents out of your context.\n\nThe subagent starts with zero conversation context. Give it a self-contained prompt with the goal, known facts, constraints, and required result. For a direct lookup, include the exact path or command. For an investigation, state the question and evidence needed instead of prescribing rigid steps.\n\nWhen continuing earlier work, resume the existing agent ID instead of spawning a new agent; its context remains available. A subagent report is only visible to you, so relay the relevant conclusions to the user. `timeout_minutes` sets the wall-clock interval before the same agent continues automatically with preserved context; `timeout_minutes=0` disables that interval.\n\nUse Agent for complex, context-heavy work, not a trivial one- or two-step task. Launch independent agents in parallel in one response when useful. After assigning a scope, do not duplicate its searches or reads and do not abandon it to redo the same work manually; both erase the intended context savings.";
253207
+ agent_default = "Launch a subagent for substantial, multi-step work in its own context and wire file. It returns a final report while keeping intermediate file contents out of your context.\n\nThe subagent starts with zero conversation context. Give it a self-contained prompt with the goal, known facts, constraints, and required result. For a direct lookup, include the exact path or command. For an investigation, state the question and evidence needed instead of prescribing rigid steps.\n\nWhen continuing earlier work, resume the existing agent ID instead of spawning a new agent; its context remains available. A subagent report is only visible to you, so relay the relevant conclusions to the user. `timeout_minutes` sets the wall-clock interval before the same agent continues automatically with preserved context; `timeout_minutes=0` disables that interval.\n\nUse Agent for complex, context-heavy work, not a trivial one- or two-step task. Launch independent agents in parallel in one response when useful. After assigning a scope, do not duplicate its searches or reads and do not abandon it to redo the same work manually; both erase the intended context savings.\n\nFor a machine-readable foreground result, pass `response_format` with a stable name and restricted object JSON Schema. The child gets no additional tools or permissions. It must return exactly one matching JSON object; BLUN validates it and allows at most one bounded repair turn. Do not combine `response_format` with `run_in_background=true`.";
252971
253208
  AgentTool = class {
252972
253209
  subagentHost;
252973
253210
  backgroundManager;
@@ -252990,10 +253227,11 @@ var init_agent$1 = __esmMin((() => {
252990
253227
  let profileName = args.subagent_type?.length ? args.subagent_type : "coder";
252991
253228
  const resumeAgentId = args.resume?.trim();
252992
253229
  if (resumeAgentId !== void 0 && resumeAgentId.length > 0) profileName = await this.subagentHost.getProfileName?.(resumeAgentId) ?? "subagent";
252993
- const { runInBackground } = resolveSubagentRunMode({
253230
+ const resolvedRunMode = resolveSubagentRunMode({
252994
253231
  allowBackground: this.allowBackground,
252995
253232
  requested: args.run_in_background
252996
253233
  });
253234
+ const runInBackground = args.response_format === void 0 ? resolvedRunMode.runInBackground : false;
252997
253235
  return {
252998
253236
  description: `${runInBackground ? "Launching background" : "Launching"} ${profileName} agent: ${args.description}`,
252999
253237
  accesses: ToolAccesses.none(),
@@ -253011,10 +253249,24 @@ var init_agent$1 = __esmMin((() => {
253011
253249
  async execution(args, { toolCallId, signal }) {
253012
253250
  try {
253013
253251
  signal.throwIfAborted();
253014
- const { runInBackground } = resolveSubagentRunMode({
253252
+ if (args.response_format !== void 0 && args.run_in_background === true) return {
253253
+ output: "response_format is supported for foreground Agent runs only; remove run_in_background=true.",
253254
+ isError: true
253255
+ };
253256
+ let responseFormat;
253257
+ try {
253258
+ responseFormat = prepareStructuredResponseFormat(args.response_format, compileToolArgsValidator);
253259
+ } catch (error) {
253260
+ return {
253261
+ output: `Invalid response_format: ${error instanceof Error ? error.message : String(error)}`,
253262
+ isError: true
253263
+ };
253264
+ }
253265
+ const resolvedRunMode = resolveSubagentRunMode({
253015
253266
  allowBackground: this.allowBackground,
253016
253267
  requested: args.run_in_background
253017
253268
  });
253269
+ const runInBackground = responseFormat === void 0 ? resolvedRunMode.runInBackground : false;
253018
253270
  const requestedProfileName = args.subagent_type?.length ? args.subagent_type : void 0;
253019
253271
  const resumeAgentId = args.resume?.trim();
253020
253272
  if (resumeAgentId !== void 0 && resumeAgentId.length > 0 && requestedProfileName !== void 0) return {
@@ -253036,6 +253288,7 @@ var init_agent$1 = __esmMin((() => {
253036
253288
  prompt: args.prompt,
253037
253289
  description: args.description,
253038
253290
  runInBackground,
253291
+ responseFormat,
253039
253292
  signal: controller.signal
253040
253293
  };
253041
253294
  let handle;
@@ -253061,7 +253314,8 @@ var init_agent$1 = __esmMin((() => {
253061
253314
  taskId = this.backgroundManager.registerTask(new AgentBackgroundTask(handle, args.description, this.subagentHost, controller, {
253062
253315
  timeoutMs: resolveSubagentTimeoutMs({ perRunMinutes: args.timeout_minutes }),
253063
253316
  runInBackground,
253064
- parentToolCallId: toolCallId
253317
+ parentToolCallId: toolCallId,
253318
+ responseFormat
253065
253319
  }), {
253066
253320
  detached: runInBackground,
253067
253321
  signal: runInBackground ? void 0 : signal
@@ -253084,7 +253338,7 @@ var init_agent$1 = __esmMin((() => {
253084
253338
  }
253085
253339
  if (runInBackground) return { output: formatBackgroundAgentResult(taskId, handle, args.description, this.allowBackground) };
253086
253340
  if (await this.backgroundManager.waitForForegroundRelease(taskId) === "detached") return { output: formatBackgroundAgentResult(taskId, handle, args.description, this.allowBackground) };
253087
- return await this.formatForegroundResult(taskId, handle);
253341
+ return await this.formatForegroundResult(taskId, handle, responseFormat);
253088
253342
  } catch (error) {
253089
253343
  return {
253090
253344
  output: `subagent error: ${launchErrorMessage(error, signal)}`,
@@ -253092,9 +253346,12 @@ var init_agent$1 = __esmMin((() => {
253092
253346
  };
253093
253347
  }
253094
253348
  }
253095
- async formatForegroundResult(taskId, handle) {
253349
+ async formatForegroundResult(taskId, handle, responseFormat) {
253096
253350
  const info = this.backgroundManager.getTask(taskId);
253097
- if (info?.status === "completed") return { output: formatForegroundAgentSuccess(handle, await this.backgroundManager.readOutput(taskId)) };
253351
+ if (info?.status === "completed") {
253352
+ const output = await this.backgroundManager.readOutput(taskId);
253353
+ return { output: responseFormat === void 0 ? formatForegroundAgentSuccess(handle, output) : output };
253354
+ }
253098
253355
  const timedOut = info?.status === "timed_out";
253099
253356
  return {
253100
253357
  output: formatForegroundAgentFailure(handle, timedOut ? "Agent timed out before automatic continuation could start." : info?.stopReason === "Interrupted by user" ? USER_INTERRUPTED_SUBAGENT_MESSAGE : info?.stopReason !== void 0 ? info.stopReason : "The subagent was stopped before it finished.", timedOut),
@@ -261161,7 +261418,7 @@ function renderHistoricalToolResultReference(toolName, toolCallId, text, outputP
261161
261418
  function safeToolResultFileStem(toolName, toolCallId) {
261162
261419
  return `${toolName}-${toolCallId}`.replace(/[^a-zA-Z0-9._-]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 80) || "tool-result";
261163
261420
  }
261164
- var TOOL_RESULT_MAX_CHARS, TOOL_RESULT_PREVIEW_CHARS, TOOL_RESULT_HISTORICAL_PREVIEW_CHARS, TOOL_RESULT_RECOVERY_PAGE_LINES, TOOL_RESULT_OFFLOAD_MARKER, TOOL_RESULT_HISTORICAL_KEEP_RECENT_MESSAGES, shouldKeepFreshToolResult, shouldOffloadToolResult, createToolResultPreview, compactPersistedToolResultReference, compactHistoricalSuccessfulToolResults, dedupeRepeatedSuccessfulToolResults, readContinuationLineOffset, selectToolResultBatchOffloads, selectHistoricalToolResultOffloads, buildToolResultOffloadTelemetry;
261421
+ var TOOL_RESULT_MAX_CHARS, TOOL_RESULT_PREVIEW_CHARS, TOOL_RESULT_HISTORICAL_PREVIEW_CHARS, TOOL_RESULT_RECOVERY_PAGE_LINES, TOOL_RESULT_OFFLOAD_MARKER, TOOL_RESULT_HISTORICAL_KEEP_RECENT_MESSAGES, shouldKeepFreshToolResult, shouldOffloadToolResult, createToolResultPreview, compactPersistedToolResultReference, compactHistoricalSuccessfulToolResults, currentTurnReadToolResultIds, currentUserTurnStartIndex, dedupeRepeatedSuccessfulToolResults, readContinuationLineOffset, selectToolResultBatchOffloads, selectHistoricalToolResultOffloads, buildToolResultOffloadTelemetry;
261165
261422
  var init_tool_result_budget = __esmMin((() => {
261166
261423
  init_dist$6();
261167
261424
  const toolResultOffloadPolicy = createRequire(import.meta.url)("./bin/tool-result-offload-policy.cjs");
@@ -261177,6 +261434,8 @@ var init_tool_result_budget = __esmMin((() => {
261177
261434
  createToolResultPreview = toolResultOffloadPolicy.createToolResultPreview;
261178
261435
  compactPersistedToolResultReference = toolResultOffloadPolicy.compactPersistedToolResultReference;
261179
261436
  compactHistoricalSuccessfulToolResults = toolResultOffloadPolicy.compactHistoricalSuccessfulToolResults;
261437
+ currentTurnReadToolResultIds = toolResultOffloadPolicy.currentTurnReadToolResultIds;
261438
+ currentUserTurnStartIndex = toolResultOffloadPolicy.currentUserTurnStartIndex;
261180
261439
  dedupeRepeatedSuccessfulToolResults = toolResultOffloadPolicy.dedupeRepeatedSuccessfulToolResults;
261181
261440
  freshToolResultIds = toolResultOffloadPolicy.freshToolResultIds;
261182
261441
  readContinuationLineOffset = toolResultOffloadPolicy.readContinuationLineOffset;
@@ -261194,8 +261453,9 @@ var ToolResultBatchOffload = class {
261194
261453
  async detect() {
261195
261454
  const history = this.agent.context.history;
261196
261455
  const tailIds = freshToolResultIds(history);
261456
+ const pinnedReadIds = currentTurnReadToolResultIds(history);
261197
261457
  const recentStart = Math.max(0, history.length - TOOL_RESULT_HISTORICAL_KEEP_RECENT_MESSAGES);
261198
- const historical = history.slice(0, recentStart).filter((message) => message?.role === "tool" && !tailIds.has(message.toolCallId));
261458
+ const historical = history.slice(0, recentStart).filter((message) => message?.role === "tool" && !tailIds.has(message.toolCallId) && !pinnedReadIds.has(message.toolCallId));
261199
261459
  const candidateBatches = [{
261200
261460
  messages: historical,
261201
261461
  historical: true,
@@ -261369,7 +261629,7 @@ function renderPersistedUserMessage(text, outputPath) {
261369
261629
  createUserMessagePreview(text)
261370
261630
  ].join("\n");
261371
261631
  }
261372
- var USER_MESSAGE_MAX_CHARS, USER_MESSAGE_OFFLOAD_MARKER, shouldOffloadUserMessage, shouldOffloadHistoricalUserMessage, createUserMessagePreview, compactHistoricalPersistedUserMessageReferences, projectHistoricalMediaParts;
261632
+ var USER_MESSAGE_MAX_CHARS, USER_MESSAGE_OFFLOAD_MARKER, shouldOffloadUserMessage, shouldOffloadHistoricalUserMessage, createUserMessagePreview, compactHistoricalPersistedUserMessageReferences, historyTextChars, projectHistoricalMediaParts;
261373
261633
  var init_user_message_offload = __esmMin((() => {
261374
261634
  const policy = createRequire(import.meta.url)("./bin/user-message-offload-policy.cjs");
261375
261635
  USER_MESSAGE_MAX_CHARS = policy.USER_MESSAGE_MAX_CHARS;
@@ -261377,6 +261637,7 @@ var init_user_message_offload = __esmMin((() => {
261377
261637
  shouldOffloadUserMessage = policy.shouldOffloadUserMessage;
261378
261638
  shouldOffloadHistoricalUserMessage = policy.shouldOffloadHistoricalUserMessage;
261379
261639
  createUserMessagePreview = policy.createUserMessagePreview;
261640
+ historyTextChars = policy.historyTextChars;
261380
261641
  compactHistoricalPersistedUserMessageReferences = policy.compactHistoricalPersistedUserMessageReferences;
261381
261642
  ({ projectHistoricalMediaParts } = createRequire(import.meta.url)("./bin/historical-media-projection-policy.cjs"));
261382
261643
  }));
@@ -261389,8 +261650,11 @@ var UserMessageOffload = class {
261389
261650
  async detect() {
261390
261651
  const history = this.agent.context.history;
261391
261652
  if (this.agent.homedir === void 0) return 0;
261653
+ const currentTurnStart = currentUserTurnStartIndex(history);
261654
+ const totalHistoryChars = historyTextChars(history);
261392
261655
  let offloaded = 0;
261393
261656
  for (let historyIndex = 0; historyIndex < history.length; historyIndex++) {
261657
+ if (historyIndex === currentTurnStart) continue;
261394
261658
  const message = history[historyIndex];
261395
261659
  const text = persistableUserMessageText(message);
261396
261660
  if (text === void 0) continue;
@@ -261398,6 +261662,7 @@ var UserMessageOffload = class {
261398
261662
  const historicalOversize = shouldOffloadHistoricalUserMessage({
261399
261663
  historyIndex,
261400
261664
  historyLength: history.length,
261665
+ historyChars: totalHistoryChars,
261401
261666
  textChars: text.length
261402
261667
  });
261403
261668
  if (!immediateOversize && !historicalOversize) continue;
@@ -261520,6 +261785,7 @@ var AssistantMessageOffload = class {
261520
261785
  async detect() {
261521
261786
  const history = this.agent.context.history;
261522
261787
  if (this.agent.homedir === void 0) return 0;
261788
+ const totalHistoryChars = historyTextChars(history);
261523
261789
  let offloaded = 0;
261524
261790
  for (let historyIndex = 0; historyIndex < history.length; historyIndex++) {
261525
261791
  const message = history[historyIndex];
@@ -261527,6 +261793,7 @@ var AssistantMessageOffload = class {
261527
261793
  if (text === void 0 || !shouldOffloadHistoricalAssistantMessage({
261528
261794
  historyIndex,
261529
261795
  historyLength: history.length,
261796
+ historyChars: totalHistoryChars,
261530
261797
  textChars: text.length
261531
261798
  })) continue;
261532
261799
  const messageHash = assistantMessageOffloadHash(message, text);
@@ -261563,6 +261830,7 @@ var AssistantMessageOffload = class {
261563
261830
  }
261564
261831
  compact(messages) {
261565
261832
  const completedToolCallIds = new Set(messages.filter((message) => message?.role === "tool" && message.toolCallId !== void 0).map((message) => message.toolCallId));
261833
+ const totalHistoryChars = historyTextChars(messages);
261566
261834
  let changed = false;
261567
261835
  const projected = messages.map((message, historyIndex) => {
261568
261836
  if (message?.role === "assistant" && message.toolCalls.length > 0) {
@@ -261572,6 +261840,7 @@ var AssistantMessageOffload = class {
261572
261840
  if (shouldCompactHistoricalAssistantToolNarration({
261573
261841
  historyIndex,
261574
261842
  historyLength: messages.length,
261843
+ historyChars: totalHistoryChars,
261575
261844
  textChars,
261576
261845
  toolCallCount: message.toolCalls.length,
261577
261846
  completedToolCallCount
@@ -261959,7 +262228,7 @@ function durablePromptOrigin(origin) {
261959
262228
  void externalReportSources;
261960
262229
  return durableOrigin;
261961
262230
  }
261962
- var BLUN_CORE_TOOL_NAMES, BLUN_LEAN_TOOL_NAMES, BLUN_ATTACHMENT_MARKER_RE, BLUN_TELEGRAM_OUTBOUND_TOOL_RE, BLUN_TELEGRAM_CHANNEL_RE, BLUN_TOOL_BUDGET_RATIO, createDeferredToolLoader, mediaToolNamesForTurnText, rankedSupportToolNamesForGoal, rankedToolNamesForTurnText, rememberDeferredToolAfterNotFound, toolSchemaBudgetTokens, enforceSingleTodoWritePerStep, projectRecurringCronHistory, TelegramDeliveryLedger, createRuntimeCognitiveTurnLifecycle, cognitiveFocusScopesForTurn, buildCognitiveWorkFocus, buildAttentionQueueItem, LLM_NOT_SET_MESSAGE, GOAL_CONTINUATION_ORIGIN, GOAL_COMPLETION_REMINDER_NAME, GOAL_BLOCKED_REMINDER_NAME, GOAL_RATE_LIMIT_PAUSE_REASON, GOAL_PROVIDER_CONNECTION_PAUSE_PREFIX, GOAL_PROVIDER_AUTH_PAUSE_PREFIX, GOAL_PROVIDER_API_PAUSE_PREFIX, GOAL_MODEL_CONFIG_PAUSE_PREFIX, GOAL_RUNTIME_PAUSE_PREFIX, GOAL_PROVIDER_FILTERED_PAUSE_REASON, GOAL_CONTINUATION_PROMPT, TurnFlow;
262231
+ var BLUN_CORE_TOOL_NAMES, BLUN_LEAN_TOOL_NAMES, BLUN_ATTACHMENT_MARKER_RE, BLUN_TELEGRAM_OUTBOUND_TOOL_RE, BLUN_TELEGRAM_CHANNEL_RE, BLUN_TOOL_BUDGET_RATIO, createDeferredToolLoader, mediaToolNamesForTurnText, rankedSupportToolNamesForGoal, rankedToolNamesForTurnText, rememberDeferredToolAfterNotFound, toolSchemaBudgetTokens, assessTodoListBounds, assessTodoMaintenanceUpdate, enforceSingleTodoWritePerStep, todoRefreshWorkCallLimit, projectRecurringCronHistory, TelegramDeliveryLedger, createRuntimeCognitiveTurnLifecycle, cognitiveFocusScopesForTurn, buildCognitiveWorkFocus, buildAttentionQueueItem, LLM_NOT_SET_MESSAGE, GOAL_CONTINUATION_ORIGIN, GOAL_COMPLETION_REMINDER_NAME, GOAL_BLOCKED_REMINDER_NAME, GOAL_RATE_LIMIT_PAUSE_REASON, GOAL_PROVIDER_CONNECTION_PAUSE_PREFIX, GOAL_PROVIDER_AUTH_PAUSE_PREFIX, GOAL_PROVIDER_API_PAUSE_PREFIX, GOAL_MODEL_CONFIG_PAUSE_PREFIX, GOAL_RUNTIME_PAUSE_PREFIX, GOAL_PROVIDER_FILTERED_PAUSE_REASON, GOAL_CONTINUATION_PROMPT, TurnFlow;
261963
262232
  var init_turn = __esmMin((() => {
261964
262233
  init_dist$4();
261965
262234
  init_src$4();
@@ -261977,7 +262246,7 @@ var init_turn = __esmMin((() => {
261977
262246
  init_user_message_offload();
261978
262247
  init_assistant_message_offload();
261979
262248
  ({ CORE_TOOL_NAMES: BLUN_CORE_TOOL_NAMES, createDeferredToolLoader, mediaToolNamesForTurnText, rankedSupportToolNamesForGoal, rankedToolNamesForTurnText, rememberDeferredToolAfterNotFound, toolSchemaBudgetTokens } = createRequire(import.meta.url)("./bin/turn-tool-performance-policy.cjs"));
261980
- ({ enforceSingleTodoWritePerStep } = createRequire(import.meta.url)("./bin/todo-list-turn-policy.cjs"));
262249
+ ({ assessTodoListBounds, assessTodoMaintenanceUpdate, enforceSingleTodoWritePerStep, todoRefreshWorkCallLimit } = createRequire(import.meta.url)("./bin/todo-list-turn-policy.cjs"));
261981
262250
  ({ projectRecurringCronHistory } = createRequire(import.meta.url)("./bin/recurring-cron-history-policy.cjs"));
261982
262251
  ({ TelegramDeliveryLedger } = createRequire(import.meta.url)("./bin/repeated-user-message-projection.cjs"));
261983
262252
  ({ createRuntimeCognitiveTurnLifecycle } = createRequire(import.meta.url)("./bin/cognitive-turn-lifecycle.cjs"));
@@ -262789,6 +263058,7 @@ var init_turn = __esmMin((() => {
262789
263058
  async runStepLoop(turnId, signal, input, origin) {
262790
263059
  let stopHookContinuationUsed = false;
262791
263060
  let goalOutcomeMessageContinuationUsed = false;
263061
+ let directSteerContinuationPending = false;
262792
263062
  const directReplyTurnStop = createDirectReplyTurnStop(blunExtractText(input));
262793
263063
  const deduper = new ToolCallDeduplicator({ telemetry: this.agent.telemetry });
262794
263064
  if (blunTurnNeedsInitialMcp(input, origin)) await blunWaitForInitialMcpBudget(this.agent.mcp, signal);
@@ -262913,7 +263183,8 @@ var init_turn = __esmMin((() => {
262913
263183
  });
262914
263184
  return { llm: this.agent.llmForTurn(workStepThinkingEffort), tools: selectedTools };
262915
263185
  }
262916
- const steerEfforts = pendingSteers.map((steer) => blunTurnHasAttachment(steer.input) ? void 0 : selectThinkingEffortForBufferedSteer(blunExtractText(blunThinkingIntentInput(steer.input)), steer.origin.kind));
263186
+ if (pendingSteers.some((steer) => directMessageAllowsWorkContinuation(blunExtractText(steer.input)))) directSteerContinuationPending = true;
263187
+ const steerEfforts = pendingSteers.map((steer) => blunTurnHasAttachment(steer.input) ? void 0 : selectThinkingEffortForBufferedSteer(blunExtractText(blunThinkingIntentInput(steer.input)), steer.origin.kind));
262917
263188
  if (steerEfforts.some((effort) => effort === void 0)) return;
262918
263189
  const steerInput = pendingSteers.flatMap((steer) => [...steer.input]);
262919
263190
  const steerThinkingEffort = "low";
@@ -262959,10 +263230,22 @@ var init_turn = __esmMin((() => {
262959
263230
  deduper.endStep();
262960
263231
  return stopForGoalBudget || directReplyTurnStop.shouldStop() ? { stopTurn: true } : void 0;
262961
263232
  },
262962
- shouldContinueAfterStop: async (ctx) => {
262963
- const { signal } = ctx;
262964
- if (this.matchingSteers().length > 0) return { continue: true };
262965
- signal.throwIfAborted();
263233
+ shouldContinueAfterStop: async (ctx) => {
263234
+ const { signal } = ctx;
263235
+ if (this.matchingSteers().length > 0) return { continue: true };
263236
+ signal.throwIfAborted();
263237
+ if (directSteerContinuationPending) {
263238
+ directSteerContinuationPending = false;
263239
+ this.agent.context.appendUserMessage([{
263240
+ type: "text",
263241
+ text: "The private Telegram DM was handled. Continue the exact work that was active before it arrived. Do not re-read completed evidence; perform the next unfinished action now."
263242
+ }], {
263243
+ kind: "system_trigger",
263244
+ name: "telegram_direct_work_resume"
263245
+ });
263246
+ this.setActiveSteerAcceptance(turnId, true);
263247
+ return { continue: true };
263248
+ }
262966
263249
  if (!goalOutcomeMessageContinuationUsed && isGoalOutcomeReminderOrigin(this.agent.context.history.at(-1)?.origin)) {
262967
263250
  goalOutcomeMessageContinuationUsed = true;
262968
263251
  if (!hasStepBudgetRemaining(loopControl?.maxStepsPerTurn, ctx.stepNumber)) {
@@ -262997,6 +263280,8 @@ var init_turn = __esmMin((() => {
262997
263280
  prepareToolExecution: async (ctx) => {
262998
263281
  const todoTurnPolicy = enforceSingleTodoWritePerStep(ctx);
262999
263282
  if (todoTurnPolicy !== void 0) return todoTurnPolicy;
263283
+ const todoBoundsPolicy = enforceTodoListBounds(this.agent, ctx);
263284
+ if (todoBoundsPolicy !== void 0) return todoBoundsPolicy;
263000
263285
  const goalTodoPolicy = enforceGoalTodoPolicy(this.agent, ctx);
263001
263286
  if (goalTodoPolicy !== void 0) return goalTodoPolicy;
263002
263287
  const ideaPolicy = enforceIdeaToolPolicy(this.agent, ctx);
@@ -264119,20 +264404,16 @@ var init_codebase_search$1 = __esmMin((() => {
264119
264404
  * `CODEBASE_INDEX_SCRIPT` env override, then to a search from cwd.
264120
264405
  */
264121
264406
  function resolveScriptPath() {
264122
- const envPath = process.env["CODEBASE_INDEX_SCRIPT"];
264123
- if (envPath !== void 0 && envPath.length > 0) return envPath;
264124
- try {
264125
- return fileURLToPath(new URL("../../../../../codebase-index/codebase_index.py", import.meta.url));
264126
- } catch {}
264127
- return "";
264407
+ return resolveCodebaseIndexScript(import.meta.url, process.env["CODEBASE_INDEX_SCRIPT"]);
264128
264408
  }
264129
- var CodebaseSearchInputSchema, SCRIPT_TIMEOUT_MS, CodebaseSearchTool;
264409
+ var resolveCodebaseIndexScript, CodebaseSearchInputSchema, SCRIPT_TIMEOUT_MS, CodebaseSearchTool;
264130
264410
  var init_codebase_search = __esmMin((() => {
264131
264411
  init_zod$1();
264132
264412
  init_rule_match();
264133
264413
  init_input_schema();
264134
264414
  init_result_builder();
264135
264415
  init_codebase_search$1();
264416
+ ({ resolveCodebaseIndexScript } = createRequire(import.meta.url)("./bin/codebase-search-runtime.cjs"));
264136
264417
  CodebaseSearchInputSchema = object({
264137
264418
  query: string().min(1, "Query cannot be empty.").describe("A natural-language question or concept describing what the code does. Examples: \"keyboard event handling\", \"retry with exponential backoff\", \"where chat messages are routed to the model\"."),
264138
264419
  top: number$1().int().positive().default(5).describe("Maximum number of results to return. Defaults to 5.")
@@ -264828,6 +265109,7 @@ var init_builtin = __esmMin((() => {
264828
265109
  init_exit_plan_mode();
264829
265110
  init_bash();
264830
265111
  init_todo_list();
265112
+ init_programmatic_tool();
264831
265113
  init_fetch_url();
264832
265114
  init_web_search();
264833
265115
  init_codebase_search();
@@ -265272,6 +265554,7 @@ var init_tool$1 = __esmMin((() => {
265272
265554
  goalToolsEnabled && new UpdateGoalTool(this.agent),
265273
265555
  this.agent.rpc?.requestQuestion && new AskUserQuestionTool(this.agent),
265274
265556
  new TodoListTool(this.toolStore),
265557
+ new ProgrammaticTool(),
265275
265558
  new CompactConversationTool(this.agent),
265276
265559
  new TaskListTool(background),
265277
265560
  new TaskOutputTool(background),
@@ -265461,6 +265744,9 @@ var init_fallback_llm = __esmMin((() => {
265461
265744
  get capability() {
265462
265745
  return this.current.llm.capability;
265463
265746
  }
265747
+ get thinkingEffort() {
265748
+ return this.current.llm.thinkingEffort;
265749
+ }
265464
265750
  async prepareRequestBoundary(params, rebuildMessages) {
265465
265751
  params.signal.throwIfAborted();
265466
265752
  this.rebuildBoundaryMessages = rebuildMessages;
@@ -314793,7 +315079,17 @@ function resolveModelCapabilities(alias, provider) {
314793
315079
  };
314794
315080
  }
314795
315081
  function toKosongProviderConfig(provider, model, blunRequestHeaders, promptCacheKey, supportEfforts) {
314796
- assertBlunProviderType(provider);
315082
+ assertSupportedProviderType(provider);
315083
+ if (provider.type === FREDRIK_GLM_PROVIDER_TYPE) return {
315084
+ type: "blun",
315085
+ model,
315086
+ baseUrl: provider.baseUrl,
315087
+ apiKey: readFredrikGlmApiKeyFile(provider),
315088
+ generationKwargs: { prompt_cache_key: promptCacheKey },
315089
+ supportEfforts: [...FREDRIK_GLM_EFFORTS],
315090
+ openAICompatible: true,
315091
+ defaultHeaders: { "User-Agent": "BLUN-King/Fredrik-GLM" }
315092
+ };
314797
315093
  const envCustomHeaders = parseBlunCodeCustomHeaders();
314798
315094
  return {
314799
315095
  type: "blun",
@@ -314814,11 +315110,12 @@ function defaultHeadersField(headers) {
314814
315110
  return { defaultHeaders: { ...headers } };
314815
315111
  }
314816
315112
  function providerApiKey(provider) {
314817
- assertBlunProviderType(provider);
315113
+ assertSupportedProviderType(provider);
315114
+ if (provider.type === FREDRIK_GLM_PROVIDER_TYPE) return readFredrikGlmApiKeyFile(provider);
314818
315115
  return providerValue(provider.apiKey, provider.env, "BLUN_API_KEY");
314819
315116
  }
314820
- function assertBlunProviderType(provider) {
314821
- if (provider.type !== "blun") throw new BlunError(ErrorCodes.MODEL_CONFIG_INVALID, "Only the BLUN provider type is supported.");
315117
+ function assertSupportedProviderType(provider) {
315118
+ if (provider.type !== "blun" && provider.type !== FREDRIK_GLM_PROVIDER_TYPE) throw new BlunError(ErrorCodes.MODEL_CONFIG_INVALID, "Only approved BLUN Code provider types are supported.");
314822
315119
  }
314823
315120
  function providerValue(configured, env, envKey) {
314824
315121
  return nonEmptyString$2(configured) ?? envValue(env, envKey);
@@ -314856,7 +315153,9 @@ var init_provider_manager = __esmMin((() => {
314856
315153
  if (providerName === void 0) throw new BlunError(ErrorCodes.CONFIG_INVALID, `Model "${model}" must define a provider in config.toml.`);
314857
315154
  const providerConfig = this.config.providers[providerName];
314858
315155
  if (providerConfig === void 0) throw new BlunError(ErrorCodes.CONFIG_INVALID, `Provider "${providerName}" for model "${model}" is not configured.`);
314859
- assertBlunProviderType(providerConfig);
315156
+ assertSupportedProviderType(providerConfig);
315157
+ if (providerConfig.type === FREDRIK_GLM_PROVIDER_TYPE && (providerName !== FREDRIK_GLM_PROVIDER_ID || !isConfiguredFredrikGlmAlias(model, alias))) throw new BlunError(ErrorCodes.CONFIG_INVALID, "Fredrik GLM provider aliases must match the approved profile-local model catalog.");
315158
+ if (providerConfig.type !== FREDRIK_GLM_PROVIDER_TYPE && isConfiguredFredrikGlmAlias(model, alias)) throw new BlunError(ErrorCodes.CONFIG_INVALID, "Fredrik GLM aliases require the approved profile-local provider.");
314860
315159
  const runtimeContext = this.runtimeModelContexts.get(model);
314861
315160
  const maxContextSize = runtimeContext?.providerName === providerName && runtimeContext.model === alias.model ? runtimeContext.maxContextTokens : effectiveAlias.maxContextSize;
314862
315161
  if (!Number.isInteger(maxContextSize) || maxContextSize <= 0) throw new BlunError(ErrorCodes.CONFIG_INVALID, `Model "${model}" must define a positive max_context_size in config.toml.`);
@@ -314870,7 +315169,7 @@ var init_provider_manager = __esmMin((() => {
314870
315169
  }, provider),
314871
315170
  alwaysThinking: (effectiveAlias.capabilities ?? []).some((c) => c.trim().toLowerCase() === "always_thinking"),
314872
315171
  maxOutputSize: effectiveAlias.maxOutputSize,
314873
- type: "blun"
315172
+ type: providerConfig.type
314874
315173
  };
314875
315174
  }
314876
315175
  setRuntimeModelContext(input) {
@@ -314890,7 +315189,7 @@ var init_provider_manager = __esmMin((() => {
314890
315189
  resolveAuth(model, options) {
314891
315190
  const { providerName } = this.resolveProviderConfig(model);
314892
315191
  const providerConfig = this.config.providers[providerName];
314893
- const apiKey = providerConfig === void 0 ? void 0 : providerApiKey(providerConfig);
315192
+ const apiKey = providerConfig?.type === "blun" ? providerApiKey(providerConfig) : void 0;
314894
315193
  const revocationStore = this.options.managedApiKeyRevocationStore;
314895
315194
  if (providerName === MANAGED_PROVIDER_NAME$2 && providerConfig?.oauth === void 0 && apiKey !== void 0 && revocationStore !== void 0) return this.resolveManagedApiKeyAuth(providerName, apiKey, revocationStore, options?.log);
314896
315195
  if (providerConfig?.oauth === void 0) return void 0;
@@ -402988,7 +403287,7 @@ function createProgram(version, onMain, onPluginNodeRunner = () => {}) {
402988
403287
  "high",
402989
403288
  "xhigh",
402990
403289
  "max"
402991
- ])).addOption(new Option("--permission-mode <mode>", "Permission mode for this invocation (auto, manual, yolo).").choices([
403290
+ ])).option("--swarm", "Start in swarm mode.", false).addOption(new Option("--permission-mode <mode>", "Permission mode for this invocation (auto, manual, yolo).").choices([
402992
403291
  "auto",
402993
403292
  "manual",
402994
403293
  "yolo"
@@ -403022,6 +403321,7 @@ function createProgram(version, onMain, onPluginNodeRunner = () => {}) {
403022
403321
  plan: raw["plan"],
403023
403322
  model: raw["model"],
403024
403323
  effort: raw["effort"],
403324
+ swarm: raw["swarm"] === true,
403025
403325
  permissionMode: raw["permissionMode"],
403026
403326
  forkSession: raw["forkSession"],
403027
403327
  outputFormat: raw["outputFormat"],
@@ -403908,6 +404208,13 @@ const BUILTIN_SLASH_COMMAND_DEFINITIONS = [
403908
404208
  completeArgs: swarmArgumentCompletions,
403909
404209
  availability: "idle-only"
403910
404210
  },
404211
+ {
404212
+ name: "model",
404213
+ aliases: [],
404214
+ descriptionKey: "command.model.description",
404215
+ priority: 96,
404216
+ availability: "always"
404217
+ },
403911
404218
  {
403912
404219
  name: "effort",
403913
404220
  aliases: ["thinking"],
@@ -404359,7 +404666,6 @@ registerUiCatalogFragment({
404359
404666
  //#endregion
404360
404667
  //#region src/tui/commands/resolve.ts
404361
404668
  const LEGACY_EFFORT_COMMANDS = new Set([
404362
- "model",
404363
404669
  "automodel",
404364
404670
  "router"
404365
404671
  ]);
@@ -416599,7 +416905,7 @@ const EFFORT_STAGE_LABELS = {
416599
416905
  medium: "Deep",
416600
416906
  high: "Forge",
416601
416907
  xhigh: "Apex",
416602
- max: "Swarm"
416908
+ max: "Max"
416603
416909
  };
416604
416910
  const EFFORT_DISPLAY_LABEL_KEYS = {
416605
416911
  off: "effort.value.off",
@@ -416728,15 +417034,13 @@ var EffortSelectorComponent = class extends Container {
416728
417034
  }
416729
417035
  if (matchesKey(data, Key.alt("s")) && this.opts.onSessionOnlySelect !== void 0) {
416730
417036
  const effort = this.selectedEffort;
416731
- if (effort === "max" && this.usesLegacyServerStageScale && this.opts.onBlunSwarmSelect !== void 0) this.opts.onBlunSwarmSelect();
416732
- else if (effort !== void 0) this.opts.onSessionOnlySelect(effort);
417037
+ if (effort !== void 0) this.opts.onSessionOnlySelect(effort);
416733
417038
  return;
416734
417039
  }
416735
417040
  if (matchesKey(data, Key.enter)) if (this.isBlunSwarmSelected) this.opts.onBlunSwarmSelect?.();
416736
417041
  else {
416737
417042
  const effort = this.selectedEffort;
416738
- if (effort === "max" && this.usesLegacyServerStageScale && this.opts.onBlunSwarmSelect !== void 0) this.opts.onBlunSwarmSelect();
416739
- else if (effort !== void 0) this.opts.onSelect(effort);
417043
+ if (effort !== void 0) this.opts.onSelect(effort);
416740
417044
  }
416741
417045
  }
416742
417046
  render(width) {
@@ -416766,9 +417070,6 @@ var EffortSelectorComponent = class extends Container {
416766
417070
  get isBlunSwarmSelected() {
416767
417071
  return this.hasSeparatedBlunSwarm && this.activeIndex === this.efforts.length;
416768
417072
  }
416769
- get usesLegacyServerStageScale() {
416770
- return isServerStageScale(this.efforts);
416771
- }
416772
417073
  get hasSeparatedBlunSwarm() {
416773
417074
  return this.opts.onBlunSwarmSelect !== void 0 && !usesInlineStageScale(this.efforts);
416774
417075
  }
@@ -417401,6 +417702,168 @@ function segmentsFor(model) {
417401
417702
  if (availability === "unsupported") return ["off"];
417402
417703
  return ["off", "on"];
417403
417704
  }
417705
+ function fredrikSelectableModels(models) {
417706
+ return Object.fromEntries(Object.entries(models).filter(([alias]) => isAllowedFredrikRuntimeAlias(alias, models)));
417707
+ }
417708
+ function fredrikProviderDisplayName(provider) {
417709
+ if (provider === "managed:blun") return PRODUCT_NAME;
417710
+ if (provider.startsWith("managed:")) return provider.slice(8);
417711
+ return provider;
417712
+ }
417713
+ function fredrikCreateModelChoices(models) {
417714
+ return Object.entries(fredrikSelectableModels(models)).map(([alias, configured]) => {
417715
+ const model = effectiveModelAlias(configured);
417716
+ const name = modelDisplayName$1(alias, model);
417717
+ const provider = fredrikProviderDisplayName(model.provider);
417718
+ return {
417719
+ alias,
417720
+ model,
417721
+ name,
417722
+ provider,
417723
+ label: `${name} (${provider})`
417724
+ };
417725
+ });
417726
+ }
417727
+ function fredrikEffortLabel(effort) {
417728
+ const label = effortDisplayLabel(effort);
417729
+ if (label !== effort || effort.length === 0) return label;
417730
+ return effort.charAt(0).toUpperCase() + effort.slice(1);
417731
+ }
417732
+ function fredrikDefaultThinkingEffort(model) {
417733
+ if (thinkingAvailability(model) === "unsupported") return "off";
417734
+ const efforts = effortsOf(model);
417735
+ if (efforts.length > 0) return model.defaultEffort ?? efforts[Math.floor(efforts.length / 2)];
417736
+ return "on";
417737
+ }
417738
+ function fredrikCommitEffort(choice, draft) {
417739
+ if (draft === "on") return fredrikDefaultThinkingEffort(choice.model);
417740
+ return draft;
417741
+ }
417742
+ var FredrikModelSelectorComponent = class extends Container {
417743
+ focused = false;
417744
+ opts;
417745
+ list;
417746
+ thinkingOverrides = /* @__PURE__ */ new Map();
417747
+ ignoreSubmitTailUntil = Date.now() + 250;
417748
+ constructor(opts) {
417749
+ super();
417750
+ this.opts = opts;
417751
+ const choices = fredrikCreateModelChoices(opts.models);
417752
+ const selectedIndex = choices.findIndex((choice) => choice.alias === (opts.selectedValue ?? opts.currentValue));
417753
+ this.list = new SearchableList({
417754
+ items: choices,
417755
+ toSearchText: (choice) => choice.label,
417756
+ initialIndex: Math.max(selectedIndex, 0),
417757
+ searchable: opts.searchable === true
417758
+ });
417759
+ }
417760
+ draftFor(choice) {
417761
+ const override = this.thinkingOverrides.get(choice.alias);
417762
+ if (override !== void 0) return override;
417763
+ if (choice.alias === this.opts.currentValue) return this.opts.currentThinkingEffort;
417764
+ const efforts = effortsOf(choice.model);
417765
+ if (efforts.length > 0) {
417766
+ const fallback = choice.model.defaultEffort ?? efforts[Math.floor(efforts.length / 2)];
417767
+ if (fallback !== void 0 && efforts.includes(fallback)) return fallback;
417768
+ return efforts[0];
417769
+ }
417770
+ return thinkingAvailability(choice.model) !== "unsupported" ? "on" : "off";
417771
+ }
417772
+ effectiveEffort(choice) {
417773
+ const draft = this.draftFor(choice);
417774
+ const segments = segmentsFor(choice.model);
417775
+ return segments.includes(draft) ? draft : segments[0];
417776
+ }
417777
+ handleInput(data) {
417778
+ if (isKeyRelease(data)) return;
417779
+ if (Date.now() <= this.ignoreSubmitTailUntil && /^[\r\n]+$/.test(data)) return;
417780
+ this.ignoreSubmitTailUntil = 0;
417781
+ if (matchesKey(data, Key.escape)) {
417782
+ if (this.list.clearQuery()) return;
417783
+ this.opts.onCancel();
417784
+ return;
417785
+ }
417786
+ if (this.list.handleKey(data)) return;
417787
+ if (matchesKey(data, Key.left) || matchesKey(data, Key.right)) {
417788
+ const selected = this.selectedChoice();
417789
+ if (selected !== void 0) {
417790
+ const segments = segmentsFor(selected.model);
417791
+ if (segments.length > 1) {
417792
+ const current = this.effectiveEffort(selected);
417793
+ const index = segments.indexOf(current);
417794
+ const delta = matchesKey(data, Key.left) ? -1 : 1;
417795
+ const next = Math.max(0, Math.min(segments.length - 1, index + delta));
417796
+ if (next !== index) this.thinkingOverrides.set(selected.alias, segments[next]);
417797
+ }
417798
+ }
417799
+ return;
417800
+ }
417801
+ if (matchesKey(data, Key.enter)) {
417802
+ const selected = this.selectedChoice();
417803
+ if (selected === void 0) return;
417804
+ this.opts.onSelect({
417805
+ alias: selected.alias,
417806
+ thinking: fredrikCommitEffort(selected, this.effectiveEffort(selected))
417807
+ });
417808
+ return;
417809
+ }
417810
+ if (matchesKey(data, Key.alt("s")) && this.opts.onSessionOnlySelect !== void 0) {
417811
+ const selected = this.selectedChoice();
417812
+ if (selected === void 0) return;
417813
+ this.opts.onSessionOnlySelect({
417814
+ alias: selected.alias,
417815
+ thinking: fredrikCommitEffort(selected, this.effectiveEffort(selected))
417816
+ });
417817
+ }
417818
+ }
417819
+ render(width) {
417820
+ const view = this.list.view();
417821
+ const totalCount = Object.keys(fredrikSelectableModels(this.opts.models)).length;
417822
+ const titleSuffix = this.opts.searchable === true && view.query.length === 0 ? currentTheme.fg("textMuted", ` (${uiText("model.typeToSearch")})`) : "";
417823
+ const lines = [
417824
+ currentTheme.fg("primary", "-".repeat(width)),
417825
+ currentTheme.boldFg("primary", ` ${uiText("model.title")}`) + titleSuffix,
417826
+ currentTheme.fg("textMuted", ` ${uiText("model.navigate")} | ${uiText("model.thinking")} (Left/Right) | Enter ${uiText("model.select")} | Esc ${uiText("model.cancel")}`),
417827
+ ""
417828
+ ];
417829
+ if (view.query.length > 0) lines.push(currentTheme.fg("primary", ` ${uiText("model.search")}: `) + currentTheme.fg("text", view.query));
417830
+ if (view.items.length === 0) lines.push(currentTheme.fg("textMuted", ` ${uiText("model.noMatches")}`));
417831
+ for (let index = view.page.start; index < view.page.end; index++) {
417832
+ const choice = view.items[index];
417833
+ if (choice === void 0) continue;
417834
+ const selected = index === view.selectedIndex;
417835
+ const current = choice.alias === this.opts.currentValue;
417836
+ const pointer = selected ? ">" : " ";
417837
+ let line = currentTheme.fg(selected ? "primary" : "textDim", ` ${pointer} `);
417838
+ line += selected ? currentTheme.boldFg("primary", choice.name) : currentTheme.fg("text", choice.name);
417839
+ line += " " + currentTheme.fg("textMuted", choice.provider);
417840
+ if (current) line += " " + currentTheme.fg("success", currentMark());
417841
+ lines.push(line);
417842
+ }
417843
+ if (view.query.length > 0) lines.push("", currentTheme.fg("textMuted", ` ${String(view.items.length)} / ${String(totalCount)}`));
417844
+ lines.push("");
417845
+ const selected = this.selectedChoice();
417846
+ if (selected !== void 0) {
417847
+ lines.push(currentTheme.fg("textMuted", ` ${uiText("model.thinking")} (Left/Right ${uiText("model.toSwitch")})`));
417848
+ lines.push(this.renderThinkingControl(selected));
417849
+ }
417850
+ lines.push("", currentTheme.fg("primary", "-".repeat(width)));
417851
+ return lines.map((line) => truncateToWidth(line, width));
417852
+ }
417853
+ selectedChoice() {
417854
+ return this.list.selected();
417855
+ }
417856
+ renderThinkingControl(choice) {
417857
+ const segment = (label, active) => active ? currentTheme.boldFg("primary", `[ ${label} ]`) : currentTheme.fg("text", ` ${label} `);
417858
+ const unavailable = (label) => currentTheme.fg("textMuted", ` ${label} (${uiText("model.unsupported")}) `);
417859
+ const efforts = effortsOf(choice.model);
417860
+ const availability = thinkingAvailability(choice.model);
417861
+ if (efforts.length === 0 && availability === "always-on") return ` ${segment(uiText("model.on"), true)} ${unavailable(uiText("model.off"))}`;
417862
+ if (efforts.length === 0 && availability === "unsupported") return ` ${unavailable(uiText("model.on"))} ${segment(uiText("model.off"), true)}`;
417863
+ const active = this.effectiveEffort(choice);
417864
+ return ` ${segmentsFor(choice.model).map((effort) => segment(fredrikEffortLabel(effort), effort === active)).join(" ")}`;
417865
+ }
417866
+ };
417404
417867
  //#endregion
417405
417868
  //#region src/tui/components/dialogs/permission-selector.ts
417406
417869
  function permissionOptions() {
@@ -419762,7 +420225,7 @@ registerUiCatalogFragment({
419762
420225
  */
419763
420226
  var { projectUnaddressedTelegramContext } = createRequire(import.meta.url)("./bin/telegram-context-projection-policy.cjs");
419764
420227
  var { enqueueTelegramUrgent, rewriteTelegramUrgentEnvelope, telegramUrgentMessage } = createRequire(import.meta.url)("./bin/telegram-urgent-policy.cjs");
419765
- var { createDirectFocusController, createDirectReplyTurnStop, enqueueTelegramDirect, readDirectFocusCheckpoint, rewriteTelegramDirectEnvelope, telegramDirectMessage, writeDirectFocusCheckpoint } = createRequire(import.meta.url)("./bin/telegram-direct-focus-policy.cjs");
420228
+ var { createDirectFocusController, createDirectReplyTurnStop, directMessageAllowsWorkContinuation, enqueueTelegramDirect, readDirectFocusCheckpoint, rewriteTelegramDirectEnvelope, telegramDirectMessage, writeDirectFocusCheckpoint } = createRequire(import.meta.url)("./bin/telegram-direct-focus-policy.cjs");
419766
420229
  var { enqueueTelegramAddressed } = createRequire(import.meta.url)("./bin/telegram-addressed-priority.cjs");
419767
420230
  var { enqueueTelegramBotPriority, telegramBotPriorityMessage } = createRequire(import.meta.url)("./bin/telegram-bot-priority.cjs");
419768
420231
  var { createAddressedChannelFocusStore } = createRequire(import.meta.url)("./bin/telegram-addressed-focus.cjs");
@@ -421206,6 +421669,47 @@ async function handleThemeCommand(host, args) {
421206
421669
  }
421207
421670
  await applyThemeChoice(host, theme);
421208
421671
  }
421672
+ function resolveFredrikModelArgument(models, raw) {
421673
+ const query = raw.trim().toLowerCase();
421674
+ if (query.length === 0) return void 0;
421675
+ return Object.entries(models).filter(([alias]) => isAllowedFredrikRuntimeAlias(alias, models)).find(([alias, model]) => [alias, model.model, model.displayName].filter((value) => typeof value === "string").some((value) => value.toLowerCase() === query))?.[0];
421676
+ }
421677
+ function showFredrikModelPicker(host, selectedValue = host.state.appState.model) {
421678
+ const models = fredrikSelectableModels(host.state.appState.availableModels);
421679
+ host.mountEditorReplacement(new FredrikModelSelectorComponent({
421680
+ models,
421681
+ currentValue: host.state.appState.model,
421682
+ selectedValue,
421683
+ currentThinkingEffort: host.state.appState.thinkingEffort,
421684
+ searchable: true,
421685
+ onSelect: (selection) => {
421686
+ host.restoreEditorAfter(() => performModelSwitch(host, selection.alias, selection.thinking, false));
421687
+ },
421688
+ onSessionOnlySelect: (selection) => {
421689
+ host.restoreEditorAfter(() => performModelSwitch(host, selection.alias, selection.thinking, false));
421690
+ },
421691
+ onCancel: () => {
421692
+ host.restoreEditor();
421693
+ }
421694
+ }));
421695
+ }
421696
+ async function handleModelCommand(host, args) {
421697
+ if (host.session === void 0) {
421698
+ host.showError(noActiveSessionMessage());
421699
+ return;
421700
+ }
421701
+ const raw = args.trim();
421702
+ if (raw.length === 0) {
421703
+ showFredrikModelPicker(host);
421704
+ return;
421705
+ }
421706
+ const alias = resolveFredrikModelArgument(host.state.appState.availableModels, raw);
421707
+ if (alias === void 0) {
421708
+ host.showError(uiText("config.model.unknownAlias", { alias: raw }));
421709
+ return;
421710
+ }
421711
+ showFredrikModelPicker(host, alias);
421712
+ }
421209
421713
  async function handleEffortCommand(host, args) {
421210
421714
  const alias = host.state.appState.model;
421211
421715
  const model = host.state.appState.availableModels[alias];
@@ -421237,7 +421741,7 @@ async function handleEffortCommand(host, args) {
421237
421741
  }));
421238
421742
  return;
421239
421743
  }
421240
- await performEffortSelection(host, alias, arg, true);
421744
+ await performEffortSelection(host, alias, arg, !isConfiguredFredrikGlmAlias(alias, model));
421241
421745
  }
421242
421746
  function showEffortPicker(host, model, segments) {
421243
421747
  const liveEffort = host.state.appState.thinkingEffort;
@@ -421248,7 +421752,7 @@ function showEffortPicker(host, model, segments) {
421248
421752
  currentValue,
421249
421753
  currentBlunSwarm: host.state.appState.swarmMode && host.state.appState.swarmModeEntry === "effort",
421250
421754
  onSelect: (effort) => {
421251
- host.restoreEditorAfter(() => performEffortSelection(host, alias, effort, true));
421755
+ host.restoreEditorAfter(() => performEffortSelection(host, alias, effort, !isConfiguredFredrikGlmAlias(alias, host.state.appState.availableModels[alias])));
421252
421756
  },
421253
421757
  onSessionOnlySelect: (effort) => {
421254
421758
  host.restoreEditorAfter(() => performEffortSelection(host, alias, effort, false));
@@ -421433,7 +421937,7 @@ async function performEffortSelection(host, alias, effort, persist) {
421433
421937
  host.setAppState({
421434
421938
  thinkingEffort: effort,
421435
421939
  modelFallbackAllowed: false,
421436
- swarmMode: false,
421940
+ swarmMode: input.cliOptions.swarm === true,
421437
421941
  swarmModeEntry: void 0
421438
421942
  });
421439
421943
  renderSwarmModeMarker(host, "inactive");
@@ -423409,7 +423913,6 @@ async function handleGoalCommand(host, args) {
423409
423913
  }
423410
423914
  const IDEA_CONTRACT_MARKER = "Work as a self-directing employee:";
423411
423915
  const IDEA_ALLOWED_CHANNELS = Object.freeze([]);
423412
- const GOAL_TODO_REFRESH_WORK_CALL_LIMIT = 8;
423413
423916
  const GOAL_TODO_EVIDENCE_LIMIT = 8;
423414
423917
  const TODO_MAINTENANCE_RECOVERY_TOOL_NAMES = new Set([
423415
423918
  "TodoList",
@@ -423435,6 +423938,22 @@ function ideaTodos(agent) {
423435
423938
  const value = agent.tools.store?.["todo"];
423436
423939
  return Array.isArray(value) ? value : [];
423437
423940
  }
423941
+ function goalTodoProgressState(agent) {
423942
+ return agent.goalTodoPolicyState ??= {
423943
+ workCallsSinceRefresh: 0,
423944
+ refreshRequired: false,
423945
+ unchangedRefreshStreak: 0,
423946
+ evidenceRevision: 0,
423947
+ lastMaintenanceEvidenceRevision: 0
423948
+ };
423949
+ }
423950
+ function enforceTodoListBounds(agent, context) {
423951
+ if (context.toolCall.name !== "TodoList" || !Array.isArray(context.args?.todos)) return;
423952
+ return assessTodoListBounds({
423953
+ currentTodos: ideaTodos(agent),
423954
+ nextTodos: context.args.todos
423955
+ });
423956
+ }
423438
423957
  function validInitialIdeaPlan(value) {
423439
423958
  if (!Array.isArray(value) || value.length === 0) return false;
423440
423959
  let active = 0;
@@ -423471,28 +423990,21 @@ function goalTodoEvidenceLabel(toolName, args, isError) {
423471
423990
  function recordGoalTodoEvidence(agent, toolName, args, isError) {
423472
423991
  if (isIdeaGoal(agent) || agent.goal.getActiveGoal() === null) return;
423473
423992
  const name = String(toolName ?? "");
423474
- const progress = agent.goalTodoPolicyState ??= {
423475
- workCallsSinceRefresh: 0,
423476
- refreshRequired: false
423477
- };
423478
- if (name === "TodoList") {
423479
- if (isError !== true) progress.recentEvidence = [];
423480
- return;
423481
- }
423993
+ const progress = goalTodoProgressState(agent);
423994
+ if (name === "TodoList") return;
423482
423995
  if (name === "UpdateGoal" || TELEGRAM_DELIVERY_TOOL_RE.test(name)) return;
423483
423996
  const recentEvidence = Array.isArray(progress.recentEvidence) ? progress.recentEvidence : [];
423484
423997
  recentEvidence.push(goalTodoEvidenceLabel(name, args, isError));
423485
423998
  progress.recentEvidence = recentEvidence.slice(-GOAL_TODO_EVIDENCE_LIMIT);
423999
+ progress.evidenceRevision = (Number(progress.evidenceRevision) || 0) + 1;
423486
424000
  }
423487
424001
  function goalTodoMaintenanceMode(agent) {
423488
424002
  if (isIdeaGoal(agent)) return null;
423489
424003
  const todos = ideaTodos(agent);
423490
424004
  if (todos.length === 0) return agent.goal.getActiveGoal() === null ? null : "initial";
423491
- const progress = agent.goalTodoPolicyState ??= {
423492
- workCallsSinceRefresh: 0,
423493
- refreshRequired: false
423494
- };
423495
- if (!progress.refreshRequired && progress.workCallsSinceRefresh < GOAL_TODO_REFRESH_WORK_CALL_LIMIT) return null;
424005
+ const progress = goalTodoProgressState(agent);
424006
+ const refreshWorkCallLimit = todoRefreshWorkCallLimit(progress.unchangedRefreshStreak);
424007
+ if (!progress.refreshRequired && progress.workCallsSinceRefresh < refreshWorkCallLimit) return null;
423496
424008
  progress.refreshRequired = true;
423497
424009
  return "refresh";
423498
424010
  }
@@ -423506,8 +424018,9 @@ function goalTodoMaintenanceTools(eligibleTools, selectedTools) {
423506
424018
  }
423507
424019
  function buildGoalTodoMaintenanceSystemPrompt(agent, mode) {
423508
424020
  const todos = ideaTodos(agent);
424021
+ const requireConcreteProgress = mode === "refresh" && (agent.goalTodoPolicyState?.unchangedRefreshStreak ?? 0) >= 2;
423509
424022
  const visibleTodoList = todos.length === 0 ? "The visible TodoList is empty." : ["Current visible TodoList:", ...todos.map((todo) => `- [${String(todo?.status ?? "pending")}] ${String(todo?.title ?? "").trim()}`)].join("\n");
423510
- const action = mode === "initial" ? "Create the visible task-specific TodoList now. Keep exactly one item in_progress and every later item pending." : "Update the visible TodoList now, before any more task work. Mark only evidenced steps done, keep the actual current step in_progress, and leave later steps pending. If the current title is stale, refine it to the concrete verified substep.";
424023
+ const action = mode === "initial" ? "Create the visible task-specific TodoList now. Keep exactly one item in_progress and every later item pending." : requireConcreteProgress ? "Recent TodoList refreshes stayed semantically unchanged despite new work-tool evidence. Make a truthful concrete transition now: mark evidenced items done, refine the active title to the next verified substep, or record blocked, waiting_approval, or aborted when that is the real state. If the available evidence proves no boundary, preserve the list unchanged; the call remains executable." : "Update the visible TodoList now, before any more task work. Mark only evidenced steps done, keep the actual current step in_progress, and leave later steps pending. If the current title is stale, refine it to the concrete verified substep.";
423511
424024
  return `<todo-maintenance>
423512
424025
  TODO MAINTENANCE IS THE ONLY ACTION FOR THIS STEP.
423513
424026
  ${action}
@@ -423519,11 +424032,12 @@ ${visibleTodoList}
423519
424032
  function buildGoalTodoMaintenanceMessages(agent, mode) {
423520
424033
  const goal = agent.goal.getActiveGoal();
423521
424034
  const todos = ideaTodos(agent);
424035
+ const requireConcreteProgress = mode === "refresh" && (agent.goalTodoPolicyState?.unchangedRefreshStreak ?? 0) >= 2;
423522
424036
  const recentEvidence = Array.isArray(agent.goalTodoPolicyState?.recentEvidence) ? agent.goalTodoPolicyState.recentEvidence.slice(-GOAL_TODO_EVIDENCE_LIMIT) : [];
423523
424037
  const objective = String(goal?.objective ?? "Continue the current multi-step task.").trim();
423524
424038
  const visibleTodoList = todos.length === 0 ? "The visible TodoList is empty." : ["Current visible TodoList:", ...todos.map((todo) => `- [${String(todo?.status ?? "pending")}] ${String(todo?.title ?? "").trim()}`)].join("\n");
423525
424039
  const verifiedEvidence = recentEvidence.length === 0 ? "No new work-tool evidence is available." : ["Recent bounded work-tool evidence since the last accepted TodoList:", ...recentEvidence.map((entry) => `- ${entry}`)].join("\n");
423526
- const action = mode === "initial" ? "Create the task-specific TodoList from the active objective. Keep exactly one item in_progress and every later item pending." : "Refresh the TodoList from the current verified state. Mark finished work done, keep the actual current step in_progress, and leave later work pending. Completed items disappear automatically after this call.";
424040
+ const action = mode === "initial" ? "Create the task-specific TodoList from the active objective. Keep exactly one item in_progress and every later item pending." : requireConcreteProgress ? "The recent refreshes stayed semantically unchanged despite new evidence. Make a truthful concrete transition now: finish evidenced items, refine the active title to the next verified substep, or use blocked, waiting_approval, or aborted when accurate. If no boundary is proven, preserve the list unchanged; never invent progress." : "Refresh the TodoList from the current verified state. Mark finished work done, keep the actual current step in_progress, and leave later work pending. Completed items disappear automatically after this call.";
423527
424041
  const request = [
423528
424042
  "Return exactly one TodoList tool call and no prose.",
423529
424043
  action,
@@ -423542,10 +424056,7 @@ function enforceGoalTodoPolicy(agent, context) {
423542
424056
  if (TELEGRAM_DELIVERY_TOOL_RE.test(String(context.toolCall.name ?? ""))) return;
423543
424057
  const activeGoal = agent.goal.getActiveGoal();
423544
424058
  const todos = ideaTodos(agent);
423545
- const progress = agent.goalTodoPolicyState ??= {
423546
- workCallsSinceRefresh: 0,
423547
- refreshRequired: false
423548
- };
424059
+ const progress = goalTodoProgressState(agent);
423549
424060
  if (todos.length === 0) {
423550
424061
  const startsTrackedList = context.toolCall.name === "TodoList" && Array.isArray(context.args?.todos) && context.args.todos.length > 0;
423551
424062
  if (activeGoal === null && !startsTrackedList) return;
@@ -423561,6 +424072,9 @@ function enforceGoalTodoPolicy(agent, context) {
423561
424072
  };
423562
424073
  progress.workCallsSinceRefresh = 0;
423563
424074
  progress.refreshRequired = false;
424075
+ progress.unchangedRefreshStreak = 0;
424076
+ progress.recentEvidence = [];
424077
+ progress.lastMaintenanceEvidenceRevision = Number(progress.evidenceRevision) || 0;
423564
424078
  progress.allVisibleWorkCompleted = false;
423565
424079
  return;
423566
424080
  }
@@ -423584,9 +424098,26 @@ function enforceGoalTodoPolicy(agent, context) {
423584
424098
  reason: "A maintained TodoList must keep exactly one item in_progress while unfinished work remains."
423585
424099
  };
423586
424100
  }
424101
+ const evidenceRevision = Number(progress.evidenceRevision) || 0;
424102
+ const lastMaintenanceEvidenceRevision = Number(progress.lastMaintenanceEvidenceRevision) || 0;
424103
+ const maintenanceUpdate = assessTodoMaintenanceUpdate({
424104
+ currentTodos: todos,
424105
+ nextTodos,
424106
+ unchangedRefreshStreak: progress.unchangedRefreshStreak,
424107
+ recentEvidenceCount: evidenceRevision > lastMaintenanceEvidenceRevision ? (progress.recentEvidence?.length ?? 0) : 0
424108
+ });
423587
424109
  progress.allVisibleWorkCompleted = nextTodos.length > 0 && nextTodos.every((todo) => todo?.status === "done");
423588
424110
  progress.workCallsSinceRefresh = 0;
423589
424111
  progress.refreshRequired = false;
424112
+ progress.unchangedRefreshStreak = maintenanceUpdate.unchangedRefreshStreak;
424113
+ progress.lastMaintenanceEvidenceRevision = evidenceRevision;
424114
+ if (!maintenanceUpdate.preserveRecentEvidence) progress.recentEvidence = [];
424115
+ if (maintenanceUpdate.emitStalledWarning) agent.emitEvent({
424116
+ type: "warning",
424117
+ code: "todo-maintenance-stalled",
424118
+ message: "TodoList stayed semantically unchanged across three evidence-bearing maintenance cycles. Work tools remain available; the next maintenance check is backed off while the active step is refined.",
424119
+ blocked: false
424120
+ });
423590
424121
  return;
423591
424122
  }
423592
424123
  if (activeGoal !== null && context.toolCall.name === "UpdateGoal" && (context.args?.status === "complete" || context.args?.status === "blocked")) {
@@ -423602,7 +424133,7 @@ function enforceGoalTodoPolicy(agent, context) {
423602
424133
  }
423603
424134
  if (context.toolCall.id === context.toolCalls[0]?.id) {
423604
424135
  progress.workCallsSinceRefresh += 1;
423605
- if (progress.workCallsSinceRefresh > GOAL_TODO_REFRESH_WORK_CALL_LIMIT) progress.refreshRequired = true;
424136
+ if (progress.workCallsSinceRefresh > todoRefreshWorkCallLimit(progress.unchangedRefreshStreak)) progress.refreshRequired = true;
423606
424137
  }
423607
424138
  if (!progress.refreshRequired) return;
423608
424139
  return {
@@ -496923,6 +497454,9 @@ async function handleBuiltInSlashCommand(host, name, args) {
496923
497454
  case "theme":
496924
497455
  await handleThemeCommand(host, args);
496925
497456
  return;
497457
+ case "model":
497458
+ await handleModelCommand(host, args);
497459
+ return;
496926
497460
  case "effort":
496927
497461
  await handleEffortCommand(host, args);
496928
497462
  return;
@@ -503142,7 +503676,10 @@ function applyManagedAccountContext(models, maxContextTokens) {
503142
503676
  maxContextSize: maxContextTokens
503143
503677
  } }
503144
503678
  };
503145
- return { [BLUN_KING_MODEL_ALIAS]: king };
503679
+ return {
503680
+ [BLUN_KING_MODEL_ALIAS]: king,
503681
+ ...configuredFredrikGlmModels(models)
503682
+ };
503146
503683
  }
503147
503684
  function normalizeKingProductEffort(value) {
503148
503685
  if (value === "none" || value === "minimal" || value === "off") return "low";
@@ -503198,7 +503735,11 @@ function nextManagedQuotaWarning(windows, highestShown) {
503198
503735
  //#region src/tui/controllers/auth-flow.ts
503199
503736
  function managedProviderOnly(providers) {
503200
503737
  const managed = providers[DEFAULT_OAUTH_PROVIDER_NAME];
503201
- return managed === void 0 ? {} : { [DEFAULT_OAUTH_PROVIDER_NAME]: managed };
503738
+ const fredrikGlm = configuredFredrikGlmProvider(providers);
503739
+ return {
503740
+ ...managed === void 0 ? {} : { [DEFAULT_OAUTH_PROVIDER_NAME]: managed },
503741
+ ...fredrikGlm === void 0 ? {} : { [FREDRIK_GLM_PROVIDER_ID]: fredrikGlm }
503742
+ };
503202
503743
  }
503203
503744
  var AuthFlowController = class {
503204
503745
  host;
@@ -517611,6 +518152,8 @@ var BlunTUI = class {
517611
518152
  yolo: startupInput.cliOptions.yolo,
517612
518153
  auto: startupInput.cliOptions.auto,
517613
518154
  plan: startupInput.cliOptions.plan,
518155
+ effort: startupInput.cliOptions.effort,
518156
+ swarm: startupInput.cliOptions.swarm,
517614
518157
  model: startupInput.cliOptions.model,
517615
518158
  startupNotice: startupInput.startupNotice
517616
518159
  }
@@ -518027,6 +518570,7 @@ var BlunTUI = class {
518027
518570
  const createSessionOptions = {
518028
518571
  workDir,
518029
518572
  model: BLUN_KING_MODEL_ALIAS,
518573
+ thinking: startup.effort,
518030
518574
  permission: startup.auto ? "auto" : startup.yolo ? "yolo" : void 0,
518031
518575
  planMode: startup.plan ? true : void 0
518032
518576
  };
@@ -518073,7 +518617,7 @@ var BlunTUI = class {
518073
518617
  }
518074
518618
  }
518075
518619
  } else session = await this.harness.createSession(createSessionOptions);
518076
- if (session !== void 0 && shouldReplayHistory) await this.applyStartupModesToResumedSession(session);
518620
+ if (session !== void 0 && (shouldReplayHistory || startup.effort !== void 0 || startup.swarm)) await this.applyStartupModesToResumedSession(session);
518077
518621
  } catch (error) {
518078
518622
  if (!isOAuthLoginRequiredError(error)) throw error;
518079
518623
  this.authFlow.enterLoginRequiredStartupState();
@@ -518124,10 +518668,6 @@ var BlunTUI = class {
518124
518668
  const sessionId = session.id;
518125
518669
  const autostartPrompt = autostart.observation === void 0 ? GOAL_CONTINUATION_PROMPT : `${GOAL_CONTINUATION_PROMPT}\n\n${autostart.observation}`;
518126
518670
  this.beginSessionRequest();
518127
- this.setAppState({
518128
- model: BLUN_KING_MODEL_ALIAS,
518129
- modelFallbackAllowed: false
518130
- });
518131
518671
  try {
518132
518672
  const result = await session.promptAccepted(autostartPrompt);
518133
518673
  if (!result.accepted && this.session?.id === sessionId) {
@@ -519164,10 +519704,6 @@ var BlunTUI = class {
519164
519704
  contextOnly: false
519165
519705
  };
519166
519706
  this.pendingChannelReplyGuard = installedGuard;
519167
- this.setAppState({
519168
- model: BLUN_KING_MODEL_ALIAS,
519169
- modelFallbackAllowed: false
519170
- });
519171
519707
  session.promptAccepted(options?.parts ?? input).then((result) => {
519172
519708
  if (result.duplicate === true) {
519173
519709
  this.traceTelegramDelivery?.({ stage: "committed", ...context.item.channelDeliveryTrace, route: "channel_command_prompt", reason: "duplicate" });
@@ -519225,10 +519761,6 @@ var BlunTUI = class {
519225
519761
  };
519226
519762
  if (installedGuard !== void 0) this.pendingChannelReplyGuard = installedGuard;
519227
519763
  const imagePart = channelImagePath !== void 0 ? buildChannelImagePart(channelImagePath) : void 0;
519228
- this.setAppState({
519229
- model: BLUN_KING_MODEL_ALIAS,
519230
- modelFallbackAllowed: false
519231
- });
519232
519764
  const directNotice = channelDirect === true ? ["A private Telegram DM has priority over background, group, and loop work.", channelDirectResume === true ? "The user explicitly resumed work. Answer any remaining direct content, then continue the exact saved work checkpoint." : "The runtime saved the active work checkpoint. Answer the private conversation naturally, without exposing internal task, cron, checkpoint, queue, or lane narration. Unless the user explicitly asks to pause, stop, or wait, continue the exact saved work checkpoint automatically after the direct conversation."].join("\n") : void 0;
519233
519765
  const focusedModelInput = directNotice === void 0 ? modelInput : `${directNotice}\n\n${modelInput}`;
519234
519766
  const promptInput = imagePart !== void 0 && this.canReadImages() ? [{
@@ -519429,10 +519961,6 @@ var BlunTUI = class {
519429
519961
  return;
519430
519962
  }
519431
519963
  this.beginSessionRequest();
519432
- this.setAppState({
519433
- model: BLUN_KING_MODEL_ALIAS,
519434
- modelFallbackAllowed: false
519435
- });
519436
519964
  session.promptAccepted(TRUNCATED_REPLY_CONTINUATION_PROMPT).then((result) => {
519437
519965
  if (!result.accepted) this.failTruncatedChannelReplyRecovery(new Error("continuation prompt rejected"));
519438
519966
  }).catch((error) => {
@@ -519703,10 +520231,6 @@ var BlunTUI = class {
519703
520231
  });
519704
520232
  this.beginSessionRequest();
519705
520233
  const sdkInput = options?.parts ?? input;
519706
- this.setAppState({
519707
- model: BLUN_KING_MODEL_ALIAS,
519708
- modelFallbackAllowed: false
519709
- });
519710
520234
  session.promptAccepted(sdkInput).catch((error) => {
519711
520235
  finishPersonalMemoryRememberIntentTurn(session.id);
519712
520236
  const message = formatErrorMessage$2(error);
@@ -519821,18 +520345,19 @@ var BlunTUI = class {
519821
520345
  }
519822
520346
  }
519823
520347
  setAppState(patch) {
520348
+ const managedAccountContextTokens = "managedAccountContextTokens" in patch ? patch.managedAccountContextTokens : this.state.appState.managedAccountContextTokens;
520349
+ const availableModels = applyManagedAccountContext(patch.availableModels ?? this.state.appState.availableModels, isValidManagedContextTokens(managedAccountContextTokens) ? managedAccountContextTokens : void 0);
520350
+ const allowedModel = (candidate) => typeof candidate === "string" && isAllowedFredrikRuntimeAlias(candidate, availableModels) ? candidate : BLUN_KING_MODEL_ALIAS;
519824
520351
  let effectivePatch = {
519825
520352
  ...patch,
519826
520353
  modelFallbackAllowed: false,
519827
- ..."model" in patch && patch.model?.trim().length ? { model: BLUN_KING_MODEL_ALIAS } : {},
519828
- ..."activeResponderModel" in patch && patch.activeResponderModel != null ? { activeResponderModel: BLUN_KING_MODEL_ALIAS } : {}
520354
+ ..."model" in patch && patch.model?.trim().length ? { model: allowedModel(patch.model) } : {},
520355
+ ..."activeResponderModel" in patch && patch.activeResponderModel != null ? { activeResponderModel: allowedModel(patch.activeResponderModel) } : {}
519829
520356
  };
519830
520357
  if ("availableModels" in patch || "managedAccountContextTokens" in patch) {
519831
- const managedAccountContextTokens = "managedAccountContextTokens" in patch ? patch.managedAccountContextTokens : this.state.appState.managedAccountContextTokens;
519832
- const effectiveModels = applyManagedAccountContext(patch.availableModels ?? this.state.appState.availableModels, isValidManagedContextTokens(managedAccountContextTokens) ? managedAccountContextTokens : void 0);
519833
- if (effectiveModels !== patch.availableModels) effectivePatch = {
520358
+ if (availableModels !== patch.availableModels) effectivePatch = {
519834
520359
  ...effectivePatch,
519835
- availableModels: effectiveModels
520360
+ availableModels
519836
520361
  };
519837
520362
  }
519838
520363
  if (!hasPatchChanges(this.state.appState, effectivePatch)) return;
@@ -519909,7 +520434,11 @@ var BlunTUI = class {
519909
520434
  return this.harness.createSession(options);
519910
520435
  }
519911
520436
  async setSession(session, options = {}) {
519912
- await session.setModel(BLUN_KING_MODEL_ALIAS, { allowFallback: false });
520437
+ const resumedStatus = await session.getStatus();
520438
+ if (!isAllowedFredrikRuntimeAlias(resumedStatus.model ?? BLUN_KING_MODEL_ALIAS, this.state.appState.availableModels)) {
520439
+ await session.setModel(BLUN_KING_MODEL_ALIAS, { allowFallback: false });
520440
+ this.showStatus(uiText("config.model.unknownAlias", { alias: resumedStatus.model ?? "" }), "warning");
520441
+ }
519913
520442
  await this.personalMemoryController.clear(this.session);
519914
520443
  const previous = this.unloadCurrentSession(approvalCancellationFeedback("switching_session"));
519915
520444
  await this.managedQuotaWarningPersistence;
@@ -519917,10 +520446,7 @@ var BlunTUI = class {
519917
520446
  resetChannelPreambleState(this.channelPreamble);
519918
520447
  this.session = session;
519919
520448
  this.managedQuotaWarningController.restore(session.id, managedQuotaWarningThresholdFromMetadata(session.getResumeState()?.sessionMetadata?.custom));
519920
- this.setAppState({
519921
- model: BLUN_KING_MODEL_ALIAS,
519922
- modelFallbackAllowed: false
519923
- });
520449
+ this.setAppState({ modelFallbackAllowed: false });
519924
520450
  this.harness.setTelemetryContext({ sessionId: session.id });
519925
520451
  this.registerSessionHandlers(session);
519926
520452
  this.syncAdditionalDirs(session);
@@ -519932,7 +520458,7 @@ var BlunTUI = class {
519932
520458
  const [status, goalResult, loopResult] = await Promise.all([session.getStatus(), session.getGoal(), session.getLoop()]);
519933
520459
  this.setAppState({
519934
520460
  sessionId: session.id,
519935
- model: BLUN_KING_MODEL_ALIAS,
520461
+ model: status.model ?? BLUN_KING_MODEL_ALIAS,
519936
520462
  modelFallbackAllowed: false,
519937
520463
  activeResponderModel: null,
519938
520464
  thinkingEffort: status.thinkingEffort,
@@ -519966,6 +520492,8 @@ var BlunTUI = class {
519966
520492
  const { startup } = this.options;
519967
520493
  if (startup.auto) await session.setPermission("auto");
519968
520494
  else if (startup.yolo) await session.setPermission("yolo");
520495
+ if (startup.effort !== void 0) await session.setThinking(startup.effort);
520496
+ if (startup.swarm && !(await session.getStatus()).swarmMode) await session.setSwarmMode(true, "effort");
519969
520497
  if (startup.plan) {
519970
520498
  if (!(await session.getStatus()).planMode) await session.setPlanMode(true);
519971
520499
  }
@@ -519974,6 +520502,8 @@ var BlunTUI = class {
519974
520502
  const { startup } = this.options;
519975
520503
  if (startup.auto) this.setAppState({ permissionMode: "auto" });
519976
520504
  else if (startup.yolo) this.setAppState({ permissionMode: "yolo" });
520505
+ if (startup.effort !== void 0) this.setAppState({ thinkingEffort: startup.effort });
520506
+ if (startup.swarm) this.setAppState({ swarmMode: true, swarmModeEntry: "effort" });
519977
520507
  if (startup.plan) this.setAppState({ planMode: true });
519978
520508
  }
519979
520509
  async activateRuntime() {
@@ -520148,7 +520678,6 @@ var BlunTUI = class {
520148
520678
  this.approvalController.cancelAll(approvalCancellationFeedback("reloading_session"));
520149
520679
  this.questionController.cancelAll("");
520150
520680
  this.resetSessionRuntime();
520151
- await session.setModel(BLUN_KING_MODEL_ALIAS, { allowFallback: false });
520152
520681
  this.session = session;
520153
520682
  this.harness.setTelemetryContext({ sessionId: session.id });
520154
520683
  this.registerSessionHandlers(session);