blun-king-cli 9.1.526 → 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 (44) hide show
  1. package/CHANGELOG.md +67 -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 +709 -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 +25 -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-copy-command-regression.js +74 -0
  28. package/scripts/check-current-turn-read-pin-mutation-regression.js +72 -0
  29. package/scripts/check-current-turn-read-pin-regression.js +94 -0
  30. package/scripts/check-deepseek-native-max-regression.js +49 -0
  31. package/scripts/check-empty-response-effort-downgrade-regression.js +48 -0
  32. package/scripts/check-fredrik-glm-mutation-regression.js +18 -0
  33. package/scripts/check-fredrik-glm-regression.js +169 -0
  34. package/scripts/check-history-pressure-offload-regression.js +77 -0
  35. package/scripts/check-programmatic-context-isolation-regression.js +193 -0
  36. package/scripts/check-programmatic-tool-regression.js +294 -0
  37. package/scripts/check-resume-replay-regression.js +2 -0
  38. package/scripts/check-startup-swarm-command-regression.js +24 -0
  39. package/scripts/check-structured-subagent-output-regression.js +331 -0
  40. package/scripts/check-telegram-direct-work-resume-regression.js +53 -0
  41. package/scripts/check-todo-progress-regression.js +416 -0
  42. package/scripts/check-tool-schema-capacity-regression.js +40 -0
  43. package/scripts/programmatic-tool-runtime.test.mjs +365 -0
  44. 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"],
