@dimi-agent/cli 0.5.4 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.mjs +308 -199
- package/package.json +1 -1
package/dist/main.mjs
CHANGED
|
@@ -119585,22 +119585,6 @@ function wrapSubagentModelError(error, boundModel, callerModelAlias) {
|
|
|
119585
119585
|
}
|
|
119586
119586
|
});
|
|
119587
119587
|
}
|
|
119588
|
-
/** Human-readable duration for the subagent timeout message. */
|
|
119589
|
-
function formatSubagentTimeoutDescription(ms) {
|
|
119590
|
-
if (ms % (3600 * 1e3) === 0) {
|
|
119591
|
-
const h = ms / (3600 * 1e3);
|
|
119592
|
-
return `${h} hour${h === 1 ? "" : "s"}`;
|
|
119593
|
-
}
|
|
119594
|
-
if (ms % (60 * 1e3) === 0) {
|
|
119595
|
-
const m = ms / (60 * 1e3);
|
|
119596
|
-
return `${m} minute${m === 1 ? "" : "s"}`;
|
|
119597
|
-
}
|
|
119598
|
-
if (ms % 1e3 === 0) {
|
|
119599
|
-
const s = ms / 1e3;
|
|
119600
|
-
return `${s} second${s === 1 ? "" : "s"}`;
|
|
119601
|
-
}
|
|
119602
|
-
return `${ms} ms`;
|
|
119603
|
-
}
|
|
119604
119588
|
var SUBAGENT_SECTION, SubagentConfigSchema, DEFAULT_SUBAGENT_TIMEOUT_MS, SUBAGENT_TIMEOUT_ENV, subagentEnvBindings, stripSubagentEnv;
|
|
119605
119589
|
var init_configSection$7 = __esmMin((() => {
|
|
119606
119590
|
init_zod$1();
|
|
@@ -121101,6 +121085,56 @@ var init_toolPolicyService = __esmMin((() => {
|
|
|
121101
121085
|
registerScopedService(2, IAgentToolPolicyService, AgentToolPolicyService, 0, "toolPolicy");
|
|
121102
121086
|
}));
|
|
121103
121087
|
//#endregion
|
|
121088
|
+
//#region ../../packages/agent-core-v2/src/agent/loop/contextSize.ts
|
|
121089
|
+
/**
|
|
121090
|
+
* Scale a token count by `percent` of the model's default window, keeping
|
|
121091
|
+
* at least `CONTEXT_SIZE_FLOOR_TOKENS` — but never more than the original
|
|
121092
|
+
* window, so models already below the floor (and `percent` at or above
|
|
121093
|
+
* 100, or unset) pass through unchanged.
|
|
121094
|
+
*/
|
|
121095
|
+
function scaleContextTokens(tokens, percent) {
|
|
121096
|
+
if (!Number.isFinite(tokens) || tokens <= 0) return tokens;
|
|
121097
|
+
if (percent === void 0 || percent <= 0 || percent >= 100) return tokens;
|
|
121098
|
+
const scaled = Math.floor(tokens * percent / 100);
|
|
121099
|
+
return Math.max(scaled, Math.min(tokens, CONTEXT_SIZE_FLOOR_TOKENS));
|
|
121100
|
+
}
|
|
121101
|
+
/**
|
|
121102
|
+
* Apply the configured context-size percentage to a model capability.
|
|
121103
|
+
* Returns the original object unchanged when nothing changes, so
|
|
121104
|
+
* `UNKNOWN_CAPABILITY` identity and other callers relying on object
|
|
121105
|
+
* equality keep working.
|
|
121106
|
+
*/
|
|
121107
|
+
function scaleModelCapabilityContext(capability, percent) {
|
|
121108
|
+
if (percent === void 0 || percent <= 0 || percent >= 100) return capability;
|
|
121109
|
+
const maxContextTokens = scaleContextTokens(capability.max_context_tokens, percent);
|
|
121110
|
+
const maxInputTokens = capability.max_input_tokens === void 0 ? void 0 : scaleContextTokens(capability.max_input_tokens, percent);
|
|
121111
|
+
if (maxContextTokens === capability.max_context_tokens && maxInputTokens === capability.max_input_tokens) return capability;
|
|
121112
|
+
return {
|
|
121113
|
+
...capability,
|
|
121114
|
+
max_context_tokens: maxContextTokens,
|
|
121115
|
+
max_input_tokens: maxInputTokens
|
|
121116
|
+
};
|
|
121117
|
+
}
|
|
121118
|
+
/**
|
|
121119
|
+
* Offered percentage levels for a model window: `100`, `95`, … descending
|
|
121120
|
+
* in `CONTEXT_SIZE_STEP_PERCENT` steps while the scaled size stays at or
|
|
121121
|
+
* above `CONTEXT_SIZE_FLOOR_TOKENS`. Empty when the window is below the
|
|
121122
|
+
* floor — the model's context size is not adjustable.
|
|
121123
|
+
*/
|
|
121124
|
+
function contextSizePercentOptions(contextWindow) {
|
|
121125
|
+
if (!Number.isFinite(contextWindow) || contextWindow <= 0) return [];
|
|
121126
|
+
const options = [];
|
|
121127
|
+
for (let percent = 100; percent >= 5; percent -= 5) {
|
|
121128
|
+
if (Math.floor(contextWindow * percent / 100) < 2e5) break;
|
|
121129
|
+
options.push(percent);
|
|
121130
|
+
}
|
|
121131
|
+
return options;
|
|
121132
|
+
}
|
|
121133
|
+
var CONTEXT_SIZE_FLOOR_TOKENS;
|
|
121134
|
+
var init_contextSize$1 = __esmMin((() => {
|
|
121135
|
+
CONTEXT_SIZE_FLOOR_TOKENS = 2e5;
|
|
121136
|
+
}));
|
|
121137
|
+
//#endregion
|
|
121104
121138
|
//#region ../../packages/agent-core-v2/src/agent/loop/configSection.ts
|
|
121105
121139
|
function parseNonNegativeInt(raw) {
|
|
121106
121140
|
const value = raw.trim();
|
|
@@ -121114,6 +121148,7 @@ var init_configSection$5 = __esmMin((() => {
|
|
|
121114
121148
|
init_config$5();
|
|
121115
121149
|
init_configSectionContributions();
|
|
121116
121150
|
init_toml();
|
|
121151
|
+
init_contextSize$1();
|
|
121117
121152
|
LOOP_CONTROL_SECTION = "loopControl";
|
|
121118
121153
|
LOOP_MAX_STEPS_PER_TURN_ENV = "DIMI_LOOP_MAX_STEPS_PER_TURN";
|
|
121119
121154
|
LOOP_MAX_RETRIES_PER_STEP_ENV = "DIMI_LOOP_MAX_RETRIES_PER_STEP";
|
|
@@ -121122,7 +121157,13 @@ var init_configSection$5 = __esmMin((() => {
|
|
|
121122
121157
|
maxRetriesPerStep: number$2().int().min(0).optional(),
|
|
121123
121158
|
maxRalphIterations: number$2().int().min(-1).optional(),
|
|
121124
121159
|
reservedContextSize: number$2().int().min(0).optional(),
|
|
121125
|
-
compactionTriggerRatio: number$2().min(.5).max(.99).optional()
|
|
121160
|
+
compactionTriggerRatio: number$2().min(.5).max(.99).optional(),
|
|
121161
|
+
/**
|
|
121162
|
+
* Effective context window as a percentage of the model's default, in
|
|
121163
|
+
* 5% steps, never below `CONTEXT_SIZE_FLOOR_TOKENS`. See
|
|
121164
|
+
* `agent/loop/contextSize.ts` for the scaling semantics.
|
|
121165
|
+
*/
|
|
121166
|
+
contextSizePercent: number$2().int().min(5).max(100).multipleOf(5).optional()
|
|
121126
121167
|
});
|
|
121127
121168
|
loopControlEnvBindings = envBindings(LoopControlSchema, {
|
|
121128
121169
|
maxStepsPerTurn: {
|
|
@@ -138782,7 +138823,15 @@ var init_subagent = __esmMin((() => {
|
|
|
138782
138823
|
async function runAgentTurn(target, request, options) {
|
|
138783
138824
|
options.signal.throwIfAborted();
|
|
138784
138825
|
const promptService = target.accessor.get(IAgentPromptService);
|
|
138785
|
-
const turn = request.kind === "prompt" ? await (await promptService.
|
|
138826
|
+
const turn = request.kind === "prompt" ? await (await (options.steer === true ? promptService.enqueueOrSteer({ message: {
|
|
138827
|
+
role: "user",
|
|
138828
|
+
content: [{
|
|
138829
|
+
type: "text",
|
|
138830
|
+
text: request.prompt
|
|
138831
|
+
}],
|
|
138832
|
+
toolCalls: [],
|
|
138833
|
+
origin: AGENT_RUN_PROMPT_ORIGIN
|
|
138834
|
+
} }) : promptService.enqueue({ message: {
|
|
138786
138835
|
role: "user",
|
|
138787
138836
|
content: [{
|
|
138788
138837
|
type: "text",
|
|
@@ -138790,7 +138839,7 @@ async function runAgentTurn(target, request, options) {
|
|
|
138790
138839
|
}],
|
|
138791
138840
|
toolCalls: [],
|
|
138792
138841
|
origin: AGENT_RUN_PROMPT_ORIGIN
|
|
138793
|
-
} })).launched : await promptService.retry();
|
|
138842
|
+
} }))).launched : await promptService.retry();
|
|
138794
138843
|
if (turn === void 0) throw new Error("Agent turn could not be started");
|
|
138795
138844
|
if (options.onReady !== void 0) turn.ready.then(() => options.onReady?.()).catch(() => {});
|
|
138796
138845
|
const completion = awaitRun(target, turn, options);
|
|
@@ -138964,7 +139013,8 @@ var init_subagentService = __esmMin((() => {
|
|
|
138964
139013
|
return runAgentTurn(handle, request, {
|
|
138965
139014
|
summaryPolicy: opts.summaryPolicy ?? this.summaryPolicyFor(handle),
|
|
138966
139015
|
signal: opts.signal,
|
|
138967
|
-
onReady: opts.onReady
|
|
139016
|
+
onReady: opts.onReady,
|
|
139017
|
+
steer: opts.steer
|
|
138968
139018
|
});
|
|
138969
139019
|
}
|
|
138970
139020
|
notifyAgentTaskStopped(context) {
|
|
@@ -139236,7 +139286,7 @@ var init_mirrorAgentRun = __esmMin((() => {
|
|
|
139236
139286
|
init_eventBus();
|
|
139237
139287
|
init_agentLifecycle();
|
|
139238
139288
|
init_subagent();
|
|
139239
|
-
})), DEFAULT_PROFILE_NAME, SubagentToolInputSchema,
|
|
139289
|
+
})), DEFAULT_PROFILE_NAME, SubagentToolInputSchema, RESUME_WITH_TYPE_UNAVAILABLE, USER_INTERRUPTED_SUBAGENT_MESSAGE, SUBAGENT_STOPPED_MESSAGE, ISubagentTool;
|
|
139240
139290
|
var init_agent$2 = __esmMin((() => {
|
|
139241
139291
|
init_zod$1();
|
|
139242
139292
|
init_instantiation();
|
|
@@ -139253,8 +139303,7 @@ var init_agent$2 = __esmMin((() => {
|
|
|
139253
139303
|
prompt: string$2().describe("Full task prompt for the subagent"),
|
|
139254
139304
|
description: string$2().describe("Short task description (3-5 words) for UI display"),
|
|
139255
139305
|
subagent_type: string$2().optional().describe("One of the available agent types (see \"Available agent types\" in this tool description). Defaults to \"coder\" when omitted."),
|
|
139256
|
-
resume: string$2().optional().describe("
|
|
139257
|
-
run_in_background: boolean$2().optional().describe("If true, return immediately without waiting for completion. Prefer false unless the task can run independently and there is a clear benefit to not waiting."),
|
|
139306
|
+
resume: string$2().optional().describe("Agent ID to message or continue instead of creating a new instance. Messaging an existing subagent works exactly like a human steering the agent: if it is still running, the prompt is injected into its current turn immediately; if it is idle, it starts a normal turn. When set, do not also pass subagent_type — the resumed agent keeps its own type, and supplying both is rejected."),
|
|
139258
139307
|
model: _enum(["secondary", "primary"]).optional().describe("Which model to run the subagent on: \"secondary\" = the configured secondary model; \"primary\" = the main model you are running on (for hard, quality-sensitive tasks). This explicit choice overrides the selected agent type's model_preference; without either, secondary is the default when configured. Only effective when a secondary model is configured; otherwise the subagent inherits your model. Ignored when resuming — resumed subagents keep their own model.")
|
|
139259
139308
|
}));
|
|
139260
139309
|
object({
|
|
@@ -139266,7 +139315,6 @@ var init_agent$2 = __esmMin((() => {
|
|
|
139266
139315
|
cache_write: number$2().int().nonnegative().optional()
|
|
139267
139316
|
}).describe("Cumulative token usage")
|
|
139268
139317
|
});
|
|
139269
|
-
BACKGROUND_AGENT_UNAVAILABLE = "Background agent execution is not available for this agent because TaskList, TaskOutput, and TaskStop are not enabled.";
|
|
139270
139318
|
RESUME_WITH_TYPE_UNAVAILABLE = "Cannot set subagent_type when resuming an existing agent. Resume by agent id only.";
|
|
139271
139319
|
USER_INTERRUPTED_SUBAGENT_MESSAGE = "The subagent was stopped before it finished by user.";
|
|
139272
139320
|
SUBAGENT_STOPPED_MESSAGE = "The subagent was stopped before it finished.";
|
|
@@ -139312,22 +139360,10 @@ function firstNonEmpty(...values) {
|
|
|
139312
139360
|
}
|
|
139313
139361
|
var init_subagentMetadata = __esmMin((() => {}));
|
|
139314
139362
|
//#endregion
|
|
139315
|
-
//#region ../../packages/agent-core-v2/src/agent/tools/agent/agent-background-disabled.md?raw
|
|
139316
|
-
var agent_background_disabled_default;
|
|
139317
|
-
var init_agent_background_disabled = __esmMin((() => {
|
|
139318
|
-
agent_background_disabled_default = "Background agent execution is disabled for this agent. Do not set `run_in_background=true` — any call that sets it is rejected before the subagent launches. Run every subagent in the foreground and wait for its result.";
|
|
139319
|
-
}));
|
|
139320
|
-
//#endregion
|
|
139321
|
-
//#region ../../packages/agent-core-v2/src/agent/tools/agent/agent-background-enabled.md?raw
|
|
139322
|
-
var agent_background_enabled_default;
|
|
139323
|
-
var init_agent_background_enabled = __esmMin((() => {
|
|
139324
|
-
agent_background_enabled_default = "When `run_in_background=true`, the subagent runs detached from this turn. The completion arrives in a later turn as a synthetic user-role message containing its result — you do not need to poll, sleep, or check on its progress. Continue with other work or respond to the user. Never fabricate or predict what the result will say.\n\nDefault to a foreground subagent (omit `run_in_background`) when your next step needs its result — foreground hands the result straight back. Reach for `run_in_background=true` only when you have other work to do while it runs and do not need its result to proceed. Never launch in the background and then immediately wait on it (by polling `TaskOutput`, sleeping, or otherwise): that just blocks the turn for no benefit — run it in the foreground instead.\n";
|
|
139325
|
-
}));
|
|
139326
|
-
//#endregion
|
|
139327
139363
|
//#region ../../packages/agent-core-v2/src/agent/tools/agent/agent.md?raw
|
|
139328
139364
|
var agent_default;
|
|
139329
139365
|
var init_agent$1 = __esmMin((() => {
|
|
139330
|
-
agent_default = "Launch a subagent to handle a task. The subagent runs as a same-process loop instance with its own context and wire file. Delegating also keeps the bulk of intermediate file contents out of your own context — you get a conclusion back instead of a pile of dumps.\n\nWriting the prompt:\n- The subagent starts with zero context — it has not seen this conversation. Brief it like a colleague who just walked into the room: state the goal, list what you already know, hand over the specifics.\n- Lookups (read this file, run that test): put the exact path or command in the prompt. The subagent should not have to search for things you already know.\n- Investigations (figure out X, find why Y): give the question, not prescribed steps — fixed steps become dead weight when the premise is wrong.\n- Do not delegate understanding. If the task hinges on a file path or line number, find it yourself first and write it into the prompt.\n\nUsage notes:\n- When the task continues earlier work a subagent already did, prefer resuming that agent (pass its `resume` id) over spawning a fresh instance — the resumed agent keeps its prior context.\n- A subagent's result is only visible to you, not to the user. When the user needs to see what a subagent produced, summarize the relevant parts yourself in your own reply.\n- Subagents use a fixed 2-hour timeout. If one times out, resume the same agent instead of starting over.\n\nWhen NOT to use Agent: skip delegation for trivial work you can do directly — reading a file whose path you already know, searching a small known set of files, or any task that takes only a step or two. Delegation has a context-handoff cost; it pays off only when the task is substantial enough to outweigh it.\n\nOnce a subagent is running, leave that scope to it: do not redo its searches or reads in parallel, and do not abandon it midway and finish the job manually. Both undo the context savings the delegation was meant to buy.\n";
|
|
139366
|
+
agent_default = "Launch a subagent to handle a task. The subagent runs as a same-process loop instance with its own context and wire file. Delegating also keeps the bulk of intermediate file contents out of your own context — you get a conclusion back instead of a pile of dumps.\n\nThe subagent is **fully asynchronous**: this tool returns immediately with the subagent's `agent_id` and a `task_id` — it never blocks your turn. Do the rest of your work while it runs; its result arrives on its own as a completion notification with the final summary. You do not need to poll, sleep, or check on it — and never fabricate or predict what the result will say.\n\nWhen you genuinely have nothing else to do and want to see how it is going:\n\n- Call `AgentOutput(agent_id=\"...\")` to read its recent rendered output (assistant text, thinking, tool calls, progress) — the same view a human sees in the TUI.\n- If it is still working, call `WaitFor` with a reasonable `timeout_seconds` instead of polling `AgentOutput` in a loop; the wait wakes you on the completion notification or the timeout, then check again with `AgentOutput`.\n\nWriting the prompt:\n- The subagent starts with zero context — it has not seen this conversation. Brief it like a colleague who just walked into the room: state the goal, list what you already know, hand over the specifics.\n- Lookups (read this file, run that test): put the exact path or command in the prompt. The subagent should not have to search for things you already know.\n- Investigations (figure out X, find why Y): give the question, not prescribed steps — fixed steps become dead weight when the premise is wrong.\n- Do not delegate understanding. If the task hinges on a file path or line number, find it yourself first and write it into the prompt.\n\nUsage notes:\n- When the task continues earlier work a subagent already did, prefer resuming that agent (pass its `resume` id) over spawning a fresh instance — the resumed agent keeps its prior context. `resume` works exactly like a human steering the agent: while the subagent is still running, your prompt is injected into its current turn immediately; when it is idle, it starts a normal turn. Use it to redirect, follow up, or send it new information.\n- A subagent's result is only visible to you, not to the user. When the user needs to see what a subagent produced, summarize the relevant parts yourself in your own reply.\n- Subagents use a fixed 2-hour timeout. If one times out, resume the same agent instead of starting over.\n\nWhen NOT to use Agent: skip delegation for trivial work you can do directly — reading a file whose path you already know, searching a small known set of files, or any task that takes only a step or two. Delegation has a context-handoff cost; it pays off only when the task is substantial enough to outweigh it.\n\nOnce a subagent is running, leave that scope to it: do not redo its searches or reads in parallel, and do not abandon it midway and finish the job manually. Both undo the context savings the delegation was meant to buy.\n";
|
|
139331
139367
|
}));
|
|
139332
139368
|
//#endregion
|
|
139333
139369
|
//#region ../../packages/agent-core-v2/src/agent/tools/agent/agentTool.ts
|
|
@@ -139351,7 +139387,7 @@ function buildProfileDescriptions(profiles, tools, isToolActive$1, showModelPref
|
|
|
139351
139387
|
return `${headerLines}\n Tools: ${activeTools.join(", ")}`;
|
|
139352
139388
|
}).join("\n");
|
|
139353
139389
|
}
|
|
139354
|
-
function
|
|
139390
|
+
function formatAsyncAgentResult(taskId, handle, description) {
|
|
139355
139391
|
return [
|
|
139356
139392
|
`task_id: ${taskId}`,
|
|
139357
139393
|
"status: running",
|
|
@@ -139361,31 +139397,11 @@ function formatBackgroundAgentResult(taskId, handle, description, allowBackgroun
|
|
|
139361
139397
|
"",
|
|
139362
139398
|
`description: ${description}`,
|
|
139363
139399
|
"",
|
|
139364
|
-
|
|
139365
|
-
`
|
|
139400
|
+
"next_step: The subagent runs fully asynchronously — continue with other work. Its final result arrives later as a completion notification.",
|
|
139401
|
+
`progress_hint: To check on it, call AgentOutput(agent_id="${handle.agentId}") to read its recent output (assistant text, thinking, tool calls). If you have nothing else to do, call WaitFor with a reasonable timeout_seconds instead of polling, then check again with AgentOutput.`,
|
|
139402
|
+
`resume_hint: To continue, redirect, or send this subagent a message — like a human steering the agent — call Agent(resume="${handle.agentId}", prompt="..."). While it is still running the prompt is injected into its current turn immediately; when idle it starts a normal turn. The parameter is agent_id ("${handle.agentId}"), NOT task_id ("${taskId}") or source_id from a later <notification>. Recovery cases: a later <notification type="task.lost" | "task.failed" | "task.killed"> for this subagent — its conversation history is preserved across session restarts and resume will pick it up.`
|
|
139366
139403
|
].join("\n");
|
|
139367
139404
|
}
|
|
139368
|
-
function formatForegroundAgentSuccess(handle, result) {
|
|
139369
|
-
return [
|
|
139370
|
-
`agent_id: ${handle.agentId}`,
|
|
139371
|
-
`actual_subagent_type: ${handle.profileName}`,
|
|
139372
|
-
"status: completed",
|
|
139373
|
-
"",
|
|
139374
|
-
"[summary]",
|
|
139375
|
-
result
|
|
139376
|
-
].join("\n");
|
|
139377
|
-
}
|
|
139378
|
-
function formatForegroundAgentFailure(handle, message, timedOut) {
|
|
139379
|
-
const lines = [
|
|
139380
|
-
`agent_id: ${handle.agentId}`,
|
|
139381
|
-
`actual_subagent_type: ${handle.profileName}`,
|
|
139382
|
-
"status: failed",
|
|
139383
|
-
"",
|
|
139384
|
-
`subagent error: ${message}`
|
|
139385
|
-
];
|
|
139386
|
-
if (timedOut) 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.`);
|
|
139387
|
-
return lines.join("\n");
|
|
139388
|
-
}
|
|
139389
139405
|
function launchErrorMessage(error, signal) {
|
|
139390
139406
|
if (isUserCancellation(signal.reason)) return USER_INTERRUPTED_SUBAGENT_MESSAGE;
|
|
139391
139407
|
if (isAbortError$1(error)) return formatSubagentStoppedMessage(errorMessage$4(signal.reason));
|
|
@@ -139413,7 +139429,6 @@ var init_agentTool = __esmMin((() => {
|
|
|
139413
139429
|
init_toolPolicy();
|
|
139414
139430
|
init_permissionMode();
|
|
139415
139431
|
init_scopeContext();
|
|
139416
|
-
init_loop();
|
|
139417
139432
|
init_userTool();
|
|
139418
139433
|
init_toolContract();
|
|
139419
139434
|
init_toolContribution();
|
|
@@ -139437,8 +139452,6 @@ var init_agentTool = __esmMin((() => {
|
|
|
139437
139452
|
init_flag$2();
|
|
139438
139453
|
init_agent$2();
|
|
139439
139454
|
init_subagent_task();
|
|
139440
|
-
init_agent_background_disabled();
|
|
139441
|
-
init_agent_background_enabled();
|
|
139442
139455
|
init_agent$1();
|
|
139443
139456
|
init_decorateParam();
|
|
139444
139457
|
init_decorate();
|
|
@@ -139461,7 +139474,6 @@ var init_agentTool = __esmMin((() => {
|
|
|
139461
139474
|
name = "Agent";
|
|
139462
139475
|
parameters = toInputJsonSchema(SubagentToolInputSchema);
|
|
139463
139476
|
callerAgentId;
|
|
139464
|
-
canRunInBackground;
|
|
139465
139477
|
constructor(lifecycle, subagents, catalog, scopeContext, tasks, profile, toolPolicy, toolRegistry, workspace, processRunner, sessionMetadata, log, permissionMode, config, flags, modelCatalog) {
|
|
139466
139478
|
this.lifecycle = lifecycle;
|
|
139467
139479
|
this.subagents = subagents;
|
|
@@ -139479,10 +139491,9 @@ var init_agentTool = __esmMin((() => {
|
|
|
139479
139491
|
this.flags = flags;
|
|
139480
139492
|
this.modelCatalog = modelCatalog;
|
|
139481
139493
|
this.callerAgentId = scopeContext.agentId;
|
|
139482
|
-
this.canRunInBackground = () => this.toolPolicy.isToolActive("TaskList") && this.toolPolicy.isToolActive("TaskOutput") && this.toolPolicy.isToolActive("TaskStop");
|
|
139483
139494
|
}
|
|
139484
139495
|
get description() {
|
|
139485
|
-
let description =
|
|
139496
|
+
let description = agent_default;
|
|
139486
139497
|
const allowlist = subagentAllowlistFor(this.catalog, this.profile.data());
|
|
139487
139498
|
const typeLines = buildProfileDescriptions(allowlist === void 0 ? this.catalog.list() : this.catalog.list().filter((profile) => allowlist.includes(profile.name)), this.knownToolReferences(), (profile, name, source) => this.toolPolicy.isToolActiveForProfile(profile, name, source), this.flags.enabled(SECONDARY_MODEL_FLAG_ID), this.flags.enabled(BACKGROUND_BASH_STDIN_FLAG_ID));
|
|
139488
139499
|
if (typeLines) description += `\n\nAvailable agent types (pass via subagent_type):\n${typeLines}`;
|
|
@@ -139508,13 +139519,13 @@ var init_agentTool = __esmMin((() => {
|
|
|
139508
139519
|
};
|
|
139509
139520
|
const profileNameForDisplay = resumeAgentId !== void 0 && resumeAgentId.length > 0 ? this.resumeProfileName(resumeAgentId) ?? "subagent" : requestedProfileName ?? "coder";
|
|
139510
139521
|
return {
|
|
139511
|
-
description:
|
|
139522
|
+
description: `Launching ${profileNameForDisplay} agent: ${args.description}`,
|
|
139512
139523
|
accesses: ToolAccesses.none(),
|
|
139513
139524
|
display: {
|
|
139514
139525
|
kind: "agent_call",
|
|
139515
139526
|
agent_name: profileNameForDisplay,
|
|
139516
139527
|
prompt: args.prompt,
|
|
139517
|
-
background:
|
|
139528
|
+
background: false
|
|
139518
139529
|
},
|
|
139519
139530
|
approvalRule: this.name,
|
|
139520
139531
|
matchesRule: (ruleArgs) => matchesGlobRuleSubject(ruleArgs, profileNameForDisplay),
|
|
@@ -139537,7 +139548,7 @@ var init_agentTool = __esmMin((() => {
|
|
|
139537
139548
|
if (isResume) {
|
|
139538
139549
|
const target = this.lifecycle.get(resumeAgentId);
|
|
139539
139550
|
if (target === void 0) throw new Error(`Agent instance "${resumeAgentId}" does not exist`);
|
|
139540
|
-
await this.
|
|
139551
|
+
await this.ensureOwnedSubagent(resumeAgentId);
|
|
139541
139552
|
agentId = target.id;
|
|
139542
139553
|
profileName = target.accessor.get(IAgentProfileService).data().profileName ?? "subagent";
|
|
139543
139554
|
} else {
|
|
@@ -139578,17 +139589,19 @@ var init_agentTool = __esmMin((() => {
|
|
|
139578
139589
|
log: this.log
|
|
139579
139590
|
});
|
|
139580
139591
|
}
|
|
139581
|
-
const runInBackground = args.run_in_background === true;
|
|
139582
139592
|
emitAgentRunSpawned(requester, agentId, {
|
|
139583
139593
|
profileName,
|
|
139584
139594
|
parentToolCallId: toolCallId,
|
|
139585
139595
|
description: args.description,
|
|
139586
|
-
runInBackground
|
|
139596
|
+
runInBackground: false
|
|
139587
139597
|
});
|
|
139588
139598
|
const mirrored = mirrorAgentRun(requester, await this.subagents.run(agentId, {
|
|
139589
139599
|
kind: "prompt",
|
|
139590
139600
|
prompt: promptText
|
|
139591
|
-
}, {
|
|
139601
|
+
}, {
|
|
139602
|
+
signal: controller.signal,
|
|
139603
|
+
steer: isResume
|
|
139604
|
+
}), {
|
|
139592
139605
|
profileName,
|
|
139593
139606
|
prompt: promptText,
|
|
139594
139607
|
signal: controller.signal,
|
|
@@ -139605,16 +139618,14 @@ var init_agentTool = __esmMin((() => {
|
|
|
139605
139618
|
}))
|
|
139606
139619
|
};
|
|
139607
139620
|
}
|
|
139608
|
-
async
|
|
139621
|
+
async ensureOwnedSubagent(agentId) {
|
|
139609
139622
|
const meta = (await this.sessionMetadata.read()).agents?.[agentId];
|
|
139610
139623
|
if (!isSubagentMeta(meta)) throw new Error(`Agent instance "${agentId}" is not a subagent`);
|
|
139611
139624
|
if (subagentParentAgentId(meta) !== this.callerAgentId) throw new Error(`Agent instance "${agentId}" does not belong to this parent agent`);
|
|
139612
|
-
if (target.accessor.get(IAgentLoopService).status().state === "running") throw new Error(`Agent instance "${agentId}" is already running and cannot run concurrently`);
|
|
139613
139625
|
}
|
|
139614
139626
|
async execution(args, { toolCallId, signal }) {
|
|
139615
139627
|
try {
|
|
139616
139628
|
signal.throwIfAborted();
|
|
139617
|
-
const runInBackground = args.run_in_background === true;
|
|
139618
139629
|
const requestedProfileName = args.subagent_type?.length ? args.subagent_type : void 0;
|
|
139619
139630
|
const resumeAgentId = args.resume?.trim();
|
|
139620
139631
|
const isResume = resumeAgentId !== void 0 && resumeAgentId.length > 0;
|
|
@@ -139622,17 +139633,12 @@ var init_agentTool = __esmMin((() => {
|
|
|
139622
139633
|
output: RESUME_WITH_TYPE_UNAVAILABLE,
|
|
139623
139634
|
isError: true
|
|
139624
139635
|
};
|
|
139625
|
-
const allowBackground = this.canRunInBackground();
|
|
139626
|
-
if (runInBackground && !allowBackground) return {
|
|
139627
|
-
output: BACKGROUND_AGENT_UNAVAILABLE,
|
|
139628
|
-
isError: true
|
|
139629
|
-
};
|
|
139630
139636
|
const timeoutMs = resolveSubagentTimeoutMs(this.config);
|
|
139631
139637
|
const controller = new AbortController();
|
|
139632
139638
|
const abortBeforeRegister = () => {
|
|
139633
139639
|
controller.abort(signal.reason);
|
|
139634
139640
|
};
|
|
139635
|
-
|
|
139641
|
+
signal.addEventListener("abort", abortBeforeRegister, { once: true });
|
|
139636
139642
|
let handle;
|
|
139637
139643
|
try {
|
|
139638
139644
|
handle = await this.launch(args, toolCallId, controller);
|
|
@@ -139640,7 +139646,6 @@ var init_agentTool = __esmMin((() => {
|
|
|
139640
139646
|
signal.removeEventListener("abort", abortBeforeRegister);
|
|
139641
139647
|
this.log.warn("subagent launch failed", {
|
|
139642
139648
|
toolCallId,
|
|
139643
|
-
runInBackground,
|
|
139644
139649
|
operation: isResume ? "resume" : "spawn",
|
|
139645
139650
|
subagentType: requestedProfileName ?? "coder",
|
|
139646
139651
|
resumeAgentId: isResume ? resumeAgentId : void 0,
|
|
@@ -139651,9 +139656,9 @@ var init_agentTool = __esmMin((() => {
|
|
|
139651
139656
|
let taskId;
|
|
139652
139657
|
try {
|
|
139653
139658
|
const registerOptions = {
|
|
139654
|
-
detached:
|
|
139659
|
+
detached: true,
|
|
139655
139660
|
timeoutMs,
|
|
139656
|
-
signal:
|
|
139661
|
+
signal: void 0
|
|
139657
139662
|
};
|
|
139658
139663
|
taskId = this.tasks.registerTask(new SubagentTask(handle, args.description, controller), registerOptions);
|
|
139659
139664
|
signal.removeEventListener("abort", abortBeforeRegister);
|
|
@@ -139661,7 +139666,7 @@ var init_agentTool = __esmMin((() => {
|
|
|
139661
139666
|
controller.abort();
|
|
139662
139667
|
handle.completion.catch(() => {});
|
|
139663
139668
|
signal.removeEventListener("abort", abortBeforeRegister);
|
|
139664
|
-
this.log?.warn("
|
|
139669
|
+
this.log?.warn("subagent task registration failed", {
|
|
139665
139670
|
toolCallId,
|
|
139666
139671
|
agentId: handle.agentId,
|
|
139667
139672
|
subagentType: handle.profileName,
|
|
@@ -139673,9 +139678,7 @@ var init_agentTool = __esmMin((() => {
|
|
|
139673
139678
|
isError: true
|
|
139674
139679
|
};
|
|
139675
139680
|
}
|
|
139676
|
-
|
|
139677
|
-
if (await this.tasks.waitForForegroundRelease(taskId) === "detached") return { output: formatBackgroundAgentResult(taskId, handle, args.description, allowBackground) };
|
|
139678
|
-
return await this.formatForegroundResult(taskId, handle, timeoutMs);
|
|
139681
|
+
return { output: formatAsyncAgentResult(taskId, handle, args.description) };
|
|
139679
139682
|
} catch (error) {
|
|
139680
139683
|
return {
|
|
139681
139684
|
output: `subagent error: ${launchErrorMessage(error, signal)}`,
|
|
@@ -139683,15 +139686,6 @@ var init_agentTool = __esmMin((() => {
|
|
|
139683
139686
|
};
|
|
139684
139687
|
}
|
|
139685
139688
|
}
|
|
139686
|
-
async formatForegroundResult(taskId, handle, timeoutMs) {
|
|
139687
|
-
const info = this.tasks.getTask(taskId);
|
|
139688
|
-
if (info?.status === "completed") return { output: formatForegroundAgentSuccess(handle, await this.tasks.readOutput(taskId)) };
|
|
139689
|
-
const timedOut = info?.status === "timed_out";
|
|
139690
|
-
return {
|
|
139691
|
-
output: formatForegroundAgentFailure(handle, timedOut ? `Agent timed out after ${formatSubagentTimeoutDescription(timeoutMs)}.` : formatSubagentStoppedMessage(info?.stopReason), timedOut),
|
|
139692
|
-
isError: true
|
|
139693
|
-
};
|
|
139694
|
-
}
|
|
139695
139689
|
};
|
|
139696
139690
|
SubagentTool = __decorate$1([
|
|
139697
139691
|
__decorateParam(0, IAgentLifecycleService),
|
|
@@ -211918,6 +211912,8 @@ var init_profileService = __esmMin((() => {
|
|
|
211918
211912
|
init_errors$11();
|
|
211919
211913
|
init_bootstrap();
|
|
211920
211914
|
init_config$5();
|
|
211915
|
+
init_configSection$5();
|
|
211916
|
+
init_contextSize$1();
|
|
211921
211917
|
init_hostEnvironment();
|
|
211922
211918
|
init_hostFileSystem();
|
|
211923
211919
|
init_sessionContext();
|
|
@@ -212005,6 +212001,7 @@ var init_profileService = __esmMin((() => {
|
|
|
212005
212001
|
this.publishToolPatternWarnings();
|
|
212006
212002
|
this.refreshSystemPrompt();
|
|
212007
212003
|
}
|
|
212004
|
+
if (domain === "loopControl") this.emitStatusUpdated();
|
|
212008
212005
|
}));
|
|
212009
212006
|
this._register(this.skillCatalog.onDidChange((sourceId) => {
|
|
212010
212007
|
if (sourceId === "plugin") this.refreshSystemPrompt();
|
|
@@ -212202,7 +212199,7 @@ var init_profileService = __esmMin((() => {
|
|
|
212202
212199
|
return {
|
|
212203
212200
|
cwd: this.cwd,
|
|
212204
212201
|
modelAlias: this.modelAlias,
|
|
212205
|
-
modelCapabilities:
|
|
212202
|
+
modelCapabilities: this.capabilitiesFor(model),
|
|
212206
212203
|
profileName: this.profileName,
|
|
212207
212204
|
thinkingLevel: this.thinkingLevel,
|
|
212208
212205
|
systemPrompt: this.systemPrompt,
|
|
@@ -212217,10 +212214,10 @@ var init_profileService = __esmMin((() => {
|
|
|
212217
212214
|
resolveModelContext() {
|
|
212218
212215
|
const modelAlias = this.model;
|
|
212219
212216
|
const model = this.modelCatalog.get(modelAlias);
|
|
212220
|
-
const loopControl = this.config.get(
|
|
212217
|
+
const loopControl = this.config.get(LOOP_CONTROL_SECTION);
|
|
212221
212218
|
return {
|
|
212222
212219
|
modelAlias,
|
|
212223
|
-
modelCapabilities:
|
|
212220
|
+
modelCapabilities: this.capabilitiesFor(model),
|
|
212224
212221
|
maxOutputSize: model.maxTokens,
|
|
212225
212222
|
alwaysThinking: modelAlwaysThinking(model) || void 0,
|
|
212226
212223
|
thinkingLevel: this.resolveThinkingState(model).effective,
|
|
@@ -212245,8 +212242,15 @@ var init_profileService = __esmMin((() => {
|
|
|
212245
212242
|
};
|
|
212246
212243
|
}
|
|
212247
212244
|
getModelCapabilities() {
|
|
212248
|
-
|
|
212249
|
-
|
|
212245
|
+
return this.capabilitiesFor(this.tryResolveRawModel());
|
|
212246
|
+
}
|
|
212247
|
+
/**
|
|
212248
|
+
* Model capabilities with the user's `[loop_control] context_size_percent`
|
|
212249
|
+
* applied to the token limits. The percentage is read on every call, so
|
|
212250
|
+
* config changes take effect on the next request without a restart.
|
|
212251
|
+
*/
|
|
212252
|
+
capabilitiesFor(model) {
|
|
212253
|
+
return scaleModelCapabilityContext(model === void 0 ? UNKNOWN_CAPABILITY : modelCapabilities(model), this.config.get(LOOP_CONTROL_SECTION)?.contextSizePercent);
|
|
212250
212254
|
}
|
|
212251
212255
|
getMaxOutputSize() {
|
|
212252
212256
|
return this.tryResolveRawModel()?.maxTokens;
|
|
@@ -214622,7 +214626,6 @@ var init_sessionSwarmService = __esmMin((() => {
|
|
|
214622
214626
|
init_abort();
|
|
214623
214627
|
init_profile();
|
|
214624
214628
|
init_permissionMode();
|
|
214625
|
-
init_loop();
|
|
214626
214629
|
init_userTool();
|
|
214627
214630
|
init_eventBus();
|
|
214628
214631
|
init_sessionAgentProfileCatalog();
|
|
@@ -214751,7 +214754,6 @@ var init_sessionSwarmService = __esmMin((() => {
|
|
|
214751
214754
|
await this.requireOwnedSubagent(callerAgentId, agentId);
|
|
214752
214755
|
const caller = this.requireHandle(callerAgentId, "Caller agent");
|
|
214753
214756
|
const child = this.requireHandle(agentId, "Agent instance");
|
|
214754
|
-
this.requireIdleSubagent(agentId, child);
|
|
214755
214757
|
const profileName = child.accessor.get(IAgentProfileService).data().profileName ?? RESUMED_PROFILE_FALLBACK;
|
|
214756
214758
|
if (!retryTurn) emitAgentRunSpawned(caller, agentId, {
|
|
214757
214759
|
profileName,
|
|
@@ -214765,15 +214767,16 @@ var init_sessionSwarmService = __esmMin((() => {
|
|
|
214765
214767
|
kind: "prompt",
|
|
214766
214768
|
prompt: options.prompt
|
|
214767
214769
|
};
|
|
214768
|
-
return this.observe(caller, child.id, profileName, request, options);
|
|
214770
|
+
return this.observe(caller, child.id, profileName, request, options, !retryTurn);
|
|
214769
214771
|
}
|
|
214770
|
-
async observe(caller, agentId, profileName, request, options) {
|
|
214772
|
+
async observe(caller, agentId, profileName, request, options, steer = false) {
|
|
214771
214773
|
return {
|
|
214772
214774
|
agentId,
|
|
214773
214775
|
profileName,
|
|
214774
214776
|
completion: mirrorAgentRun(caller, await this.subagents.run(agentId, request, {
|
|
214775
214777
|
signal: options.signal,
|
|
214776
|
-
onReady: options.onReady
|
|
214778
|
+
onReady: options.onReady,
|
|
214779
|
+
steer
|
|
214777
214780
|
}), {
|
|
214778
214781
|
profileName,
|
|
214779
214782
|
prompt: request.kind === "prompt" ? request.prompt : void 0,
|
|
@@ -214790,9 +214793,6 @@ var init_sessionSwarmService = __esmMin((() => {
|
|
|
214790
214793
|
if (handle === void 0) throw new Error(`${label} "${agentId}" does not exist`);
|
|
214791
214794
|
return handle;
|
|
214792
214795
|
}
|
|
214793
|
-
requireIdleSubagent(agentId, child) {
|
|
214794
|
-
if (child.accessor.get(IAgentLoopService).status().state === "running") throw new Error(`Agent instance "${agentId}" is already running and cannot run concurrently`);
|
|
214795
|
-
}
|
|
214796
214796
|
async requireOwnedSubagent(callerAgentId, agentId) {
|
|
214797
214797
|
const meta = await this.agentMeta(agentId);
|
|
214798
214798
|
if (!isSubagentMeta(meta)) throw new Error(`Agent instance "${agentId}" is not a subagent`);
|
|
@@ -217745,6 +217745,7 @@ var init_src$6 = __esmMin((() => {
|
|
|
217745
217745
|
init_retry();
|
|
217746
217746
|
init_configSection$5();
|
|
217747
217747
|
init_configSection$5();
|
|
217748
|
+
init_contextSize$1();
|
|
217748
217749
|
init_loop();
|
|
217749
217750
|
init_loopService();
|
|
217750
217751
|
init_loopContinuation();
|
|
@@ -309793,6 +309794,134 @@ var init_busy_input_mode_selector = __esmMin((() => {
|
|
|
309793
309794
|
};
|
|
309794
309795
|
}));
|
|
309795
309796
|
//#endregion
|
|
309797
|
+
//#region src/utils/usage/usage-format.ts
|
|
309798
|
+
function usageNumber$1(value) {
|
|
309799
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
|
|
309800
|
+
}
|
|
309801
|
+
/**
|
|
309802
|
+
* Total prompt tokens for a single generation: non-cache input + cache
|
|
309803
|
+
* read + cache write/creation. Matches the denominator pi uses for CH%.
|
|
309804
|
+
*/
|
|
309805
|
+
function promptTokenTotal(usage) {
|
|
309806
|
+
if (usage === null || usage === void 0) return 0;
|
|
309807
|
+
return usageNumber$1(usage.inputOther) + usageNumber$1(usage.inputCacheRead) + usageNumber$1(usage.inputCacheCreation);
|
|
309808
|
+
}
|
|
309809
|
+
/**
|
|
309810
|
+
* Latest-step prompt-cache hit rate as a percentage in [0, 100].
|
|
309811
|
+
* Returns `undefined` when there is no prompt traffic, or when the
|
|
309812
|
+
* provider reported neither cache reads nor cache writes (no cache
|
|
309813
|
+
* signal — same gate as pi's footer `CH` badge).
|
|
309814
|
+
*/
|
|
309815
|
+
function cacheHitRatePercent(usage) {
|
|
309816
|
+
if (usage === null || usage === void 0) return void 0;
|
|
309817
|
+
const cacheRead = usageNumber$1(usage.inputCacheRead);
|
|
309818
|
+
const cacheWrite = usageNumber$1(usage.inputCacheCreation);
|
|
309819
|
+
if (cacheRead === 0 && cacheWrite === 0) return void 0;
|
|
309820
|
+
const prompt = promptTokenTotal(usage);
|
|
309821
|
+
if (prompt <= 0) return void 0;
|
|
309822
|
+
return Math.min(100, Math.max(0, cacheRead / prompt * 100));
|
|
309823
|
+
}
|
|
309824
|
+
/** Compact footer/report label, e.g. `CH85.0%`. */
|
|
309825
|
+
function formatCacheHitRate(rate) {
|
|
309826
|
+
if (!Number.isFinite(rate)) return "CH?%";
|
|
309827
|
+
return `CH${rate.toFixed(1)}%`;
|
|
309828
|
+
}
|
|
309829
|
+
/**
|
|
309830
|
+
* Format a token count in 1024-based units: context sizes are powers of
|
|
309831
|
+
* two, so 262144 reads as "256k", not "262.1k". k values at or above
|
|
309832
|
+
* 100 are rounded to whole numbers ("977k").
|
|
309833
|
+
*/
|
|
309834
|
+
function formatTokenCount(n) {
|
|
309835
|
+
if (!Number.isFinite(n) || n < 0) return "0";
|
|
309836
|
+
if (n >= 1024 * 1024) return `${trimDecimal(n / (1024 * 1024))}M`;
|
|
309837
|
+
if (n >= 1024) {
|
|
309838
|
+
const k = n / 1024;
|
|
309839
|
+
return `${k >= 100 ? Math.round(k) : trimDecimal(k)}k`;
|
|
309840
|
+
}
|
|
309841
|
+
return String(n);
|
|
309842
|
+
}
|
|
309843
|
+
/** One decimal place, dropping a redundant ".0" ("1.0" → "1", "1.5" stays). */
|
|
309844
|
+
function trimDecimal(v) {
|
|
309845
|
+
const s = v.toFixed(1);
|
|
309846
|
+
return s.endsWith(".0") ? s.slice(0, -2) : s;
|
|
309847
|
+
}
|
|
309848
|
+
/**
|
|
309849
|
+
* Format a token count in 1000-based units ("1M", "950k", "200k"). Unlike
|
|
309850
|
+
* `formatTokenCount` (1024-based, the footer/usage convention), this keeps
|
|
309851
|
+
* exact product numbers readable: the context-size picker's floor is a
|
|
309852
|
+
* literal 200k, and 5% steps of a 1M window land on round values.
|
|
309853
|
+
*/
|
|
309854
|
+
function formatDecimalTokenCount(n) {
|
|
309855
|
+
if (!Number.isFinite(n) || n < 0) return "0";
|
|
309856
|
+
if (n >= 1e6) return `${trimDecimal(n / 1e6)}M`;
|
|
309857
|
+
if (n >= 1e3) return `${trimDecimal(n / 1e3)}k`;
|
|
309858
|
+
return String(n);
|
|
309859
|
+
}
|
|
309860
|
+
/**
|
|
309861
|
+
* Usage as a whole-number percentage of `max`, ceiled so any non-zero
|
|
309862
|
+
* usage shows at least 1%, clamped to [0, 100]. A non-positive or
|
|
309863
|
+
* non-finite `max` reports 0.
|
|
309864
|
+
*/
|
|
309865
|
+
function usagePercent(used, max) {
|
|
309866
|
+
if (!Number.isFinite(max) || max <= 0) return 0;
|
|
309867
|
+
return Math.min(100, Math.max(0, Math.ceil(used / max * 100)));
|
|
309868
|
+
}
|
|
309869
|
+
/** `usagePercent` for callers that only know the ratio (NaN-safe). */
|
|
309870
|
+
function usagePercentFromRatio(ratio) {
|
|
309871
|
+
return Math.min(100, Math.max(0, Math.ceil(safeUsageRatio(ratio) * 100)));
|
|
309872
|
+
}
|
|
309873
|
+
/**
|
|
309874
|
+
* Build a `[███░░░░░░░]` style bar. Returns a plain-ASCII string with
|
|
309875
|
+
* `filled`/`empty` glyphs — colouring is the caller's responsibility.
|
|
309876
|
+
*/
|
|
309877
|
+
function renderProgressBar(ratio, width = 20, filled = "█", empty = "░") {
|
|
309878
|
+
const clamped = safeUsageRatio(ratio);
|
|
309879
|
+
const filledCount = Math.round(clamped * width);
|
|
309880
|
+
return filled.repeat(filledCount) + empty.repeat(Math.max(0, width - filledCount));
|
|
309881
|
+
}
|
|
309882
|
+
function safeUsageRatio(ratio) {
|
|
309883
|
+
return Number.isFinite(ratio) ? Math.max(0, Math.min(ratio, 1)) : 0;
|
|
309884
|
+
}
|
|
309885
|
+
/**
|
|
309886
|
+
* Map a usage ratio to a semantic colour token — the `/usage` renderer
|
|
309887
|
+
* translates these into palette hex values.
|
|
309888
|
+
*/
|
|
309889
|
+
function ratioSeverity(ratio) {
|
|
309890
|
+
if (ratio >= .85) return "danger";
|
|
309891
|
+
if (ratio >= .5) return "warn";
|
|
309892
|
+
return "ok";
|
|
309893
|
+
}
|
|
309894
|
+
var init_usage_format = __esmMin((() => {}));
|
|
309895
|
+
//#endregion
|
|
309896
|
+
//#region src/tui/components/dialogs/context-size-selector.ts
|
|
309897
|
+
function buildOptions(contextWindow, percentOptions) {
|
|
309898
|
+
return percentOptions.map((percent) => ({
|
|
309899
|
+
value: String(percent),
|
|
309900
|
+
label: percent === 100 ? "100% (default)" : `${percent}%`,
|
|
309901
|
+
description: `${formatDecimalTokenCount(Math.floor(contextWindow * percent / 100))} tokens`
|
|
309902
|
+
}));
|
|
309903
|
+
}
|
|
309904
|
+
var ContextSizeSelectorComponent;
|
|
309905
|
+
var init_context_size_selector = __esmMin((() => {
|
|
309906
|
+
init_src$6();
|
|
309907
|
+
init_usage_format();
|
|
309908
|
+
init_choice_picker();
|
|
309909
|
+
ContextSizeSelectorComponent = class extends ChoicePickerComponent {
|
|
309910
|
+
constructor(opts) {
|
|
309911
|
+
super({
|
|
309912
|
+
title: "Context size",
|
|
309913
|
+
hint: `Model window ${formatDecimalTokenCount(opts.contextWindow)} · floor ${formatDecimalTokenCount(CONTEXT_SIZE_FLOOR_TOKENS)}`,
|
|
309914
|
+
options: buildOptions(opts.contextWindow, opts.percentOptions),
|
|
309915
|
+
currentValue: String(opts.currentPercent),
|
|
309916
|
+
onSelect: (value) => {
|
|
309917
|
+
opts.onSelect(Number(value));
|
|
309918
|
+
},
|
|
309919
|
+
onCancel: opts.onCancel
|
|
309920
|
+
});
|
|
309921
|
+
}
|
|
309922
|
+
};
|
|
309923
|
+
}));
|
|
309924
|
+
//#endregion
|
|
309796
309925
|
//#region src/tui/components/dialogs/editor-selector.ts
|
|
309797
309926
|
var EDITOR_OPTIONS, EditorSelectorComponent;
|
|
309798
309927
|
var init_editor_selector = __esmMin((() => {
|
|
@@ -310253,7 +310382,7 @@ var init_permission_selector = __esmMin((() => {
|
|
|
310253
310382
|
//#endregion
|
|
310254
310383
|
//#region src/tui/components/dialogs/settings-selector.ts
|
|
310255
310384
|
function isSettingsSelection(value) {
|
|
310256
|
-
return value === "model" || value === "theme" || value === "editor" || value === "permission" || value === "busy-input" || value === "experiments" || value === "upgrade" || value === "usage";
|
|
310385
|
+
return value === "model" || value === "theme" || value === "editor" || value === "permission" || value === "busy-input" || value === "context-size" || value === "experiments" || value === "upgrade" || value === "usage";
|
|
310257
310386
|
}
|
|
310258
310387
|
var SETTINGS_OPTIONS, SettingsSelectorComponent;
|
|
310259
310388
|
var init_settings_selector = __esmMin((() => {
|
|
@@ -310284,6 +310413,11 @@ var init_settings_selector = __esmMin((() => {
|
|
|
310284
310413
|
label: "Busy input",
|
|
310285
310414
|
description: "Choose whether Enter queues or steers while the agent is working."
|
|
310286
310415
|
},
|
|
310416
|
+
{
|
|
310417
|
+
value: "context-size",
|
|
310418
|
+
label: "Context size",
|
|
310419
|
+
description: "Cap the conversation window as a percentage of the model default (min 200k)."
|
|
310420
|
+
},
|
|
310287
310421
|
{
|
|
310288
310422
|
value: "experiments",
|
|
310289
310423
|
label: "Experiments",
|
|
@@ -310529,93 +310663,6 @@ var init_mcp_status_panel = __esmMin((() => {
|
|
|
310529
310663
|
];
|
|
310530
310664
|
}));
|
|
310531
310665
|
//#endregion
|
|
310532
|
-
//#region src/utils/usage/usage-format.ts
|
|
310533
|
-
function usageNumber$1(value) {
|
|
310534
|
-
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
|
|
310535
|
-
}
|
|
310536
|
-
/**
|
|
310537
|
-
* Total prompt tokens for a single generation: non-cache input + cache
|
|
310538
|
-
* read + cache write/creation. Matches the denominator pi uses for CH%.
|
|
310539
|
-
*/
|
|
310540
|
-
function promptTokenTotal(usage) {
|
|
310541
|
-
if (usage === null || usage === void 0) return 0;
|
|
310542
|
-
return usageNumber$1(usage.inputOther) + usageNumber$1(usage.inputCacheRead) + usageNumber$1(usage.inputCacheCreation);
|
|
310543
|
-
}
|
|
310544
|
-
/**
|
|
310545
|
-
* Latest-step prompt-cache hit rate as a percentage in [0, 100].
|
|
310546
|
-
* Returns `undefined` when there is no prompt traffic, or when the
|
|
310547
|
-
* provider reported neither cache reads nor cache writes (no cache
|
|
310548
|
-
* signal — same gate as pi's footer `CH` badge).
|
|
310549
|
-
*/
|
|
310550
|
-
function cacheHitRatePercent(usage) {
|
|
310551
|
-
if (usage === null || usage === void 0) return void 0;
|
|
310552
|
-
const cacheRead = usageNumber$1(usage.inputCacheRead);
|
|
310553
|
-
const cacheWrite = usageNumber$1(usage.inputCacheCreation);
|
|
310554
|
-
if (cacheRead === 0 && cacheWrite === 0) return void 0;
|
|
310555
|
-
const prompt = promptTokenTotal(usage);
|
|
310556
|
-
if (prompt <= 0) return void 0;
|
|
310557
|
-
return Math.min(100, Math.max(0, cacheRead / prompt * 100));
|
|
310558
|
-
}
|
|
310559
|
-
/** Compact footer/report label, e.g. `CH85.0%`. */
|
|
310560
|
-
function formatCacheHitRate(rate) {
|
|
310561
|
-
if (!Number.isFinite(rate)) return "CH?%";
|
|
310562
|
-
return `CH${rate.toFixed(1)}%`;
|
|
310563
|
-
}
|
|
310564
|
-
/**
|
|
310565
|
-
* Format a token count in 1024-based units: context sizes are powers of
|
|
310566
|
-
* two, so 262144 reads as "256k", not "262.1k". k values at or above
|
|
310567
|
-
* 100 are rounded to whole numbers ("977k").
|
|
310568
|
-
*/
|
|
310569
|
-
function formatTokenCount(n) {
|
|
310570
|
-
if (!Number.isFinite(n) || n < 0) return "0";
|
|
310571
|
-
if (n >= 1024 * 1024) return `${trimDecimal(n / (1024 * 1024))}M`;
|
|
310572
|
-
if (n >= 1024) {
|
|
310573
|
-
const k = n / 1024;
|
|
310574
|
-
return `${k >= 100 ? Math.round(k) : trimDecimal(k)}k`;
|
|
310575
|
-
}
|
|
310576
|
-
return String(n);
|
|
310577
|
-
}
|
|
310578
|
-
/** One decimal place, dropping a redundant ".0" ("1.0" → "1", "1.5" stays). */
|
|
310579
|
-
function trimDecimal(v) {
|
|
310580
|
-
const s = v.toFixed(1);
|
|
310581
|
-
return s.endsWith(".0") ? s.slice(0, -2) : s;
|
|
310582
|
-
}
|
|
310583
|
-
/**
|
|
310584
|
-
* Usage as a whole-number percentage of `max`, ceiled so any non-zero
|
|
310585
|
-
* usage shows at least 1%, clamped to [0, 100]. A non-positive or
|
|
310586
|
-
* non-finite `max` reports 0.
|
|
310587
|
-
*/
|
|
310588
|
-
function usagePercent(used, max) {
|
|
310589
|
-
if (!Number.isFinite(max) || max <= 0) return 0;
|
|
310590
|
-
return Math.min(100, Math.max(0, Math.ceil(used / max * 100)));
|
|
310591
|
-
}
|
|
310592
|
-
/** `usagePercent` for callers that only know the ratio (NaN-safe). */
|
|
310593
|
-
function usagePercentFromRatio(ratio) {
|
|
310594
|
-
return Math.min(100, Math.max(0, Math.ceil(safeUsageRatio(ratio) * 100)));
|
|
310595
|
-
}
|
|
310596
|
-
/**
|
|
310597
|
-
* Build a `[███░░░░░░░]` style bar. Returns a plain-ASCII string with
|
|
310598
|
-
* `filled`/`empty` glyphs — colouring is the caller's responsibility.
|
|
310599
|
-
*/
|
|
310600
|
-
function renderProgressBar(ratio, width = 20, filled = "█", empty = "░") {
|
|
310601
|
-
const clamped = safeUsageRatio(ratio);
|
|
310602
|
-
const filledCount = Math.round(clamped * width);
|
|
310603
|
-
return filled.repeat(filledCount) + empty.repeat(Math.max(0, width - filledCount));
|
|
310604
|
-
}
|
|
310605
|
-
function safeUsageRatio(ratio) {
|
|
310606
|
-
return Number.isFinite(ratio) ? Math.max(0, Math.min(ratio, 1)) : 0;
|
|
310607
|
-
}
|
|
310608
|
-
/**
|
|
310609
|
-
* Map a usage ratio to a semantic colour token — the `/usage` renderer
|
|
310610
|
-
* translates these into palette hex values.
|
|
310611
|
-
*/
|
|
310612
|
-
function ratioSeverity(ratio) {
|
|
310613
|
-
if (ratio >= .85) return "danger";
|
|
310614
|
-
if (ratio >= .5) return "warn";
|
|
310615
|
-
return "ok";
|
|
310616
|
-
}
|
|
310617
|
-
var init_usage_format = __esmMin((() => {}));
|
|
310618
|
-
//#endregion
|
|
310619
310666
|
//#region src/tui/components/messages/usage-panel.ts
|
|
310620
310667
|
function usageRowLabel(row) {
|
|
310621
310668
|
const window = row.window;
|
|
@@ -312380,6 +312427,62 @@ function showBusyInputModePicker(host) {
|
|
|
312380
312427
|
}
|
|
312381
312428
|
}));
|
|
312382
312429
|
}
|
|
312430
|
+
/** `[loop_control]` from a `getConfig()` result (untyped domain access). */
|
|
312431
|
+
function loopControlFromConfig(config) {
|
|
312432
|
+
return config["loopControl"];
|
|
312433
|
+
}
|
|
312434
|
+
/** The active model's default context window in tokens, or 0 when unknown. */
|
|
312435
|
+
function activeModelContextWindow(host) {
|
|
312436
|
+
const alias = host.state.appState.model;
|
|
312437
|
+
return (alias === void 0 ? void 0 : host.state.appState.availableModels[alias])?.maxContextSize ?? 0;
|
|
312438
|
+
}
|
|
312439
|
+
/**
|
|
312440
|
+
* Open the context-size picker: 5% steps from the model's default window
|
|
312441
|
+
* (100%) down while the scaled window stays at or above 200k. Models whose
|
|
312442
|
+
* window is already below 200k are not adjustable — a notice explains why
|
|
312443
|
+
* instead of opening a one-option picker.
|
|
312444
|
+
*/
|
|
312445
|
+
function showContextSizePicker(host) {
|
|
312446
|
+
const contextWindow = activeModelContextWindow(host);
|
|
312447
|
+
if (contextWindow <= 0) {
|
|
312448
|
+
host.showNotice("Context size unavailable", "No model is active. Choose a model first.");
|
|
312449
|
+
return;
|
|
312450
|
+
}
|
|
312451
|
+
const percentOptions = contextSizePercentOptions(contextWindow);
|
|
312452
|
+
if (percentOptions.length <= 1) {
|
|
312453
|
+
host.showNotice("Context size cannot be adjusted", `This model's window (${formatDecimalTokenCount(contextWindow)}) is already at or below the ${formatDecimalTokenCount(CONTEXT_SIZE_FLOOR_TOKENS)} floor.`);
|
|
312454
|
+
return;
|
|
312455
|
+
}
|
|
312456
|
+
host.harness.getConfig().then((config) => {
|
|
312457
|
+
const currentPercent = loopControlFromConfig(config)?.contextSizePercent ?? 100;
|
|
312458
|
+
host.mountEditorReplacement(new ContextSizeSelectorComponent({
|
|
312459
|
+
contextWindow,
|
|
312460
|
+
percentOptions,
|
|
312461
|
+
currentPercent,
|
|
312462
|
+
onSelect: (percent) => {
|
|
312463
|
+
host.restoreEditor();
|
|
312464
|
+
applyContextSizeChoice(host, percent);
|
|
312465
|
+
},
|
|
312466
|
+
onCancel: () => {
|
|
312467
|
+
host.restoreEditor();
|
|
312468
|
+
}
|
|
312469
|
+
}));
|
|
312470
|
+
}, (error) => {
|
|
312471
|
+
host.showError(`Failed to load context size: ${formatErrorMessage$2(error)}`);
|
|
312472
|
+
});
|
|
312473
|
+
}
|
|
312474
|
+
async function applyContextSizeChoice(host, percent) {
|
|
312475
|
+
const contextWindow = activeModelContextWindow(host);
|
|
312476
|
+
try {
|
|
312477
|
+
await host.harness.setConfig({ loopControl: { contextSizePercent: percent } });
|
|
312478
|
+
} catch (error) {
|
|
312479
|
+
host.showError(`Failed to save context size: ${formatErrorMessage$2(error)}`);
|
|
312480
|
+
return;
|
|
312481
|
+
}
|
|
312482
|
+
const maxContextTokens = contextWindow > 0 ? scaleContextTokens(contextWindow, percent) : 0;
|
|
312483
|
+
if (maxContextTokens > 0) host.setAppState({ maxContextTokens });
|
|
312484
|
+
host.showNotice(`Context size: ${percent}%`, `Effective window ${formatDecimalTokenCount(maxContextTokens)} (default ${formatDecimalTokenCount(contextWindow)}).`);
|
|
312485
|
+
}
|
|
312383
312486
|
async function showExperimentsPanel(host) {
|
|
312384
312487
|
let features;
|
|
312385
312488
|
try {
|
|
@@ -312524,6 +312627,9 @@ function handleSettingsSelection(host, value) {
|
|
|
312524
312627
|
case "busy-input":
|
|
312525
312628
|
showBusyInputModePicker(host);
|
|
312526
312629
|
return;
|
|
312630
|
+
case "context-size":
|
|
312631
|
+
showContextSizePicker(host);
|
|
312632
|
+
return;
|
|
312527
312633
|
case "experiments":
|
|
312528
312634
|
showExperimentsPanel(host);
|
|
312529
312635
|
return;
|
|
@@ -312539,6 +312645,7 @@ var MODEL_PICKER_REFRESH_TIMEOUT_MS, MODEL_SWITCH_CACHE_WARNING, EFFORT_SWITCH_C
|
|
|
312539
312645
|
var init_config = __esmMin((() => {
|
|
312540
312646
|
init_src$4();
|
|
312541
312647
|
init_busy_input_mode_selector();
|
|
312648
|
+
init_context_size_selector();
|
|
312542
312649
|
init_editor_selector();
|
|
312543
312650
|
init_effort_selector();
|
|
312544
312651
|
init_experiments_selector();
|
|
@@ -312553,6 +312660,8 @@ var init_config = __esmMin((() => {
|
|
|
312553
312660
|
init_dimi_tui$1();
|
|
312554
312661
|
init_event_payload();
|
|
312555
312662
|
init_thinking_config();
|
|
312663
|
+
init_src$6();
|
|
312664
|
+
init_usage_format();
|
|
312556
312665
|
init_info();
|
|
312557
312666
|
init_experimental_flags();
|
|
312558
312667
|
MODEL_PICKER_REFRESH_TIMEOUT_MS = 2e3;
|