@@ -403290,6 +403590,10 @@ registerUiCatalogFragment({
403290
403590
  "command.telegramAccess.description": "Manage the Telegram bot token and allowed chats",
403291
403591
  "command.add-dir.description": "Add or list an additional workspace directory",
403292
403592
  "command.experiments.description": "Manage experimental features",
403593
+ "command.copy.description": "Copy the latest assistant response to the clipboard.",
403594
+ "copy.status.copied": "Copied the latest response to the clipboard.",
403595
+ "copy.status.none": "There is no response to copy yet.",
403596
+ "copy.error.failed": "Could not copy the latest response: {error}",
403293
403597
  "command.reload.description": "Reload the session and apply runtime and interface settings",
403294
403598
  "command.reload-tui.description": "Reload interface settings only",
403295
403599
  "command.compact.description": "Compact the conversation context",
@@ -403349,6 +403653,10 @@ registerUiCatalogFragment({
403349
403653
  "command.telegramAccess.description": "Telegram-Bot-Token und freigegebene Chats verwalten",
403350
403654
  "command.add-dir.description": "Zusätzlichen Arbeitsbereichsordner hinzufügen oder vorhandene auflisten",
403351
403655
  "command.experiments.description": "Experimentelle Funktionen verwalten",
403656
+ "command.copy.description": "Neueste Assistentenantwort in die Zwischenablage übernehmen.",
403657
+ "copy.status.copied": "Die neueste Antwort wurde in die Zwischenablage übernommen.",
403658
+ "copy.status.none": "Es ist noch keine Antwort zum Kopieren verfügbar.",
403659
+ "copy.error.failed": "Die neueste Antwort konnte nicht kopiert werden: {error}",
403352
403660
  "command.reload.description": "Sitzung neu laden und Runtime- sowie Oberflächeneinstellungen anwenden",
403353
403661
  "command.reload-tui.description": "Nur Oberflächeneinstellungen neu laden",
403354
403662
  "command.compact.description": "Konversationskontext komprimieren",
@@ -403408,6 +403716,10 @@ registerUiCatalogFragment({
403408
403716
  "command.telegramAccess.description": "Gestionar el token del bot de Telegram y los chats permitidos",
403409
403717
  "command.add-dir.description": "Añadir o listar directorios adicionales del espacio de trabajo",
403410
403718
  "command.experiments.description": "Gestionar funciones experimentales",
403719
+ "command.copy.description": "Copiar la respuesta más reciente del asistente al portapapeles.",
403720
+ "copy.status.copied": "La respuesta más reciente se copió al portapapeles.",
403721
+ "copy.status.none": "Todavía no hay ninguna respuesta que copiar.",
403722
+ "copy.error.failed": "No se pudo copiar la respuesta más reciente: {error}",
403411
403723
  "command.reload.description": "Recargar la sesión y aplicar los ajustes del entorno de ejecución y de la interfaz",
403412
403724
  "command.reload-tui.description": "Recargar solo los ajustes de la interfaz",
403413
403725
  "command.compact.description": "Compactar el contexto de la conversación",
@@ -403467,6 +403779,10 @@ registerUiCatalogFragment({
403467
403779
  "command.telegramAccess.description": "Gérer le jeton du bot Telegram et les discussions autorisées",
403468
403780
  "command.add-dir.description": "Ajouter ou répertorier un répertoire d’espace de travail supplémentaire",
403469
403781
  "command.experiments.description": "Gérer les fonctions expérimentales",
403782
+ "command.copy.description": "Copier la réponse la plus récente de l’assistant dans le presse-papiers.",
403783
+ "copy.status.copied": "La réponse la plus récente a été copiée dans le presse-papiers.",
403784
+ "copy.status.none": "Aucune réponse à copier pour le moment.",
403785
+ "copy.error.failed": "Impossible de copier la réponse la plus récente : {error}",
403470
403786
  "command.reload.description": "Recharger la session et appliquer les paramètres d’exécution et d’interface",
403471
403787
  "command.reload-tui.description": "Recharger uniquement les paramètres de l’interface",
403472
403788
  "command.compact.description": "Compacter le contexte de la conversation",
@@ -403526,6 +403842,10 @@ registerUiCatalogFragment({
403526
403842
  "command.telegramAccess.description": "Hantera Telegram-botens API-token och tillåtna chattar",
403527
403843
  "command.add-dir.description": "Lägg till eller lista en extra katalog för arbetsytan",
403528
403844
  "command.experiments.description": "Hantera experimentella funktioner",
403845
+ "command.copy.description": "Kopiera assistentens senaste svar till urklipp.",
403846
+ "copy.status.copied": "Det senaste svaret kopierades till urklipp.",
403847
+ "copy.status.none": "Det finns inget svar att kopiera ännu.",
403848
+ "copy.error.failed": "Det gick inte att kopiera det senaste svaret: {error}",
403529
403849
  "command.reload.description": "Läs in sessionen på nytt och tillämpa körningsmiljö- och gränssnittsinställningar",
403530
403850
  "command.reload-tui.description": "Läs bara in gränssnittsinställningarna på nytt",
403531
403851
  "command.compact.description": "Komprimera konversationskontexten",
@@ -403585,6 +403905,10 @@ registerUiCatalogFragment({
403585
403905
  "command.telegramAccess.description": "Spravovat token telegramového bota a povolené chaty",
403586
403906
  "command.add-dir.description": "Přidat nebo vypsat další adresář pracovního prostoru",
403587
403907
  "command.experiments.description": "Spravovat experimentální funkce",
403908
+ "command.copy.description": "Zkopírovat nejnovější odpověď asistenta do schránky.",
403909
+ "copy.status.copied": "Nejnovější odpověď byla zkopírována do schránky.",
403910
+ "copy.status.none": "Zatím není k dispozici žádná odpověď ke zkopírování.",
403911
+ "copy.error.failed": "Nejnovější odpověď se nepodařilo zkopírovat: {error}",
403588
403912
  "command.reload.description": "Znovu načíst relaci a použít nastavení běhového prostředí a rozhraní",
403589
403913
  "command.reload-tui.description": "Znovu načíst pouze nastavení rozhraní",
403590
403914
  "command.compact.description": "Komprimovat kontext konverzace",
@@ -403884,6 +404208,13 @@ const BUILTIN_SLASH_COMMAND_DEFINITIONS = [
403884
404208
  completeArgs: swarmArgumentCompletions,
403885
404209
  availability: "idle-only"
403886
404210
  },
404211
+ {
404212
+ name: "model",
404213
+ aliases: [],
404214
+ descriptionKey: "command.model.description",
404215
+ priority: 96,
404216
+ availability: "always"
404217
+ },
403887
404218
  {
403888
404219
  name: "effort",
403889
404220
  aliases: ["thinking"],
@@ -403976,6 +404307,13 @@ const BUILTIN_SLASH_COMMAND_DEFINITIONS = [
403976
404307
  priority: 60,
403977
404308
  availability: "idle-only"
403978
404309
  },
404310
+ {
404311
+ name: "copy",
404312
+ aliases: [],
404313
+ descriptionKey: "command.copy.description",
404314
+ priority: 40,
404315
+ availability: "always"
404316
+ },
403979
404317
  {
403980
404318
  name: "reload",
403981
404319
  aliases: [],
@@ -404328,7 +404666,6 @@ registerUiCatalogFragment({
404328
404666
  //#endregion
404329
404667
  //#region src/tui/commands/resolve.ts
404330
404668
  const LEGACY_EFFORT_COMMANDS = new Set([
404331
- "model",
404332
404669
  "automodel",
404333
404670
  "router"
404334
404671
  ]);
@@ -416568,7 +416905,7 @@ const EFFORT_STAGE_LABELS = {
416568
416905
  medium: "Deep",
416569
416906
  high: "Forge",
416570
416907
  xhigh: "Apex",
416571
- max: "Swarm"
416908
+ max: "Max"
416572
416909
  };
416573
416910
  const EFFORT_DISPLAY_LABEL_KEYS = {
416574
416911
  off: "effort.value.off",
@@ -416697,15 +417034,13 @@ var EffortSelectorComponent = class extends Container {
416697
417034
  }
416698
417035
  if (matchesKey(data, Key.alt("s")) && this.opts.onSessionOnlySelect !== void 0) {
416699
417036
  const effort = this.selectedEffort;
416700
- if (effort === "max" && this.usesLegacyServerStageScale && this.opts.onBlunSwarmSelect !== void 0) this.opts.onBlunSwarmSelect();
416701
- else if (effort !== void 0) this.opts.onSessionOnlySelect(effort);
417037
+ if (effort !== void 0) this.opts.onSessionOnlySelect(effort);
416702
417038
  return;
416703
417039
  }
416704
417040
  if (matchesKey(data, Key.enter)) if (this.isBlunSwarmSelected) this.opts.onBlunSwarmSelect?.();
416705
417041
  else {
416706
417042
  const effort = this.selectedEffort;
416707
- if (effort === "max" && this.usesLegacyServerStageScale && this.opts.onBlunSwarmSelect !== void 0) this.opts.onBlunSwarmSelect();
416708
- else if (effort !== void 0) this.opts.onSelect(effort);
417043
+ if (effort !== void 0) this.opts.onSelect(effort);
416709
417044
  }
416710
417045
  }
416711
417046
  render(width) {
@@ -416735,9 +417070,6 @@ var EffortSelectorComponent = class extends Container {
416735
417070
  get isBlunSwarmSelected() {
416736
417071
  return this.hasSeparatedBlunSwarm && this.activeIndex === this.efforts.length;
416737
417072
  }
416738
- get usesLegacyServerStageScale() {
416739
- return isServerStageScale(this.efforts);
416740
- }
416741
417073
  get hasSeparatedBlunSwarm() {
416742
417074
  return this.opts.onBlunSwarmSelect !== void 0 && !usesInlineStageScale(this.efforts);
416743
417075
  }
@@ -417370,6 +417702,168 @@ function segmentsFor(model) {
417370
417702
  if (availability === "unsupported") return ["off"];
417371
417703
  return ["off", "on"];
417372
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
+ };
417373
417867
  //#endregion
417374
417868
  //#region src/tui/components/dialogs/permission-selector.ts
417375
417869
  function permissionOptions() {
@@ -419731,7 +420225,7 @@ registerUiCatalogFragment({
419731
420225
  */
419732
420226
  var { projectUnaddressedTelegramContext } = createRequire(import.meta.url)("./bin/telegram-context-projection-policy.cjs");
419733
420227
  var { enqueueTelegramUrgent, rewriteTelegramUrgentEnvelope, telegramUrgentMessage } = createRequire(import.meta.url)("./bin/telegram-urgent-policy.cjs");
419734
- 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");
419735
420229
  var { enqueueTelegramAddressed } = createRequire(import.meta.url)("./bin/telegram-addressed-priority.cjs");
419736
420230
  var { enqueueTelegramBotPriority, telegramBotPriorityMessage } = createRequire(import.meta.url)("./bin/telegram-bot-priority.cjs");
419737
420231
  var { createAddressedChannelFocusStore } = createRequire(import.meta.url)("./bin/telegram-addressed-focus.cjs");
@@ -421175,6 +421669,47 @@ async function handleThemeCommand(host, args) {
421175
421669
  }
421176
421670
  await applyThemeChoice(host, theme);
421177
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
+ }
421178
421713
  async function handleEffortCommand(host, args) {
421179
421714
  const alias = host.state.appState.model;
421180
421715
  const model = host.state.appState.availableModels[alias];
@@ -421206,7 +421741,7 @@ async function handleEffortCommand(host, args) {
421206
421741
  }));
421207
421742
  return;
421208
421743
  }
421209
- await performEffortSelection(host, alias, arg, true);
421744
+ await performEffortSelection(host, alias, arg, !isConfiguredFredrikGlmAlias(alias, model));
421210
421745
  }
421211
421746
  function showEffortPicker(host, model, segments) {
421212
421747
  const liveEffort = host.state.appState.thinkingEffort;
@@ -421217,7 +421752,7 @@ function showEffortPicker(host, model, segments) {
421217
421752
  currentValue,
421218
421753
  currentBlunSwarm: host.state.appState.swarmMode && host.state.appState.swarmModeEntry === "effort",
421219
421754
  onSelect: (effort) => {
421220
- host.restoreEditorAfter(() => performEffortSelection(host, alias, effort, true));
421755
+ host.restoreEditorAfter(() => performEffortSelection(host, alias, effort, !isConfiguredFredrikGlmAlias(alias, host.state.appState.availableModels[alias])));
421221
421756
  },
421222
421757
  onSessionOnlySelect: (effort) => {
421223
421758
  host.restoreEditorAfter(() => performEffortSelection(host, alias, effort, false));
@@ -421402,7 +421937,7 @@ async function performEffortSelection(host, alias, effort, persist) {
421402
421937
  host.setAppState({
421403
421938
  thinkingEffort: effort,
421404
421939
  modelFallbackAllowed: false,
421405
- swarmMode: false,
421940
+ swarmMode: input.cliOptions.swarm === true,
421406
421941
  swarmModeEntry: void 0
421407
421942
  });
421408
421943
  renderSwarmModeMarker(host, "inactive");
@@ -423378,7 +423913,6 @@ async function handleGoalCommand(host, args) {
423378
423913
  }
423379
423914
  const IDEA_CONTRACT_MARKER = "Work as a self-directing employee:";
423380
423915
  const IDEA_ALLOWED_CHANNELS = Object.freeze([]);
423381
- const GOAL_TODO_REFRESH_WORK_CALL_LIMIT = 8;
423382
423916
  const GOAL_TODO_EVIDENCE_LIMIT = 8;
423383
423917
  const TODO_MAINTENANCE_RECOVERY_TOOL_NAMES = new Set([
423384
423918
  "TodoList",
@@ -423404,6 +423938,22 @@ function ideaTodos(agent) {
423404
423938
  const value = agent.tools.store?.["todo"];
423405
423939
  return Array.isArray(value) ? value : [];
423406
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
+ }
423407
423957
  function validInitialIdeaPlan(value) {
423408
423958
  if (!Array.isArray(value) || value.length === 0) return false;
423409
423959
  let active = 0;
@@ -423440,28 +423990,21 @@ function goalTodoEvidenceLabel(toolName, args, isError) {
423440
423990
  function recordGoalTodoEvidence(agent, toolName, args, isError) {
423441
423991
  if (isIdeaGoal(agent) || agent.goal.getActiveGoal() === null) return;
423442
423992
  const name = String(toolName ?? "");
423443
- const progress = agent.goalTodoPolicyState ??= {
423444
- workCallsSinceRefresh: 0,
423445
- refreshRequired: false
423446
- };
423447
- if (name === "TodoList") {
423448
- if (isError !== true) progress.recentEvidence = [];
423449
- return;
423450
- }
423993
+ const progress = goalTodoProgressState(agent);
423994
+ if (name === "TodoList") return;
423451
423995
  if (name === "UpdateGoal" || TELEGRAM_DELIVERY_TOOL_RE.test(name)) return;
423452
423996
  const recentEvidence = Array.isArray(progress.recentEvidence) ? progress.recentEvidence : [];
423453
423997
  recentEvidence.push(goalTodoEvidenceLabel(name, args, isError));
423454
423998
  progress.recentEvidence = recentEvidence.slice(-GOAL_TODO_EVIDENCE_LIMIT);
423999
+ progress.evidenceRevision = (Number(progress.evidenceRevision) || 0) + 1;
423455
424000
  }
423456
424001
  function goalTodoMaintenanceMode(agent) {
423457
424002
  if (isIdeaGoal(agent)) return null;
423458
424003
  const todos = ideaTodos(agent);
423459
424004
  if (todos.length === 0) return agent.goal.getActiveGoal() === null ? null : "initial";
423460
- const progress = agent.goalTodoPolicyState ??= {
423461
- workCallsSinceRefresh: 0,
423462
- refreshRequired: false
423463
- };
423464
- 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;
423465
424008
  progress.refreshRequired = true;
423466
424009
  return "refresh";
423467
424010
  }
@@ -423475,8 +424018,9 @@ function goalTodoMaintenanceTools(eligibleTools, selectedTools) {
423475
424018
  }
423476
424019
  function buildGoalTodoMaintenanceSystemPrompt(agent, mode) {
423477
424020
  const todos = ideaTodos(agent);
424021
+ const requireConcreteProgress = mode === "refresh" && (agent.goalTodoPolicyState?.unchangedRefreshStreak ?? 0) >= 2;
423478
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");
423479
- 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.";
423480
424024
  return `<todo-maintenance>
423481
424025
  TODO MAINTENANCE IS THE ONLY ACTION FOR THIS STEP.
423482
424026
  ${action}
@@ -423488,11 +424032,12 @@ ${visibleTodoList}
423488
424032
  function buildGoalTodoMaintenanceMessages(agent, mode) {
423489
424033
  const goal = agent.goal.getActiveGoal();
423490
424034
  const todos = ideaTodos(agent);
424035
+ const requireConcreteProgress = mode === "refresh" && (agent.goalTodoPolicyState?.unchangedRefreshStreak ?? 0) >= 2;
423491
424036
  const recentEvidence = Array.isArray(agent.goalTodoPolicyState?.recentEvidence) ? agent.goalTodoPolicyState.recentEvidence.slice(-GOAL_TODO_EVIDENCE_LIMIT) : [];
423492
424037
  const objective = String(goal?.objective ?? "Continue the current multi-step task.").trim();
423493
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");
423494
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");
423495
- 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.";
423496
424041
  const request = [
423497
424042
  "Return exactly one TodoList tool call and no prose.",
423498
424043
  action,
@@ -423511,10 +424056,7 @@ function enforceGoalTodoPolicy(agent, context) {
423511
424056
  if (TELEGRAM_DELIVERY_TOOL_RE.test(String(context.toolCall.name ?? ""))) return;
423512
424057
  const activeGoal = agent.goal.getActiveGoal();
423513
424058
  const todos = ideaTodos(agent);
423514
- const progress = agent.goalTodoPolicyState ??= {
423515
- workCallsSinceRefresh: 0,
423516
- refreshRequired: false
423517
- };
424059
+ const progress = goalTodoProgressState(agent);
423518
424060
  if (todos.length === 0) {
423519
424061
  const startsTrackedList = context.toolCall.name === "TodoList" && Array.isArray(context.args?.todos) && context.args.todos.length > 0;
423520
424062
  if (activeGoal === null && !startsTrackedList) return;
@@ -423530,6 +424072,9 @@ function enforceGoalTodoPolicy(agent, context) {
423530
424072
  };
423531
424073
  progress.workCallsSinceRefresh = 0;
423532
424074
  progress.refreshRequired = false;
424075
+ progress.unchangedRefreshStreak = 0;
424076
+ progress.recentEvidence = [];
424077
+ progress.lastMaintenanceEvidenceRevision = Number(progress.evidenceRevision) || 0;
423533
424078
  progress.allVisibleWorkCompleted = false;
423534
424079
  return;
423535
424080
  }
@@ -423553,9 +424098,26 @@ function enforceGoalTodoPolicy(agent, context) {
423553
424098
  reason: "A maintained TodoList must keep exactly one item in_progress while unfinished work remains."
423554
424099
  };
423555
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
+ });
423556
424109
  progress.allVisibleWorkCompleted = nextTodos.length > 0 && nextTodos.every((todo) => todo?.status === "done");
423557
424110
  progress.workCallsSinceRefresh = 0;
423558
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
+ });
423559
424121
  return;
423560
424122
  }
423561
424123
  if (activeGoal !== null && context.toolCall.name === "UpdateGoal" && (context.args?.status === "complete" || context.args?.status === "blocked")) {
@@ -423571,7 +424133,7 @@ function enforceGoalTodoPolicy(agent, context) {
423571
424133
  }
423572
424134
  if (context.toolCall.id === context.toolCalls[0]?.id) {
423573
424135
  progress.workCallsSinceRefresh += 1;
423574
- if (progress.workCallsSinceRefresh > GOAL_TODO_REFRESH_WORK_CALL_LIMIT) progress.refreshRequired = true;
424136
+ if (progress.workCallsSinceRefresh > todoRefreshWorkCallLimit(progress.unchangedRefreshStreak)) progress.refreshRequired = true;
423575
424137
  }
423576
424138
  if (!progress.refreshRequired) return;
423577
424139
  return {
@@ -496816,6 +497378,25 @@ async function handlePremortemCommand(host, args) {
496816
497378
  }
496817
497379
  host.sendMessage(host.requireSession(), plan, { parts: buildPremortemPrompt(plan) });
496818
497380
  }
497381
+ function findLatestAssistantResponse(entries) {
497382
+ for (let index = entries.length - 1; index >= 0; index--) {
497383
+ const entry = entries[index];
497384
+ if (entry?.kind === "assistant" && typeof entry.content === "string" && entry.content.trim().length > 0) return entry.content;
497385
+ }
497386
+ }
497387
+ async function handleCopyCommand(host) {
497388
+ const text = findLatestAssistantResponse(host.state.transcriptEntries);
497389
+ if (text === void 0) {
497390
+ host.showStatus(uiText("copy.status.none"));
497391
+ return;
497392
+ }
497393
+ try {
497394
+ await copyTextToClipboard(text);
497395
+ host.showStatus(uiText("copy.status.copied"));
497396
+ } catch (error) {
497397
+ host.showError(uiText("copy.error.failed", { error: formatErrorMessage$2(error) }));
497398
+ }
497399
+ }
496819
497400
  async function handleBuiltInSlashCommand(host, name, args) {
496820
497401
  switch (name) {
496821
497402
  case "exit":
@@ -496858,6 +497439,9 @@ async function handleBuiltInSlashCommand(host, name, args) {
496858
497439
  case "experiments":
496859
497440
  await showExperimentsPanel(host);
496860
497441
  return;
497442
+ case "copy":
497443
+ await handleCopyCommand(host);
497444
+ return;
496861
497445
  case "reload":
496862
497446
  await handleReloadCommand(host);
496863
497447
  return;
@@ -496870,6 +497454,9 @@ async function handleBuiltInSlashCommand(host, name, args) {
496870
497454
  case "theme":
496871
497455
  await handleThemeCommand(host, args);
496872
497456
  return;
497457
+ case "model":
497458
+ await handleModelCommand(host, args);
497459
+ return;
496873
497460
  case "effort":
496874
497461
  await handleEffortCommand(host, args);
496875
497462
  return;
@@ -503089,7 +503676,10 @@ function applyManagedAccountContext(models, maxContextTokens) {
503089
503676
  maxContextSize: maxContextTokens
503090
503677
  } }
503091
503678
  };
503092
- return { [BLUN_KING_MODEL_ALIAS]: king };
503679
+ return {
503680
+ [BLUN_KING_MODEL_ALIAS]: king,
503681
+ ...configuredFredrikGlmModels(models)
503682
+ };
503093
503683
  }
503094
503684
  function normalizeKingProductEffort(value) {
503095
503685
  if (value === "none" || value === "minimal" || value === "off") return "low";
@@ -503145,7 +503735,11 @@ function nextManagedQuotaWarning(windows, highestShown) {
503145
503735
  //#region src/tui/controllers/auth-flow.ts
503146
503736
  function managedProviderOnly(providers) {
503147
503737
  const managed = providers[DEFAULT_OAUTH_PROVIDER_NAME];
503148
- 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
+ };
503149
503743
  }
503150
503744
  var AuthFlowController = class {
503151
503745
  host;
@@ -517558,6 +518152,8 @@ var BlunTUI = class {
517558
518152
  yolo: startupInput.cliOptions.yolo,
517559
518153
  auto: startupInput.cliOptions.auto,
517560
518154
  plan: startupInput.cliOptions.plan,
518155
+ effort: startupInput.cliOptions.effort,
518156
+ swarm: startupInput.cliOptions.swarm,
517561
518157
  model: startupInput.cliOptions.model,
517562
518158
  startupNotice: startupInput.startupNotice
517563
518159
  }
@@ -517974,6 +518570,7 @@ var BlunTUI = class {
517974
518570
  const createSessionOptions = {
517975
518571
  workDir,
517976
518572
  model: BLUN_KING_MODEL_ALIAS,
518573
+ thinking: startup.effort,
517977
518574
  permission: startup.auto ? "auto" : startup.yolo ? "yolo" : void 0,
517978
518575
  planMode: startup.plan ? true : void 0
517979
518576
  };
@@ -518020,7 +518617,7 @@ var BlunTUI = class {
518020
518617
  }
518021
518618
  }
518022
518619
  } else session = await this.harness.createSession(createSessionOptions);
518023
- 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);
518024
518621
  } catch (error) {
518025
518622
  if (!isOAuthLoginRequiredError(error)) throw error;
518026
518623
  this.authFlow.enterLoginRequiredStartupState();
@@ -518071,10 +518668,6 @@ var BlunTUI = class {
518071
518668
  const sessionId = session.id;
518072
518669
  const autostartPrompt = autostart.observation === void 0 ? GOAL_CONTINUATION_PROMPT : `${GOAL_CONTINUATION_PROMPT}\n\n${autostart.observation}`;
518073
518670
  this.beginSessionRequest();
518074
- this.setAppState({
518075
- model: BLUN_KING_MODEL_ALIAS,
518076
- modelFallbackAllowed: false
518077
- });
518078
518671
  try {
518079
518672
  const result = await session.promptAccepted(autostartPrompt);
518080
518673
  if (!result.accepted && this.session?.id === sessionId) {
@@ -519111,10 +519704,6 @@ var BlunTUI = class {
519111
519704
  contextOnly: false
519112
519705
  };
519113
519706
  this.pendingChannelReplyGuard = installedGuard;
519114
- this.setAppState({
519115
- model: BLUN_KING_MODEL_ALIAS,
519116
- modelFallbackAllowed: false
519117
- });
519118
519707
  session.promptAccepted(options?.parts ?? input).then((result) => {
519119
519708
  if (result.duplicate === true) {
519120
519709
  this.traceTelegramDelivery?.({ stage: "committed", ...context.item.channelDeliveryTrace, route: "channel_command_prompt", reason: "duplicate" });
@@ -519172,10 +519761,6 @@ var BlunTUI = class {
519172
519761
  };
519173
519762
  if (installedGuard !== void 0) this.pendingChannelReplyGuard = installedGuard;
519174
519763
  const imagePart = channelImagePath !== void 0 ? buildChannelImagePart(channelImagePath) : void 0;
519175
- this.setAppState({
519176
- model: BLUN_KING_MODEL_ALIAS,
519177
- modelFallbackAllowed: false
519178
- });
519179
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;
519180
519765
  const focusedModelInput = directNotice === void 0 ? modelInput : `${directNotice}\n\n${modelInput}`;
519181
519766
  const promptInput = imagePart !== void 0 && this.canReadImages() ? [{
@@ -519376,10 +519961,6 @@ var BlunTUI = class {
519376
519961
  return;
519377
519962
  }
519378
519963
  this.beginSessionRequest();
519379
- this.setAppState({
519380
- model: BLUN_KING_MODEL_ALIAS,
519381
- modelFallbackAllowed: false
519382
- });
519383
519964
  session.promptAccepted(TRUNCATED_REPLY_CONTINUATION_PROMPT).then((result) => {
519384
519965
  if (!result.accepted) this.failTruncatedChannelReplyRecovery(new Error("continuation prompt rejected"));
519385
519966
  }).catch((error) => {
@@ -519650,10 +520231,6 @@ var BlunTUI = class {
519650
520231
  });
519651
520232
  this.beginSessionRequest();
519652
520233
  const sdkInput = options?.parts ?? input;
519653
- this.setAppState({
519654
- model: BLUN_KING_MODEL_ALIAS,
519655
- modelFallbackAllowed: false
519656
- });
519657
520234
  session.promptAccepted(sdkInput).catch((error) => {
519658
520235
  finishPersonalMemoryRememberIntentTurn(session.id);
519659
520236
  const message = formatErrorMessage$2(error);
@@ -519768,18 +520345,19 @@ var BlunTUI = class {
519768
520345
  }
519769
520346
  }
519770
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;
519771
520351
  let effectivePatch = {
519772
520352
  ...patch,
519773
520353
  modelFallbackAllowed: false,
519774
- ..."model" in patch && patch.model?.trim().length ? { model: BLUN_KING_MODEL_ALIAS } : {},
519775
- ..."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) } : {}
519776
520356
  };
519777
520357
  if ("availableModels" in patch || "managedAccountContextTokens" in patch) {
519778
- const managedAccountContextTokens = "managedAccountContextTokens" in patch ? patch.managedAccountContextTokens : this.state.appState.managedAccountContextTokens;
519779
- const effectiveModels = applyManagedAccountContext(patch.availableModels ?? this.state.appState.availableModels, isValidManagedContextTokens(managedAccountContextTokens) ? managedAccountContextTokens : void 0);
519780
- if (effectiveModels !== patch.availableModels) effectivePatch = {
520358
+ if (availableModels !== patch.availableModels) effectivePatch = {
519781
520359
  ...effectivePatch,
519782
- availableModels: effectiveModels
520360
+ availableModels
519783
520361
  };
519784
520362
  }
519785
520363
  if (!hasPatchChanges(this.state.appState, effectivePatch)) return;
@@ -519856,7 +520434,11 @@ var BlunTUI = class {
519856
520434
  return this.harness.createSession(options);
519857
520435
  }
519858
520436
  async setSession(session, options = {}) {
519859
- 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
+ }
519860
520442
  await this.personalMemoryController.clear(this.session);
519861
520443
  const previous = this.unloadCurrentSession(approvalCancellationFeedback("switching_session"));
519862
520444
  await this.managedQuotaWarningPersistence;
@@ -519864,10 +520446,7 @@ var BlunTUI = class {
519864
520446
  resetChannelPreambleState(this.channelPreamble);
519865
520447
  this.session = session;
519866
520448
  this.managedQuotaWarningController.restore(session.id, managedQuotaWarningThresholdFromMetadata(session.getResumeState()?.sessionMetadata?.custom));
519867
- this.setAppState({
519868
- model: BLUN_KING_MODEL_ALIAS,
519869
- modelFallbackAllowed: false
519870
- });
520449
+ this.setAppState({ modelFallbackAllowed: false });
519871
520450
  this.harness.setTelemetryContext({ sessionId: session.id });
519872
520451
  this.registerSessionHandlers(session);
519873
520452
  this.syncAdditionalDirs(session);
@@ -519879,7 +520458,7 @@ var BlunTUI = class {
519879
520458
  const [status, goalResult, loopResult] = await Promise.all([session.getStatus(), session.getGoal(), session.getLoop()]);
519880
520459
  this.setAppState({
519881
520460
  sessionId: session.id,
519882
- model: BLUN_KING_MODEL_ALIAS,
520461
+ model: status.model ?? BLUN_KING_MODEL_ALIAS,
519883
520462
  modelFallbackAllowed: false,
519884
520463
  activeResponderModel: null,
519885
520464
  thinkingEffort: status.thinkingEffort,
@@ -519913,6 +520492,8 @@ var BlunTUI = class {
519913
520492
  const { startup } = this.options;
519914
520493
  if (startup.auto) await session.setPermission("auto");
519915
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");
519916
520497
  if (startup.plan) {
519917
520498
  if (!(await session.getStatus()).planMode) await session.setPlanMode(true);
519918
520499
  }
@@ -519921,6 +520502,8 @@ var BlunTUI = class {
519921
520502
  const { startup } = this.options;
519922
520503
  if (startup.auto) this.setAppState({ permissionMode: "auto" });
519923
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" });
519924
520507
  if (startup.plan) this.setAppState({ planMode: true });
519925
520508
  }
519926
520509
  async activateRuntime() {
@@ -520095,7 +520678,6 @@ var BlunTUI = class {
520095
520678
  this.approvalController.cancelAll(approvalCancellationFeedback("reloading_session"));
520096
520679
  this.questionController.cancelAll("");
520097
520680
  this.resetSessionRuntime();
520098
- await session.setModel(BLUN_KING_MODEL_ALIAS, { allowFallback: false });
520099
520681
  this.session = session;
520100
520682
  this.harness.setTelemetryContext({ sessionId: session.id });
520101
520683
  this.registerSessionHandlers(session);